mirror of
https://github.com/eternnoir/pyTelegramBotAPI.git
synced 2023-08-10 21:12:57 +03:00
Some small changes
* Fixed type warnings in some editors by changing `var: Type = None` to `var: Union[Type, None] = None` * changed some args from `obj['arg']` to `obj.get('arg')` if arg is optional * better PEP-8 compliance for less weak warnings * added tests for the new type `ChatInviteLink`
This commit is contained in:
parent
a9ae070256
commit
795f7fff7f
@ -9,6 +9,7 @@ import time
|
|||||||
import traceback
|
import traceback
|
||||||
from typing import List, Union
|
from typing import List, Union
|
||||||
|
|
||||||
|
# this imports are used to avoid circular import error
|
||||||
import telebot.util
|
import telebot.util
|
||||||
import telebot.types
|
import telebot.types
|
||||||
|
|
||||||
@ -23,7 +24,7 @@ logger.addHandler(console_output_handler)
|
|||||||
|
|
||||||
logger.setLevel(logging.ERROR)
|
logger.setLevel(logging.ERROR)
|
||||||
|
|
||||||
from telebot import apihelper, types, util
|
from telebot import apihelper, util, types
|
||||||
from telebot.handler_backends import MemoryHandlerBackend, FileHandlerBackend
|
from telebot.handler_backends import MemoryHandlerBackend, FileHandlerBackend
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@ -252,19 +253,30 @@ class TeleBot:
|
|||||||
drop_pending_updates = None, timeout=None):
|
drop_pending_updates = None, timeout=None):
|
||||||
"""
|
"""
|
||||||
Use this method to specify a url and receive incoming updates via an outgoing webhook. Whenever there is an
|
Use this method to specify a url and receive incoming updates via an outgoing webhook. Whenever there is an
|
||||||
update for the bot, we will send an HTTPS POST request to the specified url, containing a JSON-serialized Update.
|
update for the bot, we will send an HTTPS POST request to the specified url,
|
||||||
In case of an unsuccessful request, we will give up after a reasonable amount of attempts. Returns True on success.
|
containing a JSON-serialized Update.
|
||||||
|
In case of an unsuccessful request, we will give up after a reasonable amount of attempts.
|
||||||
|
Returns True on success.
|
||||||
|
|
||||||
:param url: HTTPS url to send updates to. Use an empty string to remove webhook integration
|
:param url: HTTPS url to send updates to. Use an empty string to remove webhook integration
|
||||||
:param certificate: Upload your public key certificate so that the root certificate in use can be checked. See our self-signed guide for details.
|
:param certificate: Upload your public key certificate so that the root certificate in use can be checked.
|
||||||
:param max_connections: Maximum allowed number of simultaneous HTTPS connections to the webhook for update delivery, 1-100. Defaults to 40. Use lower values to limit the load on your bot's server, and higher values to increase your bot's throughput.
|
See our self-signed guide for details.
|
||||||
:param allowed_updates: A JSON-serialized list of the update types you want your bot to receive. For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates of these types. See Update for a complete list of available update types. Specify an empty list to receive all updates regardless of type (default). If not specified, the previous setting will be used.
|
:param max_connections: Maximum allowed number of simultaneous HTTPS connections to the webhook
|
||||||
:param ip_address: The fixed IP address which will be used to send webhook requests instead of the IP address resolved through DNS
|
for update delivery, 1-100. Defaults to 40. Use lower values to limit the load on your bot's server,
|
||||||
|
and higher values to increase your bot's throughput.
|
||||||
|
:param allowed_updates: A JSON-serialized list of the update types you want your bot to receive.
|
||||||
|
For example, specify [“message”, “edited_channel_post”, “callback_query”] to only receive updates
|
||||||
|
of these types. See Update for a complete list of available update types.
|
||||||
|
Specify an empty list to receive all updates regardless of type (default).
|
||||||
|
If not specified, the previous setting will be used.
|
||||||
|
:param ip_address: The fixed IP address which will be used to send webhook requests instead of the IP address
|
||||||
|
resolved through DNS
|
||||||
:param drop_pending_updates: Pass True to drop all pending updates
|
:param drop_pending_updates: Pass True to drop all pending updates
|
||||||
:param timeout: Integer. Request connection timeout
|
:param timeout: Integer. Request connection timeout
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
return apihelper.set_webhook(self.token, url, certificate, max_connections, allowed_updates, ip_address, drop_pending_updates, timeout)
|
return apihelper.set_webhook(self.token, url, certificate, max_connections, allowed_updates, ip_address,
|
||||||
|
drop_pending_updates, timeout)
|
||||||
|
|
||||||
def delete_webhook(self, drop_pending_updates=None, timeout=None):
|
def delete_webhook(self, drop_pending_updates=None, timeout=None):
|
||||||
"""
|
"""
|
||||||
@ -330,14 +342,14 @@ class TeleBot:
|
|||||||
if self.skip_pending:
|
if self.skip_pending:
|
||||||
logger.debug('Skipped {0} pending messages'.format(self.__skip_updates()))
|
logger.debug('Skipped {0} pending messages'.format(self.__skip_updates()))
|
||||||
self.skip_pending = False
|
self.skip_pending = False
|
||||||
updates = self.get_updates(offset=(self.last_update_id + 1), timeout=timeout, long_polling_timeout = long_polling_timeout)
|
updates = self.get_updates(offset=(self.last_update_id + 1),
|
||||||
|
timeout=timeout, long_polling_timeout=long_polling_timeout)
|
||||||
self.process_new_updates(updates)
|
self.process_new_updates(updates)
|
||||||
|
|
||||||
def process_new_updates(self, updates):
|
def process_new_updates(self, updates):
|
||||||
upd_count = len(updates)
|
upd_count = len(updates)
|
||||||
logger.debug('Received {0} new updates'.format(upd_count))
|
logger.debug('Received {0} new updates'.format(upd_count))
|
||||||
if (upd_count == 0):
|
if upd_count == 0: return
|
||||||
return
|
|
||||||
|
|
||||||
new_messages = None
|
new_messages = None
|
||||||
new_edited_messages = None
|
new_edited_messages = None
|
||||||
@ -488,11 +500,13 @@ class TeleBot:
|
|||||||
|
|
||||||
:param timeout: Request connection timeout
|
:param timeout: Request connection timeout
|
||||||
:param long_polling_timeout: Timeout in seconds for long polling (see API docs)
|
:param long_polling_timeout: Timeout in seconds for long polling (see API docs)
|
||||||
:param logger_level: Custom logging level for infinity_polling logging. Use logger levels from logging as a value. None/NOTSET = no error logging
|
:param logger_level: Custom logging level for infinity_polling logging.
|
||||||
|
Use logger levels from logging as a value. None/NOTSET = no error logging
|
||||||
"""
|
"""
|
||||||
while not self.__stop_polling.is_set():
|
while not self.__stop_polling.is_set():
|
||||||
try:
|
try:
|
||||||
self.polling(none_stop=True, timeout=timeout, long_polling_timeout=long_polling_timeout, *args, **kwargs)
|
self.polling(none_stop=True, timeout=timeout, long_polling_timeout=long_polling_timeout,
|
||||||
|
*args, **kwargs)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if logger_level and logger_level >= logging.ERROR:
|
if logger_level and logger_level >= logging.ERROR:
|
||||||
logger.error("Infinity polling exception: %s", str(e))
|
logger.error("Infinity polling exception: %s", str(e))
|
||||||
@ -590,7 +604,7 @@ class TeleBot:
|
|||||||
self.worker_pool.clear_exceptions() #*
|
self.worker_pool.clear_exceptions() #*
|
||||||
logger.info('Stopped polling.')
|
logger.info('Stopped polling.')
|
||||||
|
|
||||||
def __non_threaded_polling(self, non_stop=False, interval=0, timeout = None, long_polling_timeout = None):
|
def __non_threaded_polling(self, non_stop=False, interval=0, timeout=None, long_polling_timeout=None):
|
||||||
logger.info('Started polling.')
|
logger.info('Started polling.')
|
||||||
self.__stop_polling.clear()
|
self.__stop_polling.clear()
|
||||||
error_interval = 0.25
|
error_interval = 0.25
|
||||||
@ -675,7 +689,8 @@ class TeleBot:
|
|||||||
def log_out(self) -> bool:
|
def log_out(self) -> bool:
|
||||||
"""
|
"""
|
||||||
Use this method to log out from the cloud Bot API server before launching the bot locally.
|
Use this method to log out from the cloud Bot API server before launching the bot locally.
|
||||||
You MUST log out the bot before running it locally, otherwise there is no guarantee that the bot will receive updates.
|
You MUST log out the bot before running it locally, otherwise there is no guarantee
|
||||||
|
that the bot will receive updates.
|
||||||
After a successful call, you can immediately log in on a local server,
|
After a successful call, you can immediately log in on a local server,
|
||||||
but will not be able to log in back to the cloud Bot API server for 10 minutes.
|
but will not be able to log in back to the cloud Bot API server for 10 minutes.
|
||||||
Returns True on success.
|
Returns True on success.
|
||||||
@ -685,13 +700,13 @@ class TeleBot:
|
|||||||
def close(self) -> bool:
|
def close(self) -> bool:
|
||||||
"""
|
"""
|
||||||
Use this method to close the bot instance before moving it from one local server to another.
|
Use this method to close the bot instance before moving it from one local server to another.
|
||||||
You need to delete the webhook before calling this method to ensure that the bot isn't launched again after server restart.
|
You need to delete the webhook before calling this method to ensure that the bot isn't launched again
|
||||||
|
after server restart.
|
||||||
The method will return error 429 in the first 10 minutes after the bot is launched.
|
The method will return error 429 in the first 10 minutes after the bot is launched.
|
||||||
Returns True on success.
|
Returns True on success.
|
||||||
"""
|
"""
|
||||||
return apihelper.close(self.token)
|
return apihelper.close(self.token)
|
||||||
|
|
||||||
|
|
||||||
def get_user_profile_photos(self, user_id, offset=None, limit=None) -> types.UserProfilePhotos:
|
def get_user_profile_photos(self, user_id, offset=None, limit=None) -> types.UserProfilePhotos:
|
||||||
"""
|
"""
|
||||||
Retrieves the user profile photos of the person with 'user_id'
|
Retrieves the user profile photos of the person with 'user_id'
|
||||||
@ -807,7 +822,8 @@ class TeleBot:
|
|||||||
apihelper.send_message(self.token, chat_id, text, disable_web_page_preview, reply_to_message_id,
|
apihelper.send_message(self.token, chat_id, text, disable_web_page_preview, reply_to_message_id,
|
||||||
reply_markup, parse_mode, disable_notification, timeout))
|
reply_markup, parse_mode, disable_notification, timeout))
|
||||||
|
|
||||||
def forward_message(self, chat_id, from_chat_id, message_id, disable_notification=None, timeout=None) -> types.Message:
|
def forward_message(self, chat_id, from_chat_id, message_id,
|
||||||
|
disable_notification=None, timeout=None) -> types.Message:
|
||||||
"""
|
"""
|
||||||
Use this method to forward messages of any kind.
|
Use this method to forward messages of any kind.
|
||||||
:param disable_notification:
|
:param disable_notification:
|
||||||
@ -897,7 +913,8 @@ class TeleBot:
|
|||||||
reply_to_message_id=None, reply_markup=None, parse_mode=None, disable_notification=None,
|
reply_to_message_id=None, reply_markup=None, parse_mode=None, disable_notification=None,
|
||||||
timeout=None, thumb=None) -> types.Message:
|
timeout=None, thumb=None) -> types.Message:
|
||||||
"""
|
"""
|
||||||
Use this method to send audio files, if you want Telegram clients to display them in the music player. Your audio must be in the .mp3 format.
|
Use this method to send audio files, if you want Telegram clients to display them in the music player.
|
||||||
|
Your audio must be in the .mp3 format.
|
||||||
:param chat_id:Unique identifier for the message recipient
|
:param chat_id:Unique identifier for the message recipient
|
||||||
:param audio:Audio file to send.
|
:param audio:Audio file to send.
|
||||||
:param caption:
|
:param caption:
|
||||||
@ -909,7 +926,7 @@ class TeleBot:
|
|||||||
:param parse_mode
|
:param parse_mode
|
||||||
:param disable_notification:
|
:param disable_notification:
|
||||||
:param timeout:
|
:param timeout:
|
||||||
:param thumb:
|
:param thumb:
|
||||||
:return: Message
|
:return: Message
|
||||||
"""
|
"""
|
||||||
parse_mode = self.parse_mode if (parse_mode is None) else parse_mode
|
parse_mode = self.parse_mode if (parse_mode is None) else parse_mode
|
||||||
@ -921,7 +938,8 @@ class TeleBot:
|
|||||||
def send_voice(self, chat_id, voice, caption=None, duration=None, reply_to_message_id=None, reply_markup=None,
|
def send_voice(self, chat_id, voice, caption=None, duration=None, reply_to_message_id=None, reply_markup=None,
|
||||||
parse_mode=None, disable_notification=None, timeout=None) -> types.Message:
|
parse_mode=None, disable_notification=None, timeout=None) -> types.Message:
|
||||||
"""
|
"""
|
||||||
Use this method to send audio files, if you want Telegram clients to display the file as a playable voice message.
|
Use this method to send audio files, if you want Telegram clients to display the file
|
||||||
|
as a playable voice message.
|
||||||
:param chat_id:Unique identifier for the message recipient.
|
:param chat_id:Unique identifier for the message recipient.
|
||||||
:param voice:
|
:param voice:
|
||||||
:param caption:
|
:param caption:
|
||||||
@ -984,7 +1002,8 @@ class TeleBot:
|
|||||||
"""
|
"""
|
||||||
Use this method to send video files, Telegram clients support mp4 videos.
|
Use this method to send video files, Telegram clients support mp4 videos.
|
||||||
:param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id
|
:param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id
|
||||||
:param data: InputFile or String : Video to send. You can either pass a file_id as String to resend a video that is already on the Telegram server
|
:param data: InputFile or String : Video to send. You can either pass a file_id as String to resend
|
||||||
|
a video that is already on the Telegram server
|
||||||
:param duration: Integer : Duration of sent video in seconds
|
:param duration: Integer : Duration of sent video in seconds
|
||||||
:param caption: String : Video caption (may also be used when resending videos by file_id).
|
:param caption: String : Video caption (may also be used when resending videos by file_id).
|
||||||
:param parse_mode:
|
:param parse_mode:
|
||||||
@ -993,7 +1012,7 @@ class TeleBot:
|
|||||||
:param reply_markup:
|
:param reply_markup:
|
||||||
:param disable_notification:
|
:param disable_notification:
|
||||||
:param timeout:
|
:param timeout:
|
||||||
:param thumb: InputFile or String : Thumbnail of the file sent
|
:param thumb: InputFile or String : Thumbnail of the file sent
|
||||||
:param width:
|
:param width:
|
||||||
:param height:
|
:param height:
|
||||||
:return:
|
:return:
|
||||||
@ -1011,7 +1030,8 @@ class TeleBot:
|
|||||||
"""
|
"""
|
||||||
Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound).
|
Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound).
|
||||||
:param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id
|
:param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id
|
||||||
:param animation: InputFile or String : Animation to send. You can either pass a file_id as String to resend an animation that is already on the Telegram server
|
:param animation: InputFile or String : Animation to send. You can either pass a file_id as String to resend an
|
||||||
|
animation that is already on the Telegram server
|
||||||
:param duration: Integer : Duration of sent video in seconds
|
:param duration: Integer : Duration of sent video in seconds
|
||||||
:param caption: String : Animation caption (may also be used when resending animation by file_id).
|
:param caption: String : Animation caption (may also be used when resending animation by file_id).
|
||||||
:param parse_mode:
|
:param parse_mode:
|
||||||
@ -1025,16 +1045,18 @@ class TeleBot:
|
|||||||
parse_mode = self.parse_mode if (parse_mode is None) else parse_mode
|
parse_mode = self.parse_mode if (parse_mode is None) else parse_mode
|
||||||
|
|
||||||
return types.Message.de_json(
|
return types.Message.de_json(
|
||||||
apihelper.send_animation(self.token, chat_id, animation, duration, caption, reply_to_message_id, reply_markup,
|
apihelper.send_animation(self.token, chat_id, animation, duration, caption, reply_to_message_id,
|
||||||
parse_mode, disable_notification, timeout, thumb))
|
reply_markup, parse_mode, disable_notification, timeout, thumb))
|
||||||
|
|
||||||
def send_video_note(self, chat_id, data, duration=None, length=None,
|
def send_video_note(self, chat_id, data, duration=None, length=None,
|
||||||
reply_to_message_id=None, reply_markup=None,
|
reply_to_message_id=None, reply_markup=None,
|
||||||
disable_notification=None, timeout=None, thumb=None) -> types.Message:
|
disable_notification=None, timeout=None, thumb=None) -> types.Message:
|
||||||
"""
|
"""
|
||||||
As of v.4.0, Telegram clients support rounded square mp4 videos of up to 1 minute long. Use this method to send video messages.
|
As of v.4.0, Telegram clients support rounded square mp4 videos of up to 1 minute long. Use this method to send
|
||||||
|
video messages.
|
||||||
:param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id
|
:param chat_id: Integer : Unique identifier for the message recipient — User or GroupChat id
|
||||||
:param data: InputFile or String : Video note to send. You can either pass a file_id as String to resend a video that is already on the Telegram server
|
:param data: InputFile or String : Video note to send. You can either pass a file_id as String to resend
|
||||||
|
a video that is already on the Telegram server
|
||||||
:param duration: Integer : Duration of sent video in seconds
|
:param duration: Integer : Duration of sent video in seconds
|
||||||
:param length: Integer : Video width and height, Can't be None and should be in range of (0, 640)
|
:param length: Integer : Video width and height, Can't be None and should be in range of (0, 640)
|
||||||
:param reply_to_message_id:
|
:param reply_to_message_id:
|
||||||
@ -1133,7 +1155,8 @@ class TeleBot:
|
|||||||
:param title: String : Name of the venue
|
:param title: String : Name of the venue
|
||||||
:param address: String : Address of the venue
|
:param address: String : Address of the venue
|
||||||
:param foursquare_id: String : Foursquare identifier of the venue
|
:param foursquare_id: String : Foursquare identifier of the venue
|
||||||
:param foursquare_type: Foursquare type of the venue, if known. (For example, “arts_entertainment/default”, “arts_entertainment/aquarium” or “food/icecream”.)
|
:param foursquare_type: Foursquare type of the venue, if known. (For example, “arts_entertainment/default”,
|
||||||
|
“arts_entertainment/aquarium” or “food/icecream”.)
|
||||||
:param disable_notification:
|
:param disable_notification:
|
||||||
:param reply_to_message_id:
|
:param reply_to_message_id:
|
||||||
:param reply_markup:
|
:param reply_markup:
|
||||||
@ -1162,7 +1185,8 @@ class TeleBot:
|
|||||||
its typing status).
|
its typing status).
|
||||||
:param chat_id:
|
:param chat_id:
|
||||||
:param action: One of the following strings: 'typing', 'upload_photo', 'record_video', 'upload_video',
|
:param action: One of the following strings: 'typing', 'upload_photo', 'record_video', 'upload_video',
|
||||||
'record_audio', 'upload_audio', 'upload_document', 'find_location', 'record_video_note', 'upload_video_note'.
|
'record_audio', 'upload_audio', 'upload_document', 'find_location', 'record_video_note',
|
||||||
|
'upload_video_note'.
|
||||||
:param timeout:
|
:param timeout:
|
||||||
:return: API reply. :type: boolean
|
:return: API reply. :type: boolean
|
||||||
"""
|
"""
|
||||||
@ -1187,7 +1211,8 @@ class TeleBot:
|
|||||||
the user is not a member of the chat, but will be able to join it. So if the user is a member of the chat
|
the user is not a member of the chat, but will be able to join it. So if the user is a member of the chat
|
||||||
they will also be removed from the chat. If you don't want this, use the parameter only_if_banned.
|
they will also be removed from the chat. If you don't want this, use the parameter only_if_banned.
|
||||||
|
|
||||||
:param chat_id: Unique identifier for the target group or username of the target supergroup or channel (in the format @username)
|
:param chat_id: Unique identifier for the target group or username of the target supergroup or channel
|
||||||
|
(in the format @username)
|
||||||
:param user_id: Unique identifier of the target user
|
:param user_id: Unique identifier of the target user
|
||||||
:param only_if_banned: Do nothing if the user is not banned
|
:param only_if_banned: Do nothing if the user is not banned
|
||||||
:return: True on success
|
:return: True on success
|
||||||
@ -1219,9 +1244,10 @@ class TeleBot:
|
|||||||
use inline bots, implies can_send_media_messages
|
use inline bots, implies can_send_media_messages
|
||||||
:param can_add_web_page_previews: Pass True, if the user may add web page previews to their messages,
|
:param can_add_web_page_previews: Pass True, if the user may add web page previews to their messages,
|
||||||
implies can_send_media_messages
|
implies can_send_media_messages
|
||||||
:param can_change_info: Pass True, if the user is allowed to change the chat title, photo and other settings. Ignored in public supergroups
|
:param can_change_info: Pass True, if the user is allowed to change the chat title, photo and other settings.
|
||||||
:param can_invite_users: Pass True, if the user is allowed to invite new users to the chat,
|
Ignored in public supergroups
|
||||||
implies can_invite_users
|
:param can_invite_users: Pass True, if the user is allowed to invite new users to the chat,
|
||||||
|
implies can_invite_users
|
||||||
:param can_pin_messages: Pass True, if the user is allowed to pin messages. Ignored in public supergroups
|
:param can_pin_messages: Pass True, if the user is allowed to pin messages. Ignored in public supergroups
|
||||||
:return: True on success
|
:return: True on success
|
||||||
"""
|
"""
|
||||||
@ -1463,9 +1489,13 @@ class TeleBot:
|
|||||||
return result
|
return result
|
||||||
return types.Message.de_json(result)
|
return types.Message.de_json(result)
|
||||||
|
|
||||||
def edit_message_media(self, media, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None) -> Union[types.Message, bool]:
|
def edit_message_media(self, media, chat_id=None, message_id=None,
|
||||||
|
inline_message_id=None, reply_markup=None) -> Union[types.Message, bool]:
|
||||||
"""
|
"""
|
||||||
Use this method to edit animation, audio, document, photo, or video messages. If a message is a part of a message album, then it can be edited only to a photo or a video. Otherwise, message type can be changed arbitrarily. When inline message is edited, new file can't be uploaded. Use previously uploaded file via its file_id or specify a URL.
|
Use this method to edit animation, audio, document, photo, or video messages.
|
||||||
|
If a message is a part of a message album, then it can be edited only to a photo or a video.
|
||||||
|
Otherwise, message type can be changed arbitrarily. When inline message is edited, new file can't be uploaded.
|
||||||
|
Use previously uploaded file via its file_id or specify a URL.
|
||||||
:param media:
|
:param media:
|
||||||
:param chat_id:
|
:param chat_id:
|
||||||
:param message_id:
|
:param message_id:
|
||||||
@ -1478,7 +1508,8 @@ class TeleBot:
|
|||||||
return result
|
return result
|
||||||
return types.Message.de_json(result)
|
return types.Message.de_json(result)
|
||||||
|
|
||||||
def edit_message_reply_markup(self, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None) -> Union[types.Message, bool]:
|
def edit_message_reply_markup(self, chat_id=None, message_id=None,
|
||||||
|
inline_message_id=None, reply_markup=None) -> Union[types.Message, bool]:
|
||||||
"""
|
"""
|
||||||
Use this method to edit only the reply markup of messages.
|
Use this method to edit only the reply markup of messages.
|
||||||
:param chat_id:
|
:param chat_id:
|
||||||
@ -1523,13 +1554,14 @@ class TeleBot:
|
|||||||
:param disable_edit_message:
|
:param disable_edit_message:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
result = apihelper.set_game_score(self.token, user_id, score, force, disable_edit_message, chat_id, message_id,
|
result = apihelper.set_game_score(self.token, user_id, score, force, disable_edit_message, chat_id,
|
||||||
inline_message_id)
|
message_id, inline_message_id)
|
||||||
if type(result) == bool:
|
if type(result) == bool:
|
||||||
return result
|
return result
|
||||||
return types.Message.de_json(result)
|
return types.Message.de_json(result)
|
||||||
|
|
||||||
def get_game_high_scores(self, user_id, chat_id=None, message_id=None, inline_message_id=None) -> List[types.GameHighScore]:
|
def get_game_high_scores(self, user_id, chat_id=None,
|
||||||
|
message_id=None, inline_message_id=None) -> List[types.GameHighScore]:
|
||||||
"""
|
"""
|
||||||
Gets top points and game play
|
Gets top points and game play
|
||||||
:param user_id:
|
:param user_id:
|
||||||
@ -1555,12 +1587,17 @@ class TeleBot:
|
|||||||
:param chat_id: Unique identifier for the target private chat
|
:param chat_id: Unique identifier for the target private chat
|
||||||
:param title: Product name
|
:param title: Product name
|
||||||
:param description: Product description
|
:param description: Product description
|
||||||
:param invoice_payload: Bot-defined invoice payload, 1-128 bytes. This will not be displayed to the user, use for your internal processes.
|
:param invoice_payload: Bot-defined invoice payload, 1-128 bytes. This will not be displayed to the user,
|
||||||
|
use for your internal processes.
|
||||||
:param provider_token: Payments provider token, obtained via @Botfather
|
:param provider_token: Payments provider token, obtained via @Botfather
|
||||||
:param currency: Three-letter ISO 4217 currency code, see https://core.telegram.org/bots/payments#supported-currencies
|
:param currency: Three-letter ISO 4217 currency code,
|
||||||
:param prices: Price breakdown, a list of components (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.)
|
see https://core.telegram.org/bots/payments#supported-currencies
|
||||||
:param start_parameter: Unique deep-linking parameter that can be used to generate this invoice when used as a start parameter
|
:param prices: Price breakdown, a list of components
|
||||||
:param photo_url: URL of the product photo for the invoice. Can be a photo of the goods or a marketing image for a service. People like it better when they see what they are paying for.
|
(e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.)
|
||||||
|
:param start_parameter: Unique deep-linking parameter that can be used to generate this invoice
|
||||||
|
when used as a start parameter
|
||||||
|
:param photo_url: URL of the product photo for the invoice. Can be a photo of the goods
|
||||||
|
or a marketing image for a service. People like it better when they see what they are paying for.
|
||||||
:param photo_size: Photo size
|
:param photo_size: Photo size
|
||||||
:param photo_width: Photo width
|
:param photo_width: Photo width
|
||||||
:param photo_height: Photo height
|
:param photo_height: Photo height
|
||||||
@ -1573,8 +1610,10 @@ class TeleBot:
|
|||||||
:param send_email_to_provider: Pass True, if user's email address should be sent to provider
|
:param send_email_to_provider: Pass True, if user's email address should be sent to provider
|
||||||
:param disable_notification: Sends the message silently. Users will receive a notification with no sound.
|
:param disable_notification: Sends the message silently. Users will receive a notification with no sound.
|
||||||
:param reply_to_message_id: If the message is a reply, ID of the original message
|
:param reply_to_message_id: If the message is a reply, ID of the original message
|
||||||
:param reply_markup: A JSON-serialized object for an inline keyboard. If empty, one 'Pay total price' button will be shown. If not empty, the first button must be a Pay button
|
:param reply_markup: A JSON-serialized object for an inline keyboard. If empty,
|
||||||
:param provider_data: A JSON-serialized data about the invoice, which will be shared with the payment provider. A detailed description of required fields should be provided by the payment provider.
|
one 'Pay total price' button will be shown. If not empty, the first button must be a Pay button
|
||||||
|
:param provider_data: A JSON-serialized data about the invoice, which will be shared with the payment provider.
|
||||||
|
A detailed description of required fields should be provided by the payment provider.
|
||||||
:param timeout:
|
:param timeout:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
@ -1651,7 +1690,7 @@ class TeleBot:
|
|||||||
:param ok:
|
:param ok:
|
||||||
:param error_message:
|
:param error_message:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
return apihelper.answer_pre_checkout_query(self.token, pre_checkout_query_id, ok, error_message)
|
return apihelper.answer_pre_checkout_query(self.token, pre_checkout_query_id, ok, error_message)
|
||||||
|
|
||||||
def edit_message_caption(self, caption, chat_id=None, message_id=None, inline_message_id=None,
|
def edit_message_caption(self, caption, chat_id=None, message_id=None, inline_message_id=None,
|
||||||
@ -1677,7 +1716,7 @@ class TeleBot:
|
|||||||
def reply_to(self, message, text, **kwargs) -> types.Message:
|
def reply_to(self, message, text, **kwargs) -> types.Message:
|
||||||
"""
|
"""
|
||||||
Convenience function for `send_message(message.chat.id, text, reply_to_message_id=message.message_id, **kwargs)`
|
Convenience function for `send_message(message.chat.id, text, reply_to_message_id=message.message_id, **kwargs)`
|
||||||
:param message:
|
:param message:
|
||||||
:param text:
|
:param text:
|
||||||
:param kwargs:
|
:param kwargs:
|
||||||
:return:
|
:return:
|
||||||
@ -1691,11 +1730,14 @@ class TeleBot:
|
|||||||
No more than 50 results per query are allowed.
|
No more than 50 results per query are allowed.
|
||||||
:param inline_query_id: Unique identifier for the answered query
|
:param inline_query_id: Unique identifier for the answered query
|
||||||
:param results: Array of results for the inline query
|
:param results: Array of results for the inline query
|
||||||
:param cache_time: The maximum amount of time in seconds that the result of the inline query may be cached on the server.
|
:param cache_time: The maximum amount of time in seconds that the result of the inline query
|
||||||
:param is_personal: Pass True, if results may be cached on the server side only for the user that sent the query.
|
may be cached on the server.
|
||||||
:param next_offset: Pass the offset that a client should send in the next query with the same text to receive more results.
|
:param is_personal: Pass True, if results may be cached on the server side only for
|
||||||
|
the user that sent the query.
|
||||||
|
:param next_offset: Pass the offset that a client should send in the next query with the same text
|
||||||
|
to receive more results.
|
||||||
:param switch_pm_parameter: If passed, clients will display a button with specified text that switches the user
|
:param switch_pm_parameter: If passed, clients will display a button with specified text that switches the user
|
||||||
to a private chat with the bot and sends the bot a start message with the parameter switch_pm_parameter
|
to a private chat with the bot and sends the bot a start message with the parameter switch_pm_parameter
|
||||||
:param switch_pm_text: Parameter for the start message sent to the bot when user presses the switch button
|
:param switch_pm_text: Parameter for the start message sent to the bot when user presses the switch button
|
||||||
:return: True means success.
|
:return: True means success.
|
||||||
"""
|
"""
|
||||||
@ -1973,18 +2015,21 @@ class TeleBot:
|
|||||||
bot.send_message(message.chat.id, 'Did someone call for help?')
|
bot.send_message(message.chat.id, 'Did someone call for help?')
|
||||||
|
|
||||||
# Handle all sent documents of type 'text/plain'.
|
# Handle all sent documents of type 'text/plain'.
|
||||||
@bot.message_handler(func=lambda message: message.document.mime_type == 'text/plain', content_types=['document'])
|
@bot.message_handler(func=lambda message: message.document.mime_type == 'text/plain',
|
||||||
|
content_types=['document'])
|
||||||
def command_handle_document(message):
|
def command_handle_document(message):
|
||||||
bot.send_message(message.chat.id, 'Document received, sir!')
|
bot.send_message(message.chat.id, 'Document received, sir!')
|
||||||
|
|
||||||
# Handle all other messages.
|
# Handle all other messages.
|
||||||
@bot.message_handler(func=lambda message: True, content_types=['audio', 'photo', 'voice', 'video', 'document', 'text', 'location', 'contact', 'sticker'])
|
@bot.message_handler(func=lambda message: True, content_types=['audio', 'photo', 'voice', 'video', 'document',
|
||||||
|
'text', 'location', 'contact', 'sticker'])
|
||||||
def default_command(message):
|
def default_command(message):
|
||||||
bot.send_message(message.chat.id, "This is the default command handler.")
|
bot.send_message(message.chat.id, "This is the default command handler.")
|
||||||
|
|
||||||
:param commands: Optional list of strings (commands to handle).
|
:param commands: Optional list of strings (commands to handle).
|
||||||
:param regexp: Optional regular expression.
|
:param regexp: Optional regular expression.
|
||||||
:param func: Optional lambda function. The lambda receives the message to test as the first parameter. It must return True if the command should handle the message.
|
:param func: Optional lambda function. The lambda receives the message to test as the first parameter.
|
||||||
|
It must return True if the command should handle the message.
|
||||||
:param content_types: Supported message content types. Must be a list. Defaults to ['text'].
|
:param content_types: Supported message content types. Must be a list. Defaults to ['text'].
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
180
telebot/types.py
180
telebot/types.py
@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from typing import Dict, List
|
from typing import Dict, List, Union
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import ujson as json
|
import ujson as json
|
||||||
@ -92,7 +92,7 @@ class JsonDeserializable(object):
|
|||||||
class Update(JsonDeserializable):
|
class Update(JsonDeserializable):
|
||||||
@classmethod
|
@classmethod
|
||||||
def de_json(cls, json_string):
|
def de_json(cls, json_string):
|
||||||
if (json_string is None): return None
|
if json_string is None: return None
|
||||||
obj = cls.check_json(json_string)
|
obj = cls.check_json(json_string)
|
||||||
update_id = obj['update_id']
|
update_id = obj['update_id']
|
||||||
message = Message.de_json(obj.get('message'))
|
message = Message.de_json(obj.get('message'))
|
||||||
@ -128,7 +128,7 @@ class Update(JsonDeserializable):
|
|||||||
class WebhookInfo(JsonDeserializable):
|
class WebhookInfo(JsonDeserializable):
|
||||||
@classmethod
|
@classmethod
|
||||||
def de_json(cls, json_string):
|
def de_json(cls, json_string):
|
||||||
if (json_string is None): return None
|
if json_string is None: return None
|
||||||
obj = cls.check_json(json_string)
|
obj = cls.check_json(json_string)
|
||||||
url = obj['url']
|
url = obj['url']
|
||||||
has_custom_certificate = obj['has_custom_certificate']
|
has_custom_certificate = obj['has_custom_certificate']
|
||||||
@ -156,7 +156,7 @@ class WebhookInfo(JsonDeserializable):
|
|||||||
class User(JsonDeserializable, Dictionaryable, JsonSerializable):
|
class User(JsonDeserializable, Dictionaryable, JsonSerializable):
|
||||||
@classmethod
|
@classmethod
|
||||||
def de_json(cls, json_string):
|
def de_json(cls, json_string):
|
||||||
if (json_string is None): return None
|
if json_string is None: return None
|
||||||
obj = cls.check_json(json_string)
|
obj = cls.check_json(json_string)
|
||||||
return cls(**obj)
|
return cls(**obj)
|
||||||
|
|
||||||
@ -197,7 +197,7 @@ class User(JsonDeserializable, Dictionaryable, JsonSerializable):
|
|||||||
class GroupChat(JsonDeserializable):
|
class GroupChat(JsonDeserializable):
|
||||||
@classmethod
|
@classmethod
|
||||||
def de_json(cls, json_string):
|
def de_json(cls, json_string):
|
||||||
if (json_string is None): return None
|
if json_string is None: return None
|
||||||
obj = cls.check_json(json_string)
|
obj = cls.check_json(json_string)
|
||||||
return cls(**obj)
|
return cls(**obj)
|
||||||
|
|
||||||
@ -249,8 +249,7 @@ class Chat(JsonDeserializable):
|
|||||||
class MessageID(JsonDeserializable):
|
class MessageID(JsonDeserializable):
|
||||||
@classmethod
|
@classmethod
|
||||||
def de_json(cls, json_string):
|
def de_json(cls, json_string):
|
||||||
if(json_string is None):
|
if json_string is None: return None
|
||||||
return None
|
|
||||||
obj = cls.check_json(json_string)
|
obj = cls.check_json(json_string)
|
||||||
message_id = obj['message_id']
|
message_id = obj['message_id']
|
||||||
return cls(message_id)
|
return cls(message_id)
|
||||||
@ -262,7 +261,7 @@ class MessageID(JsonDeserializable):
|
|||||||
class Message(JsonDeserializable):
|
class Message(JsonDeserializable):
|
||||||
@classmethod
|
@classmethod
|
||||||
def de_json(cls, json_string):
|
def de_json(cls, json_string):
|
||||||
if (json_string is None): return None
|
if json_string is None: return None
|
||||||
obj = cls.check_json(json_string)
|
obj = cls.check_json(json_string)
|
||||||
message_id = obj['message_id']
|
message_id = obj['message_id']
|
||||||
from_user = User.de_json(obj.get('from'))
|
from_user = User.de_json(obj.get('from'))
|
||||||
@ -306,7 +305,8 @@ class Message(JsonDeserializable):
|
|||||||
opts['document'] = Document.de_json(obj['document'])
|
opts['document'] = Document.de_json(obj['document'])
|
||||||
content_type = 'document'
|
content_type = 'document'
|
||||||
if 'animation' in obj:
|
if 'animation' in obj:
|
||||||
# Document content type accompanies "animation", so "animation" should be checked below "document" to override it
|
# Document content type accompanies "animation",
|
||||||
|
# so "animation" should be checked below "document" to override it
|
||||||
opts['animation'] = Animation.de_json(obj['animation'])
|
opts['animation'] = Animation.de_json(obj['animation'])
|
||||||
content_type = 'animation'
|
content_type = 'animation'
|
||||||
if 'game' in obj:
|
if 'game' in obj:
|
||||||
@ -424,49 +424,49 @@ class Message(JsonDeserializable):
|
|||||||
self.from_user: User = from_user
|
self.from_user: User = from_user
|
||||||
self.date: int = date
|
self.date: int = date
|
||||||
self.chat: Chat = chat
|
self.chat: Chat = chat
|
||||||
self.forward_from: User = None
|
self.forward_from: Union[User, None] = None
|
||||||
self.forward_from_chat: Chat = None
|
self.forward_from_chat: Union[Chat, None] = None
|
||||||
self.forward_from_message_id: int = None
|
self.forward_from_message_id: Union[int, None] = None
|
||||||
self.forward_signature: str = None
|
self.forward_signature: Union[str, None] = None
|
||||||
self.forward_sender_name: str = None
|
self.forward_sender_name: Union[str, None] = None
|
||||||
self.forward_date: int = None
|
self.forward_date: Union[int, None] = None
|
||||||
self.reply_to_message: Message = None
|
self.reply_to_message: Union[Message, None] = None
|
||||||
self.via_bot: User = None
|
self.via_bot: Union[User, None] = None
|
||||||
self.edit_date: int = None
|
self.edit_date: Union[int, None] = None
|
||||||
self.media_group_id: str = None
|
self.media_group_id: Union[str, None] = None
|
||||||
self.author_signature: str = None
|
self.author_signature: Union[str, None] = None
|
||||||
self.text: str = None
|
self.text: Union[str, None] = None
|
||||||
self.entities: List[MessageEntity] = None
|
self.entities: Union[List[MessageEntity], None] = None
|
||||||
self.caption_entities: List[MessageEntity] = None
|
self.caption_entities: Union[List[MessageEntity], None] = None
|
||||||
self.audio: Audio = None
|
self.audio: Union[Audio, None] = None
|
||||||
self.document: Document = None
|
self.document: Union[Document, None] = None
|
||||||
self.photo: List[PhotoSize] = None
|
self.photo: Union[List[PhotoSize], None] = None
|
||||||
self.sticker: Sticker = None
|
self.sticker: Union[Sticker, None] = None
|
||||||
self.video: Video = None
|
self.video: Union[Video, None] = None
|
||||||
self.video_note: VideoNote = None
|
self.video_note: Union[VideoNote, None] = None
|
||||||
self.voice: Voice = None
|
self.voice: Union[Voice, None] = None
|
||||||
self.caption: str = None
|
self.caption: Union[str, None] = None
|
||||||
self.contact: Contact = None
|
self.contact: Union[Contact, None] = None
|
||||||
self.location: Location = None
|
self.location: Union[Location, None] = None
|
||||||
self.venue: Venue = None
|
self.venue: Union[Venue, None] = None
|
||||||
self.animation: Animation = None
|
self.animation: Union[Animation, None] = None
|
||||||
self.dice: Dice = None
|
self.dice: Union[Dice, None] = None
|
||||||
self.new_chat_member: User = None # Deprecated since Bot API 3.0. Not processed anymore
|
self.new_chat_member: Union[User, None] = None # Deprecated since Bot API 3.0. Not processed anymore
|
||||||
self.new_chat_members: List[User] = None
|
self.new_chat_members: Union[List[User], None] = None
|
||||||
self.left_chat_member: User = None
|
self.left_chat_member: Union[User, None] = None
|
||||||
self.new_chat_title: str = None
|
self.new_chat_title: Union[str, None] = None
|
||||||
self.new_chat_photo: List[PhotoSize] = None
|
self.new_chat_photo: Union[List[PhotoSize], None] = None
|
||||||
self.delete_chat_photo: bool = None
|
self.delete_chat_photo: Union[bool, None] = None
|
||||||
self.group_chat_created: bool = None
|
self.group_chat_created: Union[bool, None] = None
|
||||||
self.supergroup_chat_created: bool = None
|
self.supergroup_chat_created: Union[bool, None] = None
|
||||||
self.channel_chat_created: bool = None
|
self.channel_chat_created: Union[bool, None] = None
|
||||||
self.migrate_to_chat_id: int = None
|
self.migrate_to_chat_id: Union[int, None] = None
|
||||||
self.migrate_from_chat_id: int = None
|
self.migrate_from_chat_id: Union[int, None] = None
|
||||||
self.pinned_message: Message = None
|
self.pinned_message: Union[Message, None] = None
|
||||||
self.invoice: Invoice = None
|
self.invoice: Union[Invoice, None] = None
|
||||||
self.successful_payment: SuccessfulPayment = None
|
self.successful_payment: Union[SuccessfulPayment, None] = None
|
||||||
self.connected_website: str = None
|
self.connected_website: Union[str, None] = None
|
||||||
self.reply_markup: InlineKeyboardMarkup = None
|
self.reply_markup: Union[InlineKeyboardMarkup, None] = None
|
||||||
for key in options:
|
for key in options:
|
||||||
setattr(self, key, options[key])
|
setattr(self, key, options[key])
|
||||||
self.json = json_string
|
self.json = json_string
|
||||||
@ -481,7 +481,7 @@ class Message(JsonDeserializable):
|
|||||||
message.html_text
|
message.html_text
|
||||||
>> "<b>Test</b> parse <i>formatting</i>, <a href=\"https://example.com\">url</a>, <a href=\"tg://user?id=123456\">text_mention</a> and mention @username"
|
>> "<b>Test</b> parse <i>formatting</i>, <a href=\"https://example.com\">url</a>, <a href=\"tg://user?id=123456\">text_mention</a> and mention @username"
|
||||||
|
|
||||||
Cusom subs:
|
Custom subs:
|
||||||
You can customize the substitutes. By default, there is no substitute for the entities: hashtag, bot_command, email. You can add or modify substitute an existing entity.
|
You can customize the substitutes. By default, there is no substitute for the entities: hashtag, bot_command, email. You can add or modify substitute an existing entity.
|
||||||
Example:
|
Example:
|
||||||
message.custom_subs = {"bold": "<strong class=\"example\">{text}</strong>", "italic": "<i class=\"example\">{text}</i>", "mention": "<a href={url}>{text}</a>"}
|
message.custom_subs = {"bold": "<strong class=\"example\">{text}</strong>", "italic": "<i class=\"example\">{text}</i>", "mention": "<a href={url}>{text}</a>"}
|
||||||
@ -493,15 +493,15 @@ class Message(JsonDeserializable):
|
|||||||
return text
|
return text
|
||||||
|
|
||||||
_subs = {
|
_subs = {
|
||||||
"bold" : "<b>{text}</b>",
|
"bold": "<b>{text}</b>",
|
||||||
"italic" : "<i>{text}</i>",
|
"italic": "<i>{text}</i>",
|
||||||
"pre" : "<pre>{text}</pre>",
|
"pre": "<pre>{text}</pre>",
|
||||||
"code" : "<code>{text}</code>",
|
"code": "<code>{text}</code>",
|
||||||
#"url" : "<a href=\"{url}\">{text}</a>", # @badiboy plain URLs have no text and do not need tags
|
# "url": "<a href=\"{url}\">{text}</a>", # @badiboy plain URLs have no text and do not need tags
|
||||||
"text_link": "<a href=\"{url}\">{text}</a>",
|
"text_link": "<a href=\"{url}\">{text}</a>",
|
||||||
"strikethrough": "<s>{text}</s>",
|
"strikethrough": "<s>{text}</s>",
|
||||||
"underline": "<u>{text}</u>"
|
"underline": "<u>{text}</u>"
|
||||||
}
|
}
|
||||||
|
|
||||||
if hasattr(self, "custom_subs"):
|
if hasattr(self, "custom_subs"):
|
||||||
for key, value in self.custom_subs.items():
|
for key, value in self.custom_subs.items():
|
||||||
@ -551,11 +551,12 @@ class Message(JsonDeserializable):
|
|||||||
|
|
||||||
class MessageEntity(Dictionaryable, JsonSerializable, JsonDeserializable):
|
class MessageEntity(Dictionaryable, JsonSerializable, JsonDeserializable):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def to_list_of_dicts(entity_list) -> List[Dict]:
|
def to_list_of_dicts(entity_list) -> Union[List[Dict], None]:
|
||||||
res = []
|
res = []
|
||||||
for e in entity_list:
|
for e in entity_list:
|
||||||
res.append(MessageEntity.to_dict(e))
|
res.append(MessageEntity.to_dict(e))
|
||||||
return res or None
|
return res or None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def de_json(cls, json_string):
|
def de_json(cls, json_string):
|
||||||
if json_string is None: return None
|
if json_string is None: return None
|
||||||
@ -587,7 +588,7 @@ class MessageEntity(Dictionaryable, JsonSerializable, JsonDeserializable):
|
|||||||
class Dice(JsonSerializable, Dictionaryable, JsonDeserializable):
|
class Dice(JsonSerializable, Dictionaryable, JsonDeserializable):
|
||||||
@classmethod
|
@classmethod
|
||||||
def de_json(cls, json_string):
|
def de_json(cls, json_string):
|
||||||
if (json_string is None): return None
|
if json_string is None: return None
|
||||||
obj = cls.check_json(json_string)
|
obj = cls.check_json(json_string)
|
||||||
return cls(**obj)
|
return cls(**obj)
|
||||||
|
|
||||||
@ -606,7 +607,7 @@ class Dice(JsonSerializable, Dictionaryable, JsonDeserializable):
|
|||||||
class PhotoSize(JsonDeserializable):
|
class PhotoSize(JsonDeserializable):
|
||||||
@classmethod
|
@classmethod
|
||||||
def de_json(cls, json_string):
|
def de_json(cls, json_string):
|
||||||
if (json_string is None): return None
|
if json_string is None: return None
|
||||||
obj = cls.check_json(json_string)
|
obj = cls.check_json(json_string)
|
||||||
return cls(**obj)
|
return cls(**obj)
|
||||||
|
|
||||||
@ -621,7 +622,7 @@ class PhotoSize(JsonDeserializable):
|
|||||||
class Audio(JsonDeserializable):
|
class Audio(JsonDeserializable):
|
||||||
@classmethod
|
@classmethod
|
||||||
def de_json(cls, json_string):
|
def de_json(cls, json_string):
|
||||||
if (json_string is None): return None
|
if json_string is None: return None
|
||||||
obj = cls.check_json(json_string)
|
obj = cls.check_json(json_string)
|
||||||
if 'thumb' in obj and 'file_id' in obj['thumb']:
|
if 'thumb' in obj and 'file_id' in obj['thumb']:
|
||||||
obj['thumb'] = PhotoSize.de_json(obj['thumb'])
|
obj['thumb'] = PhotoSize.de_json(obj['thumb'])
|
||||||
@ -645,7 +646,7 @@ class Audio(JsonDeserializable):
|
|||||||
class Voice(JsonDeserializable):
|
class Voice(JsonDeserializable):
|
||||||
@classmethod
|
@classmethod
|
||||||
def de_json(cls, json_string):
|
def de_json(cls, json_string):
|
||||||
if (json_string is None): return None
|
if json_string is None: return None
|
||||||
obj = cls.check_json(json_string)
|
obj = cls.check_json(json_string)
|
||||||
return cls(**obj)
|
return cls(**obj)
|
||||||
|
|
||||||
@ -682,7 +683,7 @@ class Video(JsonDeserializable):
|
|||||||
def de_json(cls, json_string):
|
def de_json(cls, json_string):
|
||||||
if json_string is None: return None
|
if json_string is None: return None
|
||||||
obj = cls.check_json(json_string)
|
obj = cls.check_json(json_string)
|
||||||
if ('thumb' in obj and 'file_id' in obj['thumb']):
|
if 'thumb' in obj and 'file_id' in obj['thumb']:
|
||||||
obj['thumb'] = PhotoSize.de_json(obj['thumb'])
|
obj['thumb'] = PhotoSize.de_json(obj['thumb'])
|
||||||
return cls(**obj)
|
return cls(**obj)
|
||||||
|
|
||||||
@ -703,7 +704,7 @@ class VideoNote(JsonDeserializable):
|
|||||||
def de_json(cls, json_string):
|
def de_json(cls, json_string):
|
||||||
if json_string is None: return None
|
if json_string is None: return None
|
||||||
obj = cls.check_json(json_string)
|
obj = cls.check_json(json_string)
|
||||||
if ('thumb' in obj and 'file_id' in obj['thumb']):
|
if 'thumb' in obj and 'file_id' in obj['thumb']:
|
||||||
obj['thumb'] = PhotoSize.de_json(obj['thumb'])
|
obj['thumb'] = PhotoSize.de_json(obj['thumb'])
|
||||||
return cls(**obj)
|
return cls(**obj)
|
||||||
|
|
||||||
@ -738,8 +739,8 @@ class Location(JsonDeserializable, JsonSerializable, Dictionaryable):
|
|||||||
obj = cls.check_json(json_string)
|
obj = cls.check_json(json_string)
|
||||||
return cls(**obj)
|
return cls(**obj)
|
||||||
|
|
||||||
def __init__(self, longitude: float, latitude: float, horizontal_accuracy:float=None,
|
def __init__(self, longitude, latitude, horizontal_accuracy=None,
|
||||||
live_period: int=None, heading: int=None, proximity_alert_radius: int=None, **kwargs):
|
live_period=None, heading=None, proximity_alert_radius=None, **kwargs):
|
||||||
self.longitude: float = longitude
|
self.longitude: float = longitude
|
||||||
self.latitude: float = latitude
|
self.latitude: float = latitude
|
||||||
self.horizontal_accuracy: float = horizontal_accuracy
|
self.horizontal_accuracy: float = horizontal_accuracy
|
||||||
@ -766,7 +767,7 @@ class Venue(JsonDeserializable):
|
|||||||
def de_json(cls, json_string):
|
def de_json(cls, json_string):
|
||||||
if json_string is None: return None
|
if json_string is None: return None
|
||||||
obj = cls.check_json(json_string)
|
obj = cls.check_json(json_string)
|
||||||
obj['location'] = Location.de_json(obj['location'])
|
obj['location'] = Location.de_json(obj.get('location'))
|
||||||
return cls(**obj)
|
return cls(**obj)
|
||||||
|
|
||||||
def __init__(self, location, title, address, foursquare_id=None, foursquare_type=None,
|
def __init__(self, location, title, address, foursquare_id=None, foursquare_type=None,
|
||||||
@ -785,11 +786,12 @@ class UserProfilePhotos(JsonDeserializable):
|
|||||||
def de_json(cls, json_string):
|
def de_json(cls, json_string):
|
||||||
if json_string is None: return None
|
if json_string is None: return None
|
||||||
obj = cls.check_json(json_string)
|
obj = cls.check_json(json_string)
|
||||||
photos = [[PhotoSize.de_json(y) for y in x] for x in obj['photos']]
|
if 'photos' in obj:
|
||||||
obj['photos'] = photos
|
photos = [[PhotoSize.de_json(y) for y in x] for x in obj['photos']]
|
||||||
|
obj['photos'] = photos
|
||||||
return cls(**obj)
|
return cls(**obj)
|
||||||
|
|
||||||
def __init__(self, total_count, photos, **kwargs):
|
def __init__(self, total_count, photos=None, **kwargs):
|
||||||
self.total_count: int = total_count
|
self.total_count: int = total_count
|
||||||
self.photos: List[PhotoSize] = photos
|
self.photos: List[PhotoSize] = photos
|
||||||
|
|
||||||
@ -859,8 +861,7 @@ class ReplyKeyboardMarkup(JsonSerializable):
|
|||||||
"""
|
"""
|
||||||
if row_width is None:
|
if row_width is None:
|
||||||
row_width = self.row_width
|
row_width = self.row_width
|
||||||
|
|
||||||
|
|
||||||
if row_width > self.max_row_keys:
|
if row_width > self.max_row_keys:
|
||||||
# Todo: Will be replaced with Exception in future releases
|
# Todo: Will be replaced with Exception in future releases
|
||||||
if not DISABLE_KEYLEN_ERROR:
|
if not DISABLE_KEYLEN_ERROR:
|
||||||
@ -946,7 +947,7 @@ class InlineKeyboardMarkup(Dictionaryable, JsonSerializable, JsonDeserializable)
|
|||||||
keyboard = [[InlineKeyboardButton.de_json(button) for button in row] for row in obj['inline_keyboard']]
|
keyboard = [[InlineKeyboardButton.de_json(button) for button in row] for row in obj['inline_keyboard']]
|
||||||
return cls(keyboard)
|
return cls(keyboard)
|
||||||
|
|
||||||
def __init__(self, keyboard=None ,row_width=3):
|
def __init__(self, keyboard=None, row_width=3):
|
||||||
"""
|
"""
|
||||||
This object represents an inline keyboard that appears
|
This object represents an inline keyboard that appears
|
||||||
right next to the message it belongs to.
|
right next to the message it belongs to.
|
||||||
@ -993,7 +994,7 @@ class InlineKeyboardMarkup(Dictionaryable, JsonSerializable, JsonDeserializable)
|
|||||||
def row(self, *args):
|
def row(self, *args):
|
||||||
"""
|
"""
|
||||||
Adds a list of InlineKeyboardButton to the keyboard.
|
Adds a list of InlineKeyboardButton to the keyboard.
|
||||||
This metod does not consider row_width.
|
This method does not consider row_width.
|
||||||
|
|
||||||
InlineKeyboardMarkup.row("A").row("B", "C").to_json() outputs:
|
InlineKeyboardMarkup.row("A").row("B", "C").to_json() outputs:
|
||||||
'{keyboard: [["A"], ["B", "C"]]}'
|
'{keyboard: [["A"], ["B", "C"]]}'
|
||||||
@ -1255,8 +1256,6 @@ class InlineQuery(JsonDeserializable):
|
|||||||
self.offset: str = offset
|
self.offset: str = offset
|
||||||
self.chat_type: str = chat_type
|
self.chat_type: str = chat_type
|
||||||
self.location: Location = location
|
self.location: Location = location
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class InputTextMessageContent(Dictionaryable):
|
class InputTextMessageContent(Dictionaryable):
|
||||||
@ -1337,7 +1336,7 @@ class InputContactMessageContent(Dictionaryable):
|
|||||||
self.vcard: str = vcard
|
self.vcard: str = vcard
|
||||||
|
|
||||||
def to_dict(self):
|
def to_dict(self):
|
||||||
json_dict = {'phone_numbe': self.phone_number, 'first_name': self.first_name}
|
json_dict = {'phone_number': self.phone_number, 'first_name': self.first_name}
|
||||||
if self.last_name:
|
if self.last_name:
|
||||||
json_dict['last_name'] = self.last_name
|
json_dict['last_name'] = self.last_name
|
||||||
if self.vcard:
|
if self.vcard:
|
||||||
@ -2043,7 +2042,10 @@ class Animation(JsonDeserializable):
|
|||||||
def de_json(cls, json_string):
|
def de_json(cls, json_string):
|
||||||
if (json_string is None): return None
|
if (json_string is None): return None
|
||||||
obj = cls.check_json(json_string)
|
obj = cls.check_json(json_string)
|
||||||
obj["thumb"] = PhotoSize.de_json(obj['thumb'])
|
if 'thumb' in obj and 'file_id' in obj['thumb']:
|
||||||
|
obj["thumb"] = PhotoSize.de_json(obj['thumb'])
|
||||||
|
else:
|
||||||
|
obj['thumb'] = None
|
||||||
return cls(**obj)
|
return cls(**obj)
|
||||||
|
|
||||||
def __init__(self, file_id, file_unique_id, width=None, height=None, duration=None,
|
def __init__(self, file_id, file_unique_id, width=None, height=None, duration=None,
|
||||||
@ -2122,10 +2124,10 @@ class OrderInfo(JsonDeserializable):
|
|||||||
def de_json(cls, json_string):
|
def de_json(cls, json_string):
|
||||||
if (json_string is None): return None
|
if (json_string is None): return None
|
||||||
obj = cls.check_json(json_string)
|
obj = cls.check_json(json_string)
|
||||||
obj['shipping_address'] = ShippingAddress.de_json(obj['shipping_address'])
|
obj['shipping_address'] = ShippingAddress.de_json(obj.get('shipping_address'))
|
||||||
return cls(**obj)
|
return cls(**obj)
|
||||||
|
|
||||||
def __init__(self, name, phone_number, email, shipping_address, **kwargs):
|
def __init__(self, name=None, phone_number=None, email=None, shipping_address=None, **kwargs):
|
||||||
self.name: str = name
|
self.name: str = name
|
||||||
self.phone_number: str = phone_number
|
self.phone_number: str = phone_number
|
||||||
self.email: str = email
|
self.email: str = email
|
||||||
@ -2160,8 +2162,7 @@ class SuccessfulPayment(JsonDeserializable):
|
|||||||
def de_json(cls, json_string):
|
def de_json(cls, json_string):
|
||||||
if (json_string is None): return None
|
if (json_string is None): return None
|
||||||
obj = cls.check_json(json_string)
|
obj = cls.check_json(json_string)
|
||||||
if 'order_info' in obj:
|
obj['order_info'] = OrderInfo.de_json(obj.get('order_info'))
|
||||||
obj['order_info'] = OrderInfo.de_json(obj['order_info'])
|
|
||||||
return cls(**obj)
|
return cls(**obj)
|
||||||
|
|
||||||
def __init__(self, currency, total_amount, invoice_payload, shipping_option_id=None, order_info=None,
|
def __init__(self, currency, total_amount, invoice_payload, shipping_option_id=None, order_info=None,
|
||||||
@ -2197,8 +2198,7 @@ class PreCheckoutQuery(JsonDeserializable):
|
|||||||
if (json_string is None): return None
|
if (json_string is None): return None
|
||||||
obj = cls.check_json(json_string)
|
obj = cls.check_json(json_string)
|
||||||
obj['from_user'] = User.de_json(obj.pop('from'))
|
obj['from_user'] = User.de_json(obj.pop('from'))
|
||||||
if 'order_info' in obj:
|
obj['order_info'] = OrderInfo.de_json(obj.get('order_info'))
|
||||||
obj['order_info'] = OrderInfo.de_json(obj['order_info'])
|
|
||||||
return cls(**obj)
|
return cls(**obj)
|
||||||
|
|
||||||
def __init__(self, id, from_user, currency, total_amount, invoice_payload, shipping_option_id=None, order_info=None, **kwargs):
|
def __init__(self, id, from_user, currency, total_amount, invoice_payload, shipping_option_id=None, order_info=None, **kwargs):
|
||||||
@ -2473,6 +2473,7 @@ class PollAnswer(JsonSerializable, JsonDeserializable, Dictionaryable):
|
|||||||
if (json_string is None): return None
|
if (json_string is None): return None
|
||||||
obj = cls.check_json(json_string)
|
obj = cls.check_json(json_string)
|
||||||
obj['user'] = User.de_json(obj['user'])
|
obj['user'] = User.de_json(obj['user'])
|
||||||
|
# Strange name, i think it should be `option_ids` not `options_ids` maybe replace that
|
||||||
obj['options_ids'] = obj.pop('option_ids')
|
obj['options_ids'] = obj.pop('option_ids')
|
||||||
return cls(**obj)
|
return cls(**obj)
|
||||||
|
|
||||||
@ -2487,6 +2488,7 @@ class PollAnswer(JsonSerializable, JsonDeserializable, Dictionaryable):
|
|||||||
def to_dict(self):
|
def to_dict(self):
|
||||||
return {'poll_id': self.poll_id,
|
return {'poll_id': self.poll_id,
|
||||||
'user': self.user.to_dict(),
|
'user': self.user.to_dict(),
|
||||||
|
#should be `option_ids` not `options_ids` could cause problems here
|
||||||
'options_ids': self.options_ids}
|
'options_ids': self.options_ids}
|
||||||
|
|
||||||
|
|
||||||
@ -2498,7 +2500,7 @@ class ChatLocation(JsonSerializable, JsonDeserializable, Dictionaryable):
|
|||||||
obj['location'] = Location.de_json(obj['location'])
|
obj['location'] = Location.de_json(obj['location'])
|
||||||
return cls(**obj)
|
return cls(**obj)
|
||||||
|
|
||||||
def __init__(self, location: Location, address: str, **kwargs):
|
def __init__(self, location, address, **kwargs):
|
||||||
self.location: Location = location
|
self.location: Location = location
|
||||||
self.address: str = address
|
self.address: str = address
|
||||||
|
|
||||||
@ -2520,8 +2522,8 @@ class ChatInviteLink(JsonSerializable, JsonDeserializable, Dictionaryable):
|
|||||||
obj['creator'] = User.de_json(obj['creator'])
|
obj['creator'] = User.de_json(obj['creator'])
|
||||||
return cls(**obj)
|
return cls(**obj)
|
||||||
|
|
||||||
def __init__(self, invite_link: str, creator: User, is_primary: bool, is_revoked: bool,
|
def __init__(self, invite_link, creator, is_primary, is_revoked,
|
||||||
expire_date: int=None, member_limit: int=None, **kwargs):
|
expire_date=None, member_limit=None, **kwargs):
|
||||||
self.invite_link: str = invite_link
|
self.invite_link: str = invite_link
|
||||||
self.creator: User = creator
|
self.creator: User = creator
|
||||||
self.is_primary: bool = is_primary
|
self.is_primary: bool = is_primary
|
||||||
|
@ -6,7 +6,7 @@ import threading
|
|||||||
import traceback
|
import traceback
|
||||||
import warnings
|
import warnings
|
||||||
import functools
|
import functools
|
||||||
from typing import Any, List, Dict
|
from typing import Any, List, Dict, Union
|
||||||
|
|
||||||
import queue as Queue
|
import queue as Queue
|
||||||
import logging
|
import logging
|
||||||
@ -36,6 +36,7 @@ content_type_service = [
|
|||||||
'supergroup_chat_created', 'channel_chat_created', 'migrate_to_chat_id', 'migrate_from_chat_id', 'pinned_message'
|
'supergroup_chat_created', 'channel_chat_created', 'migrate_to_chat_id', 'migrate_from_chat_id', 'pinned_message'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class WorkerThread(threading.Thread):
|
class WorkerThread(threading.Thread):
|
||||||
count = 0
|
count = 0
|
||||||
|
|
||||||
@ -170,15 +171,19 @@ def async_dec():
|
|||||||
def is_string(var):
|
def is_string(var):
|
||||||
return isinstance(var, str)
|
return isinstance(var, str)
|
||||||
|
|
||||||
|
|
||||||
def is_dict(var):
|
def is_dict(var):
|
||||||
return isinstance(var, dict)
|
return isinstance(var, dict)
|
||||||
|
|
||||||
|
|
||||||
def is_bytes(var):
|
def is_bytes(var):
|
||||||
return isinstance(var, bytes)
|
return isinstance(var, bytes)
|
||||||
|
|
||||||
|
|
||||||
def is_pil_image(var):
|
def is_pil_image(var):
|
||||||
return pil_imported and isinstance(var, Image.Image)
|
return pil_imported and isinstance(var, Image.Image)
|
||||||
|
|
||||||
|
|
||||||
def pil_image_to_file(image, extension='JPEG', quality='web_low'):
|
def pil_image_to_file(image, extension='JPEG', quality='web_low'):
|
||||||
if pil_imported:
|
if pil_imported:
|
||||||
photoBuffer = BytesIO()
|
photoBuffer = BytesIO()
|
||||||
@ -189,17 +194,18 @@ def pil_image_to_file(image, extension='JPEG', quality='web_low'):
|
|||||||
else:
|
else:
|
||||||
raise RuntimeError('PIL module is not imported')
|
raise RuntimeError('PIL module is not imported')
|
||||||
|
|
||||||
|
|
||||||
def is_command(text: str) -> bool:
|
def is_command(text: str) -> bool:
|
||||||
"""
|
"""
|
||||||
Checks if `text` is a command. Telegram chat commands start with the '/' character.
|
Checks if `text` is a command. Telegram chat commands start with the '/' character.
|
||||||
:param text: Text to check.
|
:param text: Text to check.
|
||||||
:return: True if `text` is a command, else False.
|
:return: True if `text` is a command, else False.
|
||||||
"""
|
"""
|
||||||
if (text is None): return False
|
if text is None: return False
|
||||||
return text.startswith('/')
|
return text.startswith('/')
|
||||||
|
|
||||||
|
|
||||||
def extract_command(text: str) -> str:
|
def extract_command(text: str) -> Union[str, None]:
|
||||||
"""
|
"""
|
||||||
Extracts the command from `text` (minus the '/') if `text` is a command (see is_command).
|
Extracts the command from `text` (minus the '/') if `text` is a command (see is_command).
|
||||||
If `text` is not a command, this function returns None.
|
If `text` is not a command, this function returns None.
|
||||||
@ -213,7 +219,7 @@ def extract_command(text: str) -> str:
|
|||||||
:param text: String to extract the command from
|
:param text: String to extract the command from
|
||||||
:return: the command if `text` is a command (according to is_command), else None.
|
:return: the command if `text` is a command (according to is_command), else None.
|
||||||
"""
|
"""
|
||||||
if (text is None): return None
|
if text is None: return None
|
||||||
return text.split()[0].split('@')[0][1:] if is_command(text) else None
|
return text.split()[0].split('@')[0][1:] if is_command(text) else None
|
||||||
|
|
||||||
|
|
||||||
@ -229,7 +235,7 @@ def extract_arguments(text: str) -> str:
|
|||||||
:param text: String to extract the arguments from a command
|
:param text: String to extract the arguments from a command
|
||||||
:return: the arguments if `text` is a command (according to is_command), else None.
|
:return: the arguments if `text` is a command (according to is_command), else None.
|
||||||
"""
|
"""
|
||||||
regexp = re.compile(r"/\w*(@\w*)*\s*([\s\S]*)",re.IGNORECASE)
|
regexp = re.compile(r"/\w*(@\w*)*\s*([\s\S]*)", re.IGNORECASE)
|
||||||
result = regexp.match(text)
|
result = regexp.match(text)
|
||||||
return result.group(2) if is_command(text) else None
|
return result.group(2) if is_command(text) else None
|
||||||
|
|
||||||
@ -247,16 +253,17 @@ def split_string(text: str, chars_per_string: int) -> List[str]:
|
|||||||
|
|
||||||
|
|
||||||
def smart_split(text: str, chars_per_string: int=MAX_MESSAGE_LENGTH) -> List[str]:
|
def smart_split(text: str, chars_per_string: int=MAX_MESSAGE_LENGTH) -> List[str]:
|
||||||
f"""
|
"""
|
||||||
Splits one string into multiple strings, with a maximum amount of `chars_per_string` characters per string.
|
Splits one string into multiple strings, with a maximum amount of `chars_per_string` characters per string.
|
||||||
This is very useful for splitting one giant message into multiples.
|
This is very useful for splitting one giant message into multiples.
|
||||||
If `chars_per_string` > {MAX_MESSAGE_LENGTH}: `chars_per_string` = {MAX_MESSAGE_LENGTH}.
|
If `chars_per_string` > 4096: `chars_per_string` = 4096.
|
||||||
Splits by '\n', '. ' or ' ' in exactly this priority.
|
Splits by '\n', '. ' or ' ' in exactly this priority.
|
||||||
|
|
||||||
:param text: The text to split
|
:param text: The text to split
|
||||||
:param chars_per_string: The number of maximum characters per part the text is split to.
|
:param chars_per_string: The number of maximum characters per part the text is split to.
|
||||||
:return: The splitted text as a list of strings.
|
:return: The splitted text as a list of strings.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def _text_before_last(substr: str) -> str:
|
def _text_before_last(substr: str) -> str:
|
||||||
return substr.join(part.split(substr)[:-1]) + substr
|
return substr.join(part.split(substr)[:-1]) + substr
|
||||||
|
|
||||||
@ -270,9 +277,9 @@ def smart_split(text: str, chars_per_string: int=MAX_MESSAGE_LENGTH) -> List[str
|
|||||||
|
|
||||||
part = text[:chars_per_string]
|
part = text[:chars_per_string]
|
||||||
|
|
||||||
if ("\n" in part): part = _text_before_last("\n")
|
if "\n" in part: part = _text_before_last("\n")
|
||||||
elif (". " in part): part = _text_before_last(". ")
|
elif ". " in part: part = _text_before_last(". ")
|
||||||
elif (" " in part): part = _text_before_last(" ")
|
elif " " in part: part = _text_before_last(" ")
|
||||||
|
|
||||||
parts.append(part)
|
parts.append(part)
|
||||||
text = text[len(part):]
|
text = text[len(part):]
|
||||||
@ -296,7 +303,7 @@ def user_link(user: types.User, include_id: bool=False) -> str:
|
|||||||
Attention: Don't forget to set parse_mode to 'HTML'!
|
Attention: Don't forget to set parse_mode to 'HTML'!
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
bot.send_message(your_user_id, user_link(message.from_user) + ' startet the bot!', parse_mode='HTML')
|
bot.send_message(your_user_id, user_link(message.from_user) + ' started the bot!', parse_mode='HTML')
|
||||||
|
|
||||||
:param user: the user (not the user_id)
|
:param user: the user (not the user_id)
|
||||||
:param include_id: include the user_id
|
:param include_id: include the user_id
|
||||||
@ -333,6 +340,7 @@ def quick_markup(values: Dict[str, Dict[str, Any]], row_width: int=2) -> types.I
|
|||||||
}
|
}
|
||||||
|
|
||||||
:param values: a dict containing all buttons to create in this format: {text: kwargs} {str:}
|
:param values: a dict containing all buttons to create in this format: {text: kwargs} {str:}
|
||||||
|
:param row_width: int row width
|
||||||
:return: InlineKeyboardMarkup
|
:return: InlineKeyboardMarkup
|
||||||
"""
|
"""
|
||||||
markup = types.InlineKeyboardMarkup(row_width=row_width)
|
markup = types.InlineKeyboardMarkup(row_width=row_width)
|
||||||
@ -363,8 +371,10 @@ def orify(e, changed_callback):
|
|||||||
e.set = lambda: or_set(e)
|
e.set = lambda: or_set(e)
|
||||||
e.clear = lambda: or_clear(e)
|
e.clear = lambda: or_clear(e)
|
||||||
|
|
||||||
|
|
||||||
def OrEvent(*events):
|
def OrEvent(*events):
|
||||||
or_event = threading.Event()
|
or_event = threading.Event()
|
||||||
|
|
||||||
def changed():
|
def changed():
|
||||||
bools = [ev.is_set() for ev in events]
|
bools = [ev.is_set() for ev in events]
|
||||||
if any(bools):
|
if any(bools):
|
||||||
@ -391,15 +401,18 @@ def per_thread(key, construct_value, reset=False):
|
|||||||
|
|
||||||
return getattr(thread_local, key)
|
return getattr(thread_local, key)
|
||||||
|
|
||||||
|
|
||||||
def chunks(lst, n):
|
def chunks(lst, n):
|
||||||
"""Yield successive n-sized chunks from lst."""
|
"""Yield successive n-sized chunks from lst."""
|
||||||
# https://stackoverflow.com/a/312464/9935473
|
# https://stackoverflow.com/a/312464/9935473
|
||||||
for i in range(0, len(lst), n):
|
for i in range(0, len(lst), n):
|
||||||
yield lst[i:i + n]
|
yield lst[i:i + n]
|
||||||
|
|
||||||
|
|
||||||
def generate_random_token():
|
def generate_random_token():
|
||||||
return ''.join(random.sample(string.ascii_letters, 16))
|
return ''.join(random.sample(string.ascii_letters, 16))
|
||||||
|
|
||||||
|
|
||||||
def deprecated(func):
|
def deprecated(func):
|
||||||
"""This is a decorator which can be used to mark functions
|
"""This is a decorator which can be used to mark functions
|
||||||
as deprecated. It will result in a warning being emitted
|
as deprecated. It will result in a warning being emitted
|
||||||
|
@ -6,6 +6,7 @@ sys.path.append('../')
|
|||||||
import time
|
import time
|
||||||
import pytest
|
import pytest
|
||||||
import os
|
import os
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
import telebot
|
import telebot
|
||||||
from telebot import types
|
from telebot import types
|
||||||
@ -407,6 +408,23 @@ class TestTeleBot:
|
|||||||
cn = tb.get_chat_members_count(GROUP_ID)
|
cn = tb.get_chat_members_count(GROUP_ID)
|
||||||
assert cn > 1
|
assert cn > 1
|
||||||
|
|
||||||
|
def test_export_chat_invite_link(self):
|
||||||
|
tb = telebot.TeleBot(TOKEN)
|
||||||
|
il = tb.export_chat_invite_link(GROUP_ID)
|
||||||
|
assert isinstance(il, str)
|
||||||
|
|
||||||
|
def test_create_revoke_detailed_chat_invite_link(self):
|
||||||
|
tb = telebot.TeleBot(TOKEN)
|
||||||
|
cil = tb.create_chat_invite_link(GROUP_ID,
|
||||||
|
(datetime.now() + timedelta(minutes=1)).timestamp(), member_limit=5)
|
||||||
|
assert isinstance(cil.invite_link, str)
|
||||||
|
assert cil.creator.id == tb.get_me().id
|
||||||
|
assert isinstance(cil.expire_date, (float, int))
|
||||||
|
assert cil.member_limit == 5
|
||||||
|
assert not cil.is_revoked
|
||||||
|
rcil = tb.revoke_chat_invite_link(GROUP_ID, cil.invite_link)
|
||||||
|
assert rcil.is_revoked
|
||||||
|
|
||||||
def test_edit_markup(self):
|
def test_edit_markup(self):
|
||||||
text = 'CI Test Message'
|
text = 'CI Test Message'
|
||||||
tb = telebot.TeleBot(TOKEN)
|
tb = telebot.TeleBot(TOKEN)
|
||||||
|
@ -219,3 +219,14 @@ def test_KeyboardButtonPollType():
|
|||||||
json_str = markup.to_json()
|
json_str = markup.to_json()
|
||||||
assert 'request_poll' in json_str
|
assert 'request_poll' in json_str
|
||||||
assert 'quiz' in json_str
|
assert 'quiz' in json_str
|
||||||
|
|
||||||
|
|
||||||
|
def test_json_chat_invite_link():
|
||||||
|
json_string = r'{"invite_link": "https://t.me/joinchat/z-abCdEFghijKlMn", "creator": {"id": 329343347, "is_bot": false, "first_name": "Test", "username": "test_user", "last_name": "User", "language_code": "en"}, "is_primary": false, "is_revoked": false, "expire_date": 1624119999, "member_limit": 10}'
|
||||||
|
invite_link = types.ChatInviteLink.de_json(json_string)
|
||||||
|
assert invite_link.invite_link == 'https://t.me/joinchat/z-abCdEFghijKlMn'
|
||||||
|
assert isinstance(invite_link.creator, types.User)
|
||||||
|
assert not invite_link.is_primary
|
||||||
|
assert not invite_link.is_revoked
|
||||||
|
assert invite_link.expire_date == 1624119999
|
||||||
|
assert invite_link.member_limit == 10
|
Loading…
Reference in New Issue
Block a user