Skip to content

Commit 28d6546

Browse files
committed
feat(invitation_to_game): use usernames (#53)
1 parent 3d7f345 commit 28d6546

File tree

9 files changed

+261
-80
lines changed

9 files changed

+261
-80
lines changed

src/ttt/application/invitation_to_game/game/invite_to_game.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,13 @@ class InviteToGame:
3434
views: InvitationToGameViews
3535
log: InvitationToGameLog
3636

37-
async def __call__(self, user_id: int, invited_user_id: int) -> None:
37+
async def __call__(
38+
self,
39+
user_id: int,
40+
user_username: str | None,
41+
invited_user_id: int,
42+
invited_user_username: str | None,
43+
) -> None:
3844
"""
3945
:raises ttt.application.common.errors.serialization_error.SerializationError:
4046
""" # noqa: E501
@@ -61,8 +67,10 @@ async def __call__(self, user_id: int, invited_user_id: int) -> None:
6167
tracking = Tracking()
6268
invitation_to_game = invite_to_game(
6369
user,
70+
user_username,
6471
invited_user,
6572
invited_user_id,
73+
invited_user_username,
6674
invitation_to_game_id,
6775
current_datetime,
6876
tracking,

src/ttt/entities/core/invitation_to_game/invitation_to_game.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@ class InvitationToGame:
4141

4242
id_: UUID
4343
inviting_user: User
44+
inviting_user_username: str | None
4445
invited_user: User
46+
invited_user_username: str | None
4547
invitation_datetime: datetime
4648
state: InvitationToGameState
4749

@@ -155,8 +157,10 @@ def invitation_to_game_datetime(expiration_datetime: datetime) -> datetime:
155157

156158
def invite_to_game( # noqa: PLR0913, PLR0917
157159
user: User,
160+
user_username: str | None,
158161
invited_user: User | None,
159162
invited_user_id: int,
163+
invited_user_username: str | None,
160164
invitation_to_game_id: UUID,
161165
current_datetime: datetime,
162166
tracking: Tracking,
@@ -170,7 +174,9 @@ def invite_to_game( # noqa: PLR0913, PLR0917
170174

171175
invitation_to_game = InvitationToGame(
172176
id_=invitation_to_game_id,
177+
inviting_user_username=user_username,
173178
inviting_user=user,
179+
invited_user_username=invited_user_username,
174180
invited_user=invited_user,
175181
invitation_datetime=current_datetime,
176182
state=InvitationToGameState.active,
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
"""
2+
add `username` columns to `invitations_to_game`.
3+
4+
Revision ID: dc1b9f720a1d
5+
Revises: 2dcb2be9e277
6+
Create Date: 2025-10-05 09:44:49.062023
7+
8+
"""
9+
10+
from collections.abc import Sequence
11+
12+
import sqlalchemy as sa
13+
from alembic import op
14+
15+
16+
revision: str = "dc1b9f720a1d"
17+
down_revision: str | None = "2dcb2be9e277"
18+
branch_labels: str | Sequence[str] | None = None
19+
depends_on: str | Sequence[str] | None = None
20+
21+
22+
def upgrade() -> None:
23+
# ### commands auto generated by Alembic - please adjust! ###
24+
op.add_column(
25+
"invitations_to_game",
26+
sa.Column("inviting_user_username", sa.String(), nullable=True),
27+
)
28+
op.add_column(
29+
"invitations_to_game",
30+
sa.Column("invited_user_username", sa.String(), nullable=True),
31+
)
32+
# ### end Alembic commands ###
33+
34+
35+
def downgrade() -> None:
36+
# ### commands auto generated by Alembic - please adjust! ###
37+
op.drop_column("invitations_to_game", "invited_user_username")
38+
op.drop_column("invitations_to_game", "inviting_user_username")
39+
# ### end Alembic commands ###

src/ttt/infrastructure/sqlalchemy/tables/invitation_to_game.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ class TableInvitationToGame(Base[InvitationToGame]):
7575
),
7676
index=True,
7777
)
78+
inviting_user_username: Mapped[str | None]
7879
invited_user_id: Mapped[int] = mapped_column(
7980
ForeignKey(
8081
"users.id",
@@ -83,6 +84,7 @@ class TableInvitationToGame(Base[InvitationToGame]):
8384
),
8485
index=True,
8586
)
87+
invited_user_username: Mapped[str | None]
8688
invitation_datetime: Mapped[datetime] = mapped_column()
8789
state: Mapped[TableInvitationToGameState] = mapped_column(
8890
invitation_to_game_state,
@@ -117,6 +119,8 @@ def __entity__(self) -> InvitationToGame:
117119
invited_user=self.invited_user.entity(),
118120
invitation_datetime=self.invitation_datetime,
119121
state=self.state.entity(),
122+
inviting_user_username=self.inviting_user_username,
123+
invited_user_username=self.invited_user_username,
120124
)
121125

122126
@classmethod
@@ -127,6 +131,8 @@ def of(cls, it: InvitationToGame) -> "TableInvitationToGame":
127131
invited_user_id=it.invited_user.id,
128132
invitation_datetime=it.invitation_datetime,
129133
state=TableInvitationToGameState.of(it.state),
134+
inviting_user_username=it.inviting_user_username,
135+
invited_user_username=it.invited_user_username,
130136
)
131137

132138

src/ttt/presentation/adapters/invitation_to_game_views.py

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ async def incoming_user_invitations_to_game_view(
6161
select(
6262
TableInvitationToGame.id,
6363
TableInvitationToGame.inviting_user_id,
64+
TableInvitationToGame.inviting_user_username,
6465
)
6566
.where(
6667
(TableInvitationToGame.invited_user_id == user_id)
@@ -74,7 +75,11 @@ async def incoming_user_invitations_to_game_view(
7475
rows = result.all()
7576

7677
invitations = [
77-
IncomingInvitationToGameData(row.id, row.inviting_user_id)
78+
IncomingInvitationToGameData(
79+
row.id,
80+
row.inviting_user_id,
81+
row.inviting_user_username,
82+
)
7883
for row in rows
7984
]
8085
self._result_buffer.result = IncomingInvitationsToGameView.of(
@@ -90,6 +95,7 @@ async def outcoming_user_invitations_to_game_view(
9095
select(
9196
TableInvitationToGame.id,
9297
TableInvitationToGame.invited_user_id,
98+
TableInvitationToGame.invited_user_username,
9399
)
94100
.where(
95101
(TableInvitationToGame.inviting_user_id == user_id)
@@ -103,7 +109,11 @@ async def outcoming_user_invitations_to_game_view(
103109
rows = result.all()
104110

105111
invitations = [
106-
OutcomingInvitationToGameData(row.id.hex, row.invited_user_id)
112+
OutcomingInvitationToGameData(
113+
row.id.hex,
114+
row.invited_user_id,
115+
row.invited_user_username,
116+
)
107117
for row in rows
108118
]
109119
self._result_buffer.result = OutcomingInvitationsToGameView.of(
@@ -117,6 +127,7 @@ async def one_incoming_invitation_to_game_view(
117127
select(
118128
TableInvitationToGame.id,
119129
TableInvitationToGame.inviting_user_id,
130+
TableInvitationToGame.inviting_user_username,
120131
)
121132
.where(
122133
(TableInvitationToGame.invited_user_id == user_id)
@@ -137,13 +148,17 @@ async def one_incoming_invitation_to_game_view(
137148
self._result_buffer.result = IncomingInvitationToGameView(
138149
id_hex=row.id.hex,
139150
inviting_user_id=row.inviting_user_id,
151+
inviting_user_username=row.inviting_user_username,
140152
)
141153

142154
async def incoming_invitation_to_game_view(
143155
self, user_id: int, invitation_to_game_id: UUID, /,
144156
) -> None:
145157
stmt = (
146-
select(TableInvitationToGame.inviting_user_id)
158+
select(
159+
TableInvitationToGame.inviting_user_id,
160+
TableInvitationToGame.inviting_user_username,
161+
)
147162
.where(
148163
(TableInvitationToGame.id == invitation_to_game_id)
149164
& (
@@ -152,15 +167,17 @@ async def incoming_invitation_to_game_view(
152167
),
153168
)
154169
)
155-
inviting_user_id = await self._session.scalar(stmt)
170+
result = await self._session.execute(stmt)
171+
row = result.first()
156172

157-
if inviting_user_id is None:
173+
if row is None:
158174
self._result_buffer.result = None
159175
return
160176

161177
self._result_buffer.result = IncomingInvitationToGameView(
162178
id_hex=invitation_to_game_id.hex,
163-
inviting_user_id=inviting_user_id,
179+
inviting_user_id=row.inviting_user_id,
180+
inviting_user_username=row.inviting_user_username,
164181
)
165182

166183
async def invitation_to_game_view(
@@ -195,6 +212,7 @@ async def _invited_user_to_game_view(
195212
view = IncomingInvitationToGameView(
196213
id_hex=invitation_to_game.id_.hex,
197214
inviting_user_id=invitation_to_game.inviting_user.id,
215+
inviting_user_username=invitation_to_game.inviting_user_username,
198216
)
199217
start_data = view.window_data()
200218
await manager.start(
@@ -230,17 +248,20 @@ async def rejected_invitation_to_game_view(
230248
invitation_to_game.inviting_user.id,
231249
)
232250

233-
inviting_user_hint = Text(
234-
"👤 Пользователь ",
235-
Code(invitation_to_game.invited_user.id),
236-
" отклонил ваше приглашение к игре",
237-
).as_html()
251+
if invitation_to_game.invited_user_username is None:
252+
public_user_id = Code(invitation_to_game.invited_user.id).as_html()
253+
else:
254+
public_user_id = f"@{invitation_to_game.invited_user_username}"
255+
256+
hint = (
257+
f"👤 Пользователь {public_user_id} отклонил ваше приглашение к игре"
258+
)
238259

239260
await gather(
240261
invited_user_manager.done(),
241262
inviting_user_manager.start(
242263
MainDialogState.notification,
243-
{"hint": inviting_user_hint},
264+
{"hint": hint},
244265
),
245266
)
246267

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
from collections.abc import Awaitable, Callable
2+
from typing import Any, Optional, Union
3+
4+
from aiogram.types import (
5+
CallbackQuery,
6+
InlineKeyboardButton,
7+
KeyboardButton,
8+
KeyboardButtonRequestUsers,
9+
LoginUrl,
10+
SwitchInlineQueryChosenChat,
11+
WebAppInfo,
12+
)
13+
from aiogram_dialog.api.internal import RawKeyboard
14+
from aiogram_dialog.api.protocols import DialogManager, DialogProtocol
15+
from aiogram_dialog.widgets.common import WhenCondition
16+
from aiogram_dialog.widgets.kbd import Keyboard
17+
from aiogram_dialog.widgets.kbd.button import OnClick
18+
from aiogram_dialog.widgets.text import Text
19+
from aiogram_dialog.widgets.widget_event import (
20+
WidgetEventProcessor,
21+
)
22+
23+
24+
UsersRequestOnClick = Callable[
25+
[CallbackQuery, "UsersRequest", DialogManager], Awaitable[Any],
26+
]
27+
28+
29+
class UsersRequest(Keyboard):
30+
def __init__(
31+
self,
32+
text: Text,
33+
id: str, # noqa: A002
34+
criteria: KeyboardButtonRequestUsers,
35+
when: WhenCondition = None,
36+
) -> None:
37+
super().__init__(id=id, when=when)
38+
self.text = text
39+
self.criteria = criteria
40+
41+
async def _process_own_callback(
42+
self,
43+
callback: CallbackQuery,
44+
dialog: DialogProtocol, # noqa: ARG002
45+
manager: DialogManager,
46+
) -> bool:
47+
return True
48+
49+
async def _render_keyboard(
50+
self,
51+
data: dict[Any, Any],
52+
manager: DialogManager,
53+
) -> RawKeyboard:
54+
return [
55+
[
56+
KeyboardButton(
57+
text=await self.text.render_text(data, manager),
58+
request_users=self.criteria,
59+
),
60+
],
61+
]

src/ttt/presentation/aiogram_dialog/main_dialog/incoming_invitation_to_game_window.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
class IncomingInvitationToGameView(EncodableToWindowData):
3737
id_hex: str
3838
inviting_user_id: int
39+
inviting_user_username: str | None
3940

4041

4142
@inject
@@ -80,10 +81,14 @@ async def incoming_invitation_to_game_html( # noqa: RUF029
8081
raise TypeError
8182

8283
invitation = manager.start_data["main"]
83-
text = Text(
84-
"👤 Приглашение к игре от ", Code(invitation["inviting_user_id"]),
85-
)
86-
return text.as_html()
84+
85+
if invitation.get("inviting_user_username") is None:
86+
text = Text(
87+
"👤 Приглашение к игре от ", Code(invitation["inviting_user_id"]),
88+
)
89+
return text.as_html()
90+
91+
return f"👤 Приглашение к игре от @{invitation["inviting_user_username"]}"
8792

8893

8994
incoming_invitation_to_game_window = Window(

0 commit comments

Comments
 (0)