#!/usr/bin/env python3 'irc3.py - Python3 IRC Bot, by Sean B. Palmer' import sys, socket, asyncore, asynchat def ensure_data(data, disallowed, strict): for byte in disallowed: if strict and (byte in data): raise ValueError('Disallowed byte: ' + str(byte)) data = data.replace(byte, b'') return data def decode(data): for encoding in ('utf-8', 'iso-8859-1', 'cp1252'): try: return data.decode(encoding) except UnicodeDecodeError: continue class Client(asynchat.async_chat): def __init__(self, nick, strict=False): asynchat.async_chat.__init__(self) self.set_terminator(b'\n') self.collect_incoming_data = self._collect_incoming_data self.nick = nick self.strict = strict def run(self, host, port=6667): self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.connect((host, port)) asyncore.loop() def found_terminator(self): data = self._get_data() data = data.rstrip(b'\r') if data.startswith(b':'): sender, data = data.split(b' ', 1) else: sender = b':' parts = [decode(p) for p in data.split(b' :', 1)] parts = parts[0].split(' ') + parts[1:] self.dispatch(parts[0], decode(sender[1:]), *parts[1:]) def dispatch(self, command, sender, *args): print('RECEIVED:', command, sender, args) def serialise(self, arg, trailing): arg = bytes(arg, 'utf-8') arg = ensure_data(arg, {b'\x00', b'\r', b'\n'}, self.strict) if not trailing: arg = ensure_data(arg, {b' ', b':'}, self.strict) elif (b' ' in arg) or (b':' in arg): arg = b':' + arg return arg def write(self, *args): data = [self.serialise(arg, False) for arg in args[:-1]] data += [self.serialise(arg, True) for arg in args[-1:]] self.send(b' '.join(data) + b'\r\n') def handle_connect(self): self.write('NICK', self.nick) self.write('USER', self.nick, '+iw', self.nick, 'ircbot.py') def msg(self, recipient, message): self.write('PRIVMSG', recipient, message) def join(self, *channels): for channel in channels: self.write('JOIN', channel) if __name__ == '__main__': print(__doc__)