Implemented polls [From test API]

This commit is contained in:
Alex RootJunior 2019-04-11 00:32:46 +03:00
parent 2a6f36b19b
commit e2e2d9c9fe
10 changed files with 107 additions and 7 deletions

View file

@ -38,5 +38,5 @@ __all__ = [
'utils' 'utils'
] ]
__version__ = '2.0.2.dev1' __version__ = '2.1.dev1'
__api_version__ = '4.1' __api_version__ = '4.1'

View file

@ -229,6 +229,10 @@ class Methods(Helper):
SET_GAME_SCORE = Item() # setGameScore SET_GAME_SCORE = Item() # setGameScore
GET_GAME_HIGH_SCORES = Item() # getGameHighScores GET_GAME_HIGH_SCORES = Item() # getGameHighScores
# Polls
SEND_POLL = Item()
STOP_POLL = Item()
@staticmethod @staticmethod
def api_url(token, method): def api_url(token, method):
""" """

View file

@ -2056,3 +2056,20 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
result = await self.request(api.Methods.GET_GAME_HIGH_SCORES, payload) result = await self.request(api.Methods.GET_GAME_HIGH_SCORES, payload)
return [types.GameHighScore(**gamehighscore) for gamehighscore in result] return [types.GameHighScore(**gamehighscore) for gamehighscore in result]
async def send_poll(self, chat_id: typing.Union[base.Integer, base.String],
question: base.String,
options: typing.List[base.String],
reply_to_message_id: typing.Union[base.Integer, None]):
options = prepare_arg(options)
payload = generate_payload(**locals())
result = await self.request(api.Methods.SEND_POLL, payload)
return types.Message(**result)
async def stop_poll(self, chat_id: typing.Union[base.String, base.Integer],
message_id: base.Integer) -> types.Poll:
payload = generate_payload(**locals())
result = await self.request(api.Methods.STOP_POLL, payload)
return types.Poll(**result)

View file

@ -64,6 +64,7 @@ class Dispatcher(DataMixin, ContextInstanceMixin):
self.callback_query_handlers = Handler(self, middleware_key='callback_query') self.callback_query_handlers = Handler(self, middleware_key='callback_query')
self.shipping_query_handlers = Handler(self, middleware_key='shipping_query') self.shipping_query_handlers = Handler(self, middleware_key='shipping_query')
self.pre_checkout_query_handlers = Handler(self, middleware_key='pre_checkout_query') self.pre_checkout_query_handlers = Handler(self, middleware_key='pre_checkout_query')
self.poll_handlers = Handler(self, middleware_key='poll')
self.errors_handlers = Handler(self, once=False, middleware_key='error') self.errors_handlers = Handler(self, once=False, middleware_key='error')
self.middleware = MiddlewareManager(self) self.middleware = MiddlewareManager(self)
@ -80,7 +81,8 @@ class Dispatcher(DataMixin, ContextInstanceMixin):
filters_factory = self.filters_factory filters_factory = self.filters_factory
filters_factory.bind(StateFilter, exclude_event_handlers=[ filters_factory.bind(StateFilter, exclude_event_handlers=[
self.errors_handlers self.errors_handlers,
self.poll_handlers
]) ])
filters_factory.bind(ContentTypeFilter, event_handlers=[ filters_factory.bind(ContentTypeFilter, event_handlers=[
self.message_handlers, self.edited_message_handlers, self.message_handlers, self.edited_message_handlers,
@ -92,7 +94,7 @@ class Dispatcher(DataMixin, ContextInstanceMixin):
filters_factory.bind(Text, event_handlers=[ filters_factory.bind(Text, event_handlers=[
self.message_handlers, self.edited_message_handlers, self.message_handlers, self.edited_message_handlers,
self.channel_post_handlers, self.edited_channel_post_handlers, self.channel_post_handlers, self.edited_channel_post_handlers,
self.callback_query_handlers self.callback_query_handlers, self.poll_handlers
]) ])
filters_factory.bind(HashTag, event_handlers=[ filters_factory.bind(HashTag, event_handlers=[
self.message_handlers, self.edited_message_handlers, self.message_handlers, self.edited_message_handlers,
@ -101,7 +103,7 @@ class Dispatcher(DataMixin, ContextInstanceMixin):
filters_factory.bind(Regexp, event_handlers=[ filters_factory.bind(Regexp, event_handlers=[
self.message_handlers, self.edited_message_handlers, self.message_handlers, self.edited_message_handlers,
self.channel_post_handlers, self.edited_channel_post_handlers, self.channel_post_handlers, self.edited_channel_post_handlers,
self.callback_query_handlers self.callback_query_handlers, self.poll_handlers
]) ])
filters_factory.bind(RegexpCommandsFilter, event_handlers=[ filters_factory.bind(RegexpCommandsFilter, event_handlers=[
self.message_handlers, self.edited_message_handlers self.message_handlers, self.edited_message_handlers
@ -185,6 +187,8 @@ class Dispatcher(DataMixin, ContextInstanceMixin):
if update.pre_checkout_query: if update.pre_checkout_query:
types.User.set_current(update.pre_checkout_query.from_user) types.User.set_current(update.pre_checkout_query.from_user)
return await self.pre_checkout_query_handlers.notify(update.pre_checkout_query) return await self.pre_checkout_query_handlers.notify(update.pre_checkout_query)
if update.poll:
return await self.poll_handlers.notify(update.poll)
except Exception as e: except Exception as e:
err = await self.errors_handlers.notify(update, e) err = await self.errors_handlers.notify(update, e)
if err: if err:
@ -796,6 +800,20 @@ class Dispatcher(DataMixin, ContextInstanceMixin):
return decorator return decorator
def register_poll_handler(self, callback, *custom_filters, run_task=None, **kwargs):
filters_set = self.filters_factory.resolve(self.poll_handlers,
*custom_filters,
**kwargs)
self.poll_handlers.register(self._wrap_async_task(callback, run_task), filters_set)
def poll_handler(self, *custom_filters, run_task=None, **kwargs):
def decorator(callback):
self.register_poll_handler(callback, *custom_filters, run_task=run_task,
**kwargs)
return callback
return decorator
def register_errors_handler(self, callback, *custom_filters, exception=None, run_task=None, **kwargs): def register_errors_handler(self, callback, *custom_filters, exception=None, run_task=None, **kwargs):
""" """
Register handler for errors Register handler for errors

View file

@ -8,7 +8,7 @@ from babel.support import LazyProxy
from aiogram import types from aiogram import types
from aiogram.dispatcher.filters.filters import BoundFilter, Filter from aiogram.dispatcher.filters.filters import BoundFilter, Filter
from aiogram.types import CallbackQuery, Message, InlineQuery from aiogram.types import CallbackQuery, Message, InlineQuery, Poll
from aiogram.utils.deprecated import warn_deprecated from aiogram.utils.deprecated import warn_deprecated
@ -164,10 +164,14 @@ class Text(Filter):
async def check(self, obj: Union[Message, CallbackQuery, InlineQuery]): async def check(self, obj: Union[Message, CallbackQuery, InlineQuery]):
if isinstance(obj, Message): if isinstance(obj, Message):
text = obj.text or obj.caption or '' text = obj.text or obj.caption or ''
if not text and obj.poll:
text = obj.poll.question
elif isinstance(obj, CallbackQuery): elif isinstance(obj, CallbackQuery):
text = obj.data text = obj.data
elif isinstance(obj, InlineQuery): elif isinstance(obj, InlineQuery):
text = obj.query text = obj.query
elif isinstance(obj, Poll):
text = obj.question
else: else:
return False return False
@ -269,12 +273,16 @@ class Regexp(Filter):
async def check(self, obj: Union[Message, CallbackQuery]): async def check(self, obj: Union[Message, CallbackQuery]):
if isinstance(obj, Message): if isinstance(obj, Message):
match = self.regexp.search(obj.text or obj.caption or '') content = obj.text or obj.caption or ''
if not content and obj.poll:
content = obj.poll.question
elif isinstance(obj, CallbackQuery) and obj.data: elif isinstance(obj, CallbackQuery) and obj.data:
match = self.regexp.search(obj.data) content = obj.data
else: else:
return False return False
match = self.regexp.search(content)
if match: if match:
return {'regexp': match} return {'regexp': match}
return False return False

View file

@ -43,6 +43,7 @@ from .passport_element_error import PassportElementError, PassportElementErrorDa
PassportElementErrorSelfie PassportElementErrorSelfie
from .passport_file import PassportFile from .passport_file import PassportFile
from .photo_size import PhotoSize from .photo_size import PhotoSize
from .poll import PollOptions, Poll
from .pre_checkout_query import PreCheckoutQuery from .pre_checkout_query import PreCheckoutQuery
from .reply_keyboard import KeyboardButton, ReplyKeyboardMarkup, ReplyKeyboardRemove from .reply_keyboard import KeyboardButton, ReplyKeyboardMarkup, ReplyKeyboardRemove
from .response_parameters import ResponseParameters from .response_parameters import ResponseParameters
@ -142,6 +143,8 @@ __all__ = (
'PassportElementErrorSelfie', 'PassportElementErrorSelfie',
'PassportFile', 'PassportFile',
'PhotoSize', 'PhotoSize',
'Poll',
'PollOptions',
'PreCheckoutQuery', 'PreCheckoutQuery',
'ReplyKeyboardMarkup', 'ReplyKeyboardMarkup',
'ReplyKeyboardRemove', 'ReplyKeyboardRemove',

View file

@ -19,6 +19,7 @@ from .location import Location
from .message_entity import MessageEntity from .message_entity import MessageEntity
from .passport_data import PassportData from .passport_data import PassportData
from .photo_size import PhotoSize from .photo_size import PhotoSize
from .poll import Poll
from .sticker import Sticker from .sticker import Sticker
from .successful_payment import SuccessfulPayment from .successful_payment import SuccessfulPayment
from .user import User from .user import User
@ -81,6 +82,7 @@ class Message(base.TelegramObject):
successful_payment: SuccessfulPayment = fields.Field(base=SuccessfulPayment) successful_payment: SuccessfulPayment = fields.Field(base=SuccessfulPayment)
connected_website: base.String = fields.Field() connected_website: base.String = fields.Field()
passport_data: PassportData = fields.Field(base=PassportData) passport_data: PassportData = fields.Field(base=PassportData)
poll: Poll = fields.Field(base=Poll)
@property @property
@functools.lru_cache() @functools.lru_cache()
@ -137,6 +139,8 @@ class Message(base.TelegramObject):
return ContentType.GROUP_CHAT_CREATED return ContentType.GROUP_CHAT_CREATED
elif self.passport_data: elif self.passport_data:
return ContentType.PASSPORT_DATA return ContentType.PASSPORT_DATA
elif self.poll:
return ContentType.POLL
else: else:
return ContentType.UNKNOWN return ContentType.UNKNOWN
@ -1539,6 +1543,7 @@ class ContentType(helper.Helper):
DELETE_CHAT_PHOTO = helper.Item() # delete_chat_photo DELETE_CHAT_PHOTO = helper.Item() # delete_chat_photo
GROUP_CHAT_CREATED = helper.Item() # group_chat_created GROUP_CHAT_CREATED = helper.Item() # group_chat_created
PASSPORT_DATA = helper.Item() # passport_data PASSPORT_DATA = helper.Item() # passport_data
POLL = helper.Item()
UNKNOWN = helper.Item() # unknown UNKNOWN = helper.Item() # unknown
ANY = helper.Item() # any ANY = helper.Item() # any
@ -1600,6 +1605,7 @@ class ContentTypes(helper.Helper):
DELETE_CHAT_PHOTO = helper.ListItem() # delete_chat_photo DELETE_CHAT_PHOTO = helper.ListItem() # delete_chat_photo
GROUP_CHAT_CREATED = helper.ListItem() # group_chat_created GROUP_CHAT_CREATED = helper.ListItem() # group_chat_created
PASSPORT_DATA = helper.ListItem() # passport_data PASSPORT_DATA = helper.ListItem() # passport_data
POLL = helper.ListItem()
UNKNOWN = helper.ListItem() # unknown UNKNOWN = helper.ListItem() # unknown
ANY = helper.ListItem() # any ANY = helper.ListItem() # any

16
aiogram/types/poll.py Normal file
View file

@ -0,0 +1,16 @@
import typing
from . import base
from . import fields
class PollOptions(base.TelegramObject):
text: base.String = fields.Field()
voter_count: base.Integer = fields.Field()
class Poll(base.TelegramObject):
id: base.String = fields.Field()
question: base.String = fields.Field()
options: typing.List[PollOptions] = fields.ListField(base=PollOptions)
is_closed: base.Boolean = fields.Field()

View file

@ -6,6 +6,7 @@ from .callback_query import CallbackQuery
from .chosen_inline_result import ChosenInlineResult from .chosen_inline_result import ChosenInlineResult
from .inline_query import InlineQuery from .inline_query import InlineQuery
from .message import Message from .message import Message
from .poll import Poll
from .pre_checkout_query import PreCheckoutQuery from .pre_checkout_query import PreCheckoutQuery
from .shipping_query import ShippingQuery from .shipping_query import ShippingQuery
from ..utils import helper from ..utils import helper
@ -28,6 +29,7 @@ class Update(base.TelegramObject):
callback_query: CallbackQuery = fields.Field(base=CallbackQuery) callback_query: CallbackQuery = fields.Field(base=CallbackQuery)
shipping_query: ShippingQuery = fields.Field(base=ShippingQuery) shipping_query: ShippingQuery = fields.Field(base=ShippingQuery)
pre_checkout_query: PreCheckoutQuery = fields.Field(base=PreCheckoutQuery) pre_checkout_query: PreCheckoutQuery = fields.Field(base=PreCheckoutQuery)
poll: Poll = fields.Field(base=Poll)
def __hash__(self): def __hash__(self):
return self.update_id return self.update_id

View file

@ -7,12 +7,16 @@ TelegramAPIError
MessageNotModified MessageNotModified
MessageToForwardNotFound MessageToForwardNotFound
MessageToDeleteNotFound MessageToDeleteNotFound
MessageWithPollNotFound
MessageIsNotAPoll
MessageIdentifierNotSpecified MessageIdentifierNotSpecified
MessageTextIsEmpty MessageTextIsEmpty
MessageCantBeEdited MessageCantBeEdited
MessageCantBeDeleted MessageCantBeDeleted
MessageToEditNotFound MessageToEditNotFound
ToMuchMessages ToMuchMessages
PollCantBeStopped
PollHasAlreadyClosed
ObjectExpectedAsReplyMarkup ObjectExpectedAsReplyMarkup
InlineKeyboardExpected InlineKeyboardExpected
ChatNotFound ChatNotFound
@ -164,6 +168,20 @@ class MessageToDeleteNotFound(MessageError):
match = 'message to delete not found' match = 'message to delete not found'
class MessageWithPollNotFound(MessageError):
"""
Will be raised when you try to stop poll with message without poll
"""
match = 'message with poll to stop not found'
class MessageIsNotAPoll(MessageError):
"""
Will be raised when you try to stop poll with message without poll
"""
match = 'message is not a poll'
class MessageIdentifierNotSpecified(MessageError): class MessageIdentifierNotSpecified(MessageError):
match = 'message identifier is not specified' match = 'message identifier is not specified'
@ -203,6 +221,14 @@ class InlineKeyboardExpected(BadRequest):
match = 'inline keyboard expected' match = 'inline keyboard expected'
class PollCantBeStopped(BadRequest):
match = "poll can't be stopped"
class PollHasAlreadyBeenClosed(BadRequest):
match = 'poll has already been closed'
class ChatNotFound(BadRequest): class ChatNotFound(BadRequest):
match = 'chat not found' match = 'chat not found'