diff --git a/astrbot/core/platform/sources/dingtalk/dingtalk_adapter.py b/astrbot/core/platform/sources/dingtalk/dingtalk_adapter.py index 37c3b09ab..b1a8156a4 100644 --- a/astrbot/core/platform/sources/dingtalk/dingtalk_adapter.py +++ b/astrbot/core/platform/sources/dingtalk/dingtalk_adapter.py @@ -1,6 +1,7 @@ import asyncio import json import threading +import time import uuid from pathlib import Path from typing import Literal, NoReturn, cast @@ -747,18 +748,59 @@ class DingtalkPlatformAdapter(Platform): async def run(self) -> None: # await self.client_.start() # 钉钉的 SDK 并没有实现真正的异步,start() 里面有堵塞方法。 + # SDK 内部已有 while True 重连循环,但需要监控 task 状态, + # 如果 task 意外退出则重新启动。 + MAX_RETRIES = 5 + RETRY_INTERVAL = 10 + def start_client(loop: asyncio.AbstractEventLoop) -> None: - try: - self._shutdown_event = threading.Event() - task = loop.create_task(self.client_.start()) - self._shutdown_event.wait() - if task.done(): - task.result() - except Exception as e: - if "Graceful shutdown" in str(e): - logger.info("钉钉适配器已被关闭") + retry_count = 0 + + def handle_retry(error_msg: str) -> bool: + """处理重试逻辑,返回 True 表示需要继续重试,False 表示放弃。""" + nonlocal retry_count + logger.error(error_msg) + retry_count += 1 + if retry_count < MAX_RETRIES: + logger.info(f"钉钉适配器尝试重连 ({retry_count}/{MAX_RETRIES})...") + time.sleep(RETRY_INTERVAL) + return True + logger.error("钉钉适配器重连失败,已达最大重试次数") + return False + + while retry_count < MAX_RETRIES: + task = None + try: + self._shutdown_event = threading.Event() + task = loop.create_task(self.client_.start()) + # 当 task 完成时唤醒线程(无论是正常退出还是异常退出) + task.add_done_callback(lambda _: self._shutdown_event.set()) + self._shutdown_event.wait() + if task.done(): + try: + exc = task.exception() + except asyncio.CancelledError: + logger.info("钉钉适配器 task 已取消") + return + if exc: + if "Graceful shutdown" in str(exc): + logger.info("钉钉适配器已被关闭") + return + if handle_retry(f"钉钉 SDK task 异常退出: {exc}"): + continue + return + # task 仍在运行,shutdown_event 被设置(正常关闭) return - logger.error(f"钉钉机器人启动失败: {e}") + except Exception as e: + if "Graceful shutdown" in str(e): + logger.info("钉钉适配器已被关闭") + return + if not handle_retry(f"钉钉机器人启动失败: {e}"): + return + finally: + # 仅在重试/失败路径取消 task,正常关闭不取消 + if task is not None and not task.done() and retry_count > 0: + task.cancel() loop = asyncio.get_running_loop() await loop.run_in_executor(None, start_client, loop)