mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2026-06-23 09:45:30 +00:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 38a33581b1 | |||
| fe821c08e6 | |||
| 0a9f4bfbdd | |||
| c4364c7166 | |||
| d63e710784 | |||
| f379f54d5a | |||
| bdf0cb91f3 | |||
| 3101ea8432 | |||
| beb8ba3db0 | |||
| f0b1aeb6fd | |||
| d65558888e | |||
| 61a66a32c8 |
@@ -0,0 +1,8 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Telegram Group
|
||||
url: https://telegram.me/pythontelegrambotgroup
|
||||
about: Questions asked on the group usually get answered faster.
|
||||
- name: IRC Channel
|
||||
url: https://webchat.freenode.net/?channels=##python-telegram-bot
|
||||
about: In case you are unable to join our group due to Telegram restrictions, you can use our IRC channel
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
test-build: True
|
||||
fail-fast: False
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v2
|
||||
- name: Initialize vendored libs
|
||||
run:
|
||||
git submodule update --init --recursive
|
||||
@@ -73,7 +73,7 @@ jobs:
|
||||
os: [ubuntu-latest]
|
||||
fail-fast: False
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v2
|
||||
- name: Initialize vendored libs
|
||||
run:
|
||||
git submodule update --init --recursive
|
||||
@@ -102,7 +102,7 @@ jobs:
|
||||
os: [ubuntu-latest]
|
||||
fail-fast: False
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v2
|
||||
- name: Initialize vendored libs
|
||||
run:
|
||||
git submodule update --init --recursive
|
||||
|
||||
@@ -37,6 +37,7 @@ The following wonderful people contributed directly or indirectly to this projec
|
||||
- `evgfilim1 <https://github.com/evgfilim1>`_
|
||||
- `franciscod <https://github.com/franciscod>`_
|
||||
- `gamgi <https://github.com/gamgi>`_
|
||||
- `Harshil <https://github.com/harshil21>`_
|
||||
- `Hugo Damer <https://github.com/HakimusGIT>`_
|
||||
- `ihoru <https://github.com/ihoru>`_
|
||||
- `Jasmin Bom <https://github.com/jsmnbom>`_
|
||||
|
||||
+29
@@ -2,6 +2,35 @@
|
||||
Changelog
|
||||
=========
|
||||
|
||||
Version 12.6
|
||||
============
|
||||
*Released 2020-04-10*
|
||||
|
||||
**Major Changes:**
|
||||
|
||||
- Bot API 4.7 support. **Note:** In ``Bot.create_new_sticker_set`` and ``Bot.add_sticker_to_set``, the order of the parameters had be changed, as the ``png_sticker`` parameter is now optional. (`#1858`_)
|
||||
|
||||
**Minor changes, CI improvements or bug fixes:**
|
||||
|
||||
- Add tests for ``swtich_inline_query(_current_chat)`` with empty string (`#1635`_)
|
||||
- Doc fixes (`#1854`_, `#1874`_, `#1884`_)
|
||||
- Update issue templates (`#1880`_)
|
||||
- Favor concrete types over "Iterable" (`#1882`_)
|
||||
- Pass last valid ``CallbackContext`` to ``TIMEOUT`` handlers of ``ConversationHandler`` (`#1826`_)
|
||||
- Tweak handling of persistence and update persistence after job calls (`#1827`_)
|
||||
- Use checkout@v2 for GitHub actions (`#1887`_)
|
||||
|
||||
.. _`#1858`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1858
|
||||
.. _`#1635`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1635
|
||||
.. _`#1854`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1854
|
||||
.. _`#1874`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1874
|
||||
.. _`#1884`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1884
|
||||
.. _`#1880`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1880
|
||||
.. _`#1882`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1882
|
||||
.. _`#1826`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1826
|
||||
.. _`#1827`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1827
|
||||
.. _`#1887`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1887
|
||||
|
||||
Version 12.5.1
|
||||
==============
|
||||
*Released 2020-03-30*
|
||||
|
||||
+1
-1
@@ -93,7 +93,7 @@ make the development of bots easy and straightforward. These classes are contain
|
||||
Telegram API support
|
||||
====================
|
||||
|
||||
All types and methods of the Telegram Bot API **4.6** are supported.
|
||||
All types and methods of the Telegram Bot API **4.7** are supported.
|
||||
|
||||
==========
|
||||
Installing
|
||||
|
||||
+2
-2
@@ -58,9 +58,9 @@ author = u'Leandro Toledo'
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '12.5' # telegram.__version__[:3]
|
||||
version = '12.6' # telegram.__version__[:3]
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '12.5.1' # telegram.__version__
|
||||
release = '12.6' # telegram.__version__
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
telegram.BotCommand
|
||||
===================
|
||||
|
||||
.. autoclass:: telegram.BotCommand
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -0,0 +1,6 @@
|
||||
telegram.Dice
|
||||
=============
|
||||
|
||||
.. autoclass:: telegram.Dice
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -9,6 +9,7 @@ telegram package
|
||||
telegram.animation
|
||||
telegram.audio
|
||||
telegram.bot
|
||||
telegram.botcommand
|
||||
telegram.callbackquery
|
||||
telegram.chat
|
||||
telegram.chataction
|
||||
@@ -17,6 +18,7 @@ telegram package
|
||||
telegram.chatphoto
|
||||
telegram.constants
|
||||
telegram.contact
|
||||
telegram.dice
|
||||
telegram.document
|
||||
telegram.error
|
||||
telegram.file
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
"""A library that provides a Python interface to the Telegram Bot API"""
|
||||
|
||||
from .base import TelegramObject
|
||||
from .botcommand import BotCommand
|
||||
from .user import User
|
||||
from .files.chatphoto import ChatPhoto
|
||||
from .chat import Chat
|
||||
@@ -36,6 +37,7 @@ from .files.location import Location
|
||||
from .files.venue import Venue
|
||||
from .files.videonote import VideoNote
|
||||
from .chataction import ChatAction
|
||||
from .dice import Dice
|
||||
from .userprofilephotos import UserProfilePhotos
|
||||
from .keyboardbutton import KeyboardButton
|
||||
from .keyboardbuttonpolltype import KeyboardButtonPollType
|
||||
@@ -157,5 +159,6 @@ __all__ = [
|
||||
'InputMediaAudio', 'InputMediaDocument', 'TelegramDecryptionError',
|
||||
'PassportElementErrorSelfie', 'PassportElementErrorTranslationFile',
|
||||
'PassportElementErrorTranslationFiles', 'PassportElementErrorUnspecified', 'Poll',
|
||||
'PollOption', 'PollAnswer', 'LoginUrl', 'KeyboardButton', 'KeyboardButtonPollType',
|
||||
'PollOption', 'PollAnswer', 'LoginUrl', 'KeyboardButton', 'KeyboardButtonPollType', 'Dice',
|
||||
'BotCommand'
|
||||
]
|
||||
|
||||
+395
-177
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env python
|
||||
# pylint: disable=R0903
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2020
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains an object that represents a Telegram Bot Command."""
|
||||
from telegram import TelegramObject
|
||||
|
||||
|
||||
class BotCommand(TelegramObject):
|
||||
"""
|
||||
This object represents a bot command.
|
||||
|
||||
Attributes:
|
||||
command (:obj:`str`): Text of the command.
|
||||
description (:obj:`str`): Description of the command.
|
||||
|
||||
Args:
|
||||
command (:obj:`str`): Text of the command, 1-32 characters. Can contain only lowercase
|
||||
English letters, digits and underscores.
|
||||
description (:obj:`str`): Description of the command, 3-256 characters.
|
||||
"""
|
||||
def __init__(self, command, description, **kwargs):
|
||||
self.command = command
|
||||
self.description = description
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, data, bot):
|
||||
if not data:
|
||||
return None
|
||||
|
||||
return cls(**data)
|
||||
@@ -0,0 +1,43 @@
|
||||
#!/usr/bin/env python
|
||||
# pylint: disable=R0903
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2020
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains an object that represents a Telegram Dice."""
|
||||
from telegram import TelegramObject
|
||||
|
||||
|
||||
class Dice(TelegramObject):
|
||||
"""
|
||||
This object represents a dice with random value from 1 to 6. (The singular form of "dice" is
|
||||
"die". However, PTB mimics the Telegram API, which uses the term "dice".)
|
||||
|
||||
Attributes:
|
||||
value (:obj:`int`): Value of the dice.
|
||||
|
||||
Args:
|
||||
value (:obj:`int`): Value of the dice, 1-6.
|
||||
"""
|
||||
def __init__(self, value, **kwargs):
|
||||
self.value = value
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, data, bot):
|
||||
if not data:
|
||||
return None
|
||||
|
||||
return cls(**data)
|
||||
@@ -29,10 +29,11 @@ from telegram.utils.promise import Promise
|
||||
|
||||
|
||||
class _ConversationTimeoutContext(object):
|
||||
def __init__(self, conversation_key, update, dispatcher):
|
||||
def __init__(self, conversation_key, update, dispatcher, callback_context):
|
||||
self.conversation_key = conversation_key
|
||||
self.update = update
|
||||
self.dispatcher = dispatcher
|
||||
self.callback_context = callback_context
|
||||
|
||||
|
||||
class ConversationHandler(Handler):
|
||||
@@ -96,8 +97,9 @@ class ConversationHandler(Handler):
|
||||
conversation_timeout (:obj:`float` | :obj:`datetime.timedelta`): Optional. When this
|
||||
handler is inactive more than this timeout (in seconds), it will be automatically
|
||||
ended. If this value is 0 (default), there will be no timeout. When it's triggered, the
|
||||
last received update will be handled by ALL the handler's who's `check_update` method
|
||||
returns True that are in the state :attr:`ConversationHandler.TIMEOUT`.
|
||||
last received update and the corresponding ``context`` will be handled by ALL the
|
||||
handler's who's `check_update` method returns True that are in the state
|
||||
:attr:`ConversationHandler.TIMEOUT`.
|
||||
name (:obj:`str`): Optional. The name for this conversationhandler. Required for
|
||||
persistence
|
||||
persistent (:obj:`bool`): Optional. If the conversations dict for this handler should be
|
||||
@@ -130,8 +132,9 @@ class ConversationHandler(Handler):
|
||||
conversation_timeout (:obj:`float` | :obj:`datetime.timedelta`, optional): When this
|
||||
handler is inactive more than this timeout (in seconds), it will be automatically
|
||||
ended. If this value is 0 or None (default), there will be no timeout. The last
|
||||
received update will be handled by ALL the handler's who's `check_update` method
|
||||
returns True that are in the state :attr:`ConversationHandler.TIMEOUT`.
|
||||
received update and the corresponding ``context`` will be handled by ALL the handler's
|
||||
who's `check_update` method returns True that are in the state
|
||||
:attr:`ConversationHandler.TIMEOUT`.
|
||||
name (:obj:`str`, optional): The name for this conversationhandler. Required for
|
||||
persistence
|
||||
persistent (:obj:`bool`, optional): If the conversations dict for this handler should be
|
||||
@@ -466,7 +469,8 @@ class ConversationHandler(Handler):
|
||||
# Add the new timeout job
|
||||
self.timeout_jobs[conversation_key] = dispatcher.job_queue.run_once(
|
||||
self._trigger_timeout, self.conversation_timeout,
|
||||
context=_ConversationTimeoutContext(conversation_key, update, dispatcher))
|
||||
context=_ConversationTimeoutContext(conversation_key, update,
|
||||
dispatcher, context))
|
||||
|
||||
if isinstance(self.map_to_parent, dict) and new_state in self.map_to_parent:
|
||||
self.update_state(self.END, conversation_key)
|
||||
@@ -503,9 +507,9 @@ class ConversationHandler(Handler):
|
||||
callback_context = None
|
||||
if isinstance(context, CallbackContext):
|
||||
job = context.job
|
||||
callback_context = context
|
||||
|
||||
context = job.context
|
||||
callback_context = context.callback_context
|
||||
|
||||
with self._timeout_jobs_lock:
|
||||
found_job = self.timeout_jobs[context.conversation_key]
|
||||
|
||||
@@ -226,6 +226,8 @@ class DictPersistence(BasePersistence):
|
||||
user_id (:obj:`int`): The user the data might have been changed for.
|
||||
data (:obj:`dict`): The :attr:`telegram.ext.dispatcher.user_data` [user_id].
|
||||
"""
|
||||
if self._user_data is None:
|
||||
self._user_data = defaultdict(dict)
|
||||
if self._user_data.get(user_id) == data:
|
||||
return
|
||||
self._user_data[user_id] = data
|
||||
@@ -238,6 +240,8 @@ class DictPersistence(BasePersistence):
|
||||
chat_id (:obj:`int`): The chat the data might have been changed for.
|
||||
data (:obj:`dict`): The :attr:`telegram.ext.dispatcher.chat_data` [chat_id].
|
||||
"""
|
||||
if self._chat_data is None:
|
||||
self._chat_data = defaultdict(dict)
|
||||
if self._chat_data.get(chat_id) == data:
|
||||
return
|
||||
self._chat_data[chat_id] = data
|
||||
|
||||
+52
-55
@@ -323,53 +323,6 @@ class Dispatcher(object):
|
||||
|
||||
"""
|
||||
|
||||
def persist_update(update):
|
||||
"""Persist a single update.
|
||||
|
||||
Args:
|
||||
update (:class:`telegram.Update`):
|
||||
The update to process.
|
||||
|
||||
"""
|
||||
if self.persistence and isinstance(update, Update):
|
||||
if self.persistence.store_bot_data:
|
||||
try:
|
||||
self.persistence.update_bot_data(self.bot_data)
|
||||
except Exception as e:
|
||||
try:
|
||||
self.dispatch_error(update, e)
|
||||
except Exception:
|
||||
message = 'Saving bot data raised an error and an ' \
|
||||
'uncaught error was raised while handling ' \
|
||||
'the error with an error_handler'
|
||||
self.logger.exception(message)
|
||||
if self.persistence.store_chat_data and update.effective_chat:
|
||||
chat_id = update.effective_chat.id
|
||||
try:
|
||||
self.persistence.update_chat_data(chat_id,
|
||||
self.chat_data[chat_id])
|
||||
except Exception as e:
|
||||
try:
|
||||
self.dispatch_error(update, e)
|
||||
except Exception:
|
||||
message = 'Saving chat data raised an error and an ' \
|
||||
'uncaught error was raised while handling ' \
|
||||
'the error with an error_handler'
|
||||
self.logger.exception(message)
|
||||
if self.persistence.store_user_data and update.effective_user:
|
||||
user_id = update.effective_user.id
|
||||
try:
|
||||
self.persistence.update_user_data(user_id,
|
||||
self.user_data[user_id])
|
||||
except Exception as e:
|
||||
try:
|
||||
self.dispatch_error(update, e)
|
||||
except Exception:
|
||||
message = 'Saving user data raised an error and an ' \
|
||||
'uncaught error was raised while handling ' \
|
||||
'the error with an error_handler'
|
||||
self.logger.exception(message)
|
||||
|
||||
# An error happened while polling
|
||||
if isinstance(update, TelegramError):
|
||||
try:
|
||||
@@ -388,13 +341,13 @@ class Dispatcher(object):
|
||||
if not context and self.use_context:
|
||||
context = CallbackContext.from_update(update, self)
|
||||
handler.handle_update(update, self, check, context)
|
||||
persist_update(update)
|
||||
self.update_persistence(update=update)
|
||||
break
|
||||
|
||||
# Stop processing with any other handler.
|
||||
except DispatcherHandlerStop:
|
||||
self.logger.debug('Stopping further handlers due to DispatcherHandlerStop')
|
||||
persist_update(update)
|
||||
self.update_persistence(update=update)
|
||||
break
|
||||
|
||||
# Dispatch any error.
|
||||
@@ -471,18 +424,62 @@ class Dispatcher(object):
|
||||
del self.handlers[group]
|
||||
self.groups.remove(group)
|
||||
|
||||
def update_persistence(self):
|
||||
def update_persistence(self, update=None):
|
||||
"""Update :attr:`user_data`, :attr:`chat_data` and :attr:`bot_data` in :attr:`persistence`.
|
||||
|
||||
Args:
|
||||
update (:class:`telegram.Update`, optional): The update to process. If passed, only the
|
||||
corresponding ``user_data`` and ``chat_data`` will be updated.
|
||||
"""
|
||||
if self.persistence:
|
||||
chat_ids = self.chat_data.keys()
|
||||
user_ids = self.user_data.keys()
|
||||
|
||||
if isinstance(update, Update):
|
||||
if update.effective_chat:
|
||||
chat_ids = [update.effective_chat.id]
|
||||
else:
|
||||
chat_ids = []
|
||||
if update.effective_user:
|
||||
user_ids = [update.effective_user.id]
|
||||
else:
|
||||
user_ids = []
|
||||
|
||||
if self.persistence.store_bot_data:
|
||||
self.persistence.update_bot_data(self.bot_data)
|
||||
try:
|
||||
self.persistence.update_bot_data(self.bot_data)
|
||||
except Exception as e:
|
||||
try:
|
||||
self.dispatch_error(update, e)
|
||||
except Exception:
|
||||
message = 'Saving bot data raised an error and an ' \
|
||||
'uncaught error was raised while handling ' \
|
||||
'the error with an error_handler'
|
||||
self.logger.exception(message)
|
||||
if self.persistence.store_chat_data:
|
||||
for chat_id in self.chat_data:
|
||||
self.persistence.update_chat_data(chat_id, self.chat_data[chat_id])
|
||||
for chat_id in chat_ids:
|
||||
try:
|
||||
self.persistence.update_chat_data(chat_id, self.chat_data[chat_id])
|
||||
except Exception as e:
|
||||
try:
|
||||
self.dispatch_error(update, e)
|
||||
except Exception:
|
||||
message = 'Saving chat data raised an error and an ' \
|
||||
'uncaught error was raised while handling ' \
|
||||
'the error with an error_handler'
|
||||
self.logger.exception(message)
|
||||
if self.persistence.store_user_data:
|
||||
for user_id in self.user_data:
|
||||
self.persistence.update_user_data(user_id, self.user_data[user_id])
|
||||
for user_id in user_ids:
|
||||
try:
|
||||
self.persistence.update_user_data(user_id, self.user_data[user_id])
|
||||
except Exception as e:
|
||||
try:
|
||||
self.dispatch_error(update, e)
|
||||
except Exception:
|
||||
message = 'Saving user data raised an error and an ' \
|
||||
'uncaught error was raised while handling ' \
|
||||
'the error with an error_handler'
|
||||
self.logger.exception(message)
|
||||
|
||||
def add_error_handler(self, callback):
|
||||
"""Registers an error handler in the Dispatcher. This handler will receive every error
|
||||
|
||||
+68
-22
@@ -236,35 +236,35 @@ class Filters(object):
|
||||
class _Text(BaseFilter):
|
||||
name = 'Filters.text'
|
||||
|
||||
class _TextIterable(BaseFilter):
|
||||
class _TextStrings(BaseFilter):
|
||||
|
||||
def __init__(self, iterable):
|
||||
self.iterable = iterable
|
||||
self.name = 'Filters.text({})'.format(iterable)
|
||||
def __init__(self, strings):
|
||||
self.strings = strings
|
||||
self.name = 'Filters.text({})'.format(strings)
|
||||
|
||||
def filter(self, message):
|
||||
if message.text:
|
||||
return message.text in self.iterable
|
||||
return message.text in self.strings
|
||||
return False
|
||||
|
||||
def __call__(self, update):
|
||||
if isinstance(update, Update):
|
||||
return self.filter(update.effective_message)
|
||||
else:
|
||||
return self._TextIterable(update)
|
||||
return self._TextStrings(update)
|
||||
|
||||
def filter(self, message):
|
||||
return bool(message.text)
|
||||
|
||||
text = _Text()
|
||||
"""Text Messages. If an iterable of strings is passed, it filters messages to only allow those
|
||||
whose text is appearing in the given iterable.
|
||||
"""Text Messages. If a list of strings is passed, it filters messages to only allow those
|
||||
whose text is appearing in the given list.
|
||||
|
||||
Examples:
|
||||
To allow any text message, simply use
|
||||
``MessageHandler(Filters.text, callback_method)``.
|
||||
|
||||
A simple usecase for passing an iterable is to allow only messages that were send by a
|
||||
A simple usecase for passing a list is to allow only messages that were send by a
|
||||
custom :class:`telegram.ReplyKeyboardMarkup`::
|
||||
|
||||
buttons = ['Start', 'Settings', 'Back']
|
||||
@@ -272,44 +272,48 @@ class Filters(object):
|
||||
...
|
||||
MessageHandler(Filters.text(buttons), callback_method)
|
||||
|
||||
Note:
|
||||
Dice messages don't have text. If you want to filter either text or dice messages, use
|
||||
``Filters.text | Filters.dice``.
|
||||
|
||||
Args:
|
||||
update (Iterable[:obj:`str`], optional): Which messages to allow. Only exact matches
|
||||
are allowed. If not specified, will allow any text message.
|
||||
update (List[:obj:`str`] | Tuple[:obj:`str`], optional): Which messages to allow. Only
|
||||
exact matches are allowed. If not specified, will allow any text message.
|
||||
"""
|
||||
|
||||
class _Caption(BaseFilter):
|
||||
name = 'Filters.caption'
|
||||
|
||||
class _CaptionIterable(BaseFilter):
|
||||
class _CaptionStrings(BaseFilter):
|
||||
|
||||
def __init__(self, iterable):
|
||||
self.iterable = iterable
|
||||
self.name = 'Filters.caption({})'.format(iterable)
|
||||
def __init__(self, strings):
|
||||
self.strings = strings
|
||||
self.name = 'Filters.caption({})'.format(strings)
|
||||
|
||||
def filter(self, message):
|
||||
if message.caption:
|
||||
return message.caption in self.iterable
|
||||
return message.caption in self.strings
|
||||
return False
|
||||
|
||||
def __call__(self, update):
|
||||
if isinstance(update, Update):
|
||||
return self.filter(update.effective_message)
|
||||
else:
|
||||
return self._CaptionIterable(update)
|
||||
return self._CaptionStrings(update)
|
||||
|
||||
def filter(self, message):
|
||||
return bool(message.caption)
|
||||
|
||||
caption = _Caption()
|
||||
"""Messages with a caption. If an iterable of strings is passed, it filters messages to only
|
||||
allow those whose caption is appearing in the given iterable.
|
||||
"""Messages with a caption. If a list of strings is passed, it filters messages to only
|
||||
allow those whose caption is appearing in the given list.
|
||||
|
||||
Examples:
|
||||
``MessageHandler(Filters.caption, callback_method)``
|
||||
|
||||
Args:
|
||||
update (Iterable[:obj:`str`], optional): Which captions to allow. Only exact matches
|
||||
are allowed. If not specified, will allow any message with a caption.
|
||||
update (List[:obj:`str`] | Tuple[:obj:`str`], optional): Which captions to allow. Only
|
||||
exact matches are allowed. If not specified, will allow any message with a caption.
|
||||
"""
|
||||
|
||||
class _Command(BaseFilter):
|
||||
@@ -427,7 +431,7 @@ class Filters(object):
|
||||
send media with wrong types that don't fit to this handler.
|
||||
|
||||
Example:
|
||||
Filters.documents.category('audio/') returnes `True` for all types
|
||||
Filters.documents.category('audio/') returns `True` for all types
|
||||
of audio sent as file, for example 'audio/mpeg' or 'audio/x-wav'
|
||||
"""
|
||||
|
||||
@@ -957,6 +961,48 @@ officedocument.wordprocessingml.document")``-
|
||||
poll = _Poll()
|
||||
"""Messages that contain a :class:`telegram.Poll`."""
|
||||
|
||||
class _Dice(BaseFilter):
|
||||
name = 'Filters.dice'
|
||||
|
||||
class _DiceValues(BaseFilter):
|
||||
|
||||
def __init__(self, values):
|
||||
self.values = [values] if isinstance(values, int) else values
|
||||
self.name = 'Filters.dice({})'.format(values)
|
||||
|
||||
def filter(self, message):
|
||||
return bool(message.dice and message.dice.value in self.values)
|
||||
|
||||
def __call__(self, update):
|
||||
if isinstance(update, Update):
|
||||
return self.filter(update.effective_message)
|
||||
else:
|
||||
return self._DiceValues(update)
|
||||
|
||||
def filter(self, message):
|
||||
return bool(message.dice)
|
||||
|
||||
dice = _Dice()
|
||||
"""Dice Messages. If an integer or a list of integers is passed, it filters messages to only
|
||||
allow those whose dice value is appearing in the given list.
|
||||
|
||||
Examples:
|
||||
To allow any dice message, simply use
|
||||
``MessageHandler(Filters.dice, callback_method)``.
|
||||
To allow only dice with value 6, use
|
||||
``MessageHandler(Filters.dice(6), callback_method)``.
|
||||
To allow only dice with value 5 `or` 6, use
|
||||
``MessageHandler(Filters.dice([5, 6]), callback_method)``.
|
||||
|
||||
Args:
|
||||
update (:obj:`int` | List[:obj:`int`], optional): Which values to allow. If not
|
||||
specified, will allow any dice message.
|
||||
|
||||
Note:
|
||||
Dice messages don't have text. If you want to filter either text or dice messages, use
|
||||
``Filters.text | Filters.dice``.
|
||||
"""
|
||||
|
||||
class language(BaseFilter):
|
||||
"""Filters messages to only allow those which are from users with a certain language code.
|
||||
|
||||
|
||||
@@ -129,10 +129,11 @@ class JobQueue(object):
|
||||
* :obj:`datetime.timedelta` will be interpreted as "time from now" in which the
|
||||
job should run.
|
||||
* :obj:`datetime.datetime` will be interpreted as a specific date and time at
|
||||
which the job should run.
|
||||
which the job should run. If the timezone (``datetime.tzinfo``) is ``None``, UTC
|
||||
will be assumed.
|
||||
* :obj:`datetime.time` will be interpreted as a specific time of day at which the
|
||||
job should run. This could be either today or, if the time has already passed,
|
||||
tomorrow.
|
||||
tomorrow. If the timezone (``time.tzinfo``) is ``None``, UTC will be assumed.
|
||||
|
||||
context (:obj:`object`, optional): Additional data needed for the callback function.
|
||||
Can be accessed through ``job.context`` in the callback. Defaults to ``None``.
|
||||
@@ -172,10 +173,11 @@ class JobQueue(object):
|
||||
* :obj:`datetime.timedelta` will be interpreted as "time from now" in which the
|
||||
job should run.
|
||||
* :obj:`datetime.datetime` will be interpreted as a specific date and time at
|
||||
which the job should run.
|
||||
which the job should run. If the timezone (``datetime.tzinfo``) is ``None``, UTC
|
||||
will be assumed.
|
||||
* :obj:`datetime.time` will be interpreted as a specific time of day at which the
|
||||
job should run. This could be either today or, if the time has already passed,
|
||||
tomorrow.
|
||||
tomorrow. If the timezone (``time.tzinfo``) is ``None``, UTC will be assumed.
|
||||
|
||||
Defaults to ``interval``
|
||||
context (:obj:`object`, optional): Additional data needed for the callback function.
|
||||
@@ -288,6 +290,7 @@ class JobQueue(object):
|
||||
if current_week_day in job.days:
|
||||
self.logger.debug('Running job %s', job.name)
|
||||
job.run(self._dispatcher)
|
||||
self._dispatcher.update_persistence()
|
||||
|
||||
except Exception:
|
||||
self.logger.exception('An uncaught error was raised while executing job %s',
|
||||
|
||||
@@ -224,6 +224,8 @@ class PicklePersistence(BasePersistence):
|
||||
user_id (:obj:`int`): The user the data might have been changed for.
|
||||
data (:obj:`dict`): The :attr:`telegram.ext.dispatcher.user_data` [user_id].
|
||||
"""
|
||||
if self.user_data is None:
|
||||
self.user_data = defaultdict(dict)
|
||||
if self.user_data.get(user_id) == data:
|
||||
return
|
||||
self.user_data[user_id] = data
|
||||
@@ -242,6 +244,8 @@ class PicklePersistence(BasePersistence):
|
||||
chat_id (:obj:`int`): The chat the data might have been changed for.
|
||||
data (:obj:`dict`): The :attr:`telegram.ext.dispatcher.chat_data` [chat_id].
|
||||
"""
|
||||
if self.chat_data is None:
|
||||
self.chat_data = defaultdict(dict)
|
||||
if self.chat_data.get(chat_id) == data:
|
||||
return
|
||||
self.chat_data[chat_id] = data
|
||||
|
||||
@@ -570,9 +570,9 @@ class Updater(object):
|
||||
"""Blocks until one of the signals are received and stops the updater.
|
||||
|
||||
Args:
|
||||
stop_signals (:obj:`iterable`): Iterable containing signals from the signal module that
|
||||
should be subscribed to. Updater.stop() will be called on receiving one of those
|
||||
signals. Defaults to (``SIGINT``, ``SIGTERM``, ``SIGABRT``).
|
||||
stop_signals (:obj:`list` | :obj:`tuple`): List containing signals from the signal
|
||||
module that should be subscribed to. Updater.stop() will be called on receiving one
|
||||
of those signals. Defaults to (``SIGINT``, ``SIGTERM``, ``SIGABRT``).
|
||||
|
||||
"""
|
||||
for sig in stop_signals:
|
||||
|
||||
@@ -138,6 +138,8 @@ class StickerSet(TelegramObject):
|
||||
is_animated (:obj:`bool`): True, if the sticker set contains animated stickers.
|
||||
contains_masks (:obj:`bool`): True, if the sticker set contains masks.
|
||||
stickers (List[:class:`telegram.Sticker`]): List of all set stickers.
|
||||
thumb (:class:`telegram.PhotoSize`): Optional. Sticker set thumbnail in the .WEBP or .TGS
|
||||
format
|
||||
|
||||
Args:
|
||||
name (:obj:`str`): Sticker set name.
|
||||
@@ -145,15 +147,20 @@ class StickerSet(TelegramObject):
|
||||
is_animated (:obj:`bool`): True, if the sticker set contains animated stickers.
|
||||
contains_masks (:obj:`bool`): True, if the sticker set contains masks.
|
||||
stickers (List[:class:`telegram.Sticker`]): List of all set stickers.
|
||||
thumb (:class:`telegram.PhotoSize`, optional): Sticker set thumbnail in the .WEBP or .TGS
|
||||
format
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, name, title, is_animated, contains_masks, stickers, bot=None, **kwargs):
|
||||
def __init__(self, name, title, is_animated, contains_masks, stickers, bot=None, thumb=None,
|
||||
**kwargs):
|
||||
self.name = name
|
||||
self.title = title
|
||||
self.is_animated = is_animated
|
||||
self.contains_masks = contains_masks
|
||||
self.stickers = stickers
|
||||
# Optionals
|
||||
self.thumb = thumb
|
||||
|
||||
self._id_attrs = (self.name,)
|
||||
|
||||
@@ -164,6 +171,7 @@ class StickerSet(TelegramObject):
|
||||
|
||||
data = super(StickerSet, StickerSet).de_json(data, bot)
|
||||
|
||||
data['thumb'] = PhotoSize.de_json(data.get('thumb'), bot)
|
||||
data['stickers'] = Sticker.de_list(data.get('stickers'), bot)
|
||||
|
||||
return StickerSet(bot=bot, **data)
|
||||
|
||||
@@ -30,27 +30,28 @@ class InlineKeyboardButton(TelegramObject):
|
||||
|
||||
Attributes:
|
||||
text (:obj:`str`): Label text on the button.
|
||||
url (:obj:`str`): Optional. HTTP url to be opened when button is pressed.
|
||||
url (:obj:`str`): Optional. HTTP or tg:// url to be opened when button is pressed.
|
||||
login_url (:class:`telegram.LoginUrl`) Optional. An HTTP URL used to automatically
|
||||
authorize the user.
|
||||
authorize the user. Can be used as a replacement for the Telegram Login Widget.
|
||||
callback_data (:obj:`str`): Optional. Data to be sent in a callback query to the bot when
|
||||
button is pressed, UTF-8 1-64 bytes.
|
||||
switch_inline_query (:obj:`str`): Optional. Will prompt the user to select one of their
|
||||
chats, open that chat and insert the bot's username and the specified inline query in
|
||||
the input field.
|
||||
the input field. Can be empty, in which case just the bot’s username will be inserted.
|
||||
switch_inline_query_current_chat (:obj:`str`): Optional. Will insert the bot's username and
|
||||
the specified inline query in the current chat's input field.
|
||||
the specified inline query in the current chat's input field. Can be empty, in which
|
||||
case just the bot’s username will be inserted.
|
||||
callback_game (:class:`telegram.CallbackGame`): Optional. Description of the game that will
|
||||
be launched when the user presses the button.
|
||||
pay (:obj:`bool`): Optional. Specify True, to send a Pay button.
|
||||
|
||||
Args:
|
||||
text (:obj:`str`): Label text on the button.
|
||||
url (:obj:`str`): HTTP url to be opened when button is pressed.
|
||||
url (:obj:`str`): HTTP or tg:// url to be opened when button is pressed.
|
||||
login_url (:class:`telegram.LoginUrl`, optional) An HTTP URL used to automatically
|
||||
authorize the user.
|
||||
authorize the user. Can be used as a replacement for the Telegram Login Widget.
|
||||
callback_data (:obj:`str`, optional): Data to be sent in a callback query to the bot when
|
||||
button is pressed, 1-64 UTF-8 bytes.
|
||||
button is pressed, UTF-8 1-64 bytes.
|
||||
switch_inline_query (:obj:`str`, optional): If set, pressing the button will prompt the
|
||||
user to select one of their chats, open that chat and insert the bot's username and the
|
||||
specified inline query in the input field. Can be empty, in which case just the bot's
|
||||
|
||||
@@ -33,9 +33,9 @@ class InlineQueryResultAudio(InlineQueryResult):
|
||||
id (:obj:`str`): Unique identifier for this result, 1-64 bytes.
|
||||
audio_url (:obj:`str`): A valid URL for the audio file.
|
||||
title (:obj:`str`): Title.
|
||||
performer (:obj:`str`): Optional. Caption, 0-200 characters.
|
||||
audio_duration (:obj:`str`): Optional. Performer.
|
||||
caption (:obj:`str`): Optional. Audio duration in seconds.
|
||||
performer (:obj:`str`): Optional. Performer.
|
||||
audio_duration (:obj:`str`): Optional. Audio duration in seconds.
|
||||
caption (:obj:`str`): Optional. Caption, 0-1024 characters after entities parsing.
|
||||
parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show
|
||||
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
|
||||
in :class:`telegram.ParseMode` for the available modes.
|
||||
@@ -48,9 +48,9 @@ class InlineQueryResultAudio(InlineQueryResult):
|
||||
id (:obj:`str`): Unique identifier for this result, 1-64 bytes.
|
||||
audio_url (:obj:`str`): A valid URL for the audio file.
|
||||
title (:obj:`str`): Title.
|
||||
performer (:obj:`str`, optional): Caption, 0-200 characters.
|
||||
audio_duration (:obj:`str`, optional): Performer.
|
||||
caption (:obj:`str`, optional): Audio duration in seconds.
|
||||
performer (:obj:`str`, optional): Performer.
|
||||
audio_duration (:obj:`str`, optional): Audio duration in seconds.
|
||||
caption (:obj:`str`, optional): Caption, 0-1024 characters after entities parsing.
|
||||
parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show
|
||||
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
|
||||
in :class:`telegram.ParseMode` for the available modes.
|
||||
|
||||
@@ -26,13 +26,13 @@ class InlineQueryResultCachedAudio(InlineQueryResult):
|
||||
"""
|
||||
Represents a link to an mp3 audio file stored on the Telegram servers. By default, this audio
|
||||
file will be sent by the user. Alternatively, you can use :attr:`input_message_content` to
|
||||
send amessage with the specified content instead of the audio.
|
||||
send a message with the specified content instead of the audio.
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): 'audio'.
|
||||
id (:obj:`str`): Unique identifier for this result, 1-64 bytes.
|
||||
audio_file_id (:obj:`str`): A valid file identifier for the audio file.
|
||||
caption (:obj:`str`): Optional. Caption, 0-1024 characters
|
||||
caption (:obj:`str`): Optional. Caption, 0-1024 characters after entities parsing.
|
||||
parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show
|
||||
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
|
||||
in :class:`telegram.ParseMode` for the available modes.
|
||||
@@ -44,7 +44,7 @@ class InlineQueryResultCachedAudio(InlineQueryResult):
|
||||
Args:
|
||||
id (:obj:`str`): Unique identifier for this result, 1-64 bytes.
|
||||
audio_file_id (:obj:`str`): A valid file identifier for the audio file.
|
||||
caption (:obj:`str`, optional): Caption, 0-1024 characters
|
||||
caption (:obj:`str`, optional): Caption, 0-1024 characters after entities parsing.
|
||||
parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show
|
||||
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
|
||||
in :class:`telegram.ParseMode` for the available modes.
|
||||
|
||||
@@ -34,7 +34,8 @@ class InlineQueryResultCachedDocument(InlineQueryResult):
|
||||
title (:obj:`str`): Title for the result.
|
||||
document_file_id (:obj:`str`): A valid file identifier for the file.
|
||||
description (:obj:`str`): Optional. Short description of the result.
|
||||
caption (:obj:`str`): Optional. Caption, 0-1024 characters
|
||||
caption (:obj:`str`): Optional. Caption of the document to be sent, 0-1024 characters
|
||||
after entities parsing.
|
||||
parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show
|
||||
bold, italic, fixed-width text or inline URLs in the media caption.. See the constants
|
||||
in :class:`telegram.ParseMode` for the available modes.
|
||||
@@ -48,7 +49,8 @@ class InlineQueryResultCachedDocument(InlineQueryResult):
|
||||
title (:obj:`str`): Title for the result.
|
||||
document_file_id (:obj:`str`): A valid file identifier for the file.
|
||||
description (:obj:`str`, optional): Short description of the result.
|
||||
caption (:obj:`str`, optional): Caption, 0-1024 characters
|
||||
caption (:obj:`str`, optional): Caption of the document to be sent, 0-1024 characters
|
||||
after entities parsing.
|
||||
parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show
|
||||
bold, italic, fixed-width text or inline URLs in the media caption.. See the constants
|
||||
in :class:`telegram.ParseMode` for the available modes.
|
||||
|
||||
@@ -34,7 +34,8 @@ class InlineQueryResultCachedGif(InlineQueryResult):
|
||||
id (:obj:`str`): Unique identifier for this result, 1-64 bytes.
|
||||
gif_file_id (:obj:`str`): A valid file identifier for the GIF file.
|
||||
title (:obj:`str`): Optional. Title for the result.
|
||||
caption (:obj:`str`): Optional. Caption, 0-1024 characters
|
||||
caption (:obj:`str`): Optional. Caption of the GIF file to be sent, 0-1024 characters
|
||||
after entities parsing.
|
||||
parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show
|
||||
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
|
||||
in :class:`telegram.ParseMode` for the available modes.
|
||||
@@ -47,7 +48,8 @@ class InlineQueryResultCachedGif(InlineQueryResult):
|
||||
id (:obj:`str`): Unique identifier for this result, 1-64 bytes.
|
||||
gif_file_id (:obj:`str`): A valid file identifier for the GIF file.
|
||||
title (:obj:`str`, optional): Title for the result.caption (:obj:`str`, optional):
|
||||
caption (:obj:`str`, optional): Caption, 0-1024 characters
|
||||
caption (:obj:`str`, optional): Caption of the GIF file to be sent, 0-1024 characters
|
||||
after entities parsing.
|
||||
parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show
|
||||
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
|
||||
in :class:`telegram.ParseMode` for the available modes.
|
||||
|
||||
@@ -34,7 +34,8 @@ class InlineQueryResultCachedMpeg4Gif(InlineQueryResult):
|
||||
id (:obj:`str`): Unique identifier for this result, 1-64 bytes.
|
||||
mpeg4_file_id (:obj:`str`): A valid file identifier for the MP4 file.
|
||||
title (:obj:`str`): Optional. Title for the result.
|
||||
caption (:obj:`str`): Optional. Caption, 0-1024 characters
|
||||
caption (:obj:`str`): Optional. Caption of the MPEG-4 file to be sent, 0-1024 characters
|
||||
after entities parsing.
|
||||
parse_mode (:obj:`str`): Send Markdown or HTML, if you want Telegram apps to show
|
||||
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
|
||||
in :class:`telegram.ParseMode` for the available modes.
|
||||
@@ -47,7 +48,8 @@ class InlineQueryResultCachedMpeg4Gif(InlineQueryResult):
|
||||
id (:obj:`str`): Unique identifier for this result, 1-64 bytes.
|
||||
mpeg4_file_id (:obj:`str`): A valid file identifier for the MP4 file.
|
||||
title (:obj:`str`, optional): Title for the result.
|
||||
caption (:obj:`str`, optional): Caption, 0-1024 characters
|
||||
caption (:obj:`str`, optional): Caption of the MPEG-4 file to be sent, 0-1024 characters
|
||||
after entities parsing.
|
||||
parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show
|
||||
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
|
||||
in :class:`telegram.ParseMode` for the available modes.
|
||||
|
||||
@@ -35,7 +35,8 @@ class InlineQueryResultCachedPhoto(InlineQueryResult):
|
||||
photo_file_id (:obj:`str`): A valid file identifier of the photo.
|
||||
title (:obj:`str`): Optional. Title for the result.
|
||||
description (:obj:`str`): Optional. Short description of the result.
|
||||
caption (:obj:`str`): Optional. Caption, 0-1024 characters
|
||||
caption (:obj:`str`): Optional. Caption of the photo to be sent, 0-1024 characters after
|
||||
entities parsing.
|
||||
parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show
|
||||
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
|
||||
in :class:`telegram.ParseMode` for the available modes.
|
||||
@@ -49,7 +50,8 @@ class InlineQueryResultCachedPhoto(InlineQueryResult):
|
||||
photo_file_id (:obj:`str`): A valid file identifier of the photo.
|
||||
title (:obj:`str`, optional): Title for the result.
|
||||
description (:obj:`str`, optional): Short description of the result.
|
||||
caption (:obj:`str`, optional): Caption, 0-1024 characters
|
||||
caption (:obj:`str`, optional): Caption of the photo to be sent, 0-1024 characters after
|
||||
entities parsing.
|
||||
parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show
|
||||
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
|
||||
in :class:`telegram.ParseMode` for the available modes.
|
||||
|
||||
@@ -37,8 +37,8 @@ class InlineQueryResultCachedSticker(InlineQueryResult):
|
||||
message to be sent instead of the sticker.
|
||||
|
||||
Args:
|
||||
id (:obj:`str`):
|
||||
sticker_file_id (:obj:`str`):
|
||||
id (:obj:`str`): Unique identifier for this result, 1-64 bytes.
|
||||
sticker_file_id (:obj:`str`): A valid file identifier of the sticker.
|
||||
reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached
|
||||
to the message.
|
||||
input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the
|
||||
|
||||
@@ -35,7 +35,8 @@ class InlineQueryResultCachedVideo(InlineQueryResult):
|
||||
video_file_id (:obj:`str`): A valid file identifier for the video file.
|
||||
title (:obj:`str`): Title for the result.
|
||||
description (:obj:`str`): Optional. Short description of the result.
|
||||
caption (:obj:`str`): Optional. Caption, 0-1024 characters after entities parsing.
|
||||
caption (:obj:`str`): Optional. Caption of the video to be sent, 0-1024 characters after
|
||||
entities parsing.
|
||||
parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show
|
||||
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
|
||||
in :class:`telegram.ParseMode` for the available modes.
|
||||
@@ -49,7 +50,8 @@ class InlineQueryResultCachedVideo(InlineQueryResult):
|
||||
video_file_id (:obj:`str`): A valid file identifier for the video file.
|
||||
title (:obj:`str`): Title for the result.
|
||||
description (:obj:`str`, optional): Short description of the result.
|
||||
caption (:obj:`str`, optional): Caption, 0-1024 characters after entities parsing.
|
||||
caption (:obj:`str`, optional): Caption of the video to be sent, 0-1024 characters after
|
||||
entities parsing.
|
||||
parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show
|
||||
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
|
||||
in :class:`telegram.ParseMode` for the available modes.
|
||||
|
||||
@@ -33,7 +33,8 @@ class InlineQueryResultDocument(InlineQueryResult):
|
||||
type (:obj:`str`): 'document'.
|
||||
id (:obj:`str`): Unique identifier for this result, 1-64 bytes.
|
||||
title (:obj:`str`): Title for the result.
|
||||
caption (:obj:`str`): Optional. Caption, 0-1024 characters
|
||||
caption (:obj:`str`): Optional. Caption of the document to be sent, 0-1024 characters
|
||||
after entities parsing.
|
||||
parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show
|
||||
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
|
||||
in :class:`telegram.ParseMode` for the available modes.
|
||||
@@ -52,7 +53,8 @@ class InlineQueryResultDocument(InlineQueryResult):
|
||||
Args:
|
||||
id (:obj:`str`): Unique identifier for this result, 1-64 bytes.
|
||||
title (:obj:`str`): Title for the result.
|
||||
caption (:obj:`str`, optional): Caption, 0-1024 characters
|
||||
caption (:obj:`str`, optional): Caption of the document to be sent, 0-1024 characters
|
||||
after entities parsing.
|
||||
parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show
|
||||
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
|
||||
in :class:`telegram.ParseMode` for the available modes.
|
||||
@@ -60,9 +62,9 @@ class InlineQueryResultDocument(InlineQueryResult):
|
||||
mime_type (:obj:`str`): Mime type of the content of the file, either "application/pdf"
|
||||
or "application/zip".
|
||||
description (:obj:`str`, optional): Short description of the result.
|
||||
reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached
|
||||
reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached
|
||||
to the message.
|
||||
input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the
|
||||
input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the
|
||||
message to be sent instead of the file.
|
||||
thumb_url (:obj:`str`, optional): URL of the thumbnail (jpeg only) for the file.
|
||||
thumb_width (:obj:`int`, optional): Thumbnail width.
|
||||
|
||||
@@ -37,14 +37,15 @@ class InlineQueryResultGif(InlineQueryResult):
|
||||
gif_duration (:obj:`int`): Optional. Duration of the GIF.
|
||||
thumb_url (:obj:`str`): URL of the static thumbnail for the result (jpeg or gif).
|
||||
title (:obj:`str`): Optional. Title for the result.
|
||||
caption (:obj:`str`): Optional. Caption, 0-1024 characters
|
||||
caption (:obj:`str`): Optional. Caption of the GIF file to be sent, 0-1024 characters
|
||||
after entities parsing.
|
||||
parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show
|
||||
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
|
||||
in :class:`telegram.ParseMode` for the available modes.
|
||||
reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached
|
||||
to the message.
|
||||
input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the
|
||||
message to be sent instead of the gif.
|
||||
message to be sent instead of the GIF animation.
|
||||
|
||||
Args:
|
||||
id (:obj:`str`): Unique identifier for this result, 1-64 bytes.
|
||||
@@ -53,15 +54,16 @@ class InlineQueryResultGif(InlineQueryResult):
|
||||
gif_height (:obj:`int`, optional): Height of the GIF.
|
||||
gif_duration (:obj:`int`, optional): Duration of the GIF
|
||||
thumb_url (:obj:`str`): URL of the static thumbnail for the result (jpeg or gif).
|
||||
title (:obj:`str`, optional): Title for the result.caption (:obj:`str`, optional):
|
||||
caption (:obj:`str`, optional): Caption, 0-1024 characters
|
||||
title (:obj:`str`, optional): Title for the result.
|
||||
caption (:obj:`str`, optional): Caption of the GIF file to be sent, 0-1024 characters
|
||||
after entities parsing.
|
||||
parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show
|
||||
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
|
||||
in :class:`telegram.ParseMode` for the available modes.
|
||||
reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached
|
||||
to the message.
|
||||
input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the
|
||||
message to be sent instead of the gif.
|
||||
message to be sent instead of the GIF animation.
|
||||
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
|
||||
|
||||
"""
|
||||
|
||||
@@ -38,14 +38,15 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult):
|
||||
mpeg4_duration (:obj:`int`): Optional. Video duration.
|
||||
thumb_url (:obj:`str`): URL of the static thumbnail (jpeg or gif) for the result.
|
||||
title (:obj:`str`): Optional. Title for the result.
|
||||
caption (:obj:`str`): Optional. Caption, 0-1024 characters
|
||||
caption (:obj:`str`): Optional. Caption of the MPEG-4 file to be sent, 0-1024 characters
|
||||
after entities parsing.
|
||||
parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show
|
||||
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
|
||||
in :class:`telegram.ParseMode` for the available modes.
|
||||
reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached
|
||||
to the message.
|
||||
input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the
|
||||
message to be sent instead of the MPEG-4 file.
|
||||
message to be sent instead of the video animation.
|
||||
|
||||
Args:
|
||||
id (:obj:`str`): Unique identifier for this result, 1-64 bytes.
|
||||
@@ -55,14 +56,15 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult):
|
||||
mpeg4_duration (:obj:`int`, optional): Video duration.
|
||||
thumb_url (:obj:`str`): URL of the static thumbnail (jpeg or gif) for the result.
|
||||
title (:obj:`str`, optional): Title for the result.
|
||||
caption (:obj:`str`, optional): Caption, 0-1024 characters
|
||||
caption (:obj:`str`, optional): Caption of the MPEG-4 file to be sent, 0-1024 characters
|
||||
after entities parsing.
|
||||
parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show
|
||||
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
|
||||
in :class:`telegram.ParseMode` for the available modes.
|
||||
reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached
|
||||
to the message.
|
||||
input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the
|
||||
message to be sent instead of the MPEG-4 file.
|
||||
message to be sent instead of the video animation.
|
||||
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
|
||||
|
||||
"""
|
||||
|
||||
@@ -38,7 +38,8 @@ class InlineQueryResultPhoto(InlineQueryResult):
|
||||
photo_height (:obj:`int`): Optional. Height of the photo.
|
||||
title (:obj:`str`): Optional. Title for the result.
|
||||
description (:obj:`str`): Optional. Short description of the result.
|
||||
caption (:obj:`str`): Optional. Caption, 0-1024 characters
|
||||
caption (:obj:`str`): Optional. Caption of the photo to be sent, 0-1024 characters after
|
||||
entities parsing.
|
||||
parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show
|
||||
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
|
||||
in :class:`telegram.ParseMode` for the available modes.
|
||||
@@ -56,7 +57,8 @@ class InlineQueryResultPhoto(InlineQueryResult):
|
||||
photo_height (:obj:`int`, optional): Height of the photo.
|
||||
title (:obj:`str`, optional): Title for the result.
|
||||
description (:obj:`str`, optional): Short description of the result.
|
||||
caption (:obj:`str`, optional): Caption, 0-1024 characters
|
||||
caption (:obj:`str`, optional): Caption of the photo to be sent, 0-1024 characters after
|
||||
entities parsing.
|
||||
parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show
|
||||
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
|
||||
in :class:`telegram.ParseMode` for the available modes.
|
||||
|
||||
@@ -29,6 +29,10 @@ class InlineQueryResultVideo(InlineQueryResult):
|
||||
:attr:`input_message_content` to send a message with the specified content instead of
|
||||
the video.
|
||||
|
||||
Note:
|
||||
If an InlineQueryResultVideo message contains an embedded video (e.g., YouTube), you must
|
||||
replace its content using :attr:`input_message_content`.
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): 'video'.
|
||||
id (:obj:`str`): Unique identifier for this result, 1-64 bytes.
|
||||
@@ -36,7 +40,8 @@ class InlineQueryResultVideo(InlineQueryResult):
|
||||
mime_type (:obj:`str`): Mime type of the content of video url, "text/html" or "video/mp4".
|
||||
thumb_url (:obj:`str`): URL of the thumbnail (jpeg only) for the video.
|
||||
title (:obj:`str`): Title for the result.
|
||||
caption (:obj:`str`): Optional. Caption, 0-1024 characters
|
||||
caption (:obj:`str`): Optional. Caption of the video to be sent, 0-1024 characters after
|
||||
entities parsing.
|
||||
parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show
|
||||
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
|
||||
in :class:`telegram.ParseMode` for the available modes.
|
||||
@@ -47,7 +52,9 @@ class InlineQueryResultVideo(InlineQueryResult):
|
||||
reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached
|
||||
to the message.
|
||||
input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the
|
||||
message to be sent instead of the video.
|
||||
message to be sent instead of the video. This field is required if
|
||||
InlineQueryResultVideo is used to send an HTML-page as a result
|
||||
(e.g., a YouTube video).
|
||||
|
||||
Args:
|
||||
id (:obj:`str`): Unique identifier for this result, 1-64 bytes.
|
||||
@@ -66,7 +73,9 @@ class InlineQueryResultVideo(InlineQueryResult):
|
||||
reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached
|
||||
to the message.
|
||||
input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the
|
||||
message to be sent instead of the video.
|
||||
message to be sent instead of the video. This field is required if
|
||||
InlineQueryResultVideo is used to send an HTML-page as a result
|
||||
(e.g., a YouTube video).
|
||||
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
|
||||
|
||||
"""
|
||||
|
||||
@@ -33,30 +33,30 @@ class InlineQueryResultVoice(InlineQueryResult):
|
||||
type (:obj:`str`): 'voice'.
|
||||
id (:obj:`str`): Unique identifier for this result, 1-64 bytes.
|
||||
voice_url (:obj:`str`): A valid URL for the voice recording.
|
||||
title (:obj:`str`): Voice message title.
|
||||
title (:obj:`str`): Recording title.
|
||||
caption (:obj:`str`): Optional. Caption, 0-1024 characters after entities parsing.
|
||||
parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show
|
||||
bold, italic, fixed-width text or inline URLs in the media caption.. See the constants
|
||||
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
|
||||
in :class:`telegram.ParseMode` for the available modes.
|
||||
voice_duration (:obj:`int`): Optional. Recording duration in seconds.
|
||||
reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached
|
||||
to the message.
|
||||
input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the
|
||||
message to be sent instead of the voice.
|
||||
message to be sent instead of the voice recording.
|
||||
|
||||
Args:
|
||||
id (:obj:`str`): Unique identifier for this result, 1-64 bytes.
|
||||
voice_url (:obj:`str`): A valid URL for the voice recording.
|
||||
title (:obj:`str`): Voice message title.
|
||||
title (:obj:`str`): Recording title.
|
||||
caption (:obj:`str`, optional): Caption, 0-1024 characters after entities parsing.
|
||||
parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show
|
||||
bold, italic, fixed-width text or inline URLs in the media caption.. See the constants
|
||||
bold, italic, fixed-width text or inline URLs in the media caption. See the constants
|
||||
in :class:`telegram.ParseMode` for the available modes.
|
||||
voice_duration (:obj:`int`, optional): Recording duration in seconds.
|
||||
reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached
|
||||
to the message.
|
||||
input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the
|
||||
message to be sent instead of the voice.
|
||||
message to be sent instead of the voice recording.
|
||||
**kwargs (:obj:`dict`): Arbitrary keyword arguments.
|
||||
|
||||
"""
|
||||
|
||||
+25
-3
@@ -23,7 +23,7 @@ from html import escape
|
||||
|
||||
from telegram import (Animation, Audio, Contact, Document, Chat, Location, PhotoSize, Sticker,
|
||||
TelegramObject, User, Video, Voice, Venue, MessageEntity, Game, Invoice,
|
||||
SuccessfulPayment, VideoNote, PassportData, Poll, InlineKeyboardMarkup)
|
||||
SuccessfulPayment, VideoNote, PassportData, Poll, InlineKeyboardMarkup, Dice)
|
||||
from telegram import ParseMode
|
||||
from telegram.utils.helpers import escape_markdown, to_timestamp, from_timestamp
|
||||
|
||||
@@ -106,6 +106,7 @@ class Message(TelegramObject):
|
||||
passport_data (:class:`telegram.PassportData`): Optional. Telegram Passport data.
|
||||
poll (:class:`telegram.Poll`): Optional. Message is a native poll,
|
||||
information about the poll.
|
||||
dice (:class:`telegram.Dice`): Optional. Message is a dice.
|
||||
reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached
|
||||
to the message.
|
||||
bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods.
|
||||
@@ -199,7 +200,7 @@ class Message(TelegramObject):
|
||||
smaller than 52 bits, so a signed 64 bit integer or double-precision float type are
|
||||
safe for storing this identifier.
|
||||
pinned_message (:class:`telegram.message`, optional): Specified message was pinned. Note
|
||||
that the Message object in this field will not contain further attr:`reply_to_message`
|
||||
that the Message object in this field will not contain further :attr:`reply_to_message`
|
||||
fields even if it is itself a reply.
|
||||
invoice (:class:`telegram.Invoice`, optional): Message is an invoice for a payment,
|
||||
information about the invoice.
|
||||
@@ -214,6 +215,7 @@ class Message(TelegramObject):
|
||||
passport_data (:class:`telegram.PassportData`, optional): Telegram Passport data.
|
||||
poll (:class:`telegram.Poll`, optional): Message is a native poll,
|
||||
information about the poll.
|
||||
dice (:class:`telegram.Dice`, optional): Message is a dice with random value from 1 to 6.
|
||||
reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached
|
||||
to the message. login_url buttons are represented as ordinary url buttons.
|
||||
default_quote (:obj:`bool`, optional): Default setting for the `quote` parameter of the
|
||||
@@ -229,7 +231,7 @@ class Message(TelegramObject):
|
||||
MESSAGE_TYPES = ['text', 'new_chat_members', 'left_chat_member', 'new_chat_title',
|
||||
'new_chat_photo', 'delete_chat_photo', 'group_chat_created',
|
||||
'supergroup_chat_created', 'channel_chat_created', 'migrate_to_chat_id',
|
||||
'migrate_from_chat_id', 'pinned_message',
|
||||
'migrate_from_chat_id', 'pinned_message', 'poll', 'dice',
|
||||
'passport_data'] + ATTACHMENT_TYPES
|
||||
|
||||
def __init__(self,
|
||||
@@ -282,6 +284,7 @@ class Message(TelegramObject):
|
||||
reply_markup=None,
|
||||
bot=None,
|
||||
default_quote=None,
|
||||
dice=None,
|
||||
**kwargs):
|
||||
# Required
|
||||
self.message_id = int(message_id)
|
||||
@@ -331,6 +334,7 @@ class Message(TelegramObject):
|
||||
self.animation = animation
|
||||
self.passport_data = passport_data
|
||||
self.poll = poll
|
||||
self.dice = dice
|
||||
self.reply_markup = reply_markup
|
||||
self.bot = bot
|
||||
self.default_quote = default_quote
|
||||
@@ -404,6 +408,7 @@ class Message(TelegramObject):
|
||||
data['successful_payment'] = SuccessfulPayment.de_json(data.get('successful_payment'), bot)
|
||||
data['passport_data'] = PassportData.de_json(data.get('passport_data'), bot)
|
||||
data['poll'] = Poll.de_json(data.get('poll'), bot)
|
||||
data['dice'] = Dice.de_json(data.get('dice'), bot)
|
||||
data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot)
|
||||
|
||||
return cls(bot=bot, **data)
|
||||
@@ -808,6 +813,23 @@ class Message(TelegramObject):
|
||||
self._quote(kwargs)
|
||||
return self.bot.send_poll(self.chat_id, *args, **kwargs)
|
||||
|
||||
def reply_dice(self, *args, **kwargs):
|
||||
"""Shortcut for::
|
||||
|
||||
bot.send_dice(update.message.chat_id, *args, **kwargs)
|
||||
|
||||
Keyword Args:
|
||||
quote (:obj:`bool`, optional): If set to ``True``, the dice is sent as an actual reply
|
||||
to this message. If ``reply_to_message_id`` is passed in ``kwargs``, this parameter
|
||||
will be ignored. Default: ``True`` in group chats and ``False`` in private chats.
|
||||
|
||||
Returns:
|
||||
:class:`telegram.Message`: On success, instance representing the message posted.
|
||||
|
||||
"""
|
||||
self._quote(kwargs)
|
||||
return self.bot.send_dice(self.chat_id, *args, **kwargs)
|
||||
|
||||
def forward(self, chat_id, *args, **kwargs):
|
||||
"""Shortcut for::
|
||||
|
||||
|
||||
@@ -32,10 +32,12 @@ class PersonalDetails(TelegramObject):
|
||||
country_code (:obj:`str`): Citizenship (ISO 3166-1 alpha-2 country code).
|
||||
residence_country_code (:obj:`str`): Country of residence (ISO 3166-1 alpha-2 country
|
||||
code).
|
||||
first_name (:obj:`str`): First Name in the language of the user's country of residence.
|
||||
middle_name (:obj:`str`): Optional. Middle Name in the language of the user's country of
|
||||
first_name_native (:obj:`str`): First Name in the language of the user's country of
|
||||
residence.
|
||||
middle_name_native (:obj:`str`): Optional. Middle Name in the language of the user's
|
||||
country of residence.
|
||||
last_name_native (:obj:`str`): Last Name in the language of the user's country of
|
||||
residence.
|
||||
last_name (:obj:`str`): Last Name in the language of the user's country of residence.
|
||||
"""
|
||||
|
||||
def __init__(self, first_name, last_name, birth_date, gender, country_code,
|
||||
|
||||
+1
-1
@@ -17,4 +17,4 @@
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
|
||||
__version__ = '12.5.1'
|
||||
__version__ = '12.6'
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.7 KiB |
Binary file not shown.
+59
-3
@@ -26,7 +26,7 @@ from future.utils import string_types
|
||||
|
||||
from telegram import (Bot, Update, ChatAction, TelegramError, User, InlineKeyboardMarkup,
|
||||
InlineKeyboardButton, InlineQueryResultArticle, InputTextMessageContent,
|
||||
ShippingOption, LabeledPrice, ChatPermissions, Poll,
|
||||
ShippingOption, LabeledPrice, ChatPermissions, Poll, BotCommand,
|
||||
InlineQueryResultDocument)
|
||||
from telegram.error import BadRequest, InvalidToken, NetworkError, RetryAfter
|
||||
from telegram.utils.helpers import from_timestamp, escape_markdown
|
||||
@@ -80,6 +80,7 @@ class TestBot(object):
|
||||
@pytest.mark.timeout(10)
|
||||
def test_get_me_and_properties(self, bot):
|
||||
get_me_bot = bot.get_me()
|
||||
commands = bot.get_my_commands()
|
||||
|
||||
assert isinstance(get_me_bot, User)
|
||||
assert get_me_bot.id == bot.id
|
||||
@@ -91,6 +92,7 @@ class TestBot(object):
|
||||
assert get_me_bot.can_read_all_group_messages == bot.can_read_all_group_messages
|
||||
assert get_me_bot.supports_inline_queries == bot.supports_inline_queries
|
||||
assert 'https://t.me/{}'.format(get_me_bot.username) == bot.link
|
||||
assert commands == bot.commands
|
||||
|
||||
@flaky(3, 1)
|
||||
@pytest.mark.timeout(10)
|
||||
@@ -174,7 +176,13 @@ class TestBot(object):
|
||||
|
||||
@flaky(3, 1)
|
||||
@pytest.mark.timeout(10)
|
||||
def test_send_and_stop_poll(self, bot, super_group_id):
|
||||
@pytest.mark.parametrize('reply_markup', [
|
||||
None,
|
||||
InlineKeyboardMarkup.from_button(InlineKeyboardButton(text='text', callback_data='data')),
|
||||
InlineKeyboardMarkup.from_button(
|
||||
InlineKeyboardButton(text='text', callback_data='data')).to_dict()
|
||||
])
|
||||
def test_send_and_stop_poll(self, bot, super_group_id, reply_markup):
|
||||
question = 'Is this a test?'
|
||||
answers = ['Yes', 'No', 'Maybe']
|
||||
message = bot.send_poll(chat_id=super_group_id, question=question, options=answers,
|
||||
@@ -190,7 +198,10 @@ class TestBot(object):
|
||||
assert not message.poll.is_closed
|
||||
assert message.poll.type == Poll.REGULAR
|
||||
|
||||
poll = bot.stop_poll(chat_id=super_group_id, message_id=message.message_id, timeout=60)
|
||||
# Since only the poll and not the complete message is returned, we can't check that the
|
||||
# reply_markup is correct. So we just test that sending doesn't give an error.
|
||||
poll = bot.stop_poll(chat_id=super_group_id, message_id=message.message_id,
|
||||
reply_markup=reply_markup, timeout=60)
|
||||
assert isinstance(poll, Poll)
|
||||
assert poll.is_closed
|
||||
assert poll.options[0].text == answers[0]
|
||||
@@ -208,6 +219,13 @@ class TestBot(object):
|
||||
assert message_quiz.poll.type == Poll.QUIZ
|
||||
assert message_quiz.poll.is_closed
|
||||
|
||||
@flaky(3, 1)
|
||||
@pytest.mark.timeout(10)
|
||||
def test_send_dice(self, bot, chat_id):
|
||||
message = bot.send_dice(chat_id)
|
||||
|
||||
assert message.dice
|
||||
|
||||
@flaky(3, 1)
|
||||
@pytest.mark.timeout(10)
|
||||
def test_send_chat_action(self, bot, chat_id):
|
||||
@@ -904,3 +922,41 @@ class TestBot(object):
|
||||
def test_send_message_default_quote(self, default_bot, chat_id):
|
||||
message = default_bot.send_message(chat_id, 'test')
|
||||
assert message.default_quote is True
|
||||
|
||||
@flaky(3, 1)
|
||||
@pytest.mark.timeout(10)
|
||||
def test_set_and_get_my_commands(self, bot):
|
||||
commands = [
|
||||
BotCommand('cmd1', 'descr1'),
|
||||
BotCommand('cmd2', 'descr2'),
|
||||
]
|
||||
bot.set_my_commands([])
|
||||
assert bot.get_my_commands() == []
|
||||
assert bot.commands == []
|
||||
assert bot.set_my_commands(commands)
|
||||
|
||||
for bc in [bot.get_my_commands(), bot.commands]:
|
||||
assert len(bc) == 2
|
||||
assert bc[0].command == 'cmd1'
|
||||
assert bc[0].description == 'descr1'
|
||||
assert bc[1].command == 'cmd2'
|
||||
assert bc[1].description == 'descr2'
|
||||
|
||||
@flaky(3, 1)
|
||||
@pytest.mark.timeout(10)
|
||||
def test_set_and_get_my_commands_strings(self, bot):
|
||||
commands = [
|
||||
['cmd1', 'descr1'],
|
||||
['cmd2', 'descr2'],
|
||||
]
|
||||
bot.set_my_commands([])
|
||||
assert bot.get_my_commands() == []
|
||||
assert bot.commands == []
|
||||
assert bot.set_my_commands(commands)
|
||||
|
||||
for bc in [bot.get_my_commands(), bot.commands]:
|
||||
assert len(bc) == 2
|
||||
assert bc[0].command == 'cmd1'
|
||||
assert bc[0].description == 'descr1'
|
||||
assert bc[1].command == 'cmd2'
|
||||
assert bc[1].description == 'descr2'
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2020
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
|
||||
import pytest
|
||||
|
||||
from telegram import BotCommand
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
def bot_command():
|
||||
return BotCommand(command='start', description='A command')
|
||||
|
||||
|
||||
class TestBotCommand(object):
|
||||
command = 'start'
|
||||
description = 'A command'
|
||||
|
||||
def test_de_json(self, bot):
|
||||
json_dict = {'command': self.command, 'description': self.description}
|
||||
bot_command = BotCommand.de_json(json_dict, bot)
|
||||
|
||||
assert bot_command.command == self.command
|
||||
assert bot_command.description == self.description
|
||||
|
||||
assert BotCommand.de_json(None, bot) is None
|
||||
|
||||
def test_to_dict(self, bot_command):
|
||||
bot_command_dict = bot_command.to_dict()
|
||||
|
||||
assert isinstance(bot_command_dict, dict)
|
||||
assert bot_command_dict['command'] == bot_command.command
|
||||
assert bot_command_dict['description'] == bot_command.description
|
||||
@@ -546,6 +546,43 @@ class TestConversationHandler(object):
|
||||
dp.job_queue.tick()
|
||||
assert handler.conversations.get((self.group.id, user1.id)) is None
|
||||
|
||||
def test_conversation_handler_timeout_update_and_context(self, cdp, bot, user1):
|
||||
context = None
|
||||
|
||||
def start_callback(u, c):
|
||||
nonlocal context, self
|
||||
context = c
|
||||
return self.start(u, c)
|
||||
|
||||
states = self.states
|
||||
timeout_handler = CommandHandler('start', None)
|
||||
states.update({ConversationHandler.TIMEOUT: [timeout_handler]})
|
||||
handler = ConversationHandler(entry_points=[CommandHandler('start', start_callback)],
|
||||
states=states, fallbacks=self.fallbacks,
|
||||
conversation_timeout=0.5)
|
||||
cdp.add_handler(handler)
|
||||
|
||||
# Start state machine, then reach timeout
|
||||
message = Message(0, user1, None, self.group, text='/start',
|
||||
entities=[MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0,
|
||||
length=len('/start'))],
|
||||
bot=bot)
|
||||
update = Update(update_id=0, message=message)
|
||||
|
||||
def timeout_callback(u, c):
|
||||
nonlocal update, context, self
|
||||
self.is_timeout = True
|
||||
assert u is update
|
||||
assert c is context
|
||||
|
||||
timeout_handler.callback = timeout_callback
|
||||
|
||||
cdp.process_update(update)
|
||||
sleep(0.5)
|
||||
cdp.job_queue.tick()
|
||||
assert handler.conversations.get((self.group.id, user1.id)) is None
|
||||
assert self.is_timeout
|
||||
|
||||
def test_conversation_timeout_keeps_extending(self, dp, bot, user1):
|
||||
handler = ConversationHandler(entry_points=self.entry_points, states=self.states,
|
||||
fallbacks=self.fallbacks, conversation_timeout=0.5)
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2020
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
|
||||
import pytest
|
||||
|
||||
from telegram import Dice
|
||||
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
def dice():
|
||||
return Dice(value=5)
|
||||
|
||||
|
||||
class TestDice(object):
|
||||
value = 4
|
||||
|
||||
def test_de_json(self, bot):
|
||||
json_dict = {'value': self.value}
|
||||
dice = Dice.de_json(json_dict, bot)
|
||||
|
||||
assert dice.value == self.value
|
||||
assert Dice.de_json(None, bot) is None
|
||||
|
||||
def test_to_dict(self, dice):
|
||||
dice_dict = dice.to_dict()
|
||||
|
||||
assert isinstance(dice_dict, dict)
|
||||
assert dice_dict['value'] == dice.value
|
||||
@@ -449,3 +449,88 @@ class TestDispatcher(object):
|
||||
with pytest.warns(TelegramDeprecationWarning):
|
||||
Dispatcher(dp.bot, dp.update_queue, job_queue=dp.job_queue, workers=0,
|
||||
use_context=False)
|
||||
|
||||
def test_error_while_persisting(self, cdp, monkeypatch):
|
||||
class OwnPersistence(BasePersistence):
|
||||
def __init__(self):
|
||||
super(OwnPersistence, self).__init__()
|
||||
self.store_user_data = True
|
||||
self.store_chat_data = True
|
||||
self.store_bot_data = True
|
||||
|
||||
def update(self, data):
|
||||
raise Exception('PersistenceError')
|
||||
|
||||
def update_bot_data(self, data):
|
||||
self.update(data)
|
||||
|
||||
def update_chat_data(self, chat_id, data):
|
||||
self.update(data)
|
||||
|
||||
def update_user_data(self, user_id, data):
|
||||
self.update(data)
|
||||
|
||||
def callback(update, context):
|
||||
pass
|
||||
|
||||
test_flag = False
|
||||
|
||||
def error(update, context):
|
||||
nonlocal test_flag
|
||||
test_flag = str(context.error) == 'PersistenceError'
|
||||
raise Exception('ErrorHandlingError')
|
||||
|
||||
def logger(message):
|
||||
assert 'uncaught error was raised while handling' in message
|
||||
|
||||
update = Update(1, message=Message(1, User(1, '', False), None, Chat(1, ''), text='Text'))
|
||||
handler = MessageHandler(Filters.all, callback)
|
||||
cdp.add_handler(handler)
|
||||
cdp.add_error_handler(error)
|
||||
monkeypatch.setattr(cdp.logger, 'exception', logger)
|
||||
|
||||
cdp.persistence = OwnPersistence()
|
||||
cdp.process_update(update)
|
||||
assert test_flag
|
||||
|
||||
def test_persisting_no_user_no_chat(self, cdp):
|
||||
class OwnPersistence(BasePersistence):
|
||||
def __init__(self):
|
||||
super(OwnPersistence, self).__init__()
|
||||
self.store_user_data = True
|
||||
self.store_chat_data = True
|
||||
self.store_bot_data = True
|
||||
self.test_flag_bot_data = False
|
||||
self.test_flag_chat_data = False
|
||||
self.test_flag_user_data = False
|
||||
|
||||
def update_bot_data(self, data):
|
||||
self.test_flag_bot_data = True
|
||||
|
||||
def update_chat_data(self, chat_id, data):
|
||||
self.test_flag_chat_data = True
|
||||
|
||||
def update_user_data(self, user_id, data):
|
||||
self.test_flag_user_data = True
|
||||
|
||||
def callback(update, context):
|
||||
pass
|
||||
|
||||
handler = MessageHandler(Filters.all, callback)
|
||||
cdp.add_handler(handler)
|
||||
cdp.persistence = OwnPersistence()
|
||||
|
||||
update = Update(1, message=Message(1, User(1, '', False), None, None, text='Text'))
|
||||
cdp.process_update(update)
|
||||
assert cdp.persistence.test_flag_bot_data
|
||||
assert cdp.persistence.test_flag_user_data
|
||||
assert not cdp.persistence.test_flag_chat_data
|
||||
|
||||
cdp.persistence.test_flag_bot_data = False
|
||||
cdp.persistence.test_flag_user_data = False
|
||||
cdp.persistence.test_flag_chat_data = False
|
||||
update = Update(1, message=Message(1, None, None, Chat(1, ''), text='Text'))
|
||||
cdp.process_update(update)
|
||||
assert cdp.persistence.test_flag_bot_data
|
||||
assert not cdp.persistence.test_flag_user_data
|
||||
assert cdp.persistence.test_flag_chat_data
|
||||
|
||||
+19
-3
@@ -20,7 +20,7 @@ import datetime
|
||||
|
||||
import pytest
|
||||
|
||||
from telegram import Message, User, Chat, MessageEntity, Document, Update
|
||||
from telegram import Message, User, Chat, MessageEntity, Document, Update, Dice
|
||||
from telegram.ext import Filters, BaseFilter
|
||||
import re
|
||||
|
||||
@@ -47,7 +47,7 @@ class TestFilters(object):
|
||||
update.message.text = '/test'
|
||||
assert (Filters.text)(update)
|
||||
|
||||
def test_filters_text_iterable(self, update):
|
||||
def test_filters_text_strings(self, update):
|
||||
update.message.text = '/test'
|
||||
assert Filters.text({'/test', 'test1'})(update)
|
||||
assert not Filters.text(['test1', 'test2'])(update)
|
||||
@@ -58,7 +58,7 @@ class TestFilters(object):
|
||||
update.message.caption = None
|
||||
assert not (Filters.caption)(update)
|
||||
|
||||
def test_filters_caption_iterable(self, update):
|
||||
def test_filters_caption_strings(self, update):
|
||||
update.message.caption = 'test'
|
||||
assert Filters.caption({'test', 'test1'})(update)
|
||||
assert not Filters.caption(['test1', 'test2'])(update)
|
||||
@@ -622,6 +622,22 @@ class TestFilters(object):
|
||||
update.message.poll = 'test'
|
||||
assert Filters.poll(update)
|
||||
|
||||
def test_filters_dice(self, update):
|
||||
update.message.dice = Dice(4)
|
||||
assert Filters.dice(update)
|
||||
update.message.dice = None
|
||||
assert not Filters.dice(update)
|
||||
|
||||
def test_filters_dice_iterable(self, update):
|
||||
update.message.dice = None
|
||||
assert not Filters.dice(5)(update)
|
||||
|
||||
update.message.dice = Dice(5)
|
||||
assert Filters.dice(5)(update)
|
||||
assert Filters.dice({5, 6})(update)
|
||||
assert not Filters.dice(1)(update)
|
||||
assert not Filters.dice([2, 3])(update)
|
||||
|
||||
def test_language_filter_single(self, update):
|
||||
update.message.from_user.language_code = 'en_US'
|
||||
assert (Filters.language('en_US'))(update)
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
import pytest
|
||||
from flaky import flaky
|
||||
|
||||
from telegram import InlineKeyboardButton, InlineKeyboardMarkup
|
||||
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, ReplyMarkup
|
||||
|
||||
|
||||
@pytest.fixture(scope='class')
|
||||
@@ -68,6 +68,26 @@ class TestInlineKeyboardMarkup(object):
|
||||
def test_expected_values(self, inline_keyboard_markup):
|
||||
assert inline_keyboard_markup.inline_keyboard == self.inline_keyboard
|
||||
|
||||
def test_expected_values_empty_switch(self, inline_keyboard_markup, bot, monkeypatch):
|
||||
def test(url, data, reply_to_message_id=None, disable_notification=None,
|
||||
reply_markup=None, timeout=None, **kwargs):
|
||||
if reply_markup is not None:
|
||||
if isinstance(reply_markup, ReplyMarkup):
|
||||
data['reply_markup'] = reply_markup.to_json()
|
||||
else:
|
||||
data['reply_markup'] = reply_markup
|
||||
|
||||
assert bool('"switch_inline_query": ""' in data['reply_markup'])
|
||||
assert bool('"switch_inline_query_current_chat": ""' in data['reply_markup'])
|
||||
|
||||
inline_keyboard_markup.inline_keyboard[0][0].callback_data = None
|
||||
inline_keyboard_markup.inline_keyboard[0][0].switch_inline_query = ''
|
||||
inline_keyboard_markup.inline_keyboard[0][1].callback_data = None
|
||||
inline_keyboard_markup.inline_keyboard[0][1].switch_inline_query_current_chat = ''
|
||||
|
||||
monkeypatch.setattr(bot, '_message', test)
|
||||
bot.send_message(123, 'test', reply_markup=inline_keyboard_markup)
|
||||
|
||||
def test_to_dict(self, inline_keyboard_markup):
|
||||
inline_keyboard_markup_dict = inline_keyboard_markup.to_dict()
|
||||
|
||||
|
||||
+21
-6
@@ -22,7 +22,7 @@ import pytest
|
||||
|
||||
from telegram import (Update, Message, User, MessageEntity, Chat, Audio, Document, Animation,
|
||||
Game, PhotoSize, Sticker, Video, Voice, VideoNote, Contact, Location, Venue,
|
||||
Invoice, SuccessfulPayment, PassportData, ParseMode, Poll, PollOption)
|
||||
Invoice, SuccessfulPayment, PassportData, ParseMode, Poll, PollOption, Dice)
|
||||
from tests.test_passport import RAW_PASSPORT_DATA
|
||||
|
||||
|
||||
@@ -97,7 +97,8 @@ def message(bot):
|
||||
'text': 'start', 'url': 'http://google.com'}, {
|
||||
'text': 'next', 'callback_data': 'abcd'}],
|
||||
[{'text': 'Cancel', 'callback_data': 'Cancel'}]]}},
|
||||
{'quote': True}
|
||||
{'quote': True},
|
||||
{'dice': Dice(4)}
|
||||
],
|
||||
ids=['forwarded_user', 'forwarded_channel', 'reply', 'edited', 'text',
|
||||
'caption_entities', 'audio', 'document', 'animation', 'game', 'photo',
|
||||
@@ -107,7 +108,7 @@ def message(bot):
|
||||
'migrated_from', 'pinned', 'invoice', 'successful_payment',
|
||||
'connected_website', 'forward_signature', 'author_signature',
|
||||
'photo_from_media_group', 'passport_data', 'poll', 'reply_markup',
|
||||
'default_quote'])
|
||||
'default_quote', 'dice'])
|
||||
def message_params(bot, request):
|
||||
return Message(message_id=TestMessage.id_,
|
||||
from_user=TestMessage.from_user,
|
||||
@@ -702,7 +703,7 @@ class TestMessage(object):
|
||||
def test_reply_poll(self, monkeypatch, message):
|
||||
def test(*args, **kwargs):
|
||||
id_ = args[0] == message.chat_id
|
||||
contact = kwargs['contact'] == 'test_poll'
|
||||
contact = kwargs['question'] == 'test_poll'
|
||||
if kwargs.get('reply_to_message_id'):
|
||||
reply = kwargs['reply_to_message_id'] == message.message_id
|
||||
else:
|
||||
@@ -710,8 +711,22 @@ class TestMessage(object):
|
||||
return id_ and contact and reply
|
||||
|
||||
monkeypatch.setattr(message.bot, 'send_poll', test)
|
||||
assert message.reply_poll(contact='test_poll')
|
||||
assert message.reply_poll(contact='test_poll', quote=True)
|
||||
assert message.reply_poll(question='test_poll')
|
||||
assert message.reply_poll(question='test_poll', quote=True)
|
||||
|
||||
def test_reply_dice(self, monkeypatch, message):
|
||||
def test(*args, **kwargs):
|
||||
id_ = args[0] == message.chat_id
|
||||
contact = kwargs['disable_notification'] is True
|
||||
if kwargs.get('reply_to_message_id'):
|
||||
reply = kwargs['reply_to_message_id'] == message.message_id
|
||||
else:
|
||||
reply = True
|
||||
return id_ and contact and reply
|
||||
|
||||
monkeypatch.setattr(message.bot, 'send_dice', test)
|
||||
assert message.reply_dice(disable_notification=True)
|
||||
assert message.reply_dice(disable_notification=True, quote=True)
|
||||
|
||||
def test_forward(self, monkeypatch, message):
|
||||
def test(*args, **kwargs):
|
||||
|
||||
@@ -29,12 +29,13 @@ import logging
|
||||
import os
|
||||
import pickle
|
||||
from collections import defaultdict
|
||||
from time import sleep
|
||||
|
||||
import pytest
|
||||
|
||||
from telegram import Update, Message, User, Chat, MessageEntity
|
||||
from telegram.ext import BasePersistence, Updater, ConversationHandler, MessageHandler, Filters, \
|
||||
PicklePersistence, CommandHandler, DictPersistence, TypeHandler
|
||||
PicklePersistence, CommandHandler, DictPersistence, TypeHandler, JobQueue
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
@@ -87,6 +88,13 @@ def updater(bot, base_persistence):
|
||||
return u
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def job_queue(bot):
|
||||
jq = JobQueue()
|
||||
yield jq
|
||||
jq.stop()
|
||||
|
||||
|
||||
class TestBasePersistence(object):
|
||||
|
||||
def test_creation(self, base_persistence):
|
||||
@@ -920,6 +928,24 @@ class TestPickelPersistence(object):
|
||||
assert nested_ch.conversations[nested_ch._get_key(update)] == 1
|
||||
assert nested_ch.conversations == pickle_persistence.conversations['name3']
|
||||
|
||||
def test_with_job(self, job_queue, cdp, pickle_persistence):
|
||||
def job_callback(context):
|
||||
context.bot_data['test1'] = '456'
|
||||
context.dispatcher.chat_data[123]['test2'] = '789'
|
||||
context.dispatcher.user_data[789]['test3'] = '123'
|
||||
|
||||
cdp.persistence = pickle_persistence
|
||||
job_queue.set_dispatcher(cdp)
|
||||
job_queue.start()
|
||||
job_queue.run_once(job_callback, 0.01)
|
||||
sleep(0.05)
|
||||
bot_data = pickle_persistence.get_bot_data()
|
||||
assert bot_data == {'test1': '456'}
|
||||
chat_data = pickle_persistence.get_chat_data()
|
||||
assert chat_data[123] == {'test2': '789'}
|
||||
user_data = pickle_persistence.get_user_data()
|
||||
assert user_data[789] == {'test3': '123'}
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def user_data_json(user_data):
|
||||
@@ -1202,3 +1228,22 @@ class TestDictPersistence(object):
|
||||
assert ch.conversations == dict_persistence.conversations['name2']
|
||||
assert nested_ch.conversations[nested_ch._get_key(update)] == 1
|
||||
assert nested_ch.conversations == dict_persistence.conversations['name3']
|
||||
|
||||
def test_with_job(self, job_queue, cdp):
|
||||
def job_callback(context):
|
||||
context.bot_data['test1'] = '456'
|
||||
context.dispatcher.chat_data[123]['test2'] = '789'
|
||||
context.dispatcher.user_data[789]['test3'] = '123'
|
||||
|
||||
dict_persistence = DictPersistence()
|
||||
cdp.persistence = dict_persistence
|
||||
job_queue.set_dispatcher(cdp)
|
||||
job_queue.start()
|
||||
job_queue.run_once(job_callback, 0.01)
|
||||
sleep(0.05)
|
||||
bot_data = dict_persistence.get_bot_data()
|
||||
assert bot_data == {'test1': '456'}
|
||||
chat_data = dict_persistence.get_chat_data()
|
||||
assert chat_data[123] == {'test2': '789'}
|
||||
user_data = dict_persistence.get_user_data()
|
||||
assert user_data[789] == {'test3': '123'}
|
||||
|
||||
+82
-9
@@ -40,6 +40,19 @@ def sticker(bot, chat_id):
|
||||
return bot.send_sticker(chat_id, sticker=f, timeout=50).sticker
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def animated_sticker_file():
|
||||
f = open('tests/data/telegram_animated_sticker.tgs', 'rb')
|
||||
yield f
|
||||
f.close()
|
||||
|
||||
|
||||
@pytest.fixture(scope='class')
|
||||
def animated_sticker(bot, chat_id):
|
||||
with open('tests/data/telegram_animated_sticker.tgs', 'rb') as f:
|
||||
return bot.send_sticker(chat_id, sticker=f, timeout=50).sticker
|
||||
|
||||
|
||||
class TestSticker(object):
|
||||
# sticker_file_url = 'https://python-telegram-bot.org/static/testfiles/telegram.webp'
|
||||
# Serving sticker from gh since our server sends wrong content_type
|
||||
@@ -245,12 +258,27 @@ class TestSticker(object):
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def sticker_set(bot):
|
||||
ss = bot.get_sticker_set('test_by_{0}'.format(bot.username))
|
||||
ss = bot.get_sticker_set('test_by_{}'.format(bot.username))
|
||||
if len(ss.stickers) > 100:
|
||||
raise Exception('stickerset is growing too large.')
|
||||
return ss
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def animated_sticker_set(bot):
|
||||
ss = bot.get_sticker_set('animated_test_by_{}'.format(bot.username))
|
||||
if len(ss.stickers) > 100:
|
||||
raise Exception('stickerset is growing too large.')
|
||||
return ss
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def sticker_set_thumb_file():
|
||||
f = open('tests/data/sticker_set_thumb.png', 'rb')
|
||||
yield f
|
||||
f.close()
|
||||
|
||||
|
||||
class TestStickerSet(object):
|
||||
title = 'Test stickers'
|
||||
is_animated = True
|
||||
@@ -258,14 +286,15 @@ class TestStickerSet(object):
|
||||
stickers = [Sticker('file_id', 'file_un_id', 512, 512, True)]
|
||||
name = 'NOTAREALNAME'
|
||||
|
||||
def test_de_json(self, bot):
|
||||
name = 'test_by_{0}'.format(bot.username)
|
||||
def test_de_json(self, bot, sticker):
|
||||
name = 'test_by_{}'.format(bot.username)
|
||||
json_dict = {
|
||||
'name': name,
|
||||
'title': self.title,
|
||||
'is_animated': self.is_animated,
|
||||
'contains_masks': self.contains_masks,
|
||||
'stickers': [x.to_dict() for x in self.stickers]
|
||||
'stickers': [x.to_dict() for x in self.stickers],
|
||||
'thumb': sticker.thumb.to_dict()
|
||||
}
|
||||
sticker_set = StickerSet.de_json(json_dict, bot)
|
||||
|
||||
@@ -274,15 +303,28 @@ class TestStickerSet(object):
|
||||
assert sticker_set.is_animated == self.is_animated
|
||||
assert sticker_set.contains_masks == self.contains_masks
|
||||
assert sticker_set.stickers == self.stickers
|
||||
assert sticker_set.thumb == sticker.thumb
|
||||
|
||||
@flaky(3, 1)
|
||||
@pytest.mark.timeout(10)
|
||||
def test_bot_methods_1(self, bot, chat_id):
|
||||
def test_bot_methods_1_png(self, bot, chat_id, sticker_file):
|
||||
with open('tests/data/telegram_sticker.png', 'rb') as f:
|
||||
file = bot.upload_sticker_file(95205500, f)
|
||||
assert file
|
||||
assert bot.add_sticker_to_set(chat_id, 'test_by_{0}'.format(bot.username),
|
||||
file.file_id, '😄')
|
||||
assert bot.add_sticker_to_set(chat_id, 'test_by_{}'.format(bot.username),
|
||||
png_sticker=file.file_id, emojis='😄')
|
||||
# Also test with file input and mask
|
||||
assert bot.add_sticker_to_set(chat_id, 'test_by_{}'.format(bot.username),
|
||||
png_sticker=sticker_file, emojis='😄',
|
||||
mask_position=MaskPosition(MaskPosition.EYES, -1, 1, 2))
|
||||
|
||||
@flaky(3, 1)
|
||||
@pytest.mark.timeout(10)
|
||||
def test_bot_methods_1_tgs(self, bot, chat_id):
|
||||
assert bot.add_sticker_to_set(
|
||||
chat_id, 'animated_test_by_{}'.format(bot.username),
|
||||
tgs_sticker=open('tests/data/telegram_animated_sticker.tgs', 'rb'),
|
||||
emojis='😄')
|
||||
|
||||
def test_sticker_set_to_dict(self, sticker_set):
|
||||
sticker_set_dict = sticker_set.to_dict()
|
||||
@@ -296,17 +338,48 @@ class TestStickerSet(object):
|
||||
|
||||
@flaky(3, 1)
|
||||
@pytest.mark.timeout(10)
|
||||
def test_bot_methods_2(self, bot, sticker_set):
|
||||
def test_bot_methods_2_png(self, bot, sticker_set):
|
||||
file_id = sticker_set.stickers[0].file_id
|
||||
assert bot.set_sticker_position_in_set(file_id, 1)
|
||||
|
||||
@flaky(3, 1)
|
||||
@pytest.mark.timeout(10)
|
||||
def test_bot_methods_2_tgs(self, bot, animated_sticker_set):
|
||||
file_id = animated_sticker_set.stickers[0].file_id
|
||||
assert bot.set_sticker_position_in_set(file_id, 1)
|
||||
|
||||
@flaky(10, 1)
|
||||
@pytest.mark.timeout(10)
|
||||
def test_bot_methods_3(self, bot, sticker_set):
|
||||
def test_bot_methods_3_png(self, bot, chat_id, sticker_set_thumb_file):
|
||||
sleep(1)
|
||||
assert bot.set_sticker_set_thumb('test_by_{}'.format(bot.username), chat_id,
|
||||
sticker_set_thumb_file)
|
||||
|
||||
@flaky(10, 1)
|
||||
@pytest.mark.timeout(10)
|
||||
def test_bot_methods_3_tgs(self, bot, chat_id, animated_sticker_file, animated_sticker_set):
|
||||
sleep(1)
|
||||
assert bot.set_sticker_set_thumb('animated_test_by_{}'.format(bot.username), chat_id,
|
||||
animated_sticker_file)
|
||||
file_id = animated_sticker_set.stickers[-1].file_id
|
||||
# also test with file input and mask
|
||||
assert bot.set_sticker_set_thumb('animated_test_by_{}'.format(bot.username), chat_id,
|
||||
file_id)
|
||||
|
||||
@flaky(10, 1)
|
||||
@pytest.mark.timeout(10)
|
||||
def test_bot_methods_4_png(self, bot, sticker_set):
|
||||
sleep(1)
|
||||
file_id = sticker_set.stickers[-1].file_id
|
||||
assert bot.delete_sticker_from_set(file_id)
|
||||
|
||||
@flaky(10, 1)
|
||||
@pytest.mark.timeout(10)
|
||||
def test_bot_methods_4_tgs(self, bot, animated_sticker_set):
|
||||
sleep(1)
|
||||
file_id = animated_sticker_set.stickers[-1].file_id
|
||||
assert bot.delete_sticker_from_set(file_id)
|
||||
|
||||
def test_get_file_instance_method(self, monkeypatch, sticker):
|
||||
def test(*args, **kwargs):
|
||||
return args[1] == sticker.file_id
|
||||
|
||||
Reference in New Issue
Block a user