def _out(self, server, target, target_str, is_channel, obj, type, tags): if type == OutType.OUT: color = utils.consts.GREEN else: color = utils.consts.RED line_str = obj.pop() if obj.prefix: line_str = "[%s] %s" % (utils.irc.color(obj.prefix, color), line_str) method = self._command_method(server, target, is_channel) if not method in ["PRIVMSG", "NOTICE"]: raise ValueError("Unknown command-method '%s'" % method) line = IRCLine.ParsedLine(method, [target_str, line_str], tags=tags) valid, trunc = line.truncate(server.hostmask(), margin=STR_MORE_LEN) if trunc: if not trunc[0] in WORD_BOUNDARIES: for boundary in WORD_BOUNDARIES: left, *right = valid.rsplit(boundary, 1) if right: valid = left trunc = right[0] + trunc obj.insert("%s %s" % (STR_CONTINUED, trunc)) valid = valid + STR_MORE line = IRCLine.parse_line(valid) server.send(line)
def _execute(self, server, commands, **kwargs): for command in commands: line = command.format(**kwargs) if IRCLine.is_human(line): line = IRCLine.parse_human(line) else: line = IRCLine.parse_line(line) server.send(line)
def get_lines(self) -> typing.List[IRCLine.ParsedLine]: lines = [] for line in self._lines: line.add_tag("batch", self.identifier) lines.append(line) lines.insert(0, IRCLine.ParsedLine("BATCH", ["+%s" % self.identifier, self.type])) lines.append(IRCLine.ParsedLine("BATCH", ["-%s" % self.identifier])) return lines
def send_message(self, event): our_hostmask = IRCLine.parse_hostmask(event["server"].hostmask()) echo = IRCLine.ParsedLine(event["line"].command, event["line"].args, source=our_hostmask, tags=event["line"].tags) echo.id = event["line"].id self.events.on("raw.received").call(line=echo, server=event["server"])
def raw(self, event): if IRCLine.is_human(event["spec"][0]): line = IRCLine.parse_human(event["spec"][0]) else: line = IRCLine.parse_line(event["spec"][0]) line = event["server"].send(line) if not line == None: event["stdout"].write("Sent: %s" % line.parsed_line.format()) else: event["stderr"].write("Line was filtered")
def rawctl(self, event): rawargs = str(event["data"]).split(" ", 1) server = self._server_from_alias(rawargs[0]) if IRCLine.is_human(rawargs[1]): line = IRCLine.parse_human(rawargs[1]) else: line = IRCLine.parse_line(rawargs[1]) line = server.send(line) if not line == None: return "Sent: " + line.parsed_line.format() else: return "Line was filtered"
def preprocess_send_privmsg(self, event): if len(event["line"].args) > 1: if ("\n" in event["line"].args[1] and event["server"].has_capability(CAP)): event["line"].invalidate() target = event["line"].args[0] lines = event["line"].args[1].split("\n") batch = IRCLine.IRCSendBatch("draft/multiline", [target]) for line in lines: line = IRCLine.ParsedLine("PRIVMSG", [target, line]) batch.add_line(line) for line in batch.get_lines(): event["server"].send(line)
def handle_353(event): channel = event["server"].channels.get(event["line"].args[2]) nicknames = event["line"].args.get(3).split(" ") # there can sometimes be a dangling space at the end of a 353 if nicknames and not nicknames[-1]: nicknames.pop(-1) for nickname in nicknames: modes = set([]) while nickname[0] in event["server"].prefix_symbols: modes.add(event["server"].prefix_symbols[nickname[0]]) nickname = nickname[1:] if event["server"].has_capability_str("userhost-in-names"): hostmask = IRCLine.parse_hostmask(nickname) nickname = hostmask.nickname user = event["server"].get_user(hostmask.nickname, username=hostmask.username, 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)
def send(self, line_parsed: IRCLine.ParsedLine, immediate: bool = False) -> typing.Optional[IRCLine.SentLine]: if not self.send_enabled: return None line_events = self.events.new_root() self.events.on("preprocess.send").on(line_parsed.command).call_unsafe( server=self, line=line_parsed, events=line_events) self.events.on("preprocess.send").call_unsafe(server=self, line=line_parsed, events=line_events) if line_parsed.valid() or line_parsed.assured(): line = line_parsed.format() line_obj = IRCLine.SentLine(line_events, datetime.datetime.utcnow(), self.hostmask(), line_parsed) self.socket.send(line_obj, immediate=immediate) if immediate: self.bot.trigger_write() return line_obj return None
def send(self, method): if self.has_text(): prefix = "" if not self._hide_prefix: prefix = utils.consts.RESET + "[%s] " % self._prefix text = self._text[:].replace("\r", "") while "\n\n" in text: text = text.replace("\n\n", "\n") full_text = "%s%s" % (prefix, text) message_factory = _message_factory(method) line = message_factory(self._target_str, full_text, tags=self._tags) if self._assured: line.assure() valid, truncated = line.truncate(self.server.hostmask(), margin=STR_MORE_LEN) if truncated: valid, truncated = self._adjust_to_word_boundaries( valid, truncated) line = IRCLine.parse_line(valid + STR_MORE) self._text = "%s%s" % (STR_CONTINUED, truncated) else: self._text = "" sent_line = self.server.send(line)
def parse_line(line: str) -> IRCLine.ParsedLine: tags = {} # type: typing.Dict[str, typing.Any] source = None # type: typing.Optional[IRCLine.Hostmask] command = None if line[0] == "@": tags_prefix, line = line[1:].split(" ", 1) for tag in filter(None, tags_prefix.split(";")): tag, sep, value = tag.partition("=") if value: tags[tag] = message_tag_unescape(value) else: tags[tag] = None line, trailing_separator, trailing_split = line.partition(" :") trailing = None # type: typing.Optional[str] if trailing_separator: trailing = trailing_split if line[0] == ":": source_str, line = line[1:].split(" ", 1) source = seperate_hostmask(source_str) command, sep, line = line.partition(" ") args = [] # type: typing.List[str] if line: # this is so that `args` is empty if `line` is empty args = line.split(" ") if not trailing == None: args.append(typing.cast(str, trailing)) return IRCLine.ParsedLine(command, args, source, tags)
def add_server(self, event): alias = event["spec"][0] hostname, sep, port = event["spec"][1].partition(":") tls = port.startswith("+") port = port.lstrip("+") if not hostname or not port or not port.isdigit(): raise utils.EventError("Please provide <hostname>:[+]<port>") port = int(port) hostmask = IRCLine.parse_hostmask(event["spec"][2]) nickname = hostmask.nickname username = hostmask.username or nickname realname = nickname bindhost = hostmask.hostname or None try: server_id = self.bot.database.servers.add(alias, hostname, port, "", tls, bindhost, nickname, username, realname) except Exception as e: event["stderr"].write("Failed to add server") self.log.error("failed to add server \"%s\"", [alias], exc_info=True) return event["stdout"].write("Added server '%s'" % alias)
def batch(self, event): identifier = event["line"].args[0] modifier, identifier = identifier[0], identifier[1:] if modifier == "+": batch_type = event["line"].args[1] args = event["line"].args[2:] batch = IRCLine.IRCBatch(identifier, batch_type, args, event["line"].tags, source=event["line"].source) event["server"].batches[identifier] = batch self.events.on("received.batch.start").call(batch=batch, server=event["server"]) else: batch = event["server"].batches[identifier] del event["server"].batches[identifier] lines = batch.get_lines() results = self.events.on("received.batch.end").call( batch=batch, server=event["server"]) for result in results: if not result == None: lines = result break for line in lines: self._handle(event["server"], line)
def raw(self, event): """ :help: Send a line of raw IRC data :usage: <raw line> :permission: raw """ if IRCLine.is_human(event["args"]): line = IRCLine.parse_human(event["args"]) else: line = IRCLine.parse_line(event["args"]) line = event["server"].send(line) if not line == None: event["stdout"].write("Sent: %s" % line.parsed_line.format()) else: event["stderr"].write("Line was filtered")
def mode(target: str, mode: str=None, args: typing.List[str]=None ) -> IRCLine.ParsedLine: command_args = [target] if mode: command_args.append(mode) if args: command_args = command_args+args return IRCLine.ParsedLine("MODE", command_args)
def whowas(target: str, amount: int=None, server: str=None ) -> IRCLine.ParsedLine: command_args = [target] if amount: command_args.append(str(amount)) if server: command_args.append(server) return IRCLine.ParsedLine("WHOWAS", command_args)
def handle_333(events, event): channel = event["server"].channels.get(event["line"].args[1]) topic_setter = IRCLine.parse_hostmask(event["line"].args[2]) topic_time = int(event["line"].args[3]) channel.set_topic_setter(topic_setter) channel.set_topic_time(topic_time) events.on("received.333").call(channel=channel, setter=topic_setter, set_at=topic_time, server=event["server"])
def send(self, line: str): results = self.events.on("preprocess.send").call_unsafe( server=self, line=line) for result in results: if result: line = result break line_stripped = line.split("\n", 1)[0].strip("\r") line_obj = IRCLine.Line(self, datetime.datetime.utcnow(), line_stripped) self.socket.send(line_obj) return line_obj
def send(self, line_parsed: IRCLine.ParsedLine): if not self.send_enabled: return None self.events.on("preprocess.send").on(line_parsed.command).call_unsafe( server=self, line=line_parsed) line = line_parsed.format() line_obj = IRCLine.SentLine(datetime.datetime.utcnow(), self.hostmask(), line_parsed) self.socket.send(line_obj) return line_obj
def batch_end(self, event): if BATCH.match(event["batch"].type): messages = [] lines = event["batch"].get_lines() for line in lines: message = line.args[1] if TAG.present(line.tags): last_message = "" if messages: last_message = messages.pop(-1) message = last_message + message messages.append(message) target = event["batch"].args[0] message = "\n".join(messages) return [ IRCLine.ParsedLine("PRIVMSG", [target, message], source=event["batch"].source) ]
def _kill(self, signum): if self._exited: return self._exited = True self.events.on("signal.interrupt").call(signum=signum) written = False for server in list(self.bot.servers.values()): if server.connected: server.socket.clear_send_buffer() line = IRCLine.ParsedLine("QUIT", ["Shutting down"]) sent_line = server.send(line, immediate=True) sent_line.events.on("send").hook(self._make_hook(server)) server.send_enabled = False written = True if not written: sys.exit()
def _line(self, command: str, unfiltered_args: typing.Sequence[typing.Optional[str]], tags={}): args: typing.List[str] = [a for a in unfiltered_args if not a is None] return IRCLine.ParsedLine(command, args, tags=tags)
def batch_end(identifier: str, tags: typing.Dict[str, str]={}): return IRCLine.ParsedLine("BATCH", ["-%s" % identifier], tags=tags)
def message(events, event): from_self = _from_self(event["server"], event["line"].source) if from_self == None: return direction = "send" if from_self else "received" target_str = event["line"].args[0] message = None if len(event["line"].args) > 1: message = event["line"].args[1] source = event["line"].source if (not event["server"].nickname or not source or source.hostmask == event["server"].name): if source: event["server"].name = event["line"].source.hostmask else: source = IRCLine.parse_hostmask(event["server"].name) target_str = event["server"].nickname or "*" if from_self: user = event["server"].get_user(event["server"].nickname) else: user = event["server"].get_user(source.nickname, username=source.username, hostname=source.hostname) # strip prefix_symbols from the start of target, for when people use # e.g. 'PRIVMSG +#channel :hi' which would send a message to only # voiced-or-above users statusmsg = "" for char in target_str: if char in event["server"].statusmsg: statusmsg += char else: break target = target_str.replace(statusmsg, "", 1) is_channel = event["server"].is_channel(target) if is_channel: if not target in event["server"].channels: return target_obj = event["server"].channels.get(target) else: target_obj = event["server"].get_user(target) kwargs = {"server": event["server"], "target": target_obj, "target_str": target_str, "user": user, "tags": event["line"].tags, "is_channel": is_channel, "from_self": from_self, "line": event["line"], "statusmsg": statusmsg} action = False if message: ctcp_message = utils.irc.parse_ctcp(message) if ctcp_message: if (not ctcp_message.command == "ACTION" or not event["line"].command == "PRIVMSG"): if event["line"].command == "PRIVMSG": ctcp_action = "request" else: ctcp_action = "response" events.on(direction).on("ctcp").on(ctcp_action).call( message=ctcp_message.message, **kwargs) events.on(direction).on("ctcp").on(ctcp_action).on( ctcp_message.command).call(message=ctcp_message.message, **kwargs) return else: message = ctcp_message.message action = True if not message == None: kwargs["message"] = message kwargs["message_split"] = message.split(" ") kwargs["action"] = action event_type = event["line"].command.lower() if event_type == "privmsg": event_type = "message" context = "channel" if is_channel else "private" hook = events.on(direction).on(event_type).on(context) buffer_line = None if message: buffer_line = IRCBuffer.BufferLine(user.nickname, message, action, event["line"].tags, from_self, event["line"].command) buffer_obj = target_obj if is_channel: hook.call(channel=target_obj, buffer_line=buffer_line, **kwargs) else: buffer_obj = target_obj if not from_self: buffer_obj = user hook.call(buffer_line=buffer_line, **kwargs) if buffer_line: buffer_obj.buffer.add(buffer_line)
def _tagmsg(self, target, state): return IRCLine.ParsedLine("TAGMSG", [target], tags={ "+typing": state, "+draft/typing": state })
def _(target, message, tags): return IRCLine.ParsedLine(command, [target, message], tags=tags)
def seperate_hostmask(hostmask: str) -> IRCLine.Hostmask: nickname, _, username = hostmask.partition("!") username, _, hostname = username.partition("@") return IRCLine.Hostmask(nickname, username, hostname, hostmask)
def new_line(self, command: str, args: typing.List[str]=None, tags: typing.Dict[str, str]=None) -> IRCLine.SendableLine: return IRCLine.SendableLine(command, args or [], len((":%s " % self.hostmask()).encode("utf8")), tags)
def send_raw(self, line: str): return self.send(IRCLine.parse_line(line))
def _post_read(self, lines: typing.List[str]): for line in lines: self.bot.log.debug("%s (raw recv) | %s", [str(self), line]) self.events.on("raw.received").call_unsafe(server=self, line=IRCLine.parse_line(line)) self.check_users()