mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-06-03 12:34:37 +00:00
refactor: 基本启动流程
This commit is contained in:
0
pkg/boot/__init__.py
Normal file
0
pkg/boot/__init__.py
Normal file
31
pkg/boot/app.py
Normal file
31
pkg/boot/app.py
Normal 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
124
pkg/boot/boot.py
Normal 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
43
pkg/boot/config.py
Normal 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
34
pkg/boot/deps.py
Normal 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
37
pkg/boot/files.py
Normal 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
47
pkg/boot/log.py
Normal 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
0
pkg/boot/misc.py
Normal file
0
pkg/config/impls/__init__.py
Normal file
0
pkg/config/impls/__init__.py
Normal 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()
|
||||
|
||||
@@ -17,7 +17,7 @@ class DatabaseManager:
|
||||
conn = None
|
||||
cursor = None
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
||||
self.reconnect()
|
||||
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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']
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user