# -*- coding: utf-8 -*- """ EarwigBot's IRC Watcher Component The IRC watcher runs on a wiki recent-changes server and listens for edits. Users cannot interact with this part of the bot. When an event occurs, we run it through rules.py's process() function, which can result in wiki bot tasks being started (located in tasks/) or messages being sent to channels on the IRC frontend. """ import logging import config from classes import Connection, RC, BrokenSocketException import rules frontend_conn = None logger = logging.getLogger("watcher") def get_connection(): """Return a new Connection() instance with connection information. Don't actually connect yet. """ cf = config.irc["watcher"] connection = Connection(cf["host"], cf["port"], cf["nick"], cf["ident"], cf["realname"], logger) return connection def main(connection, f_conn=None): """Main loop for the Watcher IRC Bot component. get_connection() should have already been called and the connection should have been started with connection.connect(). Accept the frontend connection as well as an optional parameter in order to send messages directly to frontend IRC channels. """ global frontend_conn frontend_conn = f_conn read_buffer = str() while 1: try: read_buffer = read_buffer + connection.get() except BrokenSocketException: return lines = read_buffer.split("\n") read_buffer = lines.pop() for line in lines: _process_message(connection, line) def _process_message(connection, line): """Process a single message from IRC.""" line = line.strip().split() if line[1] == "PRIVMSG": chan = line[2] # Ignore messages originating from channels not in our list, to prevent # someone PMing us false data: if chan not in config.irc["watcher"]["channels"]: return msg = ' '.join(line[3:])[1:] rc = RC(msg) # new RC object to store this event's data rc.parse() # parse a message into pagenames, usernames, etc. process_rc(rc) # report to frontend channels or start tasks # If we are pinged, pong back to the server: elif line[0] == "PING": msg = " ".join(("PONG", line[1])) connection.send(msg) # When we've finished starting up, join all watcher channels: elif line[1] == "376": for chan in config.irc["watcher"]["channels"]: connection.join(chan) def process_rc(rc): """Process a recent change event from IRC (or, an RC object). The actual processing is configurable, so we don't have that hard-coded here. We simply call rules's process() function and expect a list of channels back, which we report the event data to. """ chans = rules.process(rc) if chans and frontend_conn: pretty = rc.prettify() for chan in chans: frontend_conn.say(chan, pretty)