This repository has been archived by the owner on Apr 12, 2021. It is now read-only.
forked from nickoala/telepot
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathchatboxa_nodb.py
178 lines (128 loc) · 5.47 KB
/
chatboxa_nodb.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
import sys
import asyncio
import telepot
from telepot.delegate import per_chat_id_in
from telepot.async.delegate import call, create_open
""" Python3.4.3 or newer
$ python3.4 chatboxa_nodb.py <token> <owner_id>
Chatbox - a mailbox for chats
1. People send messages to your bot.
2. Your bot remembers the messages.
3. You read the messages later.
This version only stores the messages in memory. If the bot is killed, all messages are lost.
This version only handles text messages.
It accepts the following commands from you, the owner, only:
- /unread - tells you who has sent you messages and how many
- /next - read next sender's messages
It can be a starting point for customer-support type of bots.
"""
# Simulate a database to store unread messages
class UnreadStore(object):
def __init__(self):
self._db = {}
def put(self, msg):
chat_id = msg['chat']['id']
if chat_id not in self._db:
self._db[chat_id] = []
self._db[chat_id].append(msg)
# Pull all unread messages of a `chat_id`
def pull(self, chat_id):
messages = self._db[chat_id]
del self._db[chat_id]
# sort by date
messages.sort(key=lambda m: m['date'])
return messages
# Tells how many unread messages per chat_id
def unread_per_chat(self):
return [(k,len(v)) for k,v in self._db.items()]
# Accept commands from owner. Give him unread messages.
class OwnerHandler(telepot.helper.ChatHandler):
def __init__(self, seed_tuple, timeout, store):
super(OwnerHandler, self).__init__(seed_tuple, timeout)
self._store = store
@asyncio.coroutine
def _read_messages(self, messages):
for msg in messages:
# assume all messages are text
yield from self.sender.sendMessage(msg['text'])
@asyncio.coroutine
def on_message(self, msg):
content_type, chat_type, chat_id = telepot.glance(msg)
if content_type != 'text':
yield from self.sender.sendMessage("I don't understand")
return
command = msg['text'].strip().lower()
# Tells who has sent you how many messages
if command == '/unread':
results = self._store.unread_per_chat()
lines = []
for r in results:
n = 'ID: %d\n%d unread' % r
lines.append(n)
if not len(lines):
yield from self.sender.sendMessage('No unread messages')
else:
yield from self.sender.sendMessage('\n'.join(lines))
# read next sender's messages
elif command == '/next':
results = self._store.unread_per_chat()
if not len(results):
yield from self.sender.sendMessage('No unread messages')
return
chat_id = results[0][0]
unread_messages = self._store.pull(chat_id)
yield from self.sender.sendMessage('From ID: %d' % chat_id)
yield from self._read_messages(unread_messages)
else:
yield from self.sender.sendMessage("I don't understand")
class MessageSaver(telepot.helper.Monitor):
def __init__(self, seed_tuple, store, exclude):
# The `capture` criteria means to capture all messages.
super(MessageSaver, self).__init__(seed_tuple, capture=[{'_': lambda msg: True}])
self._store = store
self._exclude = exclude
# Store every message, except those whose sender is in the exclude list, or non-text messages.
def on_message(self, msg):
content_type, chat_type, chat_id = telepot.glance(msg)
if chat_id in self._exclude:
print('Chat id %d is excluded.' % chat_id)
return
if content_type != 'text':
print('Content type %s is ignored.' % content_type)
return
print('Storing message: %s' % msg)
self._store.put(msg)
class ChatBox(telepot.async.DelegatorBot):
def __init__(self, token, owner_id):
self._owner_id = owner_id
self._seen = set()
self._store = UnreadStore()
super(ChatBox, self).__init__(token, [
# Here is a delegate to specially handle owner commands.
(per_chat_id_in([owner_id]), create_open(OwnerHandler, 20, self._store)),
# Seed is always the same, meaning only one MessageSaver is ever spawned for entire application.
(lambda msg: 1, create_open(MessageSaver, self._store, exclude=[owner_id])),
# For senders never seen before, send him a welcome message.
(self._is_newcomer, call(self._send_welcome)),
])
# seed-calculating function: use returned value to indicate whether to spawn a delegate
def _is_newcomer(self, msg):
chat_id = msg['chat']['id']
if chat_id == self._owner_id: # Sender is owner
return None # No delegate spawned
if chat_id in self._seen: # Sender has been seen before
return None # No delegate spawned
self._seen.add(chat_id)
return [] # non-hashable ==> delegates are independent, no seed association is made.
@asyncio.coroutine
def _send_welcome(self, seed_tuple):
chat_id = seed_tuple[1]['chat']['id']
print('Sending welcome ...')
yield from self.sendMessage(chat_id, 'Hello!')
TOKEN = sys.argv[1]
OWNER_ID = int(sys.argv[2])
bot = ChatBox(TOKEN, OWNER_ID)
loop = asyncio.get_event_loop()
loop.create_task(bot.messageLoop())
print('Listening ...')
loop.run_forever()