forked from ehForwarderBot/ehForwarderBot
-
Notifications
You must be signed in to change notification settings - Fork 0
/
channel.py
279 lines (239 loc) · 8 KB
/
channel.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
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
from abc import ABCMeta, abstractmethod
# Constants Objects
class MsgType:
Text = "Text"
Image = "Image"
Audio = "Audio"
File = "File"
Location = "Location"
Video = "Video"
Link = "Link"
Sticker = "Sticker"
Unsupported = "Unsupported"
Command = "Command"
class MsgSource:
User = "User"
Group = "Group"
System = "System"
class TargetType:
Member = "Member"
Message = "Message"
Substitution = "Substitution"
class ChannelType:
Master = "Master"
Slave = "Slave"
# Objects
class EFBChannel:
__metaclass__ = ABCMeta
channel_name = "Empty Channel"
channel_emoji = "?"
channel_id = "emptyChannel"
channel_type = ChannelType.Slave
queue = None
supported_message_types = set()
stop_polling = False
def __init__(self, queue, mutex):
"""
Initialize a channel.
Args:
queue (queue.Queue): Global message queue.
mutex (threading.Lock): Global interaction thread lock.
"""
self.queue = queue
self.mutex = mutex
def get_extra_functions(self):
"""Get a list of extra functions
Returns:
dict: A dict of functions marked as extra functions. `methods[methodName]()`
"""
if self.channel_type == ChannelType.Master:
raise NameError("get_extra_function is not available on master channels.")
methods = {}
for mName in dir(self):
m = getattr(self, mName)
if getattr(m, "extra_fn", False):
methods[mName] = m
return methods
@abstractmethod
def send_message(self, msg):
"""
Send message to slave channels.
Args:
msg (EFBMsg): Message object to be sent.
Returns:
EFBMsg: The same message object with message ID.
"""
raise NotImplementedError()
@abstractmethod
def poll(self):
raise NotImplementedError()
@abstractmethod
def get_chats(self):
"""
Return a list of available chats in the channel.
Returns:
list of dict: a list of available chats in the channel.
"""
raise NotImplementedError()
@abstractmethod
def get_chat(self, chat_uid):
"""
Return the standard chat dict of the selected chat.
Args:
chat_uid (str): UID of the chat.
Returns:
dict: the standard chat dict of the chat.
Raises:
KeyError: Chat is not found in the channel.
"""
raise NotImplementedError()
class EFBMsg:
"""A message.
Attributes:
attributes (dict): Attributes used for a specific message type
channel_emoji (str): Emoji Icon for the source Channel
channel_id (str): ID for the source channel
channel_name (str): Name of the source channel
destination (dict): Destination (may be a user or a group)
member (dict): Author of this msg in a group. `None` for private messages.
origin (dict): Origin (may be a user or a group)
source (MsgSource): Source of message: User/Group/System
target (dict): Target (refers to @ messages and "reply to" messages.)
text (str): text of the message
type (MsgType): Type of message
uid (str): Unique ID of message
url (str): URL of multimedia file/Link share. `None` if N/A
path (str): Local path of multimedia file. `None` if N/A
file (file): File object to multimedia file, type "ra". `None` if N/A
mime (str): MIME type of the file. `None` if N/A
filename (str): File name of the multimedia file. `None` if N/A
`target`:
There are 3 types of targets: `Member`, `Message`, and `Substitution`
TargetType: Member
This is for the case where the message is targeting to a specific member in the group.
`target['target']` here is a `user dict`.
Example:
```
target = {
'type': TargetType.Member,
'target': {
"name": "Target name",
'alias': 'Target alias',
'uid': 'Target UID',
}
}
```
TargetType: Message
This is for the case where the message is directly replying to another message.
`target['target']` here is an `EFBMsg` object.
Example:
```
target = {
'type': TargetType.Message,
'target': EFBMsg()
}
```
TargetType: Substitution
This is for the case when user "@-ed" a list of users in the message.
`target['target']` here is a dict of correspondence between
the string used to refer to the user in the message
and a user dict.
Example:
```
target = {
'type': TargetType.Substitution,
'target': {
'@alice': {
'name': "Alice",
'alias': 'Alisi',
'uid': 123456
},
'@bob': {
'name': "Bob",
'alias': 'Baobu',
'uid': 654321
}
}
}
```
`attributes`:
A dict of attributes can be attached for some specific message types.
Please specify `None` for values not available.
Link:
```
attributes = {
"title": "Title of the article",
"description": "Description of the article",
"image": "URL to the thumbnail/featured image of the article",
"url": "URL to the article"
}
```
Location:
```
text = "Name of the location"
attributes = {
"longitude": float("A float number indicating longitude"),
"latitude": float("A float number indicating latitude")
}
```
Command:
Messages with type `Command` allow user to take action to
a specific message, including vote, add friends, etc.
Example:
```
attributes = {
"commands": [
{
"name": "A human-readable name for the command",
"callable": "name to the callable function in your channel object",
"args": [
"a list of positional parameters passed to your function"
],
"kwargs": {
"desc": "a dict of keyword parameters passed to your function"
}
},
{
"name": "Greet @blueset on Telegram",
"callable": "send_message_by_username",
"args": [
"blueset",
"Hello!"
],
"kwargs": {}
}
]
}
```
"""
channel_name = "Empty Channel"
channel_emoji = "?"
channel_id = "emptyChannel"
source = MsgSource.User
type = MsgType.Text
member = None
origin = {
"name": "Origin name",
'alias': 'Origin alias',
'uid': 'Origin UID',
}
destination = {
"channel": "channel_id",
"name": "Destination name",
'alias': 'Destination alias',
'uid': 'Destination UID',
}
target = None
uid = None
text = ""
url = None
path = None
file = None
mime = None
filename = None
attributes = {}
def __init__(self, channel=None):
if isinstance(channel, EFBChannel):
self.channel_name = channel.channel_name
self.channel_emoji = channel.channel_emoji
self.channel_id = channel.channel_id