aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/IRCBot.py6
-rw-r--r--src/IRCLineHandler.py592
-rw-r--r--src/IRCServer.py2
3 files changed, 3 insertions, 597 deletions
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: