Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions astrbot/core/platform/sources/wecom/wecom_adapter.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import asyncio
import os
import sys
import time
import uuid
from collections.abc import Awaitable, Callable
from typing import Any, cast
Expand Down Expand Up @@ -140,6 +141,8 @@ async def shutdown_trigger(self) -> None:

@register_platform_adapter("wecom", "wecom 适配器", support_streaming_message=False)
class WecomPlatformAdapter(Platform):
WECHAT_KF_TEXT_CONTENT_DEDUP_TTL_SECONDS = 15

def __init__(
self,
platform_config: dict,
Expand All @@ -166,6 +169,7 @@ def __init__(

self.server = WecomServer(self._event_queue, self.config)
self.agent_id: str | None = None
self._wechat_kf_seen_text_messages: dict[str, float] = {}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Update the type hint to reflect the use of a tuple key for deduplication, as suggested in the logic below.

Suggested change
self._wechat_kf_seen_text_messages: dict[str, float] = {}
self._wechat_kf_seen_text_messages: dict[tuple[str, str], float] = {}


self.client = WeChatClient(
self.config["corpid"].strip(),
Expand Down Expand Up @@ -210,6 +214,28 @@ def get_latest_msg_item() -> dict | None:

self.server.callback = callback

def _is_duplicate_wechat_kf_text_message(self, session_id: str, text: str) -> bool:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

New functionality should be accompanied by corresponding unit tests. Please add tests to verify the deduplication logic, including TTL expiration and session-based isolation.

References
  1. New functionality should be accompanied by corresponding unit tests.

normalized_text = text.strip()
if not normalized_text:
return False

now = time.monotonic()
expired_keys = [
key
for key, expires_at in self._wechat_kf_seen_text_messages.items()
if expires_at <= now
]
for key in expired_keys:
self._wechat_kf_seen_text_messages.pop(key, None)
Comment on lines +223 to +229
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The current cleanup logic iterates through the entire _wechat_kf_seen_text_messages dictionary on every incoming text message. This is an O(N) operation that runs on the main event loop. While the 15-second TTL likely keeps the dictionary small, a high volume of unique messages could lead to performance degradation and block the event loop. Consider cleaning up only periodically or limiting the maximum size of the deduplication cache.


dedup_key = f"{session_id}:{normalized_text}"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using string concatenation with a colon as a key can lead to collisions if the session_id (which is the external_userid) contains a colon. Using a tuple (session_id, normalized_text) is a safer and more idiomatic way to create a composite key in Python.

Suggested change
dedup_key = f"{session_id}:{normalized_text}"
dedup_key = (session_id, normalized_text)

if dedup_key in self._wechat_kf_seen_text_messages:
return True
self._wechat_kf_seen_text_messages[dedup_key] = (
now + self.WECHAT_KF_TEXT_CONTENT_DEDUP_TTL_SECONDS
)
return False

@override
async def send_by_session(
self,
Expand Down Expand Up @@ -390,6 +416,13 @@ async def convert_wechat_kf_message(self, msg: dict) -> AstrBotMessage | None:
abm.message_str = ""
if msgtype == "text":
text = msg.get("text", {}).get("content", "").strip()
if self._is_duplicate_wechat_kf_text_message(abm.session_id, text):
logger.debug(
"忽略 15 秒内重复微信客服文本消息 session_id=%s text=%s",
Comment on lines +420 to +421
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Avoid hard-coding the TTL value in the log message.

The TTL is defined as WECHAT_KF_TEXT_CONTENT_DEDUP_TTL_SECONDS = 15, but the log message hard-codes 15 秒. If the TTL changes, the log will be inaccurate.

Please format the message using the constant (e.g., %d or an f-string) so the log always reflects the configured TTL.

Suggested implementation:

            if self._is_duplicate_wechat_kf_text_message(abm.session_id, text):
                logger.debug(
                    "忽略 %d 秒内重复微信客服文本消息 session_id=%s text=%s",
                    WECHAT_KF_TEXT_CONTENT_DEDUP_TTL_SECONDS,
                    abm.session_id,
                    text,
                )

If WECHAT_KF_TEXT_CONTENT_DEDUP_TTL_SECONDS is not defined in this module, make sure it is either:

  1. Imported at the top of wecom_adapter.py from the module where it is defined, or
  2. Defined in this module if that is where it logically belongs.

abm.session_id,
text,
)
return None
abm.message = [Plain(text=text)]
abm.message_str = text
elif msgtype == "image":
Expand Down
Loading