161 lines
6.9 KiB
Python
161 lines
6.9 KiB
Python
"""Monitor and playback IRC stuff."""
|
|
import logging
|
|
from datetime import datetime
|
|
|
|
import irc.client
|
|
|
|
from ircbot.lib import Plugin, most_specific_message
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class History(Plugin):
|
|
"""Watch the history of IRC channels and try to track what users may have missed."""
|
|
|
|
what_missed_regex = r'(?i)(what did I miss\?|did I miss anything\?)$'
|
|
|
|
def __init__(self, bot, connection, event):
|
|
"""Initialize some tracking stuff."""
|
|
super(History, self).__init__(bot, connection, event)
|
|
self.channel_history = {}
|
|
self.channel_participants = {}
|
|
self.channel_leave_points = {}
|
|
|
|
def start(self):
|
|
"""Set up the handlers."""
|
|
logger.debug("%s starting up", __name__)
|
|
self.connection.add_global_handler('pubmsg', self.handle_chatter, 50)
|
|
self.connection.add_global_handler('join', self.handle_join, 50)
|
|
self.connection.add_global_handler('part', self.handle_part, 50)
|
|
self.connection.add_global_handler('quit', self.handle_quit, 50)
|
|
|
|
self.connection.reactor.add_global_regex_handler(['pubmsg', 'privmsg'], self.what_missed_regex,
|
|
self.handle_what_missed, 60)
|
|
|
|
super(History, self).start()
|
|
|
|
def stop(self):
|
|
"""Tear down handlers."""
|
|
logger.debug("%s shutting down", __name__)
|
|
self.connection.remove_global_handler('pubmsg', self.handle_chatter)
|
|
self.connection.remove_global_handler('join', self.handle_join)
|
|
self.connection.remove_global_handler('part', self.handle_part)
|
|
self.connection.remove_global_handler('quit', self.handle_quit)
|
|
|
|
self.connection.reactor.remove_global_regex_handler(['pubmsg', 'privmsg'], self.handle_what_missed)
|
|
|
|
super(History, self).stop()
|
|
|
|
def handle_chatter(self, connection, event):
|
|
"""Track IRC chatter."""
|
|
what = event.arguments[0]
|
|
where = event.target
|
|
who = irc.client.NickMask(event.source).nick
|
|
when = datetime.now()
|
|
|
|
logger.debug("tracking message for %s: (%s,%s)", where, who, what)
|
|
history = self.channel_history.setdefault(where, [])
|
|
history.append((where, when.isoformat(), who, what))
|
|
logger.debug("history for %s: %s", where, history)
|
|
|
|
# for when we maybe don't see a join, if they talked in the channel, add them to it
|
|
self._add_channel_participant(where, who)
|
|
|
|
def handle_join(self, connection, event):
|
|
"""Track who is entitled to see channel history."""
|
|
where = event.target
|
|
who = irc.client.NickMask(event.source).nick
|
|
logger.debug("%s joined %s", who, where)
|
|
|
|
self._add_channel_participant(where, who)
|
|
|
|
def handle_part(self, connection, event):
|
|
"""Note when people leave IRC channels."""
|
|
where = event.target
|
|
who = irc.client.NickMask(event.source).nick
|
|
logger.debug("%s left %s", who, where)
|
|
|
|
# if they parted the channel, they must have been in it, so note their point in history
|
|
self._add_channel_leave_point(where, who)
|
|
self._remove_channel_participant(where, who)
|
|
|
|
def handle_quit(self, connection, event):
|
|
"""Note when people leave IRC."""
|
|
who = irc.client.NickMask(event.source).nick
|
|
logger.debug("%s disconnected", who)
|
|
|
|
# find all channels the quitter was in, save their leave points
|
|
for channel in self.channel_participants.keys():
|
|
self._add_channel_leave_point(channel, who)
|
|
self._remove_channel_participant(channel, who)
|
|
|
|
def handle_what_missed(self, connection, event, match):
|
|
"""Tell the user what they missed."""
|
|
who = irc.client.NickMask(event.source).nick
|
|
if event.in_privmsg or event.addressed:
|
|
logger.debug("<%s> %s is asking for an update", who, most_specific_message(event))
|
|
if event.in_privmsg:
|
|
total_history = []
|
|
channel_count = 0
|
|
for channel in self.channel_leave_points.keys():
|
|
logger.debug("checking history slice for %s", channel)
|
|
total_history += self._missed_slice(channel, who)
|
|
self._delete_channel_leave_point(channel, who)
|
|
logger.debug("total history so far: %s", total_history)
|
|
channel_count += 1
|
|
logger.debug("final missed history: %s", total_history)
|
|
self._send_history(who, total_history)
|
|
self.bot.reply(event, f"{len(total_history)} line(s) over {channel_count} channel(s)")
|
|
return 'NO MORE'
|
|
else:
|
|
where = event.target
|
|
history = self._missed_slice(where, who)
|
|
self._delete_channel_leave_point(where, who)
|
|
self._send_history(who, history)
|
|
privmsged_str = " (PRIVMSGed)" if history else ""
|
|
self.bot.reply(event, f"{len(history)} line(s){privmsged_str}")
|
|
return 'NO MORE'
|
|
|
|
def _send_history(self, who, history):
|
|
"""Reply to who with missed history."""
|
|
for line in history:
|
|
self.bot.privmsg(who, f"{line[0]}: [{line[1]}] <{line[2]}> {line[3]}")
|
|
|
|
def _add_channel_leave_point(self, where, who):
|
|
"""Note that the given who left the channel at the current history point."""
|
|
leave_points = self.channel_leave_points.setdefault(where, {})
|
|
leave_points[who] = len(self.channel_history.setdefault(where, [])) - 1
|
|
logger.debug("leave points for %s: %s", where, leave_points)
|
|
|
|
def _delete_channel_leave_point(self, where, who):
|
|
"""Remove tracking for user's history point."""
|
|
leave_points = self.channel_leave_points.setdefault(where, {})
|
|
leave_points.pop(who, None)
|
|
logger.debug("leave points for %s: %s", where, leave_points)
|
|
|
|
def _add_channel_participant(self, where, who):
|
|
"""Add a who to the list of people who are/were in a channel."""
|
|
participants = self.channel_participants.setdefault(where, set())
|
|
participants.add(who)
|
|
logger.debug("participants for %s: %s", where, participants)
|
|
|
|
def _missed_slice(self, where, who):
|
|
"""Get the lines in where since who last left."""
|
|
leave_points = self.channel_leave_points.setdefault(where, {})
|
|
if leave_points.get(who) is not None:
|
|
leave_point = leave_points.get(who) + 1
|
|
history = self.channel_history.setdefault(where, [])
|
|
missed_history = history[leave_point:]
|
|
logger.debug("in %s, %s missed: %s", where, who, missed_history)
|
|
return missed_history
|
|
return []
|
|
|
|
def _remove_channel_participant(self, where, who):
|
|
"""Remove the specified who from the where channel's participants list."""
|
|
participants = self.channel_participants.setdefault(where, set())
|
|
participants.discard(who)
|
|
logger.debug("participants for %s: %s", where, participants)
|
|
|
|
|
|
plugin = History
|