feat: prompt users to start Telegram bot for notifications

This commit is contained in:
3252a8
2026-05-30 21:15:17 +03:00
parent 8c0e778388
commit acc222da41
28 changed files with 854 additions and 41 deletions
+6
View File
@@ -438,6 +438,11 @@ class AccountLinkingPanelTests(unittest.IsolatedAsyncioTestCase):
"bot.services.notification_service.NotificationService",
return_value=notification_service,
),
patch.object(
account_routes,
"_probe_telegram_notifications_for_user_id",
AsyncMock(),
) as probe_telegram_notifications,
):
response = await account_routes.account_telegram_link_route(request)
@@ -453,6 +458,7 @@ class AccountLinkingPanelTests(unittest.IsolatedAsyncioTestCase):
source_user_id=-100,
target_user_id=42,
)
probe_telegram_notifications.assert_awaited_once_with(request, 42)
self.assertEqual(panel_calls, ["delete", "update"])
panel_service.delete_user_from_panel.assert_awaited_once_with(
"panel-email",
@@ -108,6 +108,7 @@ def _user(**overrides):
def test_send_stage_records_telegram_and_email_channel_keys(monkeypatch):
recorded = []
status_changes = []
async def fake_has(session, subscription_id, notification_key):
return notification_key in recorded
@@ -115,8 +116,19 @@ def test_send_stage_records_telegram_and_email_channel_keys(monkeypatch):
async def fake_record(session, subscription_id, notification_key, *, sent_at=None):
recorded.append(notification_key)
async def fake_mark_status(session, user_id, status, *, telegram_id=None, checked_at=None):
status_changes.append(
{
"user_id": user_id,
"status": status,
"telegram_id": telegram_id,
"checked_at": checked_at,
}
)
monkeypatch.setattr(lifecycle.subscription_dal, "has_subscription_notification", fake_has)
monkeypatch.setattr(lifecycle.subscription_dal, "record_subscription_notification", fake_record)
monkeypatch.setattr(lifecycle, "mark_telegram_notifications_status", fake_mark_status)
bot = FakeBot()
email_service = FakeEmailService()
@@ -154,6 +166,15 @@ def test_send_stage_records_telegram_and_email_channel_keys(monkeypatch):
assert email_service.messages[0]["email"] == "user@example.test"
assert "Mirrored Telegram notice" in email_service.messages[0]["content"].html
assert recorded == ["before_3d:telegram", "before_3d:email"]
assert status_changes == [
{
"user_id": 123,
"status": lifecycle.TELEGRAM_NOTIFICATIONS_ENABLED,
"telegram_id": 555,
"checked_at": status_changes[0]["checked_at"],
}
]
assert status_changes[0]["checked_at"] is not None
def test_legacy_stage_key_suppresses_only_telegram(monkeypatch):
@@ -199,8 +220,9 @@ def test_legacy_stage_key_suppresses_only_telegram(monkeypatch):
assert recorded == ["before_3d", "before_3d:email"]
def test_terminal_telegram_failure_is_recorded_to_avoid_retry_spam(monkeypatch):
def test_unstarted_telegram_failure_marks_status_without_recording_delivery(monkeypatch):
recorded = []
status_changes = []
async def fake_has(session, subscription_id, notification_key):
return notification_key in recorded
@@ -208,8 +230,19 @@ def test_terminal_telegram_failure_is_recorded_to_avoid_retry_spam(monkeypatch):
async def fake_record(session, subscription_id, notification_key, *, sent_at=None):
recorded.append(notification_key)
async def fake_mark_status(session, user_id, status, *, telegram_id=None, checked_at=None):
status_changes.append(
{
"user_id": user_id,
"status": status,
"telegram_id": telegram_id,
"checked_at": checked_at,
}
)
monkeypatch.setattr(lifecycle.subscription_dal, "has_subscription_notification", fake_has)
monkeypatch.setattr(lifecycle.subscription_dal, "record_subscription_notification", fake_record)
monkeypatch.setattr(lifecycle, "mark_telegram_notifications_status", fake_mark_status)
bot = ChatNotFoundBot()
settings = _settings()
@@ -241,7 +274,15 @@ def test_terminal_telegram_failure_is_recorded_to_avoid_retry_spam(monkeypatch):
assert delivery.telegram_sent is False
assert delivery.email_sent is False
assert bot.calls[0][0] == 777
assert recorded == ["before_3d:telegram"]
assert recorded == []
assert status_changes == [
{
"user_id": 123,
"status": lifecycle.TELEGRAM_NOTIFICATIONS_NEEDS_START,
"telegram_id": None,
"checked_at": None,
}
]
def test_email_only_user_gets_email_name_direct_copy_and_renewal_login_link(monkeypatch):
+24
View File
@@ -0,0 +1,24 @@
from types import SimpleNamespace
from bot.services.telegram_notifications import telegram_notifications_need_prompt
def _user(status: str):
return SimpleNamespace(telegram_id=123, telegram_notifications_status=status)
def test_telegram_notifications_prompt_only_for_explicit_unreachable_statuses():
assert telegram_notifications_need_prompt(_user("needs_start")) is True
assert telegram_notifications_need_prompt(_user("blocked")) is True
assert telegram_notifications_need_prompt(_user("enabled")) is False
assert telegram_notifications_need_prompt(_user("unknown")) is False
def test_telegram_notifications_prompt_requires_linked_telegram():
user = SimpleNamespace(
email="user@example.test",
telegram_id=None,
telegram_notifications_status="needs_start",
)
assert telegram_notifications_need_prompt(user) is False