mirror of https://github.com/Mai-with-u/MaiBot.git
Compare commits
5 Commits
8b99a4af1a
...
6694c618b7
| Author | SHA1 | Date |
|---|---|---|
|
|
6694c618b7 | |
|
|
b63b7a7fb9 | |
|
|
e0d6751681 | |
|
|
a867b44867 | |
|
|
5b38423a89 |
58
bot.py
58
bot.py
|
|
@ -56,6 +56,7 @@ def run_runner_process():
|
|||
# 设置环境变量,标记子进程为 Worker 进程
|
||||
env = os.environ.copy()
|
||||
env["MAIBOT_WORKER_PROCESS"] = "1"
|
||||
env["MAIBOT_RUNNER_PID"] = str(os.getpid()) # 传递 Runner PID 供 Worker 监控
|
||||
|
||||
while True:
|
||||
logger.info(f"正在启动 {script_file}...")
|
||||
|
|
@ -175,6 +176,60 @@ def easter_egg():
|
|||
print(rainbow_text)
|
||||
|
||||
|
||||
def _start_parent_monitor():
|
||||
"""启动父进程存活监控守护线程,检测到 Runner 终止后触发优雅退出"""
|
||||
import ctypes
|
||||
import signal
|
||||
import threading
|
||||
|
||||
try:
|
||||
runner_pid = int(os.environ.get("MAIBOT_RUNNER_PID", "0"))
|
||||
except (ValueError, TypeError):
|
||||
return
|
||||
if not runner_pid:
|
||||
return
|
||||
|
||||
def is_alive_unix(pid):
|
||||
return os.getppid() == pid
|
||||
|
||||
def trigger_exit():
|
||||
# Logger 容错:解释器关闭阶段 Logger 可能已被销毁
|
||||
try:
|
||||
get_logger("main").warning("检测到 Runner 进程已终止,正在触发优雅退出...")
|
||||
except Exception:
|
||||
print("[ParentMonitor] 检测到 Runner 进程已终止,正在触发优雅退出...")
|
||||
signal.raise_signal(signal.SIGINT) # 触发 KeyboardInterrupt,走正常关闭流程
|
||||
|
||||
def monitor():
|
||||
if platform.system() == "Windows":
|
||||
# Windows: 循环外获取句柄,循环内只检查退出码,减少系统调用
|
||||
PROCESS_QUERY_LIMITED_INFORMATION = 0x1000
|
||||
STILL_ACTIVE = 259
|
||||
kernel32 = ctypes.windll.kernel32
|
||||
handle = kernel32.OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, False, runner_pid)
|
||||
if not handle:
|
||||
# 进程已不存在或无权限访问,直接触发退出
|
||||
return trigger_exit()
|
||||
try:
|
||||
exit_code = ctypes.c_ulong()
|
||||
while True:
|
||||
if not kernel32.GetExitCodeProcess(handle, ctypes.byref(exit_code)):
|
||||
break # API 调用失败,假定进程已退出
|
||||
if exit_code.value != STILL_ACTIVE:
|
||||
break # 进程已退出
|
||||
time.sleep(2)
|
||||
finally:
|
||||
kernel32.CloseHandle(handle)
|
||||
else:
|
||||
# Unix: 检测 ppid 是否变化
|
||||
while is_alive_unix(runner_pid):
|
||||
time.sleep(2)
|
||||
|
||||
trigger_exit()
|
||||
|
||||
threading.Thread(target=monitor, daemon=True, name="ParentMonitor").start()
|
||||
|
||||
|
||||
async def graceful_shutdown(): # sourcery skip: use-named-expression
|
||||
try:
|
||||
logger.info("正在优雅关闭麦麦...")
|
||||
|
|
@ -322,6 +377,9 @@ def raw_main():
|
|||
if __name__ == "__main__":
|
||||
exit_code = 0 # 用于记录程序最终的退出状态
|
||||
try:
|
||||
# 启动父进程存活监控(Runner 异常退出时自动触发优雅关闭)
|
||||
_start_parent_monitor()
|
||||
|
||||
# 获取MainSystem实例
|
||||
main_system = raw_main()
|
||||
|
||||
|
|
|
|||
|
|
@ -202,7 +202,7 @@ async def _send_message(message: MessageSending, show_log=True) -> bool:
|
|||
if legacy_exception:
|
||||
raise legacy_exception
|
||||
return False
|
||||
|
||||
|
||||
if not extra_server.is_running():
|
||||
logger.warning(f"[API Server Fallback] extra_server未运行")
|
||||
if legacy_exception:
|
||||
|
|
@ -226,8 +226,28 @@ async def _send_message(message: MessageSending, show_log=True) -> bool:
|
|||
# group_info/user_info 是消息接收者信息,放入 receiver_info
|
||||
from maim_message import MessageConverter
|
||||
|
||||
# 修复 API Server Fallback 模式下的 user_info 问题
|
||||
# 在 Legacy 模式下,MessageSending.to_dict() 的第 454 行会将 user_info 替换为 chat_stream.user_info
|
||||
# 但在 API Server Fallback 模式下,MessageConverter.to_api_send() 直接访问 message 对象,不调用 to_dict()
|
||||
# 需要手动应用相同的变通方案:在私聊场景下,user_info 应该是接收者(sender_info)
|
||||
message_for_conversion = message
|
||||
if hasattr(message, "message_info") and message.message_info.group_info is None:
|
||||
# 私聊场景:group_info 为 None
|
||||
# user_info 应该是接收者,从 chat_stream.user_info 或 sender_info 获取
|
||||
temp_dict = message.to_dict()
|
||||
if (
|
||||
hasattr(message, "chat_stream")
|
||||
and message.chat_stream
|
||||
and hasattr(message.chat_stream, "user_info")
|
||||
):
|
||||
temp_dict["message_info"]["user_info"] = message.chat_stream.user_info.to_dict()
|
||||
# 重新构建 MessageBase 对象(不保留 sender_info 等扩展属性)
|
||||
from maim_message import MessageBase
|
||||
|
||||
message_for_conversion = MessageBase.from_dict(temp_dict)
|
||||
|
||||
api_message = MessageConverter.to_api_send(
|
||||
message=message,
|
||||
message=message_for_conversion,
|
||||
api_key=target_api_key,
|
||||
platform=platform,
|
||||
)
|
||||
|
|
@ -249,6 +269,7 @@ async def _send_message(message: MessageSending, show_log=True) -> bool:
|
|||
except Exception as e:
|
||||
logger.error(f"[API Server Fallback] 发生异常: {e}")
|
||||
import traceback
|
||||
|
||||
logger.debug(traceback.format_exc())
|
||||
|
||||
# 如果 Fallback 失败,且存在 legacy 异常,则抛出 legacy 异常
|
||||
|
|
|
|||
Loading…
Reference in New Issue