Beispiel #1
0
def test_bot_legacy_permissions(sopel_bot):
    """
    Make sure permissions match after being updated from both RPL_NAMREPLY
    and RPL_WHOREPLY, #1482
    """

    nick = Identifier("Admin")

    # RPL_NAMREPLY
    pretrigger = PreTrigger("Foo",
                            ":test.example.com 353 Foo = #test :Foo ~@Admin")
    trigger = Trigger(sopel_bot.config, pretrigger, None)
    coretasks.handle_names(sopel_bot, trigger)

    assert (sopel_bot.channels["#test"].privileges[nick] ==
            sopel_bot.privileges["#test"][nick])

    # RPL_WHOREPLY
    pretrigger = PreTrigger(
        "Foo",
        ":test.example.com 352 Foo #test ~Admin adminhost test.example.com Admin Hr~ :0 Admin",
    )
    trigger = Trigger(sopel_bot.config, pretrigger, None)
    coretasks.recv_who(sopel_bot, trigger)

    assert (sopel_bot.channels["#test"].privileges[nick] ==
            sopel_bot.privileges["#test"][nick])

    assert sopel_bot.users.get(nick) is not None
Beispiel #2
0
def test_ircv3_server_time_trigger(nick, configfactory):
    line = '@time=2016-01-09T03:15:42.000Z :[email protected] PRIVMSG #Sopel :Hello, world'
    pretrigger = PreTrigger(nick, line)
    config = configfactory('default.cfg', TMP_CONFIG)
    fakematch = re.match('.*', line)

    trigger = Trigger(config, pretrigger, fakematch)
    assert trigger.time == datetime.datetime(2016, 1, 9, 3, 15, 42, 0)

    # Spec-breaking string
    line = '@time=2016-01-09T04:20 :[email protected] PRIVMSG #Sopel :Hello, world'
    pretrigger = PreTrigger(nick, line)
    assert pretrigger.time is not None
Beispiel #3
0
    def on_message(self, message):
        """Handle an incoming IRC message.

        :param str message: the received raw IRC message
        """
        self.last_raw_line = message

        pretrigger = PreTrigger(self.nick, message)
        if all(cap not in self.enabled_capabilities
               for cap in ['account-tag', 'extended-join']):
            pretrigger.tags.pop('account', None)

        if pretrigger.event == 'PING':
            self.backend.send_pong(pretrigger.args[-1])
        elif pretrigger.event == 'ERROR':
            LOGGER.error("ERROR received from server: %s", pretrigger.args[-1])
            if self.hasquit:
                # TODO: refactor direct interface with asynchat
                self.backend.close_when_done()
        elif pretrigger.event == events.ERR_NICKNAMEINUSE:
            LOGGER.error('Nickname already in use!')
            # TODO: refactor direct interface with asynchat
            self.backend.handle_close()

        self.dispatch(pretrigger)
Beispiel #4
0
def trigger_account(bot):
    line = '@account=egg :[email protected] PRIVMSG #Sopel :Hello, world'
    return Trigger(
        bot.config,
        PreTrigger(tools.Identifier('egg'), line),
        None,
        'egg')
Beispiel #5
0
    def on_message_sent(self, raw):
        """Handle any message sent through the connection.

        :param str raw: raw text message sent through the connection

        When a message is sent through the IRC connection, the bot will log
        the raw message. If necessary, it will also simulate the
        `echo-message`_ feature of IRCv3.

        .. _echo-message: https://ircv3.net/irc/#echo-message
        """
        # Log raw message
        self.log_raw(raw, '>>')

        # Simulate echo-message
        no_echo = 'echo-message' not in self.enabled_capabilities
        echoed = ['PRIVMSG', 'NOTICE']
        if no_echo and any(raw.upper().startswith(cmd) for cmd in echoed):
            # Use the hostmask we think the IRC server is using for us,
            # or something reasonable if that's not available
            host = 'localhost'
            if self.settings.core.bind_host:
                host = self.settings.core.bind_host
            else:
                try:
                    host = self.hostmask
                except KeyError:
                    pass  # we tried, and that's good enough

            pretrigger = PreTrigger(
                self.nick, ":{0}!{1}@{2} {3}".format(self.nick, self.user,
                                                     host, raw))
            self.dispatch(pretrigger)
Beispiel #6
0
def test_ircv3_extended_join_trigger(nick):
    line = ':[email protected] JOIN #Sopel bar :Real Name'
    pretrigger = PreTrigger(nick, line)

    config = MockConfig()
    config.core.owner_account = 'bar'

    fakematch = re.match('.*', line)

    trigger = Trigger(config, pretrigger, fakematch)
    assert trigger.sender == '#Sopel'
    assert trigger.raw == line
    assert trigger.is_privmsg is False
    assert trigger.hostmask == '[email protected]'
    assert trigger.user == 'foo'
    assert trigger.nick == Identifier('Foo')
    assert trigger.host == 'example.com'
    assert trigger.event == 'JOIN'
    assert trigger.match == fakematch
    assert trigger.group == fakematch.group
    assert trigger.groups == fakematch.groups
    assert trigger.args == ['#Sopel', 'bar', 'Real Name']
    assert trigger.account == 'bar'
    assert trigger.tags == {'account': 'bar'}
    assert trigger.owner is True
    assert trigger.admin is True
Beispiel #7
0
def test_ircv3_extended_join_trigger(nick, configfactory):
    line = ':[email protected] JOIN #Sopel bar :Real Name'
    pretrigger = PreTrigger(nick, line)

    config = configfactory('default.cfg', TMP_CONFIG)

    fakematch = re.match('.*', line)

    trigger = Trigger(config, pretrigger, fakematch)
    assert trigger.sender == '#Sopel'
    assert trigger.raw == line
    assert trigger.is_privmsg is False
    assert trigger.hostmask == '[email protected]'
    assert trigger.user == 'foo'
    assert trigger.nick == Identifier('Foo')
    assert trigger.host == 'example.com'
    assert trigger.event == 'JOIN'
    assert trigger.match == fakematch
    assert trigger.group == fakematch.group
    assert trigger.groups == fakematch.groups
    assert trigger.args == ['#Sopel', 'bar', 'Real Name']
    assert trigger.plain == 'Real Name'
    assert trigger.account == 'bar'
    assert trigger.tags == {'account': 'bar'}
    assert trigger.ctcp is None
    assert trigger.owner is True
    assert trigger.admin is True
Beispiel #8
0
def test_ircv3_intents_trigger(nick, configfactory):
    line = '@intent=ACTION :[email protected] PRIVMSG #Sopel :Hello, world'
    pretrigger = PreTrigger(nick, line)

    config = configfactory('default.cfg', TMP_CONFIG)
    fakematch = re.match('.*', line)

    trigger = Trigger(config, pretrigger, fakematch)
    assert trigger.sender == '#Sopel'
    assert trigger.raw == line
    assert trigger.is_privmsg is False
    assert trigger.hostmask == '[email protected]'
    assert trigger.user == 'bar'
    assert trigger.nick == Identifier('Foo')
    assert trigger.host == 'example.com'
    assert trigger.event == 'PRIVMSG'
    assert trigger.match == fakematch
    assert trigger.group == fakematch.group
    assert trigger.groups == fakematch.groups
    assert trigger.groupdict == fakematch.groupdict
    assert trigger.args == ['#Sopel', 'Hello, world']
    assert trigger.plain == 'Hello, world'
    assert trigger.tags == {'intent': 'ACTION'}
    assert trigger.ctcp == 'ACTION'
    assert trigger.account is None
    assert trigger.admin is True
    assert trigger.owner is True
Beispiel #9
0
def test_intents_trigger(nick):
    line = '@intent=ACTION :[email protected] PRIVMSG #Sopel :Hello, world'
    pretrigger = PreTrigger(nick, line)

    config = MockConfig()
    config.core.owner = 'Foo'
    config.core.admins = ['Bar']

    fakematch = re.match('.*', line)

    trigger = Trigger(config, pretrigger, fakematch)
    assert trigger.sender == '#Sopel'
    assert trigger.raw == line
    assert trigger.is_privmsg is False
    assert trigger.hostmask == '[email protected]'
    assert trigger.user == 'foo'
    assert trigger.nick == Identifier('Foo')
    assert trigger.host == 'example.com'
    assert trigger.event == 'PRIVMSG'
    assert trigger.match == fakematch
    assert trigger.group == fakematch.group
    assert trigger.groups == fakematch.groups
    assert trigger.args == ['#Sopel', 'Hello, world']
    assert trigger.tags == {'intent': 'ACTION'}
    assert trigger.admin is True
    assert trigger.owner is True
Beispiel #10
0
    def write(self, args, text=None):
        args = [self.safe(arg) for arg in args]
        if text is not None:
            text = self.safe(text)
        try:
            # Blocking lock, can't send two things at a time
            self.writing_lock.acquire()

            # From RFC2812 Internet Relay Chat: Client Protocol
            # Section 2.3
            #
            # https://tools.ietf.org/html/rfc2812.html
            #
            # IRC messages are always lines of characters terminated with a
            # CR-LF (Carriage Return - Line Feed) pair, and these messages SHALL
            # NOT exceed 512 characters in length, counting all characters
            # including the trailing CR-LF. Thus, there are 510 characters
            # maximum allowed for the command and its parameters. There is no
            # provision for continuation of message lines.

            max_length = unicode_max_length = 510
            if text is not None:
                temp = (' '.join(args) + ' :' + text)
            else:
                temp = ' '.join(args)

            # The max length of 512 is in bytes, not unicode
            while len(temp.encode('utf-8')) > max_length:
                temp = temp[:unicode_max_length]
                unicode_max_length = unicode_max_length - 1

            # Ends the message with CR-LF
            temp = temp + '\r\n'

            # Log and output the message
            self.log_raw(temp, '>>')
            self.send(temp.encode('utf-8'))
        finally:
            self.writing_lock.release()

        # Simulate echo-message
        if ('echo-message' not in self.enabled_capabilities and
                args[0].upper() in ['PRIVMSG', 'NOTICE']):
            # Use the hostmask we think the IRC server is using for us,
            # or something reasonable if that's not available
            host = 'localhost'
            if self.config.core.bind_host:
                host = self.config.core.bind_host
            else:
                try:
                    host = self.hostmask
                except KeyError:
                    pass  # we tried, and that's good enough

            pretrigger = PreTrigger(
                self.nick,
                ":{0}!{1}@{2} {3}".format(self.nick, self.user, host, temp)
            )
            self.dispatch(pretrigger)
Beispiel #11
0
def test_unusual_pretrigger(nick):
    line = 'PING'
    pretrigger = PreTrigger(nick, line)
    assert pretrigger.tags == {}
    assert pretrigger.hostmask is None
    assert pretrigger.line == line
    assert pretrigger.args == []
    assert pretrigger.event == 'PING'
Beispiel #12
0
def test_ircv3_server_time_trigger(nick):
    line = '@time=2016-01-09T03:15:42.000Z :[email protected] PRIVMSG #Sopel :Hello, world'
    pretrigger = PreTrigger(nick, line)

    config = MockConfig()
    config.core.owner = 'Foo'
    config.core.admins = ['Bar']

    fakematch = re.match('.*', line)

    trigger = Trigger(config, pretrigger, fakematch)
    assert trigger.time == datetime.datetime(2016, 1, 9, 3, 15, 42, 0)

    # Spec-breaking string
    line = '@time=2016-01-09T04:20 :[email protected] PRIVMSG #Sopel :Hello, world'
    pretrigger = PreTrigger(nick, line)
    assert pretrigger.time is not None
Beispiel #13
0
def test_url_non_matching(link, bot):
    line = PreTrigger(bot.nick, ':[email protected] PRIVMSG #channel {}'.format(link))
    matched_rules = [
        # we can ignore matches that don't come from the plugin under test
        match[0] for match in bot.rules.get_triggered_rules(bot, line)
        if match[0].get_plugin_name() == 'reddit'
    ]

    assert len(matched_rules) == 0
Beispiel #14
0
def keyword_match(bot, trigger):
    channel = trigger.sender
    nick, message = trigger.groups()
    nick = FORMATTING_RE.sub('', nick)
    LOGGER.debug("NICK")
    LOGGER.debug(f"{nick}")
    pretrigger = PreTrigger(
        bot.nick, f":{nick}!{nick}@discord.com PRIVMSG {channel} :{message}")
    bot.dispatch(pretrigger)
def test_mode_colon(sopel):
    """
    Ensure mode messages with colons are parsed properly
    """

    # RPL_NAMREPLY to create Users and (zeroed) privs
    for user in set("Uvoice Uadmin".split(" ")):
        pretrigger = PreTrigger(
            "Foo", ":test.example.com 353 Foo = #test :Foo %s" % user)
        trigger = Trigger(sopel.config, pretrigger, None)
        coretasks.handle_names(MockSopelWrapper(sopel, trigger), trigger)

    pretrigger = PreTrigger("Foo", "MODE #test +av Uadmin :Uvoice")
    trigger = Trigger(sopel.config, pretrigger, None)
    coretasks.track_modes(MockSopelWrapper(sopel, trigger), trigger)

    assert sopel.channels["#test"].privileges[Identifier("Uvoice")] == VOICE
    assert sopel.channels["#test"].privileges[Identifier("Uadmin")] == ADMIN
Beispiel #16
0
def test_ircv3_account_tag_trigger(nick, configfactory):
    line = '@account=bar :[email protected] PRIVMSG #Sopel :Hello, world'
    pretrigger = PreTrigger(nick, line)

    config = configfactory('default.cfg', TMP_CONFIG_ACCOUNT)
    fakematch = re.match('.*', line)

    trigger = Trigger(config, pretrigger, fakematch)
    assert trigger.admin is True
    assert trigger.owner is True
Beispiel #17
0
def test_join_pretrigger(nick):
    line = ':[email protected] JOIN #Sopel'
    pretrigger = PreTrigger(nick, line)
    assert pretrigger.tags == {}
    assert pretrigger.hostmask == '[email protected]'
    assert pretrigger.line == line
    assert pretrigger.args == ['#Sopel']
    assert pretrigger.event == 'JOIN'
    assert pretrigger.nick == Identifier('Foo')
    assert pretrigger.user == 'foo'
    assert pretrigger.host == 'example.com'
    assert pretrigger.sender == Identifier('#Sopel')
Beispiel #18
0
def test_ircv3_extended_join_pretrigger(nick):
    line = ':[email protected] JOIN #Sopel bar :Real Name'
    pretrigger = PreTrigger(nick, line)
    assert pretrigger.tags == {'account': 'bar'}
    assert pretrigger.hostmask == '[email protected]'
    assert pretrigger.line == line
    assert pretrigger.args == ['#Sopel', 'bar', 'Real Name']
    assert pretrigger.event == 'JOIN'
    assert pretrigger.nick == Identifier('Foo')
    assert pretrigger.user == 'foo'
    assert pretrigger.host == 'example.com'
    assert pretrigger.sender == Identifier('#Sopel')
Beispiel #19
0
def test_intents_pretrigger(nick):
    line = '@intent=ACTION :[email protected] PRIVMSG #Sopel :Hello, world'
    pretrigger = PreTrigger(nick, line)
    assert pretrigger.tags == {'intent': 'ACTION'}
    assert pretrigger.hostmask == '[email protected]'
    assert pretrigger.line == line
    assert pretrigger.args == ['#Sopel', 'Hello, world']
    assert pretrigger.event == 'PRIVMSG'
    assert pretrigger.nick == Identifier('Foo')
    assert pretrigger.user == 'foo'
    assert pretrigger.host == 'example.com'
    assert pretrigger.sender == '#Sopel'
Beispiel #20
0
def test_quit_pretrigger(nick):
    line = ':[email protected] QUIT :quit message text'
    pretrigger = PreTrigger(nick, line)
    assert pretrigger.tags == {}
    assert pretrigger.hostmask == '[email protected]'
    assert pretrigger.line == line
    assert pretrigger.args == ['quit message text']
    assert pretrigger.event == 'QUIT'
    assert pretrigger.nick == Identifier('Foo')
    assert pretrigger.user == 'foo'
    assert pretrigger.host == 'example.com'
    assert pretrigger.sender is None
Beispiel #21
0
def test_pm_pretrigger(nick):
    line = ':[email protected] PRIVMSG Sopel :Hello, world'
    pretrigger = PreTrigger(nick, line)
    assert pretrigger.tags == {}
    assert pretrigger.hostmask == '[email protected]'
    assert pretrigger.line == line
    assert pretrigger.args == ['Sopel', 'Hello, world']
    assert pretrigger.event == 'PRIVMSG'
    assert pretrigger.nick == Identifier('Foo')
    assert pretrigger.user == 'foo'
    assert pretrigger.host == 'example.com'
    assert pretrigger.sender == Identifier('Foo')
Beispiel #22
0
def test_ctcp_data_pretrigger(nick):
    line = ':[email protected] PRIVMSG Sopel :\x01PING 1123321\x01'
    pretrigger = PreTrigger(nick, line)
    assert pretrigger.tags == {'intent': 'PING'}
    assert pretrigger.hostmask == '[email protected]'
    assert pretrigger.line == line
    assert pretrigger.args == ['Sopel', '1123321']
    assert pretrigger.event == 'PRIVMSG'
    assert pretrigger.nick == Identifier('Foo')
    assert pretrigger.user == 'foo'
    assert pretrigger.host == 'example.com'
    assert pretrigger.sender == Identifier('Foo')
Beispiel #23
0
def test_ctcp_intent_pretrigger(nick):
    line = ':[email protected] PRIVMSG Sopel :\x01VERSION\x01'
    pretrigger = PreTrigger(nick, line)
    assert pretrigger.tags == {'intent': 'VERSION'}
    assert pretrigger.hostmask == '[email protected]'
    assert pretrigger.line == line
    assert pretrigger.args == ['Sopel', '']
    assert pretrigger.text == '\x01VERSION\x01'
    assert pretrigger.event == 'PRIVMSG'
    assert pretrigger.nick == Identifier('Foo')
    assert pretrigger.user == 'foo'
    assert pretrigger.host == 'example.com'
    assert pretrigger.sender == Identifier('Foo')
Beispiel #24
0
def test_ircv3_account_tag_trigger(nick):
    line = '@account=Foo :[email protected] PRIVMSG #Sopel :Hello, world'
    pretrigger = PreTrigger(nick, line)

    config = MockConfig()
    config.core.owner_account = 'Foo'
    config.core.admins = ['Bar']

    fakematch = re.match('.*', line)

    trigger = Trigger(config, pretrigger, fakematch)
    assert trigger.admin is True
    assert trigger.owner is True
def test_bot_mixed_mode_types(sopel):
    """
    Ensure mixed argument- and non-argument- modes are handled
    Sopel 6.6.6 and older did not behave well. #1575
    """

    # RPL_NAMREPLY to create Users and (zeroed) privs
    for user in set("Uvoice Uop Uadmin Uvoice2 Uop2 Uadmin2".split(" ")):
        pretrigger = PreTrigger(
            "Foo", ":test.example.com 353 Foo = #test :Foo %s" % user)
        trigger = Trigger(sopel.config, pretrigger, None)
        coretasks.handle_names(MockSopelWrapper(sopel, trigger), trigger)

    # Non-attribute-requiring non-permission mode
    pretrigger = PreTrigger("Foo", "MODE #test +amov Uadmin Uop Uvoice")
    trigger = Trigger(sopel.config, pretrigger, None)
    coretasks.track_modes(MockSopelWrapper(sopel, trigger), trigger)

    assert sopel.channels["#test"].privileges[Identifier("Uvoice")] == VOICE
    assert sopel.channels["#test"].privileges[Identifier("Uop")] == OP
    assert sopel.channels["#test"].privileges[Identifier("Uadmin")] == ADMIN

    # Attribute-requiring non-permission modes
    # This results in a _send_who, which isn't supported in MockSopel or this
    # test, so we just make sure it results in an exception instead of privesc.
    pretrigger = PreTrigger("Foo",
                            "MODE #test +abov Uadmin2 x!y@z Uop2 Uvoice2")
    trigger = Trigger(sopel.config, pretrigger, None)
    try:
        coretasks.track_modes(MockSopelWrapper(sopel, trigger), trigger)
    except AttributeError as e:
        if e.args[
                0] == "'MockSopel' object has no attribute 'enabled_capabilities'":
            return

    assert sopel.channels["#test"].privileges[Identifier("Uvoice2")] == VOICE
    assert sopel.channels["#test"].privileges[Identifier("Uop2")] == OP
    assert sopel.channels["#test"].privileges[Identifier("Uadmin2")] == ADMIN
Beispiel #26
0
def test_ctcp_action_pretrigger(nick):
    line = ':[email protected] PRIVMSG #Sopel :\x01ACTION Hello, world\x01'
    pretrigger = PreTrigger(nick, line)
    assert pretrigger.tags == {'intent': 'ACTION'}
    assert pretrigger.hostmask == '[email protected]'
    assert pretrigger.line == line
    assert pretrigger.args == ['#Sopel', 'Hello, world']
    assert pretrigger.text == '\x01ACTION Hello, world\x01'
    assert pretrigger.plain == 'Hello, world'
    assert pretrigger.event == 'PRIVMSG'
    assert pretrigger.nick == Identifier('Foo')
    assert pretrigger.user == 'foo'
    assert pretrigger.host == 'example.com'
    assert pretrigger.sender == '#Sopel'
Beispiel #27
0
def test_tags_pretrigger(nick):
    line = '@foo=bar;baz;sopel.chat/special=value :[email protected] PRIVMSG #Sopel :Hello, world'
    pretrigger = PreTrigger(nick, line)
    assert pretrigger.tags == {'baz': None,
                               'foo': 'bar',
                               'sopel.chat/special': 'value'}
    assert pretrigger.hostmask == '[email protected]'
    assert pretrigger.line == line
    assert pretrigger.args == ['#Sopel', 'Hello, world']
    assert pretrigger.event == 'PRIVMSG'
    assert pretrigger.nick == Identifier('Foo')
    assert pretrigger.user == 'foo'
    assert pretrigger.host == 'example.com'
    assert pretrigger.sender == '#Sopel'
def test_bot_mixed_mode_removal(sopel):
    """
    Ensure mixed mode types like -h+a are handled
    Sopel 6.6.6 and older did not handle this correctly. #1575
    """

    # RPL_NAMREPLY to create Users and (zeroed) privs
    for user in set("Uvoice Uop".split(" ")):
        pretrigger = PreTrigger(
            "Foo", ":test.example.com 353 Foo = #test :Foo %s" % user)
        trigger = Trigger(sopel.config, pretrigger, None)
        coretasks.handle_names(MockSopelWrapper(sopel, trigger), trigger)

    pretrigger = PreTrigger("Foo", "MODE #test +qao Uvoice Uvoice Uvoice")
    trigger = Trigger(sopel.config, pretrigger, None)
    coretasks.track_modes(MockSopelWrapper(sopel, trigger), trigger)

    pretrigger = PreTrigger(
        "Foo", "MODE #test -o+o-qa+v Uvoice Uop Uvoice Uvoice Uvoice")
    trigger = Trigger(sopel.config, pretrigger, None)
    coretasks.track_modes(MockSopelWrapper(sopel, trigger), trigger)

    assert sopel.channels["#test"].privileges[Identifier("Uvoice")] == VOICE
    assert sopel.channels["#test"].privileges[Identifier("Uop")] == OP
def test_bot_mixed_modes(sopel):
    """
    Ensure mixed modes like +vha are tracked correctly.
    Sopel 6.6.6 and older would assign all modes to all users. #1575
    """

    # RPL_NAMREPLY to create Users and (zeroed) privs
    for user in set("Unothing Uvoice Uhalfop Uop Uadmin Uowner".split(" ")):
        pretrigger = PreTrigger(
            "Foo", ":test.example.com 353 Foo = #test :Foo %s" % user)
        trigger = Trigger(sopel.config, pretrigger, None)
        coretasks.handle_names(MockSopelWrapper(sopel, trigger), trigger)

    pretrigger = PreTrigger(
        "Foo", "MODE #test +qvhao Uowner Uvoice Uhalfop Uadmin Uop")
    trigger = Trigger(sopel.config, pretrigger, None)
    coretasks.track_modes(MockSopelWrapper(sopel, trigger), trigger)

    assert sopel.channels["#test"].privileges[Identifier("Unothing")] == 0
    assert sopel.channels["#test"].privileges[Identifier("Uvoice")] == VOICE
    assert sopel.channels["#test"].privileges[Identifier("Uhalfop")] == HALFOP
    assert sopel.channels["#test"].privileges[Identifier("Uop")] == OP
    assert sopel.channels["#test"].privileges[Identifier("Uadmin")] == ADMIN
    assert sopel.channels["#test"].privileges[Identifier("Uowner")] == OWNER
Beispiel #30
0
    def found_terminator(self):
        line = self.buffer
        if line.endswith('\r'):
            line = line[:-1]
        self.buffer = ''
        self.last_ping_time = datetime.now()
        pretrigger = PreTrigger(self.nick, line)

        if pretrigger.event == 'PING':
            self.write(('PONG', pretrigger.args[-1]))
        elif pretrigger.event == 'ERROR':
            LOGGER.error("ERROR recieved from server: %s", pretrigger.args[-1])
            if self.hasquit:
                self.close_when_done()
        elif pretrigger.event == '433':
            stderr('Nickname already in use!')
            self.handle_close()

        self.dispatch(pretrigger)