Skip to content
This repository was archived by the owner on Nov 26, 2018. It is now read-only.

Commit 6c96051

Browse files
gakervbabiy
authored andcommitted
Addes message service
1 parent 82be820 commit 6c96051

3 files changed

Lines changed: 155 additions & 6 deletions

File tree

botbot_plugins/base.py

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,15 @@
44

55
import fakeredis
66

7+
class PrivateMessage(object):
8+
"""
9+
A holder object for sending a private message.
10+
This is used in botbot.apps.plugins.runner
11+
"""
12+
def __init__(self, nick, msg):
13+
self.nick = nick
14+
self.msg = msg
15+
716

817
class BasePlugin(object):
918
"All plugins inherit this class"
@@ -44,6 +53,14 @@ def retrieve(self, key):
4453
value = unicode(value, 'utf-8')
4554
return value
4655

56+
def delete(self, key):
57+
"""Deletes a stored `key`
58+
59+
DEL: http://redis.io/commands/del
60+
"""
61+
ukey = self._unique_key(key)
62+
return self.app.storage.delete(ukey) == 1
63+
4764
def incr(self, key):
4865
"""Increments counter specified by `key`. If necessary, creates
4966
counter and initializes to 0.
@@ -61,8 +78,11 @@ class DummyLine(object):
6178
def __init__(self, packet):
6279
self.text = packet['text']
6380
self.full_text = packet['text']
64-
self.user = 'repl_user'
81+
self.user = packet.get('User', 'repl_user')
82+
self._channel_name = packet.get('Channel', '#dummy-channel')
6583
self.is_direct_message = self.check_direct_message()
84+
self._command = packet.get('Command', 'PRIVMSG')
85+
self._is_message = self._command == 'PRIVMSG'
6686

6787
def check_direct_message(self):
6888
"""Are you addressing the bot?"""
@@ -71,6 +91,18 @@ def check_direct_message(self):
7191
return True
7292
return False
7393

94+
def __str__(self):
95+
return self.full_text
96+
97+
def __repr__(self):
98+
return str(self)
99+
100+
101+
class DummyPrivateMessage(object):
102+
def __init__(self, nick, msg):
103+
self.nick = nick
104+
self.msg = msg
105+
74106

75107
REPL_INTRO = """
76108
#########################
@@ -101,6 +133,7 @@ def __init__(self, *args, **kwargs):
101133
self.storage = fakeredis.FakeStrictRedis()
102134
self.messages_router = {}
103135
self.mentions_router = {}
136+
self.firehose_router = {}
104137
self.plugin_configs = {}
105138
if 'test_plugin' in kwargs:
106139
self.test_mode = True
@@ -120,7 +153,7 @@ def register(self, plugin):
120153
attr = getattr(plugin, key)
121154
if (not key.startswith('__') and
122155
getattr(attr, 'route_rule', None) and
123-
attr.route_rule[0] in ('messages', 'mentions')):
156+
attr.route_rule[0] in ('messages', 'mentions', 'firehose')):
124157
self.output('Route {}: {} ({}, {})'.format(attr.route_rule[0],
125158
plugin.slug, key,
126159
attr.route_rule[1]))
@@ -140,12 +173,16 @@ def set_config(self, plugin_slug, fields_dict):
140173
"""Manually set a plugin config. Used for testing"""
141174
self.plugin_configs[plugin_slug].fields.update(fields_dict)
142175

143-
def respond(self, text):
176+
def respond(self, text, **kwargs):
144177
"""Listens for incoming messages"""
145178
if text.startswith('!!'):
146179
return self.do_config(text)
147180
self.responses = []
148-
line = DummyLine({'text': text})
181+
182+
packet = {'text': text}
183+
packet.update(kwargs)
184+
185+
line = DummyLine(packet)
149186
self.dispatch(line)
150187
if self.test_mode:
151188
return self.responses
@@ -183,6 +220,8 @@ def dispatch(self, line):
183220
if line.is_direct_message:
184221
self.check_routes_for_matches(line, self.mentions_router)
185222

223+
self.check_routes_for_matches(line, self.firehose_router)
224+
186225
def check_routes_for_matches(self, line, router):
187226
"""Checks if line matches the routes' rules and calls functions"""
188227
for _, route_list in router.items():
@@ -191,7 +230,11 @@ def check_routes_for_matches(self, line, router):
191230
if match:
192231
response = func(line, **match.groupdict())
193232
if response:
194-
self.responses.append(response)
195-
self.output('[o__o]: ' + response)
233+
if isinstance(response, PrivateMessage):
234+
self.responses.append(response.msg)
235+
self.output('[o__o]: ' + response.msg)
236+
else:
237+
self.responses.append(response)
238+
self.output('[o__o]: ' + response)
196239

197240
app = DummyApp()
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
"""
2+
Message service plugin
3+
"""
4+
import json
5+
from ..base import BasePlugin, PrivateMessage
6+
from ..decorators import listens_to_mentions
7+
8+
9+
class Plugin(BasePlugin):
10+
"""
11+
I can leave messages for users who are offline. To have me send
12+
a message to your colleague `MrTaubyPants` on your behalf when he
13+
comes online, ask me in this format:
14+
15+
{{ nick }}: message MrTaubyPants Message you want to leave.
16+
"""
17+
18+
slug = 'message_service'
19+
20+
def find_message(self, line):
21+
"""
22+
Find any messages for a user after they have logged in.
23+
"""
24+
if line._command == "JOIN":
25+
messages = self.retrieve(line.user)
26+
if messages:
27+
self.delete(line.user)
28+
messages = json.loads(messages)
29+
if hasattr(line, '_channel_name'):
30+
out = "Beep BEEP! You received the following messages in {0} when you were offline.".format(
31+
line._channel_name)
32+
else:
33+
out = "You received the following messages when you were offline."
34+
for message in messages:
35+
out += "\n{0}".format(message)
36+
return PrivateMessage(line.user, out)
37+
38+
find_message.route_rule = ('firehose', ur'(.*)')
39+
40+
@listens_to_mentions(r'^message\s+(?P<nick>[\w\-_]+)\s+(?P<message>.*)$')
41+
def store_message(self, line, nick, message):
42+
"""
43+
Store a message
44+
"""
45+
message = "From {0} '{1}'".format(
46+
line.user, message)
47+
# does the user have any messages waiting?
48+
messages = self.retrieve(nick)
49+
if not messages:
50+
messages = set()
51+
else:
52+
messages = set(json.loads(messages))
53+
54+
messages.add(message)
55+
messages = list(messages)
56+
self.store(nick, json.dumps(messages))
57+
return u"{0}, I will tell {1} when they appear online.".format(
58+
line.user, nick)
59+
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# -*- coding: utf-8 -*-
2+
"""
3+
Message service tests
4+
"""
5+
import pytest
6+
from botbot_plugins.base import DummyApp
7+
from botbot_plugins.plugins import message_service
8+
9+
10+
@pytest.fixture
11+
def app():
12+
return DummyApp(test_plugin=message_service.Plugin())
13+
14+
15+
def test_remember(app):
16+
"""
17+
[d__d]: message user foobar
18+
"""
19+
responses = app.respond(r'@message george Are you going to the meeting?')
20+
assert responses == [u'repl_user, I will tell george when they appear online.']
21+
22+
23+
def test_remind_user(app):
24+
"""
25+
The user should be messaged when joining the channel
26+
"""
27+
responses = app.respond(r'@message george Are you going to the meeting?')
28+
assert responses == [u'repl_user, I will tell george when they appear online.']
29+
30+
responses = app.respond('george joined the channel', **{
31+
'Command': 'JOIN',
32+
'User': 'george'})
33+
assert responses == ["Beep BEEP! You received the following messages in #dummy-channel when you were offline.\nFrom repl_user 'Are you going to the meeting?'"]
34+
35+
def test_multiple_reminders(app):
36+
"""
37+
Multiple reminders should work too.
38+
"""
39+
responses = app.respond(r'@message george Are you going to the meeting?')
40+
responses = app.respond(r'@message george I think I will be going.')
41+
42+
responses = app.respond('george joined the channel', **{
43+
'Command': 'JOIN',
44+
'User': 'george'})
45+
assert responses == ["Beep BEEP! You received the following messages in #dummy-channel when you were offline.\nFrom repl_user 'I think I will be going.'\nFrom repl_user 'Are you going to the meeting?'"]
46+
47+

0 commit comments

Comments
 (0)