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
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
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)
def trigger_account(bot): line = '@account=egg :[email protected] PRIVMSG #Sopel :Hello, world' return Trigger( bot.config, PreTrigger(tools.Identifier('egg'), line), None, 'egg')
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)
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
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
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
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
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)
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'
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
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
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
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
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')
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')
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'
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
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')
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')
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')
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
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'
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
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)