fulvia/modules/remind.py

198 lines
4.9 KiB
Python
Raw Normal View History

2018-03-16 03:13:43 -04:00
#!/usr/bin/env python3
"""
Reminds of things.
"""
import os
import re
import time
import sqlite3
import threading
2020-01-07 18:58:19 -05:00
from datetime import datetime
2018-03-16 03:13:43 -04:00
2019-10-08 12:39:13 -04:00
import config
2018-03-16 03:13:43 -04:00
from module import commands, example
class MonitorThread(threading.Thread):
"""
A custom custom thread class for monitoring the time to announce
reminders. It allows itself to be stopped when there are no reminders
to look for.
"""
def __init__(self, bot):
threading.Thread.__init__(self)
self._bot = bot
self.stop = threading.Event()
def run(self):
while not self._bot.stillConnected():
time.sleep(1)
# don't try to say anything if we're not fully connected yet
while not self.stop.is_set():
now = int(time.time())
unixtimes = [int(key) for key in self._bot.memory["remind"].keys()]
oldtimes = [t for t in unixtimes if t <= now]
if oldtimes:
for oldtime in oldtimes:
for reminder in self._bot.memory["remind"][oldtime]:
channel, nick, message = reminder
if message:
self._bot.msg(channel, nick + ': ' + message)
else:
self._bot.msg(channel, nick + '!')
del self._bot.memory["remind"][oldtime]
delete_reminder(self._bot, oldtime)
if not self._bot.memory["remind"] or not self._bot.stillConnected():
self.stop.set()
self.stop.wait(2.5)
2018-03-16 03:13:43 -04:00
del self._bot.memory["remind_monitor"]
def start_monitor(bot):
"""
Starts the monitor thread. Does nothing if one is already running.
"""
if bot.memory.get("remind_monitor"):
return
t = MonitorThread(bot)
t.start()
bot.memory["remind_monitor"] = t
def load_database(bot):
"""
Loads all entries from the 'remind' table in the bot's database and
stores them in memory
"""
data = {}
reminds = bot.db.execute("SELECT * FROM remind").fetchall()
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)
"""
bot.db.execute("INSERT INTO remind (unixtime, channel, nick, message) "
"VALUES(?,?,?,?)", (unixtime,) + reminder)
def delete_reminder(bot, unixtime):
"""
Deletes a reminder from the 'remind' table in the bot's database, using
unixtime as the key.
"""
bot.db.execute("DELETE FROM remind WHERE unixtime = ?", (unixtime,))
def setup(bot):
con = bot.db.connect()
cur = con.cursor()
try:
cur.execute("SELECT * FROM remind").fetchone()
except sqlite3.OperationalError:
cur.execute("CREATE TABLE remind("
"unixtime INTEGER DEFAULT (STRFTIME('%s', 'now')),"
"channel TEXT,"
"nick TEXT,"
"message TEXT)")
con.commit()
con.close()
bot.memory["remind"] = load_database(bot)
start_monitor(bot)
2019-06-28 08:06:39 -04:00
regex = (
"(?=\d+[ywdhms])"
"(?:(\d+)y)?"
"(?:(\d+)w)?"
"(?:(\d+)d)?"
"(?:(\d+)h)?"
"(?:(\d+)m)?"
"(?:(\d+)s)?"
)
multiplier = [
60*60*24*365,
60*60*24*7,
60*60*24,
60*60,
60,
1
]
2018-03-16 03:13:43 -04:00
@commands('remind')
@example('.remind 3h45m Go to class')
def remind(bot, trigger):
"""Gives you a reminder in the given amount of time."""
2020-01-07 18:58:19 -05:00
if len(trigger.args) == 1:
2018-05-25 15:21:18 -04:00
return bot.msg("Missing arguments for reminder command.")
2020-01-07 18:58:19 -05:00
if len(trigger.args) == 2:
2019-06-28 08:06:39 -04:00
reminder = ''
else:
2020-01-07 18:58:19 -05:00
reminder = ' '.join(trigger.args[2:])
2018-03-16 03:13:43 -04:00
duration = 0
2020-01-07 18:58:19 -05:00
for n, group in enumerate(re.search(regex, trigger.args[1]).groups()):
2019-06-28 08:06:39 -04:00
if not group:
continue
duration += int(group) * multiplier[n]
2018-03-16 03:13:43 -04:00
create_reminder(bot, trigger, duration, reminder)
@commands('at')
@example('.at 13:47 Do your homework!')
@example('.at 16:30UTC-5 Do cocaine')
@example('.at 14:45:45 Remove dick from oven')
def at(bot, trigger):
"""
2020-01-07 18:58:19 -05:00
Gives you a reminder at the given time and date. Datetime must be in
YYYY-MM-DD HH:MM:SS format. Only the bot's timezone is used.
2018-03-16 03:13:43 -04:00
"""
2020-01-07 18:58:19 -05:00
if len(trigger.args) == 1:
return bot.msg("Missing arguments for reminder command.")
if len(trigger.args) == 2:
reminder = ''
2018-03-16 03:13:43 -04:00
else:
2020-01-07 18:58:19 -05:00
reminder = ' '.join(trigger.args[2:])
2018-03-16 03:13:43 -04:00
2020-01-07 18:58:19 -05:00
try:
at_time = datetime.strptime(trigger.args[1], '%Y-%m-%d %H:%M:%S')
except ValueError:
return bot.msg("Datetime improperly formatted.")
diff = at_time - datetime.now()
duration = diff.seconds
2018-03-16 03:13:43 -04:00
2020-01-07 18:58:19 -05:00
create_reminder(bot, trigger, duration, reminder)
2018-03-16 03:13:43 -04:00
def create_reminder(bot, trigger, duration, message):
"""
Inserts the reminder into the bot's memory and database so it can
eventually announce for it.
"""
t = int(time.time()) + duration
reminder = (trigger.channel, trigger.nick, message)
try:
bot.memory["remind"][t].append(reminder)
except KeyError:
bot.memory["remind"][t] = [reminder]
start_monitor(bot)
insert_reminder(bot, t, reminder)
if duration >= 60:
2020-01-07 18:58:19 -05:00
remind_at = datetime.fromtimestamp(t)
2019-10-08 12:39:13 -04:00
t_format = config.default_time_format
2020-01-07 18:58:19 -05:00
timef = datetime.strftime(remind_at, t_format)
2018-03-16 03:13:43 -04:00
bot.reply('Okay, will remind at %s' % timef)
else:
bot.reply('Okay, will remind in %s secs' % duration)