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

0
pkg/boot/__init__.py Normal file
View File

31
pkg/boot/app.py Normal file
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
pkg/boot/boot.py Normal file
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
pkg/boot/config.py Normal file
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
pkg/boot/deps.py Normal file
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
pkg/boot/files.py Normal file
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
pkg/boot/log.py Normal file
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

0
pkg/boot/misc.py Normal file
View File

View File

View File

@@ -14,7 +14,6 @@ class ConfigManager:
def __init__(self, cfg_file: file_model.ConfigFile) -> None:
self.file = cfg_file
self.data = {}
context.set_config_manager(self)
async def load_config(self):
self.data = await self.file.load()

View File

@@ -17,7 +17,7 @@ class DatabaseManager:
conn = None
cursor = None
def __init__(self):
def __init__(self, *args, **kwargs):
self.reconnect()

View File

@@ -1,3 +1,5 @@
from __future__ import annotations
import logging
import openai
@@ -8,6 +10,7 @@ from ..utils import context
from ..audit import gatherer
from ..openai import modelmgr
from ..openai.api import model as api_model
from ..boot import app
class OpenAIInteract:
@@ -26,12 +29,27 @@ class OpenAIInteract:
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.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(
api_key=self.key_mgr.get_using_key(),

View File

@@ -1,3 +1,5 @@
from __future__ import annotations
import json
import os
import logging
@@ -16,6 +18,8 @@ from ..plugin import models as plugin_models
import tips as tips_custom
from ..qqbot import adapter as msadapter
from ..boot import app
# 检查消息是否符合泛响应匹配机制
def check_response_rule(group_id:int, text: str):
@@ -101,7 +105,7 @@ class QQBotManager:
ban_person = []
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
self.timeout = config['process_message_timeout']

View File

@@ -6,6 +6,7 @@ from . import apigroup
from .groups import main
from .groups import usage
from .groups import plugin
from ...utils import context
BACKEND_URL = "https://api.qchatgpt.rockchin.top/api/v2"
@@ -33,3 +34,5 @@ class V2CenterAPI:
self.main = main.V2MainDataAPI(BACKEND_URL)
self.usage = usage.V2UsageDataAPI(BACKEND_URL)
self.plugin = plugin.V2PluginDataAPI(BACKEND_URL)
context.set_center_v2_api(self)