Skip to content

Commit 20024fc

Browse files
authored
Merge branch 'master' into feature/chat-photos
2 parents 812381e + d271711 commit 20024fc

File tree

15 files changed

+528
-14
lines changed

15 files changed

+528
-14
lines changed

botogram/bot.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ def __init__(self, api_connection):
9292
self.register_update_processor("edited_channel_post",
9393
messages.process_channel_post_edited)
9494
self.register_update_processor("callback_query", callbacks.process)
95+
self.register_update_processor("poll", messages.process_poll_update)
9596

9697
self._bot_id = str(uuid.uuid4())
9798

@@ -142,6 +143,11 @@ def process_message(self, func):
142143
self._main_component.add_process_message_hook(func)
143144
return func
144145

146+
def poll_update(self, func):
147+
"""Add a poll update hook"""
148+
self._main_component.add_poll_update_hook(func)
149+
return func
150+
145151
def message_equals(self, string, ignore_case=True):
146152
"""Add a message equals hook"""
147153
def __(func):

botogram/components.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ def __new__(cls, *args, **kwargs):
4747
self.__messages_edited_hooks = []
4848
self.__channel_post_hooks = []
4949
self.__channel_post_edited_hooks = []
50+
self.__poll_update_hooks = []
5051

5152
self._component_id = str(uuid.uuid4())
5253

@@ -75,6 +76,14 @@ def add_process_message_hook(self, func):
7576
hook = hooks.ProcessMessageHook(func, self)
7677
self.__processors.append(hook)
7778

79+
def add_poll_update_hook(self, func):
80+
"""Add a poll update hook"""
81+
if not callable(func):
82+
raise ValueError("A poll update hook must be callable")
83+
84+
hook = hooks.PollUpdateHook(func, self)
85+
self.__poll_update_hooks.append(hook)
86+
7887
def add_message_equals_hook(self, string, func, ignore_case=True):
7988
"""Add a message equals hook"""
8089
if not callable(func):
@@ -220,6 +229,7 @@ def _get_chains(self):
220229
]
221230
return {
222231
"messages": messages,
232+
"poll_updates": [self.__poll_update_hooks],
223233
"memory_preparers": [self.__memory_preparers],
224234
"tasks": [self.__timers],
225235
"chat_unavalable_hooks": [self.__chat_unavailable_hooks],

botogram/frozenbot.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,10 @@ def process_message(self, func):
111111
"""Register a message processor hook"""
112112
raise FrozenBotError("Can't add hooks to a bot at runtime")
113113

114+
def poll_update(self, func):
115+
"""Register a poll update hook"""
116+
raise FrozenBotError("Can't add hooks to a bot at runtime")
117+
114118
def message_equals(self, string, ignore_case=True):
115119
"""Add a message equals hook"""
116120
raise FrozenBotError("Can't add hooks to a bot at runtime")

botogram/hooks.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,13 @@ class ProcessMessageHook(Hook):
8181
pass
8282

8383

84+
class PollUpdateHook(Hook):
85+
"""Underlying hook for @bot.poll_update"""
86+
87+
def _call(self, bot, update):
88+
return bot._call(self.func, self.component_id, poll=update.poll)
89+
90+
8491
class MemoryPreparerHook(Hook):
8592
"""Underlying hook for @bot.prepare_memory"""
8693

botogram/messages.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,19 @@ def process_channel_post_edited(bot, chains, update):
8181

8282
bot.logger.debug("No hook actually processed the #%s update." %
8383
update.update_id)
84+
85+
86+
def process_poll_update(bot, chains, update):
87+
"""Process a poll update"""
88+
for hook in chains["poll_updates"]:
89+
bot.logger.debug("Processing poll update in update #%s with"
90+
"the hook %s..." % (update.update_id, hook.name))
91+
92+
result = hook.call(bot, update)
93+
if result is True:
94+
bot.logger.debug("Update %s was just processed by the %s hook." %
95+
(update.update_id, hook.name))
96+
return
97+
98+
bot.logger.debug("No hook actually processed the #%s update." %
99+
update.update_id)

botogram/objects/__init__.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,12 @@
2222

2323
from .chats import User, Chat, UserProfilePhotos, Permissions
2424
from .media import PhotoSize, Photo, Audio, Voice, Document, Sticker, \
25-
Video, VideoNote, Contact, Location, Venue
25+
Video, VideoNote, Animation, Contact, Location, Venue
2626
from .messages import Message
2727
from .markup import ReplyKeyboardMarkup, ReplyKeyboardHide, ForceReply
28+
from .polls import Poll, PollOption
2829
from .updates import Update, Updates
29-
from .mixins import Album
30+
from .mixins import Album
3031

3132
__all__ = [
3233
# Chats-related objects
@@ -43,6 +44,7 @@
4344
"Sticker",
4445
"Video",
4546
"VideoNote",
47+
"Animation",
4648
"Contact",
4749
"Location",
4850
"Venue",
@@ -56,6 +58,10 @@
5658
"ReplyKeyboardHide",
5759
"ForceReply",
5860

61+
# Polls-related objects
62+
"Poll",
63+
"PollOption",
64+
5965
# Updates-related objects
6066
"Update",
6167
"Updates",

botogram/objects/media.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,27 @@ class Video(BaseObject, mixins.FileMixin):
213213
_check_equality_ = "file_id"
214214

215215

216+
class Animation(BaseObject, mixins.FileMixin):
217+
"""Telegram API representation of an animation
218+
219+
https://core.telegram.org/bots/api#animation
220+
"""
221+
222+
required = {
223+
"file_id": str,
224+
"width": int,
225+
"height": int,
226+
"duration": int,
227+
}
228+
optional = {
229+
"thumb": PhotoSize,
230+
"file_name": str,
231+
"mime_type": str,
232+
"file_size": int,
233+
}
234+
_check_equality_ = "file_id"
235+
236+
216237
class Contact(BaseObject):
217238
"""Telegram API representation of a contact
218239

botogram/objects/messages.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@
2525
from .. import utils
2626
from .chats import User, Chat
2727
from .media import Audio, Voice, Document, Photo, Sticker, Video, VideoNote, \
28-
Contact, Location, Venue
28+
Animation, Contact, Location, Venue
29+
from .polls import Poll
2930

3031

3132
_url_protocol_re = re.compile(r"^https?:\/\/|s?ftp:\/\/|mailto:", re.I)
@@ -346,10 +347,12 @@ def from_(self):
346347
"sticker": Sticker,
347348
"video": Video,
348349
"video_note": VideoNote,
350+
"animation": Animation,
349351
"caption": str,
350352
"contact": Contact,
351353
"location": Location,
352354
"venue": Venue,
355+
"poll": Poll,
353356
"new_chat_member": User,
354357
"left_chat_member": User,
355358
"new_chat_title": str,

botogram/objects/mixins.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,37 @@ def send_video_note(self, path=None, file_id=None, duration=None,
241241
return self._api.call("sendVideoNote", args, files,
242242
expect=_objects().Message)
243243

244+
@_require_api
245+
def send_gif(self, path=None, file_id=None, url=None, duration=None,
246+
width=None, height=None, caption=None, thumb=None,
247+
reply_to=None, extra=None, attach=None,
248+
notify=True, syntax=None):
249+
"""Send an animation"""
250+
args = self._get_call_args(reply_to, extra, attach, notify)
251+
if duration is not None:
252+
args["duration"] = duration
253+
if caption is not None:
254+
args["caption"] = caption
255+
if syntax is not None:
256+
syntax = syntaxes.guess_syntax(caption, syntax)
257+
args["parse_mode"] = syntax
258+
if width is not None:
259+
args["width"] = width
260+
if height is not None:
261+
args["height"] = height
262+
263+
files = dict()
264+
args["animation"], files["animation"] = self._get_file_args(path,
265+
file_id,
266+
url)
267+
if files["animation"] is None:
268+
del files["animation"]
269+
if thumb is not None:
270+
files["thumb"] = thumb
271+
272+
return self._api.call("sendAnimation", args, files,
273+
expect=_objects().Message)
274+
244275
@_require_api
245276
def send_file(self, path=None, file_id=None, url=None, thumb=None,
246277
reply_to=None, extra=None, attach=None,
@@ -336,6 +367,16 @@ def send_contact(self, phone, first_name, last_name=None, *, reply_to=None,
336367

337368
return self._api.call("sendContact", args, expect=_objects().Message)
338369

370+
@_require_api
371+
def send_poll(self, question, *kargs, reply_to=None, extra=None,
372+
attach=None, notify=True):
373+
"""Send a poll"""
374+
args = self._get_call_args(reply_to, extra, attach, notify)
375+
args["question"] = question
376+
args["options"] = json.dumps(list(kargs))
377+
378+
return self._api.call("sendPoll", args, expect=_objects().Message)
379+
339380
@_require_api
340381
def delete_message(self, message):
341382
"""Delete a message from chat"""
@@ -526,6 +567,10 @@ def reply_with_video_note(self, *args, **kwargs):
526567
"""Reply with a video note to the current message"""
527568
return self.chat.send_video_note(*args, reply_to=self, **kwargs)
528569

570+
@_require_api
571+
def reply_with_gif(self, *args, **kwargs):
572+
return self.chat.send_gif(*args, reply_to=self, **kwargs)
573+
529574
@_require_api
530575
def reply_with_file(self, *args, **kwargs):
531576
"""Reply with a generic file to the current chat"""
@@ -556,6 +601,11 @@ def reply_with_album(self, *args, **kwargs):
556601
"""Reply with an album to the current message"""
557602
return self.chat.send_album(*args, reply_to=self, **kwargs)
558603

604+
@_require_api
605+
def reply_with_poll(self, *args, **kwargs):
606+
"""Reply with a poll to the current message"""
607+
return self.chat.send_poll(*args, reply_to=self, **kwargs)
608+
559609
@_require_api
560610
def delete(self):
561611
"""Delete the message"""
@@ -564,6 +614,27 @@ def delete(self):
564614
"message_id": self.id,
565615
})
566616

617+
@_require_api
618+
def stop_poll(self, extra=None, attach=None):
619+
"""Stops a poll"""
620+
args = dict()
621+
args["chat_id"] = self.chat.id
622+
args["message_id"] = self.id
623+
624+
if extra is not None:
625+
_deprecated_message(
626+
"The extra parameter", "1.0", "use the attach parameter", -3
627+
)
628+
args["reply_markup"] = json.dumps(extra.serialize())
629+
if attach is not None:
630+
if not hasattr(attach, "_serialize_attachment"):
631+
raise ValueError("%s is not an attachment" % attach)
632+
args["reply_markup"] = json.dumps(attach._serialize_attachment(
633+
self.chat
634+
))
635+
return self._api.call("stopPoll", args,
636+
expect=_objects().Poll)
637+
567638

568639
class FileMixin:
569640
"""Add some methods for files"""

botogram/objects/polls.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Copyright (c) 2015-2019 The Botogram Authors (see AUTHORS)
2+
#
3+
# Permission is hereby granted, free of charge, to any person obtaining a copy
4+
# of this software and associated documentation files (the "Software"), to deal
5+
# in the Software without restriction, including without limitation the rights
6+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
# copies of the Software, and to permit persons to whom the Software is
8+
# furnished to do so, subject to the following conditions:
9+
#
10+
# The above copyright notice and this permission notice shall be included in
11+
# all copies or substantial portions of the Software.
12+
#
13+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18+
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19+
# DEALINGS IN THE SOFTWARE.
20+
21+
from .base import BaseObject, multiple
22+
23+
24+
class PollOption(BaseObject):
25+
required = {
26+
"text": str,
27+
"voter_count": int,
28+
}
29+
30+
31+
class Poll(BaseObject):
32+
required = {
33+
"id": str,
34+
"question": str,
35+
"options": multiple(PollOption),
36+
"is_closed": bool,
37+
}

0 commit comments

Comments
 (0)