feat(handler): 添加消息去重功能防止飞书重复投递

实现基于(user_id, content)的消息去重机制,避免飞书在网络抖动时重复投递相同消息。使用10秒时间窗口判断重复消息,超过窗口的旧记录会被自动清理。
This commit is contained in:
Yuyao Huang (Sam) 2026-03-29 07:10:58 +08:00
parent cbeafa35a5
commit d6183594d6

View File

@ -25,6 +25,26 @@ _ws_connected: bool = False
_last_message_time: float = 0.0 _last_message_time: float = 0.0
_reconnect_count: int = 0 _reconnect_count: int = 0
# Deduplication: drop Feishu re-deliveries by (user_id, content) within a short window.
# Feishu retries on network hiccups within ~60s using the same payload.
# We use a 10s window: identical content from the same user within 10s is a re-delivery,
# not a deliberate repeat (user intentional repeats arrive after the bot has already replied).
_recent_messages: dict[tuple[str, str], float] = {} # key: (user_id, content) → timestamp
_DEDUP_WINDOW = 10.0 # seconds
def _is_duplicate(user_id: str, content: str) -> bool:
"""Return True if this (user, content) pair arrived within the dedup window."""
now = time.time()
expired = [k for k, ts in _recent_messages.items() if now - ts > _DEDUP_WINDOW]
for k in expired:
del _recent_messages[k]
key = (user_id, content)
if key in _recent_messages:
return True
_recent_messages[key] = now
return False
def get_ws_status() -> dict[str, Any]: def get_ws_status() -> dict[str, Any]:
"""Return WebSocket connection status.""" """Return WebSocket connection status."""
@ -79,10 +99,14 @@ def _handle_message(data: P2ImMessageReceiveV1) -> None:
logger.info("Empty text after stripping, ignoring") logger.info("Empty text after stripping, ignoring")
return return
logger.info("✉ ...%s%r", open_id[-8:], text[:80])
user_id = open_id or chat_id user_id = open_id or chat_id
if _is_duplicate(user_id, text):
logger.info("Dropping duplicate delivery: user=...%s text=%r", user_id[-8:], text[:60])
return
logger.info("✉ ...%s%r", open_id[-8:], text[:80])
if _main_loop is None: if _main_loop is None:
logger.error("Main event loop not set; cannot process message") logger.error("Main event loop not set; cannot process message")
return return