diff --git a/.env.example b/.env.example index 5bdaa1fe..153e89f9 100644 --- a/.env.example +++ b/.env.example @@ -30,6 +30,9 @@ TELEGRAM_BOT_TOKEN= TELEGRAM_CHAT_ID= TELEGRAM_CHAT_IDS= POLYWEATHER_TELEGRAM_GROUP_ID= +# Optional: restrict message-points accrual to these chat IDs. +# Example: POLYWEATHER_BOT_POINTS_CHAT_IDS=-1003965137823 +POLYWEATHER_BOT_POINTS_CHAT_IDS= POLYWEATHER_GROUP_MEMBER_PRICE_USDC=5 POLYWEATHER_PUBLIC_PRICE_USDC=10 TELEGRAM_QUERY_TOPIC_CHAT_ID= diff --git a/src/bot/io_layer.py b/src/bot/io_layer.py index a5913d91..1f24a55a 100644 --- a/src/bot/io_layer.py +++ b/src/bot/io_layer.py @@ -15,6 +15,8 @@ from src.bot.settings import ( MESSAGE_POINTS, ) from src.database.db_manager import DBManager +from src.utils.telegram_chat_ids import get_telegram_chat_ids_from_env +from src.utils.telegram_chat_ids import parse_telegram_chat_ids class BotIOLayer: @@ -29,6 +31,7 @@ class BotIOLayer: self.message_cooldown_map = self._parse_int_map( os.getenv("POLYWEATHER_BOT_MESSAGE_COOLDOWN_BY_CHAT") ) + self.points_chat_ids = self._resolve_points_chat_ids() self.query_topic_chat_id = str( os.getenv("TELEGRAM_QUERY_TOPIC_CHAT_ID") or "" ).strip() @@ -98,6 +101,22 @@ class BotIOLayer: return self.message_cooldown_map[chat_key] return MESSAGE_COOLDOWN_SEC + @staticmethod + def _resolve_points_chat_ids() -> set[str]: + dedicated = parse_telegram_chat_ids( + os.getenv("POLYWEATHER_BOT_POINTS_CHAT_IDS"), + os.getenv("POLYWEATHER_BOT_POINTS_CHAT_ID"), + ) + if dedicated: + return set(dedicated) + return set(get_telegram_chat_ids_from_env()) + + def _is_points_chat_allowed(self, chat_id: Any) -> bool: + if not self.points_chat_ids: + return True + chat_key = str(chat_id).strip() if chat_id is not None else "" + return chat_key in self.points_chat_ids + def _resolve_query_target( self, source_chat_id: Any, @@ -249,6 +268,14 @@ class BotIOLayer: chat = getattr(message, "chat", None) if not chat or chat.type not in ("group", "supergroup"): return + if not self._is_points_chat_allowed(getattr(chat, "id", None)): + logger.info( + "message points skipped chat_id={} thread_id={} user_id={} reason=chat_not_allowed", + getattr(chat, "id", None), + getattr(message, "message_thread_id", None), + getattr(getattr(message, "from_user", None), "id", None), + ) + return user = message.from_user username = self.display_name(user) diff --git a/tests/test_bot_io_layer_points.py b/tests/test_bot_io_layer_points.py new file mode 100644 index 00000000..c1b476e1 --- /dev/null +++ b/tests/test_bot_io_layer_points.py @@ -0,0 +1,54 @@ +from __future__ import annotations + +from types import SimpleNamespace + +from src.bot.io_layer import BotIOLayer + + +class DummyDB: + def __init__(self): + self.upserts = [] + self.activities = [] + + def upsert_user(self, telegram_id, username): + self.upserts.append((telegram_id, username)) + + def add_message_activity(self, telegram_id, **kwargs): + self.activities.append({"telegram_id": telegram_id, **kwargs}) + return {"awarded": True, "points_added": kwargs.get("points_to_add", 0)} + + +def _message(chat_id: int | str, text: str = "有效发言"): + return SimpleNamespace( + text=text, + from_user=SimpleNamespace(id=123, username="alice", first_name="Alice"), + chat=SimpleNamespace(id=chat_id, type="supergroup"), + message_thread_id=None, + ) + + +def test_group_message_points_include_configured_forum_chat(monkeypatch): + monkeypatch.delenv("POLYWEATHER_BOT_POINTS_CHAT_IDS", raising=False) + monkeypatch.delenv("POLYWEATHER_BOT_POINTS_CHAT_ID", raising=False) + monkeypatch.setenv("TELEGRAM_CHAT_IDS", "-1003965137823") + monkeypatch.delenv("TELEGRAM_CHAT_ID", raising=False) + db = DummyDB() + io_layer = BotIOLayer(bot=SimpleNamespace(), db=db) + + io_layer.track_group_text_activity(_message(-1003965137823)) + + assert db.upserts == [(123, "alice")] + assert len(db.activities) == 1 + assert db.activities[0]["telegram_id"] == 123 + + +def test_group_message_points_skip_unconfigured_chat_when_allowlist_exists(monkeypatch): + monkeypatch.setenv("POLYWEATHER_BOT_POINTS_CHAT_IDS", "-1003965137823") + monkeypatch.setenv("TELEGRAM_CHAT_IDS", "-1003965137823") + db = DummyDB() + io_layer = BotIOLayer(bot=SimpleNamespace(), db=db) + + io_layer.track_group_text_activity(_message(-1000000000000)) + + assert db.upserts == [] + assert db.activities == []