fix(discord): 修复WebSocket连接检查并添加错误日志
refactor(config): 更新配置文件的网络和认证信息 feat(cross-platform): 为跨平台消息处理添加异常捕获和日志
This commit is contained in:
@@ -84,117 +84,117 @@ class DiscordBotWrapper:
|
|||||||
content = ""
|
content = ""
|
||||||
files = []
|
files = []
|
||||||
|
|
||||||
for node in nodes:
|
|
||||||
if node.get("type") == "node":
|
|
||||||
node_data = node.get("data", {})
|
|
||||||
node_content = node_data.get("content", [])
|
|
||||||
|
|
||||||
if isinstance(node_content, str):
|
|
||||||
import re
|
|
||||||
cq_pattern = r'\[CQ:([^,]+)(?:,([^\]]+))?\]'
|
|
||||||
matches = list(re.finditer(cq_pattern, node_content))
|
|
||||||
|
|
||||||
if not matches:
|
|
||||||
content += f"{node_content}\n"
|
|
||||||
else:
|
|
||||||
last_end = 0
|
|
||||||
for match in matches:
|
|
||||||
if match.start() > last_end:
|
|
||||||
content += node_content[last_end:match.start()]
|
|
||||||
|
|
||||||
cq_type = match.group(1)
|
|
||||||
cq_params_str = match.group(2) or ""
|
|
||||||
|
|
||||||
params = {}
|
|
||||||
if cq_params_str:
|
|
||||||
for param in cq_params_str.split(','):
|
|
||||||
if '=' in param:
|
|
||||||
k, v = param.split('=', 1)
|
|
||||||
params[k] = v
|
|
||||||
|
|
||||||
if cq_type in ("image", "video", "record"):
|
|
||||||
file_url = params.get("url") or params.get("file")
|
|
||||||
if file_url:
|
|
||||||
if str(file_url).startswith("http"):
|
|
||||||
content += f"\n{file_url}\n"
|
|
||||||
elif str(file_url).startswith("base64://"):
|
|
||||||
import base64
|
|
||||||
import io
|
|
||||||
b64_data = str(file_url)[9:]
|
|
||||||
if b64_data.startswith("data:image") or b64_data.startswith("data:audio") or b64_data.startswith("data:video"):
|
|
||||||
b64_data = b64_data.split(",", 1)[1]
|
|
||||||
try:
|
|
||||||
file_bytes = base64.b64decode(b64_data)
|
|
||||||
filename = "file.png" if cq_type == "image" else ("file.mp4" if cq_type == "video" else "file.ogg")
|
|
||||||
files.append(discord.File(fp=io.BytesIO(file_bytes), filename=filename))
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"解析 Base64 文件失败: {e}")
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
files.append(discord.File(file_url))
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"无法读取本地文件 {file_url}: {e}")
|
|
||||||
elif cq_type == "face":
|
|
||||||
# QQ 表情,简单转为文本
|
|
||||||
face_id = params.get("id")
|
|
||||||
content += f"[表情:{face_id}]"
|
|
||||||
elif cq_type == "at":
|
|
||||||
qq_id = params.get("qq")
|
|
||||||
if qq_id == "all":
|
|
||||||
content += "@everyone "
|
|
||||||
else:
|
|
||||||
content += f"<@{qq_id}> "
|
|
||||||
|
|
||||||
last_end = match.end()
|
|
||||||
|
|
||||||
if last_end < len(node_content):
|
|
||||||
content += node_content[last_end:]
|
|
||||||
content += "\n"
|
|
||||||
elif isinstance(node_content, list):
|
|
||||||
for seg in node_content:
|
|
||||||
if isinstance(seg, dict):
|
|
||||||
seg_type = seg.get("type")
|
|
||||||
seg_data = seg.get("data", {})
|
|
||||||
|
|
||||||
if seg_type == "text":
|
|
||||||
content += seg_data.get("text", "")
|
|
||||||
elif seg_type in ("image", "video", "record"):
|
|
||||||
file_url = seg_data.get("url") or seg_data.get("file")
|
|
||||||
if file_url:
|
|
||||||
if isinstance(file_url, bytes):
|
|
||||||
import io
|
|
||||||
try:
|
|
||||||
filename = "file.png" if seg_type == "image" else ("file.mp4" if seg_type == "video" else "file.ogg")
|
|
||||||
files.append(discord.File(fp=io.BytesIO(file_url), filename=filename))
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"解析 bytes 文件失败: {e}")
|
|
||||||
elif str(file_url).startswith("http"):
|
|
||||||
content += f"\n{file_url}\n"
|
|
||||||
elif str(file_url).startswith("base64://") or "data:image" in str(file_url) or "data:audio" in str(file_url) or "data:video" in str(file_url):
|
|
||||||
import base64
|
|
||||||
import io
|
|
||||||
b64_data = str(file_url)
|
|
||||||
if b64_data.startswith("base64://"):
|
|
||||||
b64_data = b64_data[9:]
|
|
||||||
if b64_data.startswith("data:image") or b64_data.startswith("data:audio") or b64_data.startswith("data:video"):
|
|
||||||
b64_data = b64_data.split(",", 1)[1]
|
|
||||||
try:
|
|
||||||
file_bytes = base64.b64decode(b64_data)
|
|
||||||
filename = "file.png" if seg_type == "image" else ("file.mp4" if seg_type == "video" else "file.ogg")
|
|
||||||
files.append(discord.File(fp=io.BytesIO(file_bytes), filename=filename))
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"解析 Base64 文件失败: {e}")
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
files.append(discord.File(file_url))
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"无法读取本地文件 {file_url}: {e}")
|
|
||||||
elif seg_type == "face":
|
|
||||||
face_id = seg_data.get("id")
|
|
||||||
content += f"[表情:{face_id}]"
|
|
||||||
content += "\n"
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
for node in nodes:
|
||||||
|
if node.get("type") == "node":
|
||||||
|
node_data = node.get("data", {})
|
||||||
|
node_content = node_data.get("content", [])
|
||||||
|
|
||||||
|
if isinstance(node_content, str):
|
||||||
|
import re
|
||||||
|
cq_pattern = r'\[CQ:([^,]+)(?:,([^\]]+))?\]'
|
||||||
|
matches = list(re.finditer(cq_pattern, node_content))
|
||||||
|
|
||||||
|
if not matches:
|
||||||
|
content += f"{node_content}\n"
|
||||||
|
else:
|
||||||
|
last_end = 0
|
||||||
|
for match in matches:
|
||||||
|
if match.start() > last_end:
|
||||||
|
content += node_content[last_end:match.start()]
|
||||||
|
|
||||||
|
cq_type = match.group(1)
|
||||||
|
cq_params_str = match.group(2) or ""
|
||||||
|
|
||||||
|
params = {}
|
||||||
|
if cq_params_str:
|
||||||
|
for param in cq_params_str.split(','):
|
||||||
|
if '=' in param:
|
||||||
|
k, v = param.split('=', 1)
|
||||||
|
params[k] = v
|
||||||
|
|
||||||
|
if cq_type in ("image", "video", "record"):
|
||||||
|
file_url = params.get("url") or params.get("file")
|
||||||
|
if file_url:
|
||||||
|
if str(file_url).startswith("http"):
|
||||||
|
content += f"\n{file_url}\n"
|
||||||
|
elif str(file_url).startswith("base64://"):
|
||||||
|
import base64
|
||||||
|
import io
|
||||||
|
b64_data = str(file_url)[9:]
|
||||||
|
if b64_data.startswith("data:image") or b64_data.startswith("data:audio") or b64_data.startswith("data:video"):
|
||||||
|
b64_data = b64_data.split(",", 1)[1]
|
||||||
|
try:
|
||||||
|
file_bytes = base64.b64decode(b64_data)
|
||||||
|
filename = "file.png" if cq_type == "image" else ("file.mp4" if cq_type == "video" else "file.ogg")
|
||||||
|
files.append(discord.File(fp=io.BytesIO(file_bytes), filename=filename))
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"解析 Base64 文件失败: {e}")
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
files.append(discord.File(file_url))
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"无法读取本地文件 {file_url}: {e}")
|
||||||
|
elif cq_type == "face":
|
||||||
|
# QQ 表情,简单转为文本
|
||||||
|
face_id = params.get("id")
|
||||||
|
content += f"[表情:{face_id}]"
|
||||||
|
elif cq_type == "at":
|
||||||
|
qq_id = params.get("qq")
|
||||||
|
if qq_id == "all":
|
||||||
|
content += "@everyone "
|
||||||
|
else:
|
||||||
|
content += f"<@{qq_id}> "
|
||||||
|
|
||||||
|
last_end = match.end()
|
||||||
|
|
||||||
|
if last_end < len(node_content):
|
||||||
|
content += node_content[last_end:]
|
||||||
|
content += "\n"
|
||||||
|
elif isinstance(node_content, list):
|
||||||
|
for seg in node_content:
|
||||||
|
if isinstance(seg, dict):
|
||||||
|
seg_type = seg.get("type")
|
||||||
|
seg_data = seg.get("data", {})
|
||||||
|
|
||||||
|
if seg_type == "text":
|
||||||
|
content += seg_data.get("text", "")
|
||||||
|
elif seg_type in ("image", "video", "record"):
|
||||||
|
file_url = seg_data.get("url") or seg_data.get("file")
|
||||||
|
if file_url:
|
||||||
|
if isinstance(file_url, bytes):
|
||||||
|
import io
|
||||||
|
try:
|
||||||
|
filename = "file.png" if seg_type == "image" else ("file.mp4" if seg_type == "video" else "file.ogg")
|
||||||
|
files.append(discord.File(fp=io.BytesIO(file_url), filename=filename))
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"解析 bytes 文件失败: {e}")
|
||||||
|
elif str(file_url).startswith("http"):
|
||||||
|
content += f"\n{file_url}\n"
|
||||||
|
elif str(file_url).startswith("base64://") or "data:image" in str(file_url) or "data:audio" in str(file_url) or "data:video" in str(file_url):
|
||||||
|
import base64
|
||||||
|
import io
|
||||||
|
b64_data = str(file_url)
|
||||||
|
if b64_data.startswith("base64://"):
|
||||||
|
b64_data = b64_data[9:]
|
||||||
|
if b64_data.startswith("data:image") or b64_data.startswith("data:audio") or b64_data.startswith("data:video"):
|
||||||
|
b64_data = b64_data.split(",", 1)[1]
|
||||||
|
try:
|
||||||
|
file_bytes = base64.b64decode(b64_data)
|
||||||
|
filename = "file.png" if seg_type == "image" else ("file.mp4" if seg_type == "video" else "file.ogg")
|
||||||
|
files.append(discord.File(fp=io.BytesIO(file_bytes), filename=filename))
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"解析 Base64 文件失败: {e}")
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
files.append(discord.File(file_url))
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"无法读取本地文件 {file_url}: {e}")
|
||||||
|
elif seg_type == "face":
|
||||||
|
face_id = seg_data.get("id")
|
||||||
|
content += f"[表情:{face_id}]"
|
||||||
|
content += "\n"
|
||||||
|
|
||||||
if content or files:
|
if content or files:
|
||||||
# target is usually event, we can use event.bot.send
|
# target is usually event, we can use event.bot.send
|
||||||
if isinstance(target, GroupMessageEvent):
|
if isinstance(target, GroupMessageEvent):
|
||||||
@@ -209,6 +209,8 @@ class DiscordBotWrapper:
|
|||||||
await user.dm_channel.send(content=content, files=files if files else None)
|
await user.dm_channel.send(content=content, files=files if files else None)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"发送 Discord 合并转发消息失败: {e}")
|
logger.error(f"发送 Discord 合并转发消息失败: {e}")
|
||||||
|
import traceback
|
||||||
|
logger.error(f"异常堆栈: {traceback.format_exc()}")
|
||||||
|
|
||||||
class DiscordToOneBotConverter:
|
class DiscordToOneBotConverter:
|
||||||
"""
|
"""
|
||||||
@@ -416,45 +418,105 @@ class DiscordToOneBotConverter:
|
|||||||
content = ""
|
content = ""
|
||||||
files = []
|
files = []
|
||||||
|
|
||||||
# 统一转换为列表处理
|
try:
|
||||||
if not isinstance(message, list):
|
# 统一转换为列表处理
|
||||||
message = [message]
|
if not isinstance(message, list):
|
||||||
|
message = [message]
|
||||||
import re
|
|
||||||
|
|
||||||
for segment in message:
|
|
||||||
if isinstance(segment, str):
|
|
||||||
# 尝试解析 CQ 码
|
|
||||||
cq_pattern = r'\[CQ:([^,]+)(?:,([^\]]+))?\]'
|
|
||||||
matches = list(re.finditer(cq_pattern, segment))
|
|
||||||
|
|
||||||
if not matches:
|
import re
|
||||||
content += segment
|
|
||||||
continue
|
for segment in message:
|
||||||
|
if isinstance(segment, str):
|
||||||
|
# 尝试解析 CQ 码
|
||||||
|
cq_pattern = r'\[CQ:([^,]+)(?:,([^\]]+))?\]'
|
||||||
|
matches = list(re.finditer(cq_pattern, segment))
|
||||||
|
|
||||||
last_end = 0
|
if not matches:
|
||||||
for match in matches:
|
content += segment
|
||||||
# 添加 CQ 码之前的纯文本
|
continue
|
||||||
if match.start() > last_end:
|
|
||||||
content += segment[last_end:match.start()]
|
|
||||||
|
|
||||||
cq_type = match.group(1)
|
last_end = 0
|
||||||
cq_params_str = match.group(2) or ""
|
for match in matches:
|
||||||
|
# 添加 CQ 码之前的纯文本
|
||||||
# 解析参数
|
if match.start() > last_end:
|
||||||
params = {}
|
content += segment[last_end:match.start()]
|
||||||
if cq_params_str:
|
|
||||||
for param in cq_params_str.split(','):
|
cq_type = match.group(1)
|
||||||
if '=' in param:
|
cq_params_str = match.group(2) or ""
|
||||||
k, v = param.split('=', 1)
|
|
||||||
params[k] = v
|
# 解析参数
|
||||||
|
params = {}
|
||||||
|
if cq_params_str:
|
||||||
|
for param in cq_params_str.split(','):
|
||||||
|
if '=' in param:
|
||||||
|
k, v = param.split('=', 1)
|
||||||
|
params[k] = v
|
||||||
|
|
||||||
|
if cq_type in ("image", "video", "record"):
|
||||||
|
file_url = params.get("url") or params.get("file")
|
||||||
|
if file_url:
|
||||||
|
if str(file_url).startswith("http"):
|
||||||
|
content += f"\n{file_url}"
|
||||||
|
elif str(file_url).startswith("base64://") or "data:image" in str(file_url) or "data:audio" in str(file_url) or "data:video" in str(file_url):
|
||||||
|
import base64
|
||||||
|
import io
|
||||||
|
b64_data = str(file_url)
|
||||||
|
if b64_data.startswith("base64://"):
|
||||||
|
b64_data = b64_data[9:]
|
||||||
|
if b64_data.startswith("data:image") or b64_data.startswith("data:audio") or b64_data.startswith("data:video"):
|
||||||
|
b64_data = b64_data.split(",", 1)[1]
|
||||||
|
try:
|
||||||
|
file_bytes = base64.b64decode(b64_data)
|
||||||
|
filename = "file.png" if cq_type == "image" else ("file.mp4" if cq_type == "video" else "file.ogg")
|
||||||
|
files.append(discord.File(fp=io.BytesIO(file_bytes), filename=filename))
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"解析 Base64 文件失败: {e}")
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
files.append(discord.File(file_url))
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"无法读取本地文件 {file_url}: {e}")
|
||||||
|
elif cq_type == "face":
|
||||||
|
face_id = params.get("id")
|
||||||
|
content += f"[表情:{face_id}]"
|
||||||
|
elif cq_type == "at":
|
||||||
|
qq_id = params.get("qq")
|
||||||
|
if qq_id == "all":
|
||||||
|
content += "@everyone "
|
||||||
|
else:
|
||||||
|
content += f"<@{qq_id}> "
|
||||||
|
|
||||||
if cq_type in ("image", "video", "record"):
|
last_end = match.end()
|
||||||
file_url = params.get("url") or params.get("file")
|
|
||||||
|
# 添加最后一个 CQ 码之后的纯文本
|
||||||
|
if last_end < len(segment):
|
||||||
|
content += segment[last_end:]
|
||||||
|
|
||||||
|
elif isinstance(segment, OneBotMessageSegment):
|
||||||
|
# 解析 OneBot 的 MessageSegment
|
||||||
|
seg_type = segment.type
|
||||||
|
seg_data = segment.data
|
||||||
|
|
||||||
|
if seg_type == "text":
|
||||||
|
content += seg_data.get("text", "")
|
||||||
|
elif seg_type in ("image", "video", "record"):
|
||||||
|
# OneBot 的图片/视频/语音通常有 file (URL或本地路径) 或 url 字段
|
||||||
|
file_url = seg_data.get("url") or seg_data.get("file")
|
||||||
|
|
||||||
if file_url:
|
if file_url:
|
||||||
if str(file_url).startswith("http"):
|
# 处理 bytes 类型
|
||||||
|
if isinstance(file_url, bytes):
|
||||||
|
import io
|
||||||
|
try:
|
||||||
|
filename = "file.png" if seg_type == "image" else ("file.mp4" if seg_type == "video" else "file.ogg")
|
||||||
|
files.append(discord.File(fp=io.BytesIO(file_url), filename=filename))
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"解析 bytes 文件失败: {e}")
|
||||||
|
elif str(file_url).startswith("http"):
|
||||||
|
# 如果是网络 URL,直接拼接到文本中,Discord 会自动解析预览
|
||||||
content += f"\n{file_url}"
|
content += f"\n{file_url}"
|
||||||
elif str(file_url).startswith("base64://") or "data:image" in str(file_url) or "data:audio" in str(file_url) or "data:video" in str(file_url):
|
elif str(file_url).startswith("base64://") or "data:image" in str(file_url) or "data:audio" in str(file_url) or "data:video" in str(file_url):
|
||||||
|
# 处理 Base64 文件 (需要解码并作为文件上传)
|
||||||
import base64
|
import base64
|
||||||
import io
|
import io
|
||||||
b64_data = str(file_url)
|
b64_data = str(file_url)
|
||||||
@@ -464,91 +526,31 @@ class DiscordToOneBotConverter:
|
|||||||
b64_data = b64_data.split(",", 1)[1]
|
b64_data = b64_data.split(",", 1)[1]
|
||||||
try:
|
try:
|
||||||
file_bytes = base64.b64decode(b64_data)
|
file_bytes = base64.b64decode(b64_data)
|
||||||
filename = "file.png" if cq_type == "image" else ("file.mp4" if cq_type == "video" else "file.ogg")
|
filename = "file.png" if seg_type == "image" else ("file.mp4" if seg_type == "video" else "file.ogg")
|
||||||
files.append(discord.File(fp=io.BytesIO(file_bytes), filename=filename))
|
files.append(discord.File(fp=io.BytesIO(file_bytes), filename=filename))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"解析 Base64 文件失败: {e}")
|
logger.error(f"解析 Base64 文件失败: {e}")
|
||||||
else:
|
else:
|
||||||
|
# 假设是本地文件路径
|
||||||
try:
|
try:
|
||||||
files.append(discord.File(file_url))
|
files.append(discord.File(file_url))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"无法读取本地文件 {file_url}: {e}")
|
logger.error(f"无法读取本地文件 {file_url}: {e}")
|
||||||
elif cq_type == "face":
|
elif seg_type == "face":
|
||||||
face_id = params.get("id")
|
face_id = seg_data.get("id")
|
||||||
content += f"[表情:{face_id}]"
|
content += f"[表情:{face_id}]"
|
||||||
elif cq_type == "at":
|
elif seg_type == "at":
|
||||||
qq_id = params.get("qq")
|
qq_id = seg_data.get("qq")
|
||||||
if qq_id == "all":
|
if qq_id == "all":
|
||||||
content += "@everyone "
|
content += "@everyone "
|
||||||
else:
|
else:
|
||||||
|
# 尝试将 QQ 号映射回 Discord ID (这里简单处理,直接拼接)
|
||||||
content += f"<@{qq_id}> "
|
content += f"<@{qq_id}> "
|
||||||
|
elif seg_type == "reply":
|
||||||
last_end = match.end()
|
# 忽略回复段,或者你可以尝试映射 message_id
|
||||||
|
pass
|
||||||
# 添加最后一个 CQ 码之后的纯文本
|
|
||||||
if last_end < len(segment):
|
|
||||||
content += segment[last_end:]
|
|
||||||
|
|
||||||
elif isinstance(segment, OneBotMessageSegment):
|
|
||||||
# 解析 OneBot 的 MessageSegment
|
|
||||||
seg_type = segment.type
|
|
||||||
seg_data = segment.data
|
|
||||||
|
|
||||||
if seg_type == "text":
|
|
||||||
content += seg_data.get("text", "")
|
|
||||||
elif seg_type in ("image", "video", "record"):
|
|
||||||
# OneBot 的图片/视频/语音通常有 file (URL或本地路径) 或 url 字段
|
|
||||||
file_url = seg_data.get("url") or seg_data.get("file")
|
|
||||||
|
|
||||||
if file_url:
|
|
||||||
# 处理 bytes 类型
|
|
||||||
if isinstance(file_url, bytes):
|
|
||||||
import io
|
|
||||||
try:
|
|
||||||
filename = "file.png" if seg_type == "image" else ("file.mp4" if seg_type == "video" else "file.ogg")
|
|
||||||
files.append(discord.File(fp=io.BytesIO(file_url), filename=filename))
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"解析 bytes 文件失败: {e}")
|
|
||||||
elif str(file_url).startswith("http"):
|
|
||||||
# 如果是网络 URL,直接拼接到文本中,Discord 会自动解析预览
|
|
||||||
content += f"\n{file_url}"
|
|
||||||
elif str(file_url).startswith("base64://") or "data:image" in str(file_url) or "data:audio" in str(file_url) or "data:video" in str(file_url):
|
|
||||||
# 处理 Base64 文件 (需要解码并作为文件上传)
|
|
||||||
import base64
|
|
||||||
import io
|
|
||||||
b64_data = str(file_url)
|
|
||||||
if b64_data.startswith("base64://"):
|
|
||||||
b64_data = b64_data[9:]
|
|
||||||
if b64_data.startswith("data:image") or b64_data.startswith("data:audio") or b64_data.startswith("data:video"):
|
|
||||||
b64_data = b64_data.split(",", 1)[1]
|
|
||||||
try:
|
|
||||||
file_bytes = base64.b64decode(b64_data)
|
|
||||||
filename = "file.png" if seg_type == "image" else ("file.mp4" if seg_type == "video" else "file.ogg")
|
|
||||||
files.append(discord.File(fp=io.BytesIO(file_bytes), filename=filename))
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"解析 Base64 文件失败: {e}")
|
|
||||||
else:
|
|
||||||
# 假设是本地文件路径
|
|
||||||
try:
|
|
||||||
files.append(discord.File(file_url))
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"无法读取本地文件 {file_url}: {e}")
|
|
||||||
elif seg_type == "face":
|
|
||||||
face_id = seg_data.get("id")
|
|
||||||
content += f"[表情:{face_id}]"
|
|
||||||
elif seg_type == "at":
|
|
||||||
qq_id = seg_data.get("qq")
|
|
||||||
if qq_id == "all":
|
|
||||||
content += "@everyone "
|
|
||||||
else:
|
|
||||||
# 尝试将 QQ 号映射回 Discord ID (这里简单处理,直接拼接)
|
|
||||||
content += f"<@{qq_id}> "
|
|
||||||
elif seg_type == "reply":
|
|
||||||
# 忽略回复段,或者你可以尝试映射 message_id
|
|
||||||
pass
|
|
||||||
|
|
||||||
# 发送消息到 Discord
|
# 发送消息到 Discord
|
||||||
try:
|
|
||||||
# 如果内容为空但有文件,Discord 允许发送
|
# 如果内容为空但有文件,Discord 允许发送
|
||||||
if content or files:
|
if content or files:
|
||||||
await channel.send(content=content, files=files if files else None)
|
await channel.send(content=content, files=files if files else None)
|
||||||
@@ -556,3 +558,5 @@ class DiscordToOneBotConverter:
|
|||||||
logger.warning("尝试发送空消息到 Discord,已拦截")
|
logger.warning("尝试发送空消息到 Discord,已拦截")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"发送 Discord 消息失败: {e}")
|
logger.error(f"发送 Discord 消息失败: {e}")
|
||||||
|
import traceback
|
||||||
|
logger.error(f"异常堆栈: {traceback.format_exc()}")
|
||||||
|
|||||||
@@ -51,195 +51,205 @@ async def handle_qq_message(
|
|||||||
@matcher.on_message()
|
@matcher.on_message()
|
||||||
async def handle_qq_group_message(event: GroupMessageEvent):
|
async def handle_qq_group_message(event: GroupMessageEvent):
|
||||||
"""处理 QQ 群消息,转发到 Discord"""
|
"""处理 QQ 群消息,转发到 Discord"""
|
||||||
if not config.ENABLE_CROSS_PLATFORM:
|
|
||||||
return
|
|
||||||
|
|
||||||
group_id = event.group_id
|
|
||||||
mapped_channel = None
|
|
||||||
for discord_channel_id, info in config.CROSS_PLATFORM_MAP.items():
|
|
||||||
if info["qq_group_id"] == group_id:
|
|
||||||
mapped_channel = discord_channel_id
|
|
||||||
break
|
|
||||||
|
|
||||||
if mapped_channel is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
content = ""
|
|
||||||
attachments = []
|
|
||||||
|
|
||||||
if isinstance(event.message, list):
|
|
||||||
has_forward_node = any(isinstance(seg, MessageSegment) and seg.type == "node" for seg in event.message)
|
|
||||||
|
|
||||||
if has_forward_node:
|
|
||||||
forward_nodes = [seg for seg in event.message if isinstance(seg, MessageSegment) and seg.type == "node"]
|
|
||||||
forward_nodes_dict = [{"type": seg.type, "data": seg.data} for seg in forward_nodes]
|
|
||||||
content, attachments = await parse_forward_nodes(forward_nodes_dict)
|
|
||||||
else:
|
|
||||||
for segment in event.message:
|
|
||||||
if isinstance(segment, MessageSegment):
|
|
||||||
if segment.type == "text":
|
|
||||||
content += segment.data.get("text", "")
|
|
||||||
elif segment.type == "image":
|
|
||||||
file_url = segment.data.get("url") or segment.data.get("file")
|
|
||||||
file_name = segment.data.get("filename")
|
|
||||||
if file_url:
|
|
||||||
file_url = html.unescape(str(file_url))
|
|
||||||
if not file_name:
|
|
||||||
file_name = os.path.basename(file_url.split('?')[0]) or f"image_{len(attachments)}.jpg"
|
|
||||||
attachments.append({"type": "image", "url": file_url, "filename": file_name})
|
|
||||||
elif segment.type == "video":
|
|
||||||
file_url = segment.data.get("url") or segment.data.get("file")
|
|
||||||
file_name = segment.data.get("filename")
|
|
||||||
if file_url:
|
|
||||||
file_url = html.unescape(str(file_url))
|
|
||||||
if not file_name:
|
|
||||||
file_name = os.path.basename(file_url.split('?')[0]) or f"video_{len(attachments)}.mp4"
|
|
||||||
attachments.append({"type": "video", "url": file_url, "filename": file_name})
|
|
||||||
elif segment.type == "record":
|
|
||||||
file_url = segment.data.get("url") or segment.data.get("file")
|
|
||||||
file_name = segment.data.get("filename")
|
|
||||||
if file_url:
|
|
||||||
file_url = html.unescape(str(file_url))
|
|
||||||
if not file_name:
|
|
||||||
file_name = os.path.basename(file_url.split('?')[0]) or f"record_{len(attachments)}.amr"
|
|
||||||
attachments.append({"type": "record", "url": file_url, "filename": file_name})
|
|
||||||
content += f"\n[语音: {file_name}]\n"
|
|
||||||
elif segment.type == "file":
|
|
||||||
file_url = segment.data.get("url") or segment.data.get("file")
|
|
||||||
file_name = segment.data.get("filename")
|
|
||||||
if file_url:
|
|
||||||
file_url = html.unescape(str(file_url))
|
|
||||||
if not file_name:
|
|
||||||
file_name = os.path.basename(file_url.split('?')[0]) or f"file_{len(attachments)}"
|
|
||||||
attachments.append({"type": "file", "url": file_url, "filename": file_name})
|
|
||||||
content += f"\n[文件: {file_name}]\n"
|
|
||||||
logger.debug(f"[CrossPlatform] QQ 消息识别到文件: {file_name}, URL: {file_url}")
|
|
||||||
elif segment.type == "at":
|
|
||||||
qq_id = segment.data.get("qq")
|
|
||||||
if qq_id and qq_id != "all":
|
|
||||||
content += f"@{qq_id} "
|
|
||||||
elif qq_id == "all":
|
|
||||||
content += "@所有人 "
|
|
||||||
elif isinstance(segment, str):
|
|
||||||
content += segment
|
|
||||||
elif isinstance(event.message, str):
|
|
||||||
content = event.message
|
|
||||||
|
|
||||||
import re
|
|
||||||
local_file_pattern = r'(http://[\w\.-]+:\d+/download\?id=file_[a-zA-Z0-9_]+)'
|
|
||||||
matches = re.finditer(local_file_pattern, content)
|
|
||||||
for match in matches:
|
|
||||||
file_url = match.group(1)
|
|
||||||
file_name = f"video_{len(attachments)}.mp4"
|
|
||||||
attachments.append({"type": "video", "url": file_url, "filename": file_name})
|
|
||||||
|
|
||||||
content = content.strip()
|
|
||||||
|
|
||||||
group_name = ""
|
|
||||||
try:
|
try:
|
||||||
group_info = await event.bot.get_group_info(event.group_id)
|
if not config.ENABLE_CROSS_PLATFORM:
|
||||||
group_name = group_info.get("group_name", "")
|
return
|
||||||
except Exception:
|
|
||||||
group_name = f"群{group_id}"
|
group_id = event.group_id
|
||||||
|
mapped_channel = None
|
||||||
await handle_qq_message(
|
for discord_channel_id, info in config.CROSS_PLATFORM_MAP.items():
|
||||||
nickname=event.sender.nickname or event.sender.card or str(event.user_id),
|
if info["qq_group_id"] == group_id:
|
||||||
user_id=event.user_id,
|
mapped_channel = discord_channel_id
|
||||||
group_name=group_name,
|
break
|
||||||
group_id=group_id,
|
|
||||||
content=content,
|
if mapped_channel is None:
|
||||||
attachments=attachments
|
return
|
||||||
)
|
|
||||||
|
content = ""
|
||||||
|
attachments = []
|
||||||
|
|
||||||
|
if isinstance(event.message, list):
|
||||||
|
has_forward_node = any(isinstance(seg, MessageSegment) and seg.type == "node" for seg in event.message)
|
||||||
|
|
||||||
|
if has_forward_node:
|
||||||
|
forward_nodes = [seg for seg in event.message if isinstance(seg, MessageSegment) and seg.type == "node"]
|
||||||
|
forward_nodes_dict = [{"type": seg.type, "data": seg.data} for seg in forward_nodes]
|
||||||
|
content, attachments = await parse_forward_nodes(forward_nodes_dict)
|
||||||
|
else:
|
||||||
|
for segment in event.message:
|
||||||
|
if isinstance(segment, MessageSegment):
|
||||||
|
if segment.type == "text":
|
||||||
|
content += segment.data.get("text", "")
|
||||||
|
elif segment.type == "image":
|
||||||
|
file_url = segment.data.get("url") or segment.data.get("file")
|
||||||
|
file_name = segment.data.get("filename")
|
||||||
|
if file_url:
|
||||||
|
file_url = html.unescape(str(file_url))
|
||||||
|
if not file_name:
|
||||||
|
file_name = os.path.basename(file_url.split('?')[0]) or f"image_{len(attachments)}.jpg"
|
||||||
|
attachments.append({"type": "image", "url": file_url, "filename": file_name})
|
||||||
|
elif segment.type == "video":
|
||||||
|
file_url = segment.data.get("url") or segment.data.get("file")
|
||||||
|
file_name = segment.data.get("filename")
|
||||||
|
if file_url:
|
||||||
|
file_url = html.unescape(str(file_url))
|
||||||
|
if not file_name:
|
||||||
|
file_name = os.path.basename(file_url.split('?')[0]) or f"video_{len(attachments)}.mp4"
|
||||||
|
attachments.append({"type": "video", "url": file_url, "filename": file_name})
|
||||||
|
elif segment.type == "record":
|
||||||
|
file_url = segment.data.get("url") or segment.data.get("file")
|
||||||
|
file_name = segment.data.get("filename")
|
||||||
|
if file_url:
|
||||||
|
file_url = html.unescape(str(file_url))
|
||||||
|
if not file_name:
|
||||||
|
file_name = os.path.basename(file_url.split('?')[0]) or f"record_{len(attachments)}.amr"
|
||||||
|
attachments.append({"type": "record", "url": file_url, "filename": file_name})
|
||||||
|
content += f"\n[语音: {file_name}]\n"
|
||||||
|
elif segment.type == "file":
|
||||||
|
file_url = segment.data.get("url") or segment.data.get("file")
|
||||||
|
file_name = segment.data.get("filename")
|
||||||
|
if file_url:
|
||||||
|
file_url = html.unescape(str(file_url))
|
||||||
|
if not file_name:
|
||||||
|
file_name = os.path.basename(file_url.split('?')[0]) or f"file_{len(attachments)}"
|
||||||
|
attachments.append({"type": "file", "url": file_url, "filename": file_name})
|
||||||
|
content += f"\n[文件: {file_name}]\n"
|
||||||
|
logger.debug(f"[CrossPlatform] QQ 消息识别到文件: {file_name}, URL: {file_url}")
|
||||||
|
elif segment.type == "at":
|
||||||
|
qq_id = segment.data.get("qq")
|
||||||
|
if qq_id and qq_id != "all":
|
||||||
|
content += f"@{qq_id} "
|
||||||
|
elif qq_id == "all":
|
||||||
|
content += "@所有人 "
|
||||||
|
elif isinstance(segment, str):
|
||||||
|
content += segment
|
||||||
|
elif isinstance(event.message, str):
|
||||||
|
content = event.message
|
||||||
|
|
||||||
|
import re
|
||||||
|
local_file_pattern = r'(http://[\w\.-]+:\d+/download\?id=file_[a-zA-Z0-9_]+)'
|
||||||
|
matches = re.finditer(local_file_pattern, content)
|
||||||
|
for match in matches:
|
||||||
|
file_url = match.group(1)
|
||||||
|
file_name = f"video_{len(attachments)}.mp4"
|
||||||
|
attachments.append({"type": "video", "url": file_url, "filename": file_name})
|
||||||
|
|
||||||
|
content = content.strip()
|
||||||
|
|
||||||
|
group_name = ""
|
||||||
|
try:
|
||||||
|
group_info = await event.bot.get_group_info(event.group_id)
|
||||||
|
group_name = group_info.get("group_name", "")
|
||||||
|
except Exception:
|
||||||
|
group_name = f"群{group_id}"
|
||||||
|
|
||||||
|
await handle_qq_message(
|
||||||
|
nickname=event.sender.nickname or event.sender.card or str(event.user_id),
|
||||||
|
user_id=event.user_id,
|
||||||
|
group_name=group_name,
|
||||||
|
group_id=group_id,
|
||||||
|
content=content,
|
||||||
|
attachments=attachments
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"[CrossPlatform] 处理 QQ 群消息失败: {e}")
|
||||||
|
import traceback
|
||||||
|
logger.error(f"[CrossPlatform] 异常堆栈: {traceback.format_exc()}")
|
||||||
|
|
||||||
@matcher.on_message()
|
@matcher.on_message()
|
||||||
async def handle_discord_message_event(event: Any):
|
async def handle_discord_message_event(event: Any):
|
||||||
"""处理 Discord 消息事件(通过适配器注入)"""
|
"""处理 Discord 消息事件(通过适配器注入)"""
|
||||||
if not config.ENABLE_CROSS_PLATFORM:
|
try:
|
||||||
return
|
if not config.ENABLE_CROSS_PLATFORM:
|
||||||
|
return
|
||||||
|
|
||||||
|
logger.debug(f"[CrossPlatform] handle_discord_message_event 触发: {event}")
|
||||||
|
if not hasattr(event, '_is_discord_message'):
|
||||||
|
logger.debug(f"[CrossPlatform] 事件没有 _is_discord_message 属性,跳过")
|
||||||
|
return
|
||||||
|
|
||||||
|
logger.debug(f"[CrossPlatform] 检测到 Discord 事件")
|
||||||
|
discord_channel_id = getattr(event, 'discord_channel_id', None)
|
||||||
|
if discord_channel_id is None:
|
||||||
|
logger.debug(f"[CrossPlatform] discord_channel_id 为 None")
|
||||||
|
return
|
||||||
|
|
||||||
|
content = ""
|
||||||
|
attachments = []
|
||||||
|
|
||||||
logger.debug(f"[CrossPlatform] handle_discord_message_event 触发: {event}")
|
logger.debug(f"[CrossPlatform] 开始处理 Discord 事件消息: channel_id={discord_channel_id}")
|
||||||
if not hasattr(event, '_is_discord_message'):
|
|
||||||
logger.debug(f"[CrossPlatform] 事件没有 _is_discord_message 属性,跳过")
|
|
||||||
return
|
|
||||||
|
|
||||||
logger.debug(f"[CrossPlatform] 检测到 Discord 事件")
|
if hasattr(event, 'message') and isinstance(event.message, list):
|
||||||
discord_channel_id = getattr(event, 'discord_channel_id', None)
|
has_text_content = False
|
||||||
if discord_channel_id is None:
|
for segment in event.message:
|
||||||
logger.debug(f"[CrossPlatform] discord_channel_id 为 None")
|
if isinstance(segment, MessageSegment):
|
||||||
return
|
if segment.type == "text":
|
||||||
|
content += segment.data.get("text", "")
|
||||||
|
has_text_content = True
|
||||||
|
elif segment.type == "image":
|
||||||
|
file_url = segment.data.get("url") or segment.data.get("file")
|
||||||
|
file_name = segment.data.get("filename")
|
||||||
|
if file_url:
|
||||||
|
file_name = file_name or os.path.basename(str(file_url).split('?')[0]) or "image"
|
||||||
|
attachment_item = {"type": "image", "url": str(file_url), "filename": file_name}
|
||||||
|
if attachment_item not in attachments:
|
||||||
|
attachments.append(attachment_item)
|
||||||
|
content += f"\n[图片: {file_name}]\n"
|
||||||
|
elif segment.type == "video":
|
||||||
|
file_url = segment.data.get("url") or segment.data.get("file")
|
||||||
|
file_name = segment.data.get("filename")
|
||||||
|
if file_url:
|
||||||
|
file_name = file_name or os.path.basename(str(file_url).split('?')[0]) or "video"
|
||||||
|
attachment_item = {"type": "video", "url": str(file_url), "filename": file_name}
|
||||||
|
if attachment_item not in attachments:
|
||||||
|
attachments.append(attachment_item)
|
||||||
|
content += f"\n[视频: {file_name}]\n"
|
||||||
|
elif segment.type == "record":
|
||||||
|
file_url = segment.data.get("url") or segment.data.get("file")
|
||||||
|
file_name = segment.data.get("filename")
|
||||||
|
if file_url:
|
||||||
|
file_name = file_name or os.path.basename(str(file_url).split('?')[0]) or "record"
|
||||||
|
attachment_item = {"type": "record", "url": str(file_url), "filename": file_name}
|
||||||
|
if attachment_item not in attachments:
|
||||||
|
attachments.append(attachment_item)
|
||||||
|
content += f"\n[语音: {file_name}]\n"
|
||||||
|
elif segment.type == "file":
|
||||||
|
file_url = segment.data.get("url") or segment.data.get("file")
|
||||||
|
file_name = segment.data.get("filename")
|
||||||
|
if file_url:
|
||||||
|
file_name = file_name or os.path.basename(str(file_url).split('?')[0]) or "file"
|
||||||
|
attachment_item = {"type": "file", "url": str(file_url), "filename": file_name}
|
||||||
|
if attachment_item not in attachments:
|
||||||
|
attachments.append(attachment_item)
|
||||||
|
content += f"\n[文件: {file_name}]\n"
|
||||||
|
logger.debug(f"[CrossPlatform] Discord 消息识别到文件: {file_name}, URL: {file_url}")
|
||||||
|
else:
|
||||||
|
content = event.raw_message or ""
|
||||||
|
|
||||||
content = ""
|
content = content.strip()
|
||||||
attachments = []
|
|
||||||
|
# 如果 content 为空但有附件(如只有表情),使用 raw_message 作为 content
|
||||||
logger.debug(f"[CrossPlatform] 开始处理 Discord 事件消息: channel_id={discord_channel_id}")
|
if not content and attachments:
|
||||||
|
content = event.raw_message or ""
|
||||||
if hasattr(event, 'message') and isinstance(event.message, list):
|
|
||||||
has_text_content = False
|
logger.debug(f"[CrossPlatform] Discord 消息内容: '{content}', 附件数量: {len(attachments)}")
|
||||||
for segment in event.message:
|
|
||||||
if isinstance(segment, MessageSegment):
|
discord_username = getattr(event, 'discord_username', 'Unknown')
|
||||||
if segment.type == "text":
|
discord_discriminator = getattr(event, 'discord_discriminator', '')
|
||||||
content += segment.data.get("text", "")
|
|
||||||
has_text_content = True
|
logger.debug(f"[CrossPlatform] 调用 handle_discord_message: username={discord_username}, channel_id={discord_channel_id}")
|
||||||
elif segment.type == "image":
|
await handle_discord_message(
|
||||||
file_url = segment.data.get("url") or segment.data.get("file")
|
username=discord_username,
|
||||||
file_name = segment.data.get("filename")
|
discriminator=discord_discriminator,
|
||||||
if file_url:
|
content=content,
|
||||||
file_name = file_name or os.path.basename(str(file_url).split('?')[0]) or "image"
|
channel_id=discord_channel_id,
|
||||||
attachment_item = {"type": "image", "url": str(file_url), "filename": file_name}
|
attachments=attachments,
|
||||||
if attachment_item not in attachments:
|
embed=None
|
||||||
attachments.append(attachment_item)
|
)
|
||||||
content += f"\n[图片: {file_name}]\n"
|
except Exception as e:
|
||||||
elif segment.type == "video":
|
logger.error(f"[CrossPlatform] 处理 Discord 消息事件失败: {e}")
|
||||||
file_url = segment.data.get("url") or segment.data.get("file")
|
import traceback
|
||||||
file_name = segment.data.get("filename")
|
logger.error(f"[CrossPlatform] 异常堆栈: {traceback.format_exc()}")
|
||||||
if file_url:
|
|
||||||
file_name = file_name or os.path.basename(str(file_url).split('?')[0]) or "video"
|
|
||||||
attachment_item = {"type": "video", "url": str(file_url), "filename": file_name}
|
|
||||||
if attachment_item not in attachments:
|
|
||||||
attachments.append(attachment_item)
|
|
||||||
content += f"\n[视频: {file_name}]\n"
|
|
||||||
elif segment.type == "record":
|
|
||||||
file_url = segment.data.get("url") or segment.data.get("file")
|
|
||||||
file_name = segment.data.get("filename")
|
|
||||||
if file_url:
|
|
||||||
file_name = file_name or os.path.basename(str(file_url).split('?')[0]) or "record"
|
|
||||||
attachment_item = {"type": "record", "url": str(file_url), "filename": file_name}
|
|
||||||
if attachment_item not in attachments:
|
|
||||||
attachments.append(attachment_item)
|
|
||||||
content += f"\n[语音: {file_name}]\n"
|
|
||||||
elif segment.type == "file":
|
|
||||||
file_url = segment.data.get("url") or segment.data.get("file")
|
|
||||||
file_name = segment.data.get("filename")
|
|
||||||
if file_url:
|
|
||||||
file_name = file_name or os.path.basename(str(file_url).split('?')[0]) or "file"
|
|
||||||
attachment_item = {"type": "file", "url": str(file_url), "filename": file_name}
|
|
||||||
if attachment_item not in attachments:
|
|
||||||
attachments.append(attachment_item)
|
|
||||||
content += f"\n[文件: {file_name}]\n"
|
|
||||||
logger.debug(f"[CrossPlatform] Discord 消息识别到文件: {file_name}, URL: {file_url}")
|
|
||||||
else:
|
|
||||||
content = event.raw_message or ""
|
|
||||||
|
|
||||||
content = content.strip()
|
|
||||||
|
|
||||||
# 如果 content 为空但有附件(如只有表情),使用 raw_message 作为 content
|
|
||||||
if not content and attachments:
|
|
||||||
content = event.raw_message or ""
|
|
||||||
|
|
||||||
logger.debug(f"[CrossPlatform] Discord 消息内容: '{content}', 附件数量: {len(attachments)}")
|
|
||||||
|
|
||||||
discord_username = getattr(event, 'discord_username', 'Unknown')
|
|
||||||
discord_discriminator = getattr(event, 'discord_discriminator', '')
|
|
||||||
|
|
||||||
logger.debug(f"[CrossPlatform] 调用 handle_discord_message: username={discord_username}, channel_id={discord_channel_id}")
|
|
||||||
await handle_discord_message(
|
|
||||||
username=discord_username,
|
|
||||||
discriminator=discord_discriminator,
|
|
||||||
content=content,
|
|
||||||
channel_id=discord_channel_id,
|
|
||||||
attachments=attachments,
|
|
||||||
embed=None
|
|
||||||
)
|
|
||||||
|
|
||||||
@matcher.command("cross_config", "跨平台配置", permission=Permission.ADMIN)
|
@matcher.command("cross_config", "跨平台配置", permission=Permission.ADMIN)
|
||||||
async def cross_config_command(event: MessageEvent):
|
async def cross_config_command(event: MessageEvent):
|
||||||
|
|||||||
@@ -88,6 +88,9 @@ class DiscordAdapter(discord.Client if DISCORD_AVAILABLE else object):
|
|||||||
await matcher.handle_event(mock_event.bot, mock_event)
|
await matcher.handle_event(mock_event.bot, mock_event)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(f"处理 Discord 消息时发生异常: {e}")
|
self.logger.error(f"处理 Discord 消息时发生异常: {e}")
|
||||||
|
# 记录详细的异常信息
|
||||||
|
import traceback
|
||||||
|
self.logger.error(f"异常堆栈: {traceback.format_exc()}")
|
||||||
|
|
||||||
async def start_redis_subscription(self):
|
async def start_redis_subscription(self):
|
||||||
"""启动 Redis 订阅以处理跨平台消息发送"""
|
"""启动 Redis 订阅以处理跨平台消息发送"""
|
||||||
@@ -96,7 +99,7 @@ class DiscordAdapter(discord.Client if DISCORD_AVAILABLE else object):
|
|||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
channel_name = "neobot_cross_platform"
|
channel_name = "neobot_discord_send"
|
||||||
pubsub = redis_manager.redis.pubsub()
|
pubsub = redis_manager.redis.pubsub()
|
||||||
await pubsub.subscribe(channel_name)
|
await pubsub.subscribe(channel_name)
|
||||||
|
|
||||||
@@ -386,16 +389,20 @@ class DiscordAdapter(discord.Client if DISCORD_AVAILABLE else object):
|
|||||||
"""
|
"""
|
||||||
self.logger.info(f"心跳机制已启动,间隔: {interval}秒")
|
self.logger.info(f"心跳机制已启动,间隔: {interval}秒")
|
||||||
|
|
||||||
while self.is_closed() is False:
|
while not self.is_closed():
|
||||||
try:
|
try:
|
||||||
await asyncio.sleep(interval)
|
await asyncio.sleep(interval)
|
||||||
|
|
||||||
# discord.py 的 ws 对象是 DiscordWebSocket,它没有 closed 属性
|
# 检查 WebSocket 连接状态
|
||||||
# 我们可以通过检查 self.is_closed() 或者 ws.open 来判断
|
if self.ws is not None:
|
||||||
if self.ws is not None and not getattr(self.ws, 'open', True):
|
# 正确检查 WebSocket 状态
|
||||||
self.logger.warning("检测到 WebSocket 连接已关闭,触发重连...")
|
if not getattr(self.ws, 'open', False):
|
||||||
await self.ws.close(4000)
|
self.logger.warning("检测到 WebSocket 连接已关闭,触发重连...")
|
||||||
break
|
try:
|
||||||
|
await self.ws.close(code=4000)
|
||||||
|
except Exception as close_error:
|
||||||
|
self.logger.error(f"关闭 WebSocket 连接时出错: {close_error}")
|
||||||
|
break
|
||||||
|
|
||||||
self.logger.debug(f"心跳正常: {self.user}")
|
self.logger.debug(f"心跳正常: {self.user}")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user