#!/usr/bin/env python3 """ Reminds of things. """ import os import re import time import threading import collections import codecs import pytz from datetime import datetime from module import commands, example, NOLIMIT import tools from tools.time import get_timezone, format_time def filename(self): name = self.nick + '-' + self.config.core.host + '.reminders.db' return os.path.join(self.config.core.homedir, name) def load_database(name): data = {} if os.path.isfile(name): f = codecs.open(name, 'r') for line in f: unixtime, channel, nick, message = line.split('\t') message = message.rstrip('\n') t = int(float(unixtime)) # WTFs going on here? reminder = (channel, nick, message) try: data[t].append(reminder) except KeyError: data[t] = [reminder] f.close() return data def dump_database(name, data): f = codecs.open(name, 'w', encoding='utf-8') for unixtime, reminders in tools.iteritems(data): for channel, nick, message in reminders: f.write('%s\t%s\t%s\t%s\n' % (unixtime, channel, nick, message)) f.close() def setup(bot): bot.rfn = filename(bot) bot.rdb = load_database(bot.rfn) def monitor(bot): time.sleep(5) while True: now = int(time.time()) unixtimes = [int(key) for key in bot.rdb] oldtimes = [t for t in unixtimes if t <= now] if oldtimes: for oldtime in oldtimes: for (channel, nick, message) in bot.rdb[oldtime]: if message: bot.say(nick + ': ' + message, channel) else: bot.say(nick + '!', channel) del bot.rdb[oldtime] dump_database(bot.rfn, bot.rdb) time.sleep(2.5) targs = (bot,) t = threading.Thread(target=monitor, args=targs) t.start() scaling = collections.OrderedDict([ ('years', 365.25 * 24 * 3600), ('year', 365.25 * 24 * 3600), ('yrs', 365.25 * 24 * 3600), ('y', 365.25 * 24 * 3600), ('months', 29.53059 * 24 * 3600), ('month', 29.53059 * 24 * 3600), ('mo', 29.53059 * 24 * 3600), ('weeks', 7 * 24 * 3600), ('week', 7 * 24 * 3600), ('wks', 7 * 24 * 3600), ('wk', 7 * 24 * 3600), ('w', 7 * 24 * 3600), ('days', 24 * 3600), ('day', 24 * 3600), ('d', 24 * 3600), ('hours', 3600), ('hour', 3600), ('hrs', 3600), ('hr', 3600), ('h', 3600), ('minutes', 60), ('minute', 60), ('mins', 60), ('min', 60), ('m', 60), ('seconds', 1), ('second', 1), ('secs', 1), ('sec', 1), ('s', 1), ]) periods = '|'.join(scaling.keys()) @commands('remind') @example('.remind 3h45m Go to class') def remind(bot, trigger): """Gives you a reminder in the given amount of time.""" if not trigger.group(2): bot.say("Missing arguments for reminder command.") return NOLIMIT if trigger.group(3) and not trigger.group(4): bot.say("No message given for reminder.") return NOLIMIT duration = 0 message = filter(None, re.split('(\d+(?:\.\d+)? ?(?:(?i)' + periods + ')) ?', trigger.group(2))[1:]) reminder = '' stop = False for piece in message: grp = re.match('(\d+(?:\.\d+)?) ?(.*) ?', piece) if grp and not stop: length = float(grp.group(1)) factor = scaling.get(grp.group(2).lower(), 60) duration += length * factor else: reminder = reminder + piece stop = True if duration == 0: return bot.reply("Sorry, didn't understand the input.") if duration % 1: duration = int(duration) + 1 else: duration = int(duration) timezone = get_timezone( bot.db, bot.config, None, trigger.nick, trigger.sender) create_reminder(bot, trigger, duration, reminder, timezone) @commands('at') @example('.at 13:47 Do your homework!') def at(bot, trigger): """ Gives you a reminder at the given time. Takes hh:mm:ssTimezone message. Timezone is any timezone Sopel takes elsewhere; the best choices are those from the tzdb; a list of valid options is available at http://sopel.chat/tz . The seconds and timezone are optional. """ if not trigger.group(2): bot.say("No arguments given for reminder command.") return NOLIMIT if trigger.group(3) and not trigger.group(4): bot.say("No message given for reminder.") return NOLIMIT regex = re.compile(r'(\d+):(\d+)(?::(\d+))?([^\s\d]+)? (.*)') match = regex.match(trigger.group(2)) if not match: bot.reply("Sorry, but I didn't understand your input.") return NOLIMIT hour, minute, second, tz, message = match.groups() if not second: second = '0' if pytz: timezone = get_timezone(bot.db, bot.config, tz, trigger.nick, trigger.sender) if not timezone: timezone = 'UTC' now = datetime.now(pytz.timezone(timezone)) at_time = datetime(now.year, now.month, now.day, int(hour), int(minute), int(second), tzinfo=now.tzinfo) timediff = at_time - now else: if tz and tz.upper() != 'UTC': bot.reply("I don't have timzeone support installed.") return NOLIMIT now = datetime.now() at_time = datetime(now.year, now.month, now.day, int(hour), int(minute), int(second)) timediff = at_time - now duration = timediff.seconds if duration < 0: duration += 86400 create_reminder(bot, trigger, duration, message, 'UTC') def create_reminder(bot, trigger, duration, message, tz): t = int(time.time()) + duration reminder = (trigger.sender, trigger.nick, message) try: bot.rdb[t].append(reminder) except KeyError: bot.rdb[t] = [reminder] dump_database(bot.rfn, bot.rdb) if duration >= 60: remind_at = datetime.utcfromtimestamp(t) timef = format_time(bot.db, bot.config, tz, trigger.nick, trigger.sender, remind_at) bot.reply('Okay, will remind at %s' % timef) else: bot.reply('Okay, will remind in %s secs' % duration)