import re, threading import Utils RE_PREFIXES = re.compile(r"\bPREFIX=\((\w+)\)(\W+)(?:\b|$)") RE_CHANMODES = re.compile( r"\bCHANMODES=(\w*),(\w*),(\w*),(\w*)(?:\b|$)") RE_CHANTYPES = re.compile(r"\bCHANTYPES=(\W+)(?:\b|$)") class LineHandler(object): def __init__(self, bot): self.bot = bot bot.events.on("raw").on("PING").hook(self.ping) bot.events.on("raw").on("001").hook(self.handle_001, default_event=True) bot.events.on("raw").on("005").hook(self.handle_005) bot.events.on("raw").on("311").hook(self.handle_311, default_event=True) bot.events.on("raw").on("332").hook(self.handle_332, default_event=True) bot.events.on("raw").on("333").hook(self.handle_333, default_event=True) bot.events.on("raw").on("353").hook(self.handle_353, default_event=True) bot.events.on("raw").on("366").hook(self.handle_366, default_event=True) bot.events.on("raw").on("421").hook(self.handle_421, default_event=True) bot.events.on("raw").on("352").hook(self.handle_352, default_event=True) bot.events.on("raw").on("324").hook(self.handle_324, default_event=True) bot.events.on("raw").on("329").hook(self.handle_329, default_event=True) bot.events.on("raw").on("433").hook(self.handle_433, default_event=True) bot.events.on("raw").on("477").hook(self.handle_477, default_event=True) bot.events.on("raw").on("JOIN").hook(self.join) bot.events.on("raw").on("PART").hook(self.quit) bot.events.on("raw").on("QUIT").hook(self.quit) bot.events.on("raw").on("NICK").hook(self.nick) bot.events.on("raw").on("MODE").hook(self.mode) bot.events.on("raw").on("KICK").hook(self.kick) bot.events.on("raw").on("INVITE").hook(self.invite) bot.events.on("raw").on("PRIVMSG").hook(self.privmsg) bot.events.on("raw").on("NOTICE").hook(self.notice) bot.events.on("raw").on("CAP").hook(self.cap) bot.events.on("raw").on("authenticate").hook(self.authenticate) def handle(self, server, line): original_line = line prefix = None command = None if line[0] == ":": prefix, command, line = line[1:].split(" ", 2) else: command, line = line.split(" ", 1) arbitrary = None if ":" in line: line, arbitrary = line.split(":", 1) if line.endswith(" "): line = line[:-1] args = line.split(" ") hooks = self.bot.events.on("raw").on(command).get_hooks() default_event = False for hook in hooks: if hook.kwargs.get("default_event", False): default_event = True break #server, prefix, command, args, arbitrary self.bot.events.on("raw").on(command).call(server=server, prefix=prefix, args=args, arbitrary=arbitrary) if default_event or not hooks: if command.isdigit(): self.bot.events.on("received").on("numeric").on(command ).call(line=line, line_split=line.split(" "), number=command, server=server) else: self.bot.events.on("received").on(command).call( line=line, line_split=line.split(" "), command=command, server=server) def ping(self, event): nonce = event["arbitrary"] event["server"].send_pong(nonce) def handle_001(self, event): event["server"].name = Utils.remove_colon(event["prefix"]) event["server"].set_own_nickname(event["args"][0]) event["server"].send_whois(event["server"].nickname) def handle_005(self, event): isupport_line = " ".join(event["args"][1:]) if "NAMESX" in isupport_line: event["server"].send("PROTOCTL NAMESX") match = re.search(RE_PREFIXES, isupport_line) if match: event["server"].mode_prefixes.clear() modes = match.group(1) prefixes = match.group(2) for i, prefix in enumerate(prefixes): if i < len(modes): event["server"].mode_prefixes[prefix] = modes[i] match = re.search(RE_CHANMODES, isupport_line) if match: event["server"].channel_modes = list(match.group(4)) match = re.search(RE_CHANTYPES, isupport_line) if match: event["server"].channel_types = list(match.group(1)) self.bot.events.on("received").on("numeric").on("005").call( isupport=isupport_line, server=event["server"]) # whois respose (nickname, username, realname, hostname) def handle_311(self, event): nickname = event["args"][1] if event["server"].is_own_nickname(nickname): target = event["server"] else: target = event["server"].get_user(nickname) target.username = event["args"][2] target.hostname = event["args"][3] target.realname = event["arbitrary"] # on-join channel topic line def handle_332(self, event): event["server"].get_channel(event["args"][1]).set_topic( event["arbitrary"]) # on-join channel topic set by/at def handle_333(self, event): channel = event["server"].get_channel(event["args"][1]) topic_setter_hostmask = event["args"][2] nickname, username, hostname = Utils.seperate_hostmask( topic_setter_hostmask) topic_time = int(event["args"][3]) if event["args"][3].isdigit( ) else None channel.set_topic_setter(nickname, username, hostname) channel.set_topic_time(topic_time) # on-join user list with status symbols def handle_353(self, event): channel = event["server"].get_channel(event["args"][2]) nicknames = event["arbitrary"].split() for nickname in nicknames: if nickname.strip(): modes = set([]) while nickname[0] in event["server"].mode_prefixes: modes.add(event["server"].mode_prefixes[nickname[0]]) nickname = nickname[1:] user = event["server"].get_user(nickname) user.join_channel(channel) channel.add_user(user) for mode in modes: channel.add_mode(mode, nickname) # on-join user list has finished def handle_366(self, event): event["server"].send_who(event["args"][1]) # on user joining channel def join(self, event): nickname, username, hostname = Utils.seperate_hostmask( event["prefix"]) channel = event["server"].get_channel( event["arbitrary"] or event["args"][0]) if not event["server"].is_own_nickname(nickname): user = event["server"].get_user(nickname) if not event["server"].has_user(nickname): user.username = username user.hostname = hostname channel.add_user(user) user.join_channel(channel) self.bot.events.on("received").on("join").call(channel=channel, user=user, server=event["server"]) else: if channel.name in event["server"].attempted_join: del event["server"].attempted_join[channel.name] self.bot.events.on("self").on("join").call(channel=channel, server=event["server"]) channel.send_mode() # on user parting channel def part(self, event): nickname, username, hostname = Utils.seperate_hostmask( event["prefix"]) channel = event["server"].get_channel(event["args"][0]) reason = event["arbitrary"] or "" if not event["server"].is_own_nickname(nickname): user = event["server"].get_user(nickname) self.bot.events.on("received").on("part").call(channel=channel, reason=reason, user=user, server=event["server"]) channel.remove_user(user) user.part_channel(channel) if not len(user.channels): event["server"].remove_user(user) else: event["server"].remove_channel(channel) self.bot.events.on("self").on("part").call(channel=channel, reason=reason, server=event["server"]) # unknown command sent by us, oops! def handle_421(self, event): print("warning: unknown command '%s'." % event["args"][1]) # a user has disconnected! def quit(self, event): nickname, username, hostname = Utils.seperate_hostmask( event["prefix"]) reason = event["arbitrary"] or "" if not event["server"].is_own_nickname(nickname): user = event["server"].get_user(nickname) event["server"].remove_user(user) self.bot.events.on("received").on("quit").call(reason=reason, user=user, server=event["server"]) else: event["server"].disconnect() # the server is telling us about its capabilities! def cap(self, event): capabilities = (event["arbitrary"] or "").split(" ") self.bot.events.on("received").on("cap").call( subcommand=event["args"][1], capabilities=capabilities, server=event["server"]) # the server is asking for authentication def authenticate(self, event): self.bot.events.on("received").on("authenticate").call( message=event["args"][0], server=event["server"]) # someone has changed their nickname def nick(self, event): nickname, username, hostname = Utils.seperate_hostmask( event["prefix"]) new_nickname = event["arbitrary"] if not event["server"].is_own_nickname(nickname): user = event["server"].get_user(nickname) old_nickname = user.nickname user.set_nickname(new_nickname) event["server"].change_user_nickname(old_nickname, new_nickname) self.bot.events.on("received").on("nick").call( new_nickname=new_nickname, old_nickname=old_nickname, user=user, server=event["server"]) else: old_nickname = event["server"].nickname event["server"].set_own_nickname(new_nickname) self.bot.events.on("self").on("nick").call( server=event["server"], new_nickname=new_nickname, old_nickname=old_nickname) # something's mode has changed def mode(self, event): nickname, username, hostname = Utils.seperate_hostmask( event["prefix"]) target = event["args"][0] is_channel = target[0] in event["server"].channel_types if is_channel: channel = event["server"].get_channel(target) remove = False args = event["args"][2:] modes = event["args"][1] for i, char in enumerate(modes): if char == "+": remove = False elif char == "-": remove = True else: if char in event["server"].channel_modes: if remove: channel.remove_mode(char) else: channel.add_mode(char) elif char in event["server"].mode_prefixes.values( ) and len(args): nickname = args.pop(0) if remove: channel.remove_mode(char, nickname) else: channel.add_mode(char, nickname) elif len(args): args.pop(0) self.bot.events.on("received").on("mode").call(modes=modes, mode_args=args, channel=channel, server=event["server"]) elif event["server"].is_own_nickname(target): modes = event["arbitrary"] remove = False for i, char in enumerate(modes): if char == "+": remove = False elif char == "-": remove = True else: if remove: event["server"].remove_own_mode(char) else: event["server"].add_own_mode(char) self.bot.events.on("self").on("mode").call(modes=modes, server=event["server"]) # I've been invited somewhere def invite(self, event): nickname, username, hostname = Utils.seperate_hostmask( event["prefix"]) target_channel = event["arbitrary"] user = event["server"].get_user(nickname) self.bot.events.on("received").on("invite").call( user=user, target_channel=target_channel, server=event["server"]) # we've received a message def privmsg(self, event): nickname, username, hostname = Utils.seperate_hostmask( event["prefix"]) user = event["server"].get_user(nickname) message = event["arbitrary"] or "" message_split = message.split(" ") target = event["args"][0] action = message.startswith("\01ACTION ") and message.endswith("\01") if action: message = message.replace("\01ACTION ", "", 1)[:-1] if target[0] in event["server"].channel_types: channel = event["server"].get_channel(event["args"][0]) self.bot.events.on("received").on("message").on("channel").call( user=user, message=message, message_split=message_split, channel=channel, action=action, server=event["server"]) channel.buffer.add_line(user.nickname, message, action) elif event["server"].is_own_nickname(target): self.bot.events.on("received").on("message").on("private").call( user=user, message=message, message_split=message_split, action=action, server=event["server"]) user.buffer.add_line(user.nickname, message, action) # we've received a notice def notice(self, event): nickname, username, hostname = Utils.seperate_hostmask( event["prefix"]) message = event["arbitrary"] or "" message_split = message.split(" ") target = event["args"][0] if nickname == event["server"].name or target == "*": self.bot.events.on("received.server-notice").call( message=message, message_split=message_split, server=event["server"]) else: user = event["server"].get_user(nickname) if target[0] in event["server"].channel_types: channel = event["server"].get_channel(target) self.bot.events.on("received.notice.channel").call( message=message, message_split=message_split, user=user, server=event["server"], channel=channel) elif event["server"].is_own_nickname(target): self.bot.events.on("received.notice.private").call( message=message, message_split=message_split, user=user, server=event["server"]) # response to a WHO command for user information def handle_352(self, event): user = event["server"].get_user(event["args"][5]) user.username = event["args"][2] user.hostname = event["args"][3] # response to an empty mode command def handle_324(self, event): channel = event["server"].get_channel(event["args"][1]) modes = event["args"][2] if modes[0] == "+" and modes[1:]: for mode in modes[1:]: if mode in event["server"].channel_modes: channel.add_mode(mode) # channel creation unix timestamp def handle_329(self, event): channel = event["server"].get_channel(event["args"][1]) channel.creation_timestamp = int(event["args"][2]) # nickname already in use def handle_433(self, event): pass # we need a registered nickname for this channel def handle_477(self, event): if event["args"][1].lower() in event["server"].attempted_join: self.bot.add_timer("rejoin", 5, channel_name=event["args"][1], key=event["server"].attempted_join[event["args"][1].lower()], server_id=event["server"].id) # someone's been kicked from a channel def kick(self, event): nickname, username, hostname = Utils.seperate_hostmask( event["prefix"]) user = event["server"].get_user(nickname) target = event["args"][1] channel = event["server"].get_channel(event["args"][0]) reason = event["arbitrary"] or "" if not event["server"].is_own_nickname(target): target_user = event["server"].get_user(target) self.bot.events.on("received").on("kick").call(channel=channel, reason=reason, target_user=target_user, user=user, server=event["server"]) else: self.bot.events.on("self").on("kick").call(channel=channel, reason=reason, user=user, server=event["server"])