Skip to content

Commit 0a956ed

Browse files
committed
Add support and documentation for polls
1 parent 07f05dd commit 0a956ed

File tree

5 files changed

+186
-9
lines changed

5 files changed

+186
-9
lines changed

botogram/objects/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
Video, VideoNote, 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
2930
from .mixins import Album
3031

@@ -56,6 +57,10 @@
5657
"ReplyKeyboardHide",
5758
"ForceReply",
5859

60+
# Polls-related objects
61+
"Poll",
62+
"PollOption",
63+
5964
# Updates-related objects
6065
"Update",
6166
"Updates",

botogram/objects/messages.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
from .chats import User, Chat
2727
from .media import Audio, Voice, Document, Photo, Sticker, Video, VideoNote, \
2828
Contact, Location, Venue
29+
from .polls import Poll
2930

3031

3132
_url_protocol_re = re.compile(r"^https?:\/\/|s?ftp:\/\/|mailto:", re.I)
@@ -348,6 +349,7 @@ def from_(self):
348349
"contact": Contact,
349350
"location": Location,
350351
"venue": Venue,
352+
"poll": Poll,
351353
"new_chat_member": User,
352354
"left_chat_member": User,
353355
"new_chat_title": str,

botogram/objects/mixins.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,16 @@ def send_contact(self, phone, first_name, last_name=None, *, reply_to=None,
343343

344344
return self._api.call("sendContact", args, expect=_objects().Message)
345345

346+
@_require_api
347+
def send_poll(self, question, *kargs, reply_to=None, extra=None,
348+
attach=None, notify=True):
349+
"""Send a poll"""
350+
args = self._get_call_args(reply_to, extra, attach, notify)
351+
args["question"] = question
352+
args["options"] = json.dumps(list(kargs))
353+
354+
return self._api.call("sendPoll", args, expect=_objects().Message)
355+
346356
@_require_api
347357
def delete_message(self, message):
348358
"""Delete a message from chat"""
@@ -505,6 +515,11 @@ def reply_with_album(self, *args, **kwargs):
505515
"""Reply with an album to the current message"""
506516
return self.chat.send_album(*args, reply_to=self, **kwargs)
507517

518+
@_require_api
519+
def reply_with_poll(self, *args, **kwargs):
520+
"""Reply with a poll to the current message"""
521+
return self.chat.send_poll(*args, reply_to=self, **kwargs)
522+
508523
@_require_api
509524
def delete(self):
510525
"""Delete the message"""
@@ -513,6 +528,27 @@ def delete(self):
513528
"message_id": self.id,
514529
})
515530

531+
@_require_api
532+
def stop_poll(self, extra=None, attach=None):
533+
"""Stops a poll"""
534+
args = dict()
535+
args["chat_id"] = self.chat.id
536+
args["message_id"] = self.id
537+
538+
if extra is not None:
539+
_deprecated_message(
540+
"The extra parameter", "1.0", "use the attach parameter", -3
541+
)
542+
args["reply_markup"] = json.dumps(extra.serialize())
543+
if attach is not None:
544+
if not hasattr(attach, "_serialize_attachment"):
545+
raise ValueError("%s is not an attachment" % attach)
546+
args["reply_markup"] = json.dumps(attach._serialize_attachment(
547+
self.chat
548+
))
549+
return self._api.call("stopPoll", args,
550+
expect=_objects().Poll)
551+
516552

517553
class FileMixin:
518554
"""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+
}

docs/api/telegram.rst

Lines changed: 106 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1375,7 +1375,6 @@ about its business.
13751375
The *notify* parameter defines if your message should
13761376
trigger the notification on the client side (yes by default).
13771377

1378-
13791378
.. code-block:: python
13801379
@bot.command("my_cats")
13811380
def my_cats(chat):
@@ -1394,12 +1393,36 @@ about its business.
13941393
:param album: The :py:class:`~botogram.Album` send to the chat
13951394
:param int reply_to: The ID of the :py:class:`~botogram.Message` this one is replying to.
13961395
:param bool notify: If you want to trigger a notification on the client
1397-
13981396
:returns: The messages you sent
13991397
:rtype: list of :py:class:`~botogram.Message`
14001398

14011399
.. versionadded:: 0.6
14021400

1401+
.. py:method:: send_poll(question, *options, [reply_to=None, extra=None, attach=None, notify=True])
1402+
1403+
Send a poll to the chat. A Telegram poll is made by a question and a list of options
1404+
(you can specify them as arguments).
1405+
1406+
.. code-block:: python
1407+
1408+
@bot.command("latest_poll")
1409+
def latest_poll(chat, message, args):
1410+
chat.send("This is our last poll, please answer honestly!")
1411+
chat.send_poll("What's your favorite color?", "Red", "Green", "Blue")
1412+
# Or, alternate syntax:
1413+
chat.send_poll("What's your favorite color?", *["Red", "Green", "Blue"])
1414+
1415+
:param str question: Poll question, 1-255 characters
1416+
:param *str options: List of answer options, 2-10 string of 1-100 characters each
1417+
:param int reply_to: The ID of the :py:class:`~botogram.Message` this one is replying to
1418+
:param object attach: An extra thing to attach to the message
1419+
:param object extra: An extra reply interface object to attach
1420+
:param bool notify: If you want to trigger a notification on the client
1421+
:returns: The message you sent
1422+
:rtype: ~botogram.Message
1423+
1424+
.. versionadded:: 0.7
1425+
14031426
.. py:method:: delete_message(message)
14041427
14051428
Delete the message with the provided ID or :py:class:`~botogram.Message` object.
@@ -2214,22 +2237,20 @@ about its business.
22142237

22152238
.. versionadded:: 0.3
22162239

2217-
22182240
.. py:method:: reply_with_album([album=None, notify=True])
22192241
2220-
Send album to the chat. This method returns an instance of :py:class:`~botogram.Album` or sends the :py:class:`~botogram.Album` provided by the album variable.
2242+
Reply to this message with an album. This method returns an instance of :py:class:`~botogram.Album` or sends the :py:class:`~botogram.Album` provided by the album variable.
22212243
The *notify* parameter defines if your message should
22222244
trigger the notification on the client side (yes by default).
22232245

2224-
22252246
.. code-block:: python
22262247
@bot.command("my_cats")
22272248
def my_cats(message):
22282249
album = botogram.Album()
22292250
album.add_photo('tiger.jpg', caption='<b>Tiger</b>, the father', syntax='HTML')
22302251
album.add_photo(url='https://http.cat/100.jpg', caption='Simba, the cat-mother of the year!')
22312252
album.add_photo(file_id='some file ID here', caption='...and Sassy the daughter')
2232-
message.reply_with_album(album)
2253+
message.reply_with_album(album)
22332254
22342255
@bot.command("my_dogs")
22352256
def my_dogs(message):
@@ -2238,14 +2259,57 @@ about its business.
22382259
album.add_photo('shilla.jpg', caption='Shilla is so jealous!')
22392260
22402261
:param album: The :py:class:`~botogram.Album` send to the chat
2241-
22422262
:param bool notify: If you want to trigger a notification on the client
2243-
22442263
:returns: The messages you sent
22452264
:rtype: list of :py:class:`~botogram.Message`
22462265

22472266
.. versionadded:: 0.6
22482267

2268+
.. py:method:: reply_with_poll(question, *options, [extra=None, attach=None, notify=True])
2269+
2270+
Reply to this message with a poll. A Telegram poll is made by a question and a list of options
2271+
(you can specify them as arguments).
2272+
2273+
.. code-block:: python
2274+
2275+
@bot.command("latest_poll")
2276+
def latest_poll(chat, message, args):
2277+
chat.send("This is our last poll, please answer honestly!")
2278+
message.reply_with_poll("What's your favorite color?", "Red", "Green", "Blue")
2279+
# Or, alternate syntax:
2280+
message.reply_with_poll("What's your favorite color?", *["Red", "Green", "Blue"])
2281+
2282+
:param str question: Poll question, 1-255 characters
2283+
:param *str options: List of answer options, 2-10 string of 1-100 characters each
2284+
:param object attach: An extra thing to attach to the message
2285+
:param object extra: An extra reply interface object to attach
2286+
:param bool notify: If you want to trigger a notification on the client
2287+
:returns: The message you sent
2288+
:rtype: ~botogram.Message
2289+
2290+
.. versionadded:: 0.7
2291+
2292+
.. py:method:: stop_poll([extra=None, attach=None])
2293+
2294+
Stop a poll sent by a bot.
2295+
2296+
.. code-block:: python
2297+
import time
2298+
2299+
@bot.command("quiz")
2300+
def latest_quiz(chat, message, args):
2301+
chat.send("Please answer within 10 seconds!")
2302+
msg = chat.send_poll("What's the capital of Italy?", "Milan", "Rome", "New York")
2303+
time.sleep(10)
2304+
poll = msg.stop_poll()
2305+
chat.send("{n} correct answers!".format(n=poll.options[1].voter_count))
2306+
2307+
:param object attach: An extra thing to attach to the message
2308+
:param object extra: An extra reply interface object to attach
2309+
:returns: The stopped poll with the final results
2310+
:rtype: ~botogram.Poll
2311+
2312+
.. versionadded:: 0.7
22492313

22502314
.. py:class:: botogram.Photo
22512315
@@ -2700,7 +2764,6 @@ about its business.
27002764

27012765
.. py:class:: botogram.Album
27022766
2703-
27042767
This object represents an album (a group of photos and videos).
27052768

27062769
.. py:method:: add_photo([path=None, url=None, file_id=None, caption=None, syntax=None])
@@ -2733,6 +2796,40 @@ about its business.
27332796
.. versionadded:: 0.6
27342797

27352798

2799+
.. py:class:: botogram.Poll
2800+
2801+
This class represents a poll.
2802+
2803+
.. py:attribute:: id
2804+
2805+
The unique poll string identifier.
2806+
2807+
.. py:attribute:: question
2808+
2809+
The poll question.
2810+
2811+
.. py:attribute:: options
2812+
2813+
A list of :py:class:`~botogram.PollOption` identifying the poll options.
2814+
2815+
.. py:attribute:: is_closed
2816+
2817+
A boolean indicating if the poll is closed.
2818+
2819+
2820+
.. py:class:: botogram.PollOption
2821+
2822+
This class represents a :py:class:`~botogram.Poll` option.
2823+
2824+
.. py:attribute:: text
2825+
2826+
The poll option text.
2827+
2828+
.. py:attribute:: voter_count
2829+
2830+
Number of users that voted for this option.
2831+
2832+
27362833
.. py:class:: botogram.Update
27372834
27382835
This class represents an update received by the bot. You should not need to

0 commit comments

Comments
 (0)