| @@ -20,7 +20,9 @@ | |||||
| # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
| # SOFTWARE. | # SOFTWARE. | ||||
| import ast | |||||
| from itertools import chain | from itertools import chain | ||||
| import operator | |||||
| import random | import random | ||||
| from threading import Thread | from threading import Thread | ||||
| import time | import time | ||||
| @@ -46,6 +48,32 @@ class Remind(Command): | |||||
| if command in SNOOZE: | if command in SNOOZE: | ||||
| return "snooze" | return "snooze" | ||||
| @staticmethod | |||||
| def _parse_time(arg): | |||||
| """Parse the wait time for a reminder.""" | |||||
| ast_to_op = { | |||||
| ast.Add: operator.add, ast.Sub: operator.sub, | |||||
| ast.Mult: operator.mul, ast.Div: operator.truediv, | |||||
| ast.FloorDiv: operator.floordiv, ast.Mod: operator.mod, | |||||
| ast.Pow: operator.pow | |||||
| } | |||||
| def _evaluate(node): | |||||
| """Convert an AST node into a real number or raise an exception.""" | |||||
| if isinstance(node, ast.Num): | |||||
| assert isinstance(node.n, (int, long, float)) | |||||
| return node.n | |||||
| elif isinstance(node, ast.BinOp): | |||||
| left, right = _evaluate(node.left), _evaluate(node.right) | |||||
| return ast_to_op[type(node.op)](left, right) | |||||
| else: | |||||
| raise ValueError(node) | |||||
| try: | |||||
| parsed = int(_evaluate(ast.parse(arg, mode="eval").body)) | |||||
| assert parsed > 0 | |||||
| except (SyntaxError, AssertionError, KeyError): | |||||
| raise ValueError(arg) | |||||
| return parsed | |||||
| def _really_get_reminder_by_id(self, user, rid): | def _really_get_reminder_by_id(self, user, rid): | ||||
| """Return the _Reminder object that corresponds to a particular ID. | """Return the _Reminder object that corresponds to a particular ID. | ||||
| @@ -75,9 +103,8 @@ class Remind(Command): | |||||
| def _create_reminder(self, data, user): | def _create_reminder(self, data, user): | ||||
| """Create a new reminder for the given user.""" | """Create a new reminder for the given user.""" | ||||
| try: | try: | ||||
| wait = int(data.args[0]) | |||||
| assert wait > 0 | |||||
| except (ValueError, AssertionError): | |||||
| wait = self._parse_time(data.args[0]) | |||||
| except ValueError: | |||||
| msg = "Invalid time \x02{0}\x0F. Time must be a positive integer, in seconds." | msg = "Invalid time \x02{0}\x0F. Time must be a positive integer, in seconds." | ||||
| return self.reply(data, msg.format(data.args[0])) | return self.reply(data, msg.format(data.args[0])) | ||||
| message = " ".join(data.args[1:]) | message = " ".join(data.args[1:]) | ||||
| @@ -115,10 +142,9 @@ class Remind(Command): | |||||
| """Snooze a reminder to be re-triggered after a period of time.""" | """Snooze a reminder to be re-triggered after a period of time.""" | ||||
| if arg: | if arg: | ||||
| try: | try: | ||||
| duration = int(data.args[arg]) | |||||
| assert duration > 0 | |||||
| duration = self._parse_time(data.args[arg]) | |||||
| reminder.wait = duration | reminder.wait = duration | ||||
| except (IndexError, ValueError, AssertionError): | |||||
| except (IndexError, ValueError): | |||||
| pass | pass | ||||
| reminder.start() | reminder.start() | ||||
| end_time = time.strftime("%b %d %H:%M:%S %Z", reminder.end) | end_time = time.strftime("%b %d %H:%M:%S %Z", reminder.end) | ||||