Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 235e0b9b8f | |||
| d5a9bed8a4 | |||
| d7dc8a7612 | |||
| 08cd3ca40c | |||
| a13562dcea | |||
| d7a0c0d1d0 | |||
| c0729b2d29 | |||
| a80f474290 |
@@ -11,6 +11,10 @@ import threading
|
||||
import time
|
||||
import asyncio
|
||||
from util.plugin_dev.api.v1.config import update_config
|
||||
from SparkleLogging.utils.core import LogManager
|
||||
from logging import Logger
|
||||
|
||||
logger: Logger = LogManager.GetLogger(log_name='astrbot-core')
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -28,7 +32,6 @@ class DashBoardHelper():
|
||||
def __init__(self, global_object, config: dict):
|
||||
self.loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(self.loop)
|
||||
self.logger = global_object.logger
|
||||
dashboard_data = global_object.dashboard_data
|
||||
dashboard_data.configs = {
|
||||
"data": []
|
||||
@@ -42,7 +45,6 @@ class DashBoardHelper():
|
||||
@self.dashboard.register("post_configs")
|
||||
def on_post_configs(post_configs: dict):
|
||||
try:
|
||||
# self.logger.log(f"收到配置更新请求", gu.LEVEL_INFO, tag="可视化面板")
|
||||
if 'base_config' in post_configs:
|
||||
self.save_config(
|
||||
post_configs['base_config'], namespace='') # 基础配置
|
||||
@@ -54,7 +56,6 @@ class DashBoardHelper():
|
||||
threading.Thread(target=self.dashboard.shutdown_bot,
|
||||
args=(2,), daemon=True).start()
|
||||
except Exception as e:
|
||||
# self.logger.log(f"在保存配置时发生错误:{e}", gu.LEVEL_ERROR, tag="可视化面板")
|
||||
raise e
|
||||
|
||||
# 将 config.yaml、 中的配置解析到 dashboard_data.configs 中
|
||||
@@ -470,7 +471,7 @@ class DashBoardHelper():
|
||||
]
|
||||
|
||||
except Exception as e:
|
||||
self.logger.log(f"配置文件解析错误:{e}", gu.LEVEL_ERROR)
|
||||
logger.error(f"配置文件解析错误:{e}")
|
||||
raise e
|
||||
|
||||
def save_config(self, post_config: list, namespace: str):
|
||||
|
||||
+50
-26
@@ -1,13 +1,3 @@
|
||||
from flask import Flask, request
|
||||
from flask.logging import default_handler
|
||||
from werkzeug.serving import make_server
|
||||
from util import general_utils as gu
|
||||
from dataclasses import dataclass
|
||||
import logging
|
||||
from cores.database.conn import dbConn
|
||||
from util.cmd_config import CmdConfig
|
||||
from util.updator import check_update, update_project, request_release_info
|
||||
from cores.astrbot.types import *
|
||||
import util.plugin_util as putil
|
||||
import websockets
|
||||
import json
|
||||
@@ -17,6 +7,18 @@ import os
|
||||
import sys
|
||||
import time
|
||||
|
||||
from flask import Flask, request
|
||||
from flask.logging import default_handler
|
||||
from werkzeug.serving import make_server
|
||||
from util import general_utils as gu
|
||||
from dataclasses import dataclass
|
||||
from cores.database.conn import dbConn
|
||||
from util.cmd_config import CmdConfig
|
||||
from util.updator import check_update, update_project, request_release_info
|
||||
from cores.astrbot.types import *
|
||||
from SparkleLogging.utils.core import LogManager
|
||||
from logging import Logger
|
||||
logger: Logger = LogManager.GetLogger(log_name='astrbot-core')
|
||||
|
||||
@dataclass
|
||||
class DashBoardData():
|
||||
@@ -41,11 +43,8 @@ class AstrBotDashBoard():
|
||||
self.dashboard_data: DashBoardData = global_object.dashboard_data
|
||||
self.dashboard_be = Flask(
|
||||
__name__, static_folder="dist", static_url_path="/")
|
||||
log = logging.getLogger('werkzeug')
|
||||
log.setLevel(logging.ERROR)
|
||||
self.funcs = {}
|
||||
self.cc = CmdConfig()
|
||||
self.logger = global_object.logger
|
||||
self.ws_clients = {} # remote_ip: ws
|
||||
# 启动 websocket 服务器
|
||||
self.ws_server = websockets.serve(self.__handle_msg, "0.0.0.0", 6186)
|
||||
@@ -55,6 +54,22 @@ class AstrBotDashBoard():
|
||||
# 返回页面
|
||||
return self.dashboard_be.send_static_file("index.html")
|
||||
|
||||
@self.dashboard_be.get("/config")
|
||||
def rt_config():
|
||||
return self.dashboard_be.send_static_file("index.html")
|
||||
|
||||
@self.dashboard_be.get("/logs")
|
||||
def rt_logs():
|
||||
return self.dashboard_be.send_static_file("index.html")
|
||||
|
||||
@self.dashboard_be.get("/extension")
|
||||
def rt_extension():
|
||||
return self.dashboard_be.send_static_file("index.html")
|
||||
|
||||
@self.dashboard_be.get("/dashboard/default")
|
||||
def rt_dashboard():
|
||||
return self.dashboard_be.send_static_file("index.html")
|
||||
|
||||
@self.dashboard_be.post("/api/authenticate")
|
||||
def authenticate():
|
||||
username = self.cc.get("dashboard_username", "")
|
||||
@@ -179,9 +194,9 @@ class AstrBotDashBoard():
|
||||
post_data = request.json
|
||||
repo_url = post_data["url"]
|
||||
try:
|
||||
self.logger.log(f"正在安装插件 {repo_url}", tag="可视化面板")
|
||||
logger.info(f"正在安装插件 {repo_url}")
|
||||
putil.install_plugin(repo_url, self.dashboard_data.plugins)
|
||||
self.logger.log(f"安装插件 {repo_url} 成功", tag="可视化面板")
|
||||
logger.info(f"安装插件 {repo_url} 成功")
|
||||
return Response(
|
||||
status="success",
|
||||
message="安装成功~",
|
||||
@@ -199,10 +214,10 @@ class AstrBotDashBoard():
|
||||
post_data = request.json
|
||||
plugin_name = post_data["name"]
|
||||
try:
|
||||
self.logger.log(f"正在卸载插件 {plugin_name}", tag="可视化面板")
|
||||
logger.info(f"正在卸载插件 {plugin_name}")
|
||||
putil.uninstall_plugin(
|
||||
plugin_name, self.dashboard_data.plugins)
|
||||
self.logger.log(f"卸载插件 {plugin_name} 成功", tag="可视化面板")
|
||||
logger.info(f"卸载插件 {plugin_name} 成功")
|
||||
return Response(
|
||||
status="success",
|
||||
message="卸载成功~",
|
||||
@@ -220,9 +235,9 @@ class AstrBotDashBoard():
|
||||
post_data = request.json
|
||||
plugin_name = post_data["name"]
|
||||
try:
|
||||
self.logger.log(f"正在更新插件 {plugin_name}", tag="可视化面板")
|
||||
logger.info(f"正在更新插件 {plugin_name}")
|
||||
putil.update_plugin(plugin_name, self.dashboard_data.plugins)
|
||||
self.logger.log(f"更新插件 {plugin_name} 成功", tag="可视化面板")
|
||||
logger.info(f"更新插件 {plugin_name} 成功")
|
||||
return Response(
|
||||
status="success",
|
||||
message="更新成功~",
|
||||
@@ -416,21 +431,29 @@ class AstrBotDashBoard():
|
||||
return func
|
||||
return decorator
|
||||
|
||||
async def get_log_history(self):
|
||||
try:
|
||||
with open("logs/astrbot-core/astrbot-core.log", "r", encoding="utf-8") as f:
|
||||
return f.readlines()[-100:]
|
||||
except Exception as e:
|
||||
logger.warning(f"读取日志历史失败: {e.__str__()}")
|
||||
return []
|
||||
|
||||
async def __handle_msg(self, websocket, path):
|
||||
address = websocket.remote_address
|
||||
# self.logger.log(f"和 {address} 建立了 websocket 连接", tag="可视化面板")
|
||||
self.ws_clients[address] = websocket
|
||||
data = ''.join(self.logger.history).replace('\n', '\r\n')
|
||||
data = await self.get_log_history()
|
||||
data = ''.join(data).replace('\n', '\r\n')
|
||||
await websocket.send(data)
|
||||
while True:
|
||||
try:
|
||||
msg = await websocket.recv()
|
||||
except websockets.exceptions.ConnectionClosedError:
|
||||
# self.logger.log(f"和 {address} 的 websocket 连接已断开", tag="可视化面板")
|
||||
# logger.info(f"和 {address} 的 websocket 连接已断开")
|
||||
del self.ws_clients[address]
|
||||
break
|
||||
except Exception as e:
|
||||
# self.logger.log(f"和 {path} 的 websocket 连接发生了错误: {e.__str__()}", tag="可视化面板")
|
||||
# logger.info(f"和 {path} 的 websocket 连接发生了错误: {e.__str__()}")
|
||||
del self.ws_clients[address]
|
||||
break
|
||||
|
||||
@@ -441,11 +464,12 @@ class AstrBotDashBoard():
|
||||
|
||||
def run(self):
|
||||
threading.Thread(target=self.run_ws_server, args=(self.loop,)).start()
|
||||
self.logger.log("已启动 websocket 服务器", tag="可视化面板")
|
||||
logger.info("已启动 websocket 服务器")
|
||||
ip_address = gu.get_local_ip_addresses()
|
||||
ip_str = f"http://{ip_address}:6185\n\thttp://localhost:6185"
|
||||
self.logger.log(
|
||||
f"\n==================\n您可访问:\n\n\t{ip_str}\n\n来登录可视化面板,默认账号密码为空。\n注意: 所有配置项现已全量迁移至 cmd_config.json 文件下,可登录可视化面板在线修改配置。\n==================\n", tag="可视化面板")
|
||||
logger.info(
|
||||
f"\n==================\n您可访问:\n\n\t{ip_str}\n\n来登录可视化面板,默认账号密码为空。\n注意: 所有配置项现已全量迁移至 cmd_config.json 文件下,可登录可视化面板在线修改配置。\n==================\n")
|
||||
|
||||
http_server = make_server(
|
||||
'0.0.0.0', 6185, self.dashboard_be, threaded=True)
|
||||
http_server.serve_forever()
|
||||
|
||||
+33
-36
@@ -19,7 +19,7 @@ from addons.baidu_aip_judge import BaiduJudge
|
||||
from model.provider.provider import Provider
|
||||
from model.command.command import Command
|
||||
from util import general_utils as gu
|
||||
from util.general_utils import Logger, upload, run_monitor
|
||||
from util.general_utils import upload, run_monitor
|
||||
from util.cmd_config import CmdConfig as cc
|
||||
from util.cmd_config import init_astrbot_config_items
|
||||
from .types import *
|
||||
@@ -27,7 +27,10 @@ from addons.dashboard.helper import DashBoardHelper
|
||||
from addons.dashboard.server import DashBoardData
|
||||
from cores.database.conn import dbConn
|
||||
from model.platform._message_result import MessageResult
|
||||
from SparkleLogging.utils.core import LogManager
|
||||
from logging import Logger
|
||||
|
||||
logger: Logger = LogManager.GetLogger(log_name='astrbot-core')
|
||||
|
||||
# 用户发言频率
|
||||
user_frequency = {}
|
||||
@@ -37,7 +40,7 @@ frequency_time = 60
|
||||
frequency_count = 10
|
||||
|
||||
# 版本
|
||||
version = '3.1.12'
|
||||
version = '3.1.13'
|
||||
|
||||
# 语言模型
|
||||
OPENAI_OFFICIAL = 'openai_official'
|
||||
@@ -58,7 +61,6 @@ init_astrbot_config_items()
|
||||
|
||||
# 全局对象
|
||||
_global_object: GlobalObject = None
|
||||
logger: Logger = Logger()
|
||||
|
||||
# 语言模型选择
|
||||
|
||||
@@ -80,7 +82,6 @@ def init(cfg):
|
||||
global baidu_judge, chosen_provider
|
||||
global frequency_count, frequency_time
|
||||
global _global_object
|
||||
global logger
|
||||
|
||||
# 迁移旧配置
|
||||
gu.try_migrate_config(cfg)
|
||||
@@ -94,8 +95,7 @@ def init(cfg):
|
||||
_global_object = GlobalObject()
|
||||
_global_object.version = version
|
||||
_global_object.base_config = cfg
|
||||
_global_object.logger = logger
|
||||
logger.log("AstrBot v"+version, gu.LEVEL_INFO)
|
||||
logger.info("AstrBot v"+version)
|
||||
|
||||
if 'reply_prefix' in cfg:
|
||||
# 适配旧版配置
|
||||
@@ -107,10 +107,10 @@ def init(cfg):
|
||||
_global_object.reply_prefix = cfg['reply_prefix']
|
||||
|
||||
# 语言模型提供商
|
||||
logger.log("正在载入语言模型...", gu.LEVEL_INFO)
|
||||
logger.info("正在载入语言模型...")
|
||||
prov = privider_chooser(cfg)
|
||||
if OPENAI_OFFICIAL in prov:
|
||||
logger.log("初始化:OpenAI官方", gu.LEVEL_INFO)
|
||||
logger.info("初始化:OpenAI官方")
|
||||
if cfg['openai']['key'] is not None and cfg['openai']['key'] != [None]:
|
||||
from model.provider.openai_official import ProviderOpenAIOfficial
|
||||
from model.command.openai_official import CommandOpenAIOfficial
|
||||
@@ -131,9 +131,9 @@ def init(cfg):
|
||||
if 'baidu_aip' in cfg and 'enable' in cfg['baidu_aip'] and cfg['baidu_aip']['enable']:
|
||||
try:
|
||||
baidu_judge = BaiduJudge(cfg['baidu_aip'])
|
||||
logger.log("百度内容审核初始化成功", gu.LEVEL_INFO)
|
||||
logger.info("百度内容审核初始化成功")
|
||||
except BaseException as e:
|
||||
logger.log("百度内容审核初始化失败", gu.LEVEL_ERROR)
|
||||
logger.info("百度内容审核初始化失败")
|
||||
|
||||
threading.Thread(target=upload, args=(
|
||||
_global_object, ), daemon=True).start()
|
||||
@@ -151,7 +151,7 @@ def init(cfg):
|
||||
else:
|
||||
_global_object.unique_session = False
|
||||
except BaseException as e:
|
||||
logger.log("独立会话配置错误: "+str(e), gu.LEVEL_ERROR)
|
||||
logger.info("独立会话配置错误: "+str(e))
|
||||
|
||||
nick_qq = cc.get("nick_qq", None)
|
||||
if nick_qq == None:
|
||||
@@ -166,33 +166,33 @@ def init(cfg):
|
||||
global llm_wake_prefix
|
||||
llm_wake_prefix = cc.get("llm_wake_prefix", "")
|
||||
|
||||
logger.log("正在载入插件...", gu.LEVEL_INFO)
|
||||
logger.info("正在载入插件...")
|
||||
# 加载插件
|
||||
_command = Command(None, _global_object)
|
||||
ok, err = putil.plugin_reload(_global_object.cached_plugins)
|
||||
if ok:
|
||||
logger.log(
|
||||
f"成功载入 {len(_global_object.cached_plugins)} 个插件", gu.LEVEL_INFO)
|
||||
logger.info(
|
||||
f"成功载入 {len(_global_object.cached_plugins)} 个插件")
|
||||
else:
|
||||
logger.log(err, gu.LEVEL_ERROR)
|
||||
logger.info(err)
|
||||
|
||||
if chosen_provider is None:
|
||||
llm_command_instance[NONE_LLM] = _command
|
||||
chosen_provider = NONE_LLM
|
||||
|
||||
logger.log("正在载入机器人消息平台", gu.LEVEL_INFO)
|
||||
# logger.log("提示:需要添加管理员 ID 才能使用 update/plugin 等指令),可在可视化面板添加。(如已添加可忽略)", gu.LEVEL_WARNING)
|
||||
logger.info("正在载入机器人消息平台")
|
||||
# logger.info("提示:需要添加管理员 ID 才能使用 update/plugin 等指令),可在可视化面板添加。(如已添加可忽略)")
|
||||
platform_str = ""
|
||||
# GOCQ
|
||||
if 'gocqbot' in cfg and cfg['gocqbot']['enable']:
|
||||
logger.log("启用 QQ_GOCQ 机器人消息平台", gu.LEVEL_INFO)
|
||||
logger.info("启用 QQ_GOCQ 机器人消息平台")
|
||||
threading.Thread(target=run_gocq_bot, args=(
|
||||
cfg, _global_object), daemon=True).start()
|
||||
platform_str += "QQ_GOCQ,"
|
||||
|
||||
# QQ频道
|
||||
if 'qqbot' in cfg and cfg['qqbot']['enable'] and cfg['qqbot']['appid'] != None:
|
||||
logger.log("启用 QQ_OFFICIAL 机器人消息平台", gu.LEVEL_INFO)
|
||||
logger.info("启用 QQ_OFFICIAL 机器人消息平台")
|
||||
threading.Thread(target=run_qqchan_bot, args=(
|
||||
cfg, _global_object), daemon=True).start()
|
||||
platform_str += "QQ_OFFICIAL,"
|
||||
@@ -221,12 +221,12 @@ def init(cfg):
|
||||
threading.Thread(target=run_monitor, args=(
|
||||
_global_object,), daemon=True).start()
|
||||
|
||||
logger.log(
|
||||
"如果有任何问题, 请在 https://github.com/Soulter/AstrBot 上提交 issue 或加群 322154837。", gu.LEVEL_INFO)
|
||||
logger.log("请给 https://github.com/Soulter/AstrBot 点个 star。", gu.LEVEL_INFO)
|
||||
logger.info(
|
||||
"如果有任何问题, 请在 https://github.com/Soulter/AstrBot 上提交 issue 或加群 322154837。")
|
||||
logger.info("请给 https://github.com/Soulter/AstrBot 点个 star。")
|
||||
if platform_str == '':
|
||||
platform_str = "(未启动任何平台,请前往面板添加)"
|
||||
logger.log(f"🎉 项目启动完成")
|
||||
logger.info(f"🎉 项目启动完成")
|
||||
|
||||
dashboard_thread.join()
|
||||
|
||||
@@ -245,10 +245,8 @@ def run_qqchan_bot(cfg: dict, global_object: GlobalObject):
|
||||
platform_name="qqchan", platform_instance=qqchannel_bot, origin="internal"))
|
||||
qqchannel_bot.run()
|
||||
except BaseException as e:
|
||||
logger.log("启动QQ频道机器人时出现错误, 原因如下: " + str(e),
|
||||
gu.LEVEL_CRITICAL, tag="QQ频道")
|
||||
logger.log(r"如果您是初次启动,请前往可视化面板填写配置。详情请看:https://astrbot.soulter.top/center/。" +
|
||||
str(e), gu.LEVEL_CRITICAL)
|
||||
logger.error("启动 QQ 频道机器人时出现错误, 原因如下: " + str(e))
|
||||
logger.error(r"如果您是初次启动,请前往可视化面板填写配置。详情请看:https://astrbot.soulter.top/center/。")
|
||||
|
||||
|
||||
'''
|
||||
@@ -263,17 +261,17 @@ def run_gocq_bot(cfg: dict, _global_object: GlobalObject):
|
||||
host = cc.get("gocq_host", "127.0.0.1")
|
||||
port = cc.get("gocq_websocket_port", 6700)
|
||||
http_port = cc.get("gocq_http_port", 5700)
|
||||
logger.log(
|
||||
f"正在检查连接...host: {host}, ws port: {port}, http port: {http_port}", tag="QQ")
|
||||
logger.info(
|
||||
f"正在检查连接...host: {host}, ws port: {port}, http port: {http_port}")
|
||||
while True:
|
||||
if not gu.port_checker(port=port, host=host) or not gu.port_checker(port=http_port, host=host):
|
||||
if not noticed:
|
||||
noticed = True
|
||||
logger.log(
|
||||
f"连接到{host}:{port}(或{http_port})失败。程序会每隔 5s 自动重试。", gu.LEVEL_CRITICAL, tag="QQ")
|
||||
logger.warning(
|
||||
f"连接到{host}:{port}(或{http_port})失败。程序会每隔 5s 自动重试。")
|
||||
time.sleep(5)
|
||||
else:
|
||||
logger.log("检查完毕,未发现问题。", tag="QQ")
|
||||
logger.info("已连接到 gocq。")
|
||||
break
|
||||
try:
|
||||
qq_gocq = QQGOCQ(cfg=cfg, message_handler=oper_msg,
|
||||
@@ -345,7 +343,6 @@ async def oper_msg(message: AstrBotMessage,
|
||||
reg_platform = p
|
||||
break
|
||||
if not reg_platform:
|
||||
_global_object.logger.log(f"未找到平台 {platform} 的实例。", gu.LEVEL_ERROR)
|
||||
raise Exception(f"未找到平台 {platform} 的实例。")
|
||||
|
||||
# 统计数据,如频道消息量
|
||||
@@ -401,7 +398,7 @@ async def oper_msg(message: AstrBotMessage,
|
||||
if not check:
|
||||
return MessageResult(f"你的提问得到的回复未通过【百度AI内容审核】服务, 不予回复。\n\n{msg}")
|
||||
if chosen_provider == NONE_LLM:
|
||||
logger.log("一条消息由于 Bot 未启动任何语言模型并且未触发指令而将被忽略。", gu.LEVEL_WARNING)
|
||||
logger.info("一条消息由于 Bot 未启动任何语言模型并且未触发指令而将被忽略。")
|
||||
return
|
||||
try:
|
||||
if llm_wake_prefix != "" and not message_str.startswith(llm_wake_prefix):
|
||||
@@ -432,7 +429,7 @@ async def oper_msg(message: AstrBotMessage,
|
||||
|
||||
llm_result_str = _global_object.reply_prefix + llm_result_str
|
||||
except BaseException as e:
|
||||
logger.log(f"调用异常:{traceback.format_exc()}", gu.LEVEL_ERROR)
|
||||
logger.info(f"调用异常:{traceback.format_exc()}")
|
||||
return MessageResult(f"调用语言模型例程时出现异常。原因: {str(e)}")
|
||||
|
||||
# 切换回原来的语言模型
|
||||
@@ -485,4 +482,4 @@ async def oper_msg(message: AstrBotMessage,
|
||||
try:
|
||||
return MessageResult(llm_result_str)
|
||||
except BaseException as e:
|
||||
logger.log("回复消息错误: \n"+str(e), gu.LEVEL_ERROR)
|
||||
logger.info("回复消息错误: \n"+str(e))
|
||||
|
||||
@@ -123,7 +123,6 @@ class GlobalObject:
|
||||
cnt_total: int # 总消息数
|
||||
default_personality: dict
|
||||
dashboard_data = None
|
||||
logger: None
|
||||
|
||||
def __init__(self):
|
||||
self.nick = None # gocq 的昵称
|
||||
|
||||
@@ -1,28 +1,63 @@
|
||||
|
||||
import os
|
||||
import sys
|
||||
from pip._internal import main as pipmain
|
||||
import warnings
|
||||
import traceback
|
||||
import threading
|
||||
from SparkleLogging.utils.core import LogManager
|
||||
from logging import Formatter, Logger
|
||||
|
||||
warnings.filterwarnings("ignore")
|
||||
abs_path = os.path.dirname(os.path.realpath(sys.argv[0])) + '/'
|
||||
|
||||
logger: Logger = None
|
||||
|
||||
logo_tmpl = """
|
||||
___ _______.___________..______ .______ ______ .___________.
|
||||
/ \ / | || _ \ | _ \ / __ \ | |
|
||||
/ ^ \ | (----`---| |----`| |_) | | |_) | | | | | `---| |----`
|
||||
/ /_\ \ \ \ | | | / | _ < | | | | | |
|
||||
/ _____ \ .----) | | | | |\ \----.| |_) | | `--' | | |
|
||||
/__/ \__\ |_______/ |__| | _| `._____||______/ \______/ |__|
|
||||
|
||||
"""
|
||||
|
||||
def make_necessary_dirs():
|
||||
os.makedirs("data/config", exist_ok=True)
|
||||
os.makedirs("temp", exist_ok=True)
|
||||
|
||||
def main():
|
||||
logger = LogManager.GetLogger(
|
||||
log_name='astrbot-core',
|
||||
out_to_console=True,
|
||||
# HTTPpost_url='http://localhost:6185/api/log',
|
||||
# http_mode = True,
|
||||
custom_formatter=Formatter('[%(asctime)s| %(name)s - %(levelname)s|%(filename)s:%(lineno)d]: %(message)s', datefmt="%H:%M:%S")
|
||||
)
|
||||
logger.info(logo_tmpl)
|
||||
# config.yaml 配置文件加载和环境确认
|
||||
try:
|
||||
import botpy, logging, yaml
|
||||
import cores.astrbot.core as qqBot
|
||||
import yaml
|
||||
# delete qqbotpy's logger
|
||||
for handler in logging.root.handlers[:]:
|
||||
logging.root.removeHandler(handler)
|
||||
ymlfile = open(abs_path+"configs/config.yaml", 'r', encoding='utf-8')
|
||||
cfg = yaml.safe_load(ymlfile)
|
||||
except ImportError as import_error:
|
||||
traceback.print_exc()
|
||||
print(import_error)
|
||||
input("第三方库未完全安装完毕,请退出程序重试。")
|
||||
logger.error(import_error)
|
||||
logger.error("检测到一些依赖库没有安装。由于兼容性问题,AstrBot 此版本将不会自动为您安装依赖库。请您先自行安装,然后重试。")
|
||||
logger.info("如何安装?如果:")
|
||||
logger.info("- Windows 启动器部署且使用启动器下载了 Python的:在 launcher.exe 所在目录下的地址框输入 powershell,然后执行 .\python\python.exe -m pip install .\AstrBot\requirements.txt")
|
||||
logger.info("- Windows 启动器部署且使用自己之前下载的 Python的:在 launcher.exe 所在目录下的地址框输入 powershell,然后执行 python -m pip install .\AstrBot\requirements.txt")
|
||||
logger.info("- 自行 clone 源码部署的:python -m pip install -r requirements.txt")
|
||||
logger.info("- 如果还不会,加群 322154837 ")
|
||||
input("按任意键退出。")
|
||||
exit()
|
||||
except FileNotFoundError as file_not_found:
|
||||
print(file_not_found)
|
||||
logger.error(file_not_found)
|
||||
input("配置文件不存在,请检查是否已经下载配置文件。")
|
||||
exit()
|
||||
except BaseException as e:
|
||||
raise e
|
||||
|
||||
@@ -31,76 +66,26 @@ def main():
|
||||
os.environ['HTTP_PROXY'] = cfg['http_proxy']
|
||||
if 'https_proxy' in cfg and cfg['https_proxy'] != '':
|
||||
os.environ['HTTPS_PROXY'] = cfg['https_proxy']
|
||||
|
||||
os.environ['NO_PROXY'] = 'https://api.sgroup.qq.com'
|
||||
|
||||
# 检查并创建 temp 文件夹
|
||||
if not os.path.exists(abs_path + "temp"):
|
||||
os.mkdir(abs_path+"temp")
|
||||
if not os.path.exists(abs_path + "data"):
|
||||
os.mkdir(abs_path+"data")
|
||||
if not os.path.exists(abs_path + "data/config"):
|
||||
os.mkdir(abs_path+"data/config")
|
||||
make_necessary_dirs()
|
||||
|
||||
# 启动主程序(cores/qqbot/core.py)
|
||||
qqBot.init(cfg)
|
||||
|
||||
|
||||
def check_env(ch_mirror=False):
|
||||
def check_env():
|
||||
if not (sys.version_info.major == 3 and sys.version_info.minor >= 9):
|
||||
print("请使用Python3.9+运行本项目")
|
||||
input("按任意键退出...")
|
||||
logger.error("请使用 Python3.9+ 运行本项目。按任意键退出。")
|
||||
input("")
|
||||
exit()
|
||||
|
||||
if os.path.exists('requirements.txt'):
|
||||
pth = 'requirements.txt'
|
||||
else:
|
||||
pth = 'QQChannelChatGPT' + os.sep + 'requirements.txt'
|
||||
print("正在检查或下载第三方库,请耐心等待...")
|
||||
try:
|
||||
if ch_mirror:
|
||||
print("使用阿里云镜像")
|
||||
pipmain(['install', '-r', pth, '-i',
|
||||
'https://mirrors.aliyun.com/pypi/simple/'])
|
||||
else:
|
||||
pipmain(['install', '-r', pth])
|
||||
except BaseException as e:
|
||||
print(e)
|
||||
while True:
|
||||
res = input(
|
||||
"安装失败。\n如报错ValueError: check_hostname requires server_hostname,请尝试先关闭代理后重试。\n1.输入y回车重试\n2. 输入c回车使用国内镜像源下载\n3. 输入其他按键回车继续往下执行。")
|
||||
if res == "y":
|
||||
try:
|
||||
pipmain(['install', '-r', pth])
|
||||
break
|
||||
except BaseException as e:
|
||||
print(e)
|
||||
continue
|
||||
elif res == "c":
|
||||
try:
|
||||
pipmain(['install', '-r', pth, '-i',
|
||||
'https://mirrors.aliyun.com/pypi/simple/'])
|
||||
break
|
||||
except BaseException as e:
|
||||
print(e)
|
||||
continue
|
||||
else:
|
||||
break
|
||||
print("第三方库检查完毕。")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
args = sys.argv
|
||||
|
||||
if '-cn' in args:
|
||||
check_env(True)
|
||||
else:
|
||||
check_env()
|
||||
|
||||
check_env()
|
||||
t = threading.Thread(target=main, daemon=True)
|
||||
t.start()
|
||||
try:
|
||||
t.join()
|
||||
except KeyboardInterrupt as e:
|
||||
print("退出 AstrBot。")
|
||||
logger.info("退出 AstrBot。")
|
||||
exit()
|
||||
|
||||
@@ -13,7 +13,6 @@ from nakuru.entities.components import (
|
||||
from util import general_utils as gu
|
||||
from model.provider.provider import Provider
|
||||
from util.cmd_config import CmdConfig as cc
|
||||
from util.general_utils import Logger
|
||||
from cores.astrbot.types import (
|
||||
GlobalObject,
|
||||
AstrMessageEvent,
|
||||
@@ -24,6 +23,10 @@ from cores.astrbot.types import (
|
||||
)
|
||||
|
||||
from typing import List, Tuple
|
||||
from SparkleLogging.utils.core import LogManager
|
||||
from logging import Logger
|
||||
|
||||
logger: Logger = LogManager.GetLogger(log_name='astrbot-core')
|
||||
|
||||
PLATFORM_QQCHAN = 'qqchan'
|
||||
PLATFORM_GOCQ = 'gocq'
|
||||
@@ -35,7 +38,6 @@ class Command:
|
||||
def __init__(self, provider: Provider, global_object: GlobalObject = None):
|
||||
self.provider = provider
|
||||
self.global_object = global_object
|
||||
self.logger: Logger = global_object.logger
|
||||
|
||||
async def check_command(self,
|
||||
message,
|
||||
@@ -85,11 +87,11 @@ class Command:
|
||||
if hit:
|
||||
return True, res
|
||||
except BaseException as e:
|
||||
self.logger.log(
|
||||
f"{plugin.metadata.plugin_name} 插件异常,原因: {str(e)}\n如果你没有相关装插件的想法, 请直接忽略此报错, 不影响其他功能的运行。", level=gu.LEVEL_WARNING)
|
||||
logger.error(
|
||||
f"{plugin.metadata.plugin_name} 插件异常,原因: {str(e)}\n如果你没有相关装插件的想法, 请直接忽略此报错, 不影响其他功能的运行。")
|
||||
except BaseException as e:
|
||||
self.logger.log(
|
||||
f"{plugin.metadata.plugin_name} 插件异常,原因: {str(e)}\n如果你没有相关装插件的想法, 请直接忽略此报错, 不影响其他功能的运行。", level=gu.LEVEL_WARNING)
|
||||
logger.error(
|
||||
f"{plugin.metadata.plugin_name} 插件异常,原因: {str(e)}\n如果你没有相关装插件的想法, 请直接忽略此报错, 不影响其他功能的运行。")
|
||||
|
||||
if self.command_start_with(message, "nick"):
|
||||
return True, self.set_nick(message, platform, role)
|
||||
@@ -248,7 +250,7 @@ class Command:
|
||||
p = gu.create_markdown_image(msg)
|
||||
return [Image.fromFileSystem(p),]
|
||||
except BaseException as e:
|
||||
self.logger.log(str(e))
|
||||
logger.error(str(e))
|
||||
return msg
|
||||
|
||||
def command_start_with(self, message: str, *args):
|
||||
|
||||
@@ -16,6 +16,10 @@ import time
|
||||
from ._platfrom import Platform
|
||||
from ._message_parse import nakuru_message_parse_rev
|
||||
from cores.astrbot.types import MessageType, AstrBotMessage, MessageMember
|
||||
from SparkleLogging.utils.core import LogManager
|
||||
from logging import Logger
|
||||
|
||||
logger: Logger = LogManager.GetLogger(log_name='astrbot-core')
|
||||
|
||||
|
||||
class FakeSource:
|
||||
@@ -34,7 +38,6 @@ class QQGOCQ(Platform):
|
||||
self.waiting = {}
|
||||
self.cc = CmdConfig()
|
||||
self.cfg = cfg
|
||||
self.logger: gu.Logger = global_object.logger
|
||||
|
||||
try:
|
||||
self.nick_qq = cfg['nick_qq']
|
||||
@@ -106,8 +109,8 @@ class QQGOCQ(Platform):
|
||||
self.client.run()
|
||||
|
||||
async def handle_msg(self, message: AstrBotMessage):
|
||||
self.logger.log(
|
||||
f"{message.sender.nickname}/{message.sender.user_id} -> {self.parse_message_outline(message)}", tag="QQ_GOCQ")
|
||||
logger.info(
|
||||
f"{message.sender.nickname}/{message.sender.user_id} -> {self.parse_message_outline(message)}")
|
||||
|
||||
assert isinstance(message.raw_message,
|
||||
(GroupMessage, FriendMessage, GuildMessage))
|
||||
@@ -188,8 +191,8 @@ class QQGOCQ(Platform):
|
||||
|
||||
res = result_message
|
||||
|
||||
self.logger.log(
|
||||
f"{source.user_id} <- {self.parse_message_outline(res)}", tag="QQ_GOCQ")
|
||||
logger.info(
|
||||
f"{source.user_id} <- {self.parse_message_outline(res)}")
|
||||
|
||||
if isinstance(source, int):
|
||||
source = FakeSource("GroupMessage", source)
|
||||
|
||||
@@ -18,10 +18,12 @@ from ._message_parse import (
|
||||
from cores.astrbot.types import MessageType, AstrBotMessage, MessageMember
|
||||
from typing import Union, List
|
||||
from nakuru.entities.components import BaseMessageComponent
|
||||
from SparkleLogging.utils.core import LogManager
|
||||
from logging import Logger
|
||||
|
||||
logger: Logger = LogManager.GetLogger(log_name='astrbot-core')
|
||||
|
||||
# QQ 机器人官方框架
|
||||
|
||||
|
||||
class botClient(Client):
|
||||
def set_platform(self, platform: 'QQOfficial'):
|
||||
self.platform = platform
|
||||
@@ -59,7 +61,6 @@ class QQOfficial(Platform):
|
||||
self.token = cfg['qqbot']['token']
|
||||
self.secret = cfg['qqbot_secret']
|
||||
self.unique_session = cfg['uniqueSessionMode']
|
||||
self.logger: gu.Logger = global_object.logger
|
||||
qq_group = cfg['qqofficial_enable_group_message']
|
||||
|
||||
if qq_group:
|
||||
@@ -104,8 +105,8 @@ class QQOfficial(Platform):
|
||||
is_group = message.type != MessageType.FRIEND_MESSAGE
|
||||
|
||||
_t = "/私聊" if not is_group else ""
|
||||
self.logger.log(
|
||||
f"{message.sender.nickname}({message.sender.user_id}{_t}) -> {self.parse_message_outline(message)}", tag="QQ_OFFICIAL")
|
||||
logger.info(
|
||||
f"{message.sender.nickname}({message.sender.user_id}{_t}) -> {self.parse_message_outline(message)}")
|
||||
|
||||
# 解析出 session_id
|
||||
if self.unique_session or not is_group:
|
||||
@@ -157,8 +158,8 @@ class QQOfficial(Platform):
|
||||
source = message
|
||||
assert isinstance(source, (botpy.message.Message,
|
||||
botpy.message.GroupMessage, botpy.message.DirectMessage))
|
||||
self.logger.log(
|
||||
f"{message.sender.nickname}({message.sender.user_id}) <- {self.parse_message_outline(res)}", tag="QQ_OFFICIAL")
|
||||
logger.info(
|
||||
f"{message.sender.nickname}({message.sender.user_id}) <- {self.parse_message_outline(res)}")
|
||||
|
||||
plain_text = ''
|
||||
image_path = ''
|
||||
|
||||
@@ -14,7 +14,10 @@ from cores.database.conn import dbConn
|
||||
from model.provider.provider import Provider
|
||||
from util import general_utils as gu
|
||||
from util.cmd_config import CmdConfig
|
||||
from util.general_utils import Logger
|
||||
from SparkleLogging.utils.core import LogManager
|
||||
from logging import Logger
|
||||
|
||||
logger: Logger = LogManager.GetLogger(log_name='astrbot-core')
|
||||
|
||||
|
||||
abs_path = os.path.dirname(os.path.realpath(sys.argv[0])) + '/'
|
||||
@@ -23,7 +26,6 @@ abs_path = os.path.dirname(os.path.realpath(sys.argv[0])) + '/'
|
||||
class ProviderOpenAIOfficial(Provider):
|
||||
def __init__(self, cfg):
|
||||
self.cc = CmdConfig()
|
||||
self.logger = Logger()
|
||||
|
||||
self.key_list = []
|
||||
# 如果 cfg['key'] 中有长度为 1 的字符串,那么是格式错误,直接报错
|
||||
@@ -43,7 +45,7 @@ class ProviderOpenAIOfficial(Provider):
|
||||
self.api_base = None
|
||||
if 'api_base' in cfg and cfg['api_base'] != 'none' and cfg['api_base'] != '':
|
||||
self.api_base = cfg['api_base']
|
||||
self.logger.log(f"设置 api_base 为: {self.api_base}", tag="OpenAI")
|
||||
logger.info(f"设置 api_base 为: {self.api_base}")
|
||||
|
||||
# 创建 OpenAI Client
|
||||
self.client = AsyncOpenAI(
|
||||
@@ -52,8 +54,6 @@ class ProviderOpenAIOfficial(Provider):
|
||||
)
|
||||
|
||||
self.openai_model_configs: dict = cfg['chatGPTConfigs']
|
||||
self.logger.log(
|
||||
f'加载 OpenAI Chat Configs: {self.openai_model_configs}', tag="OpenAI")
|
||||
self.openai_configs = cfg
|
||||
# 会话缓存
|
||||
self.session_dict = {}
|
||||
@@ -69,10 +69,9 @@ class ProviderOpenAIOfficial(Provider):
|
||||
db1 = dbConn()
|
||||
for session in db1.get_all_session():
|
||||
self.session_dict[session[0]] = json.loads(session[1])['data']
|
||||
self.logger.log("读取历史记录成功。", tag="OpenAI")
|
||||
logger.info("读取历史记录成功。")
|
||||
except BaseException as e:
|
||||
self.logger.log("读取历史记录失败,但不影响使用。",
|
||||
level=gu.LEVEL_ERROR, tag="OpenAI")
|
||||
logger.info("读取历史记录失败,但不影响使用。")
|
||||
|
||||
# 创建转储定时器线程
|
||||
threading.Thread(target=self.dump_history, daemon=True).start()
|
||||
@@ -143,15 +142,12 @@ class ProviderOpenAIOfficial(Provider):
|
||||
if self.openai_model_configs['max_tokens'] < len(_encoded_prompt):
|
||||
prompt = self.enc.decode(_encoded_prompt[:int(
|
||||
self.openai_model_configs['max_tokens']*0.80)])
|
||||
self.logger.log(f"注意,有一部分 prompt 文本由于超出 token 限制而被截断。",
|
||||
level=gu.LEVEL_WARNING, tag="OpenAI")
|
||||
logger.info(f"注意,有一部分 prompt 文本由于超出 token 限制而被截断。")
|
||||
|
||||
cache_data_list, new_record, req = self.wrap(
|
||||
prompt, session_id, image_url)
|
||||
self.logger.log(f"cache: {str(cache_data_list)}",
|
||||
level=gu.LEVEL_DEBUG, tag="OpenAI")
|
||||
self.logger.log(f"request: {str(req)}",
|
||||
level=gu.LEVEL_DEBUG, tag="OpenAI")
|
||||
logger.debug(f"cache: {str(cache_data_list)}")
|
||||
logger.debug(f"request: {str(req)}")
|
||||
retry = 0
|
||||
response = None
|
||||
err = ''
|
||||
@@ -194,15 +190,15 @@ class ProviderOpenAIOfficial(Provider):
|
||||
if 'Invalid content type. image_url is only supported by certain models.' in str(e):
|
||||
raise e
|
||||
if 'You exceeded' in str(e) or 'Billing hard limit has been reached' in str(e) or 'No API key provided' in str(e) or 'Incorrect API key provided' in str(e):
|
||||
self.logger.log("当前 Key 已超额或异常, 正在切换",
|
||||
level=gu.LEVEL_WARNING, tag="OpenAI")
|
||||
logger.info("当前 Key 已超额或异常, 正在切换",
|
||||
)
|
||||
self.key_stat[self.client.api_key]['exceed'] = True
|
||||
is_switched = self.handle_switch_key()
|
||||
if not is_switched:
|
||||
raise e
|
||||
retry -= 1
|
||||
elif 'maximum context length' in str(e):
|
||||
self.logger.log("token 超限, 清空对应缓存,并进行消息截断", tag="OpenAI")
|
||||
logger.info("token 超限, 清空对应缓存,并进行消息截断")
|
||||
self.session_dict[session_id] = []
|
||||
prompt = prompt[:int(len(prompt)*truncate_rate)]
|
||||
truncate_rate -= 0.05
|
||||
@@ -213,17 +209,17 @@ class ProviderOpenAIOfficial(Provider):
|
||||
time.sleep(30)
|
||||
continue
|
||||
else:
|
||||
self.logger.log(str(e), level=gu.LEVEL_ERROR, tag="OpenAI")
|
||||
logger.error(str(e))
|
||||
time.sleep(2)
|
||||
err = str(e)
|
||||
retry += 1
|
||||
if retry >= 10:
|
||||
self.logger.log(
|
||||
r"如果报错, 且您的机器在中国大陆内, 请确保您的电脑已经设置好代理软件(梯子), 并在配置文件设置了系统代理地址。详见 https://github.com/Soulter/QQChannelChatGPT/wiki", tag="OpenAI")
|
||||
logger.warning(
|
||||
r"如果报错, 且您的机器在中国大陆内, 请确保您的电脑已经设置好代理软件(梯子), 并在配置文件设置了系统代理地址。详见 https://github.com/Soulter/QQChannelChatGPT/wiki")
|
||||
raise BaseException("连接出错: "+str(err))
|
||||
assert isinstance(response, ChatCompletion)
|
||||
self.logger.log(
|
||||
f"OPENAI RESPONSE: {response.usage}", level=gu.LEVEL_DEBUG, tag="OpenAI")
|
||||
logger.debug(
|
||||
f"OPENAI RESPONSE: {response.usage}")
|
||||
|
||||
# 结果分类
|
||||
choice = response.choices[0]
|
||||
@@ -288,18 +284,16 @@ class ProviderOpenAIOfficial(Provider):
|
||||
image_url.append(response.data[i].url)
|
||||
break
|
||||
except Exception as e:
|
||||
self.logger.log(str(e), level=gu.LEVEL_ERROR)
|
||||
logger.warning(str(e))
|
||||
if 'You exceeded' in str(e) or 'Billing hard limit has been reached' in str(
|
||||
e) or 'No API key provided' in str(e) or 'Incorrect API key provided' in str(e):
|
||||
self.logger.log("当前 Key 已超额或者不正常, 正在切换",
|
||||
level=gu.LEVEL_WARNING, tag="OpenAI")
|
||||
logger.warning("当前 Key 已超额或者不正常, 正在切换")
|
||||
self.key_stat[self.client.api_key]['exceed'] = True
|
||||
is_switched = self.handle_switch_key()
|
||||
if not is_switched:
|
||||
raise e
|
||||
elif 'Your request was rejected as a result of our safety system.' in str(e):
|
||||
self.logger.log("您的请求被 OpenAI 安全系统拒绝, 请稍后再试",
|
||||
level=gu.LEVEL_WARNING, tag="OpenAI")
|
||||
logger.warning("您的请求被 OpenAI 安全系统拒绝, 请稍后再试")
|
||||
raise e
|
||||
else:
|
||||
retry += 1
|
||||
@@ -378,12 +372,12 @@ class ProviderOpenAIOfficial(Provider):
|
||||
continue
|
||||
is_all_exceed = False
|
||||
self.client.api_key = key
|
||||
self.logger.log(
|
||||
f"切换到 Key: {key}(已使用 token: {self.key_stat[key]['used']})", level=gu.LEVEL_INFO, tag="OpenAI")
|
||||
logger.warning(
|
||||
f"切换到 Key: {key}(已使用 token: {self.key_stat[key]['used']})")
|
||||
break
|
||||
if is_all_exceed:
|
||||
self.logger.log(
|
||||
"所有 Key 已超额", level=gu.LEVEL_CRITICAL, tag="OpenAI")
|
||||
logger.warning(
|
||||
"所有 Key 已超额")
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
+5
-4
@@ -4,15 +4,16 @@ requests
|
||||
openai~=1.2.3
|
||||
qq-botpy
|
||||
chardet~=5.1.0
|
||||
Pillow~=9.4.0
|
||||
GitPython~=3.1.31
|
||||
Pillow
|
||||
GitPython
|
||||
nakuru-project
|
||||
beautifulsoup4
|
||||
googlesearch-python
|
||||
tiktoken
|
||||
readability-lxml
|
||||
baidu-aip~=4.16.9
|
||||
baidu-aip
|
||||
websockets
|
||||
flask
|
||||
psutil
|
||||
lxml_html_clean
|
||||
lxml_html_clean
|
||||
SparkleLogging
|
||||
|
||||
+1
-127
@@ -14,132 +14,6 @@ import json
|
||||
import sys
|
||||
import psutil
|
||||
|
||||
PLATFORM_GOCQ = 'gocq'
|
||||
PLATFORM_QQCHAN = 'qqchan'
|
||||
|
||||
FG_COLORS = {
|
||||
"black": "30",
|
||||
"red": "31",
|
||||
"green": "32",
|
||||
"yellow": "33",
|
||||
"blue": "34",
|
||||
"purple": "35",
|
||||
"cyan": "36",
|
||||
"white": "37",
|
||||
"default": "39",
|
||||
}
|
||||
|
||||
BG_COLORS = {
|
||||
"black": "40",
|
||||
"red": "41",
|
||||
"green": "42",
|
||||
"yellow": "43",
|
||||
"blue": "44",
|
||||
"purple": "45",
|
||||
"cyan": "46",
|
||||
"white": "47",
|
||||
"default": "49",
|
||||
}
|
||||
|
||||
LEVEL_DEBUG = "DEBUG"
|
||||
LEVEL_INFO = "INFO"
|
||||
LEVEL_WARNING = "WARN"
|
||||
LEVEL_ERROR = "ERROR"
|
||||
LEVEL_CRITICAL = "CRITICAL"
|
||||
|
||||
# 为了兼容旧版
|
||||
level_codes = {
|
||||
LEVEL_DEBUG: logging.DEBUG,
|
||||
LEVEL_INFO: logging.INFO,
|
||||
LEVEL_WARNING: logging.WARNING,
|
||||
LEVEL_ERROR: logging.ERROR,
|
||||
LEVEL_CRITICAL: logging.CRITICAL,
|
||||
}
|
||||
|
||||
level_colors = {
|
||||
"INFO": "green",
|
||||
"WARN": "yellow",
|
||||
"ERROR": "red",
|
||||
"CRITICAL": "purple",
|
||||
}
|
||||
|
||||
|
||||
class Logger:
|
||||
def __init__(self) -> None:
|
||||
self.history = []
|
||||
|
||||
def log(
|
||||
self,
|
||||
msg: str,
|
||||
level: str = "INFO",
|
||||
tag: str = "System",
|
||||
fg: str = None,
|
||||
bg: str = None,
|
||||
max_len: int = 50000,
|
||||
err: Exception = None,):
|
||||
"""
|
||||
日志打印函数
|
||||
"""
|
||||
_set_level_code = level_codes[LEVEL_INFO]
|
||||
if 'LOG_LEVEL' in os.environ and os.environ['LOG_LEVEL'] in level_codes:
|
||||
_set_level_code = level_codes[os.environ['LOG_LEVEL']]
|
||||
|
||||
if level in level_codes and level_codes[level] < _set_level_code:
|
||||
return
|
||||
|
||||
if err is not None:
|
||||
msg += "\n异常原因: " + str(err)
|
||||
level = LEVEL_ERROR
|
||||
|
||||
if len(msg) > max_len:
|
||||
msg = msg[:max_len] + "..."
|
||||
now = datetime.datetime.now().strftime("%H:%M:%S")
|
||||
|
||||
pres = []
|
||||
for line in msg.split("\n"):
|
||||
if line == "\n":
|
||||
pres.append("")
|
||||
else:
|
||||
pres.append(f"[{now}] [{tag}/{level}] {line}")
|
||||
|
||||
if level == "INFO":
|
||||
if fg is None:
|
||||
fg = FG_COLORS["green"]
|
||||
if bg is None:
|
||||
bg = BG_COLORS["default"]
|
||||
elif level == "WARN":
|
||||
if fg is None:
|
||||
fg = FG_COLORS["yellow"]
|
||||
if bg is None:
|
||||
bg = BG_COLORS["default"]
|
||||
elif level == "ERROR":
|
||||
if fg is None:
|
||||
fg = FG_COLORS["red"]
|
||||
if bg is None:
|
||||
bg = BG_COLORS["default"]
|
||||
elif level == "CRITICAL":
|
||||
if fg is None:
|
||||
fg = FG_COLORS["purple"]
|
||||
if bg is None:
|
||||
bg = BG_COLORS["default"]
|
||||
|
||||
ret = ""
|
||||
for line in pres:
|
||||
ret += f"\033[{fg};{bg}m{line}\033[0m\n"
|
||||
try:
|
||||
requests.post("http://localhost:6185/api/log",
|
||||
data=ret[:-1].encode(), timeout=1)
|
||||
except BaseException as e:
|
||||
pass
|
||||
self.history.append(ret)
|
||||
if len(self.history) > 100:
|
||||
self.history = self.history[-100:]
|
||||
print(ret[:-1])
|
||||
|
||||
|
||||
log = Logger().log
|
||||
|
||||
|
||||
def port_checker(port: int, host: str = "localhost"):
|
||||
sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sk.settimeout(1)
|
||||
@@ -477,7 +351,7 @@ def save_temp_img(img: Image) -> str:
|
||||
if time.time() - ctime > 3600:
|
||||
os.remove(path)
|
||||
except Exception as e:
|
||||
print(f"清除临时文件失败: {e}", level=LEVEL_WARNING, tag="GeneralUtils")
|
||||
print(f"清除临时文件失败: {e}")
|
||||
|
||||
# 获得时间戳
|
||||
timestamp = int(time.time())
|
||||
|
||||
+12
-13
@@ -103,11 +103,7 @@ def plugin_reload(cached_plugins: RegisteredPlugins):
|
||||
module_path = plugin['module_path']
|
||||
root_dir_name = plugin['pname']
|
||||
|
||||
if module_path in registered_map:
|
||||
# 之前注册过
|
||||
module = importlib.reload(module)
|
||||
else:
|
||||
module = __import__("addons.plugins." +
|
||||
module = __import__("addons.plugins." +
|
||||
root_dir_name + "." + p, fromlist=[p])
|
||||
|
||||
cls = get_classes(p, module)
|
||||
@@ -138,13 +134,15 @@ def plugin_reload(cached_plugins: RegisteredPlugins):
|
||||
except BaseException as e:
|
||||
fail_rec += f"注册插件 {module_path} 失败, 原因: {str(e)}\n"
|
||||
continue
|
||||
cached_plugins.append(RegisteredPlugin(
|
||||
metadata=metadata,
|
||||
plugin_instance=obj,
|
||||
module=module,
|
||||
module_path=module_path,
|
||||
root_dir_name=root_dir_name
|
||||
))
|
||||
|
||||
if module_path not in registered_map:
|
||||
cached_plugins.append(RegisteredPlugin(
|
||||
metadata=metadata,
|
||||
plugin_instance=obj,
|
||||
module=module,
|
||||
module_path=module_path,
|
||||
root_dir_name=root_dir_name
|
||||
))
|
||||
except BaseException as e:
|
||||
traceback.print_exc()
|
||||
fail_rec += f"加载{p}插件出现问题,原因 {str(e)}\n"
|
||||
@@ -208,7 +206,8 @@ def update_plugin(plugin_name: str, cached_plugins: RegisteredPlugins):
|
||||
repo.remotes.origin.pull()
|
||||
# 读取插件的requirements.txt
|
||||
if os.path.exists(os.path.join(plugin_path, "requirements.txt")):
|
||||
if pipmain(['install', '-r', os.path.join(plugin_path, "requirements.txt"), '--quiet']) != 0:
|
||||
print("正在安装插件依赖...")
|
||||
if pipmain(['install', '-r', os.path.join(plugin_path, "requirements.txt")]) != 0:
|
||||
raise Exception("插件依赖安装失败, 需要您手动pip安装对应插件的依赖。")
|
||||
ok, err = plugin_reload(cached_plugins)
|
||||
if not ok:
|
||||
|
||||
+3
-4
@@ -111,7 +111,7 @@ def update_project(update_data: list,
|
||||
else:
|
||||
# 更新到最新版本对应的commit
|
||||
try:
|
||||
repo.remotes.origin.fetch()
|
||||
repo.git.fetch()
|
||||
repo.git.checkout(update_data[0]['tag_name'])
|
||||
if reboot: _reboot()
|
||||
except BaseException as e:
|
||||
@@ -123,7 +123,7 @@ def update_project(update_data: list,
|
||||
for data in update_data:
|
||||
if data['tag_name'] == version:
|
||||
try:
|
||||
repo.remotes.origin.fetch()
|
||||
repo.git.fetch()
|
||||
repo.git.checkout(data['tag_name'])
|
||||
flag = True
|
||||
if reboot: _reboot()
|
||||
@@ -135,8 +135,7 @@ def update_project(update_data: list,
|
||||
def checkout_branch(branch_name: str):
|
||||
repo = find_repo()
|
||||
try:
|
||||
origin = repo.remotes.origin
|
||||
origin.fetch()
|
||||
repo.git.fetch()
|
||||
repo.git.checkout(branch_name)
|
||||
repo.git.pull("origin", branch_name, "-f")
|
||||
return True
|
||||
|
||||
Reference in New Issue
Block a user