55import os
66import random
77import time
8+ from types import SimpleNamespace
89from typing import Any , cast
910
1011import botpy
@@ -90,8 +91,50 @@ def __init__(
9091 )
9192 self ._lock = asyncio .Lock ()
9293
93- def _build_content_key (self , content : str , sender_id : str ) -> str | None :
94- return build_sender_content_dedup_key (content , sender_id )
94+ def _id_dedup_enabled (self , message_id : str ) -> bool :
95+ return self ._message_ids .ttl_seconds > 0 and bool (message_id )
96+
97+ def _content_dedup_enabled (self ) -> bool :
98+ return self ._content_keys .ttl_seconds > 0
99+
100+ def _register_message_id (self , message_id : str ) -> bool :
101+ """Return True if duplicate by ID, False otherwise (and register)."""
102+ if self ._message_ids .contains (message_id ):
103+ logger .debug (
104+ "[QQOfficial] Duplicate message detected (by ID): %s..." ,
105+ message_id [:50 ],
106+ )
107+ return True
108+
109+ self ._message_ids .add (message_id )
110+ return False
111+
112+ def _register_content (
113+ self ,
114+ message_id : str ,
115+ content : str ,
116+ sender_id : str ,
117+ id_dedup_enabled : bool ,
118+ ) -> bool :
119+ """Return True if duplicate by content, False otherwise (and register)."""
120+ content_key = build_sender_content_dedup_key (content , sender_id )
121+ if content_key is None :
122+ logger .debug ("[QQOfficial] New message registered: %s..." , message_id [:50 ])
123+ return False
124+
125+ if self ._content_keys .contains (content_key ):
126+ logger .debug (
127+ "[QQOfficial] Duplicate message detected (by content): %s" ,
128+ content_key ,
129+ )
130+ # Preserve existing behavior: do not keep message_id on content duplicates
131+ if id_dedup_enabled :
132+ self ._message_ids .discard (message_id )
133+ return True
134+
135+ self ._content_keys .add (content_key )
136+ logger .debug ("[QQOfficial] New message registered: %s..." , message_id [:50 ])
137+ return False
95138
96139 async def is_duplicate (
97140 self ,
@@ -100,22 +143,14 @@ async def is_duplicate(
100143 sender_id : str = "" ,
101144 ) -> bool :
102145 async with self ._lock :
103- id_dedup_enabled = self ._message_ids . ttl_seconds > 0 and bool (message_id )
104- content_dedup_enabled = self ._content_keys . ttl_seconds > 0
146+ id_dedup_enabled = self ._id_dedup_enabled (message_id )
147+ content_dedup_enabled = self ._content_dedup_enabled ()
105148
106149 if not id_dedup_enabled and not content_dedup_enabled :
107150 return False
108151
109- # 1) ID-based dedup
110- if id_dedup_enabled :
111- if self ._message_ids .contains (message_id ):
112- logger .debug (
113- "[QQOfficial] Duplicate message detected (by ID): %s..." ,
114- message_id [:50 ],
115- )
116- return True
117-
118- self ._message_ids .add (message_id )
152+ if id_dedup_enabled and self ._register_message_id (message_id ):
153+ return True
119154
120155 # 2) Content-based dedup
121156 if not content_dedup_enabled :
@@ -124,26 +159,12 @@ async def is_duplicate(
124159 )
125160 return False
126161
127- content_key = self ._build_content_key (content , sender_id )
128- if content_key is None :
129- logger .debug (
130- "[QQOfficial] New message registered: %s..." , message_id [:50 ]
131- )
132- return False
133-
134- if self ._content_keys .contains (content_key ):
135- logger .debug (
136- "[QQOfficial] Duplicate message detected (by content): %s" ,
137- content_key ,
138- )
139- # Preserve existing behavior: do not keep message_id on content duplicates
140- if id_dedup_enabled :
141- self ._message_ids .discard (message_id )
142- return True
143-
144- self ._content_keys .add (content_key )
145- logger .debug ("[QQOfficial] New message registered: %s..." , message_id [:50 ])
146- return False
162+ return self ._register_content (
163+ message_id = message_id ,
164+ content = content ,
165+ sender_id = sender_id ,
166+ id_dedup_enabled = id_dedup_enabled ,
167+ )
147168
148169
149170class botClient (Client ):
@@ -278,9 +299,11 @@ def __init__(
278299 async def should_handle_raw_message (self , message : Any ) -> bool :
279300 """Return False if the raw incoming message should be dropped."""
280301 sender_id = _extract_sender_id (message )
281- content = getattr (message , "content" , "" ) or ""
302+ message_id = str (getattr (message , "id" , "" ) or "" )
303+ raw_content = getattr (message , "content" , "" )
304+ content = str (raw_content or "" )
282305 is_duplicate = await self ._deduplicator .is_duplicate (
283- message . id ,
306+ message_id ,
284307 content ,
285308 sender_id ,
286309 )
@@ -327,7 +350,7 @@ async def _send_by_session_common(
327350
328351 payload : dict [str , Any ] = {"content" : plain_text , "msg_id" : msg_id }
329352 ret : Any = None
330- send_helper = self .client
353+ send_helper = SimpleNamespace ( bot = self .client )
331354
332355 if session .message_type == MessageType .GROUP_MESSAGE :
333356 scene = self ._session_scene .get (session .session_id )
0 commit comments