diff --git a/cardinal.py b/cardinal.py index dcd815c..11fd87a 100755 --- a/cardinal.py +++ b/cardinal.py @@ -51,6 +51,7 @@ def setup_logging(config=None): spec.add_option('network', basestring, 'irc.freenode.net') spec.add_option('port', int, 6667) spec.add_option('server_password', basestring, None) + spec.add_option('server_commands', list, []) spec.add_option('ssl', bool, False) spec.add_option('storage', basestring, os.path.join( os.path.dirname(os.path.realpath(sys.argv[0])), diff --git a/cardinal/bot.py b/cardinal/bot.py index e7cee39..c71f2d2 100644 --- a/cardinal/bot.py +++ b/cardinal/bot.py @@ -138,6 +138,11 @@ def signedOn(self): self.factory.plugins, self.factory.blacklist) + if self.factory.server_commands: + self.logger.info("Sending server commands") + for command in self.factory.server_commands: + self.send(command) + # Attempt to identify with NickServ, if a password was given if self.factory.password: self.logger.info("Attempting to identify with NickServ") @@ -559,6 +564,7 @@ def reactor(self): def __init__(self, network, server_password=None, + server_commands=None, channels=None, nickname='Cardinal', password=None, @@ -580,6 +586,9 @@ def __init__(self, storage -- A string containing path to storage directory. """ + if server_commands is None: + server_commands = [] + if plugins is None: plugins = [] @@ -592,6 +601,7 @@ def __init__(self, self.logger = logging.getLogger(__name__) self.network = network.lower() self.server_password = server_password + self.server_commands = server_commands self.channels = channels self.nickname = nickname self.password = password diff --git a/cardinal/test_bot.py b/cardinal/test_bot.py index af02e80..93d9cd4 100644 --- a/cardinal/test_bot.py +++ b/cardinal/test_bot.py @@ -14,8 +14,6 @@ from cardinal import exceptions, plugins from cardinal.bot import ( - LOCKED, - UNLOCKED, CardinalBot, CardinalBotFactory, user_info, @@ -122,6 +120,7 @@ def test_signedOn_sets_bot_mode_joins_and_instantiates_plugin_manager( channels = ['#channel1', '#channel2'] mock_factory = self.cardinal.factory = Mock() + mock_factory.server_commands = [] mock_factory.channels = channels mock_factory.password = None @@ -152,6 +151,7 @@ def test_signedOn_messages_nickserv( mock_join, ): mock_factory = self.cardinal.factory = Mock() + mock_factory.server_commands = [] mock_factory.channels = [] mock_factory.password = 'password' @@ -166,6 +166,38 @@ def test_signedOn_messages_nickserv( assert isinstance(self.cardinal.uptime, datetime) assert self.cardinal.booted == mock_factory.booted + @patch.object(CardinalBot, 'msg') + @patch.object(CardinalBot, 'send') + @patch('cardinal.bot.PluginManager', autospec=True) + def test_signedOn_sends_server_commands( + self, + mock_plugin_manager, + mock_send, + _mock_msg, + ): + command1 = 'AUTH foobar' + command2 = 'PING' + + mock_factory = self.cardinal.factory = Mock() + mock_factory.nickname = 'Cardinal' + mock_factory.server_commands = [ + command1, + command2, + ] + mock_factory.channels = [] + mock_factory.password = None + + self.cardinal.signedOn() + + mock_send.assert_has_calls([ + call(command1), + call(command2), + call('MODE {} +B'.format(mock_factory.nickname)) + ]) + + assert isinstance(self.cardinal.uptime, datetime) + assert self.cardinal.booted == mock_factory.booted + def test_joined(self): # this just logs, and I'm not interested in testing log messages self.cardinal.joined('#channel') @@ -183,7 +215,7 @@ def test_lineReceived(self, mock_parent_linereceived): @patch('cardinal.bot.irc.IRCClient.lineReceived') def test_lineReceived_non_utf8(self, mock_parent_linereceived): - line = b":irc-us-east-2.darkscience.net 332 Cardinal #pirates :\x031 \x0311,10[\x031]\x031,1\x1f\xc3\x82\xc2\xaf\x1f\x0313,6[\x031]\x031,1\x1f\xc3\x82\xc2\xaf\x1f\x0311,10[\x031]\x031,1\x1f\xc3\x82\xc2\xaf\x1f\x0313,6[\x031]\x031,1\x1f\xc3\x82\xc2\xaf\x1f\x0311,10[\x031]\x031,1\x1f\xc3\x82\xc2\xaf\x1f\x0313,6[\x031]\x031,1\x1f\xc3\x82\xc2\xaf\x1f\x0311,10[\x031]\x03\x0311,6\x030 Pirates Game! - Welcome aboard Dark Sails, Season 4, Mod: Pauper Privateers! - \x1dJoin wit\' !Pirates\x1d - \x0311\x1fwww.piratesirc.com\x1f \x0311,6\x0311,10[\x031]\x031,1\x1f\xc3\x82\xc2\xaf\x1f\x0313,6[\x031]\x031,1\x1f\xc3\x82\xc2" + line = b":irc-us-east-2.darkscience.net 332 Cardinal #pirates :\x031 \x0311,10[\x031]\x031,1\x1f\xc3\x82\xc2\xaf\x1f\x0313,6[\x031]\x031,1\x1f\xc3\x82\xc2\xaf\x1f\x0311,10[\x031]\x031,1\x1f\xc3\x82\xc2\xaf\x1f\x0313,6[\x031]\x031,1\x1f\xc3\x82\xc2\xaf\x1f\x0311,10[\x031]\x031,1\x1f\xc3\x82\xc2\xaf\x1f\x0313,6[\x031]\x031,1\x1f\xc3\x82\xc2\xaf\x1f\x0311,10[\x031]\x03\x0311,6\x030 Pirates Game! - Welcome aboard Dark Sails, Season 4, Mod: Pauper Privateers! - \x1dJoin wit\' !Pirates\x1d - \x0311\x1fwww.piratesirc.com\x1f \x0311,6\x0311,10[\x031]\x031,1\x1f\xc3\x82\xc2\xaf\x1f\x0313,6[\x031]\x031,1\x1f\xc3\x82\xc2" # noqa: E501 expected_line = line.decode('utf-8', 'replace') self.cardinal.lineReceived(line) @@ -566,8 +598,6 @@ def test_get_db(self): assert len(self.cardinal.db_locks) == 1 db_path = list(self.cardinal.db_locks.keys())[0] - lock = self.cardinal.db_locks[db_path] - assert lock == UNLOCKED assert db_path.endswith(os.path.join( 'database', 'test-{}.json'.format(network))) @@ -580,7 +610,7 @@ def test_get_db_not_network_specific(self): with tempdir('database') as database_path: mock_factory.storage_path = os.path.dirname(database_path) - db = self.cardinal.get_db('test', network_specific=False) + self.cardinal.get_db('test', network_specific=False) assert len(self.cardinal.db_locks) == 1 db_path = list(self.cardinal.db_locks.keys())[0] @@ -635,7 +665,7 @@ def test_db_contextmanager_exception(self): db = self.cardinal.get_db('test', network_specific=False) try: - with db() as db_ob: + with db() as db_obj: assert db_obj == {} db_obj['x'] = True raise Exception() @@ -680,6 +710,7 @@ def test_constructor_args_defaults(self): assert self.factory.network == 'irc.testnet.test' assert self.factory.server_password is None + assert self.factory.server_commands == [] assert self.factory.password is None assert self.factory.channels == [] assert self.factory.nickname == 'Cardinal' @@ -699,7 +730,9 @@ def test_constructor_args_defaults(self): assert self.factory.last_reconnection_wait is None def test_constructor_args_non_default(self): + network = 'IrC.TeStNeT.TeSt' server_password = 's3rv3r_p4ssw0rd' + server_commands = ['AUTH password', 'MODE +b foobar'] channels = ['#channel1', '#channel2'] nickname = 'Cardinal|unit-test' password = 'p4ssw0rd' @@ -712,8 +745,9 @@ def test_constructor_args_non_default(self): storage = '/path/to/storage' factory = CardinalBotFactory( - 'IrC.TeStNeT.TeSt', + network, server_password, + server_commands, channels, nickname, password, @@ -725,6 +759,7 @@ def test_constructor_args_non_default(self): ) assert factory.network == 'irc.testnet.test' + assert factory.server_commands == server_commands assert factory.server_password == server_password assert factory.password == password assert factory.channels == channels