test weichat

This commit is contained in:
MuXinCG
2026-02-15 13:33:59 +08:00
parent 61cf79e525
commit 123a5de179
7 changed files with 336 additions and 11 deletions
-2
View File
@@ -179,7 +179,6 @@ prompt_refs = {
base_middleware = [
create_memory_middleware(MEMORY_DIR, extraction_model=chat_model),
create_skills_middleware(backend),
]
# Default agent (no checkpointer) — used by langgraph dev / LangSmith / notebooks.
@@ -247,7 +246,6 @@ def create_cli_agent(workspace_dir: str | None = None, checkpointer=None):
mw = [
create_memory_middleware(MEMORY_DIR, extraction_model=chat_model),
create_skills_middleware(be),
]
# Re-load MCP tools from current config (picks up /mcp add changes)
+1 -1
View File
@@ -275,7 +275,7 @@ class InboundConsumer:
await channel.start_typing(msg.chat_id)
async for event in _timeout_aiter(
stream_agent_events(self.agent, msg.content, thread_id),
stream_agent_events(self.agent, msg.content, thread_id, media=msg.media or None),
self._inference_timeout,
):
event_type = event.get("type")
+72 -3
View File
@@ -21,6 +21,7 @@ TOKEN_URL = "https://api.dingtalk.com/v1.0/oauth2/accessToken"
SEND_URL = "https://api.dingtalk.com/v1.0/robot/oToMessages/batchSend"
MEDIA_SEND_URL = "https://api.dingtalk.com/v1.0/robot/oToMessages/batchSend"
MEDIA_UPLOAD_URL = "https://oapi.dingtalk.com/media/upload"
FILE_DOWNLOAD_URL = "https://api.dingtalk.com/v1.0/robot/messageFiles/download"
@dataclass
@@ -67,6 +68,23 @@ class DingTalkChannel(Channel, WebSocketMixin, TokenMixin):
resp = await self._http_client.post(url, json=body, headers=headers)
return resp.json()
async def _resolve_download_code(self, download_code: str) -> str | None:
"""Exchange a DingTalk downloadCode for a real download URL."""
try:
token = await self._ensure_token()
data = await self._api_post(
FILE_DOWNLOAD_URL,
{"downloadCode": download_code, "robotCode": self.config.client_id},
headers={"x-acs-dingtalk-access-token": token},
)
url = data.get("downloadUrl") or ""
if url:
return url
logger.warning(f"DingTalk downloadCode resolve failed: {data}")
except Exception as e:
logger.warning(f"DingTalk downloadCode resolve error: {e}")
return None
# ── WebSocketMixin ────────────────────────────────────────────
async def _get_ws_url(self) -> str:
@@ -103,19 +121,67 @@ class DingTalkChannel(Channel, WebSocketMixin, TokenMixin):
payload = json.loads(payload) if isinstance(payload, str) else payload
text_obj = payload.get("text", {})
content = (text_obj.get("content", "") if isinstance(text_obj, dict) else str(text_obj)).strip()
content = content or payload.get("content", "").strip()
if not content:
return
raw_content = payload.get("content", "")
content = raw_content.strip() if isinstance(raw_content, str) else ""
# Download attachments if present
annotations: list[str] = []
media_paths: list[str] = []
# DingTalk file/image messages may put download info in
# payload["content"] (as a dict) instead of in a dedicated
# "fileContent"/"imageContent" key.
raw_content_obj = payload.get("content")
if isinstance(raw_content_obj, dict) and raw_content_obj not in [
payload.get(k) for k in ("imageContent", "fileContent", "videoContent", "audioContent")
]:
msg_type = payload.get("msgtype") or payload.get("msgType") or ""
media_label = msg_type or "file"
file_size = raw_content_obj.get("fileSize") or raw_content_obj.get("downloadSize") or 0
file_name = raw_content_obj.get("fileName") or raw_content_obj.get("name") or f"dingtalk_{msg_type}"
download_code = raw_content_obj.get("downloadCode") or ""
download_url = raw_content_obj.get("downloadUrl") or ""
# downloadCode is NOT a URL — resolve it via DingTalk API first
if download_code and not download_code.startswith("http"):
resolved = await self._resolve_download_code(download_code)
if resolved:
download_url = resolved
elif download_code:
download_url = download_code
if download_url:
try:
dl_token = await self._ensure_token()
dl_headers = {"x-acs-dingtalk-access-token": dl_token}
except Exception:
dl_headers = None
local, ann = await self._download_attachment(
download_url, f"dingtalk_{file_name}",
headers=dl_headers,
file_size=int(file_size) if file_size else None,
)
if local:
media_paths.append(local)
if ann:
ann = ann.replace("[attachment:", f"[{media_label}:")
annotations.append(ann)
elif file_name:
annotations.append(f"[{media_label}: {file_name}]")
for att_key in ("imageContent", "fileContent", "videoContent", "audioContent"):
att = payload.get(att_key)
if att and isinstance(att, dict):
file_size = att.get("fileSize") or att.get("downloadSize") or 0
file_name = att.get("fileName", att_key)
download_url = att.get("downloadCode") or att.get("downloadUrl") or ""
download_code = att.get("downloadCode") or ""
download_url = att.get("downloadUrl") or ""
# Resolve downloadCode via API if it's not a URL
if download_code and not download_code.startswith("http"):
resolved = await self._resolve_download_code(download_code)
if resolved:
download_url = resolved
elif download_code:
download_url = download_code
# DingTalk audioContent is voice messages
media_label = "voice" if att_key == "audioContent" else att_key
if download_url and (self.config.include_attachments if hasattr(self.config, 'include_attachments') else True):
@@ -142,6 +208,9 @@ class DingTalkChannel(Channel, WebSocketMixin, TokenMixin):
else:
annotations.append(f"[{media_label}: {file_name}]")
if not content and not media_paths and not annotations:
return
sender_id = payload.get("senderStaffId") or payload.get("senderId", "")
conv_id = payload.get("conversationId", "")
is_group = payload.get("conversationType") == "2"
+14 -4
View File
@@ -124,14 +124,24 @@ class EmailChannel(Channel, PollingMixin):
attachments = []
if msg.is_multipart():
for part in msg.walk():
content_disp = str(part.get("Content-Disposition", ""))
content_disp = part.get("Content-Disposition") or ""
content_type = part.get_content_type() or ""
is_attachment = "attachment" in content_disp
is_attachment = "attachment" in content_disp.lower()
is_inline_image = (
"inline" in content_disp
"inline" in content_disp.lower()
and content_type.startswith("image/")
)
if is_attachment or is_inline_image:
# Also detect non-text parts with a filename but no
# Content-Disposition header (common for PDFs, docs,
# etc. sent by some email clients).
is_named_file = (
not is_attachment
and not is_inline_image
and part.get_filename()
and not content_type.startswith("multipart/")
and not content_type.startswith("text/")
)
if is_attachment or is_inline_image or is_named_file:
filename = part.get_filename() or "attachment"
filename = _decode_hdr(filename)
payload_data = part.get_payload(decode=True)
+18
View File
@@ -316,6 +316,7 @@ class WeChatChannel(Channel, WebhookMixin, TokenMixin):
except Exception:
return web.Response(status=400)
logger.info(f"WeChat callback POST received, body length={len(body)}")
xml_data = parse_xml(body)
# If encrypted, decrypt first
@@ -351,6 +352,8 @@ class WeChatChannel(Channel, WebhookMixin, TokenMixin):
msg_id = xml_data.get("MsgId", "")
create_time = xml_data.get("CreateTime", "")
logger.info(f"WeChat message received: type={msg_type}, from={from_user}, id={msg_id}, keys={list(xml_data.keys())}")
if not from_user:
return
@@ -431,6 +434,21 @@ class WeChatChannel(Channel, WebhookMixin, TokenMixin):
lat = xml_data.get("Location_X", "")
lon = xml_data.get("Location_Y", "")
text = f"[位置] {label} ({lat}, {lon})"
elif msg_type == "file":
media_id = xml_data.get("MediaId", "")
file_name = xml_data.get("Title", f"wechat_file_{msg_id}")
logger.info(f"WeChat file message: name={file_name}, media_id={media_id!r}, keys={list(xml_data.keys())}")
if media_id:
local, ann = await self._download_wechat_media(
media_id, f"wechat_file_{msg_id}_{file_name}",
)
logger.info(f"WeChat file download result: local={local}, ann={ann}")
if local:
media_paths.append(local)
if ann:
annotations.append(ann)
if not media_paths:
annotations.append(f"[file: {file_name}]")
elif msg_type == "link":
title = xml_data.get("Title", "")
description = xml_data.get("Description", "")
+38 -1
View File
@@ -4,6 +4,9 @@ Async generator that streams events from an agent graph,
plus helpers for processing AI message chunks and tool results.
"""
import base64
import mimetypes
import os
from typing import Any, AsyncIterator
from langchain_core.messages import AIMessage, AIMessageChunk # type: ignore[import-untyped]
@@ -64,6 +67,7 @@ async def stream_agent_events(
message: str,
thread_id: str,
metadata: dict | None = None,
media: list[str] | None = None,
) -> AsyncIterator[dict]:
"""Stream events from the agent graph using async iteration.
@@ -75,6 +79,7 @@ async def stream_agent_events(
thread_id: Thread ID for conversation persistence
metadata: Optional metadata dict merged into the LangGraph config
(e.g. agent_name, updated_at for checkpoint persistence).
media: Optional list of local file paths for attachments.
Yields:
Event dicts: thinking, text, tool_call, tool_result,
@@ -247,9 +252,41 @@ async def stream_agent_events(
# 4) No real names available yet -- return generic WITHOUT caching
return "sub-agent"
# Build user message content: text + inline images + file path references
user_content: str | list[dict[str, Any]] = message
if media:
_IMAGE_EXTS = frozenset({".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp"})
_MAX_INLINE_SIZE = 5 * 1024 * 1024 # 5 MB
content_blocks: list[dict[str, Any]] = []
if message:
content_blocks.append({"type": "text", "text": message})
file_refs: list[str] = []
for path in media:
ext = os.path.splitext(path)[1].lower()
if ext in _IMAGE_EXTS and os.path.isfile(path):
fsize = os.path.getsize(path)
if fsize <= _MAX_INLINE_SIZE:
mime = mimetypes.guess_type(path)[0] or "image/png"
with open(path, "rb") as fh:
b64 = base64.b64encode(fh.read()).decode("ascii")
content_blocks.append({"type": "image_url", "image_url": {
"url": f"data:{mime};base64,{b64}",
}})
else:
file_refs.append(path)
else:
file_refs.append(path)
if file_refs:
ref_text = "\n".join(
f"[attached file: {os.path.basename(p)}] path: {p}" for p in file_refs
)
content_blocks.append({"type": "text", "text": ref_text})
if content_blocks:
user_content = content_blocks
try:
async for chunk in agent.astream(
{"messages": [{"role": "user", "content": message}]},
{"messages": [{"role": "user", "content": user_content}]},
config=config,
stream_mode="messages",
subgraphs=True,
+193
View File
@@ -0,0 +1,193 @@
2026-02-15 02:24:12,880 [INFO] (client.py:162)_bot_login [botpy] 登录机器人账号中...
2026-02-15 02:24:13,074 [INFO] (robot.py:65)update_access_token [botpy] access_token expires_in 2629
2026-02-15 02:24:13,559 [INFO] (client.py:181)_bot_init [botpy] 程序启动...
2026-02-15 02:24:13,559 [INFO] (connection.py:60)multi_run [botpy] 最大并发连接数: 1, 启动会话数: 1
2026-02-15 02:24:13,559 [INFO] (client.py:242)bot_connect [botpy] 会话启动中...
2026-02-15 02:24:13,560 [INFO] (gateway.py:115)ws_connect [botpy] 启动中...
2026-02-15 02:24:13,751 [INFO] (gateway.py:142)ws_identify [botpy] 鉴权中...
2026-02-15 02:24:13,885 [INFO] (gateway.py:85)on_message [botpy] 机器人「EvoSci-测试中」启动成功!
2026-02-15 02:24:13,886 [INFO] (gateway.py:223)_send_heart [botpy] 心跳维持启动...
2026-02-15 02:30:47,988 [INFO] (client.py:162)_bot_login [botpy] 登录机器人账号中...
2026-02-15 02:30:48,188 [INFO] (robot.py:65)update_access_token [botpy] access_token expires_in 2234
2026-02-15 02:30:48,703 [INFO] (client.py:181)_bot_init [botpy] 程序启动...
2026-02-15 02:30:48,704 [INFO] (connection.py:60)multi_run [botpy] 最大并发连接数: 1, 启动会话数: 1
2026-02-15 02:30:48,704 [INFO] (client.py:242)bot_connect [botpy] 会话启动中...
2026-02-15 02:30:48,704 [INFO] (gateway.py:115)ws_connect [botpy] 启动中...
2026-02-15 02:30:48,877 [INFO] (gateway.py:142)ws_identify [botpy] 鉴权中...
2026-02-15 02:30:48,997 [INFO] (gateway.py:85)on_message [botpy] 机器人「EvoSci-测试中」启动成功!
2026-02-15 02:30:48,998 [INFO] (gateway.py:223)_send_heart [botpy] 心跳维持启动...
2026-02-15 02:36:24,277 [INFO] (client.py:162)_bot_login [botpy] 登录机器人账号中...
2026-02-15 02:36:24,464 [INFO] (robot.py:65)update_access_token [botpy] access_token expires_in 1898
2026-02-15 02:36:25,424 [INFO] (client.py:181)_bot_init [botpy] 程序启动...
2026-02-15 02:36:25,424 [INFO] (connection.py:60)multi_run [botpy] 最大并发连接数: 1, 启动会话数: 1
2026-02-15 02:36:25,425 [INFO] (client.py:242)bot_connect [botpy] 会话启动中...
2026-02-15 02:36:25,425 [INFO] (gateway.py:115)ws_connect [botpy] 启动中...
2026-02-15 02:36:25,602 [INFO] (gateway.py:142)ws_identify [botpy] 鉴权中...
2026-02-15 02:36:25,733 [INFO] (gateway.py:85)on_message [botpy] 机器人「EvoSci-测试中」启动成功!
2026-02-15 02:36:25,733 [INFO] (gateway.py:223)_send_heart [botpy] 心跳维持启动...
2026-02-15 03:06:25,660 [INFO] (gateway.py:56)on_closed [botpy] 关闭, 返回码: 4009, 返回信息: Session timed out
2026-02-15 03:06:25,660 [INFO] (gateway.py:134)ws_connect [botpy] ws关闭, 停止接收消息!
2026-02-15 03:06:30,663 [INFO] (connection.py:60)multi_run [botpy] 最大并发连接数: 1, 启动会话数: 1
2026-02-15 03:06:30,663 [INFO] (client.py:242)bot_connect [botpy] 会话启动中...
2026-02-15 03:06:30,663 [INFO] (gateway.py:115)ws_connect [botpy] 启动中...
2026-02-15 03:06:30,839 [INFO] (gateway.py:175)ws_resume [botpy] 重连启动...
2026-02-15 03:06:30,934 [INFO] (gateway.py:90)on_message [botpy] 机器人重连成功!
2026-02-15 03:06:30,934 [INFO] (gateway.py:223)_send_heart [botpy] 心跳维持启动...
2026-02-15 03:36:30,778 [INFO] (gateway.py:56)on_closed [botpy] 关闭, 返回码: 4009, 返回信息: Session timed out
2026-02-15 03:36:30,780 [INFO] (gateway.py:134)ws_connect [botpy] ws关闭, 停止接收消息!
2026-02-15 03:36:35,782 [INFO] (connection.py:60)multi_run [botpy] 最大并发连接数: 1, 启动会话数: 1
2026-02-15 03:36:35,782 [INFO] (client.py:242)bot_connect [botpy] 会话启动中...
2026-02-15 03:36:35,782 [INFO] (gateway.py:115)ws_connect [botpy] 启动中...
2026-02-15 03:36:35,972 [INFO] (gateway.py:175)ws_resume [botpy] 重连启动...
2026-02-15 03:36:36,482 [INFO] (robot.py:65)update_access_token [botpy] access_token expires_in 7200
2026-02-15 03:36:36,573 [INFO] (gateway.py:90)on_message [botpy] 机器人重连成功!
2026-02-15 03:36:36,573 [INFO] (gateway.py:223)_send_heart [botpy] 心跳维持启动...
2026-02-15 04:06:35,954 [INFO] (gateway.py:56)on_closed [botpy] 关闭, 返回码: 4009, 返回信息: Session timed out
2026-02-15 04:06:35,956 [INFO] (gateway.py:134)ws_connect [botpy] ws关闭, 停止接收消息!
2026-02-15 04:06:40,958 [INFO] (connection.py:60)multi_run [botpy] 最大并发连接数: 1, 启动会话数: 1
2026-02-15 04:06:40,959 [INFO] (client.py:242)bot_connect [botpy] 会话启动中...
2026-02-15 04:06:40,959 [INFO] (gateway.py:115)ws_connect [botpy] 启动中...
2026-02-15 04:06:41,163 [INFO] (gateway.py:175)ws_resume [botpy] 重连启动...
2026-02-15 04:06:41,281 [INFO] (gateway.py:90)on_message [botpy] 机器人重连成功!
2026-02-15 04:06:41,281 [INFO] (gateway.py:223)_send_heart [botpy] 心跳维持启动...
2026-02-15 04:36:41,169 [INFO] (gateway.py:56)on_closed [botpy] 关闭, 返回码: 4009, 返回信息: Session timed out
2026-02-15 04:36:41,170 [INFO] (gateway.py:134)ws_connect [botpy] ws关闭, 停止接收消息!
2026-02-15 04:36:46,172 [INFO] (connection.py:60)multi_run [botpy] 最大并发连接数: 1, 启动会话数: 1
2026-02-15 04:36:46,173 [INFO] (client.py:242)bot_connect [botpy] 会话启动中...
2026-02-15 04:36:46,173 [INFO] (gateway.py:115)ws_connect [botpy] 启动中...
2026-02-15 04:36:46,402 [INFO] (gateway.py:175)ws_resume [botpy] 重连启动...
2026-02-15 04:36:46,499 [INFO] (gateway.py:90)on_message [botpy] 机器人重连成功!
2026-02-15 04:36:46,500 [INFO] (gateway.py:223)_send_heart [botpy] 心跳维持启动...
2026-02-15 05:06:46,456 [INFO] (gateway.py:56)on_closed [botpy] 关闭, 返回码: 4009, 返回信息: Session timed out
2026-02-15 05:06:46,457 [INFO] (gateway.py:134)ws_connect [botpy] ws关闭, 停止接收消息!
2026-02-15 05:06:51,460 [INFO] (connection.py:60)multi_run [botpy] 最大并发连接数: 1, 启动会话数: 1
2026-02-15 05:06:51,461 [INFO] (client.py:242)bot_connect [botpy] 会话启动中...
2026-02-15 05:06:51,461 [INFO] (gateway.py:115)ws_connect [botpy] 启动中...
2026-02-15 05:06:51,666 [INFO] (gateway.py:175)ws_resume [botpy] 重连启动...
2026-02-15 05:06:51,774 [INFO] (gateway.py:90)on_message [botpy] 机器人重连成功!
2026-02-15 05:06:51,775 [INFO] (gateway.py:223)_send_heart [botpy] 心跳维持启动...
2026-02-15 05:36:51,710 [INFO] (gateway.py:56)on_closed [botpy] 关闭, 返回码: 4009, 返回信息: Session timed out
2026-02-15 05:36:51,712 [INFO] (gateway.py:134)ws_connect [botpy] ws关闭, 停止接收消息!
2026-02-15 05:36:56,715 [INFO] (connection.py:60)multi_run [botpy] 最大并发连接数: 1, 启动会话数: 1
2026-02-15 05:36:56,715 [INFO] (client.py:242)bot_connect [botpy] 会话启动中...
2026-02-15 05:36:56,715 [INFO] (gateway.py:115)ws_connect [botpy] 启动中...
2026-02-15 05:36:56,922 [INFO] (gateway.py:175)ws_resume [botpy] 重连启动...
2026-02-15 05:36:57,135 [INFO] (robot.py:65)update_access_token [botpy] access_token expires_in 7200
2026-02-15 05:36:57,242 [INFO] (gateway.py:90)on_message [botpy] 机器人重连成功!
2026-02-15 05:36:57,242 [INFO] (gateway.py:223)_send_heart [botpy] 心跳维持启动...
2026-02-15 06:06:56,940 [INFO] (gateway.py:56)on_closed [botpy] 关闭, 返回码: 4009, 返回信息: Session timed out
2026-02-15 06:06:56,942 [INFO] (gateway.py:134)ws_connect [botpy] ws关闭, 停止接收消息!
2026-02-15 06:07:01,945 [INFO] (connection.py:60)multi_run [botpy] 最大并发连接数: 1, 启动会话数: 1
2026-02-15 06:07:01,945 [INFO] (client.py:242)bot_connect [botpy] 会话启动中...
2026-02-15 06:07:01,945 [INFO] (gateway.py:115)ws_connect [botpy] 启动中...
2026-02-15 06:07:02,134 [INFO] (gateway.py:175)ws_resume [botpy] 重连启动...
2026-02-15 06:07:02,241 [INFO] (gateway.py:90)on_message [botpy] 机器人重连成功!
2026-02-15 06:07:02,241 [INFO] (gateway.py:223)_send_heart [botpy] 心跳维持启动...
2026-02-15 06:37:02,157 [INFO] (gateway.py:56)on_closed [botpy] 关闭, 返回码: 4009, 返回信息: Session timed out
2026-02-15 06:37:02,158 [INFO] (gateway.py:134)ws_connect [botpy] ws关闭, 停止接收消息!
2026-02-15 06:37:07,160 [INFO] (connection.py:60)multi_run [botpy] 最大并发连接数: 1, 启动会话数: 1
2026-02-15 06:37:07,160 [INFO] (client.py:242)bot_connect [botpy] 会话启动中...
2026-02-15 06:37:07,160 [INFO] (gateway.py:115)ws_connect [botpy] 启动中...
2026-02-15 06:37:07,377 [INFO] (gateway.py:175)ws_resume [botpy] 重连启动...
2026-02-15 06:37:07,482 [INFO] (gateway.py:90)on_message [botpy] 机器人重连成功!
2026-02-15 06:37:07,483 [INFO] (gateway.py:223)_send_heart [botpy] 心跳维持启动...
2026-02-15 07:07:07,303 [INFO] (gateway.py:56)on_closed [botpy] 关闭, 返回码: 4009, 返回信息: Session timed out
2026-02-15 07:07:07,305 [INFO] (gateway.py:134)ws_connect [botpy] ws关闭, 停止接收消息!
2026-02-15 07:07:12,308 [INFO] (connection.py:60)multi_run [botpy] 最大并发连接数: 1, 启动会话数: 1
2026-02-15 07:07:12,308 [INFO] (client.py:242)bot_connect [botpy] 会话启动中...
2026-02-15 07:07:12,308 [INFO] (gateway.py:115)ws_connect [botpy] 启动中...
2026-02-15 07:07:12,494 [INFO] (gateway.py:175)ws_resume [botpy] 重连启动...
2026-02-15 07:07:12,651 [INFO] (gateway.py:90)on_message [botpy] 机器人重连成功!
2026-02-15 07:07:12,651 [INFO] (gateway.py:223)_send_heart [botpy] 心跳维持启动...
2026-02-15 07:37:12,481 [INFO] (gateway.py:56)on_closed [botpy] 关闭, 返回码: 4009, 返回信息: Session timed out
2026-02-15 07:37:12,483 [INFO] (gateway.py:134)ws_connect [botpy] ws关闭, 停止接收消息!
2026-02-15 07:37:17,485 [INFO] (connection.py:60)multi_run [botpy] 最大并发连接数: 1, 启动会话数: 1
2026-02-15 07:37:17,486 [INFO] (client.py:242)bot_connect [botpy] 会话启动中...
2026-02-15 07:37:17,486 [INFO] (gateway.py:115)ws_connect [botpy] 启动中...
2026-02-15 07:37:17,722 [INFO] (gateway.py:175)ws_resume [botpy] 重连启动...
2026-02-15 07:37:17,927 [INFO] (robot.py:65)update_access_token [botpy] access_token expires_in 7200
2026-02-15 07:37:18,044 [INFO] (gateway.py:90)on_message [botpy] 机器人重连成功!
2026-02-15 07:37:18,044 [INFO] (gateway.py:223)_send_heart [botpy] 心跳维持启动...
2026-02-15 08:07:17,716 [INFO] (gateway.py:56)on_closed [botpy] 关闭, 返回码: 4009, 返回信息: Session timed out
2026-02-15 08:07:17,718 [INFO] (gateway.py:134)ws_connect [botpy] ws关闭, 停止接收消息!
2026-02-15 08:07:22,721 [INFO] (connection.py:60)multi_run [botpy] 最大并发连接数: 1, 启动会话数: 1
2026-02-15 08:07:22,721 [INFO] (client.py:242)bot_connect [botpy] 会话启动中...
2026-02-15 08:07:22,722 [INFO] (gateway.py:115)ws_connect [botpy] 启动中...
2026-02-15 08:07:22,913 [INFO] (gateway.py:175)ws_resume [botpy] 重连启动...
2026-02-15 08:07:23,017 [INFO] (gateway.py:90)on_message [botpy] 机器人重连成功!
2026-02-15 08:07:23,018 [INFO] (gateway.py:223)_send_heart [botpy] 心跳维持启动...
2026-02-15 08:37:22,911 [INFO] (gateway.py:56)on_closed [botpy] 关闭, 返回码: 4009, 返回信息: Session timed out
2026-02-15 08:37:22,912 [INFO] (gateway.py:134)ws_connect [botpy] ws关闭, 停止接收消息!
2026-02-15 08:37:27,914 [INFO] (connection.py:60)multi_run [botpy] 最大并发连接数: 1, 启动会话数: 1
2026-02-15 08:37:27,915 [INFO] (client.py:242)bot_connect [botpy] 会话启动中...
2026-02-15 08:37:27,915 [INFO] (gateway.py:115)ws_connect [botpy] 启动中...
2026-02-15 08:37:28,153 [INFO] (gateway.py:175)ws_resume [botpy] 重连启动...
2026-02-15 08:37:28,263 [INFO] (gateway.py:90)on_message [botpy] 机器人重连成功!
2026-02-15 08:37:28,263 [INFO] (gateway.py:223)_send_heart [botpy] 心跳维持启动...
2026-02-15 09:07:28,149 [INFO] (gateway.py:56)on_closed [botpy] 关闭, 返回码: 4009, 返回信息: Session timed out
2026-02-15 09:07:28,150 [INFO] (gateway.py:134)ws_connect [botpy] ws关闭, 停止接收消息!
2026-02-15 09:07:33,152 [INFO] (connection.py:60)multi_run [botpy] 最大并发连接数: 1, 启动会话数: 1
2026-02-15 09:07:33,153 [INFO] (client.py:242)bot_connect [botpy] 会话启动中...
2026-02-15 09:07:33,153 [INFO] (gateway.py:115)ws_connect [botpy] 启动中...
2026-02-15 09:07:33,396 [INFO] (gateway.py:175)ws_resume [botpy] 重连启动...
2026-02-15 09:07:33,515 [INFO] (gateway.py:90)on_message [botpy] 机器人重连成功!
2026-02-15 09:07:33,515 [INFO] (gateway.py:223)_send_heart [botpy] 心跳维持启动...
2026-02-15 09:37:33,390 [INFO] (gateway.py:56)on_closed [botpy] 关闭, 返回码: 4009, 返回信息: Session timed out
2026-02-15 09:37:33,392 [INFO] (gateway.py:134)ws_connect [botpy] ws关闭, 停止接收消息!
2026-02-15 09:37:38,394 [INFO] (connection.py:60)multi_run [botpy] 最大并发连接数: 1, 启动会话数: 1
2026-02-15 09:37:38,394 [INFO] (client.py:242)bot_connect [botpy] 会话启动中...
2026-02-15 09:37:38,394 [INFO] (gateway.py:115)ws_connect [botpy] 启动中...
2026-02-15 09:37:38,590 [INFO] (gateway.py:175)ws_resume [botpy] 重连启动...
2026-02-15 09:37:38,807 [INFO] (robot.py:65)update_access_token [botpy] access_token expires_in 7200
2026-02-15 09:37:38,924 [INFO] (gateway.py:90)on_message [botpy] 机器人重连成功!
2026-02-15 09:37:38,924 [INFO] (gateway.py:223)_send_heart [botpy] 心跳维持启动...
2026-02-15 10:07:38,573 [INFO] (gateway.py:56)on_closed [botpy] 关闭, 返回码: 4009, 返回信息: Session timed out
2026-02-15 10:07:38,575 [INFO] (gateway.py:134)ws_connect [botpy] ws关闭, 停止接收消息!
2026-02-15 10:07:43,577 [INFO] (connection.py:60)multi_run [botpy] 最大并发连接数: 1, 启动会话数: 1
2026-02-15 10:07:43,578 [INFO] (client.py:242)bot_connect [botpy] 会话启动中...
2026-02-15 10:07:43,578 [INFO] (gateway.py:115)ws_connect [botpy] 启动中...
2026-02-15 10:07:43,746 [INFO] (gateway.py:175)ws_resume [botpy] 重连启动...
2026-02-15 10:07:43,845 [INFO] (gateway.py:90)on_message [botpy] 机器人重连成功!
2026-02-15 10:07:43,845 [INFO] (gateway.py:223)_send_heart [botpy] 心跳维持启动...
2026-02-15 10:37:43,780 [INFO] (gateway.py:56)on_closed [botpy] 关闭, 返回码: 4009, 返回信息: Session timed out
2026-02-15 10:37:43,782 [INFO] (gateway.py:134)ws_connect [botpy] ws关闭, 停止接收消息!
2026-02-15 10:37:48,783 [INFO] (connection.py:60)multi_run [botpy] 最大并发连接数: 1, 启动会话数: 1
2026-02-15 10:37:48,783 [INFO] (client.py:242)bot_connect [botpy] 会话启动中...
2026-02-15 10:37:48,783 [INFO] (gateway.py:115)ws_connect [botpy] 启动中...
2026-02-15 10:37:48,961 [INFO] (gateway.py:175)ws_resume [botpy] 重连启动...
2026-02-15 10:37:49,089 [INFO] (gateway.py:90)on_message [botpy] 机器人重连成功!
2026-02-15 10:37:49,089 [INFO] (gateway.py:223)_send_heart [botpy] 心跳维持启动...
2026-02-15 11:07:48,997 [INFO] (gateway.py:56)on_closed [botpy] 关闭, 返回码: 4009, 返回信息: Session timed out
2026-02-15 11:07:48,999 [INFO] (gateway.py:134)ws_connect [botpy] ws关闭, 停止接收消息!
2026-02-15 11:07:54,001 [INFO] (connection.py:60)multi_run [botpy] 最大并发连接数: 1, 启动会话数: 1
2026-02-15 11:07:54,002 [INFO] (client.py:242)bot_connect [botpy] 会话启动中...
2026-02-15 11:07:54,002 [INFO] (gateway.py:115)ws_connect [botpy] 启动中...
2026-02-15 11:07:54,244 [INFO] (gateway.py:175)ws_resume [botpy] 重连启动...
2026-02-15 11:07:54,353 [INFO] (gateway.py:90)on_message [botpy] 机器人重连成功!
2026-02-15 11:07:54,353 [INFO] (gateway.py:223)_send_heart [botpy] 心跳维持启动...
2026-02-15 11:37:54,235 [INFO] (gateway.py:56)on_closed [botpy] 关闭, 返回码: 4009, 返回信息: Session timed out
2026-02-15 11:37:54,236 [INFO] (gateway.py:134)ws_connect [botpy] ws关闭, 停止接收消息!
2026-02-15 11:37:59,237 [INFO] (connection.py:60)multi_run [botpy] 最大并发连接数: 1, 启动会话数: 1
2026-02-15 11:37:59,238 [INFO] (client.py:242)bot_connect [botpy] 会话启动中...
2026-02-15 11:37:59,238 [INFO] (gateway.py:115)ws_connect [botpy] 启动中...
2026-02-15 11:37:59,430 [INFO] (gateway.py:175)ws_resume [botpy] 重连启动...
2026-02-15 11:37:59,621 [INFO] (robot.py:65)update_access_token [botpy] access_token expires_in 7200
2026-02-15 11:37:59,732 [INFO] (gateway.py:90)on_message [botpy] 机器人重连成功!
2026-02-15 11:37:59,733 [INFO] (gateway.py:223)_send_heart [botpy] 心跳维持启动...
2026-02-15 12:07:59,439 [INFO] (gateway.py:56)on_closed [botpy] 关闭, 返回码: 4009, 返回信息: Session timed out
2026-02-15 12:07:59,440 [INFO] (gateway.py:134)ws_connect [botpy] ws关闭, 停止接收消息!
2026-02-15 12:08:04,442 [INFO] (connection.py:60)multi_run [botpy] 最大并发连接数: 1, 启动会话数: 1
2026-02-15 12:08:04,443 [INFO] (client.py:242)bot_connect [botpy] 会话启动中...
2026-02-15 12:08:04,443 [INFO] (gateway.py:115)ws_connect [botpy] 启动中...
2026-02-15 12:08:04,630 [INFO] (gateway.py:175)ws_resume [botpy] 重连启动...
2026-02-15 12:08:04,737 [INFO] (gateway.py:90)on_message [botpy] 机器人重连成功!
2026-02-15 12:08:04,738 [INFO] (gateway.py:223)_send_heart [botpy] 心跳维持启动...
2026-02-15 12:33:10,772 [INFO] (client.py:162)_bot_login [botpy] 登录机器人账号中...
2026-02-15 12:33:10,925 [INFO] (robot.py:65)update_access_token [botpy] access_token expires_in 3889
2026-02-15 12:33:11,815 [INFO] (client.py:181)_bot_init [botpy] 程序启动...
2026-02-15 12:33:11,815 [INFO] (connection.py:60)multi_run [botpy] 最大并发连接数: 1, 启动会话数: 1
2026-02-15 12:33:11,815 [INFO] (client.py:242)bot_connect [botpy] 会话启动中...
2026-02-15 12:33:11,816 [INFO] (gateway.py:115)ws_connect [botpy] 启动中...
2026-02-15 12:33:11,999 [INFO] (gateway.py:142)ws_identify [botpy] 鉴权中...
2026-02-15 12:33:12,145 [INFO] (gateway.py:85)on_message [botpy] 机器人「EvoSci-测试中」启动成功!
2026-02-15 12:33:12,145 [INFO] (gateway.py:223)_send_heart [botpy] 心跳维持启动...