From 35407fbd073a850a5d48ca6fc32ca17af8caca53 Mon Sep 17 00:00:00 2001 From: Ben Kurtovic Date: Wed, 19 Feb 2014 18:45:13 -0500 Subject: [PATCH] Allow !remind to accept operations on time (#43). --- earwigbot/commands/remind.py | 38 ++++++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/earwigbot/commands/remind.py b/earwigbot/commands/remind.py index 49e2d52..2cf674e 100644 --- a/earwigbot/commands/remind.py +++ b/earwigbot/commands/remind.py @@ -20,7 +20,9 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +import ast from itertools import chain +import operator import random from threading import Thread import time @@ -46,6 +48,32 @@ class Remind(Command): if command in 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): """Return the _Reminder object that corresponds to a particular ID. @@ -75,9 +103,8 @@ class Remind(Command): def _create_reminder(self, data, user): """Create a new reminder for the given user.""" 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." return self.reply(data, msg.format(data.args[0])) message = " ".join(data.args[1:]) @@ -115,10 +142,9 @@ class Remind(Command): """Snooze a reminder to be re-triggered after a period of time.""" if arg: try: - duration = int(data.args[arg]) - assert duration > 0 + duration = self._parse_time(data.args[arg]) reminder.wait = duration - except (IndexError, ValueError, AssertionError): + except (IndexError, ValueError): pass reminder.start() end_time = time.strftime("%b %d %H:%M:%S %Z", reminder.end)