Skip to content

Commit 6707d74

Browse files
deker104python273
authored andcommitted
Add VkBotLongPoll (#153)
1 parent c950b6b commit 6707d74

4 files changed

Lines changed: 249 additions & 1 deletion

File tree

docs/bots_longpoll.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
Модуль `bots_longpoll` (VkBotLongPoll)
2+
=======================================
3+
4+
Модуль для работы с Bots Long Poll
5+
6+
.. module:: vk_api.bots_longpoll
7+
.. autoclass:: VkBotLongPoll
8+
:members:
9+
.. autoclass:: BotEvent
10+
:members:

docs/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ vk_api – Python модуль для написания скриптов для
2626
vk_api
2727
audio
2828
longpoll
29+
bots_longpoll
2930
execute
3031
requests_pool
3132
tools

examples/messages_bot/messages_bot.py renamed to examples/messages_bot/user_messages_bot.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ def main():
2222
return
2323
"""
2424

25-
# Авторизация группы:
25+
# Авторизация группы (для групп рекомендуется использовать VkBotLongPoll):
2626
# при передаче token вызывать vk_session.auth не нужно
2727
"""
2828
vk_session = vk_api.VkApi(token='токен с доступом к сообщениям и фото')

vk_api/bot_longpoll.py

Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
import requests
2+
from enum import Enum
3+
4+
CHAT_START_ID = int(2E9)
5+
6+
7+
class DotDict(dict):
8+
__getattr__ = dict.get
9+
__setattr__ = dict.__setitem__
10+
__delattr__ = dict.__delitem__
11+
12+
13+
class VkBotEventType(Enum):
14+
MESSAGE_NEW = 'message_new'
15+
MESSAGE_REPLY = 'message_reply'
16+
MESSAGE_EDIT = 'message_edit'
17+
18+
MESSAGE_TYPING_STATE = 'message_typing_state'
19+
20+
MESSAGE_ALLOW = 'message_allow'
21+
22+
MESSAGE_DENY = 'message_deny'
23+
24+
PHOTO_NEW = 'photo_new'
25+
26+
PHOTO_COMMENT_NEW = 'photo_comment_new'
27+
PHOTO_COMMENT_EDIT = 'photo_comment_edit'
28+
PHOTO_COMMENT_RESTORE = 'photo_comment_restore'
29+
30+
PHOTO_COMMENT_DELETE = 'photo_comment_delete'
31+
32+
AUDIO_NEW = 'audio_new'
33+
34+
VIDEO_NEW = 'video_new'
35+
36+
VIDEO_COMMENT_NEW = 'video_comment_new'
37+
VIDEO_COMMENT_EDIT = 'video_comment_edit'
38+
VIDEO_COMMENT_RESTORE = 'video_comment_restore'
39+
40+
VIDEO_COMMENT_DELETE = 'video_comment_delete'
41+
42+
WALL_POST_NEW = 'wall_post_new'
43+
WALL_REPOST = 'wall_repost'
44+
45+
WALL_REPLY_NEW = 'wall_reply_new'
46+
WALL_REPLY_EDIT = 'wall_reply_edit'
47+
WALL_REPLY_RESTORE = 'wall_reply_restore'
48+
49+
WALL_REPLY_DELETE = 'wall_reply_delete'
50+
51+
BOARD_POST_NEW = 'board_post_new'
52+
BOARD_POST_EDIT = 'board_post_edit'
53+
BOARD_POST_RESTORE = 'board_post_restore'
54+
55+
BOARD_POST_DELETE = 'board_post_delete'
56+
57+
MARKET_COMMENT_NEW = 'market_comment_new'
58+
MARKET_COMMENT_EDIT = 'market_comment_edit'
59+
MARKET_COMMENT_RESTORE = 'market_comment_restore'
60+
61+
MARKET_COMMENT_DELETE = 'market_comment_delete'
62+
63+
GROUP_LEAVE = 'group_leave'
64+
65+
GROUP_JOIN = 'group_join'
66+
67+
USER_BLOCK = 'user_block'
68+
69+
USER_UNBLOCK = 'user_unblock'
70+
71+
POLL_VOTE_NEW = 'poll_vote_new'
72+
73+
GROUP_OFFICERS_EDIT = 'group_officers_edit'
74+
75+
GROUP_CHANGE_SETTINGS = 'group_change_settings'
76+
77+
GROUP_CHANGE_PHOTO = 'group_change_photo'
78+
79+
80+
class VkBotEvent(object):
81+
""" Событие Bots Long Poll
82+
83+
Имеет поля в соответствии с `документацией <https://vk.com/dev/groups_events>`_.
84+
"""
85+
86+
__slots__ = (
87+
'raw',
88+
't', 'type',
89+
'obj', 'object',
90+
'group_id'
91+
)
92+
93+
def __init__(self, raw):
94+
self.raw = raw
95+
96+
try:
97+
self.type = VkBotEventType(raw['type'])
98+
except ValueError:
99+
self.type = raw['type']
100+
101+
self.t = self.type # shortcut
102+
103+
self.object = DotDict(raw['object'])
104+
self.obj = self.object
105+
106+
self.group_id = raw['group_id']
107+
108+
def __repr__(self):
109+
return f'<{type(self)}({self.raw})>'
110+
111+
class VkBotMessageEvent(VkBotEvent):
112+
""" Событие с сообщением Bots Long Poll """
113+
114+
__slots__ = ('from_user', 'from_chat', 'from_group', 'chat_id')
115+
116+
def __init__(self, raw):
117+
super(VkBotMessageEvent, self).__init__(raw)
118+
119+
self.from_user = False
120+
self.from_chat = False
121+
self.from_group = False
122+
self.chat_id = None
123+
124+
if self.obj.peer_id < 0:
125+
self.from_group = True
126+
elif self.obj.peer_id < CHAT_START_ID:
127+
self.from_user = True
128+
else:
129+
self.from_chat = True
130+
self.chat_id = self.obj.peer_id - CHAT_START_ID
131+
132+
133+
class VkBotLongPoll(object):
134+
""" Класс для работы с Bots Long Poll сервером
135+
136+
`Подробнее в документации VK API <https://vk.com/dev/bots_longpoll>`__.
137+
138+
:param vk: объект :class:`VkApi`
139+
:param group_id: id группы
140+
:param wait: время ожидания
141+
"""
142+
143+
__slots__ = (
144+
'vk', 'wait', 'group_id',
145+
'url', 'session',
146+
'key', 'server', 'ts'
147+
)
148+
149+
CLASS_BY_EVENT_TYPE = {
150+
VkBotEventType.MESSAGE_NEW.value: VkBotMessageEvent,
151+
VkBotEventType.MESSAGE_REPLY.value: VkBotMessageEvent,
152+
VkBotEventType.MESSAGE_EDIT.value: VkBotMessageEvent,
153+
}
154+
DEFAULT_EVENT_CLASS = VkBotEvent
155+
156+
def __init__(self, vk, group_id, wait=25):
157+
self.vk = vk
158+
self.group_id = group_id
159+
self.wait = wait
160+
161+
self.url = None
162+
self.key = None
163+
self.server = None
164+
self.ts = None
165+
166+
self.session = requests.Session()
167+
168+
self.update_longpoll_server()
169+
170+
def _parse_event(self, raw_event):
171+
event_class = self.CLASS_BY_EVENT_TYPE.get(
172+
raw_event['type'],
173+
self.DEFAULT_EVENT_CLASS
174+
)
175+
return event_class(raw_event)
176+
177+
def update_longpoll_server(self, update_ts=True):
178+
values = {
179+
'group_id': self.group_id
180+
}
181+
response = self.vk.method('groups.getLongPollServer', values)
182+
183+
self.key = response['key']
184+
self.server = response['server']
185+
186+
self.url = self.server
187+
188+
if update_ts:
189+
self.ts = response['ts']
190+
191+
def check(self):
192+
"""
193+
Получить события от сервера один раз
194+
195+
:returns: `list` of :class:`Event`
196+
"""
197+
198+
values = {
199+
'act': 'a_check',
200+
'key': self.key,
201+
'ts': self.ts,
202+
'wait': self.wait,
203+
}
204+
205+
response = self.session.get(
206+
self.url,
207+
params=values,
208+
timeout=self.wait + 10
209+
).json()
210+
211+
if 'failed' not in response:
212+
self.ts = response['ts']
213+
return [
214+
self._parse_event(raw_event)
215+
for raw_event in response['updates']
216+
]
217+
218+
elif response['failed'] == 1:
219+
self.ts = response['ts']
220+
221+
elif response['failed'] == 2:
222+
self.update_longpoll_server(update_ts=False)
223+
224+
elif response['failed'] == 3:
225+
self.update_longpoll_server()
226+
227+
return []
228+
229+
def listen(self):
230+
"""
231+
Слушать сервер
232+
233+
:yields: :class:`Event`
234+
"""
235+
while True:
236+
for event in self.check():
237+
yield event

0 commit comments

Comments
 (0)