sopel/modules/remind.py
2018-03-16 12:08:02 -04:00

254 lines
6.2 KiB
Python
Executable File

#!/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 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)