@@ -1950,6 +1950,7 @@ def __init__(self, cookies_str=None, cookie_id: str = "default", user_id: int =
19501950 self.last_heartbeat_response = 0
19511951 self.last_sent_heartbeat_mid = None
19521952 self.pending_heartbeat_mids = deque(maxlen=32)
1953+ self.lwp_response_waiters = {}
19531954 self.heartbeat_task = None
19541955 self.ws = None
19551956 self.last_non_heartbeat_message_time = 0
@@ -12169,6 +12170,68 @@ async def init(self, ws):
1216912170 await ws.send(json.dumps(msg))
1217012171 logger.info(f'【{self.cookie_id}】连接注册完成')
1217112172
12173+ async def _ack_lwp_message(self, ws, message: dict) -> None:
12174+ try:
12175+ headers = message.get("headers", {}) if isinstance(message, dict) else {}
12176+ ack = {
12177+ "code": 200,
12178+ "headers": {
12179+ "mid": headers.get("mid", generate_mid()),
12180+ "sid": headers.get("sid", ""),
12181+ }
12182+ }
12183+ if 'app-key' in headers:
12184+ ack["headers"]["app-key"] = headers["app-key"]
12185+ if 'ua' in headers:
12186+ ack["headers"]["ua"] = headers["ua"]
12187+ if 'dt' in headers:
12188+ ack["headers"]["dt"] = headers["dt"]
12189+ await ws.send(json.dumps(ack))
12190+ except Exception:
12191+ pass
12192+
12193+ def _resolve_lwp_response_waiter(self, message_data: dict) -> bool:
12194+ try:
12195+ headers = message_data.get("headers", {}) if isinstance(message_data, dict) else {}
12196+ mid = str(headers.get("mid") or "")
12197+ if not mid:
12198+ return False
12199+ future = self.lwp_response_waiters.pop(mid, None)
12200+ if not future:
12201+ return False
12202+ if not future.done():
12203+ future.set_result(message_data)
12204+ return True
12205+ except Exception as e:
12206+ logger.debug(f"【{self.cookie_id}】分发LWP响应失败: {self._safe_str(e)}")
12207+ return False
12208+
12209+ async def _request_lwp_on_current_ws(self, lwp: str, body: list, timeout: int = 10):
12210+ ws = self.ws
12211+ if not ws or getattr(ws, 'closed', False):
12212+ return {"code": "not_connected", "reason": "账号WebSocket未连接"}
12213+
12214+ mid = generate_mid()
12215+ loop = asyncio.get_running_loop()
12216+ future = loop.create_future()
12217+ self.lwp_response_waiters[mid] = future
12218+ try:
12219+ await ws.send(json.dumps({
12220+ "lwp": lwp,
12221+ "headers": {"mid": mid},
12222+ "body": body,
12223+ }))
12224+ response = await asyncio.wait_for(future, timeout=max(1, int(timeout or 10)))
12225+ return response.get("body", {}) if isinstance(response, dict) else {}
12226+ except asyncio.TimeoutError:
12227+ return {"code": "timeout", "reason": "IM请求超时"}
12228+ except Exception as e:
12229+ return {"code": "request_failed", "reason": self._safe_str(e)}
12230+ finally:
12231+ current = self.lwp_response_waiters.pop(mid, None)
12232+ if current and not current.done():
12233+ current.cancel()
12234+
1217212235 async def list_all_conversations(self, cid: str, page_size: int = 20):
1217312236 """拉取指定会话的历史消息。"""
1217412237 logger.info(f"【{self.cookie_id}】开始通过独立临时连接拉取历史消息: chat_id={cid}, page_size={page_size}")
@@ -12291,6 +12354,85 @@ async def list_all_conversations(self, cid: str, page_size: int = 20):
1229112354
1229212355 return []
1229312356
12357+ async def list_newest_conversations(self, start_timestamp: int = None, limit: int = 20):
12358+ """通过当前主WebSocket拉取最近会话列表,避免临时连接挤掉主监听。"""
12359+ if start_timestamp in (None, '', 0, '0'):
12360+ start_timestamp = 9007199254740991
12361+
12362+ try:
12363+ start_timestamp = int(start_timestamp)
12364+ except (TypeError, ValueError):
12365+ start_timestamp = 9007199254740991
12366+
12367+ try:
12368+ limit = max(1, min(int(limit or 20), 100))
12369+ except (TypeError, ValueError):
12370+ limit = 20
12371+
12372+ logger.info(
12373+ f"【{self.cookie_id}】开始通过主连接拉取最近会话: "
12374+ f"cursor={start_timestamp}, limit={limit}"
12375+ )
12376+
12377+ last_body = {}
12378+ for attempt in range(3):
12379+ body = await self._request_lwp_on_current_ws(
12380+ "/r/Conversation/listNewestPagination",
12381+ [start_timestamp, limit],
12382+ timeout=10,
12383+ )
12384+ last_body = body if isinstance(body, dict) else {}
12385+ if isinstance(last_body, dict) and last_body.get("code") == "400600001" and attempt < 2:
12386+ wait_seconds = (attempt + 1) * 2
12387+ logger.warning(
12388+ f"【{self.cookie_id}】最近会话拉取被流控,{wait_seconds}秒后重试 "
12389+ f"({attempt + 1}/3)"
12390+ )
12391+ await asyncio.sleep(wait_seconds)
12392+ continue
12393+ logger.info(
12394+ f"【{self.cookie_id}】最近会话拉取完成: "
12395+ f"count={len(last_body.get('userConvs', [])) if isinstance(last_body, dict) else 0}"
12396+ )
12397+ return last_body
12398+
12399+ return last_body
12400+
12401+ async def list_conversation_messages_page(self, cid: str, start_timestamp: int = None, limit: int = 20):
12402+ """通过当前主WebSocket分页拉取指定会话消息,避免临时连接挤掉主监听。"""
12403+ normalized_cid = str(cid or '').strip()
12404+ if not normalized_cid:
12405+ return {"code": "missing_cid", "reason": "缺少会话ID"}
12406+ full_cid = normalized_cid if '@goofish' in normalized_cid else f"{normalized_cid}@goofish"
12407+
12408+ if start_timestamp in (None, '', 0, '0'):
12409+ start_timestamp = 9007199254740991
12410+
12411+ try:
12412+ start_timestamp = int(start_timestamp)
12413+ except (TypeError, ValueError):
12414+ start_timestamp = 9007199254740991
12415+
12416+ try:
12417+ limit = max(1, min(int(limit or 20), 100))
12418+ except (TypeError, ValueError):
12419+ limit = 20
12420+
12421+ logger.info(
12422+ f"【{self.cookie_id}】开始通过主连接分页拉取消息: "
12423+ f"chat_id={normalized_cid}, cursor={start_timestamp}, limit={limit}"
12424+ )
12425+ body = await self._request_lwp_on_current_ws(
12426+ "/r/MessageManager/listUserMessages",
12427+ [full_cid, False, start_timestamp, limit, False],
12428+ timeout=10,
12429+ )
12430+ logger.info(
12431+ f"【{self.cookie_id}】分页消息拉取完成: "
12432+ f"chat_id={normalized_cid}, count={len(body.get('userMessageModels', [])) if isinstance(body, dict) else 0}"
12433+ )
12434+ return body if isinstance(body, dict) else {}
12435+
1229412436 async def fetch_conversation_history_once(self, cid: str, page_size: int = 20):
1229512437 """使用独立临时实例拉取历史消息,避免影响主连接状态。"""
1229612438 isolated_live = XianyuLive(
@@ -16080,6 +16222,10 @@ async def main(self):
1608016222 async for message in websocket:
1608116223 try:
1608216224 message_data = json.loads(message)
16225+
16226+ if self._resolve_lwp_response_waiter(message_data):
16227+ await self._ack_lwp_message(websocket, message_data)
16228+ continue
1608316229
1608416230 # 提取消息标识用于日志追踪(防止异步处理导致日志混乱)
1608516231 msg_id = "unknown"
0 commit comments