Skip to content

Commit 6a7b622

Browse files
authored
fix: resolve Discord/Misskey hot reload issue by fixing client_self_id misuse (#7331)
* fix: resolve Discord/Misskey hot reload issue by fixing client_self_id misuse fixes: #7187 closes: #7188 * fix: english logging
1 parent 5886c43 commit 6a7b622

File tree

7 files changed

+99
-87
lines changed

7 files changed

+99
-87
lines changed

astrbot/core/platform/sources/discord/client.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,19 +35,19 @@ def __init__(self, token: str, proxy: str | None = None) -> None:
3535
async def on_ready(self) -> None:
3636
"""当机器人成功连接并准备就绪时触发"""
3737
if self.user is None:
38-
logger.error("[Discord] 客户端未正确加载用户信息 (self.user is None)")
38+
logger.error("[Discord] Bot user not loaded correctly (self.user is None)")
3939
return
4040

41-
logger.info(f"[Discord] 已作为 {self.user} (ID: {self.user.id}) 登录")
42-
logger.info("[Discord] 客户端已准备就绪。")
41+
logger.info(f"[Discord] Logged in as {self.user} (ID: {self.user.id})")
42+
logger.info("[Discord] Client is ready.")
4343

4444
if self.on_ready_once_callback and not self._ready_once_fired:
4545
self._ready_once_fired = True
4646
try:
4747
await self.on_ready_once_callback()
4848
except Exception as e:
4949
logger.error(
50-
f"[Discord] on_ready_once_callback 执行失败: {e}",
50+
f"[Discord] Failed to execute on_ready_once_callback: {e}",
5151
exc_info=True,
5252
)
5353

@@ -99,7 +99,7 @@ async def on_message(self, message: discord.Message) -> None:
9999
return
100100

101101
logger.debug(
102-
f"[Discord] 收到原始消息 from {message.author.name}: {message.content}",
102+
f"[Discord] Received raw message from {message.author.name}: {message.content}",
103103
)
104104

105105
if self.on_message_received:

astrbot/core/platform/sources/discord/discord_platform_adapter.py

Lines changed: 67 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ def __init__(
4646
) -> None:
4747
super().__init__(platform_config, event_queue)
4848
self.settings = platform_settings
49-
self.client_self_id: str | None = None
49+
self.bot_self_id: str | None = None
5050
self.registered_handlers = []
5151
# 指令注册相关
5252
self.enable_command_register = self.config.get("discord_command_register", True)
@@ -64,7 +64,7 @@ async def send_by_session(
6464
"""通过会话发送消息"""
6565
if self.client.user is None:
6666
logger.error(
67-
"[Discord] 客户端未就绪 (self.client.user is None),无法发送消息"
67+
"[Discord] Client is not ready (self.client.user is None); message send skipped"
6868
)
6969
return
7070

@@ -92,10 +92,10 @@ async def send_by_session(
9292

9393
message_obj.message_str = message_chain.get_plain_text()
9494
message_obj.sender = MessageMember(
95-
user_id=str(self.client_self_id),
95+
user_id=str(self.bot_self_id),
9696
nickname=self.client.user.display_name,
9797
)
98-
message_obj.self_id = cast(str, self.client_self_id)
98+
message_obj.self_id = cast(str, self.bot_self_id)
9999
message_obj.session_id = session.session_id
100100
message_obj.message = message_chain.chain
101101

@@ -115,7 +115,7 @@ def meta(self) -> PlatformMetadata:
115115
"""返回平台元数据"""
116116
return PlatformMetadata(
117117
"discord",
118-
"Discord 适配器",
118+
"Discord Adapter",
119119
id=cast(str, self.config.get("id")),
120120
default_config_tmpl=self.config,
121121
support_streaming_message=False,
@@ -127,29 +127,36 @@ async def run(self) -> None:
127127

128128
# 初始化回调函数
129129
async def on_received(message_data) -> None:
130-
logger.debug(f"[Discord] 收到消息: {message_data}")
131-
if self.client_self_id is None:
132-
self.client_self_id = message_data.get("bot_id")
130+
logger.debug(f"[Discord] Message received: {message_data}")
131+
if self.bot_self_id is None:
132+
self.bot_self_id = message_data.get("bot_id")
133133
abm = await self.convert_message(data=message_data)
134134
await self.handle_msg(abm)
135135

136136
# 初始化 Discord 客户端
137137
token = str(self.config.get("discord_token"))
138138
if not token:
139-
logger.error("[Discord] Bot Token 未配置。请在配置文件中正确设置 token。")
139+
logger.error(
140+
"[Discord] Bot token is not configured. Please set a valid token in the config file."
141+
)
140142
return
141143

142144
proxy = self.config.get("discord_proxy") or None
143145
self.client = DiscordBotClient(token, proxy)
144146
self.client.on_message_received = on_received
145147

146148
async def callback() -> None:
147-
if self.enable_command_register:
148-
await self._collect_and_register_commands()
149-
if self.activity_name:
150-
await self.client.change_presence(
151-
status=discord.Status.online,
152-
activity=discord.CustomActivity(name=self.activity_name),
149+
try:
150+
if self.enable_command_register:
151+
await self._collect_and_register_commands()
152+
if self.activity_name:
153+
await self.client.change_presence(
154+
status=discord.Status.online,
155+
activity=discord.CustomActivity(name=self.activity_name),
156+
)
157+
except Exception as e:
158+
logger.error(
159+
f"[Discord] on_ready_once_callback err: {e}", exc_info=True
153160
)
154161

155162
self.client.on_ready_once_callback = callback
@@ -158,11 +165,16 @@ async def callback() -> None:
158165
self._polling_task = asyncio.create_task(self.client.start_polling())
159166
await self.shutdown_event.wait()
160167
except discord.errors.LoginFailure:
161-
logger.error("[Discord] 登录失败。请检查你的 Bot Token 是否正确。")
168+
logger.error(
169+
"[Discord] Login failed. Please check whether the bot token is correct."
170+
)
162171
except discord.errors.ConnectionClosed:
163-
logger.warning("[Discord] Discord 的连接已关闭。")
172+
logger.warning("[Discord] Connection with Discord has been closed.")
164173
except Exception as e:
165-
logger.error(f"[Discord] 适配器运行时发生意外错误: {e}", exc_info=True)
174+
logger.error(
175+
f"[Discord] Unexpected error while adapter is running: {e}",
176+
exc_info=True,
177+
)
166178

167179
def _get_message_type(
168180
self,
@@ -241,7 +253,7 @@ def _convert_message_to_abm(self, data: dict) -> AstrBotMessage:
241253
)
242254
abm.message = message_chain
243255
abm.raw_message = message
244-
abm.self_id = cast(str, self.client_self_id)
256+
abm.self_id = cast(str, self.bot_self_id)
245257
abm.session_id = str(message.channel.id)
246258
abm.message_id = str(message.id)
247259
return abm
@@ -264,7 +276,7 @@ async def handle_msg(self, message: AstrBotMessage, followup_webhook=None) -> No
264276

265277
if self.client.user is None:
266278
logger.error(
267-
"[Discord] 客户端未就绪 (self.client.user is None),无法处理消息"
279+
"[Discord] Client is not ready (self.client.user is None); message handling skipped"
268280
)
269281
return
270282

@@ -283,7 +295,7 @@ async def handle_msg(self, message: AstrBotMessage, followup_webhook=None) -> No
283295
raw_message = message.raw_message
284296
if not isinstance(raw_message, discord.Message):
285297
logger.warning(
286-
f"[Discord] 收到非 Message 类型的消息: {type(raw_message)},已忽略。"
298+
f"[Discord] Non-Message type received and ignored: {type(raw_message)}"
287299
)
288300
return
289301

@@ -324,20 +336,9 @@ async def handle_msg(self, message: AstrBotMessage, followup_webhook=None) -> No
324336

325337
@override
326338
async def terminate(self) -> None:
327-
"""终止适配器"""
328-
logger.info("[Discord] 正在终止适配器... (step 1: cancel polling task)")
339+
logger.info("[Discord] Shutting down adapter...")
329340
self.shutdown_event.set()
330-
# 优先 cancel polling_task
331-
if self._polling_task:
332-
self._polling_task.cancel()
333-
try:
334-
await asyncio.wait_for(self._polling_task, timeout=10)
335-
except asyncio.CancelledError:
336-
logger.info("[Discord] polling_task 已取消。")
337-
except Exception as e:
338-
logger.warning(f"[Discord] polling_task 取消异常: {e}")
339-
logger.info("[Discord] 正在清理已注册的斜杠指令... (step 2)")
340-
# 清理指令
341+
logger.info("[Discord] Cleaning up commands...")
341342
if self.enable_command_register and self.client:
342343
try:
343344
await asyncio.wait_for(
@@ -347,24 +348,37 @@ async def terminate(self) -> None:
347348
),
348349
timeout=10,
349350
)
350-
logger.info("[Discord] 指令清理完成。")
351+
logger.info("[Discord] Commands cleaned up successfully.")
352+
except Exception as e:
353+
logger.warning(
354+
f"[Discord] Error occurred while cleaning up commands: {e}"
355+
)
356+
357+
if self._polling_task:
358+
self._polling_task.cancel()
359+
try:
360+
await asyncio.wait_for(self._polling_task, timeout=10)
361+
except asyncio.CancelledError:
362+
logger.info("[Discord] Polling task cancelled successfully.")
351363
except Exception as e:
352-
logger.error(f"[Discord] 清理指令时发生错误: {e}", exc_info=True)
353-
logger.info("[Discord] 正在关闭 Discord 客户端... (step 3)")
364+
logger.warning(
365+
f"[Discord] Error occurred while cancelling polling task: {e}"
366+
)
367+
logger.info("[Discord] Closing client connection...")
354368
if self.client and hasattr(self.client, "close"):
355369
try:
356370
await asyncio.wait_for(self.client.close(), timeout=10)
357371
except Exception as e:
358-
logger.warning(f"[Discord] 客户端关闭异常: {e}")
359-
logger.info("[Discord] 适配器已终止。")
372+
logger.warning(f"[Discord] Error occurred while closing client: {e}")
373+
logger.info("[Discord] Adapter shutdown complete.")
360374

361375
def register_handler(self, handler_info) -> None:
362376
"""注册处理器信息"""
363377
self.registered_handlers.append(handler_info)
364378

365379
async def _collect_and_register_commands(self) -> None:
366380
"""收集所有指令并注册到Discord"""
367-
logger.info("[Discord] 开始收集并注册斜杠指令...")
381+
logger.info("[Discord] Collecting and registering slash commands...")
368382
registered_commands = []
369383

370384
for handler_md in star_handlers_registry:
@@ -405,15 +419,15 @@ async def _collect_and_register_commands(self) -> None:
405419

406420
if registered_commands:
407421
logger.info(
408-
f"[Discord] 准备同步 {len(registered_commands)} 个指令: {', '.join(registered_commands)}",
422+
f"[Discord] Ready to sync {len(registered_commands)} commands: {', '.join(registered_commands)}",
409423
)
410424
else:
411-
logger.info("[Discord] 没有发现可注册的指令。")
425+
logger.info("[Discord] No commands found for registration.")
412426

413427
# 使用 Pycord 的方法同步指令
414428
# 注意:这可能需要一些时间,并且有频率限制
415429
await self.client.sync_commands()
416-
logger.info("[Discord] 指令同步完成。")
430+
logger.info("[Discord] Command synchronization completed.")
417431

418432
def _create_dynamic_callback(self, cmd_name: str):
419433
"""为每个指令动态创建一个异步回调函数"""
@@ -422,17 +436,17 @@ async def dynamic_callback(
422436
ctx: discord.ApplicationContext, params: str | None = None
423437
) -> None:
424438
# 将平台特定的前缀'/'剥离,以适配通用的CommandFilter
425-
logger.debug(f"[Discord] 回调函数触发: {cmd_name}")
426-
logger.debug(f"[Discord] 回调函数参数: {ctx}")
427-
logger.debug(f"[Discord] 回调函数参数: {params}")
439+
logger.debug(f"[Discord] Callback triggered: {cmd_name}")
440+
logger.debug(f"[Discord] Callback context: {ctx}")
441+
logger.debug(f"[Discord] Callback params: {params}")
428442
message_str_for_filter = cmd_name
429443
if params:
430444
message_str_for_filter += f" {params}"
431445

432446
logger.debug(
433-
f"[Discord] 斜杠指令 '{cmd_name}' 被触发。 "
434-
f"原始参数: '{params}'. "
435-
f"构建的指令字符串: '{message_str_for_filter}'",
447+
f"[Discord] Slash command '{cmd_name}' triggered. "
448+
f"Raw params: '{params}'. "
449+
f"Built command string: '{message_str_for_filter}'",
436450
)
437451

438452
# 尝试立即响应,防止超时
@@ -441,7 +455,7 @@ async def dynamic_callback(
441455
await ctx.defer()
442456
followup_webhook = ctx.followup
443457
except Exception as e:
444-
logger.warning(f"[Discord] 指令 '{cmd_name}' defer 失败: {e}")
458+
logger.warning(f"[Discord] Failed to defer command '{cmd_name}': {e}")
445459

446460
# 2. 构建 AstrBotMessage
447461
channel = ctx.channel
@@ -465,7 +479,7 @@ async def dynamic_callback(
465479
)
466480
abm.message = [Plain(text=message_str_for_filter)]
467481
abm.raw_message = ctx.interaction
468-
abm.self_id = cast(str, self.client_self_id)
482+
abm.self_id = cast(str, self.bot_self_id)
469483
abm.session_id = str(ctx.channel_id)
470484
abm.message_id = str(ctx.interaction.id)
471485

@@ -503,10 +517,10 @@ def _extract_command_info(
503517

504518
# Discord 斜杠指令名称规范
505519
if not re.match(r"^[a-z0-9_-]{1,32}$", cmd_name):
506-
logger.debug(f"[Discord] 跳过不符合规范的指令: {cmd_name}")
520+
logger.debug(f"[Discord] Skipping invalid slash command format: {cmd_name}")
507521
return None
508522

509-
description = handler_metadata.desc or f"指令: {cmd_name}"
523+
description = handler_metadata.desc or f"Command: {cmd_name}"
510524
if len(description) > 100:
511525
description = f"{description[:97]}..."
512526

0 commit comments

Comments
 (0)