Skip to content

Commit 0026d0f

Browse files
authored
feat: randomize meme button colors (#303)
* feat: randomize meme button colors * fix: format random button color test
1 parent f3c3d28 commit 0026d0f

6 files changed

Lines changed: 109 additions & 7 deletions

File tree

src/tgbot/buttons.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import inspect
2+
import random
3+
from typing import Any
4+
5+
from telegram import InlineKeyboardButton
6+
7+
INLINE_KEYBOARD_BUTTON_STYLES = ("primary", "success", "danger")
8+
9+
_INLINE_BUTTON_SUPPORTS_STYLE = (
10+
"style" in inspect.signature(InlineKeyboardButton.__init__).parameters
11+
)
12+
13+
14+
def select_random_inline_keyboard_button_style() -> str:
15+
return random.choices(INLINE_KEYBOARD_BUTTON_STYLES, k=1)[0]
16+
17+
18+
def styled_inline_keyboard_button(
19+
text: str,
20+
*,
21+
style: str | None = None,
22+
api_kwargs: dict[str, Any] | None = None,
23+
**kwargs: Any,
24+
) -> InlineKeyboardButton:
25+
if style is None:
26+
return InlineKeyboardButton(text, api_kwargs=api_kwargs, **kwargs)
27+
28+
if _INLINE_BUTTON_SUPPORTS_STYLE:
29+
return InlineKeyboardButton(text, style=style, api_kwargs=api_kwargs, **kwargs)
30+
31+
merged_api_kwargs = dict(api_kwargs or {})
32+
merged_api_kwargs["style"] = style
33+
return InlineKeyboardButton(text, api_kwargs=merged_api_kwargs, **kwargs)

src/tgbot/handlers/chat/group_meme_reaction.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,15 @@
22

33
from sqlalchemy import text
44
from sqlalchemy.dialects.postgresql import insert
5-
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update
5+
from telegram import InlineKeyboardMarkup, Update
66
from telegram.error import BadRequest
77
from telegram.ext import ContextTypes
88

99
from src.database import chat_meme_reaction, execute, fetch_all
10+
from src.tgbot.buttons import (
11+
select_random_inline_keyboard_button_style,
12+
styled_inline_keyboard_button,
13+
)
1014

1115
logger = logging.getLogger(__name__)
1216

@@ -21,8 +25,16 @@ def build_meme_reaction_keyboard(
2125
return InlineKeyboardMarkup(
2226
[
2327
[
24-
InlineKeyboardButton(like_text, callback_data=f"cmr:{meme_id}:1"),
25-
InlineKeyboardButton(dislike_text, callback_data=f"cmr:{meme_id}:2"),
28+
styled_inline_keyboard_button(
29+
like_text,
30+
callback_data=f"cmr:{meme_id}:1",
31+
style=select_random_inline_keyboard_button_style(),
32+
),
33+
styled_inline_keyboard_button(
34+
dislike_text,
35+
callback_data=f"cmr:{meme_id}:2",
36+
style=select_random_inline_keyboard_button_style(),
37+
),
2638
]
2739
]
2840
)

src/tgbot/senders/keyboards.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
from src.storage.constants import (
77
MemeSourceStatus,
88
)
9+
from src.tgbot.buttons import (
10+
select_random_inline_keyboard_button_style,
11+
styled_inline_keyboard_button,
12+
)
913
from src.tgbot.constants import (
1014
MEME_BUTTON_CALLBACK_DATA_PATTERN,
1115
MEME_QUEUE_IS_EMPTY_ALERT_CALLBACK_DATA,
@@ -88,19 +92,22 @@ def reaction_callback(reaction_id: int) -> str:
8892
variant=share_button_variant,
8993
interface_lang=interface_lang,
9094
meme_type=meme_type,
95+
style=select_random_inline_keyboard_button_style(),
9196
)
9297
]
9398
)
9499

95100
keyboard.append(
96101
[
97-
InlineKeyboardButton(
102+
styled_inline_keyboard_button(
98103
like,
99104
callback_data=reaction_callback(Reaction.LIKE.value),
105+
style=select_random_inline_keyboard_button_style(),
100106
),
101-
InlineKeyboardButton(
107+
styled_inline_keyboard_button(
102108
dislike,
103109
callback_data=reaction_callback(Reaction.DISLIKE.value),
110+
style=select_random_inline_keyboard_button_style(),
104111
),
105112
]
106113
)

src/tgbot/sharing.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from src import localizer
1111
from src.config import settings
1212
from src.flows.events import safe_emit
13+
from src.tgbot.buttons import styled_inline_keyboard_button
1314
from src.tgbot.service import assign_experiment, get_experiment_variant
1415

1516
logger = logging.getLogger(__name__)
@@ -126,12 +127,13 @@ def build_meme_share_button(
126127
variant: str,
127128
interface_lang: str | None,
128129
meme_type: str | None = None,
130+
style: str | None = None,
129131
) -> InlineKeyboardButton:
130132
# Cached inline results are reliable for photos/videos. Animation file IDs
131133
# may be GIF or MPEG4, so use the URL adapter until we store the subtype.
132134
supports_exact_inline = meme_type not in {"animation"}
133135
if variant == MEME_SHARE_BUTTON_INLINE and supports_exact_inline:
134-
return InlineKeyboardButton(
136+
return styled_inline_keyboard_button(
135137
text,
136138
switch_inline_query_chosen_chat=SwitchInlineQueryChosenChat(
137139
query=get_meme_inline_query(meme_id),
@@ -140,9 +142,11 @@ def build_meme_share_button(
140142
allow_group_chats=True,
141143
allow_channel_chats=True,
142144
),
145+
style=style,
143146
)
144147

145-
return InlineKeyboardButton(
148+
return styled_inline_keyboard_button(
146149
text,
147150
url=get_meme_share_url(user_id, meme_id, interface_lang),
151+
style=style,
148152
)
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from src.tgbot.handlers.chat.group_meme_reaction import build_meme_reaction_keyboard
2+
3+
4+
def test_group_meme_reaction_keyboard_assigns_random_styles(monkeypatch):
5+
styles = iter(["success", "danger"])
6+
7+
monkeypatch.setattr(
8+
"src.tgbot.buttons.random.choices",
9+
lambda population, k: [next(styles)],
10+
)
11+
12+
markup = build_meme_reaction_keyboard(meme_id=42, likes=3, dislikes=2)
13+
14+
assert [button.to_dict()["style"] for button in markup.inline_keyboard[0]] == [
15+
"success",
16+
"danger",
17+
]
18+
assert [button.callback_data for button in markup.inline_keyboard[0]] == [
19+
"cmr:42:1",
20+
"cmr:42:2",
21+
]

tests/tgbot/test_meme_like_count_experiment.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,31 @@ def test_keyboard_renders_share_button_above_reactions(monkeypatch):
4040
assert markup.inline_keyboard[0][0].url.startswith("https://t.me/share/url?")
4141

4242

43+
def test_keyboard_assigns_random_styles_to_buttons_under_meme(monkeypatch):
44+
styles = iter(["primary", "success", "danger"])
45+
46+
monkeypatch.setattr("src.tgbot.senders.keyboards.random.choice", lambda hearts: "❤️")
47+
monkeypatch.setattr(
48+
"src.tgbot.buttons.random.choices",
49+
lambda population, k: [next(styles)],
50+
)
51+
52+
markup = meme_reaction_keyboard(
53+
meme_id=1,
54+
user_id=2,
55+
referral_button_text="Send to a friend",
56+
visible_like_count=None,
57+
share_button_variant=MEME_SHARE_BUTTON_URL,
58+
interface_lang="en",
59+
)
60+
61+
assert [button.to_dict()["style"] for row in markup.inline_keyboard for button in row] == [
62+
"primary",
63+
"success",
64+
"danger",
65+
]
66+
67+
4368
def test_keyboard_renders_visible_like_count(monkeypatch):
4469
monkeypatch.setattr("src.tgbot.senders.keyboards.random.choice", lambda hearts: "❤️")
4570

0 commit comments

Comments
 (0)