mirror of
https://github.com/aiogram/aiogram.git
synced 2026-04-08 16:37:47 +00:00
Merge branch 'dev-2.x'
This commit is contained in:
commit
45480890ae
28 changed files with 700 additions and 88 deletions
|
|
@ -6,7 +6,7 @@
|
|||
[](https://pypi.python.org/pypi/aiogram)
|
||||
[](https://pypi.python.org/pypi/aiogram)
|
||||
[](https://pypi.python.org/pypi/aiogram)
|
||||
[](https://core.telegram.org/bots/api)
|
||||
[](https://core.telegram.org/bots/api)
|
||||
[](http://docs.aiogram.dev/en/latest/?badge=latest)
|
||||
[](https://github.com/aiogram/aiogram/issues)
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ AIOGramBot
|
|||
:target: https://pypi.python.org/pypi/aiogram
|
||||
:alt: Supported python versions
|
||||
|
||||
.. image:: https://img.shields.io/badge/Telegram%20Bot%20API-5.0-blue.svg?style=flat-square&logo=telegram
|
||||
.. image:: https://img.shields.io/badge/Telegram%20Bot%20API-5.1-blue.svg?style=flat-square&logo=telegram
|
||||
:target: https://core.telegram.org/bots/api
|
||||
:alt: Telegram Bot API
|
||||
|
||||
|
|
|
|||
|
|
@ -43,5 +43,5 @@ __all__ = (
|
|||
'utils',
|
||||
)
|
||||
|
||||
__version__ = '2.11.2'
|
||||
__api_version__ = '5.0'
|
||||
__version__ = '2.12'
|
||||
__api_version__ = '5.1'
|
||||
|
|
|
|||
|
|
@ -189,7 +189,7 @@ class Methods(Helper):
|
|||
"""
|
||||
Helper for Telegram API Methods listed on https://core.telegram.org/bots/api
|
||||
|
||||
List is updated to Bot API 5.0
|
||||
List is updated to Bot API 5.1
|
||||
"""
|
||||
mode = HelperMode.lowerCamelCase
|
||||
|
||||
|
|
@ -231,6 +231,9 @@ class Methods(Helper):
|
|||
SET_CHAT_ADMINISTRATOR_CUSTOM_TITLE = Item() # setChatAdministratorCustomTitle
|
||||
SET_CHAT_PERMISSIONS = Item() # setChatPermissions
|
||||
EXPORT_CHAT_INVITE_LINK = Item() # exportChatInviteLink
|
||||
CREATE_CHAT_INVITE_LINK = Item() # createChatInviteLink
|
||||
EDIT_CHAT_INVITE_LINK = Item() # editChatInviteLink
|
||||
REVOKE_CHAT_INVITE_LINK = Item() # revokeChatInviteLink
|
||||
SET_CHAT_PHOTO = Item() # setChatPhoto
|
||||
DELETE_CHAT_PHOTO = Item() # deleteChatPhoto
|
||||
SET_CHAT_TITLE = Item() # setChatTitle
|
||||
|
|
|
|||
|
|
@ -1550,28 +1550,43 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
|
|||
result = await self.request(api.Methods.GET_FILE, payload)
|
||||
return types.File(**result)
|
||||
|
||||
async def kick_chat_member(self, chat_id: typing.Union[base.Integer, base.String], user_id: base.Integer,
|
||||
until_date: typing.Union[
|
||||
base.Integer, datetime.datetime, datetime.timedelta, None] = None) -> base.Boolean:
|
||||
async def kick_chat_member(self,
|
||||
chat_id: typing.Union[base.Integer, base.String],
|
||||
user_id: base.Integer,
|
||||
until_date: typing.Union[base.Integer, datetime.datetime,
|
||||
datetime.timedelta, None] = None,
|
||||
revoke_messages: typing.Optional[base.Boolean] = None,
|
||||
) -> base.Boolean:
|
||||
"""
|
||||
Use this method to kick a user from a group, a supergroup or a channel.
|
||||
In the case of supergroups and channels, the user will not be able to return to the group
|
||||
on their own using invite links, etc., unless unbanned first.
|
||||
In the case of supergroups and channels, the user will not be able to return
|
||||
to the chat on their own using invite links, etc., unless unbanned first.
|
||||
|
||||
The bot must be an administrator in the chat for this to work and must have the appropriate admin rights.
|
||||
|
||||
Note: In regular groups (non-supergroups), this method will only work if the ‘All Members Are Admins’ setting
|
||||
is off in the target group.
|
||||
Otherwise members may only be removed by the group's creator or by the member that added them.
|
||||
The bot must be an administrator in the chat for this to work and must have
|
||||
the appropriate admin rights.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#kickchatmember
|
||||
|
||||
:param chat_id: Unique identifier for the target group or username of the target supergroup or channel
|
||||
:param chat_id: Unique identifier for the target group or username of the
|
||||
target supergroup or channel (in the format @channelusername)
|
||||
:type chat_id: :obj:`typing.Union[base.Integer, base.String]`
|
||||
|
||||
:param user_id: Unique identifier of the target user
|
||||
:type user_id: :obj:`base.Integer`
|
||||
:param until_date: Date when the user will be unbanned, unix time
|
||||
:type until_date: :obj:`typing.Optional[base.Integer]`
|
||||
|
||||
:param until_date: Date when the user will be unbanned. If user is banned
|
||||
for more than 366 days or less than 30 seconds from the current time they
|
||||
are considered to be banned forever. Applied for supergroups and channels
|
||||
only.
|
||||
:type until_date: :obj:`typing.Union[base.Integer, datetime.datetime,
|
||||
datetime.timedelta, None]`
|
||||
|
||||
:param revoke_messages: Pass True to delete all messages from the chat for
|
||||
the user that is being removed. If False, the user will be able to see
|
||||
messages in the group that were sent before the user was removed. Always
|
||||
True for supergroups and channels.
|
||||
:type revoke_messages: :obj:`typing.Optional[base.Boolean]`
|
||||
|
||||
:return: Returns True on success
|
||||
:rtype: :obj:`base.Boolean`
|
||||
"""
|
||||
|
|
@ -1675,10 +1690,12 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
|
|||
chat_id: typing.Union[base.Integer, base.String],
|
||||
user_id: base.Integer,
|
||||
is_anonymous: typing.Optional[base.Boolean] = None,
|
||||
can_manage_chat: typing.Optional[base.Boolean] = None,
|
||||
can_change_info: typing.Optional[base.Boolean] = None,
|
||||
can_post_messages: typing.Optional[base.Boolean] = None,
|
||||
can_edit_messages: typing.Optional[base.Boolean] = None,
|
||||
can_delete_messages: typing.Optional[base.Boolean] = None,
|
||||
can_manage_voice_chats: typing.Optional[base.Boolean] = None,
|
||||
can_invite_users: typing.Optional[base.Boolean] = None,
|
||||
can_restrict_members: typing.Optional[base.Boolean] = None,
|
||||
can_pin_messages: typing.Optional[base.Boolean] = None,
|
||||
|
|
@ -1700,6 +1717,11 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
|
|||
:param is_anonymous: Pass True, if the administrator's presence in the chat is hidden
|
||||
:type is_anonymous: :obj:`typing.Optional[base.Boolean]`
|
||||
|
||||
:param can_manage_chat: Pass True, if the administrator can access the chat event log, chat statistics,
|
||||
message statistics in channels, see channel members, see anonymous administrators in supergroups
|
||||
and ignore slow mode. Implied by any other administrator privilege
|
||||
:type can_manage_chat: :obj:`typing.Optional[base.Boolean]`
|
||||
|
||||
:param can_change_info: Pass True, if the administrator can change chat title, photo and other settings
|
||||
:type can_change_info: :obj:`typing.Optional[base.Boolean]`
|
||||
|
||||
|
|
@ -1712,6 +1734,9 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
|
|||
:param can_delete_messages: Pass True, if the administrator can delete messages of other users
|
||||
:type can_delete_messages: :obj:`typing.Optional[base.Boolean]`
|
||||
|
||||
:param can_manage_voice_chats: Pass True, if the administrator can manage voice chats, supergroups only
|
||||
:type can_manage_voice_chats: :obj:`typing.Optional[base.Boolean]`
|
||||
|
||||
:param can_invite_users: Pass True, if the administrator can invite new users to the chat
|
||||
:type can_invite_users: :obj:`typing.Optional[base.Boolean]`
|
||||
|
||||
|
|
@ -1789,6 +1814,100 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
|
|||
result = await self.request(api.Methods.EXPORT_CHAT_INVITE_LINK, payload)
|
||||
return result
|
||||
|
||||
async def create_chat_invite_link(self,
|
||||
chat_id: typing.Union[base.Integer, base.String],
|
||||
expire_date: typing.Union[base.Integer, datetime.datetime,
|
||||
datetime.timedelta, None] = None,
|
||||
member_limit: typing.Optional[base.Integer] = None,
|
||||
) -> types.ChatInviteLink:
|
||||
"""
|
||||
Use this method to create an additional invite link for a chat.
|
||||
The bot must be an administrator in the chat for this to work and must have
|
||||
the appropriate admin rights. The link can be revoked using the method
|
||||
revokeChatInviteLink.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#createchatinvitelink
|
||||
|
||||
:param chat_id: Unique identifier for the target chat or username of the
|
||||
target channel (in the format @channelusername)
|
||||
:type chat_id: :obj:`typing.Union[base.Integer, base.String]`
|
||||
|
||||
:param expire_date: Point in time when the link will expire
|
||||
:type expire_date: :obj:`typing.Union[base.Integer, datetime.datetime,
|
||||
datetime.timedelta, None]`
|
||||
|
||||
:param member_limit: Maximum number of users that can be members of the chat
|
||||
simultaneously after joining the chat via this invite link; 1-99999
|
||||
:type member_limit: :obj:`typing.Optional[base.Integer]`
|
||||
|
||||
:return: the new invite link as ChatInviteLink object.
|
||||
:rtype: :obj:`types.ChatInviteLink`
|
||||
"""
|
||||
expire_date = prepare_arg(expire_date)
|
||||
payload = generate_payload(**locals())
|
||||
|
||||
result = await self.request(api.Methods.CREATE_CHAT_INVITE_LINK, payload)
|
||||
return result
|
||||
|
||||
async def edit_chat_invite_link(self,
|
||||
chat_id: typing.Union[base.Integer, base.String],
|
||||
invite_link: base.String,
|
||||
expire_date: typing.Union[base.Integer, datetime.datetime,
|
||||
datetime.timedelta, None] = None,
|
||||
member_limit: typing.Optional[base.Integer] = None,
|
||||
) -> types.ChatInviteLink:
|
||||
"""
|
||||
Use this method to edit a non-primary invite link created by the bot.
|
||||
The bot must be an administrator in the chat for this to work and must have
|
||||
the appropriate admin rights.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#editchatinvitelink
|
||||
|
||||
:param chat_id: Unique identifier for the target chat or username of the
|
||||
target channel (in the format @channelusername)
|
||||
:type chat_id: :obj:`typing.Union[base.Integer, base.String]`
|
||||
|
||||
:param invite_link: The invite link to edit
|
||||
:type invite_link: :obj:`base.String`
|
||||
|
||||
:param expire_date: Point in time (Unix timestamp) when the link will expire
|
||||
:type expire_date: :obj:`typing.Union[base.Integer, datetime.datetime,
|
||||
datetime.timedelta, None]`
|
||||
|
||||
:param member_limit: Maximum number of users that can be members of the chat
|
||||
simultaneously after joining the chat via this invite link; 1-99999
|
||||
:type member_limit: :obj:`typing.Optional[base.Integer]`
|
||||
|
||||
:return: edited invite link as a ChatInviteLink object.
|
||||
"""
|
||||
expire_date = prepare_arg(expire_date)
|
||||
payload = generate_payload(**locals())
|
||||
|
||||
result = await self.request(api.Methods.EDIT_CHAT_INVITE_LINK, payload)
|
||||
return result
|
||||
|
||||
async def revoke_chat_invite_link(self,
|
||||
chat_id: typing.Union[base.Integer, base.String],
|
||||
invite_link: base.String,
|
||||
) -> types.ChatInviteLink:
|
||||
"""
|
||||
Use this method to revoke an invite link created by the bot.
|
||||
If the primary link is revoked, a new link is automatically generated.
|
||||
The bot must be an administrator in the chat for this to work and must have
|
||||
the appropriate admin rights.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#revokechatinvitelink
|
||||
|
||||
:param chat_id: Unique identifier for the target chat or username of the
|
||||
target channel (in the format @channelusername)
|
||||
:param invite_link: The invite link to revoke
|
||||
:return: the revoked invite link as ChatInviteLink object
|
||||
"""
|
||||
payload = generate_payload(**locals())
|
||||
|
||||
result = await self.request(api.Methods.REVOKE_CHAT_INVITE_LINK, payload)
|
||||
return result
|
||||
|
||||
async def set_chat_photo(self, chat_id: typing.Union[base.Integer, base.String],
|
||||
photo: base.InputFile) -> base.Boolean:
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -160,6 +160,26 @@ class LoggingMiddleware(BaseMiddleware):
|
|||
self.logger.debug(f"{HANDLED_STR[bool(len(results))]} poll answer [ID:{poll_answer.poll_id}] "
|
||||
f"from user [ID:{poll_answer.user.id}]")
|
||||
|
||||
async def on_pre_process_my_chat_member(self, my_chat_member_update, data):
|
||||
self.logger.info(f"Received chat member update "
|
||||
f"for user [ID:{my_chat_member_update.from_user.id}]. "
|
||||
f"Old state: {my_chat_member_update.old_chat_member.to_python()} "
|
||||
f"New state: {my_chat_member_update.new_chat_member.to_python()} ")
|
||||
|
||||
async def on_post_process_my_chat_member(self, my_chat_member_update, results, data):
|
||||
self.logger.debug(f"{HANDLED_STR[bool(len(results))]} my_chat_member "
|
||||
f"for user [ID:{my_chat_member_update.from_user.id}]")
|
||||
|
||||
async def on_pre_process_chat_member(self, chat_member_update, data):
|
||||
self.logger.info(f"Received chat member update "
|
||||
f"for user [ID:{chat_member_update.from_user.id}]. "
|
||||
f"Old state: {chat_member_update.old_chat_member.to_python()} "
|
||||
f"New state: {chat_member_update.new_chat_member.to_python()} ")
|
||||
|
||||
async def on_post_process_chat_member(self, chat_member_update, results, data):
|
||||
self.logger.debug(f"{HANDLED_STR[bool(len(results))]} chat_member "
|
||||
f"for user [ID:{chat_member_update.from_user.id}]")
|
||||
|
||||
|
||||
class LoggingFilter(logging.Filter):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -78,6 +78,8 @@ class Dispatcher(DataMixin, ContextInstanceMixin):
|
|||
self.pre_checkout_query_handlers = Handler(self, middleware_key='pre_checkout_query')
|
||||
self.poll_handlers = Handler(self, middleware_key='poll')
|
||||
self.poll_answer_handlers = Handler(self, middleware_key='poll_answer')
|
||||
self.my_chat_member_handlers = Handler(self, middleware_key='my_chat_member')
|
||||
self.chat_member_handlers = Handler(self, middleware_key='chat_member')
|
||||
self.errors_handlers = Handler(self, once=False, middleware_key='error')
|
||||
|
||||
self.middleware = MiddlewareManager(self)
|
||||
|
|
@ -163,6 +165,7 @@ class Dispatcher(DataMixin, ContextInstanceMixin):
|
|||
self.edited_channel_post_handlers,
|
||||
self.callback_query_handlers,
|
||||
self.inline_query_handlers,
|
||||
self.chat_member_handlers,
|
||||
])
|
||||
filters_factory.bind(IDFilter, event_handlers=[
|
||||
self.message_handlers,
|
||||
|
|
@ -171,6 +174,8 @@ class Dispatcher(DataMixin, ContextInstanceMixin):
|
|||
self.edited_channel_post_handlers,
|
||||
self.callback_query_handlers,
|
||||
self.inline_query_handlers,
|
||||
self.chat_member_handlers,
|
||||
self.my_chat_member_handlers,
|
||||
])
|
||||
filters_factory.bind(IsReplyFilter, event_handlers=[
|
||||
self.message_handlers,
|
||||
|
|
@ -196,6 +201,8 @@ class Dispatcher(DataMixin, ContextInstanceMixin):
|
|||
self.channel_post_handlers,
|
||||
self.edited_channel_post_handlers,
|
||||
self.callback_query_handlers,
|
||||
self.my_chat_member_handlers,
|
||||
self.chat_member_handlers
|
||||
])
|
||||
|
||||
def __del__(self):
|
||||
|
|
@ -286,6 +293,14 @@ class Dispatcher(DataMixin, ContextInstanceMixin):
|
|||
types.PollAnswer.set_current(update.poll_answer)
|
||||
types.User.set_current(update.poll_answer.user)
|
||||
return await self.poll_answer_handlers.notify(update.poll_answer)
|
||||
if update.my_chat_member:
|
||||
types.ChatMemberUpdated.set_current(update.my_chat_member)
|
||||
types.User.set_current(update.my_chat_member.from_user)
|
||||
return await self.my_chat_member_handlers.notify(update.my_chat_member)
|
||||
if update.chat_member:
|
||||
types.ChatMemberUpdated.set_current(update.chat_member)
|
||||
types.User.set_current(update.chat_member.from_user)
|
||||
return await self.chat_member_handlers.notify(update.chat_member)
|
||||
except Exception as e:
|
||||
err = await self.errors_handlers.notify(update, e)
|
||||
if err:
|
||||
|
|
@ -1005,6 +1020,118 @@ class Dispatcher(DataMixin, ContextInstanceMixin):
|
|||
|
||||
return decorator
|
||||
|
||||
def register_my_chat_member_handler(self,
|
||||
callback: typing.Callable,
|
||||
*custom_filters,
|
||||
run_task: typing.Optional[bool] = None,
|
||||
**kwargs) -> None:
|
||||
"""
|
||||
Register handler for my_chat_member
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: python3
|
||||
|
||||
dp.register_my_chat_member_handler(some_my_chat_member_handler)
|
||||
|
||||
:param callback:
|
||||
:param custom_filters:
|
||||
:param run_task: run callback in task (no wait results)
|
||||
:param kwargs:
|
||||
"""
|
||||
filters_set = self.filters_factory.resolve(
|
||||
self.my_chat_member_handlers,
|
||||
*custom_filters,
|
||||
**kwargs,
|
||||
)
|
||||
self.my_chat_member_handlers.register(
|
||||
handler=self._wrap_async_task(callback, run_task),
|
||||
filters=filters_set,
|
||||
)
|
||||
|
||||
def my_chat_member_handler(self, *custom_filters, run_task=None, **kwargs):
|
||||
"""
|
||||
Decorator for my_chat_member handler
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: python3
|
||||
|
||||
@dp.my_chat_member_handler()
|
||||
async def some_handler(my_chat_member: types.ChatMemberUpdated)
|
||||
|
||||
:param custom_filters:
|
||||
:param run_task: run callback in task (no wait results)
|
||||
:param kwargs:
|
||||
"""
|
||||
|
||||
def decorator(callback):
|
||||
self.register_my_chat_member_handler(
|
||||
callback,
|
||||
*custom_filters,
|
||||
run_task=run_task,
|
||||
**kwargs,
|
||||
)
|
||||
return callback
|
||||
|
||||
return decorator
|
||||
|
||||
def register_chat_member_handler(self,
|
||||
callback: typing.Callable,
|
||||
*custom_filters,
|
||||
run_task: typing.Optional[bool] = None,
|
||||
**kwargs) -> None:
|
||||
"""
|
||||
Register handler for chat_member
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: python3
|
||||
|
||||
dp.register_chat_member_handler(some_chat_member_handler)
|
||||
|
||||
:param callback:
|
||||
:param custom_filters:
|
||||
:param run_task: run callback in task (no wait results)
|
||||
:param kwargs:
|
||||
"""
|
||||
filters_set = self.filters_factory.resolve(
|
||||
self.chat_member_handlers,
|
||||
*custom_filters,
|
||||
**kwargs,
|
||||
)
|
||||
self.chat_member_handlers.register(
|
||||
handler=self._wrap_async_task(callback, run_task),
|
||||
filters=filters_set,
|
||||
)
|
||||
|
||||
def chat_member_handler(self, *custom_filters, run_task=None, **kwargs):
|
||||
"""
|
||||
Decorator for chat_member handler
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: python3
|
||||
|
||||
@dp.chat_member_handler()
|
||||
async def some_handler(chat_member: types.ChatMemberUpdated)
|
||||
|
||||
:param custom_filters:
|
||||
:param run_task: run callback in task (no wait results)
|
||||
:param kwargs:
|
||||
"""
|
||||
|
||||
def decorator(callback):
|
||||
self.register_chat_member_handler(
|
||||
callback,
|
||||
*custom_filters,
|
||||
run_task=run_task,
|
||||
**kwargs,
|
||||
)
|
||||
return callback
|
||||
|
||||
return decorator
|
||||
|
||||
def register_errors_handler(self, callback, *custom_filters, exception=None, run_task=None, **kwargs):
|
||||
"""
|
||||
Register handler for errors
|
||||
|
|
@ -1084,8 +1211,11 @@ class Dispatcher(DataMixin, ContextInstanceMixin):
|
|||
if rate is None:
|
||||
rate = self.throttling_rate_limit
|
||||
if user_id is None and chat_id is None:
|
||||
user_id = types.User.get_current().id
|
||||
chat_id = types.Chat.get_current().id
|
||||
chat_obj = types.Chat.get_current()
|
||||
chat_id = chat_obj.id if chat_obj else None
|
||||
|
||||
user_obj = types.User.get_current()
|
||||
user_id = user_obj.id if user_obj else None
|
||||
|
||||
# Detect current time
|
||||
now = time.time()
|
||||
|
|
@ -1136,8 +1266,11 @@ class Dispatcher(DataMixin, ContextInstanceMixin):
|
|||
raise RuntimeError('This storage does not provide Leaky Bucket')
|
||||
|
||||
if user_id is None and chat_id is None:
|
||||
user_id = types.User.get_current()
|
||||
chat_id = types.Chat.get_current()
|
||||
chat_obj = types.Chat.get_current()
|
||||
chat_id = chat_obj.id if chat_obj else None
|
||||
|
||||
user_obj = types.User.get_current()
|
||||
user_id = user_obj.id if user_obj else None
|
||||
|
||||
bucket = await self.storage.get_bucket(chat=chat_id, user=user_id)
|
||||
data = bucket.get(key, {})
|
||||
|
|
@ -1158,8 +1291,11 @@ class Dispatcher(DataMixin, ContextInstanceMixin):
|
|||
raise RuntimeError('This storage does not provide Leaky Bucket')
|
||||
|
||||
if user_id is None and chat_id is None:
|
||||
user_id = types.User.get_current()
|
||||
chat_id = types.Chat.get_current()
|
||||
chat_obj = types.Chat.get_current()
|
||||
chat_id = chat_obj.id if chat_obj else None
|
||||
|
||||
user_obj = types.User.get_current()
|
||||
user_id = user_obj.id if user_obj else None
|
||||
|
||||
bucket = await self.storage.get_bucket(chat=chat_id, user=user_id)
|
||||
if bucket and key in bucket:
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ from babel.support import LazyProxy
|
|||
|
||||
from aiogram import types
|
||||
from aiogram.dispatcher.filters.filters import BoundFilter, Filter
|
||||
from aiogram.types import CallbackQuery, ChatType, InlineQuery, Message, Poll
|
||||
from aiogram.types import CallbackQuery, ChatType, InlineQuery, Message, Poll, ChatMemberUpdated
|
||||
|
||||
ChatIDArgumentType = typing.Union[typing.Iterable[typing.Union[int, str]], str, int]
|
||||
|
||||
|
|
@ -529,6 +529,8 @@ class StateFilter(BoundFilter):
|
|||
self.states = states
|
||||
|
||||
def get_target(self, obj):
|
||||
if isinstance(obj, CallbackQuery):
|
||||
return getattr(getattr(getattr(obj, 'message', None),'chat', None), 'id', None), getattr(getattr(obj, 'from_user', None), 'id', None)
|
||||
return getattr(getattr(obj, 'chat', None), 'id', None), getattr(getattr(obj, 'from_user', None), 'id', None)
|
||||
|
||||
async def check(self, obj):
|
||||
|
|
@ -604,7 +606,7 @@ class IDFilter(Filter):
|
|||
|
||||
return result
|
||||
|
||||
async def check(self, obj: Union[Message, CallbackQuery, InlineQuery]):
|
||||
async def check(self, obj: Union[Message, CallbackQuery, InlineQuery, ChatMemberUpdated]):
|
||||
if isinstance(obj, Message):
|
||||
user_id = None
|
||||
if obj.from_user is not None:
|
||||
|
|
@ -619,6 +621,9 @@ class IDFilter(Filter):
|
|||
elif isinstance(obj, InlineQuery):
|
||||
user_id = obj.from_user.id
|
||||
chat_id = None
|
||||
elif isinstance(obj, ChatMemberUpdated):
|
||||
user_id = obj.from_user.id
|
||||
chat_id = obj.chat.id
|
||||
else:
|
||||
return False
|
||||
|
||||
|
|
@ -663,19 +668,21 @@ class AdminFilter(Filter):
|
|||
|
||||
return result
|
||||
|
||||
async def check(self, obj: Union[Message, CallbackQuery, InlineQuery]) -> bool:
|
||||
async def check(self, obj: Union[Message, CallbackQuery, InlineQuery, ChatMemberUpdated]) -> bool:
|
||||
user_id = obj.from_user.id
|
||||
|
||||
if self._check_current:
|
||||
if isinstance(obj, Message):
|
||||
message = obj
|
||||
chat = obj.chat
|
||||
elif isinstance(obj, CallbackQuery) and obj.message:
|
||||
message = obj.message
|
||||
chat = obj.message.chat
|
||||
elif isinstance(obj, ChatMemberUpdated):
|
||||
chat = obj.chat
|
||||
else:
|
||||
return False
|
||||
if message.chat.type == ChatType.PRIVATE: # there is no admin in private chats
|
||||
if chat.type == ChatType.PRIVATE: # there is no admin in private chats
|
||||
return False
|
||||
chat_ids = [message.chat.id]
|
||||
chat_ids = [chat.id]
|
||||
else:
|
||||
chat_ids = self._chat_ids
|
||||
|
||||
|
|
@ -719,11 +726,13 @@ class ChatTypeFilter(BoundFilter):
|
|||
|
||||
self.chat_type: typing.Set[str] = set(chat_type)
|
||||
|
||||
async def check(self, obj: Union[Message, CallbackQuery]):
|
||||
async def check(self, obj: Union[Message, CallbackQuery, ChatMemberUpdated]):
|
||||
if isinstance(obj, Message):
|
||||
obj = obj.chat
|
||||
elif isinstance(obj, CallbackQuery):
|
||||
obj = obj.message.chat
|
||||
elif isinstance(obj, ChatMemberUpdated):
|
||||
obj = obj.chat
|
||||
else:
|
||||
warnings.warn("ChatTypeFilter doesn't support %s as input", type(obj))
|
||||
return False
|
||||
|
|
|
|||
|
|
@ -7,8 +7,10 @@ from .bot_command import BotCommand
|
|||
from .callback_game import CallbackGame
|
||||
from .callback_query import CallbackQuery
|
||||
from .chat import Chat, ChatActions, ChatType
|
||||
from .chat_invite_link import ChatInviteLink
|
||||
from .chat_location import ChatLocation
|
||||
from .chat_member import ChatMember, ChatMemberStatus
|
||||
from .chat_member_updated import ChatMemberUpdated
|
||||
from .chat_permissions import ChatPermissions
|
||||
from .chat_photo import ChatPhoto
|
||||
from .chosen_inline_result import ChosenInlineResult
|
||||
|
|
@ -40,6 +42,7 @@ from .location import Location
|
|||
from .login_url import LoginUrl
|
||||
from .mask_position import MaskPosition
|
||||
from .message import ContentType, ContentTypes, Message, ParseMode
|
||||
from .message_auto_delete_timer_changed import MessageAutoDeleteTimerChanged
|
||||
from .message_entity import MessageEntity, MessageEntityType
|
||||
from .message_id import MessageId
|
||||
from .order_info import OrderInfo
|
||||
|
|
@ -67,6 +70,9 @@ from .venue import Venue
|
|||
from .video import Video
|
||||
from .video_note import VideoNote
|
||||
from .voice import Voice
|
||||
from .voice_chat_ended import VoiceChatEnded
|
||||
from .voice_chat_participants_invited import VoiceChatParticipantsInvited
|
||||
from .voice_chat_started import VoiceChatStarted
|
||||
from .webhook_info import WebhookInfo
|
||||
|
||||
__all__ = (
|
||||
|
|
@ -79,9 +85,11 @@ __all__ = (
|
|||
'CallbackQuery',
|
||||
'Chat',
|
||||
'ChatActions',
|
||||
'ChatInviteLink',
|
||||
'ChatLocation',
|
||||
'ChatMember',
|
||||
'ChatMemberStatus',
|
||||
'ChatMemberUpdated',
|
||||
'ChatPermissions',
|
||||
'ChatPhoto',
|
||||
'ChatType',
|
||||
|
|
@ -143,6 +151,7 @@ __all__ = (
|
|||
'MaskPosition',
|
||||
'MediaGroup',
|
||||
'Message',
|
||||
'MessageAutoDeleteTimerChanged',
|
||||
'MessageEntity',
|
||||
'MessageEntityType',
|
||||
'MessageId',
|
||||
|
|
@ -180,6 +189,9 @@ __all__ = (
|
|||
'Video',
|
||||
'VideoNote',
|
||||
'Voice',
|
||||
'VoiceChatEnded',
|
||||
'VoiceChatParticipantsInvited',
|
||||
'VoiceChatStarted',
|
||||
'WebhookInfo',
|
||||
'base',
|
||||
'fields',
|
||||
|
|
|
|||
|
|
@ -212,7 +212,7 @@ class TelegramObject(ContextInstanceMixin, metaclass=MetaTelegramObject):
|
|||
"""
|
||||
if item in self.props:
|
||||
return self.props[item].get_value(self)
|
||||
raise KeyError(item)
|
||||
return self.values[item]
|
||||
|
||||
def __setitem__(self, key: str, value: typing.Any) -> None:
|
||||
"""
|
||||
|
|
@ -224,6 +224,7 @@ class TelegramObject(ContextInstanceMixin, metaclass=MetaTelegramObject):
|
|||
"""
|
||||
if key in self.props:
|
||||
return self.props[key].set_value(self, value, self.conf.get('parent', None))
|
||||
self.values[key] = value
|
||||
raise KeyError(key)
|
||||
|
||||
def __contains__(self, item: str) -> bool:
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import datetime
|
|||
import typing
|
||||
|
||||
from . import base, fields
|
||||
from .chat_invite_link import ChatInviteLink
|
||||
from .chat_location import ChatLocation
|
||||
from .chat_member import ChatMember
|
||||
from .chat_permissions import ChatPermissions
|
||||
|
|
@ -113,7 +114,7 @@ class Chat(base.TelegramObject):
|
|||
|
||||
async def update_chat(self):
|
||||
"""
|
||||
User this method to update Chat data
|
||||
Use this method to update Chat data
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
|
@ -185,30 +186,47 @@ class Chat(base.TelegramObject):
|
|||
"""
|
||||
return await self.bot.set_chat_description(self.id, description)
|
||||
|
||||
async def kick(self, user_id: base.Integer,
|
||||
until_date: typing.Union[
|
||||
base.Integer, datetime.datetime, datetime.timedelta, None] = None) -> base.Boolean:
|
||||
async def kick(self,
|
||||
user_id: base.Integer,
|
||||
until_date: typing.Union[base.Integer, datetime.datetime,
|
||||
datetime.timedelta, None] = None,
|
||||
revoke_messages: typing.Optional[base.Boolean] = None,
|
||||
) -> base.Boolean:
|
||||
"""
|
||||
Use this method to kick a user from a group, a supergroup or a channel.
|
||||
In the case of supergroups and channels, the user will not be able to return to the group
|
||||
on their own using invite links, etc., unless unbanned first.
|
||||
In the case of supergroups and channels, the user will not be able to return
|
||||
to the chat on their own using invite links, etc., unless unbanned first.
|
||||
|
||||
The bot must be an administrator in the chat for this to work and must have the appropriate admin rights.
|
||||
|
||||
Note: In regular groups (non-supergroups), this method will only work if the ‘All Members Are Admins’ setting
|
||||
is off in the target group.
|
||||
Otherwise members may only be removed by the group's creator or by the member that added them.
|
||||
The bot must be an administrator in the chat for this to work and must have
|
||||
the appropriate admin rights.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#kickchatmember
|
||||
|
||||
:param user_id: Unique identifier of the target user
|
||||
:type user_id: :obj:`base.Integer`
|
||||
:param until_date: Date when the user will be unbanned, unix time.
|
||||
:type until_date: :obj:`typing.Optional[base.Integer]`
|
||||
:return: Returns True on success.
|
||||
|
||||
:param until_date: Date when the user will be unbanned. If user is banned
|
||||
for more than 366 days or less than 30 seconds from the current time they
|
||||
are considered to be banned forever. Applied for supergroups and channels
|
||||
only.
|
||||
:type until_date: :obj:`typing.Union[base.Integer, datetime.datetime,
|
||||
datetime.timedelta, None]`
|
||||
|
||||
:param revoke_messages: Pass True to delete all messages from the chat for
|
||||
the user that is being removed. If False, the user will be able to see
|
||||
messages in the group that were sent before the user was removed. Always
|
||||
True for supergroups and channels.
|
||||
:type revoke_messages: :obj:`typing.Optional[base.Boolean]`
|
||||
|
||||
:return: Returns True on success
|
||||
:rtype: :obj:`base.Boolean`
|
||||
"""
|
||||
return await self.bot.kick_chat_member(self.id, user_id=user_id, until_date=until_date)
|
||||
return await self.bot.kick_chat_member(
|
||||
chat_id=self.id,
|
||||
user_id=user_id,
|
||||
until_date=until_date,
|
||||
revoke_messages=revoke_messages,
|
||||
)
|
||||
|
||||
async def unban(self,
|
||||
user_id: base.Integer,
|
||||
|
|
@ -554,6 +572,41 @@ class Chat(base.TelegramObject):
|
|||
|
||||
return self.invite_link
|
||||
|
||||
async def create_invite_link(self,
|
||||
expire_date: typing.Union[base.Integer, datetime.datetime,
|
||||
datetime.timedelta, None] = None,
|
||||
member_limit: typing.Optional[base.Integer] = None,
|
||||
) -> ChatInviteLink:
|
||||
""" Shortcut for createChatInviteLink method. """
|
||||
return await self.bot.create_chat_invite_link(
|
||||
chat_id=self.id,
|
||||
expire_date=expire_date,
|
||||
member_limit=member_limit,
|
||||
)
|
||||
|
||||
async def edit_invite_link(self,
|
||||
invite_link: base.String,
|
||||
expire_date: typing.Union[base.Integer, datetime.datetime,
|
||||
datetime.timedelta, None] = None,
|
||||
member_limit: typing.Optional[base.Integer] = None,
|
||||
) -> ChatInviteLink:
|
||||
""" Shortcut for editChatInviteLink method. """
|
||||
return await self.bot.edit_chat_invite_link(
|
||||
chat_id=self.id,
|
||||
invite_link=invite_link,
|
||||
expire_date=expire_date,
|
||||
member_limit=member_limit,
|
||||
)
|
||||
|
||||
async def revoke_invite_link(self,
|
||||
invite_link: base.String,
|
||||
) -> ChatInviteLink:
|
||||
""" Shortcut for revokeChatInviteLink method. """
|
||||
return await self.bot.revoke_chat_invite_link(
|
||||
chat_id=self.id,
|
||||
invite_link=invite_link,
|
||||
)
|
||||
|
||||
def __int__(self):
|
||||
return self.id
|
||||
|
||||
|
|
|
|||
20
aiogram/types/chat_invite_link.py
Normal file
20
aiogram/types/chat_invite_link.py
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
from datetime import datetime
|
||||
|
||||
from . import base
|
||||
from . import fields
|
||||
from .user import User
|
||||
|
||||
|
||||
class ChatInviteLink(base.TelegramObject):
|
||||
"""
|
||||
Represents an invite link for a chat.
|
||||
|
||||
https://core.telegram.org/bots/api#chatinvitelink
|
||||
"""
|
||||
|
||||
invite_link: base.String = fields.Field()
|
||||
creator: User = fields.Field(base=User)
|
||||
is_primary: base.Boolean = fields.Field()
|
||||
is_revoked: base.Boolean = fields.Field()
|
||||
expire_date: datetime = fields.DateTimeField()
|
||||
member_limit: base.Integer = fields.Field()
|
||||
|
|
@ -16,22 +16,24 @@ class ChatMember(base.TelegramObject):
|
|||
status: base.String = fields.Field()
|
||||
custom_title: base.String = fields.Field()
|
||||
is_anonymous: base.Boolean = fields.Field()
|
||||
until_date: datetime.datetime = fields.DateTimeField()
|
||||
can_be_edited: base.Boolean = fields.Field()
|
||||
can_change_info: base.Boolean = fields.Field()
|
||||
can_manage_chat: base.Boolean = fields.Field()
|
||||
can_post_messages: base.Boolean = fields.Field()
|
||||
can_edit_messages: base.Boolean = fields.Field()
|
||||
can_delete_messages: base.Boolean = fields.Field()
|
||||
can_invite_users: base.Boolean = fields.Field()
|
||||
can_manage_voice_chats: base.Boolean = fields.Field()
|
||||
can_restrict_members: base.Boolean = fields.Field()
|
||||
can_pin_messages: base.Boolean = fields.Field()
|
||||
can_promote_members: base.Boolean = fields.Field()
|
||||
can_change_info: base.Boolean = fields.Field()
|
||||
can_invite_users: base.Boolean = fields.Field()
|
||||
can_pin_messages: base.Boolean = fields.Field()
|
||||
is_member: base.Boolean = fields.Field()
|
||||
can_send_messages: base.Boolean = fields.Field()
|
||||
can_send_media_messages: base.Boolean = fields.Field()
|
||||
can_send_polls: base.Boolean = fields.Field()
|
||||
can_send_other_messages: base.Boolean = fields.Field()
|
||||
can_add_web_page_previews: base.Boolean = fields.Field()
|
||||
until_date: datetime.datetime = fields.DateTimeField()
|
||||
|
||||
def is_chat_creator(self) -> bool:
|
||||
return ChatMemberStatus.is_chat_creator(self.status)
|
||||
|
|
|
|||
22
aiogram/types/chat_member_updated.py
Normal file
22
aiogram/types/chat_member_updated.py
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import datetime
|
||||
|
||||
from . import base
|
||||
from . import fields
|
||||
from .chat import Chat
|
||||
from .chat_invite_link import ChatInviteLink
|
||||
from .chat_member import ChatMember
|
||||
from .user import User
|
||||
|
||||
|
||||
class ChatMemberUpdated(base.TelegramObject):
|
||||
"""
|
||||
This object represents changes in the status of a chat member.
|
||||
|
||||
https://core.telegram.org/bots/api#chatmemberupdated
|
||||
"""
|
||||
chat: Chat = fields.Field(base=Chat)
|
||||
from_user: User = fields.Field(base=User)
|
||||
date: datetime.datetime = fields.DateTimeField()
|
||||
old_chat_member: ChatMember = fields.Field(base=ChatMember)
|
||||
new_chat_member: ChatMember = fields.Field(base=ChatMember)
|
||||
invite_link: ChatInviteLink = fields.Field(base=ChatInviteLink)
|
||||
|
|
@ -3,9 +3,7 @@ from . import base, fields
|
|||
|
||||
class Dice(base.TelegramObject):
|
||||
"""
|
||||
This object represents a dice with random value from 1 to 6.
|
||||
(Yes, we're aware of the “proper” singular of die.
|
||||
But it's awkward, and we decided to help it change. One dice at a time!)
|
||||
This object represents an animated emoji that displays a random value.
|
||||
|
||||
https://core.telegram.org/bots/api#dice
|
||||
"""
|
||||
|
|
@ -19,3 +17,4 @@ class DiceEmoji:
|
|||
BASKETBALL = '🏀'
|
||||
FOOTBALL = '⚽'
|
||||
SLOT_MACHINE = '🎰'
|
||||
BOWLING = '🎳'
|
||||
|
|
|
|||
|
|
@ -311,76 +311,96 @@ class MediaGroup(base.TelegramObject):
|
|||
self.attach(animation)
|
||||
'''
|
||||
|
||||
def attach_audio(self, audio: base.InputFile,
|
||||
def attach_audio(self, audio: typing.Union[InputMediaAudio, base.InputFile],
|
||||
thumb: typing.Union[base.InputFile, base.String] = None,
|
||||
caption: base.String = None,
|
||||
width: base.Integer = None, height: base.Integer = None,
|
||||
duration: base.Integer = None,
|
||||
performer: base.String = None,
|
||||
title: base.String = None,
|
||||
parse_mode: base.String = None):
|
||||
parse_mode: base.String = None,
|
||||
caption_entities: typing.Optional[typing.List[MessageEntity]] = None):
|
||||
"""
|
||||
Attach animation
|
||||
Attach audio
|
||||
|
||||
:param audio:
|
||||
:param thumb:
|
||||
:param caption:
|
||||
:param width:
|
||||
:param height:
|
||||
:param duration:
|
||||
:param performer:
|
||||
:param title:
|
||||
:param parse_mode:
|
||||
:param caption_entities:
|
||||
"""
|
||||
if not isinstance(audio, InputMedia):
|
||||
audio = InputMediaAudio(media=audio, thumb=thumb, caption=caption,
|
||||
width=width, height=height, duration=duration,
|
||||
duration=duration,
|
||||
performer=performer, title=title,
|
||||
parse_mode=parse_mode)
|
||||
parse_mode=parse_mode,
|
||||
caption_entities=caption_entities)
|
||||
self.attach(audio)
|
||||
|
||||
def attach_document(self, document: base.InputFile, thumb: typing.Union[base.InputFile, base.String] = None,
|
||||
caption: base.String = None, parse_mode: base.String = None):
|
||||
def attach_document(self, document: typing.Union[InputMediaDocument, base.InputFile],
|
||||
thumb: typing.Union[base.InputFile, base.String] = None,
|
||||
caption: base.String = None, parse_mode: base.String = None,
|
||||
caption_entities: typing.Optional[typing.List[MessageEntity]] = None,
|
||||
disable_content_type_detection: typing.Optional[base.Boolean] = None):
|
||||
"""
|
||||
Attach document
|
||||
|
||||
:param parse_mode:
|
||||
|
||||
:param document:
|
||||
:param caption:
|
||||
:param thumb:
|
||||
:param document:
|
||||
:param parse_mode:
|
||||
:param caption_entities:
|
||||
:param disable_content_type_detection:
|
||||
"""
|
||||
if not isinstance(document, InputMedia):
|
||||
document = InputMediaDocument(media=document, thumb=thumb, caption=caption, parse_mode=parse_mode)
|
||||
document = InputMediaDocument(media=document, thumb=thumb, caption=caption,
|
||||
parse_mode=parse_mode, caption_entities=caption_entities,
|
||||
disable_content_type_detection=disable_content_type_detection)
|
||||
self.attach(document)
|
||||
|
||||
def attach_photo(self, photo: typing.Union[InputMediaPhoto, base.InputFile],
|
||||
caption: base.String = None):
|
||||
caption: base.String = None, parse_mode: base.String = None,
|
||||
caption_entities: typing.Optional[typing.List[MessageEntity]] = None):
|
||||
"""
|
||||
Attach photo
|
||||
|
||||
:param photo:
|
||||
:param caption:
|
||||
:param parse_mode:
|
||||
:param caption_entities:
|
||||
"""
|
||||
if not isinstance(photo, InputMedia):
|
||||
photo = InputMediaPhoto(media=photo, caption=caption)
|
||||
photo = InputMediaPhoto(media=photo, caption=caption, parse_mode=parse_mode,
|
||||
caption_entities=caption_entities)
|
||||
self.attach(photo)
|
||||
|
||||
def attach_video(self, video: typing.Union[InputMediaVideo, base.InputFile],
|
||||
thumb: typing.Union[base.InputFile, base.String] = None,
|
||||
caption: base.String = None,
|
||||
width: base.Integer = None, height: base.Integer = None, duration: base.Integer = None):
|
||||
width: base.Integer = None, height: base.Integer = None,
|
||||
duration: base.Integer = None, parse_mode: base.String = None,
|
||||
caption_entities: typing.Optional[typing.List[MessageEntity]] = None,
|
||||
supports_streaming: base.Boolean = None):
|
||||
"""
|
||||
Attach video
|
||||
|
||||
:param video:
|
||||
:param thumb:
|
||||
:param caption:
|
||||
:param width:
|
||||
:param height:
|
||||
:param duration:
|
||||
:param parse_mode:
|
||||
:param caption_entities:
|
||||
:param supports_streaming:
|
||||
"""
|
||||
if not isinstance(video, InputMedia):
|
||||
video = InputMediaVideo(media=video, thumb=thumb, caption=caption,
|
||||
width=width, height=height, duration=duration)
|
||||
width=width, height=height, duration=duration,
|
||||
parse_mode=parse_mode, caption_entities=caption_entities,
|
||||
supports_streaming=supports_streaming)
|
||||
self.attach(video)
|
||||
|
||||
def to_python(self) -> typing.List:
|
||||
|
|
|
|||
|
|
@ -4,10 +4,6 @@ import datetime
|
|||
import functools
|
||||
import typing
|
||||
|
||||
from ..utils import helper
|
||||
from ..utils import markdown as md
|
||||
from ..utils.deprecated import deprecated
|
||||
from ..utils.text_decorations import html_decoration, markdown_decoration
|
||||
from . import base, fields
|
||||
from .animation import Animation
|
||||
from .audio import Audio
|
||||
|
|
@ -21,6 +17,7 @@ from .inline_keyboard import InlineKeyboardMarkup
|
|||
from .input_media import InputMedia, MediaGroup
|
||||
from .invoice import Invoice
|
||||
from .location import Location
|
||||
from .message_auto_delete_timer_changed import MessageAutoDeleteTimerChanged
|
||||
from .message_entity import MessageEntity
|
||||
from .message_id import MessageId
|
||||
from .passport_data import PassportData
|
||||
|
|
@ -35,6 +32,12 @@ from .venue import Venue
|
|||
from .video import Video
|
||||
from .video_note import VideoNote
|
||||
from .voice import Voice
|
||||
from .voice_chat_ended import VoiceChatEnded
|
||||
from .voice_chat_participants_invited import VoiceChatParticipantsInvited
|
||||
from .voice_chat_started import VoiceChatStarted
|
||||
from ..utils import helper
|
||||
from ..utils import markdown as md
|
||||
from ..utils.text_decorations import html_decoration, markdown_decoration
|
||||
|
||||
|
||||
class Message(base.TelegramObject):
|
||||
|
|
@ -86,6 +89,7 @@ class Message(base.TelegramObject):
|
|||
group_chat_created: base.Boolean = fields.Field()
|
||||
supergroup_chat_created: base.Boolean = fields.Field()
|
||||
channel_chat_created: base.Boolean = fields.Field()
|
||||
message_auto_delete_timer_changed: MessageAutoDeleteTimerChanged = fields.Field(base=MessageAutoDeleteTimerChanged)
|
||||
migrate_to_chat_id: base.Integer = fields.Field()
|
||||
migrate_from_chat_id: base.Integer = fields.Field()
|
||||
pinned_message: Message = fields.Field(base="Message")
|
||||
|
|
@ -94,6 +98,9 @@ class Message(base.TelegramObject):
|
|||
connected_website: base.String = fields.Field()
|
||||
passport_data: PassportData = fields.Field(base=PassportData)
|
||||
proximity_alert_triggered: ProximityAlertTriggered = fields.Field(base=ProximityAlertTriggered)
|
||||
voice_chat_started: VoiceChatStarted = fields.Field(base=VoiceChatStarted)
|
||||
voice_chat_ended: VoiceChatEnded = fields.Field(base=VoiceChatEnded)
|
||||
voice_chat_participants_invited: VoiceChatParticipantsInvited = fields.Field(base=VoiceChatParticipantsInvited)
|
||||
reply_markup: InlineKeyboardMarkup = fields.Field(base=InlineKeyboardMarkup)
|
||||
|
||||
@property
|
||||
|
|
@ -139,6 +146,8 @@ class Message(base.TelegramObject):
|
|||
return ContentType.SUCCESSFUL_PAYMENT
|
||||
if self.connected_website:
|
||||
return ContentType.CONNECTED_WEBSITE
|
||||
if self.message_auto_delete_timer_changed:
|
||||
return ContentType.MESSAGE_AUTO_DELETE_TIMER_CHANGED
|
||||
if self.migrate_from_chat_id:
|
||||
return ContentType.MIGRATE_FROM_CHAT_ID
|
||||
if self.migrate_to_chat_id:
|
||||
|
|
@ -157,6 +166,12 @@ class Message(base.TelegramObject):
|
|||
return ContentType.PASSPORT_DATA
|
||||
if self.proximity_alert_triggered:
|
||||
return ContentType.PROXIMITY_ALERT_TRIGGERED
|
||||
if self.voice_chat_started:
|
||||
return ContentType.VOICE_CHAT_STARTED
|
||||
if self.voice_chat_ended:
|
||||
return ContentType.VOICE_CHAT_ENDED
|
||||
if self.voice_chat_participants_invited:
|
||||
return ContentType.VOICE_CHAT_PARTICIPANTS_INVITED
|
||||
|
||||
return ContentType.UNKNOWN
|
||||
|
||||
|
|
@ -960,6 +975,9 @@ class Message(base.TelegramObject):
|
|||
live_period: typing.Optional[base.Integer] = None,
|
||||
disable_notification: typing.Optional[base.Boolean] = None,
|
||||
allow_sending_without_reply: typing.Optional[base.Boolean] = None,
|
||||
horizontal_accuracy: typing.Optional[base.Float] = None,
|
||||
heading: typing.Optional[base.Integer] = None,
|
||||
proximity_alert_radius: typing.Optional[base.Integer] = None,
|
||||
reply_markup: typing.Union[
|
||||
InlineKeyboardMarkup,
|
||||
ReplyKeyboardMarkup,
|
||||
|
|
@ -980,9 +998,22 @@ class Message(base.TelegramObject):
|
|||
:param longitude: Longitude of the location
|
||||
:type longitude: :obj:`base.Float`
|
||||
|
||||
:param horizontal_accuracy: The radius of uncertainty for the location,
|
||||
measured in meters; 0-1500
|
||||
:type horizontal_accuracy: :obj:`typing.Optional[base.Float]`
|
||||
|
||||
:param live_period: Period in seconds for which the location will be updated
|
||||
:type live_period: :obj:`typing.Optional[base.Integer]`
|
||||
|
||||
:param heading: For live locations, a direction in which the user is moving,
|
||||
in degrees. Must be between 1 and 360 if specified.
|
||||
:type heading: :obj:`typing.Optional[base.Integer]`
|
||||
|
||||
:param proximity_alert_radius: For live locations, a maximum distance for
|
||||
proximity alerts about approaching another chat member, in meters. Must
|
||||
be between 1 and 100000 if specified.
|
||||
:type proximity_alert_radius: :obj:`typing.Optional[base.Integer]`
|
||||
|
||||
:param disable_notification: Sends the message silently. Users will receive a notification with no sound.
|
||||
:type disable_notification: :obj:`typing.Optional[base.Boolean]`
|
||||
|
||||
|
|
@ -1005,7 +1036,10 @@ class Message(base.TelegramObject):
|
|||
chat_id=self.chat.id,
|
||||
latitude=latitude,
|
||||
longitude=longitude,
|
||||
horizontal_accuracy=horizontal_accuracy,
|
||||
live_period=live_period,
|
||||
heading=heading,
|
||||
proximity_alert_radius=proximity_alert_radius,
|
||||
disable_notification=disable_notification,
|
||||
reply_to_message_id=self.message_id if reply else None,
|
||||
allow_sending_without_reply=allow_sending_without_reply,
|
||||
|
|
@ -1381,6 +1415,30 @@ class Message(base.TelegramObject):
|
|||
allow_sending_without_reply=allow_sending_without_reply,
|
||||
reply_markup=reply_markup,
|
||||
)
|
||||
|
||||
async def answer_chat_action(
|
||||
self,
|
||||
action: base.String,
|
||||
) -> base.Boolean:
|
||||
"""
|
||||
Use this method when you need to tell the user that something is happening on the bot's side.
|
||||
The status is set for 5 seconds or less
|
||||
(when a message arrives from your bot, Telegram clients clear its typing status).
|
||||
|
||||
We only recommend using this method when a response from the bot will take
|
||||
a noticeable amount of time to arrive.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#sendchataction
|
||||
|
||||
:param action: Type of action to broadcast
|
||||
:type action: :obj:`base.String`
|
||||
:return: Returns True on success
|
||||
:rtype: :obj:`base.Boolean`
|
||||
"""
|
||||
return await self.bot.send_chat_action(
|
||||
chat_id=self.chat.id,
|
||||
action=action,
|
||||
)
|
||||
|
||||
async def reply(
|
||||
self,
|
||||
|
|
@ -2057,6 +2115,9 @@ class Message(base.TelegramObject):
|
|||
longitude: base.Float,
|
||||
live_period: typing.Optional[base.Integer] = None,
|
||||
disable_notification: typing.Optional[base.Boolean] = None,
|
||||
horizontal_accuracy: typing.Optional[base.Float] = None,
|
||||
heading: typing.Optional[base.Integer] = None,
|
||||
proximity_alert_radius: typing.Optional[base.Integer] = None,
|
||||
reply_markup: typing.Union[
|
||||
InlineKeyboardMarkup,
|
||||
ReplyKeyboardMarkup,
|
||||
|
|
@ -2073,18 +2134,37 @@ class Message(base.TelegramObject):
|
|||
|
||||
:param latitude: Latitude of the location
|
||||
:type latitude: :obj:`base.Float`
|
||||
|
||||
:param longitude: Longitude of the location
|
||||
:type longitude: :obj:`base.Float`
|
||||
|
||||
:param horizontal_accuracy: The radius of uncertainty for the location,
|
||||
measured in meters; 0-1500
|
||||
:type horizontal_accuracy: :obj:`typing.Optional[base.Float]`
|
||||
|
||||
:param live_period: Period in seconds for which the location will be updated
|
||||
:type live_period: :obj:`typing.Optional[base.Integer]`
|
||||
|
||||
:param heading: For live locations, a direction in which the user is moving,
|
||||
in degrees. Must be between 1 and 360 if specified.
|
||||
:type heading: :obj:`typing.Optional[base.Integer]`
|
||||
|
||||
:param proximity_alert_radius: For live locations, a maximum distance for
|
||||
proximity alerts about approaching another chat member, in meters. Must
|
||||
be between 1 and 100000 if specified.
|
||||
:type proximity_alert_radius: :obj:`typing.Optional[base.Integer]`
|
||||
|
||||
:param disable_notification: Sends the message silently. Users will receive a notification with no sound.
|
||||
:type disable_notification: :obj:`typing.Optional[base.Boolean]`
|
||||
|
||||
:param reply_markup: Additional interface options. A JSON-serialized object for an inline keyboard,
|
||||
custom reply keyboard, instructions to remove reply keyboard or to force a reply from the user
|
||||
:type reply_markup: :obj:`typing.Union[types.InlineKeyboardMarkup,
|
||||
types.ReplyKeyboardMarkup, types.ReplyKeyboardRemove, types.ForceReply, None]`
|
||||
|
||||
:param reply: fill 'reply_to_message_id'
|
||||
:type reply: :obj:`base.Boolean`
|
||||
|
||||
:return: On success, the sent Message is returned.
|
||||
:rtype: :obj:`types.Message`
|
||||
"""
|
||||
|
|
@ -2092,7 +2172,10 @@ class Message(base.TelegramObject):
|
|||
chat_id=self.chat.id,
|
||||
latitude=latitude,
|
||||
longitude=longitude,
|
||||
horizontal_accuracy=horizontal_accuracy,
|
||||
live_period=live_period,
|
||||
heading=heading,
|
||||
proximity_alert_radius=proximity_alert_radius,
|
||||
disable_notification=disable_notification,
|
||||
reply_to_message_id=self.message_id if reply else None,
|
||||
reply_markup=reply_markup,
|
||||
|
|
@ -2740,11 +2823,6 @@ class Message(base.TelegramObject):
|
|||
message_id=self.message_id,
|
||||
)
|
||||
|
||||
@deprecated(
|
||||
"This method deprecated since Bot API 4.5. Use method `copy_to` instead. \n"
|
||||
"Read more: https://core.telegram.org/bots/api#copymessage",
|
||||
stacklevel=3
|
||||
)
|
||||
async def send_copy(
|
||||
self: Message,
|
||||
chat_id: typing.Union[str, int],
|
||||
|
|
@ -2847,6 +2925,14 @@ class Message(base.TelegramObject):
|
|||
return await self.bot.send_poll(
|
||||
question=self.poll.question,
|
||||
options=[option.text for option in self.poll.options],
|
||||
is_anonymous=self.poll.is_anonymous,
|
||||
allows_multiple_answers=self.poll.allows_multiple_answers
|
||||
**kwargs,
|
||||
)
|
||||
elif self.dice:
|
||||
kwargs.pop("parse_mode")
|
||||
return await self.bot.send_dice(
|
||||
emoji=self.dice.emoji,
|
||||
**kwargs,
|
||||
)
|
||||
else:
|
||||
|
|
@ -2936,6 +3022,7 @@ class ContentType(helper.Helper):
|
|||
INVOICE = helper.Item() # invoice
|
||||
SUCCESSFUL_PAYMENT = helper.Item() # successful_payment
|
||||
CONNECTED_WEBSITE = helper.Item() # connected_website
|
||||
MESSAGE_AUTO_DELETE_TIMER_CHANGED = helper.Item() # message_auto_delete_timer_changed
|
||||
MIGRATE_TO_CHAT_ID = helper.Item() # migrate_to_chat_id
|
||||
MIGRATE_FROM_CHAT_ID = helper.Item() # migrate_from_chat_id
|
||||
PINNED_MESSAGE = helper.Item() # pinned_message
|
||||
|
|
@ -2945,6 +3032,9 @@ class ContentType(helper.Helper):
|
|||
GROUP_CHAT_CREATED = helper.Item() # group_chat_created
|
||||
PASSPORT_DATA = helper.Item() # passport_data
|
||||
PROXIMITY_ALERT_TRIGGERED = helper.Item() # proximity_alert_triggered
|
||||
VOICE_CHAT_STARTED = helper.Item() # voice_chat_started
|
||||
VOICE_CHAT_ENDED = helper.Item() # voice_chat_ended
|
||||
VOICE_CHAT_PARTICIPANTS_INVITED = helper.Item() # voice_chat_participants_invited
|
||||
|
||||
UNKNOWN = helper.Item() # unknown
|
||||
ANY = helper.Item() # any
|
||||
|
|
|
|||
11
aiogram/types/message_auto_delete_timer_changed.py
Normal file
11
aiogram/types/message_auto_delete_timer_changed.py
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
from . import base
|
||||
from . import fields
|
||||
|
||||
|
||||
class MessageAutoDeleteTimerChanged(base.TelegramObject):
|
||||
"""
|
||||
This object represents a service message about a change in auto-delete timer settings.
|
||||
|
||||
https://core.telegram.org/bots/api#messageautodeletetimerchanged
|
||||
"""
|
||||
message_auto_delete_time: base.Integer = fields.Field()
|
||||
|
|
@ -3,6 +3,7 @@ from __future__ import annotations
|
|||
from . import base
|
||||
from . import fields
|
||||
from .callback_query import CallbackQuery
|
||||
from .chat_member_updated import ChatMemberUpdated
|
||||
from .chosen_inline_result import ChosenInlineResult
|
||||
from .inline_query import InlineQuery
|
||||
from .message import Message
|
||||
|
|
@ -31,6 +32,8 @@ class Update(base.TelegramObject):
|
|||
pre_checkout_query: PreCheckoutQuery = fields.Field(base=PreCheckoutQuery)
|
||||
poll: Poll = fields.Field(base=Poll)
|
||||
poll_answer: PollAnswer = fields.Field(base=PollAnswer)
|
||||
my_chat_member: ChatMemberUpdated = fields.Field(base=ChatMemberUpdated)
|
||||
chat_member: ChatMemberUpdated = fields.Field(base=ChatMemberUpdated)
|
||||
|
||||
def __hash__(self):
|
||||
return self.update_id
|
||||
|
|
@ -61,6 +64,8 @@ class AllowedUpdates(helper.Helper):
|
|||
PRE_CHECKOUT_QUERY = helper.ListItem() # pre_checkout_query
|
||||
POLL = helper.ListItem() # poll
|
||||
POLL_ANSWER = helper.ListItem() # poll_answer
|
||||
MY_CHAT_MEMBER = helper.ListItem() # my_chat_member
|
||||
CHAT_MEMBER = helper.ListItem() # chat_member
|
||||
|
||||
CHOSEN_INLINE_QUERY = deprecated.DeprecatedReadOnlyClassVar(
|
||||
"`CHOSEN_INLINE_QUERY` is a deprecated value for allowed update. "
|
||||
|
|
|
|||
13
aiogram/types/voice_chat_ended.py
Normal file
13
aiogram/types/voice_chat_ended.py
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
from . import base
|
||||
from . import fields
|
||||
from . import mixins
|
||||
|
||||
|
||||
class VoiceChatEnded(base.TelegramObject, mixins.Downloadable):
|
||||
"""
|
||||
This object represents a service message about a voice chat ended in the chat.
|
||||
|
||||
https://core.telegram.org/bots/api#voicechatended
|
||||
"""
|
||||
|
||||
duration: base.Integer = fields.Field()
|
||||
16
aiogram/types/voice_chat_participants_invited.py
Normal file
16
aiogram/types/voice_chat_participants_invited.py
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import typing
|
||||
|
||||
from . import base
|
||||
from . import fields
|
||||
from . import mixins
|
||||
from .user import User
|
||||
|
||||
|
||||
class VoiceChatParticipantsInvited(base.TelegramObject, mixins.Downloadable):
|
||||
"""
|
||||
This object represents a service message about new members invited to a voice chat.
|
||||
|
||||
https://core.telegram.org/bots/api#voicechatparticipantsinvited
|
||||
"""
|
||||
|
||||
users: typing.List[User] = fields.ListField(base=User)
|
||||
12
aiogram/types/voice_chat_started.py
Normal file
12
aiogram/types/voice_chat_started.py
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
from . import base
|
||||
from . import mixins
|
||||
|
||||
|
||||
class VoiceChatStarted(base.TelegramObject, mixins.Downloadable):
|
||||
"""
|
||||
This object represents a service message about a voice chat started in the chat.
|
||||
Currently holds no information.
|
||||
|
||||
https://core.telegram.org/bots/api#voicechatstarted
|
||||
"""
|
||||
pass
|
||||
|
|
@ -494,6 +494,20 @@ class MethodIsNotAvailable(BadRequest):
|
|||
match = "Method is available only for supergroups"
|
||||
|
||||
|
||||
class CantRestrictChatOwner(BadRequest):
|
||||
"""
|
||||
Raises when bot restricts the chat owner
|
||||
"""
|
||||
match = 'Can\'t remove chat owner'
|
||||
|
||||
|
||||
class UserIsAnAdministratorOfTheChat(BadRequest):
|
||||
"""
|
||||
Raises when bot restricts the chat admin
|
||||
"""
|
||||
match = 'User is an administrator of the chat'
|
||||
|
||||
|
||||
class NotFound(TelegramAPIError, _MatchErrorMixin):
|
||||
__group = True
|
||||
|
||||
|
|
|
|||
|
|
@ -15,12 +15,13 @@ def split_text(text: str, length: int = MAX_MESSAGE_LENGTH) -> typing.List[str]:
|
|||
return [text[i:i + length] for i in range(0, len(text), length)]
|
||||
|
||||
|
||||
def safe_split_text(text: str, length: int = MAX_MESSAGE_LENGTH) -> typing.List[str]:
|
||||
def safe_split_text(text: str, length: int = MAX_MESSAGE_LENGTH, split_separator: str = ' ') -> typing.List[str]:
|
||||
"""
|
||||
Split long text
|
||||
|
||||
:param text:
|
||||
:param length:
|
||||
:param split_separator
|
||||
:return:
|
||||
"""
|
||||
# TODO: More informative description
|
||||
|
|
@ -30,7 +31,7 @@ def safe_split_text(text: str, length: int = MAX_MESSAGE_LENGTH) -> typing.List[
|
|||
while temp_text:
|
||||
if len(temp_text) > length:
|
||||
try:
|
||||
split_pos = temp_text[:length].rindex(' ')
|
||||
split_pos = temp_text[:length].rindex(split_separator)
|
||||
except ValueError:
|
||||
split_pos = length
|
||||
if split_pos < length // 4 * 3:
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ Welcome to aiogram's documentation!
|
|||
:target: https://pypi.python.org/pypi/aiogram
|
||||
:alt: Supported python versions
|
||||
|
||||
.. image:: https://img.shields.io/badge/Telegram%20Bot%20API-5.0-blue.svg?style=flat-square&logo=telegram
|
||||
.. image:: https://img.shields.io/badge/Telegram%20Bot%20API-5.1-blue.svg?style=flat-square&logo=telegram
|
||||
:target: https://core.telegram.org/bots/api
|
||||
:alt: Telegram Bot API
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ From sources
|
|||
$ cd aiogram
|
||||
$ python setup.py install
|
||||
|
||||
Or if you want to install stable version (The same with version form PyPi):
|
||||
Or if you want to install stable version (The same with version from PyPi):
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ dp = Dispatcher(bot)
|
|||
@dp.message_handler(chat_type=[ChatType.PRIVATE, ChatType.CHANNEL])
|
||||
async def send_welcome(message: types.Message):
|
||||
"""
|
||||
This handler will be called when user sends `/start` or `/help` command
|
||||
This handler will be called when user sends message in private chat or channel
|
||||
"""
|
||||
await message.reply("Hi!\nI'm hearing your messages in private chats and channels")
|
||||
|
||||
|
|
@ -33,7 +33,7 @@ async def send_welcome(message: types.Message):
|
|||
@dp.message_handler(chat_type=ChatType.PRIVATE)
|
||||
async def send_welcome(message: types.Message):
|
||||
"""
|
||||
This handler will be called when user sends `/start` or `/help` command
|
||||
This handler will be called when user sends message in private chat
|
||||
"""
|
||||
await message.reply("Hi!\nI'm hearing your messages only in private chats")
|
||||
|
||||
|
|
|
|||
34
examples/separate_api_route_example.py
Normal file
34
examples/separate_api_route_example.py
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
# NOTE: This is an example of an integration between
|
||||
# externally created Application object and the aiogram's dispatcher
|
||||
# This can be used for a custom route, for instance
|
||||
|
||||
from aiogram import Bot, Dispatcher, types
|
||||
from aiogram.dispatcher.webhook import configure_app
|
||||
from aiohttp import web
|
||||
|
||||
|
||||
bot = Bot(token=config.bot_token)
|
||||
dp = Dispatcher(bot)
|
||||
|
||||
|
||||
|
||||
@dp.message_handler(commands=["start"])
|
||||
async def cmd_start(message: types.Message):
|
||||
await message.reply("start!")
|
||||
|
||||
|
||||
# handle /api route
|
||||
async def api_handler(request):
|
||||
return web.json_response({"status": "OK"}, status=200)
|
||||
|
||||
|
||||
app = web.Application()
|
||||
# add a custom route
|
||||
app.add_routes([web.post('/api', api_handler)])
|
||||
# every request to /bot route will be retransmitted to dispatcher to be handled
|
||||
# as a bot update
|
||||
configure_app(dp, app, "/bot")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
web.run_app(app, port=9000)
|
||||
Loading…
Add table
Add a link
Reference in a new issue