refactor: 基本启动流程

This commit is contained in:
RockChinQ
2024-01-23 20:55:20 +08:00
parent e63c6ac723
commit 8d35ecd711
14 changed files with 345 additions and 5 deletions
View File
+31
View File
@@ -0,0 +1,31 @@
from __future__ import annotations
import logging
from ..qqbot import manager as qqbot_mgr
from ..openai import manager as openai_mgr
from ..config import manager as config_mgr
from ..database import manager as database_mgr
from ..utils.center import v2 as center_mgr
class Application:
im_mgr: qqbot_mgr.QQBotManager = None
llm_mgr: openai_mgr.OpenAIInteract = None
cfg_mgr: config_mgr.ConfigManager = None
tips_mgr: config_mgr.ConfigManager = None
db_mgr: database_mgr.DatabaseManager = None
ctr_mgr: center_mgr.V2CenterAPI = None
logger: logging.Logger = None
def __init__(self):
pass
async def run(self):
pass
+124
View File
@@ -0,0 +1,124 @@
from __future__ import print_function
import os
import sys
from . import files
from . import deps
from . import log
from . import config
from . import app
from ..audit import identifier
from ..database import manager as db_mgr
from ..openai import manager as llm_mgr
from ..openai import session as llm_session
from ..openai import dprompt as llm_dprompt
from ..qqbot import manager as im_mgr
from ..qqbot.cmds import aamgr as im_cmd_aamgr
from ..plugin import host as plugin_host
from ..utils.center import v2 as center_v2
from ..utils import updater
from ..utils import context
use_override = False
async def make_app() -> app.Application:
global use_override
generated_files = await files.generate_files()
if generated_files:
print("以下文件不存在,已自动生成,请修改配置文件后重启:")
for file in generated_files:
print("-", file)
sys.exit(0)
missing_deps = await deps.check_deps()
if missing_deps:
print("以下依赖包未安装,将自动安装,请完成后重启程序:")
for dep in missing_deps:
print("-", dep)
await deps.install_deps(missing_deps)
sys.exit(0)
qcg_logger = await log.init_logging()
# 生成标识符
identifier.init()
cfg_mgr = await config.load_config()
context.set_config_manager(cfg_mgr)
cfg = cfg_mgr.data
# 检查是否携带了 --override 或 -r 参数
if '--override' in sys.argv or '-r' in sys.argv:
use_override = True
if use_override:
overrided = await config.override_config_manager(cfg_mgr)
if overrided:
qcg_logger.info("以下配置项已使用 override.json 覆盖:" + ",".join(overrided))
tips_mgr = await config.load_tips()
# 初始化文字转图片
from pkg.utils import text2img
# TODO make it async
text2img.initialize()
# 检查管理员QQ号
if cfg_mgr.data['admin_qq'] == 0:
qcg_logger.warning("未设置管理员QQ号,将无法使用管理员命令,请在 config.py 中修改 admin_qq")
# TODO make it async
llm_dprompt.register_all()
im_cmd_aamgr.register_all()
im_cmd_aamgr.apply_privileges()
# 构建组建实例
ap = app.Application()
ap.logger = qcg_logger
ap.cfg_mgr = cfg_mgr
ap.tips_mgr = tips_mgr
center_v2_api = center_v2.V2CenterAPI(
basic_info={
"host_id": identifier.identifier['host_id'],
"instance_id": identifier.identifier['instance_id'],
"semantic_version": updater.get_current_tag(),
"platform": sys.platform,
},
runtime_info={
"admin_id": "{}".format(cfg['admin_qq']),
"msg_source": cfg['msg_source_adapter'],
}
)
ap.ctr_mgr = center_v2_api
db_mgr_inst = db_mgr.DatabaseManager(ap)
# TODO make it async
db_mgr_inst.initialize_database()
ap.db_mgr = db_mgr_inst
llm_mgr_inst = llm_mgr.OpenAIInteract(ap)
ap.llm_mgr = llm_mgr_inst
# TODO make it async
llm_session.load_sessions()
im_mgr_inst = im_mgr.QQBotManager(first_time_init=True, ap=ap)
ap.im_mgr = im_mgr_inst
# TODO make it async
plugin_host.load_plugins()
# plugin_host.initialize_plugins()
return ap
async def main():
app_inst = await make_app()
await app_inst.run()
+43
View File
@@ -0,0 +1,43 @@
import json
from ..config import manager as config_mgr
from ..config.impls import pymodule
async def load_config() -> config_mgr.ConfigManager:
"""加载配置文件"""
cfg_inst = pymodule.PythonModuleConfigFile(
"config.py",
"config-template.py"
)
cfg_mgr = config_mgr.ConfigManager(cfg_inst)
await cfg_mgr.load_config()
return cfg_mgr
async def load_tips() -> config_mgr.ConfigManager:
"""加载提示文件"""
tips_inst = pymodule.PythonModuleConfigFile(
"tips.py",
"tips-custom-template.py"
)
tips_mgr = config_mgr.ConfigManager(tips_inst)
await tips_mgr.load_config()
return tips_mgr
async def override_config_manager(cfg_mgr: config_mgr.ConfigManager) -> list[str]:
override_json = json.load(open("override.json", "r", encoding="utf-8"))
overrided = []
config = cfg_mgr.data
for key in override_json:
if key in config:
config[key] = override_json[key]
overrided.append(key)
return overrided
+34
View File
@@ -0,0 +1,34 @@
import pip
required_deps = {
"requests": "requests",
"openai": "openai",
"dulwich": "dulwich",
"colorlog": "colorlog",
"mirai": "yiri-mirai-rc",
"func_timeout": "func_timeout",
"PIL": "pillow",
"nakuru": "nakuru-project-idk",
"CallingGPT": "CallingGPT",
"tiktoken": "tiktoken",
"yaml": "pyyaml",
"aiohttp": "aiohttp",
}
async def check_deps() -> list[str]:
global required_deps
missing_deps = []
for dep in required_deps:
try:
__import__(dep)
except ImportError:
missing_deps.append(dep)
return missing_deps
async def install_deps(deps: list[str]):
global required_deps
for dep in deps:
pip.main(["install", required_deps[dep]])
+37
View File
@@ -0,0 +1,37 @@
from __future__ import annotations
import os
import shutil
import sys
required_files = {
"config.py": "config-template.py",
"banlist.py": "res/templates/banlist-template.py",
"tips.py": "tips-custom-template.py",
"sensitive.json": "res/templates/sensitive-template.json",
"scenario/default.json": "scenario/default-template.json",
"cmdpriv.json": "res/templates/cmdpriv-template.json",
}
required_paths = [
"plugins",
"prompts",
"temp",
"logs"
]
async def generate_files() -> list[str]:
global required_files, required_paths
for required_paths in required_paths:
if not os.path.exists(required_paths):
os.mkdir(required_paths)
generated_files = []
for file in required_files:
if not os.path.exists(file):
shutil.copyfile(required_files[file], file)
generated_files.append(file)
return generated_files
+47
View File
@@ -0,0 +1,47 @@
import logging
import os
import sys
import time
import colorlog
log_colors_config = {
'DEBUG': 'green', # cyan white
'INFO': 'white',
'WARNING': 'yellow',
'ERROR': 'red',
'CRITICAL': 'cyan',
}
async def init_logging() -> logging.Logger:
level = logging.INFO
if 'DEBUG' in os.environ and os.environ['DEBUG'] in ['true', '1']:
level = logging.DEBUG
log_file_name = "logs/qcg-%s.log" % time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime())
qcg_logger = logging.getLogger("qcg")
qcg_logger.setLevel(level)
log_handlers: logging.Handler = [
logging.StreamHandler(sys.stdout),
logging.FileHandler(log_file_name)
]
for handler in log_handlers:
handler.setLevel(level)
handler.setFormatter(
colorlog.ColoredFormatter(
fmt="[%(asctime)s.%(msecs)03d] %(pathname)s (%(lineno)d) - [%(levelname)s] :\n%(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
log_colors=log_colors_config
)
)
qcg_logger.addHandler(handler)
return qcg_logger
View File
View File
-1
View File
@@ -14,7 +14,6 @@ class ConfigManager:
def __init__(self, cfg_file: file_model.ConfigFile) -> None: def __init__(self, cfg_file: file_model.ConfigFile) -> None:
self.file = cfg_file self.file = cfg_file
self.data = {} self.data = {}
context.set_config_manager(self)
async def load_config(self): async def load_config(self):
self.data = await self.file.load() self.data = await self.file.load()
+1 -1
View File
@@ -17,7 +17,7 @@ class DatabaseManager:
conn = None conn = None
cursor = None cursor = None
def __init__(self): def __init__(self, *args, **kwargs):
self.reconnect() self.reconnect()
+20 -2
View File
@@ -1,3 +1,5 @@
from __future__ import annotations
import logging import logging
import openai import openai
@@ -8,6 +10,7 @@ from ..utils import context
from ..audit import gatherer from ..audit import gatherer
from ..openai import modelmgr from ..openai import modelmgr
from ..openai.api import model as api_model from ..openai.api import model as api_model
from ..boot import app
class OpenAIInteract: class OpenAIInteract:
@@ -26,12 +29,27 @@ class OpenAIInteract:
client: openai.Client = None client: openai.Client = None
def __init__(self, api_key: str): def __init__(self, ap: app.Application):
cfg= ap.cfg_mgr.data
api_key = cfg['openai_config']['api_key']
self.key_mgr = keymgr.KeysManager(api_key) self.key_mgr = keymgr.KeysManager(api_key)
self.audit_mgr = gatherer.DataGatherer() self.audit_mgr = gatherer.DataGatherer()
# logging.info("文字总使用量:%d", self.audit_mgr.get_total_text_length()) # 配置OpenAI proxy
openai.proxies = None # 先重置,因为重载后可能需要清除proxy
if "http_proxy" in cfg['openai_config'] and cfg['openai_config']["http_proxy"] is not None:
openai.proxies = {
"http": cfg['openai_config']["http_proxy"],
"https": cfg['openai_config']["http_proxy"]
}
# 配置openai api_base
if "reverse_proxy" in cfg['openai_config'] and cfg['openai_config']["reverse_proxy"] is not None:
logging.debug("设置反向代理: "+cfg['openai_config']['reverse_proxy'])
openai.base_url = cfg['openai_config']["reverse_proxy"]
self.client = openai.Client( self.client = openai.Client(
api_key=self.key_mgr.get_using_key(), api_key=self.key_mgr.get_using_key(),
+5 -1
View File
@@ -1,3 +1,5 @@
from __future__ import annotations
import json import json
import os import os
import logging import logging
@@ -16,6 +18,8 @@ from ..plugin import models as plugin_models
import tips as tips_custom import tips as tips_custom
from ..qqbot import adapter as msadapter from ..qqbot import adapter as msadapter
from ..boot import app
# 检查消息是否符合泛响应匹配机制 # 检查消息是否符合泛响应匹配机制
def check_response_rule(group_id:int, text: str): def check_response_rule(group_id:int, text: str):
@@ -101,7 +105,7 @@ class QQBotManager:
ban_person = [] ban_person = []
ban_group = [] ban_group = []
def __init__(self, first_time_init=True): def __init__(self, first_time_init=True, ap: app.Application = None):
config = context.get_config_manager().data config = context.get_config_manager().data
self.timeout = config['process_message_timeout'] self.timeout = config['process_message_timeout']
+3
View File
@@ -6,6 +6,7 @@ from . import apigroup
from .groups import main from .groups import main
from .groups import usage from .groups import usage
from .groups import plugin from .groups import plugin
from ...utils import context
BACKEND_URL = "https://api.qchatgpt.rockchin.top/api/v2" BACKEND_URL = "https://api.qchatgpt.rockchin.top/api/v2"
@@ -33,3 +34,5 @@ class V2CenterAPI:
self.main = main.V2MainDataAPI(BACKEND_URL) self.main = main.V2MainDataAPI(BACKEND_URL)
self.usage = usage.V2UsageDataAPI(BACKEND_URL) self.usage = usage.V2UsageDataAPI(BACKEND_URL)
self.plugin = plugin.V2PluginDataAPI(BACKEND_URL) self.plugin = plugin.V2PluginDataAPI(BACKEND_URL)
context.set_center_v2_api(self)