Пример #1
0
 def setUp(self):
     factory = TalkBackBotFactory(
         self._channel,
         self._us,
         'Jane Doe',
         FakePicker(QUOTE),
         ['twss'],
     )
     self.bot = factory.buildProtocol(('127.0.0.1', 0))
     self.fake_transport = proto_helpers.StringTransport()
     self.bot.makeConnection(self.fake_transport)
     self.bot.signedOn()
     self.bot.joined(self._channel)
     self.fake_transport.clear()
Пример #2
0
    def makeService(self, options):
        """Construct the talkbackbot service."""
        config = ConfigParser()
        config.read([options['config']])
        triggers = [
            trigger.strip()
            for trigger in config.get('talkback', 'triggers').split('\n')
            if trigger.strip()
        ]

        return TalkBackBotFactory(
            endpoint=config.get('irc', 'endpoint'),
            channel=config.get('irc', 'channel'),
            nickname=config.get('irc', 'nickname'),
            realname=config.get('irc', 'realname'),
            quotesFilename=config.get('talkback', 'quotesFilename'),
            triggers=triggers,
        )
Пример #3
0
    def startService(self):
        from twisted.internet import reactor

        def connected(bot):
            self._bot = bot

        def failure(err):
            log.err(err, _why='Could not connect to specified server.')
            reactor.stop()

        quotes = QuotePicker(self._quotesFilename)
        client = clientFromString(reactor, self._endpoint)
        factory = TalkBackBotFactory(
            self._channel,
            self._nickname,
            self._realname,
            quotes,
            self._triggers,
        )

        return client.connect(factory).addCallbacks(connected, failure)
Пример #4
0
    def startService(self):
        """Construct a client & connect to server."""
        from twisted.internet import reactor
        '''If you import twisted.internet.reactor without first installing 
        a specific reactor implementation, then Twisted will install
         the default reactor for you. The particular one you get will 
         depend on the operating system and Twisted version you are using. 
         For that reason, it is general practice not to import the reactor 
         at the top level of modules to avoid accidentally installing 
         the default reactor. Instead, import the reactor in the same scope 
         in which you use it.'''
        ''' The event loop is a programming construct that waits for and 
        dispatches events or messages in a program. It works by calling some 
        internal or external “event provider”, which generally blocks 
        until an event has arrived, and then calls the relevant event handler 
        (“dispatches the event”). The reactor provides basic interfaces 
        to a number of services, including network communications, threading, 
        and event dispatching.'''

        def connected(bot):
            self._bot = bot

        def failure(err):
            log.err(err, _why="Could not connect to specified server.")
            reactor.stop()

        quotes = QuotePicker(self._quotesFilename)
        client = clientFromString(reactor, self._endpoint)
        factory = TalkBackBotFactory(
            self._channel,
            self._nickname,
            self._realname,
            quotes,
            self._triggers,
        )
        return client.connect(factory).addCallbacks(connected, failure)
Пример #5
0
class TestTalkBackBot(unittest.TestCase):

    CHANNEL = "#testchannel"
    QUOTE = "Nobody minds having what is too good for them. ~ Jane Austen"
    USERNAME = "******"

    def setUp(self):
        super(TestTalkBackBot, self).setUp()
        self.factory = TalkBackBotFactory(test_settings)
        self.bot = self.factory.buildProtocol(None)
        self.bot.msg = mock.MagicMock()
        self.bot.join = mock.MagicMock()

    def test_init_no_quotes_source(self):
        settings = types.ModuleType('test_url_settings')
        settings.CHANNEL = "#inane"

        try:
            TalkBackBotFactory(settings)
            self.fail(
                "Should not be able to initialize bot with no QUOTES_FILE or QUOTES_URL"
            )
        except AttributeError as e:
            self.assertEqual(
                'Must specify either QUOTES_URL or QUOTES_FILE in settings',
                str(e))

    def test_init_with_file(self):
        settings = types.ModuleType('test_url_settings')
        settings.CHANNEL = "#inane"
        settings.QUOTES_FILE = os.path.join(os.getcwd(),
                                            "tests/test_quotes.txt")

        bot = TalkBackBotFactory(settings)

        expected = [
            ' A fool without fear is sometimes wiser than an angel with fear. ~ Nancy Astor\n',
            "    You don't manage people, you manage things. You lead people. ~ Grace Hopper"
        ]
        self.assertEqual(expected, bot.quotation.quotes)

    def test_init_with_url(self):
        settings = types.ModuleType('test_url_settings')
        settings.CHANNEL = "#inane"
        settings.QUOTES_URL = "https://example.com/api/v1/quotations/?limit=1"

        bot = TalkBackBotFactory(settings)

        self.assertEqual("https://example.com/api/v1/quotations/?limit=1",
                         bot.quotation.quotes_url)

    @mock.patch('twisted.words.protocols.irc.IRCClient.connectionMade')
    @mock.patch('logging.info')
    def test_connectionMade(self, mock_log_info, mock_made):
        self.bot.connectionMade()

        mock_made.assert_called_once_with(self.bot)
        mock_log_info.assert_called_once_with('connectionMade')

    @mock.patch('twisted.words.protocols.irc.IRCClient.connectionLost')
    @mock.patch('logging.info')
    def test_connectionLost(self, mock_log_info, mock_lost):
        self.bot.connectionLost('bad stuff')

        mock_lost.assert_called_once_with(self.bot, 'bad stuff')
        mock_log_info.assert_called_once_with('connectionLost')

    @mock.patch('logging.info')
    def test_signedOn(self, mock_log_info):
        self.bot.signedOn()

        self.bot.join.assert_called_once_with('#test')
        mock_log_info.assert_called_once_with('Signed on')

    @mock.patch('logging.info')
    def test_joined(self, mock_log_info):
        self.bot.joined(self.CHANNEL)
        mock_log_info.assert_called_once_with('[shesaidbot has joined #test]')

    def test_privmsg__no_trigger(self):
        """Shouldn't send a quote if message does not match trigger"""
        self.bot.privmsg(self.USERNAME, self.CHANNEL, "hi")
        self.assertFalse(self.bot.msg.called)

    def test_privmsg__with_trigger(self):
        """Should send a quote if message matches trigger"""
        self.bot.privmsg(self.USERNAME, self.CHANNEL, "twss")
        self.bot.msg.assert_called_with(self.CHANNEL, self.QUOTE)

    def test_privmsg__private_message(self):
        """ For private messages, should send quote directly to user """
        self.bot.privmsg(self.USERNAME, test_settings.NICKNAME, "hi")
        self.bot.msg.assert_called_with(self.USERNAME, self.QUOTE)

    def test_privmsg__private_message_truncated_nickname(self):
        """ Send quote directly to user even if name is truncated """
        self.bot.privmsg(self.USERNAME, test_settings.NICKNAME[:-2], "hi")
        self.bot.msg.assert_called_with(self.USERNAME, self.QUOTE)

    def test_privmsg__private_message_alternate_nickname(self):
        """ Send quote directly to user even if using alternate nickname """
        self.bot.privmsg(self.USERNAME, test_settings.NICKNAME + '_', "hi")
        self.bot.msg.assert_called_with(self.USERNAME, self.QUOTE)

    @mock.patch('logging.info')
    def test_clientConnectionLost(self, mock_log_info):
        mock_connector = mock.MagicMock(connect=mock.MagicMock())

        self.factory.clientConnectionLost(mock_connector, 'error occurred')

        mock_connector.connect.assert_called_once_with()
        mock_log_info.assert_called_once_with('connection lost, reconnecting')

    @mock.patch('logging.info')
    def test_clientConnectionFailed(self, mock_log_info):
        mock_connector = mock.MagicMock()

        self.factory.clientConnectionFailed(mock_connector, 'error occurred')

        mock_connector.connect.assert_called_once_with()
        mock_log_info.assert_called_once_with(
            'connection failed: error occurred')
Пример #6
0
 def setUp(self):
     super(TestTalkBackBot, self).setUp()
     self.factory = TalkBackBotFactory(test_settings)
     self.bot = self.factory.buildProtocol(None)
     self.bot.msg = mock.MagicMock()
     self.bot.join = mock.MagicMock()
Пример #7
0
 def setUp(self):
     super(TestTalkBackBot, self).setUp()
     factory = TalkBackBotFactory(test_settings)
     self.bot = factory.buildProtocol(None)
     self.bot.msg = mock.MagicMock()
Пример #8
0
class TestTalkBackBot(unittest.TestCase):

    CHANNEL = "#testchannel"
    QUOTE = "Nobody minds having what is too good for them. ~ Jane Austen"
    USERNAME = "******"

    def setUp(self):
        super(TestTalkBackBot, self).setUp()
        self.factory = TalkBackBotFactory(tests.test_settings)
        self.bot = self.factory.buildProtocol(None)
        self.bot.msg = mock.MagicMock()
        self.bot.join = mock.MagicMock()

    def test_init_no_quotes_source(self):
        settings = types.ModuleType('test_url_settings')
        settings.CHANNEL = "#inane"

        try:
            TalkBackBotFactory(settings)
            self.fail("Should not be able to initialize bot with no QUOTES_FILE or QUOTES_URL")
        except AttributeError as e:
            self.assertEqual('Must specify either QUOTES_URL or QUOTES_FILE in settings', str(e))

    def test_init_with_file(self):
        settings = types.ModuleType('test_url_settings')
        settings.CHANNEL = "#inane"
        settings.QUOTES_FILE = os.path.join(os.getcwd(), "tests/test_quotes.txt")

        bot = TalkBackBotFactory(settings)

        expected = [
            ' A fool without fear is sometimes wiser than an angel with fear. ~ Nancy Astor\n',
            "    You don't manage people, you manage things. You lead people. ~ Grace Hopper"
        ]
        self.assertEqual(expected, bot.quotation.quotes)

    def test_init_with_url(self):
        settings = types.ModuleType('test_url_settings')
        settings.CHANNEL = "#inane"
        settings.QUOTES_URL = "https://example.com/api/v2/quotations/?limit=1"

        bot = TalkBackBotFactory(settings)

        self.assertEqual("https://example.com/api/v2/quotations/?limit=1", bot.quotation.quotes_url)

    @mock.patch('twisted.words.protocols.irc.IRCClient.connectionMade')
    @mock.patch('logging.info')
    def test_connectionMade(self, mock_log_info, mock_made):
        self.bot.connectionMade()

        mock_made.assert_called_once_with(self.bot)
        mock_log_info.assert_called_once_with('connectionMade')

    @mock.patch('twisted.words.protocols.irc.IRCClient.connectionLost')
    @mock.patch('logging.info')
    def test_connectionLost(self, mock_log_info, mock_lost):
        self.bot.connectionLost('bad stuff')

        mock_lost.assert_called_once_with(self.bot, 'bad stuff')
        mock_log_info.assert_called_once_with('connectionLost')

    @mock.patch('logging.info')
    def test_signedOn(self, mock_log_info):
        self.bot.signedOn()

        self.bot.join.assert_called_once_with('#test')
        mock_log_info.assert_called_once_with('Signed on')

    @mock.patch('logging.info')
    def test_joined(self, mock_log_info):
        self.bot.joined(self.CHANNEL)
        mock_log_info.assert_called_once_with('[shesaidbot has joined #test]')

    def test_privmsg__no_trigger(self):
        """Shouldn't send a quote if message does not match trigger"""
        self.bot.privmsg(self.USERNAME, self.CHANNEL, "hi")
        self.assertFalse(self.bot.msg.called)

    def test_privmsg__with_trigger(self):
        """Should send a quote if message matches trigger"""
        self.bot.privmsg(self.USERNAME, self.CHANNEL, "twss")
        self.bot.msg.assert_called_with(self.CHANNEL, self.QUOTE)

    def test_privmsg__private_message(self):
        """ For private messages, should send quote directly to user """
        self.bot.privmsg(self.USERNAME, tests.test_settings.NICKNAME, "hi")
        self.bot.msg.assert_called_with(self.USERNAME, self.QUOTE)

    def test_privmsg__private_message_truncated_nickname(self):
        """ Send quote directly to user even if name is truncated """
        self.bot.privmsg(self.USERNAME, tests.test_settings.NICKNAME[:-2], "hi")
        self.bot.msg.assert_called_with(self.USERNAME, self.QUOTE)

    def test_privmsg__private_message_alternate_nickname(self):
        """ Send quote directly to user even if using alternate nickname """
        self.bot.privmsg(self.USERNAME, tests.test_settings.NICKNAME + '_', "hi")
        self.bot.msg.assert_called_with(self.USERNAME, self.QUOTE)

    @mock.patch('logging.info')
    def test_clientConnectionLost(self, mock_log_info):
        mock_connector = mock.MagicMock(connect=mock.MagicMock())

        self.factory.clientConnectionLost(mock_connector, 'error occurred')

        mock_connector.connect.assert_called_once_with()
        mock_log_info.assert_called_once_with('connection lost, reconnecting')

    @mock.patch('logging.info')
    def test_clientConnectionFailed(self, mock_log_info):
        mock_connector = mock.MagicMock()

        self.factory.clientConnectionFailed(mock_connector, 'error occurred')

        mock_connector.connect.assert_called_once_with()
        mock_log_info.assert_called_once_with('connection failed: error occurred')