def parse_line(self, line: str) -> Event: message = Message.parse(line) command = message.command command_params = message.parameters # Reply to pings immediately if command == "PING": self.conn.send("PONG " + command_params[-1], log=False) # Parse the command and params # Content content_raw = _get_param(message, content_params) if content_raw is not None: content = irc_clean(content_raw) else: content = None # Event type event_type = irc_command_to_event_type.get(command, EventType.other) target = _get_param(message, target_params) # Parse for CTCP if event_type is EventType.message and content_raw.startswith("\x01"): possible_ctcp = content_raw[1:] if content_raw.endswith("\x01"): possible_ctcp = possible_ctcp[:-1] if "\x01" in possible_ctcp: logger.debug( "[%s] Invalid CTCP message received, " "treating it as a mornal message", self.conn.name, ) ctcp_text = None else: ctcp_text = possible_ctcp ctcp_text_split = ctcp_text.split(None, 1) if ctcp_text_split[0] == "ACTION": # this is a CTCP ACTION, set event_type and content accordingly event_type = EventType.action content = irc_clean(ctcp_text_split[1]) else: # this shouldn't be considered a regular message event_type = EventType.other else: ctcp_text = None # Channel channel = _get_param(message, chan_params) prefix = message.prefix if prefix is None: nick = None user = None host = None mask = None else: nick = prefix.nick user = prefix.user host = prefix.host mask = prefix.mask if channel: # TODO Migrate plugins to accept the original case of the channel channel = channel.lower() channel = channel.split()[0] # Just in case there is more data # Channel for a PM is the sending user if channel == self.conn.nick.lower(): channel = nick.lower() else: # If the channel isn't set, it's the sending user/server channel = nick.lower() if nick else nick # Set up parsed message # TODO: Do we really want to send the raw `prefix` and `command_params` here? event = Event( bot=self.bot, conn=self.conn, event_type=event_type, content_raw=content_raw, content=content, target=target, channel=channel, nick=nick, user=user, host=host, mask=mask, irc_raw=line, irc_prefix=mask, irc_command=command, irc_paramlist=command_params, irc_ctcp_text=ctcp_text, irc_tags=message.tags, ) return event
def data_received(self, data): self._input_buffer += data while b"\r\n" in self._input_buffer: line_data, self._input_buffer = self._input_buffer.split(b"\r\n", 1) line = decode(line_data) try: message = Message.parse(line) except Exception: logger.exception( "[%s] Error occurred while parsing IRC line '%s' from %s", self.conn.name, line, self.conn.describe_server() ) continue command = message.command command_params = message.parameters # Reply to pings immediately if command == "PING": self.conn.send("PONG " + command_params[-1], log=False) # Parse the command and params # Content if command_params.has_trail: content_raw = command_params[-1] content = irc_clean(content_raw) else: content_raw = None content = None # Event type event_type = irc_command_to_event_type.get( command, EventType.other ) # Target (for KICK, INVITE) if event_type is EventType.kick: target = command_params[1] elif command in ("INVITE", "MODE"): target = command_params[0] else: # TODO: Find more commands which give a target target = None # Parse for CTCP if event_type is EventType.message and content_raw.startswith("\x01"): possible_ctcp = content_raw[1:] if content_raw.endswith('\x01'): possible_ctcp = possible_ctcp[:-1] if '\x01' in possible_ctcp: logger.debug( "[%s] Invalid CTCP message received, " "treating it as a mornal message", self.conn.name ) ctcp_text = None else: ctcp_text = possible_ctcp ctcp_text_split = ctcp_text.split(None, 1) if ctcp_text_split[0] == "ACTION": # this is a CTCP ACTION, set event_type and content accordingly event_type = EventType.action content = irc_clean(ctcp_text_split[1]) else: # this shouldn't be considered a regular message event_type = EventType.other else: ctcp_text = None # Channel channel = None if command_params: if command in ["NOTICE", "PRIVMSG", "KICK", "JOIN", "PART", "MODE"]: channel = command_params[0] elif command == "INVITE": channel = command_params[1] elif len(command_params) > 2 or not (command_params.has_trail and len(command_params) == 1): channel = command_params[0] prefix = message.prefix if prefix is None: nick = None user = None host = None mask = None else: nick = prefix.nick user = prefix.user host = prefix.host mask = prefix.mask if channel: # TODO Migrate plugins to accept the original case of the channel channel = channel.lower() channel = channel.split()[0] # Just in case there is more data if channel == self.conn.nick.lower(): channel = nick.lower() # Set up parsed message # TODO: Do we really want to send the raw `prefix` and `command_params` here? event = Event( bot=self.bot, conn=self.conn, event_type=event_type, content_raw=content_raw, content=content, target=target, channel=channel, nick=nick, user=user, host=host, mask=mask, irc_raw=line, irc_prefix=mask, irc_command=command, irc_paramlist=command_params, irc_ctcp_text=ctcp_text ) # handle the message, async async_util.wrap_future(self.bot.process(event), loop=self.loop)
def test_has_trail(text, has_trail): """Ensure that a message with trailing arguments is recorded as having a tril""" msg = Message.parse(text) assert msg.parameters.has_trail == has_trail
def test_parse_bytes(self): """Test parsing bytes""" line = Message.parse(b"COMMAND some params :and stuff") assert line.command == "COMMAND" assert line.parameters == ["some", "params", "and stuff"]
def test_has_trail(text, has_trail): msg = Message.parse(text) assert msg.parameters.has_trail == has_trail
def test_parse_bytes(self): line = Message.parse(b"COMMAND some params :and stuff") assert line.command == 'COMMAND' assert line.parameters == ['some', 'params', 'and stuff']