#!/usr/bin/env python3 """ Reminds of things. """ import os import re import time import sqlite3 import threading import collections from datetime import datetime import pytz from module import commands, example, NOLIMIT 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 init_database(bot): """ Initializes the 'remind' table in the bot's database. Does nothing if the table already exists. """ con = bot.db.connect() cur = con.cursor() try: cur.execute("SELECT * FROM remind").fetchone() except sqlite3.OperationalError: cur.execute("CREATE TABLE remind(" "unixtime INT DEFAULT (STRFTIME('%s', 'now'))," "channel STRING," "nick STRING," "message STRING)") con.commit() con.close() def load_database(bot): """ Loads all entries from the 'remind' table in the bot's database and stores them in memory """ data = {} con = bot.db.connect() cur = con.cursor() reminds = cur.execute("SELECT * FROM remind").fetchall() con.close() for remind in reminds: unixtime, channel, nick, message = remind reminder = (channel, nick, message) try: data[unixtime].append(reminder) except KeyError: data[unixtime] = [reminder] return data def insert_reminder(bot, unixtime, reminder): """ Inserts a new reminder into the 'remind' table in the bot's database. reminder - a tuple containing (channel, nick, message) """ con = bot.db.connect() cur = con.cursor() cur.execute("INSERT INTO remind (unixtime, channel, nick, message) " "VALUES(?,?,?,?)", (unixtime,) + reminder) con.commit() con.close() def delete_reminder(bot, unixtime): """ Deletes a reminder from the 'remind' table in the bot's database, using unixtime as the key. """ con = bot.db.connect() cur = con.cursor() cur.execute("DELETE FROM remind WHERE unixtime = ?", (unixtime,)) con.commit() con.close() def setup(bot): init_database(bot) bot.rdb = load_database(bot) def monitor(bot): time.sleep(10) 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] delete_reminder(bot, oldtime) 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] insert_reminder(bot, t, reminder) 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)