feat(access): демо-QR — многоразовое приглашение для показа
CI / ruff · mypy · pytest (pull_request) Successful in 1h3m54s
CI / ruff · mypy · pytest (pull_request) Successful in 1h3m54s
- invites.max_uses/uses: обычное приглашение — 1 вход, демо-QR — до DEMO_INVITE_MAX_USES (50), живёт DEMO_INVITE_TTL_HOURS (3 ч); захват атомарный, UPDATE … WHERE uses < max_uses - users.guest: вошедшие по демо-QR; «🧹 Отключить гостей демо» отзывает всех и гасит демо-QR - «👥 Участники»: кнопка «🎤 Демо-QR», гости в списке — 🎟 - миграция 0003 (ADD COLUMN без batch — SQLite не пересоздаёт users); проверена на Postgres - SPEC A-3a, ADR-017, DEMO.md, .env.example Refs #55 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -21,8 +21,8 @@ def tg(texts: Texts, sessions: SessionMaker, access: AccessService, repo: Upload
|
||||
return TgClient(build_dispatcher(texts, sessions, access, repo))
|
||||
|
||||
|
||||
async def invite_token(tg: TgClient) -> str:
|
||||
await tg.press(ADMIN, MembersCb(action=MembersAction.INVITE).pack())
|
||||
async def invite_token(tg: TgClient, action: MembersAction = MembersAction.INVITE) -> str:
|
||||
await tg.press(ADMIN, MembersCb(action=action).pack())
|
||||
[photo] = tg.session.sent(SendPhoto)
|
||||
match = re.search(r"https://t\.me/tgit_test_bot\?start=([A-Za-z0-9_-]+)", photo.caption or "")
|
||||
assert match, photo.caption
|
||||
@@ -93,3 +93,23 @@ async def test_cancel_revoke_returns_to_list(tg: TgClient, texts: Texts) -> None
|
||||
|
||||
[edit] = tg.session.sent(EditMessageText)
|
||||
assert edit.text.startswith(texts.members.title.format(count=1))
|
||||
|
||||
|
||||
async def test_demo_qr_lets_many_in_and_guests_are_revoked_together(tg: TgClient, texts: Texts) -> None:
|
||||
token = await invite_token(tg, MembersAction.DEMO_INVITE)
|
||||
|
||||
for n in range(3):
|
||||
await tg.send(NEWBIE + n, f"/start {token}")
|
||||
assert [r.text for r in tg.session.sent(SendMessage)] == [texts.start.welcome] * 3
|
||||
|
||||
tg.session.requests.clear()
|
||||
await tg.press(ADMIN, MembersCb(action=MembersAction.ASK_REVOKE_GUESTS).pack())
|
||||
await tg.press(ADMIN, MembersCb(action=MembersAction.REVOKE_GUESTS).pack())
|
||||
confirm, done = tg.session.sent(EditMessageText)
|
||||
assert confirm.text == texts.members.confirm_guests
|
||||
assert done.text.startswith(texts.members.guests_revoked.format(count=3))
|
||||
|
||||
tg.session.requests.clear()
|
||||
await tg.send(NEWBIE, texts.menu.commit)
|
||||
await tg.send(NEWBIE + 10, f"/start {token}")
|
||||
assert [r.text for r in tg.session.sent(SendMessage)] == [texts.start.no_access, texts.start.invite_rejected]
|
||||
|
||||
Reference in New Issue
Block a user