Exemplo n.º 1
0
    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
Exemplo n.º 2
0
    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)
Exemplo n.º 3
0
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
Exemplo n.º 4
0
 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"]
Exemplo n.º 5
0
def test_has_trail(text, has_trail):
    msg = Message.parse(text)
    assert msg.parameters.has_trail == has_trail
Exemplo n.º 6
0
 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']