From c87728a87d9962870d45cd40edffd35b35dfe1c1 Mon Sep 17 00:00:00 2001 From: jesopo Date: Sun, 30 Sep 2018 19:43:20 +0100 Subject: Move src/IRCLineHandler.py to modules/line_handler.py --- modules/line_handler.py | 597 ++++++++++++++++++++++++++++++++++++++++++++++++ src/IRCBot.py | 6 +- src/IRCLineHandler.py | 592 ----------------------------------------------- src/IRCServer.py | 2 +- start.py | 5 +- 5 files changed, 602 insertions(+), 600 deletions(-) create mode 100644 modules/line_handler.py delete mode 100644 src/IRCLineHandler.py diff --git a/modules/line_handler.py b/modules/line_handler.py new file mode 100644 index 00000000..41fa73cf --- /dev/null +++ b/modules/line_handler.py @@ -0,0 +1,597 @@ +import re, threading +from src 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|$)") +RE_CASEMAPPING = re.compile(r"\bCASEMAPPING=(\S+)") +RE_MODES = re.compile(r"[-+]\w+") + +CAPABILITIES = {"multi-prefix", "chghost", "invite-notify", "account-tag", + "account-notify", "extended-join", "away-notify", "userhost-in-names", + "draft/message-tags-0.2", "server-time", "cap-notify", + "batch", "draft/labeled-response"} + +class Module(object): + def __init__(self, bot, events, exports): + self.bot = bot + self.events = events + + events.on("raw").hook(self.handle) + + events.on("raw.001").hook(self.handle_001, default_event=True) + events.on("raw.005").hook(self.handle_005) + events.on("raw.311").hook(self.handle_311, default_event=True) + events.on("raw.332").hook(self.handle_332) + events.on("raw.333").hook(self.handle_333) + events.on("raw.353").hook(self.handle_353, default_event=True) + events.on("raw.366").hook(self.handle_366, default_event=True) + events.on("raw.421").hook(self.handle_421, default_event=True) + events.on("raw.352").hook(self.handle_352, default_event=True) + events.on("raw.354").hook(self.handle_354, default_event=True) + events.on("raw.324").hook(self.handle_324, default_event=True) + events.on("raw.329").hook(self.handle_329, default_event=True) + events.on("raw.433").hook(self.handle_433, default_event=True) + events.on("raw.477").hook(self.handle_477, default_event=True) + + events.on("raw.PING").hook(self.ping) + events.on("raw.JOIN").hook(self.join) + events.on("raw.PART").hook(self.part) + events.on("raw.QUIT").hook(self.quit) + events.on("raw.NICK").hook(self.nick) + events.on("raw.MODE").hook(self.mode) + events.on("raw.KICK").hook(self.kick) + events.on("raw.INVITE").hook(self.invite) + events.on("raw.TOPIC").hook(self.topic) + events.on("raw.PRIVMSG").hook(self.privmsg) + events.on("raw.NOTICE").hook(self.notice) + + events.on("raw.CAP").hook(self.cap) + events.on("raw.AUTHENTICATE").hook(self.authenticate) + events.on("raw.CHGHOST").hook(self.chghost) + events.on("raw.ACCOUNT").hook(self.account) + events.on("raw.TAGMSG").hook(self.tagmsg) + events.on("raw.AWAY").hook(self.away) + events.on("raw.BATCH").hook(self.batch) + + def handle(self, event): + line = original_line = event["line"] + tags = {} + prefix = None + command = None + + if line[0] == "@": + tags_prefix, line = line[1:].split(" ", 1) + for tag in tags_prefix.split(";"): + if tag: + tag_split = tag.split("=", 1) + tags[tag_split[0]] = "".join(tag_split[1:]) + if "batch" in tags and tags["batch"] in event["server"].batches: + server.batches[tag["batch"]].append(line) + return + + arbitrary = None + if " :" in line: + line, arbitrary = line.split(" :", 1) + if line.endswith(" "): + line = line[:-1] + if line[0] == ":": + prefix, command = line[1:].split(" ", 1) + prefix = Utils.seperate_hostmask(prefix) + if " " in command: + command, line = command.split(" ", 1) + else: + line = "" + else: + command = line + if " " in line: + command, line = line.split(" ", 1) + args = line.split(" ") + + hooks = self.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 + last = arbitrary or args[-1] + + #server, prefix, command, args, arbitrary + self.events.on("raw").on(command).call(server=event["server"], + last=last, prefix=prefix, args=args, arbitrary=arbitrary, + tags=tags) + if default_event or not hooks: + if command.isdigit(): + self.events.on("received.numeric").on(command).call( + line=original_line, server=event["server"], tags=tags, + last=last, line_split=original_line.split(" "), + number=command) + else: + self.events.on("received").on(command).call( + line=original_line, line_split=original_line.split(" "), + command=command, server=event["server"], tags=tags, + last=last) + + # ping from the server + def ping(self, event): + event["server"].send_pong(event["last"]) + + # first numeric line the server sends + def handle_001(self, event): + event["server"].name = event["prefix"].nickname + event["server"].set_own_nickname(event["args"][0]) + event["server"].send_whois(event["server"].nickname) + + # server telling us what it supports + 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)) + + match = re.search(RE_CASEMAPPING, isupport_line) + if match: + event["server"].case_mapping = match.group(1) + + self.events.on("received.numeric.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): + channel = event["server"].get_channel(event["args"][1]) + + channel.set_topic(event["arbitrary"]) + self.events.on("received.numeric.332").call(channel=channel, + server=event["server"], topic=event["arbitrary"]) + + # channel topic changed + def topic(self, event): + user = event["server"].get_user(event["prefix"].nickname) + channel = event["server"].get_channel(event["args"][0]) + channel.set_topic(event["arbitrary"]) + self.events.on("received.topic").call(channel=channel, + server=event["server"], topic=event["arbitrary"], user=user) + + # 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] + topic_setter = Utils.seperate_hostmask(topic_setter_hostmask) + topic_time = int(event["args"][3]) if event["args"][3].isdigit( + ) else None + + channel.set_topic_setter(topic_setter.nickname, topic_setter.username, + topic_setter.hostname) + channel.set_topic_time(topic_time) + self.events.on("received.numeric.333").call(channel=channel, + setter=topic_setter.nickname, set_at=topic_time, + server=event["server"]) + + # /names response, also on-join user list + def handle_353(self, event): + channel = event["server"].get_channel(event["args"][2]) + nicknames = event["arbitrary"].split() + for nickname in nicknames: + modes = set([]) + + while nickname[0] in event["server"].mode_prefixes: + modes.add(event["server"].mode_prefixes[nickname[0]]) + nickname = nickname[1:] + + if "userhost-in-names" in event["server"].capabilities: + hostmask = Utils.seperate_hostmask(nickname) + nickname = hostmask.nickname + user = event["server"].get_user(hostmask.nickname) + user.username = hostmask.username + user.hostname = hostmask.hostname + else: + 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_whox(event["args"][1], "n", "ahnrtu", "111") + + # on user joining channel + def join(self, event): + account = None + realname = None + if len(event["args"]) == 2: + channel = event["server"].get_channel(event["args"][0]) + if not event["args"] == "*": + account = event["args"][1] + realname = event["arbitrary"] + else: + channel = event["server"].get_channel(event["last"]) + + if not event["server"].is_own_nickname(event["prefix"].nickname): + user = event["server"].get_user(event["prefix"].nickname) + if not user.username and not user.hostname: + user.username = event["prefix"].username + user.hostname = event["prefix"].hostname + + if account: + user.identified_account = account + user.identified_account_id = event["server"].get_user( + account).get_id() + if realname: + user.realname = realname + + channel.add_user(user) + user.join_channel(channel) + self.events.on("received.join").call(channel=channel, + user=user, server=event["server"], account=account, + realname=realname) + else: + if channel.name in event["server"].attempted_join: + del event["server"].attempted_join[channel.name] + self.events.on("self.join").call(channel=channel, + server=event["server"], account=account, realname=realname) + channel.send_mode() + + # on user parting channel + def part(self, event): + channel = event["server"].get_channel(event["args"][0]) + reason = event["arbitrary"] or "" + + if not event["server"].is_own_nickname(event["prefix"].nickname): + user = event["server"].get_user(event["prefix"].nickname) + self.events.on("received.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: + self.events.on("self.part").call(channel=channel, + reason=reason, server=event["server"]) + event["server"].remove_channel(channel) + + # 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): + reason = event["arbitrary"] or "" + + if not event["server"].is_own_nickname(event["prefix"].nickname): + user = event["server"].get_user(event["prefix"].nickname) + event["server"].remove_user(user) + self.events.on("received.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_list = (event["arbitrary"] or "").split(" ") + capabilities = {} + for capability in capabilities_list: + argument = None + if "=" in capability: + capability, argument = capability.split("=", 1) + capabilities[capability] = argument + + subcommand = event["args"][1].lower() + is_multiline = len(event["args"]) > 2 and event["args"][2] == "*" + + if subcommand == "ls": + event["server"].server_capabilities.update(capabilities) + if not is_multiline: + matched_capabilities = set(event["server" + ].server_capabilities.keys()) & CAPABILITIES + if matched_capabilities: + event["server"].queue_capabilities(matched_capabilities) + + self.events.on("received.cap.ls").call( + capabilities=event["server"].server_capabilities, + server=event["server"]) + + if event["server"].has_capability_queue(): + event["server"].send_capability_queue() + else: + event["server"].send_capability_end() + elif subcommand == "new": + event["server"].capabilities.update(set(capabilities.keys())) + self.events.on("received.cap.new").call(server=event["server"], + capabilities=capabilities) + elif subcommand == "del": + event["server"].capabilities.difference_update(set( + capabilities.keys())) + self.events.on("received.cap.del").call(server=event["server"], + capabilities=capabilities) + elif subcommand == "ack": + event["server"].capabilities.update(capabilities) + if not is_multiline: + self.events.on("received.cap.ack").call( + capabilities=event["server"].capabilities, + server=event["server"]) + + if not event["server"].waiting_for_capabilities(): + event["server"].send_capability_end() + elif subcommand == "nack": + event["server"].send_capability_end() + + # the server is asking for authentication + def authenticate(self, event): + self.events.on("received.authenticate").call( + message=event["args"][0], server=event["server"]) + + # someone has changed their nickname + def nick(self, event): + new_nickname = event["arbitrary"] + if not event["server"].is_own_nickname(event["prefix"].nickname): + user = event["server"].get_user(event["prefix"].nickname) + old_nickname = user.nickname + user.set_nickname(new_nickname) + event["server"].change_user_nickname(old_nickname, new_nickname) + + self.events.on("received.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.events.on("self.nick").call(server=event["server"], + new_nickname=new_nickname, old_nickname=old_nickname) + + # something's mode has changed + def mode(self, event): + user = event["server"].get_user(event["prefix"].nickname) + 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:] + _args = args[:] + modes = RE_MODES.findall(event["args"][1]) + for chunk in modes: + remove = chunk[0] == "-" + for mode in chunk[1:]: + if mode in event["server"].channel_modes: + channel.change_mode(remove, mode) + elif mode in event["server"].mode_prefixes.values( + ) and len(args): + channel.change_mode(remove, mode, args.pop(0)) + else: + args.pop(0) + self.events.on("received.mode.channel").call(modes=modes, + mode_args=_args, channel=channel, server=event["server"], + user=user) + elif event["server"].is_own_nickname(target): + modes = RE_MODES.findall(event["last"]) + for chunk in modes: + remove = chunk[0] == "-" + for mode in chunk[1:]: + event["server"].change_own_mode(remove, mode) + self.events.on("self.mode").call(modes=modes, + server=event["server"]) + + # someone (maybe me!) has been invited somewhere + def invite(self, event): + target_channel = event["last"] + user = event["server"].get_user(event["prefix"].nickname) + target_user = event["server"].get_user(event["args"][0]) + self.events.on("received.invite").call(user=user, + target_channel=target_channel, server=event["server"], + target_user=target_user) + + # we've received a message + def privmsg(self, event): + user = event["server"].get_user(event["prefix"].nickname) + message = event["arbitrary"] or "" + message_split = message.split(" ") + target = event["args"][0] + action = message.startswith("\x01ACTION ") + if action: + message = message.replace("\x01ACTION ", "", 1) + if message.endswith("\x01"): + message = message[:-1] + + if "account" in event["tags"]: + user.identified_account = event["tags"]["account"] + user.identified_account_id = event["server"].get_user( + event["tags"]["account"]).get_id() + + kwargs = {"message": message, "message_split": message_split, + "server": event["server"], "tags": event["tags"], + "action": action} + + if target[0] in event["server"].channel_types: + channel = event["server"].get_channel(event["args"][0]) + self.events.on("received.message.channel").call( + user=user, channel=channel, **kwargs) + channel.buffer.add_line(user.nickname, message, action) + elif event["server"].is_own_nickname(target): + self.events.on("received.message.private").call( + user=user, **kwargs) + user.buffer.add_line(user.nickname, message, action) + + # we've received a notice + def notice(self, event): + message = event["arbitrary"] or "" + message_split = message.split(" ") + target = event["args"][0] + + if not event["prefix"] or event["prefix"].hostmask == event["server" + ].name or target == "*" or (not event["prefix"].hostname and + not event["server"].name): + event["server"].name = event["prefix"].hostmask + + self.events.on("received.server-notice").call( + message=message, message_split=message_split, + server=event["server"]) + else: + user = event["server"].get_user(event["prefix"].nickname) + + if target[0] in event["server"].channel_types: + channel = event["server"].get_channel(target) + self.events.on("received.notice.channel").call( + message=message, message_split=message_split, user=user, + server=event["server"], channel=channel, + tags=event["tags"]) + elif event["server"].is_own_nickname(target): + self.events.on("received.notice.private").call( + message=message, message_split=message_split, user=user, + server=event["server"], tags=event["tags"]) + + # IRCv3 TAGMSG, used to send tags without any other information + def tagmsg(self, event): + user = event["server"].get_user(event["prefix"].nickname) + target = event["args"][0] + + if target[0] in event["server"].channel_types: + channel = event["server"].get_channel(target) + self.events.on("received.tagmsg.channel").call(channel=channel, + user=user, tags=event["tags"], server=event["server"]) + elif event["server"].is_own_nickname(target): + self.events.on("received.tagmsg.private").call( + user=user, tags=event["tags"], server=event["server"]) + + # IRCv3 AWAY, used to notify us that a client we can see has changed /away + def away(self, event): + user = event["server"].get_user(event["prefix"].nickname) + message = event["arbitrary"] + if message: + user.away = True + self.events.on("received.away.on").call(user=user, + server=event["server"], message=message) + else: + user.away = False + self.events.on("received.away.off").call(user=user, + server=event["server"]) + + def batch(self, event): + identifier = event["args"][0] + modifier, identifier = identifier[0], identifier[1:] + if modifier == "+": + event["server"].batches[identifier] = [] + else: + lines = event["server"].batches[identifier] + del event["server"].batches[identifier] + for line in lines: + self.handle(event["server"], line) + + # IRCv3 CHGHOST, a user's username and/or hostname has changed + def chghost(self, event): + username = event["args"][0] + hostname = event["args"][1] + + if not event["server"].is_own_nickname(event["prefix"].nickname): + target = event["server"].get_user("nickanme") + else: + target = event["server"] + target.username = username + target.hostname = hostname + + def account(self, event): + user = event["server"].get_user(event["prefix"].nickname) + + if not event["args"][0] == "*": + user.identified_account = event["args"][0] + user.identified_account_id = event["server"].get_user( + event["args"][0]).get_id() + self.events.on("received.account.login").call(user=user, + server=event["server"], account=event["args"][0]) + else: + user.identified_account = None + user.identified_account_id = None + self.events.on("received.account.logout").call(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 a WHOX command for user information, including account name + def handle_354(self, event): + if event["args"][1] == "111": + username = event["args"][2] + hostname = event["args"][3] + nickname = event["args"][4] + account = event["args"][5] + realname = event["last"] + + user = event["server"].get_user(nickname) + user.username = username + user.hostname = hostname + user.realname = realname + if not account == "0": + user.identified_account = account + + # 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): + channel_name = Utils.irc_lower(event["server"], event["args"][1]) + if channel_name in event["server"]: + key = event["server"].attempted_join[channel_name] + self.timers.add("rejoin", 5, channel_name=channe_name, key=key, + server_id=event["server"].id) + + # someone's been kicked from a channel + def kick(self, event): + user = event["server"].get_user(event["prefix"].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.events.on("received.kick").call(channel=channel, + reason=reason, target_user=target_user, user=user, + server=event["server"]) + else: + self.events.on("self.kick").call(channel=channel, + reason=reason, user=user, server=event["server"]) diff --git a/src/IRCBot.py b/src/IRCBot.py index 01121978..a776d8d6 100644 --- a/src/IRCBot.py +++ b/src/IRCBot.py @@ -1,10 +1,9 @@ import os, select, sys, threading, time, traceback, uuid -from . import EventManager, Exports, IRCLineHandler, IRCServer, Logging -from . import ModuleManager +from . import EventManager, Exports, IRCServer, Logging, ModuleManager class Bot(object): def __init__(self, directory, args, cache, config, database, events, - exports, line_handler, log, modules, timers): + exports, log, modules, timers): self.directory = directory self.args = args self.cache = cache @@ -12,7 +11,6 @@ class Bot(object): self.database = database self._events = events self._exports = exports - self.line_handler = line_handler self.log = log self.modules = modules self.timers = timers diff --git a/src/IRCLineHandler.py b/src/IRCLineHandler.py deleted file mode 100644 index 805daa20..00000000 --- a/src/IRCLineHandler.py +++ /dev/null @@ -1,592 +0,0 @@ -import re, threading -from . 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|$)") -RE_CASEMAPPING = re.compile(r"\bCASEMAPPING=(\S+)") -RE_MODES = re.compile(r"[-+]\w+") - -CAPABILITIES = {"multi-prefix", "chghost", "invite-notify", "account-tag", - "account-notify", "extended-join", "away-notify", "userhost-in-names", - "draft/message-tags-0.2", "server-time", "cap-notify", - "batch", "draft/labeled-response"} - -class LineHandler(object): - def __init__(self, events, timers): - self.events = events - self.timers = timers - events.on("raw.PING").hook(self.ping) - - events.on("raw.001").hook(self.handle_001, default_event=True) - events.on("raw.005").hook(self.handle_005) - events.on("raw.311").hook(self.handle_311, default_event=True) - events.on("raw.332").hook(self.handle_332) - events.on("raw.333").hook(self.handle_333) - events.on("raw.353").hook(self.handle_353, default_event=True) - events.on("raw.366").hook(self.handle_366, default_event=True) - events.on("raw.421").hook(self.handle_421, default_event=True) - events.on("raw.352").hook(self.handle_352, default_event=True) - events.on("raw.354").hook(self.handle_354, default_event=True) - events.on("raw.324").hook(self.handle_324, default_event=True) - events.on("raw.329").hook(self.handle_329, default_event=True) - events.on("raw.433").hook(self.handle_433, default_event=True) - events.on("raw.477").hook(self.handle_477, default_event=True) - - events.on("raw.JOIN").hook(self.join) - events.on("raw.PART").hook(self.part) - events.on("raw.QUIT").hook(self.quit) - events.on("raw.NICK").hook(self.nick) - events.on("raw.MODE").hook(self.mode) - events.on("raw.KICK").hook(self.kick) - events.on("raw.INVITE").hook(self.invite) - events.on("raw.TOPIC").hook(self.topic) - events.on("raw.PRIVMSG").hook(self.privmsg) - events.on("raw.NOTICE").hook(self.notice) - - events.on("raw.CAP").hook(self.cap) - events.on("raw.AUTHENTICATE").hook(self.authenticate) - events.on("raw.CHGHOST").hook(self.chghost) - events.on("raw.ACCOUNT").hook(self.account) - events.on("raw.TAGMSG").hook(self.tagmsg) - events.on("raw.AWAY").hook(self.away) - events.on("raw.BATCH").hook(self.batch) - - def handle(self, server, line): - original_line = line - tags = {} - prefix = None - command = None - - if line[0] == "@": - tags_prefix, line = line[1:].split(" ", 1) - for tag in tags_prefix.split(";"): - if tag: - tag_split = tag.split("=", 1) - tags[tag_split[0]] = "".join(tag_split[1:]) - if "batch" in tags and tags["batch"] in server.batches: - server.batches[tag["batch"]].append(line) - return - - arbitrary = None - if " :" in line: - line, arbitrary = line.split(" :", 1) - if line.endswith(" "): - line = line[:-1] - if line[0] == ":": - prefix, command = line[1:].split(" ", 1) - prefix = Utils.seperate_hostmask(prefix) - if " " in command: - command, line = command.split(" ", 1) - else: - line = "" - else: - command = line - if " " in line: - command, line = line.split(" ", 1) - args = line.split(" ") - - hooks = self.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 - last = arbitrary or args[-1] - - #server, prefix, command, args, arbitrary - self.events.on("raw").on(command).call(server=server, last=last, - prefix=prefix, args=args, arbitrary=arbitrary, tags=tags) - if default_event or not hooks: - if command.isdigit(): - self.events.on("received.numeric").on(command).call( - line=original_line, server=server, tags=tags, last=last, - line_split=original_line.split(" "), number=command) - else: - self.events.on("received").on(command).call( - line=original_line, line_split=original_line.split(" "), - command=command, server=server, tags=tags, last=last) - - # ping from the server - def ping(self, event): - event["server"].send_pong(event["last"]) - - # first numeric line the server sends - def handle_001(self, event): - event["server"].name = event["prefix"].nickname - event["server"].set_own_nickname(event["args"][0]) - event["server"].send_whois(event["server"].nickname) - - # server telling us what it supports - 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)) - - match = re.search(RE_CASEMAPPING, isupport_line) - if match: - event["server"].case_mapping = match.group(1) - - self.events.on("received.numeric.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): - channel = event["server"].get_channel(event["args"][1]) - - channel.set_topic(event["arbitrary"]) - self.events.on("received.numeric.332").call(channel=channel, - server=event["server"], topic=event["arbitrary"]) - - # channel topic changed - def topic(self, event): - user = event["server"].get_user(event["prefix"].nickname) - channel = event["server"].get_channel(event["args"][0]) - channel.set_topic(event["arbitrary"]) - self.events.on("received.topic").call(channel=channel, - server=event["server"], topic=event["arbitrary"], user=user) - - # 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] - topic_setter = Utils.seperate_hostmask(topic_setter_hostmask) - topic_time = int(event["args"][3]) if event["args"][3].isdigit( - ) else None - - channel.set_topic_setter(topic_setter.nickname, topic_setter.username, - topic_setter.hostname) - channel.set_topic_time(topic_time) - self.events.on("received.numeric.333").call(channel=channel, - setter=topic_setter.nickname, set_at=topic_time, - server=event["server"]) - - # /names response, also on-join user list - def handle_353(self, event): - channel = event["server"].get_channel(event["args"][2]) - nicknames = event["arbitrary"].split() - for nickname in nicknames: - modes = set([]) - - while nickname[0] in event["server"].mode_prefixes: - modes.add(event["server"].mode_prefixes[nickname[0]]) - nickname = nickname[1:] - - if "userhost-in-names" in event["server"].capabilities: - hostmask = Utils.seperate_hostmask(nickname) - nickname = hostmask.nickname - user = event["server"].get_user(hostmask.nickname) - user.username = hostmask.username - user.hostname = hostmask.hostname - else: - 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_whox(event["args"][1], "n", "ahnrtu", "111") - - # on user joining channel - def join(self, event): - account = None - realname = None - if len(event["args"]) == 2: - channel = event["server"].get_channel(event["args"][0]) - if not event["args"] == "*": - account = event["args"][1] - realname = event["arbitrary"] - else: - channel = event["server"].get_channel(event["last"]) - - if not event["server"].is_own_nickname(event["prefix"].nickname): - user = event["server"].get_user(event["prefix"].nickname) - if not user.username and not user.hostname: - user.username = event["prefix"].username - user.hostname = event["prefix"].hostname - - if account: - user.identified_account = account - user.identified_account_id = event["server"].get_user( - account).get_id() - if realname: - user.realname = realname - - channel.add_user(user) - user.join_channel(channel) - self.events.on("received.join").call(channel=channel, - user=user, server=event["server"], account=account, - realname=realname) - else: - if channel.name in event["server"].attempted_join: - del event["server"].attempted_join[channel.name] - self.events.on("self.join").call(channel=channel, - server=event["server"], account=account, realname=realname) - channel.send_mode() - - # on user parting channel - def part(self, event): - channel = event["server"].get_channel(event["args"][0]) - reason = event["arbitrary"] or "" - - if not event["server"].is_own_nickname(event["prefix"].nickname): - user = event["server"].get_user(event["prefix"].nickname) - self.events.on("received.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: - self.events.on("self.part").call(channel=channel, - reason=reason, server=event["server"]) - event["server"].remove_channel(channel) - - # 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): - reason = event["arbitrary"] or "" - - if not event["server"].is_own_nickname(event["prefix"].nickname): - user = event["server"].get_user(event["prefix"].nickname) - event["server"].remove_user(user) - self.events.on("received.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_list = (event["arbitrary"] or "").split(" ") - capabilities = {} - for capability in capabilities_list: - argument = None - if "=" in capability: - capability, argument = capability.split("=", 1) - capabilities[capability] = argument - - subcommand = event["args"][1].lower() - is_multiline = len(event["args"]) > 2 and event["args"][2] == "*" - - if subcommand == "ls": - event["server"].server_capabilities.update(capabilities) - if not is_multiline: - matched_capabilities = set(event["server" - ].server_capabilities.keys()) & CAPABILITIES - if matched_capabilities: - event["server"].queue_capabilities(matched_capabilities) - - self.events.on("received.cap.ls").call( - capabilities=event["server"].server_capabilities, - server=event["server"]) - - if event["server"].has_capability_queue(): - event["server"].send_capability_queue() - else: - event["server"].send_capability_end() - elif subcommand == "new": - event["server"].capabilities.update(set(capabilities.keys())) - self.events.on("received.cap.new").call(server=event["server"], - capabilities=capabilities) - elif subcommand == "del": - event["server"].capabilities.difference_update(set( - capabilities.keys())) - self.events.on("received.cap.del").call(server=event["server"], - capabilities=capabilities) - elif subcommand == "ack": - event["server"].capabilities.update(capabilities) - if not is_multiline: - self.events.on("received.cap.ack").call( - capabilities=event["server"].capabilities, - server=event["server"]) - - if not event["server"].waiting_for_capabilities(): - event["server"].send_capability_end() - elif subcommand == "nack": - event["server"].send_capability_end() - - # the server is asking for authentication - def authenticate(self, event): - self.events.on("received.authenticate").call( - message=event["args"][0], server=event["server"]) - - # someone has changed their nickname - def nick(self, event): - new_nickname = event["arbitrary"] - if not event["server"].is_own_nickname(event["prefix"].nickname): - user = event["server"].get_user(event["prefix"].nickname) - old_nickname = user.nickname - user.set_nickname(new_nickname) - event["server"].change_user_nickname(old_nickname, new_nickname) - - self.events.on("received.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.events.on("self.nick").call(server=event["server"], - new_nickname=new_nickname, old_nickname=old_nickname) - - # something's mode has changed - def mode(self, event): - user = event["server"].get_user(event["prefix"].nickname) - 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:] - _args = args[:] - modes = RE_MODES.findall(event["args"][1]) - for chunk in modes: - remove = chunk[0] == "-" - for mode in chunk[1:]: - if mode in event["server"].channel_modes: - channel.change_mode(remove, mode) - elif mode in event["server"].mode_prefixes.values( - ) and len(args): - channel.change_mode(remove, mode, args.pop(0)) - else: - args.pop(0) - self.events.on("received.mode.channel").call(modes=modes, - mode_args=_args, channel=channel, server=event["server"], - user=user) - elif event["server"].is_own_nickname(target): - modes = RE_MODES.findall(event["last"]) - for chunk in modes: - remove = chunk[0] == "-" - for mode in chunk[1:]: - event["server"].change_own_mode(remove, mode) - self.events.on("self.mode").call(modes=modes, - server=event["server"]) - - # someone (maybe me!) has been invited somewhere - def invite(self, event): - target_channel = event["last"] - user = event["server"].get_user(event["prefix"].nickname) - target_user = event["server"].get_user(event["args"][0]) - self.events.on("received.invite").call(user=user, - target_channel=target_channel, server=event["server"], - target_user=target_user) - - # we've received a message - def privmsg(self, event): - user = event["server"].get_user(event["prefix"].nickname) - message = event["arbitrary"] or "" - message_split = message.split(" ") - target = event["args"][0] - action = message.startswith("\x01ACTION ") - if action: - message = message.replace("\x01ACTION ", "", 1) - if message.endswith("\x01"): - message = message[:-1] - - if "account" in event["tags"]: - user.identified_account = event["tags"]["account"] - user.identified_account_id = event["server"].get_user( - event["tags"]["account"]).get_id() - - kwargs = {"message": message, "message_split": message_split, - "server": event["server"], "tags": event["tags"], - "action": action} - - if target[0] in event["server"].channel_types: - channel = event["server"].get_channel(event["args"][0]) - self.events.on("received.message.channel").call( - user=user, channel=channel, **kwargs) - channel.buffer.add_line(user.nickname, message, action) - elif event["server"].is_own_nickname(target): - self.events.on("received.message.private").call( - user=user, **kwargs) - user.buffer.add_line(user.nickname, message, action) - - # we've received a notice - def notice(self, event): - message = event["arbitrary"] or "" - message_split = message.split(" ") - target = event["args"][0] - - if not event["prefix"] or event["prefix"].hostmask == event["server" - ].name or target == "*" or (not event["prefix"].hostname and - not event["server"].name): - event["server"].name = event["prefix"].hostmask - - self.events.on("received.server-notice").call( - message=message, message_split=message_split, - server=event["server"]) - else: - user = event["server"].get_user(event["prefix"].nickname) - - if target[0] in event["server"].channel_types: - channel = event["server"].get_channel(target) - self.events.on("received.notice.channel").call( - message=message, message_split=message_split, user=user, - server=event["server"], channel=channel, - tags=event["tags"]) - elif event["server"].is_own_nickname(target): - self.events.on("received.notice.private").call( - message=message, message_split=message_split, user=user, - server=event["server"], tags=event["tags"]) - - # IRCv3 TAGMSG, used to send tags without any other information - def tagmsg(self, event): - user = event["server"].get_user(event["prefix"].nickname) - target = event["args"][0] - - if target[0] in event["server"].channel_types: - channel = event["server"].get_channel(target) - self.events.on("received.tagmsg.channel").call(channel=channel, - user=user, tags=event["tags"], server=event["server"]) - elif event["server"].is_own_nickname(target): - self.events.on("received.tagmsg.private").call( - user=user, tags=event["tags"], server=event["server"]) - - # IRCv3 AWAY, used to notify us that a client we can see has changed /away - def away(self, event): - user = event["server"].get_user(event["prefix"].nickname) - message = event["arbitrary"] - if message: - user.away = True - self.events.on("received.away.on").call(user=user, - server=event["server"], message=message) - else: - user.away = False - self.events.on("received.away.off").call(user=user, - server=event["server"]) - - def batch(self, event): - identifier = event["args"][0] - modifier, identifier = identifier[0], identifier[1:] - if modifier == "+": - event["server"].batches[identifier] = [] - else: - lines = event["server"].batches[identifier] - del event["server"].batches[identifier] - for line in lines: - self.handle(event["server"], line) - - # IRCv3 CHGHOST, a user's username and/or hostname has changed - def chghost(self, event): - username = event["args"][0] - hostname = event["args"][1] - - if not event["server"].is_own_nickname(event["prefix"].nickname): - target = event["server"].get_user("nickanme") - else: - target = event["server"] - target.username = username - target.hostname = hostname - - def account(self, event): - user = event["server"].get_user(event["prefix"].nickname) - - if not event["args"][0] == "*": - user.identified_account = event["args"][0] - user.identified_account_id = event["server"].get_user( - event["args"][0]).get_id() - self.events.on("received.account.login").call(user=user, - server=event["server"], account=event["args"][0]) - else: - user.identified_account = None - user.identified_account_id = None - self.events.on("received.account.logout").call(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 a WHOX command for user information, including account name - def handle_354(self, event): - if event["args"][1] == "111": - username = event["args"][2] - hostname = event["args"][3] - nickname = event["args"][4] - account = event["args"][5] - realname = event["last"] - - user = event["server"].get_user(nickname) - user.username = username - user.hostname = hostname - user.realname = realname - if not account == "0": - user.identified_account = account - - # 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): - channel_name = Utils.irc_lower(event["server"], event["args"][1]) - if channel_name in event["server"]: - key = event["server"].attempted_join[channel_name] - self.timers.add("rejoin", 5, channel_name=channe_name, key=key, - server_id=event["server"].id) - - # someone's been kicked from a channel - def kick(self, event): - user = event["server"].get_user(event["prefix"].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.events.on("received.kick").call(channel=channel, - reason=reason, target_user=target_user, user=user, - server=event["server"]) - else: - self.events.on("self.kick").call(channel=channel, - reason=reason, user=user, server=event["server"]) diff --git a/src/IRCServer.py b/src/IRCServer.py index d88cc38a..4e4281fb 100644 --- a/src/IRCServer.py +++ b/src/IRCServer.py @@ -198,7 +198,7 @@ class Server(object): def parse_line(self, line): if not line: return - self.bot.line_handler.handle(self, line) + self.events.on("raw").call(server=self, line=line) self.check_users() def check_users(self): for user in self.new_users: diff --git a/start.py b/start.py index 132d7fb7..408afed0 100755 --- a/start.py +++ b/start.py @@ -2,7 +2,7 @@ import argparse, os, sys, time from src import Cache, Config, Database, EventManager, Exports, IRCBot -from src import IRCLineHandler, Logging, ModuleManager, Timers +from src import Logging, ModuleManager, Timers def bool_input(s): result = input("%s (Y/n): " % s) @@ -37,12 +37,11 @@ database = Database.Database(log, args.database) events = events = EventManager.EventHook(log) exports = exports = Exports.Exports() timers = Timers.Timers(database, events, log) -line_handler = IRCLineHandler.LineHandler(events, timers) modules = modules = ModuleManager.ModuleManager(events, exports, config, log, os.path.join(directory, "modules")) bot = IRCBot.Bot(directory, args, cache, config, database, events, - exports, line_handler, log, modules, timers) + exports, log, modules, timers) whitelist = bot.get_setting("module-whitelist", []) blacklist = bot.get_setting("module-blacklist", []) -- cgit v1.3.1-10-gc9f91