feat: implement basic command handlers, orchestrator, database manager, and account binding UI
This commit is contained in:
@@ -8,6 +8,8 @@ class DummyBot:
|
||||
def __init__(self):
|
||||
self.replies = []
|
||||
self.sent_messages = []
|
||||
self.approved_join_requests = []
|
||||
self.declined_join_requests = []
|
||||
|
||||
def reply_to(self, message, text, parse_mode=None, disable_web_page_preview=None):
|
||||
self.replies.append(
|
||||
@@ -41,6 +43,19 @@ class DummyBot:
|
||||
|
||||
return _decorator
|
||||
|
||||
def chat_join_request_handler(self, *args, **kwargs): # pragma: no cover - decorator stub
|
||||
def _decorator(func):
|
||||
self.join_request_handler = func
|
||||
return func
|
||||
|
||||
return _decorator
|
||||
|
||||
def approve_chat_join_request(self, chat_id, user_id):
|
||||
self.approved_join_requests.append({"chat_id": chat_id, "user_id": user_id})
|
||||
|
||||
def decline_chat_join_request(self, chat_id, user_id):
|
||||
self.declined_join_requests.append({"chat_id": chat_id, "user_id": user_id})
|
||||
|
||||
|
||||
def _message(text: str):
|
||||
return SimpleNamespace(
|
||||
@@ -127,3 +142,107 @@ def test_basic_handler_markets_rejects_channel_chat():
|
||||
|
||||
assert len(bot.replies) == 1
|
||||
assert "仅支持私聊机器人查询" in bot.replies[0]["text"]
|
||||
|
||||
|
||||
def _join_request(user_id: int = 12345, chat_id: int = -100123):
|
||||
return SimpleNamespace(
|
||||
from_user=SimpleNamespace(id=user_id, username="ada", first_name="Ada"),
|
||||
chat=SimpleNamespace(id=chat_id, title="PolyWeather Pro"),
|
||||
)
|
||||
|
||||
|
||||
def test_join_request_auto_approves_bound_active_pro_user(monkeypatch):
|
||||
monkeypatch.setenv("POLYWEATHER_TELEGRAM_GROUP_ID", "-100123")
|
||||
bot = DummyBot()
|
||||
db = SimpleNamespace(
|
||||
list_supabase_user_ids_for_telegram=lambda telegram_id: ["user-1"]
|
||||
if telegram_id == 12345
|
||||
else []
|
||||
)
|
||||
io_layer = SimpleNamespace(
|
||||
build_welcome_text=lambda: "WELCOME",
|
||||
build_points_rank_text=lambda _user: "TOP",
|
||||
db=db,
|
||||
)
|
||||
entitlement = SimpleNamespace(
|
||||
has_active_subscription=lambda user_id, respect_requirement=False: user_id == "user-1"
|
||||
)
|
||||
handler = BasicCommandHandler(
|
||||
bot=bot,
|
||||
io_layer=io_layer,
|
||||
runtime_status_provider=lambda: RuntimeStatus(
|
||||
started_at="2026-03-12 00:00:00 UTC",
|
||||
loops=[],
|
||||
command_access_mode="group_member",
|
||||
protected_commands=["/city", "/deb"],
|
||||
required_group_chat_id="-100123",
|
||||
),
|
||||
entitlement_service=entitlement,
|
||||
)
|
||||
|
||||
result = handler.handle_chat_join_request(_join_request())
|
||||
|
||||
assert result == "approved"
|
||||
assert bot.approved_join_requests == [{"chat_id": -100123, "user_id": 12345}]
|
||||
assert bot.declined_join_requests == []
|
||||
|
||||
|
||||
def test_join_request_keeps_unbound_user_pending_by_default(monkeypatch):
|
||||
monkeypatch.setenv("POLYWEATHER_TELEGRAM_GROUP_ID", "-100123")
|
||||
bot = DummyBot()
|
||||
db = SimpleNamespace(list_supabase_user_ids_for_telegram=lambda telegram_id: [])
|
||||
io_layer = SimpleNamespace(
|
||||
build_welcome_text=lambda: "WELCOME",
|
||||
build_points_rank_text=lambda _user: "TOP",
|
||||
db=db,
|
||||
)
|
||||
entitlement = SimpleNamespace(has_active_subscription=lambda *_args, **_kwargs: False)
|
||||
handler = BasicCommandHandler(
|
||||
bot=bot,
|
||||
io_layer=io_layer,
|
||||
runtime_status_provider=lambda: RuntimeStatus(
|
||||
started_at="2026-03-12 00:00:00 UTC",
|
||||
loops=[],
|
||||
command_access_mode="group_member",
|
||||
protected_commands=["/city", "/deb"],
|
||||
required_group_chat_id="-100123",
|
||||
),
|
||||
entitlement_service=entitlement,
|
||||
)
|
||||
|
||||
result = handler.handle_chat_join_request(_join_request())
|
||||
|
||||
assert result == "pending:unbound"
|
||||
assert bot.approved_join_requests == []
|
||||
assert bot.declined_join_requests == []
|
||||
|
||||
|
||||
def test_join_request_can_decline_ineligible_user_when_configured(monkeypatch):
|
||||
monkeypatch.setenv("POLYWEATHER_TELEGRAM_GROUP_ID", "-100123")
|
||||
monkeypatch.setenv("POLYWEATHER_TELEGRAM_JOIN_INELIGIBLE_ACTION", "decline")
|
||||
bot = DummyBot()
|
||||
db = SimpleNamespace(list_supabase_user_ids_for_telegram=lambda telegram_id: ["user-1"])
|
||||
io_layer = SimpleNamespace(
|
||||
build_welcome_text=lambda: "WELCOME",
|
||||
build_points_rank_text=lambda _user: "TOP",
|
||||
db=db,
|
||||
)
|
||||
entitlement = SimpleNamespace(has_active_subscription=lambda *_args, **_kwargs: False)
|
||||
handler = BasicCommandHandler(
|
||||
bot=bot,
|
||||
io_layer=io_layer,
|
||||
runtime_status_provider=lambda: RuntimeStatus(
|
||||
started_at="2026-03-12 00:00:00 UTC",
|
||||
loops=[],
|
||||
command_access_mode="group_member",
|
||||
protected_commands=["/city", "/deb"],
|
||||
required_group_chat_id="-100123",
|
||||
),
|
||||
entitlement_service=entitlement,
|
||||
)
|
||||
|
||||
result = handler.handle_chat_join_request(_join_request())
|
||||
|
||||
assert result == "declined:no_active_subscription"
|
||||
assert bot.approved_join_requests == []
|
||||
assert bot.declined_join_requests == [{"chat_id": -100123, "user_id": 12345}]
|
||||
|
||||
Reference in New Issue
Block a user