Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions src/tgbot/buttons.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import inspect
import random
from typing import Any

from telegram import InlineKeyboardButton

INLINE_KEYBOARD_BUTTON_STYLES = ("primary", "success", "danger")

_INLINE_BUTTON_SUPPORTS_STYLE = (
"style" in inspect.signature(InlineKeyboardButton.__init__).parameters
)


def select_random_inline_keyboard_button_style() -> str:
return random.choices(INLINE_KEYBOARD_BUTTON_STYLES, k=1)[0]


def styled_inline_keyboard_button(
text: str,
*,
style: str | None = None,
api_kwargs: dict[str, Any] | None = None,
**kwargs: Any,
) -> InlineKeyboardButton:
if style is None:
return InlineKeyboardButton(text, api_kwargs=api_kwargs, **kwargs)

if _INLINE_BUTTON_SUPPORTS_STYLE:
return InlineKeyboardButton(text, style=style, api_kwargs=api_kwargs, **kwargs)

merged_api_kwargs = dict(api_kwargs or {})
merged_api_kwargs["style"] = style
return InlineKeyboardButton(text, api_kwargs=merged_api_kwargs, **kwargs)
18 changes: 15 additions & 3 deletions src/tgbot/handlers/chat/group_meme_reaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@

from sqlalchemy import text
from sqlalchemy.dialects.postgresql import insert
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update
from telegram import InlineKeyboardMarkup, Update
from telegram.error import BadRequest
from telegram.ext import ContextTypes

from src.database import chat_meme_reaction, execute, fetch_all
from src.tgbot.buttons import (
select_random_inline_keyboard_button_style,
styled_inline_keyboard_button,
)

logger = logging.getLogger(__name__)

Expand All @@ -21,8 +25,16 @@ def build_meme_reaction_keyboard(
return InlineKeyboardMarkup(
[
[
InlineKeyboardButton(like_text, callback_data=f"cmr:{meme_id}:1"),
InlineKeyboardButton(dislike_text, callback_data=f"cmr:{meme_id}:2"),
styled_inline_keyboard_button(
like_text,
callback_data=f"cmr:{meme_id}:1",
style=select_random_inline_keyboard_button_style(),
),
styled_inline_keyboard_button(
dislike_text,
callback_data=f"cmr:{meme_id}:2",
style=select_random_inline_keyboard_button_style(),
),
]
]
)
Expand Down
11 changes: 9 additions & 2 deletions src/tgbot/senders/keyboards.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
from src.storage.constants import (
MemeSourceStatus,
)
from src.tgbot.buttons import (
select_random_inline_keyboard_button_style,
styled_inline_keyboard_button,
)
from src.tgbot.constants import (
MEME_BUTTON_CALLBACK_DATA_PATTERN,
MEME_QUEUE_IS_EMPTY_ALERT_CALLBACK_DATA,
Expand Down Expand Up @@ -88,19 +92,22 @@ def reaction_callback(reaction_id: int) -> str:
variant=share_button_variant,
interface_lang=interface_lang,
meme_type=meme_type,
style=select_random_inline_keyboard_button_style(),
)
]
)

keyboard.append(
[
InlineKeyboardButton(
styled_inline_keyboard_button(
like,
callback_data=reaction_callback(Reaction.LIKE.value),
style=select_random_inline_keyboard_button_style(),
),
InlineKeyboardButton(
styled_inline_keyboard_button(
dislike,
callback_data=reaction_callback(Reaction.DISLIKE.value),
style=select_random_inline_keyboard_button_style(),
),
]
)
Expand Down
8 changes: 6 additions & 2 deletions src/tgbot/sharing.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from src import localizer
from src.config import settings
from src.flows.events import safe_emit
from src.tgbot.buttons import styled_inline_keyboard_button
from src.tgbot.service import assign_experiment, get_experiment_variant

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -126,12 +127,13 @@ def build_meme_share_button(
variant: str,
interface_lang: str | None,
meme_type: str | None = None,
style: str | None = None,
) -> InlineKeyboardButton:
# Cached inline results are reliable for photos/videos. Animation file IDs
# may be GIF or MPEG4, so use the URL adapter until we store the subtype.
supports_exact_inline = meme_type not in {"animation"}
if variant == MEME_SHARE_BUTTON_INLINE and supports_exact_inline:
return InlineKeyboardButton(
return styled_inline_keyboard_button(
text,
switch_inline_query_chosen_chat=SwitchInlineQueryChosenChat(
query=get_meme_inline_query(meme_id),
Expand All @@ -140,9 +142,11 @@ def build_meme_share_button(
allow_group_chats=True,
allow_channel_chats=True,
),
style=style,
)

return InlineKeyboardButton(
return styled_inline_keyboard_button(
text,
url=get_meme_share_url(user_id, meme_id, interface_lang),
style=style,
)
21 changes: 21 additions & 0 deletions tests/tgbot/test_group_meme_reaction.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from src.tgbot.handlers.chat.group_meme_reaction import build_meme_reaction_keyboard


def test_group_meme_reaction_keyboard_assigns_random_styles(monkeypatch):
styles = iter(["success", "danger"])

monkeypatch.setattr(
"src.tgbot.buttons.random.choices",
lambda population, k: [next(styles)],
)

markup = build_meme_reaction_keyboard(meme_id=42, likes=3, dislikes=2)

assert [button.to_dict()["style"] for button in markup.inline_keyboard[0]] == [
"success",
"danger",
]
assert [button.callback_data for button in markup.inline_keyboard[0]] == [
"cmr:42:1",
"cmr:42:2",
]
25 changes: 25 additions & 0 deletions tests/tgbot/test_meme_like_count_experiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,31 @@ def test_keyboard_renders_share_button_above_reactions(monkeypatch):
assert markup.inline_keyboard[0][0].url.startswith("https://t.me/share/url?")


def test_keyboard_assigns_random_styles_to_buttons_under_meme(monkeypatch):
styles = iter(["primary", "success", "danger"])

monkeypatch.setattr("src.tgbot.senders.keyboards.random.choice", lambda hearts: "❤️")
monkeypatch.setattr(
"src.tgbot.buttons.random.choices",
lambda population, k: [next(styles)],
)

markup = meme_reaction_keyboard(
meme_id=1,
user_id=2,
referral_button_text="Send to a friend",
visible_like_count=None,
share_button_variant=MEME_SHARE_BUTTON_URL,
interface_lang="en",
)

assert [button.to_dict()["style"] for row in markup.inline_keyboard for button in row] == [
"primary",
"success",
"danger",
]


def test_keyboard_renders_visible_like_count(monkeypatch):
monkeypatch.setattr("src.tgbot.senders.keyboards.random.choice", lambda hearts: "❤️")

Expand Down
Loading