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
3cdc2f750b
25 changed files with 580 additions and 183 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)
|
||||
|
|
@ -29,9 +29,10 @@
|
|||
import asyncio
|
||||
from aiogram import Bot
|
||||
|
||||
BOT_TOKEN = ""
|
||||
|
||||
async def main():
|
||||
bot = Bot(token=BOT-TOKEN)
|
||||
bot = Bot(token=BOT_TOKEN)
|
||||
|
||||
try:
|
||||
me = await bot.get_me()
|
||||
|
|
@ -48,6 +49,8 @@ asyncio.run(main())
|
|||
import asyncio
|
||||
from aiogram import Bot, Dispatcher, types
|
||||
|
||||
BOT_TOKEN = ""
|
||||
|
||||
async def start_handler(event: types.Message):
|
||||
await event.answer(
|
||||
f"Hello, {event.from_user.get_mention(as_html=True)} 👋!",
|
||||
|
|
@ -55,7 +58,7 @@ async def start_handler(event: types.Message):
|
|||
)
|
||||
|
||||
async def main():
|
||||
bot = Bot(token=BOT-TOKEN)
|
||||
bot = Bot(token=BOT_TOKEN)
|
||||
try:
|
||||
disp = Dispatcher(bot=bot)
|
||||
disp.register_message_handler(start_handler, commands={"start", "restart"})
|
||||
|
|
|
|||
|
|
@ -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.2-blue.svg?style=flat-square&logo=telegram
|
||||
.. image:: https://img.shields.io/badge/Telegram%20Bot%20API-5.3-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.13'
|
||||
__api_version__ = '5.2'
|
||||
__version__ = '2.14'
|
||||
__api_version__ = '5.3'
|
||||
|
|
|
|||
|
|
@ -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.2
|
||||
List is updated to Bot API 5.3
|
||||
"""
|
||||
mode = HelperMode.lowerCamelCase
|
||||
|
||||
|
|
@ -225,6 +225,7 @@ class Methods(Helper):
|
|||
GET_USER_PROFILE_PHOTOS = Item() # getUserProfilePhotos
|
||||
GET_FILE = Item() # getFile
|
||||
KICK_CHAT_MEMBER = Item() # kickChatMember
|
||||
BAN_CHAT_MEMBER = Item() # banChatMember
|
||||
UNBAN_CHAT_MEMBER = Item() # unbanChatMember
|
||||
RESTRICT_CHAT_MEMBER = Item() # restrictChatMember
|
||||
PROMOTE_CHAT_MEMBER = Item() # promoteChatMember
|
||||
|
|
@ -244,12 +245,14 @@ class Methods(Helper):
|
|||
LEAVE_CHAT = Item() # leaveChat
|
||||
GET_CHAT = Item() # getChat
|
||||
GET_CHAT_ADMINISTRATORS = Item() # getChatAdministrators
|
||||
GET_CHAT_MEMBERS_COUNT = Item() # getChatMembersCount
|
||||
GET_CHAT_MEMBER_COUNT = Item() # getChatMemberCount
|
||||
GET_CHAT_MEMBERS_COUNT = Item() # getChatMembersCount (renamed to getChatMemberCount)
|
||||
GET_CHAT_MEMBER = Item() # getChatMember
|
||||
SET_CHAT_STICKER_SET = Item() # setChatStickerSet
|
||||
DELETE_CHAT_STICKER_SET = Item() # deleteChatStickerSet
|
||||
ANSWER_CALLBACK_QUERY = Item() # answerCallbackQuery
|
||||
SET_MY_COMMANDS = Item() # setMyCommands
|
||||
DELETE_MY_COMMANDS = Item() # deleteMyCommands
|
||||
GET_MY_COMMANDS = Item() # getMyCommands
|
||||
|
||||
# Updating messages
|
||||
|
|
|
|||
|
|
@ -1562,41 +1562,42 @@ 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,
|
||||
revoke_messages: typing.Optional[base.Boolean] = None,
|
||||
) -> base.Boolean:
|
||||
async def ban_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 chat on their own using invite links, etc., unless unbanned first.
|
||||
Use this method to ban a user in a group, a supergroup or a
|
||||
channel. 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. Returns True on success.
|
||||
|
||||
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#banchatmember
|
||||
|
||||
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 (in the format @channelusername)
|
||||
: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. 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 until_date: Date when the user will be unbanned, unix
|
||||
time. 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.
|
||||
: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
|
||||
|
|
@ -1605,7 +1606,22 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
|
|||
until_date = prepare_arg(until_date)
|
||||
payload = generate_payload(**locals())
|
||||
|
||||
return await self.request(api.Methods.KICK_CHAT_MEMBER, payload)
|
||||
return await self.request(api.Methods.BAN_CHAT_MEMBER, payload)
|
||||
|
||||
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:
|
||||
"""Renamed to ban_chat_member."""
|
||||
return await self.ban_chat_member(
|
||||
chat_id=chat_id,
|
||||
user_id=user_id,
|
||||
until_date=until_date,
|
||||
revoke_messages=revoke_messages,
|
||||
)
|
||||
|
||||
async def unban_chat_member(self,
|
||||
chat_id: typing.Union[base.Integer, base.String],
|
||||
|
|
@ -2130,13 +2146,13 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
|
|||
payload = generate_payload(**locals())
|
||||
|
||||
result = await self.request(api.Methods.GET_CHAT_ADMINISTRATORS, payload)
|
||||
return [types.ChatMember(**chatmember) for chatmember in result]
|
||||
return [types.ChatMember.resolve(**chat_member) for chat_member in result]
|
||||
|
||||
async def get_chat_members_count(self, chat_id: typing.Union[base.Integer, base.String]) -> base.Integer:
|
||||
async def get_chat_member_count(self, chat_id: typing.Union[base.Integer, base.String]) -> base.Integer:
|
||||
"""
|
||||
Use this method to get the number of members in a chat.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#getchatmemberscount
|
||||
Source: https://core.telegram.org/bots/api#getchatmembercount
|
||||
|
||||
:param chat_id: Unique identifier for the target chat or username of the target supergroup or channel
|
||||
:type chat_id: :obj:`typing.Union[base.Integer, base.String]`
|
||||
|
|
@ -2145,7 +2161,11 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
|
|||
"""
|
||||
payload = generate_payload(**locals())
|
||||
|
||||
return await self.request(api.Methods.GET_CHAT_MEMBERS_COUNT, payload)
|
||||
return await self.request(api.Methods.GET_CHAT_MEMBER_COUNT, payload)
|
||||
|
||||
async def get_chat_members_count(self, chat_id: typing.Union[base.Integer, base.String]) -> base.Integer:
|
||||
"""Renamed to get_chat_member_count."""
|
||||
return await self.get_chat_member_count(chat_id)
|
||||
|
||||
async def get_chat_member(self, chat_id: typing.Union[base.Integer, base.String],
|
||||
user_id: base.Integer) -> types.ChatMember:
|
||||
|
|
@ -2164,7 +2184,7 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
|
|||
payload = generate_payload(**locals())
|
||||
|
||||
result = await self.request(api.Methods.GET_CHAT_MEMBER, payload)
|
||||
return types.ChatMember(**result)
|
||||
return types.ChatMember.resolve(**result)
|
||||
|
||||
async def set_chat_sticker_set(self, chat_id: typing.Union[base.Integer, base.String],
|
||||
sticker_set_name: base.String) -> base.Boolean:
|
||||
|
|
@ -2241,31 +2261,95 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
|
|||
|
||||
return await self.request(api.Methods.ANSWER_CALLBACK_QUERY, payload)
|
||||
|
||||
async def set_my_commands(self, commands: typing.List[types.BotCommand]) -> base.Boolean:
|
||||
async def set_my_commands(self,
|
||||
commands: typing.List[types.BotCommand],
|
||||
scope: typing.Optional[types.BotCommandScope] = None,
|
||||
language_code: typing.Optional[base.String] = None,
|
||||
) -> base.Boolean:
|
||||
"""
|
||||
Use this method to change the list of the bot's commands.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#setmycommands
|
||||
|
||||
:param commands: A JSON-serialized list of bot commands to be set as the list of the bot's commands.
|
||||
At most 100 commands can be specified.
|
||||
:param commands: A JSON-serialized list of bot commands to be
|
||||
set as the list of the bot's commands. At most 100 commands
|
||||
can be specified.
|
||||
:type commands: :obj: `typing.List[types.BotCommand]`
|
||||
|
||||
:param scope: A JSON-serialized object, describing scope of
|
||||
users for which the commands are relevant. Defaults to
|
||||
BotCommandScopeDefault.
|
||||
:type scope: :obj: `typing.Optional[types.BotCommandScope]`
|
||||
|
||||
:param language_code: A two-letter ISO 639-1 language code. If
|
||||
empty, commands will be applied to all users from the given
|
||||
scope, for whose language there are no dedicated commands
|
||||
:type language_code: :obj: `typing.Optional[base.String]`
|
||||
|
||||
:return: Returns True on success.
|
||||
:rtype: :obj:`base.Boolean`
|
||||
"""
|
||||
commands = prepare_arg(commands)
|
||||
scope = prepare_arg(scope)
|
||||
payload = generate_payload(**locals())
|
||||
|
||||
return await self.request(api.Methods.SET_MY_COMMANDS, payload)
|
||||
|
||||
async def get_my_commands(self) -> typing.List[types.BotCommand]:
|
||||
async def delete_my_commands(self,
|
||||
scope: typing.Optional[types.BotCommandScope] = None,
|
||||
language_code: typing.Optional[base.String] = None,
|
||||
) -> base.Boolean:
|
||||
"""
|
||||
Use this method to get the current list of the bot's commands.
|
||||
Use this method to delete the list of the bot's commands for the
|
||||
given scope and user language. After deletion, higher level
|
||||
commands will be shown to affected users.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#deletemycommands
|
||||
|
||||
:param scope: A JSON-serialized object, describing scope of
|
||||
users for which the commands are relevant. Defaults to
|
||||
BotCommandScopeDefault.
|
||||
:type scope: :obj: `typing.Optional[types.BotCommandScope]`
|
||||
|
||||
:param language_code: A two-letter ISO 639-1 language code. If
|
||||
empty, commands will be applied to all users from the given
|
||||
scope, for whose language there are no dedicated commands
|
||||
:type language_code: :obj: `typing.Optional[base.String]`
|
||||
|
||||
:return: Returns True on success.
|
||||
:rtype: :obj:`base.Boolean`
|
||||
"""
|
||||
scope = prepare_arg(scope)
|
||||
payload = generate_payload(**locals())
|
||||
|
||||
return await self.request(api.Methods.DELETE_MY_COMMANDS, payload)
|
||||
|
||||
async def get_my_commands(self,
|
||||
scope: typing.Optional[types.BotCommandScope] = None,
|
||||
language_code: typing.Optional[base.String] = None,
|
||||
) -> typing.List[types.BotCommand]:
|
||||
"""
|
||||
Use this method to get the current list of the bot's commands
|
||||
for the given scope and user language. Returns Array of
|
||||
BotCommand on success. If commands aren't set, an empty list is
|
||||
returned.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#getmycommands
|
||||
:return: Returns Array of BotCommand on success.
|
||||
|
||||
:param scope: A JSON-serialized object, describing scope of
|
||||
users for which the commands are relevant. Defaults to
|
||||
BotCommandScopeDefault.
|
||||
:type scope: :obj: `typing.Optional[types.BotCommandScope]`
|
||||
|
||||
:param language_code: A two-letter ISO 639-1 language code. If
|
||||
empty, commands will be applied to all users from the given
|
||||
scope, for whose language there are no dedicated commands
|
||||
:type language_code: :obj: `typing.Optional[base.String]`
|
||||
|
||||
:return: Returns Array of BotCommand on success or empty list.
|
||||
:rtype: :obj:`typing.List[types.BotCommand]`
|
||||
"""
|
||||
scope = prepare_arg(scope)
|
||||
payload = generate_payload(**locals())
|
||||
|
||||
result = await self.request(api.Methods.GET_MY_COMMANDS, payload)
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ class MemoryStorage(BaseStorage):
|
|||
data: typing.Dict = None):
|
||||
chat, user = self.resolve_address(chat=chat, user=user)
|
||||
self.data[chat][user]['data'] = copy.deepcopy(data)
|
||||
self._cleanup(chat, user)
|
||||
|
||||
async def reset_state(self, *,
|
||||
chat: typing.Union[str, int, None] = None,
|
||||
|
|
@ -74,6 +75,7 @@ class MemoryStorage(BaseStorage):
|
|||
await self.set_state(chat=chat, user=user, state=None)
|
||||
if with_data:
|
||||
await self.set_data(chat=chat, user=user, data={})
|
||||
self._cleanup(chat, user)
|
||||
|
||||
def has_bucket(self):
|
||||
return True
|
||||
|
|
@ -91,6 +93,7 @@ class MemoryStorage(BaseStorage):
|
|||
bucket: typing.Dict = None):
|
||||
chat, user = self.resolve_address(chat=chat, user=user)
|
||||
self.data[chat][user]['bucket'] = copy.deepcopy(bucket)
|
||||
self._cleanup(chat, user)
|
||||
|
||||
async def update_bucket(self, *,
|
||||
chat: typing.Union[str, int, None] = None,
|
||||
|
|
@ -100,3 +103,9 @@ class MemoryStorage(BaseStorage):
|
|||
bucket = {}
|
||||
chat, user = self.resolve_address(chat=chat, user=user)
|
||||
self.data[chat][user]['bucket'].update(bucket, **kwargs)
|
||||
|
||||
def _cleanup(self, chat, user):
|
||||
if self.data[chat][user] == {'state': None, 'data': {}, 'bucket': {}}:
|
||||
del self.data[chat][user]
|
||||
if not self.data[chat]:
|
||||
del self.data[chat]
|
||||
|
|
|
|||
|
|
@ -142,9 +142,11 @@ class MongoStorage(BaseStorage):
|
|||
data: Dict = None):
|
||||
chat, user = self.check_address(chat=chat, user=user)
|
||||
db = await self.get_db()
|
||||
|
||||
await db[DATA].update_one(filter={'chat': chat, 'user': user},
|
||||
update={'$set': {'data': data}}, upsert=True)
|
||||
if not data:
|
||||
await db[DATA].delete_one(filter={'chat': chat, 'user': user})
|
||||
else:
|
||||
await db[DATA].update_one(filter={'chat': chat, 'user': user},
|
||||
update={'$set': {'data': data}}, upsert=True)
|
||||
|
||||
async def get_data(self, *, chat: Union[str, int, None] = None, user: Union[str, int, None] = None,
|
||||
default: Optional[dict] = None) -> Dict:
|
||||
|
|
|
|||
|
|
@ -110,10 +110,12 @@ class RedisStorage(BaseStorage):
|
|||
chat, user = self.check_address(chat=chat, user=user)
|
||||
addr = f"fsm:{chat}:{user}"
|
||||
|
||||
record = {'state': state, 'data': data, 'bucket': bucket}
|
||||
|
||||
conn = await self.redis()
|
||||
await conn.execute('SET', addr, json.dumps(record))
|
||||
if state is None and data == bucket == {}:
|
||||
await conn.execute('DEL', addr)
|
||||
else:
|
||||
record = {'state': state, 'data': data, 'bucket': bucket}
|
||||
await conn.execute('SET', addr, json.dumps(record))
|
||||
|
||||
async def get_state(self, *, chat: typing.Union[str, int, None] = None, user: typing.Union[str, int, None] = None,
|
||||
default: typing.Optional[str] = None) -> typing.Optional[str]:
|
||||
|
|
@ -222,7 +224,7 @@ class RedisStorage2(BaseStorage):
|
|||
await dp.storage.wait_closed()
|
||||
|
||||
"""
|
||||
def __init__(self, host: str = 'localhost', port=6379, db=None, password=None,
|
||||
def __init__(self, host: str = 'localhost', port=6379, db=None, password=None,
|
||||
ssl=None, pool_size=10, loop=None, prefix='fsm',
|
||||
state_ttl: int = 0,
|
||||
data_ttl: int = 0,
|
||||
|
|
@ -304,7 +306,10 @@ class RedisStorage2(BaseStorage):
|
|||
chat, user = self.check_address(chat=chat, user=user)
|
||||
key = self.generate_key(chat, user, STATE_DATA_KEY)
|
||||
redis = await self.redis()
|
||||
await redis.set(key, json.dumps(data), expire=self._data_ttl)
|
||||
if data:
|
||||
await redis.set(key, json.dumps(data), expire=self._data_ttl)
|
||||
else:
|
||||
await redis.delete(key)
|
||||
|
||||
async def update_data(self, *, chat: typing.Union[str, int, None] = None, user: typing.Union[str, int, None] = None,
|
||||
data: typing.Dict = None, **kwargs):
|
||||
|
|
@ -332,7 +337,10 @@ class RedisStorage2(BaseStorage):
|
|||
chat, user = self.check_address(chat=chat, user=user)
|
||||
key = self.generate_key(chat, user, STATE_BUCKET_KEY)
|
||||
redis = await self.redis()
|
||||
await redis.set(key, json.dumps(bucket), expire=self._bucket_ttl)
|
||||
if bucket:
|
||||
await redis.set(key, json.dumps(bucket), expire=self._bucket_ttl)
|
||||
else:
|
||||
await redis.delete(key)
|
||||
|
||||
async def update_bucket(self, *, chat: typing.Union[str, int, None] = None,
|
||||
user: typing.Union[str, int, None] = None,
|
||||
|
|
|
|||
|
|
@ -4,6 +4,10 @@ from .animation import Animation
|
|||
from .audio import Audio
|
||||
from .auth_widget_data import AuthWidgetData
|
||||
from .bot_command import BotCommand
|
||||
from .bot_command_scope import BotCommandScope, BotCommandScopeAllChatAdministrators, \
|
||||
BotCommandScopeAllGroupChats, BotCommandScopeAllPrivateChats, BotCommandScopeChat, \
|
||||
BotCommandScopeChatAdministrators, BotCommandScopeChatMember, \
|
||||
BotCommandScopeDefault, BotCommandScopeType
|
||||
from .callback_game import CallbackGame
|
||||
from .callback_query import CallbackQuery
|
||||
from .chat import Chat, ChatActions, ChatType
|
||||
|
|
@ -82,6 +86,15 @@ __all__ = (
|
|||
'Audio',
|
||||
'AuthWidgetData',
|
||||
'BotCommand',
|
||||
'BotCommandScope',
|
||||
'BotCommandScopeAllChatAdministrators',
|
||||
'BotCommandScopeAllGroupChats',
|
||||
'BotCommandScopeAllPrivateChats',
|
||||
'BotCommandScopeChat',
|
||||
'BotCommandScopeChatAdministrators',
|
||||
'BotCommandScopeChatMember',
|
||||
'BotCommandScopeDefault',
|
||||
'BotCommandScopeType',
|
||||
'CallbackGame',
|
||||
'CallbackQuery',
|
||||
'Chat',
|
||||
|
|
|
|||
121
aiogram/types/bot_command_scope.py
Normal file
121
aiogram/types/bot_command_scope.py
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
import typing
|
||||
|
||||
from . import base, fields
|
||||
from ..utils import helper
|
||||
|
||||
|
||||
class BotCommandScopeType(helper.Helper):
|
||||
mode = helper.HelperMode.lowercase
|
||||
|
||||
DEFAULT = helper.Item() # default
|
||||
ALL_PRIVATE_CHATS = helper.Item() # all_private_chats
|
||||
ALL_GROUP_CHATS = helper.Item() # all_group_chats
|
||||
ALL_CHAT_ADMINISTRATORS = helper.Item() # all_chat_administrators
|
||||
CHAT = helper.Item() # chat
|
||||
CHAT_ADMINISTRATORS = helper.Item() # chat_administrators
|
||||
CHAT_MEMBER = helper.Item() # chat_member
|
||||
|
||||
|
||||
class BotCommandScope(base.TelegramObject):
|
||||
"""
|
||||
This object represents the scope to which bot commands are applied.
|
||||
Currently, the following 7 scopes are supported:
|
||||
BotCommandScopeDefault
|
||||
BotCommandScopeAllPrivateChats
|
||||
BotCommandScopeAllGroupChats
|
||||
BotCommandScopeAllChatAdministrators
|
||||
BotCommandScopeChat
|
||||
BotCommandScopeChatAdministrators
|
||||
BotCommandScopeChatMember
|
||||
|
||||
https://core.telegram.org/bots/api#botcommandscope
|
||||
"""
|
||||
type: base.String = fields.Field()
|
||||
|
||||
@classmethod
|
||||
def from_type(cls, type: str, **kwargs: typing.Any):
|
||||
if type == BotCommandScopeType.DEFAULT:
|
||||
return BotCommandScopeDefault(type=type, **kwargs)
|
||||
if type == BotCommandScopeType.ALL_PRIVATE_CHATS:
|
||||
return BotCommandScopeAllPrivateChats(type=type, **kwargs)
|
||||
if type == BotCommandScopeType.ALL_GROUP_CHATS:
|
||||
return BotCommandScopeAllGroupChats(type=type, **kwargs)
|
||||
if type == BotCommandScopeType.ALL_CHAT_ADMINISTRATORS:
|
||||
return BotCommandScopeAllChatAdministrators(type=type, **kwargs)
|
||||
if type == BotCommandScopeType.CHAT:
|
||||
return BotCommandScopeChat(type=type, **kwargs)
|
||||
if type == BotCommandScopeType.CHAT_ADMINISTRATORS:
|
||||
return BotCommandScopeChatAdministrators(type=type, **kwargs)
|
||||
if type == BotCommandScopeType.CHAT_MEMBER:
|
||||
return BotCommandScopeChatMember(type=type, **kwargs)
|
||||
raise ValueError(f"Unknown BotCommandScope type {type!r}")
|
||||
|
||||
|
||||
class BotCommandScopeDefault(BotCommandScope):
|
||||
"""
|
||||
Represents the default scope of bot commands.
|
||||
Default commands are used if no commands with a narrower scope are
|
||||
specified for the user.
|
||||
"""
|
||||
type = fields.Field(default=BotCommandScopeType.DEFAULT)
|
||||
|
||||
|
||||
class BotCommandScopeAllPrivateChats(BotCommandScope):
|
||||
"""
|
||||
Represents the scope of bot commands, covering all private chats.
|
||||
"""
|
||||
type = fields.Field(default=BotCommandScopeType.ALL_PRIVATE_CHATS)
|
||||
|
||||
|
||||
class BotCommandScopeAllGroupChats(BotCommandScope):
|
||||
"""
|
||||
Represents the scope of bot commands, covering all group and
|
||||
supergroup chats.
|
||||
"""
|
||||
type = fields.Field(default=BotCommandScopeType.ALL_GROUP_CHATS)
|
||||
|
||||
|
||||
class BotCommandScopeAllChatAdministrators(BotCommandScope):
|
||||
"""
|
||||
Represents the scope of bot commands, covering all group and
|
||||
supergroup chat administrators.
|
||||
"""
|
||||
type = fields.Field(default=BotCommandScopeType.ALL_CHAT_ADMINISTRATORS)
|
||||
|
||||
|
||||
class BotCommandScopeChat(BotCommandScope):
|
||||
"""
|
||||
Represents the scope of bot commands, covering a specific chat.
|
||||
"""
|
||||
type = fields.Field(default=BotCommandScopeType.CHAT)
|
||||
chat_id: typing.Union[base.String, base.Integer] = fields.Field()
|
||||
|
||||
def __init__(self, chat_id: typing.Union[base.String, base.Integer], **kwargs):
|
||||
super().__init__(chat_id=chat_id, **kwargs)
|
||||
|
||||
|
||||
class BotCommandScopeChatAdministrators(BotCommandScopeChat):
|
||||
"""
|
||||
Represents the scope of bot commands, covering all administrators
|
||||
of a specific group or supergroup chat.
|
||||
"""
|
||||
type = fields.Field(default=BotCommandScopeType.CHAT_ADMINISTRATORS)
|
||||
chat_id: typing.Union[base.String, base.Integer] = fields.Field()
|
||||
|
||||
|
||||
class BotCommandScopeChatMember(BotCommandScopeChat):
|
||||
"""
|
||||
Represents the scope of bot commands, covering a specific member of
|
||||
a group or supergroup chat.
|
||||
"""
|
||||
type = fields.Field(default=BotCommandScopeType.CHAT_MEMBER)
|
||||
chat_id: typing.Union[base.String, base.Integer] = fields.Field()
|
||||
user_id: base.Integer = fields.Field()
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
chat_id: typing.Union[base.String, base.Integer],
|
||||
user_id: base.Integer,
|
||||
**kwargs,
|
||||
):
|
||||
super().__init__(chat_id=chat_id, user_id=user_id, **kwargs)
|
||||
|
|
@ -301,7 +301,7 @@ class Chat(base.TelegramObject):
|
|||
can_send_other_messages=can_send_other_messages,
|
||||
can_add_web_page_previews=can_add_web_page_previews)
|
||||
|
||||
async def promote(self,
|
||||
async def promote(self,
|
||||
user_id: base.Integer,
|
||||
is_anonymous: typing.Optional[base.Boolean] = None,
|
||||
can_change_info: typing.Optional[base.Boolean] = None,
|
||||
|
|
@ -321,36 +321,36 @@ class Chat(base.TelegramObject):
|
|||
|
||||
:param user_id: Unique identifier of the target user
|
||||
:type user_id: :obj:`base.Integer`
|
||||
|
||||
|
||||
: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_change_info: Pass True, if the administrator can change chat title, photo and other settings
|
||||
:type can_change_info: :obj:`typing.Optional[base.Boolean]`
|
||||
|
||||
|
||||
:param can_post_messages: Pass True, if the administrator can create channel posts, channels only
|
||||
:type can_post_messages: :obj:`typing.Optional[base.Boolean]`
|
||||
|
||||
|
||||
:param can_edit_messages: Pass True, if the administrator can edit messages of other users, channels only
|
||||
:type can_edit_messages: :obj:`typing.Optional[base.Boolean]`
|
||||
|
||||
|
||||
: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_invite_users: Pass True, if the administrator can invite new users to the chat
|
||||
:type can_invite_users: :obj:`typing.Optional[base.Boolean]`
|
||||
|
||||
|
||||
:param can_restrict_members: Pass True, if the administrator can restrict, ban or unban chat members
|
||||
:type can_restrict_members: :obj:`typing.Optional[base.Boolean]`
|
||||
|
||||
|
||||
:param can_pin_messages: Pass True, if the administrator can pin messages, supergroups only
|
||||
:type can_pin_messages: :obj:`typing.Optional[base.Boolean]`
|
||||
|
||||
|
||||
:param can_promote_members: Pass True, if the administrator can add new administrators
|
||||
with a subset of his own privileges or demote administrators that he has promoted,
|
||||
directly or indirectly (promoted by administrators that were appointed by him)
|
||||
:type can_promote_members: :obj:`typing.Optional[base.Boolean]`
|
||||
|
||||
|
||||
:return: Returns True on success.
|
||||
:rtype: :obj:`base.Boolean`
|
||||
"""
|
||||
|
|
@ -484,16 +484,20 @@ class Chat(base.TelegramObject):
|
|||
"""
|
||||
return await self.bot.get_chat_administrators(self.id)
|
||||
|
||||
async def get_members_count(self) -> base.Integer:
|
||||
async def get_member_count(self) -> base.Integer:
|
||||
"""
|
||||
Use this method to get the number of members in a chat.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#getchatmemberscount
|
||||
Source: https://core.telegram.org/bots/api#getchatmembercount
|
||||
|
||||
:return: Returns Int on success.
|
||||
:rtype: :obj:`base.Integer`
|
||||
"""
|
||||
return await self.bot.get_chat_members_count(self.id)
|
||||
return await self.bot.get_chat_member_count(self.id)
|
||||
|
||||
async def get_members_count(self) -> base.Integer:
|
||||
"""Renamed to get_member_count."""
|
||||
return await self.get_member_count(self.id)
|
||||
|
||||
async def get_member(self, user_id: base.Integer) -> ChatMember:
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -1,53 +1,11 @@
|
|||
import datetime
|
||||
from typing import Optional
|
||||
|
||||
from . import base
|
||||
from . import fields
|
||||
from . import base, fields
|
||||
from .user import User
|
||||
from ..utils import helper
|
||||
|
||||
|
||||
class ChatMember(base.TelegramObject):
|
||||
"""
|
||||
This object contains information about one member of a chat.
|
||||
|
||||
https://core.telegram.org/bots/api#chatmember
|
||||
"""
|
||||
user: User = fields.Field(base=User)
|
||||
status: base.String = fields.Field()
|
||||
custom_title: base.String = fields.Field()
|
||||
is_anonymous: base.Boolean = fields.Field()
|
||||
can_be_edited: 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_manage_voice_chats: base.Boolean = fields.Field()
|
||||
can_restrict_members: 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)
|
||||
|
||||
def is_chat_admin(self) -> bool:
|
||||
return ChatMemberStatus.is_chat_admin(self.status)
|
||||
|
||||
def is_chat_member(self) -> bool:
|
||||
return ChatMemberStatus.is_chat_member(self.status)
|
||||
|
||||
def __int__(self) -> int:
|
||||
return self.user.id
|
||||
|
||||
|
||||
class ChatMemberStatus(helper.Helper):
|
||||
"""
|
||||
Chat member status
|
||||
|
|
@ -55,11 +13,13 @@ class ChatMemberStatus(helper.Helper):
|
|||
mode = helper.HelperMode.lowercase
|
||||
|
||||
CREATOR = helper.Item() # creator
|
||||
OWNER = CREATOR # creator
|
||||
ADMINISTRATOR = helper.Item() # administrator
|
||||
MEMBER = helper.Item() # member
|
||||
RESTRICTED = helper.Item() # restricted
|
||||
LEFT = helper.Item() # left
|
||||
KICKED = helper.Item() # kicked
|
||||
BANNED = KICKED # kicked
|
||||
|
||||
@classmethod
|
||||
def is_chat_creator(cls, role: str) -> bool:
|
||||
|
|
@ -72,3 +32,141 @@ class ChatMemberStatus(helper.Helper):
|
|||
@classmethod
|
||||
def is_chat_member(cls, role: str) -> bool:
|
||||
return role in (cls.MEMBER, cls.ADMINISTRATOR, cls.CREATOR, cls.RESTRICTED)
|
||||
|
||||
@classmethod
|
||||
def get_class_by_status(cls, status: str) -> Optional["ChatMember"]:
|
||||
return {
|
||||
cls.OWNER: ChatMemberOwner,
|
||||
cls.ADMINISTRATOR: ChatMemberAdministrator,
|
||||
cls.MEMBER: ChatMemberMember,
|
||||
cls.RESTRICTED: ChatMemberRestricted,
|
||||
cls.LEFT: ChatMemberLeft,
|
||||
cls.BANNED: ChatMemberBanned,
|
||||
}.get(status)
|
||||
|
||||
|
||||
class ChatMember(base.TelegramObject):
|
||||
"""
|
||||
This object contains information about one member of a chat.
|
||||
Currently, the following 6 types of chat members are supported:
|
||||
ChatMemberOwner
|
||||
ChatMemberAdministrator
|
||||
ChatMemberMember
|
||||
ChatMemberRestricted
|
||||
ChatMemberLeft
|
||||
ChatMemberBanned
|
||||
|
||||
https://core.telegram.org/bots/api#chatmember
|
||||
"""
|
||||
status: base.String = fields.Field()
|
||||
user: User = fields.Field(base=User)
|
||||
|
||||
def __int__(self) -> int:
|
||||
return self.user.id
|
||||
|
||||
@classmethod
|
||||
def resolve(cls, **kwargs) -> "ChatMember":
|
||||
status = kwargs.get("status")
|
||||
mapping = {
|
||||
ChatMemberStatus.OWNER: ChatMemberOwner,
|
||||
ChatMemberStatus.ADMINISTRATOR: ChatMemberAdministrator,
|
||||
ChatMemberStatus.MEMBER: ChatMemberMember,
|
||||
ChatMemberStatus.RESTRICTED: ChatMemberRestricted,
|
||||
ChatMemberStatus.LEFT: ChatMemberLeft,
|
||||
ChatMemberStatus.BANNED: ChatMemberBanned,
|
||||
}
|
||||
class_ = mapping.get(status)
|
||||
if class_ is None:
|
||||
raise ValueError(f"Can't find `ChatMember` class for status `{status}`")
|
||||
|
||||
return class_(**kwargs)
|
||||
|
||||
|
||||
class ChatMemberOwner(ChatMember):
|
||||
"""
|
||||
Represents a chat member that owns the chat and has all
|
||||
administrator privileges.
|
||||
https://core.telegram.org/bots/api#chatmemberowner
|
||||
"""
|
||||
status: base.String = fields.Field(default=ChatMemberStatus.OWNER)
|
||||
user: User = fields.Field(base=User)
|
||||
custom_title: base.String = fields.Field()
|
||||
is_anonymous: base.Boolean = fields.Field()
|
||||
|
||||
|
||||
class ChatMemberAdministrator(ChatMember):
|
||||
"""
|
||||
Represents a chat member that has some additional privileges.
|
||||
|
||||
https://core.telegram.org/bots/api#chatmemberadministrator
|
||||
"""
|
||||
status: base.String = fields.Field(default=ChatMemberStatus.ADMINISTRATOR)
|
||||
user: User = fields.Field(base=User)
|
||||
can_be_edited: base.Boolean = fields.Field()
|
||||
custom_title: base.String = fields.Field()
|
||||
is_anonymous: 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_manage_voice_chats: base.Boolean = fields.Field()
|
||||
can_restrict_members: 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()
|
||||
|
||||
|
||||
class ChatMemberMember(ChatMember):
|
||||
"""
|
||||
Represents a chat member that has no additional privileges or
|
||||
restrictions.
|
||||
|
||||
https://core.telegram.org/bots/api#chatmembermember
|
||||
"""
|
||||
status: base.String = fields.Field(default=ChatMemberStatus.MEMBER)
|
||||
user: User = fields.Field(base=User)
|
||||
|
||||
|
||||
class ChatMemberRestricted(ChatMember):
|
||||
"""
|
||||
Represents a chat member that is under certain restrictions in the
|
||||
chat. Supergroups only.
|
||||
|
||||
https://core.telegram.org/bots/api#chatmemberrestricted
|
||||
"""
|
||||
status: base.String = fields.Field(default=ChatMemberStatus.RESTRICTED)
|
||||
user: User = fields.Field(base=User)
|
||||
is_member: 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()
|
||||
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()
|
||||
|
||||
|
||||
class ChatMemberLeft(ChatMember):
|
||||
"""
|
||||
Represents a chat member that isn't currently a member of the chat,
|
||||
but may join it themselves.
|
||||
|
||||
https://core.telegram.org/bots/api#chatmemberleft
|
||||
"""
|
||||
status: base.String = fields.Field(default=ChatMemberStatus.LEFT)
|
||||
user: User = fields.Field(base=User)
|
||||
|
||||
|
||||
class ChatMemberBanned(ChatMember):
|
||||
"""
|
||||
Represents a chat member that was banned in the chat and can't
|
||||
return to the chat or view chat messages.
|
||||
|
||||
https://core.telegram.org/bots/api#chatmemberbanned
|
||||
"""
|
||||
status: base.String = fields.Field(default=ChatMemberStatus.BANNED)
|
||||
user: User = fields.Field(base=User)
|
||||
until_date: datetime.datetime = fields.DateTimeField()
|
||||
|
|
|
|||
|
|
@ -6,31 +6,28 @@ from . import fields
|
|||
|
||||
class ForceReply(base.TelegramObject):
|
||||
"""
|
||||
Upon receiving a message with this object,
|
||||
Telegram clients will display a reply interface to the user
|
||||
(act as if the user has selected the bot‘s message and tapped ’Reply').
|
||||
This can be extremely useful if you want to create user-friendly step-by-step
|
||||
Upon receiving a message with this object, Telegram clients will
|
||||
display a reply interface to the user (act as if the user has
|
||||
selected the bot's message and tapped 'Reply'). This can be
|
||||
extremely useful if you want to create user-friendly step-by-step
|
||||
interfaces without having to sacrifice privacy mode.
|
||||
|
||||
Example: A poll bot for groups runs in privacy mode
|
||||
(only receives commands, replies to its messages and mentions).
|
||||
There could be two ways to create a new poll
|
||||
|
||||
The last option is definitely more attractive.
|
||||
And if you use ForceReply in your bot‘s questions, it will receive the user’s answers even
|
||||
if it only receives replies, commands and mentions — without any extra work for the user.
|
||||
|
||||
https://core.telegram.org/bots/api#forcereply
|
||||
"""
|
||||
force_reply: base.Boolean = fields.Field(default=True)
|
||||
input_field_placeholder: base.String = fields.Field()
|
||||
selective: base.Boolean = fields.Field()
|
||||
|
||||
@classmethod
|
||||
def create(cls, selective: typing.Optional[base.Boolean] = None):
|
||||
def create(cls,
|
||||
input_field_placeholder: typing.Optional[base.String] = None,
|
||||
selective: typing.Optional[base.Boolean] = None,
|
||||
) -> 'ForceReply':
|
||||
"""
|
||||
Create new force reply
|
||||
|
||||
:param selective:
|
||||
:param input_field_placeholder:
|
||||
:return:
|
||||
"""
|
||||
return cls(selective=selective)
|
||||
return cls(selective=selective, input_field_placeholder=input_field_placeholder)
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ class InputMedia(base.TelegramObject):
|
|||
thumb: typing.Union[base.InputFile, base.String] = fields.Field(alias='thumb', on_change='_thumb_changed')
|
||||
caption: base.String = fields.Field()
|
||||
parse_mode: base.String = fields.Field()
|
||||
caption_entities: typing.List[MessageEntity] = fields.ListField(base=MessageEntity)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self._thumb_file = None
|
||||
|
|
|
|||
|
|
@ -194,7 +194,8 @@ class Message(base.TelegramObject):
|
|||
|
||||
:return: bool
|
||||
"""
|
||||
return self.text and self.text.startswith("/")
|
||||
text = self.text or self.caption
|
||||
return text and text.startswith("/")
|
||||
|
||||
def get_full_command(self) -> typing.Optional[typing.Tuple[str, str]]:
|
||||
"""
|
||||
|
|
@ -203,8 +204,9 @@ class Message(base.TelegramObject):
|
|||
:return: tuple of (command, args)
|
||||
"""
|
||||
if self.is_command():
|
||||
command, *args = self.text.split(maxsplit=1)
|
||||
args = args[-1] if args else ""
|
||||
text = self.text or self.caption
|
||||
command, *args = text.split(maxsplit=1)
|
||||
args = args[0] if args else ""
|
||||
return command, args
|
||||
|
||||
def get_command(self, pure=False) -> typing.Optional[str]:
|
||||
|
|
@ -271,7 +273,7 @@ class Message(base.TelegramObject):
|
|||
|
||||
:return: str
|
||||
"""
|
||||
|
||||
|
||||
if self.chat.type == ChatType.PRIVATE:
|
||||
raise TypeError("Invalid chat type!")
|
||||
url = "https://t.me/"
|
||||
|
|
@ -1420,7 +1422,7 @@ 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,
|
||||
|
|
@ -2931,7 +2933,7 @@ class Message(base.TelegramObject):
|
|||
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
|
||||
allows_multiple_answers=self.poll.allows_multiple_answers,
|
||||
**kwargs,
|
||||
)
|
||||
elif self.dice:
|
||||
|
|
|
|||
|
|
@ -18,23 +18,32 @@ class KeyboardButtonPollType(base.TelegramObject):
|
|||
|
||||
class ReplyKeyboardMarkup(base.TelegramObject):
|
||||
"""
|
||||
This object represents a custom keyboard with reply options (see Introduction to bots for details and examples).
|
||||
This object represents a custom keyboard with reply options
|
||||
(see https://core.telegram.org/bots#keyboards to bots for details
|
||||
and examples).
|
||||
|
||||
https://core.telegram.org/bots/api#replykeyboardmarkup
|
||||
"""
|
||||
keyboard: 'typing.List[typing.List[KeyboardButton]]' = fields.ListOfLists(base='KeyboardButton', default=[])
|
||||
resize_keyboard: base.Boolean = fields.Field()
|
||||
one_time_keyboard: base.Boolean = fields.Field()
|
||||
input_field_placeholder: base.String = fields.Field()
|
||||
selective: base.Boolean = fields.Field()
|
||||
|
||||
def __init__(self, keyboard: 'typing.List[typing.List[KeyboardButton]]' = None,
|
||||
resize_keyboard: base.Boolean = None,
|
||||
one_time_keyboard: base.Boolean = None,
|
||||
input_field_placeholder: base.String = None,
|
||||
selective: base.Boolean = None,
|
||||
row_width: base.Integer = 3):
|
||||
super(ReplyKeyboardMarkup, self).__init__(keyboard=keyboard, resize_keyboard=resize_keyboard,
|
||||
one_time_keyboard=one_time_keyboard, selective=selective,
|
||||
conf={'row_width': row_width})
|
||||
super().__init__(
|
||||
keyboard=keyboard,
|
||||
resize_keyboard=resize_keyboard,
|
||||
one_time_keyboard=one_time_keyboard,
|
||||
input_field_placeholder=input_field_placeholder,
|
||||
selective=selective,
|
||||
conf={'row_width': row_width},
|
||||
)
|
||||
|
||||
@property
|
||||
def row_width(self):
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from functools import lru_cache
|
||||
|
||||
from . import base
|
||||
from . import fields
|
||||
from .callback_query import CallbackQuery
|
||||
|
|
@ -76,7 +74,5 @@ class AllowedUpdates(helper.Helper):
|
|||
)
|
||||
|
||||
@classmethod
|
||||
@lru_cache(1)
|
||||
def default(cls):
|
||||
excluded = cls.CHAT_MEMBER + cls.MY_CHAT_MEMBER
|
||||
return list(filter(lambda item: item not in excluded, cls.all()))
|
||||
return []
|
||||
|
|
|
|||
|
|
@ -368,7 +368,7 @@ class WrongFileIdentifier(BadRequest):
|
|||
|
||||
|
||||
class GroupDeactivated(BadRequest):
|
||||
match = 'group is deactivated'
|
||||
match = 'Group chat was deactivated'
|
||||
|
||||
|
||||
class PhotoAsInputFileRequired(BadRequest):
|
||||
|
|
|
|||
|
|
@ -185,7 +185,7 @@ class MarkdownDecoration(TextDecoration):
|
|||
return f"`{value}`"
|
||||
|
||||
def pre(self, value: str) -> str:
|
||||
return f"```{value}```"
|
||||
return f"```\n{value}\n```"
|
||||
|
||||
def pre_language(self, value: str, language: str) -> str:
|
||||
return f"```{language}\n{value}\n```"
|
||||
|
|
|
|||
|
|
@ -16,3 +16,4 @@ aiohttp-socks>=0.3.4
|
|||
rethinkdb>=2.4.1
|
||||
coverage==4.5.3
|
||||
motor>=2.2.0
|
||||
pytest-lazy-fixture==0.6.*
|
||||
|
|
|
|||
|
|
@ -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.2-blue.svg?style=flat-square&logo=telegram
|
||||
.. image:: https://img.shields.io/badge/Telegram%20Bot%20API-5.3-blue.svg?style=flat-square&logo=telegram
|
||||
:target: https://core.telegram.org/bots/api
|
||||
:alt: Telegram Bot API
|
||||
|
||||
|
|
|
|||
|
|
@ -1,33 +0,0 @@
|
|||
import pytest
|
||||
|
||||
from aiogram.contrib.fsm_storage.redis import RedisStorage2
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
async def store(redis_options):
|
||||
s = RedisStorage2(**redis_options)
|
||||
try:
|
||||
yield s
|
||||
finally:
|
||||
conn = await s.redis()
|
||||
await conn.flushdb()
|
||||
await s.close()
|
||||
await s.wait_closed()
|
||||
|
||||
|
||||
@pytest.mark.redis
|
||||
class TestRedisStorage2:
|
||||
@pytest.mark.asyncio
|
||||
async def test_set_get(self, store):
|
||||
assert await store.get_data(chat='1234') == {}
|
||||
await store.set_data(chat='1234', data={'foo': 'bar'})
|
||||
assert await store.get_data(chat='1234') == {'foo': 'bar'}
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_close_and_open_connection(self, store):
|
||||
await store.set_data(chat='1234', data={'foo': 'bar'})
|
||||
assert await store.get_data(chat='1234') == {'foo': 'bar'}
|
||||
pool_id = id(store._redis)
|
||||
await store.close()
|
||||
assert await store.get_data(chat='1234') == {'foo': 'bar'} # new pool was opened at this point
|
||||
assert id(store._redis) != pool_id
|
||||
79
tests/contrib/fsm_storage/test_storage.py
Normal file
79
tests/contrib/fsm_storage/test_storage.py
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
import pytest
|
||||
|
||||
from aiogram.contrib.fsm_storage.memory import MemoryStorage
|
||||
from aiogram.contrib.fsm_storage.redis import RedisStorage2, RedisStorage
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
@pytest.mark.redis
|
||||
async def redis_store(redis_options):
|
||||
s = RedisStorage(**redis_options)
|
||||
try:
|
||||
yield s
|
||||
finally:
|
||||
conn = await s.redis()
|
||||
await conn.execute('FLUSHDB')
|
||||
await s.close()
|
||||
await s.wait_closed()
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
@pytest.mark.redis
|
||||
async def redis_store2(redis_options):
|
||||
s = RedisStorage2(**redis_options)
|
||||
try:
|
||||
yield s
|
||||
finally:
|
||||
conn = await s.redis()
|
||||
await conn.flushdb()
|
||||
await s.close()
|
||||
await s.wait_closed()
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
async def memory_store():
|
||||
yield MemoryStorage()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"store", [
|
||||
pytest.lazy_fixture('redis_store'),
|
||||
pytest.lazy_fixture('redis_store2'),
|
||||
pytest.lazy_fixture('memory_store'),
|
||||
]
|
||||
)
|
||||
class TestStorage:
|
||||
@pytest.mark.asyncio
|
||||
async def test_set_get(self, store):
|
||||
assert await store.get_data(chat='1234') == {}
|
||||
await store.set_data(chat='1234', data={'foo': 'bar'})
|
||||
assert await store.get_data(chat='1234') == {'foo': 'bar'}
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_reset(self, store):
|
||||
await store.set_data(chat='1234', data={'foo': 'bar'})
|
||||
await store.reset_data(chat='1234')
|
||||
assert await store.get_data(chat='1234') == {}
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_reset_empty(self, store):
|
||||
await store.reset_data(chat='1234')
|
||||
assert await store.get_data(chat='1234') == {}
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"store", [
|
||||
pytest.lazy_fixture('redis_store'),
|
||||
pytest.lazy_fixture('redis_store2'),
|
||||
]
|
||||
)
|
||||
class TestRedisStorage2:
|
||||
@pytest.mark.asyncio
|
||||
async def test_close_and_open_connection(self, store):
|
||||
await store.set_data(chat='1234', data={'foo': 'bar'})
|
||||
assert await store.get_data(chat='1234') == {'foo': 'bar'}
|
||||
pool_id = id(store._redis)
|
||||
await store.close()
|
||||
assert await store.get_data(chat='1234') == {
|
||||
'foo': 'bar'} # new pool was opened at this point
|
||||
assert id(store._redis) != pool_id
|
||||
|
|
@ -427,7 +427,7 @@ async def test_get_chat_administrators(bot: Bot):
|
|||
""" getChatAdministrators method test """
|
||||
from .types.dataset import CHAT, CHAT_MEMBER
|
||||
chat = types.Chat(**CHAT)
|
||||
member = types.ChatMember(**CHAT_MEMBER)
|
||||
member = types.ChatMember.resolve(**CHAT_MEMBER)
|
||||
|
||||
async with FakeTelegram(message_data=[CHAT_MEMBER, CHAT_MEMBER]):
|
||||
result = await bot.get_chat_administrators(chat_id=chat.id)
|
||||
|
|
@ -435,14 +435,14 @@ async def test_get_chat_administrators(bot: Bot):
|
|||
assert len(result) == 2
|
||||
|
||||
|
||||
async def test_get_chat_members_count(bot: Bot):
|
||||
async def test_get_chat_member_count(bot: Bot):
|
||||
""" getChatMembersCount method test """
|
||||
from .types.dataset import CHAT
|
||||
chat = types.Chat(**CHAT)
|
||||
count = 5
|
||||
|
||||
async with FakeTelegram(message_data=count):
|
||||
result = await bot.get_chat_members_count(chat_id=chat.id)
|
||||
result = await bot.get_chat_member_count(chat_id=chat.id)
|
||||
assert result == count
|
||||
|
||||
|
||||
|
|
@ -450,7 +450,7 @@ async def test_get_chat_member(bot: Bot):
|
|||
""" getChatMember method test """
|
||||
from .types.dataset import CHAT, CHAT_MEMBER
|
||||
chat = types.Chat(**CHAT)
|
||||
member = types.ChatMember(**CHAT_MEMBER)
|
||||
member = types.ChatMember.resolve(**CHAT_MEMBER)
|
||||
|
||||
async with FakeTelegram(message_data=CHAT_MEMBER):
|
||||
result = await bot.get_chat_member(chat_id=chat.id, user_id=member.user.id)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
from aiogram import types
|
||||
from .dataset import CHAT_MEMBER
|
||||
|
||||
chat_member = types.ChatMember(**CHAT_MEMBER)
|
||||
chat_member = types.ChatMember.resolve(**CHAT_MEMBER)
|
||||
|
||||
|
||||
def test_export():
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue