I had been wanting to write an IRC bot with asynchronous IO for a while. My bot responds to pings, but can be extended by defining functions and registering them to get called when the bot receives certain commands. My bot uses the RFC 1459 USER command and parameters, but can be made to comply with RFC 2812 by replacing my on_connect function with a compliant one. Here is what I wrote based on the asyncore and asynchat modules:

import asynchat
import asyncore
import collections
import socket

def parse(data):
    data = data.split(b' ')
    if data[0].startswith(b':'): #prefix present
        prefix = data[0][1:]
        data = data[1:]
    else:
        prefix = None
    command = data[0]
    data = b' '.join(data[1:])
    if data.startswith(b':'): #only trailing parameter
        parameters = [data[1:]]
    else:
        data = data.split(b' :')
        if len(data) > 1: #trailing parameter present
            trailing = b' :'.join(data[1:])
        else:
            trailing = None
        parameters = data[0].split(b' ') #regular parameters
        if trailing is not None:
            parameters.append(trailing) #add trailing parameter to regular parameters
    return prefix, command, parameters

class Connection(asynchat.async_chat):

    def __init__(self, nick, user, realname, host, port=6667):
        self.nick = nick
        self.user = user
        self.realname = realname
        self.address = (host, port)
        self.received = list()
        asynchat.async_chat.__init__(self)
        self.set_terminator(b'\n')
        self.handlers = collections.defaultdict(set)

    def collect_incoming_data(self, data):
        self.received.append(data)

    def found_terminator(self):
        data = b''.join(self.received).rstrip(b'\r')
        del self.received[:]
        prefix, command, parameters = parse(data)
        try:
            for handler in self.handlers[command]:
                handler(self, prefix, parameters)
        except Exception as exception:
            for handler in self.handlers[exception]:
                handler(self, exception)

    def message(self, string):
        string = ''.join((string, '\r\n'))
        self.push(string.encode())

    def establish(self):
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
        self.connect(self.address)

    #We use a "plugin" oriented system so we can be flexible with how we handle events, messages, and errors.
    def handle(self, command, handler):
        self.handlers[command.encode()].add(handler)

    def ignore(self, command, handler):
        self.handlers[command.encode()].discard(handler)

    def subscribe(self, topic, handler):
        self.handlers[topic].add(handler)

    def unsubscribe(self, topic, handler):
        self.handlers[topic].discard(handler)

    def handle_connect(self):
        try:
            for handler in self.handlers['connect']:
                handler(self)
        except Exception as exception:
            for handler in self.handlers[exception]:
                handler(self, exception)

if __name__ == '__main__':
    
    def on_keyerror(connection, exception):
        pass
    
    def pong(connection, prefix, parameters):
        connection.message('PONG {0}'.format(parameters[0]))

    def on_connect(connection):
        connection.message('NICK {0}'.format(connection.nick))
        connection.message('USER {0} {1} bla :{2}'.format(connection.user, connection.address[0], connection.realname))
        
    c = Connection('lrh9bot', 'lrh9bot', 'lrh9bot', 'irc.dal.net')
    
    c.subscribe(KeyError, on_keyerror)
    c.handle('PING', pong)
    c.subscribe('connect', on_connect)
    
    c.establish()
    asyncore.loop()

I flagged this as a bad post because the code is wrong. I should have tested it more thoroughly. It fails to send messages appropriately. It will respond to pings correctly, but if I write code to send a notice or a private message, it will take a matter of minutes to send any data, and it is only partially transmitted. Possible problems I have considered are blocking problems, asyncore timeout problems, and buffered sending problems. I haven't been able to resolve this issue on my own, so I am examining other irc bot implementations for a possible solution

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.