diff --git a/src/agentchat-providers/src/telegram.rs b/src/agentchat-providers/src/telegram.rs index 160352e..ee596ca 100644 --- a/src/agentchat-providers/src/telegram.rs +++ b/src/agentchat-providers/src/telegram.rs @@ -228,10 +228,11 @@ pub fn normalize(update: &Value, bot: i64, username: &str) -> Option { ("", &[][..]) }; let address = strip_bot_address(text, entities, bot, username); - if chat.kind != "private" - && !address.is_addressed - && message["reply_to_message"]["from"]["id"].as_i64() != Some(bot) - { + let reply = &message["reply_to_message"]; + // Plain topic messages reply to the topic's creation message, which the bot may own. + let replies_to_bot = + reply["from"]["id"].as_i64() == Some(bot) && reply.get("forum_topic_created").is_none(); + if chat.kind != "private" && !address.is_addressed && !replies_to_bot { return None; } let files = attachments(message); @@ -741,6 +742,28 @@ mod tests { assert_eq!(a.text, "😀 hi"); } #[test] + fn bot_created_forum_topic_root_is_not_a_reply_to_the_bot() { + let update = |reply: Value| { + json!({"update_id": 1, "message": { + "message_id": 20, + "chat": {"id": -100, "type": "supergroup", "is_forum": true}, + "from": {"id": 5, "is_bot": false, "first_name": "Alice"}, + "is_topic_message": true, + "message_thread_id": 7, + "reply_to_message": reply, + "text": "just chatting", + }}) + }; + // Telegram links every plain message in a topic to its creation message. + let root = json!({ + "message_id": 7, "from": {"id": 1, "is_bot": true}, + "forum_topic_created": {"name": "Support", "icon_color": 7322096}, + }); + assert!(normalize(&update(root), 1, "bot").is_none()); + let answer = json!({"message_id": 8, "from": {"id": 1, "is_bot": true}, "text": "hi"}); + assert!(normalize(&update(answer), 1, "bot").is_some()); + } + #[test] fn malformed_utf16_and_expanding_casefold_are_safe() { let address = strip_bot_address( "@İ", diff --git a/tests/channels/telegram/test_events.py b/tests/channels/telegram/test_events.py index cf2721a..fa6bb42 100644 --- a/tests/channels/telegram/test_events.py +++ b/tests/channels/telegram/test_events.py @@ -53,6 +53,15 @@ def test_text_mention_and_reply_to_bot_are_addressed(): assert read(group_update("thanks", reply_to_message=reply)).text == "thanks" +def test_topic_chatter_under_a_bot_created_topic_is_not_addressed(): + # Plain topic messages carry the topic's creation message as reply_to_message. + root = {"message_id": 7, "from": dict(BOT), "forum_topic_created": {"name": "Support"}} + topic = {"is_topic_message": True, "message_thread_id": 7} + assert read(group_update("just chatting", reply_to_message=root, **topic)) is None + answer = {"message_id": 8, "from": dict(BOT), "text": "hi"} + assert read(group_update("thanks", reply_to_message=answer, **topic)).text == "thanks" + + @pytest.mark.parametrize( "update", [