Added full support of Bot API 7.2 (#1444)

* Added base support of Bot API 7.2

* Added base support of Bot API 7.2

* Fixing tests and content types for Telegram Bot API 7.2 update (#1453)

* Fixing tests and content types for Telegram Bot API 7.2

* Adding changelog for 1453 PR

* Fixes + coverage

* Replace `BusinessConnection.date` type

* Reformat code

* Refactor UserContextMiddleware to use EventContext class

This update significantly refactors UserContextMiddleware to leverage a new class, EventContext. Instead of resolving event context as a tuple, it now produces an instance of EventContext. Additional adjustments include supporting a business connection ID for event context identification and facilitating backwards compatibility. Tests and other files were also updated accordingly for these changes.

* Cover FSM key builder (business_connection_id

* Added changelog

---------

Co-authored-by: RoLOQ <roman.fedunn@gmail.com>
This commit is contained in:
Alex Root Junior 2024-04-22 13:48:49 +03:00 committed by GitHub
parent 5f157beb26
commit 057478621b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
147 changed files with 3509 additions and 651 deletions

View file

@ -26,7 +26,7 @@ from aiogram.exceptions import (
TelegramUnauthorizedError,
)
from aiogram.methods import DeleteMessage, GetMe, TelegramMethod
from aiogram.types import UNSET_PARSE_MODE, User, LinkPreviewOptions
from aiogram.types import UNSET_PARSE_MODE, LinkPreviewOptions, User
from aiogram.types.base import UNSET_DISABLE_WEB_PAGE_PREVIEW, UNSET_PROTECT_CONTENT
from tests.mocked_bot import MockedBot

View file

@ -1,3 +1,4 @@
from aiogram.enums import StickerFormat
from aiogram.methods import AddStickerToSet
from aiogram.types import InputSticker
from tests.mocked_bot import MockedBot
@ -10,7 +11,9 @@ class TestAddStickerToSet:
response: bool = await bot.add_sticker_to_set(
user_id=42,
name="test stickers pack",
sticker=InputSticker(sticker="file id", emoji_list=[":)"]),
sticker=InputSticker(
sticker="file id", format=StickerFormat.STATIC, emoji_list=[":)"]
),
)
request = bot.get_request()
assert response == prepare_result.result

View file

@ -13,8 +13,10 @@ class TestCreateNewStickerSet:
name="name",
title="title",
stickers=[
InputSticker(sticker="file id", emoji_list=[":)"]),
InputSticker(sticker=FSInputFile("file.png"), emoji_list=["=("]),
InputSticker(sticker="file id", format=StickerFormat.STATIC, emoji_list=[":)"]),
InputSticker(
sticker=FSInputFile("file.png"), format=StickerFormat.STATIC, emoji_list=["=("]
),
],
sticker_format=StickerFormat.STATIC,
)

View file

@ -0,0 +1,24 @@
from aiogram.methods import GetBusinessConnection
from aiogram.types import BusinessConnection, User
from tests.mocked_bot import MockedBot
class TestGetBusinessConnection:
async def test_bot_method(self, bot: MockedBot):
prepare_result = bot.add_result_for(
GetBusinessConnection,
ok=True,
result=BusinessConnection(
id="test",
user=User(id=42, is_bot=False, first_name="User"),
user_chat_id=42,
date=42,
can_reply=True,
is_enabled=True,
),
)
response: BusinessConnection = await bot.get_business_connection(
business_connection_id="test"
)
request = bot.get_request()
assert response == prepare_result.result

View file

@ -0,0 +1,21 @@
from aiogram.methods import ReplaceStickerInSet
from aiogram.types import InputSticker
from tests.mocked_bot import MockedBot
class TestReplaceStickerInSet:
async def test_bot_method(self, bot: MockedBot):
prepare_result = bot.add_result_for(ReplaceStickerInSet, ok=True, result=True)
response: bool = await bot.replace_sticker_in_set(
user_id=42,
name="test",
old_sticker="test",
sticker=InputSticker(
sticker="test",
format="static",
emoji_list=["test"],
),
)
request = bot.get_request()
assert response == prepare_result.result

View file

@ -1,3 +1,4 @@
from aiogram.enums import StickerFormat
from aiogram.methods import Request, SetStickerSetThumbnail
from tests.mocked_bot import MockedBot
@ -6,6 +7,8 @@ class TestSetStickerSetThumbnail:
async def test_bot_method(self, bot: MockedBot):
prepare_result = bot.add_result_for(SetStickerSetThumbnail, ok=True, result=None)
response: bool = await bot.set_sticker_set_thumbnail(name="test", user_id=42)
response: bool = await bot.set_sticker_set_thumbnail(
name="test", format=StickerFormat.STATIC, user_id=42
)
request = bot.get_request()
assert response == prepare_result.result

View file

@ -71,6 +71,7 @@ from aiogram.types import (
PollOption,
ProximityAlertTriggered,
ReactionTypeCustomEmoji,
SharedUser,
Sticker,
Story,
SuccessfulPayment,
@ -469,7 +470,7 @@ TEST_MESSAGE_USERS_SHARED = Message(
from_user=None,
users_shared=UsersShared(
request_id=0,
user_ids=[1, 2],
users=[SharedUser(user_id=1), SharedUser(user_id=2)],
),
)
TEST_CHAT_SHARED = Message(
@ -485,8 +486,6 @@ TEST_MESSAGE_STORY = Message(
chat=Chat(id=42, type="private"),
from_user=User(id=42, is_bot=False, first_name="Test"),
story=Story(chat=Chat(id=42, type="private"), id=42),
forward_signature="Test",
forward_date=datetime.datetime.now(),
)
TEST_MESSAGE_GIVEAWAY = Message(
@ -527,13 +526,6 @@ TEST_MESSAGE_GIVEAWAY_COMPLETED = Message(
from_user=None,
giveaway_completed=GiveawayCompleted(winner_count=10),
)
TEST_MESSAGE_HAS_MEDIA_SPOILER = Message(
message_id=42,
date=datetime.datetime.now(),
chat=Chat(id=42, type="private"),
from_user=None,
has_media_spoiler=True,
)
TEST_MESSAGE_GENERAL_FORUM_TOPIC_HIDDEN = Message(
message_id=42,
date=datetime.datetime.now(),
@ -552,7 +544,6 @@ TEST_MESSAGE_WRITE_ACCESS_ALLOWED = Message(
message_id=42,
date=datetime.datetime.now(),
chat=Chat(id=42, type="private"),
from_user=None,
write_access_allowed=WriteAccessAllowed(),
)
TEST_MESSAGE_BOOST_ADDED = Message(
@ -625,7 +616,6 @@ MESSAGES_AND_CONTENT_TYPES = [
[TEST_MESSAGE_GIVEAWAY_CREATED, ContentType.GIVEAWAY_CREATED],
[TEST_MESSAGE_GIVEAWAY_WINNERS, ContentType.GIVEAWAY_WINNERS],
[TEST_MESSAGE_GIVEAWAY_COMPLETED, ContentType.GIVEAWAY_COMPLETED],
[TEST_MESSAGE_HAS_MEDIA_SPOILER, ContentType.HAS_MEDIA_SPOILER],
[TEST_MESSAGE_GENERAL_FORUM_TOPIC_HIDDEN, ContentType.GENERAL_FORUM_TOPIC_HIDDEN],
[TEST_MESSAGE_GENERAL_FORUM_TOPIC_UNHIDDEN, ContentType.GENERAL_FORUM_TOPIC_UNHIDDEN],
[TEST_MESSAGE_WRITE_ACCESS_ALLOWED, ContentType.WRITE_ACCESS_ALLOWED],
@ -674,7 +664,6 @@ MESSAGES_AND_COPY_METHODS = [
[TEST_MESSAGE_USER_SHARED, None],
[TEST_CHAT_SHARED, None],
[TEST_MESSAGE_GIVEAWAY_COMPLETED, None],
[TEST_MESSAGE_HAS_MEDIA_SPOILER, None],
[TEST_MESSAGE_WEB_APP_DATA, None],
[TEST_FORUM_TOPIC_CREATED, None],
[TEST_FORUM_TOPIC_EDITED, None],

View file

@ -16,6 +16,8 @@ from aiogram.dispatcher.event.bases import UNHANDLED, SkipHandler
from aiogram.dispatcher.router import Router
from aiogram.methods import GetMe, GetUpdates, SendMessage, TelegramMethod
from aiogram.types import (
BusinessConnection,
BusinessMessagesDeleted,
CallbackQuery,
Chat,
ChatBoost,
@ -525,6 +527,65 @@ class TestDispatcher:
True,
False,
),
pytest.param(
"deleted_business_messages",
Update(
update_id=42,
deleted_business_messages=BusinessMessagesDeleted(
chat=Chat(id=-42, type="private"),
business_connection_id="qwerty",
message_ids=[1, 2, 3],
),
),
True,
False,
),
pytest.param(
"business_connection",
Update(
update_id=42,
business_connection=BusinessConnection(
id="qwerty",
user=User(id=42, is_bot=False, first_name="Test"),
user_chat_id=42,
date=datetime.datetime.now(),
can_reply=True,
is_enabled=True,
),
),
False,
True,
),
pytest.param(
"edited_business_message",
Update(
update_id=42,
edited_business_message=Message(
message_id=42,
date=datetime.datetime.now(),
text="test",
chat=Chat(id=42, type="private"),
from_user=User(id=42, is_bot=False, first_name="Test"),
),
),
True,
True,
),
pytest.param(
"business_message",
Update(
update_id=42,
business_message=Message(
message_id=42,
date=datetime.datetime.now(),
text="test",
chat=Chat(id=42, type="private"),
from_user=User(id=42, is_bot=False, first_name="Test"),
),
),
True,
True,
),
],
)
async def test_listen_update(

View file

@ -2,8 +2,11 @@ from unittest.mock import patch
import pytest
from aiogram.dispatcher.middlewares.user_context import UserContextMiddleware
from aiogram.types import Update
from aiogram.dispatcher.middlewares.user_context import (
EventContext,
UserContextMiddleware,
)
from aiogram.types import Chat, Update, User
async def next_handler(*args, **kwargs):
@ -18,9 +21,23 @@ class TestUserContextMiddleware:
async def test_call(self):
middleware = UserContextMiddleware()
data = {}
with patch.object(UserContextMiddleware, "resolve_event_context", return_value=[1, 2, 3]):
chat = Chat(id=1, type="private", title="Test")
user = User(id=2, first_name="Test", is_bot=False)
thread_id = 3
with patch.object(
UserContextMiddleware,
"resolve_event_context",
return_value=EventContext(user=user, chat=chat, thread_id=3),
):
await middleware(next_handler, Update(update_id=42), data)
assert data["event_chat"] == 1
assert data["event_from_user"] == 2
assert data["event_thread_id"] == 3
event_context = data["event_context"]
assert isinstance(event_context, EventContext)
assert event_context.chat is chat
assert event_context.user is user
assert event_context.thread_id == thread_id
assert data["event_chat"] is chat
assert data["event_from_user"] is user
assert data["event_thread_id"] == thread_id

View file

@ -12,26 +12,62 @@ BOT_ID = 42
CHAT_ID = -1
USER_ID = 2
THREAD_ID = 3
BUSINESS_CONNECTION_ID = "4"
FIELD = "data"
class TestRedisDefaultKeyBuilder:
@pytest.mark.parametrize(
"with_bot_id,with_destiny,result",
"key_builder,result",
[
[False, False, f"{PREFIX}:{CHAT_ID}:{USER_ID}:{FIELD}"],
[True, False, f"{PREFIX}:{BOT_ID}:{CHAT_ID}:{USER_ID}:{FIELD}"],
[True, True, f"{PREFIX}:{BOT_ID}:{CHAT_ID}:{USER_ID}:{DEFAULT_DESTINY}:{FIELD}"],
[False, True, f"{PREFIX}:{CHAT_ID}:{USER_ID}:{DEFAULT_DESTINY}:{FIELD}"],
[
DefaultKeyBuilder(
prefix=PREFIX,
with_bot_id=True,
with_destiny=True,
with_business_connection_id=True,
),
f"{PREFIX}:{BOT_ID}:{BUSINESS_CONNECTION_ID}:{CHAT_ID}:{USER_ID}:{DEFAULT_DESTINY}:{FIELD}",
],
[
DefaultKeyBuilder(prefix=PREFIX, with_bot_id=True, with_destiny=True),
f"{PREFIX}:{BOT_ID}:{CHAT_ID}:{USER_ID}:{DEFAULT_DESTINY}:{FIELD}",
],
[
DefaultKeyBuilder(
prefix=PREFIX, with_bot_id=True, with_business_connection_id=True
),
f"{PREFIX}:{BOT_ID}:{BUSINESS_CONNECTION_ID}:{CHAT_ID}:{USER_ID}:{FIELD}",
],
[
DefaultKeyBuilder(prefix=PREFIX, with_bot_id=True),
f"{PREFIX}:{BOT_ID}:{CHAT_ID}:{USER_ID}:{FIELD}",
],
[
DefaultKeyBuilder(
prefix=PREFIX, with_destiny=True, with_business_connection_id=True
),
f"{PREFIX}:{BUSINESS_CONNECTION_ID}:{CHAT_ID}:{USER_ID}:{DEFAULT_DESTINY}:{FIELD}",
],
[
DefaultKeyBuilder(prefix=PREFIX, with_destiny=True),
f"{PREFIX}:{CHAT_ID}:{USER_ID}:{DEFAULT_DESTINY}:{FIELD}",
],
[
DefaultKeyBuilder(prefix=PREFIX, with_business_connection_id=True),
f"{PREFIX}:{BUSINESS_CONNECTION_ID}:{CHAT_ID}:{USER_ID}:{FIELD}",
],
[DefaultKeyBuilder(prefix=PREFIX), f"{PREFIX}:{CHAT_ID}:{USER_ID}:{FIELD}"],
],
)
async def test_generate_key(self, with_bot_id: bool, with_destiny: bool, result: str):
key_builder = DefaultKeyBuilder(
prefix=PREFIX,
with_bot_id=with_bot_id,
with_destiny=with_destiny,
async def test_generate_key(self, key_builder: DefaultKeyBuilder, result: str):
key = StorageKey(
chat_id=CHAT_ID,
user_id=USER_ID,
bot_id=BOT_ID,
business_connection_id=BUSINESS_CONNECTION_ID,
destiny=DEFAULT_DESTINY,
)
key = StorageKey(chat_id=CHAT_ID, user_id=USER_ID, bot_id=BOT_ID, destiny=DEFAULT_DESTINY)
assert key_builder.build(key, FIELD) == result
async def test_destiny_check(self):