dr.botzo/countdown/ircplugin.py

168 lines
7.3 KiB
Python

"""Access to countdown items through bot commands."""
import logging
import re
import threading
import time
import irc.client
import parsedatetime as pdt
from dateutil.parser import parse
from dateutil.relativedelta import relativedelta
from django.utils import timezone
from countdown.models import CountdownItem
from ircbot.lib import Plugin, reply_destination_for_event
log = logging.getLogger('countdown.ircplugin')
class Countdown(Plugin):
"""Report on countdown items."""
new_reminder_regex = r'remind\s+([^\s]+)\s+(at|in|on)\s+(.*?)\s+(to|that)\s+(.*)'
def __init__(self, bot, connection, event):
"""Initialize some stuff."""
self.running_reminders = []
self.send_reminders = True
t = threading.Thread(target=self.reminder_thread)
t.daemon = True
t.start()
super(Countdown, self).__init__(bot, connection, event)
def start(self):
"""Set up handlers."""
self.running_reminders = []
self.send_reminders = True
self.connection.reactor.add_global_regex_handler(['pubmsg', 'privmsg'], r'^!countdown\s+list$',
self.handle_item_list, -20)
self.connection.reactor.add_global_regex_handler(['pubmsg', 'privmsg'], r'^!countdown\s+(\S+)$',
self.handle_item_detail, -20)
# let this interrupt markov
self.connection.add_global_handler('pubmsg', self.handle_new_reminder, -50)
self.connection.add_global_handler('privmsg', self.handle_new_reminder, -50)
super(Countdown, self).start()
def stop(self):
"""Tear down handlers."""
self.running_reminders = []
self.send_reminders = False
self.connection.reactor.remove_global_regex_handler(['pubmsg', 'privmsg'], self.handle_item_list)
self.connection.reactor.remove_global_regex_handler(['pubmsg', 'privmsg'], self.handle_item_detail)
self.connection.remove_global_handler('pubmsg', self.handle_new_reminder)
self.connection.remove_global_handler('privmsg', self.handle_new_reminder)
super(Countdown, self).stop()
def reminder_thread(self):
"""After IRC is started up, begin sending reminders."""
time.sleep(30)
while self.send_reminders:
reminders = CountdownItem.objects.filter(is_reminder=True, sent_reminder=False)
for reminder in reminders:
log.debug("%s @ %s", reminder.reminder_message, reminder.at_time)
if reminder.at_time <= timezone.now():
log.info("sending %s to %s", reminder.reminder_message, reminder.reminder_target)
self.bot.reply(None, reminder.reminder_message, explicit_target=reminder.reminder_target)
reminder.sent_reminder = True
reminder.save()
time.sleep(1)
def handle_new_reminder(self, connection, event):
"""Watch IRC for requests to remind new things, create countdown items."""
what = event.arguments[0]
my_nick = connection.get_nickname()
addressed_my_nick = r'^{0:s}[:,]\s+'.format(my_nick)
sender_nick = irc.client.NickMask(event.source).nick
sent_location = reply_destination_for_event(event)
if re.search(addressed_my_nick, what, re.IGNORECASE) is not None:
# we were addressed, were we told to add a reminder?
trimmed_what = re.sub(addressed_my_nick, '', what)
log.debug(trimmed_what)
match = re.match(self.new_reminder_regex, trimmed_what, re.IGNORECASE)
if match:
log.debug("%s is a new reminder request", trimmed_what)
who = match.group(1)
when_type = match.group(2)
when = match.group(3)
text = match.group(5)
log.debug("%s / %s / %s", who, when, text)
item_name = '{0:s}-{1:s}'.format(sender_nick, timezone.now().strftime('%s'))
# parse when to send the notification
if when_type == 'in':
# relative time
calendar = pdt.Calendar()
when_t = calendar.parseDT(when, timezone.localtime(timezone.now()),
tzinfo=timezone.get_current_timezone())[0]
else:
# absolute time
when_t = timezone.make_aware(parse(when))
# parse the person to address, if anyone, when sending the notification
if who == 'us' or who == sent_location:
message = text
elif who == 'me':
message = '{0:s}: {1:s}'.format(sender_nick, text)
else:
message = '{0:s}: {1:s}'.format(who, text)
log.debug("%s / %s / %s", item_name, when_t, message)
countdown_item = CountdownItem.objects.create(name=item_name, at_time=when_t, is_reminder=True,
reminder_message=message, reminder_target=sent_location)
log.info("created countdown item %s", str(countdown_item))
self.bot.reply(event, "ok, i'll message {0:s} at {1:s}".format(sent_location, str(when_t)))
return 'NO MORE'
def handle_item_detail(self, connection, event, match):
"""Provide the details of one countdown item."""
name = match.group(1)
if name != 'list':
try:
item = CountdownItem.objects.get(name=name)
rdelta = relativedelta(item.at_time, timezone.now())
relstr = "{0:s} will occur in ".format(name)
if rdelta.years != 0:
relstr += "{0:s} year{1:s} ".format(str(rdelta.years), "s" if abs(rdelta.years) != 1 else "")
if rdelta.months != 0:
relstr += "{0:s} month{1:s}, ".format(str(rdelta.months), "s" if abs(rdelta.months) != 1 else "")
if rdelta.days != 0:
relstr += "{0:s} day{1:s}, ".format(str(rdelta.days), "s" if abs(rdelta.days) != 1 else "")
if rdelta.hours != 0:
relstr += "{0:s} hour{1:s}, ".format(str(rdelta.hours), "s" if abs(rdelta.hours) != 1 else "")
if rdelta.minutes != 0:
relstr += "{0:s} minute{1:s}, ".format(str(rdelta.minutes), "s" if abs(rdelta.minutes) != 1 else "")
if rdelta.seconds != 0:
relstr += "{0:s} second{1:s}, ".format(str(rdelta.seconds), "s" if abs(rdelta.seconds) != 1 else "")
# remove trailing comma from output
reply = relstr[0:-2]
return self.bot.reply(event, reply)
except CountdownItem.DoesNotExist:
return self.bot.reply(event, "countdown item '{0:s}' not found".format(name))
def handle_item_list(self, connection, event, match):
"""List all countdown items."""
items = CountdownItem.objects.all()
if len(items) > 0:
reply = "countdown items: {0:s}".format(", ".join([x.name for x in items]))
return self.bot.reply(event, reply)
else:
return self.bot.reply(event, "no countdown items are configured")
plugin = Countdown