Skip to content
Snippets Groups Projects
irker.py 3.54 KiB
Newer Older
Eric S. Raymond's avatar
Eric S. Raymond committed
#!/usr/bin/env python
"""
irker - a simple IRC multiplexer daemon

Takes JSON objects of the form {'channel':<channel-url>, 'message':<text>}
Eric S. Raymond's avatar
Eric S. Raymond committed
and relays messages to IRC channels.

Run this as a daemon in order to maimntain stateful connections to IRC
servers; this will allow it to respond to server pings and minimize
join/leave traffic.
Eric S. Raymond's avatar
Eric S. Raymond committed

Requires Python 2.6.

Eric S. Raymond's avatar
Eric S. Raymond committed
"""
import os, sys, json, irclib, exceptions, getopt, urlparse
import threading, Queue
Eric S. Raymond's avatar
Eric S. Raymond committed

class SessionException(exceptions.Exception):
    def __init__(self, message):
        exceptions.Exception.__init__(self)
        self.message = message

class Session():
    "IRC session and message queue processing."
Eric S. Raymond's avatar
Eric S. Raymond committed
    count = 1
    def __init__(self, irc, url):
        self.irc = irc
        self.url = url
        # The consumer thread
        self.queue = Queue.Queue()
        self.thread = threading.Thread(target=self.dequeue)
        self.thread.daemon = True
        self.thread.start()
        # Server connection setup
        parsed = urlparse.urlparse(url)
        host, sep, port = parsed.netloc.partition(':')
        if not port:
            port = 6667
        self.servername = host
        self.channel = parsed.path.lstrip('/')
        self.port = int(port)
Eric S. Raymond's avatar
Eric S. Raymond committed
        self.server = self.irc.server()
        print "connecting: server=%s port=%s name=%s" % (self.servername, self.port, self.name())
        #self.server.connect(self.servername, self.port, self.name())
Eric S. Raymond's avatar
Eric S. Raymond committed
        Session.count += 1
    def enqueue(self, message):
        "Enque a message for transmission."
        self.queue.put(message)
    def dequeue(self):
        "Try to ship pending messages from the queue."
        while True:
            message = self.queue.get()
            self.ship(self.channel, message)
            self.queue.task_done()
    def name(self):
        "Generate a unique name for this session."
Eric S. Raymond's avatar
Eric S. Raymond committed
        return "irker" + str(Session.count)
    def await(self):
Eric S. Raymond's avatar
Eric S. Raymond committed
        "Block until processing of all queued messages is done."
        self.queue.join()
    def ship(self, channel, message):
        "Ship a message to the channel."
        print "%s: %s" % (channel, message)
Eric S. Raymond's avatar
Eric S. Raymond committed
        #self.server.connection.join(chaannel)
        #self.server.privmsg(channel, message)
Eric S. Raymond's avatar
Eric S. Raymond committed
class Irker:
    "Persistent IRC multiplexer."
    def __init__(self):
Eric S. Raymond's avatar
Eric S. Raymond committed
        self.irc = irclib.IRC()
Eric S. Raymond's avatar
Eric S. Raymond committed
        thread = threading.Thread(target=self.irc.process_forever)
        self.irc._thread = thread
        thread.daemon = True
        thread.start()
        self.sessions = {}
Eric S. Raymond's avatar
Eric S. Raymond committed
    def logerr(self, errmsg):
        "Log a processing error."
        sys.stderr.write("irker: " + errmsg + "\n")
    def run(self, ifp, await=True):
Eric S. Raymond's avatar
Eric S. Raymond committed
        "Accept JSON relay requests from specified stream."
        while True:
            inp = ifp.readline()
            if not inp:
                break
            try:
                request = json.loads(inp.strip())
            except ValueError:
                self.logerr("can't recognize JSON on input.")
Eric S. Raymond's avatar
Eric S. Raymond committed
                break
            self.relay(request)
        if await:
            for session in self.sessions.values():
                session.await()
Eric S. Raymond's avatar
Eric S. Raymond committed
    def relay(self, request):
        if "channel" not in request or "message" not in request:
            self.logerr("ill-formed reqest")
        else:
            channel = request['channel']
            message = request['message']
            if channel not in self.sessions:
Eric S. Raymond's avatar
Eric S. Raymond committed
                self.sessions[channel] = Session(self.irc, channel)
            self.sessions[channel].enqueue(message)
Eric S. Raymond's avatar
Eric S. Raymond committed

if __name__ == '__main__':
    irker = Irker()
    irker.run(sys.stdin)