diff --git a/.gitignore b/.gitignore index b9069a7..7b2a70a 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,4 @@ bard.json !/docker-compose.yaml res/instance_id.json .DS_Store +/data \ No newline at end of file diff --git a/pkg/audit/center/groups/main.py b/pkg/audit/center/groups/main.py index 24e5287..3a31a65 100644 --- a/pkg/audit/center/groups/main.py +++ b/pkg/audit/center/groups/main.py @@ -12,8 +12,7 @@ class V2MainDataAPI(apigroup.APIGroup): super().__init__(prefix+"/main", ap) async def do(self, *args, **kwargs): - config = self.ap.cfg_mgr.data - if not config['report_usage']: + if not self.ap.system_cfg.data['report-usage']: return None return await super().do(*args, **kwargs) diff --git a/pkg/audit/center/groups/plugin.py b/pkg/audit/center/groups/plugin.py index 312d5e8..627b116 100644 --- a/pkg/audit/center/groups/plugin.py +++ b/pkg/audit/center/groups/plugin.py @@ -12,8 +12,7 @@ class V2PluginDataAPI(apigroup.APIGroup): super().__init__(prefix+"/plugin", ap) async def do(self, *args, **kwargs): - config = self.ap.cfg_mgr.data - if not config['report_usage']: + if not self.ap.system_cfg.data['report-usage']: return None return await super().do(*args, **kwargs) diff --git a/pkg/audit/center/groups/usage.py b/pkg/audit/center/groups/usage.py index 02d8938..8a8bdf0 100644 --- a/pkg/audit/center/groups/usage.py +++ b/pkg/audit/center/groups/usage.py @@ -12,8 +12,7 @@ class V2UsageDataAPI(apigroup.APIGroup): super().__init__(prefix+"/usage", ap) async def do(self, *args, **kwargs): - config = self.ap.cfg_mgr.data - if not config['report_usage']: + if not self.ap.system_cfg.data['report-usage']: return None return await super().do(*args, **kwargs) diff --git a/pkg/command/cmdmgr.py b/pkg/command/cmdmgr.py index cf2dbc8..b852175 100644 --- a/pkg/command/cmdmgr.py +++ b/pkg/command/cmdmgr.py @@ -6,7 +6,7 @@ from ..core import app, entities as core_entities from ..provider import entities as llm_entities from . import entities, operator, errors -from .operators import func, plugin, default, reset, list as list_cmd, last, next, delc, resend, prompt, cfg, cmd, help, version, update +from .operators import func, plugin, default, reset, list as list_cmd, last, next, delc, resend, prompt, cmd, help, version, update class CommandManager: @@ -85,9 +85,11 @@ class CommandManager: """ privilege = 1 - if query.sender_id == self.ap.cfg_mgr.data['admin_qq'] \ - or query.sender_id in self.ap.cfg_mgr['admin_qq']: + + if f'{query.launcher_type.value}_{query.launcher_id}' in self.ap.system_cfg.data['admin-sessions']: privilege = 2 + + print(f'privilege: {privilege}') ctx = entities.ExecuteContext( query=query, diff --git a/pkg/command/operator.py b/pkg/command/operator.py index 1b29e6c..d0b731a 100644 --- a/pkg/command/operator.py +++ b/pkg/command/operator.py @@ -24,6 +24,7 @@ def operator_class( cls.help = help cls.usage = usage cls.parent_class = parent_class + cls.lowest_privilege = privilege preregistered_operators.append(cls) diff --git a/pkg/command/operators/cfg.py b/pkg/command/operators/cfg.py deleted file mode 100644 index b67ff3e..0000000 --- a/pkg/command/operators/cfg.py +++ /dev/null @@ -1,98 +0,0 @@ -from __future__ import annotations - -import typing -import json - -from .. import operator, entities, cmdmgr, errors - - -@operator.operator_class( - name="cfg", - help="配置项管理", - usage='!cfg <配置项> [配置值]\n!cfg all', - privilege=2 -) -class CfgOperator(operator.CommandOperator): - - async def execute( - self, - context: entities.ExecuteContext - ) -> typing.AsyncGenerator[entities.CommandReturn, None]: - """执行 - """ - reply = '' - - params = context.crt_params - cfg_mgr = self.ap.cfg_mgr - - false = False - true = True - - reply_str = "" - if len(params) == 0: - yield entities.CommandReturn(error=errors.ParamNotEnoughError('请提供配置项名称')) - else: - cfg_name = params[0] - if cfg_name == 'all': - reply_str = "[bot]所有配置项:\n\n" - for cfg in cfg_mgr.data.keys(): - if not cfg.startswith('__') and not cfg == 'logging': - # 根据配置项类型进行格式化,如果是字典则转换为json并格式化 - if isinstance(cfg_mgr.data[cfg], str): - reply_str += "{}: \"{}\"\n".format(cfg, cfg_mgr.data[cfg]) - elif isinstance(cfg_mgr.data[cfg], dict): - # 不进行unicode转义,并格式化 - reply_str += "{}: {}\n".format(cfg, - json.dumps(cfg_mgr.data[cfg], - ensure_ascii=False, indent=4)) - else: - reply_str += "{}: {}\n".format(cfg, cfg_mgr.data[cfg]) - yield entities.CommandReturn(text=reply_str) - else: - cfg_entry_path = cfg_name.split('.') - - try: - if len(params) == 1: # 未指定配置值,返回配置项值 - cfg_entry = cfg_mgr.data[cfg_entry_path[0]] - if len(cfg_entry_path) > 1: - for i in range(1, len(cfg_entry_path)): - cfg_entry = cfg_entry[cfg_entry_path[i]] - - if isinstance(cfg_entry, str): - reply_str = "[bot]配置项{}: \"{}\"\n".format(cfg_name, cfg_entry) - elif isinstance(cfg_entry, dict): - reply_str = "[bot]配置项{}: {}\n".format(cfg_name, - json.dumps(cfg_entry, - ensure_ascii=False, indent=4)) - else: - reply_str = "[bot]配置项{}: {}\n".format(cfg_name, cfg_entry) - - yield entities.CommandReturn(text=reply_str) - else: - cfg_value = " ".join(params[1:]) - - cfg_value = eval(cfg_value) - - cfg_entry = cfg_mgr.data[cfg_entry_path[0]] - if len(cfg_entry_path) > 1: - for i in range(1, len(cfg_entry_path) - 1): - cfg_entry = cfg_entry[cfg_entry_path[i]] - if isinstance(cfg_entry[cfg_entry_path[-1]], type(cfg_value)): - cfg_entry[cfg_entry_path[-1]] = cfg_value - yield entities.CommandReturn(text="配置项{}修改成功".format(cfg_name)) - else: - # reply = ["[bot]err:配置项{}类型不匹配".format(cfg_name)] - yield entities.CommandReturn(error=errors.CommandOperationError("配置项{}类型不匹配".format(cfg_name))) - else: - cfg_mgr.data[cfg_entry_path[0]] = cfg_value - # reply = ["[bot]配置项{}修改成功".format(cfg_name)] - yield entities.CommandReturn(text="配置项{}修改成功".format(cfg_name)) - except KeyError: - # reply = ["[bot]err:未找到配置项 {}".format(cfg_name)] - yield entities.CommandReturn(error=errors.CommandOperationError("未找到配置项 {}".format(cfg_name))) - except NameError: - # reply = ["[bot]err:值{}不合法(字符串需要使用双引号包裹)".format(cfg_value)] - yield entities.CommandReturn(error=errors.CommandOperationError("值{}不合法(字符串需要使用双引号包裹)".format(cfg_value))) - except ValueError: - # reply = ["[bot]err:未找到配置项 {}".format(cfg_name)] - yield entities.CommandReturn(error=errors.CommandOperationError("未找到配置项 {}".format(cfg_name))) diff --git a/pkg/command/operators/help.py b/pkg/command/operators/help.py index c99c294..570e103 100644 --- a/pkg/command/operators/help.py +++ b/pkg/command/operators/help.py @@ -16,7 +16,7 @@ class HelpOperator(operator.CommandOperator): self, context: entities.ExecuteContext ) -> typing.AsyncGenerator[entities.CommandReturn, None]: - help = self.ap.tips_mgr.data['help_message'] + help = self.ap.system_cfg.data['help-message'] help += '\n发送命令 !cmd 可查看命令列表' diff --git a/pkg/config/manager.py b/pkg/config/manager.py index 8377a51..b75f020 100644 --- a/pkg/config/manager.py +++ b/pkg/config/manager.py @@ -4,6 +4,9 @@ from . import model as file_model from .impls import pymodule, json as json_file +managers: ConfigManager = [] + + class ConfigManager: """配置文件管理器""" diff --git a/pkg/core/app.py b/pkg/core/app.py index cd9607b..f91d898 100644 --- a/pkg/core/app.py +++ b/pkg/core/app.py @@ -31,9 +31,15 @@ class Application: tool_mgr: llm_tool_mgr.ToolManager = None - cfg_mgr: config_mgr.ConfigManager = None + command_cfg: config_mgr.ConfigManager = None - tips_mgr: config_mgr.ConfigManager = None + pipeline_cfg: config_mgr.ConfigManager = None + + platform_cfg: config_mgr.ConfigManager = None + + provider_cfg: config_mgr.ConfigManager = None + + system_cfg: config_mgr.ConfigManager = None ctr_mgr: center_mgr.V2CenterAPI = None diff --git a/pkg/core/boot.py b/pkg/core/boot.py index c251624..ef5fe2e 100644 --- a/pkg/core/boot.py +++ b/pkg/core/boot.py @@ -32,7 +32,7 @@ async def make_app() -> app.Application: generated_files = await files.generate_files() if generated_files: - print("以下文件不存在,已自动生成,请修改配置文件后重启:") + print("以下文件不存在,已自动生成,请按需修改配置文件后重启:") for file in generated_files: print("-", file) @@ -52,31 +52,23 @@ async def make_app() -> app.Application: # 生成标识符 identifier.init() - cfg_mgr = await config.load_python_module_config("config.py", "config-template.py") - cfg = cfg_mgr.data + # ========== 加载配置文件 ========== - # 检查是否携带了 --override 或 -r 参数 - if "--override" in sys.argv or "-r" in sys.argv: - use_override = True + command_cfg = await config.load_json_config("data/config/command.json", "templates/command.json") + pipeline_cfg = await config.load_json_config("data/config/pipeline.json", "templates/pipeline.json") + platform_cfg = await config.load_json_config("data/config/platform.json", "templates/platform.json") + provider_cfg = await config.load_json_config("data/config/provider.json", "templates/provider.json") + system_cfg = await config.load_json_config("data/config/system.json", "templates/system.json") - 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_python_module_config( - "tips.py", "tips-custom-template.py" - ) - - # 检查管理员QQ号 - if cfg_mgr.data["admin_qq"] == 0: - qcg_logger.warning("未设置管理员QQ号,将无法使用管理员命令,请在 config.py 中修改 admin_qq") - - # 构建组建实例 + # ========== 构建应用实例 ========== ap = app.Application() ap.logger = qcg_logger - ap.cfg_mgr = cfg_mgr - ap.tips_mgr = tips_mgr + + ap.command_cfg = command_cfg + ap.pipeline_cfg = pipeline_cfg + ap.platform_cfg = platform_cfg + ap.provider_cfg = provider_cfg + ap.system_cfg = system_cfg proxy_mgr = proxy.ProxyManager(ap) await proxy_mgr.initialize() @@ -95,8 +87,8 @@ async def make_app() -> app.Application: "platform": sys.platform, }, runtime_info={ - "admin_id": "{}".format(cfg["admin_qq"]), - "msg_source": cfg["msg_source_adapter"], + "admin_id": "{}".format(system_cfg.data["admin-sessions"]), + "msg_source": platform_cfg.data["platform-adapter"], }, ) ap.ctr_mgr = center_v2_api diff --git a/pkg/core/bootutils/files.py b/pkg/core/bootutils/files.py index 25a8ba5..975e3aa 100644 --- a/pkg/core/bootutils/files.py +++ b/pkg/core/bootutils/files.py @@ -6,19 +6,25 @@ import sys required_files = { - "config.py": "config-template.py", - "banlist.py": "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", + "plugins/__init__.py": "templates/__init__.py", + "plugins/plugins.json": "templates/plugin-settings.json", + "data/config/command.json": "templates/command.json", + "data/config/pipeline.json": "templates/pipeline.json", + "data/config/platform.json": "templates/platform.json", + "data/config/provider.json": "templates/provider.json", + "data/config/system.json": "templates/system.json", + "data/config/sensitive-words.json": "templates/sensitive-words.json", + "data/scenario/default.json": "templates/scenario-template.json", } required_paths = [ - "plugins", - "prompts", "temp", - "logs" + "data", + "data/prompts", + "data/scenario", + "data/logs", + "data/config", + "plugins" ] async def generate_files() -> list[str]: diff --git a/pkg/core/bootutils/log.py b/pkg/core/bootutils/log.py index 4bc0e4d..a63b7c9 100644 --- a/pkg/core/bootutils/log.py +++ b/pkg/core/bootutils/log.py @@ -21,7 +21,7 @@ async def init_logging() -> logging.Logger: if "DEBUG" in os.environ and os.environ["DEBUG"] in ["true", "1"]: level = logging.DEBUG - log_file_name = "logs/qcg-%s.log" % time.strftime( + log_file_name = "data/logs/qcg-%s.log" % time.strftime( "%Y-%m-%d-%H-%M-%S", time.localtime() ) diff --git a/pkg/core/controller.py b/pkg/core/controller.py index 9cf4727..12403af 100644 --- a/pkg/core/controller.py +++ b/pkg/core/controller.py @@ -8,8 +8,6 @@ from . import app, entities from ..pipeline import entities as pipeline_entities from ..plugin import events -DEFAULT_QUERY_CONCURRENCY = 10 - class Controller: """总控制器 @@ -21,7 +19,7 @@ class Controller: def __init__(self, ap: app.Application): self.ap = ap - self.semaphore = asyncio.Semaphore(DEFAULT_QUERY_CONCURRENCY) + self.semaphore = asyncio.Semaphore(self.ap.system_cfg.data['pipeline-concurrency']) async def consumer(self): """事件处理循环 @@ -150,9 +148,9 @@ class Controller: try: await self._execute_from_stage(0, query) except Exception as e: - self.ap.logger.error(f"处理请求时出错 {query}: {e}") - # self.ap.logger.debug(f"处理请求时出错 {query}: {e}", exc_info=True) - traceback.print_exc() + self.ap.logger.error(f"处理请求时出错 query_id={query.query_id}: {e}") + self.ap.logger.debug(f"Traceback: {traceback.format_exc()}") + # traceback.print_exc() finally: self.ap.logger.debug(f"Query {query} processed") diff --git a/pkg/pipeline/bansess/bansess.py b/pkg/pipeline/bansess/bansess.py index a0f63c3..60d2358 100644 --- a/pkg/pipeline/bansess/bansess.py +++ b/pkg/pipeline/bansess/bansess.py @@ -23,54 +23,28 @@ class BanSessionCheckStage(stage.PipelineStage): stage_inst_name: str ) -> entities.StageProcessResult: - if not self.banlist_mgr.data['enable']: - return entities.StageProcessResult( - result_type=entities.ResultType.CONTINUE, - new_query=query - ) - + found = False + + mode = self.ap.pipeline_cfg.data['access-control']['mode'] + + sess_list = self.ap.pipeline_cfg.data['access-control'][mode] + + if (query.launcher_type == 'group' and 'group_*' in sess_list) \ + or (query.launcher_type == 'person' and 'person_*' in sess_list): + found = True + else: + for sess in sess_list: + if sess == f"{query.launcher_type}_{query.launcher_id}": + found = True + break + result = False - if query.launcher_type == 'group': - if not self.banlist_mgr.data['enable_group']: # 未启用群聊响应 - result = True - # 检查是否显式声明发起人QQ要被person忽略 - elif query.sender_id in self.banlist_mgr.data['person']: - result = True - else: - for group_rule in self.banlist_mgr.data['group']: - if type(group_rule) == int: - if group_rule == query.launcher_id: - result = True - elif type(group_rule) == str: - if group_rule.startswith('!'): - reg_str = group_rule[1:] - if re.match(reg_str, str(query.launcher_id)): - result = False - break - else: - if re.match(group_rule, str(query.launcher_id)): - result = True - elif query.launcher_type == 'person': - if not self.banlist_mgr.data['enable_private']: - result = True - else: - for person_rule in self.banlist_mgr.data['person']: - if type(person_rule) == int: - if person_rule == query.launcher_id: - result = True - elif type(person_rule) == str: - if person_rule.startswith('!'): - reg_str = person_rule[1:] - if re.match(reg_str, str(query.launcher_id)): - result = False - break - else: - if re.match(person_rule, str(query.launcher_id)): - result = True + if mode == 'blacklist': + result = found return entities.StageProcessResult( result_type=entities.ResultType.CONTINUE if not result else entities.ResultType.INTERRUPT, new_query=query, - debug_notice=f'根据禁用列表忽略消息: {query.launcher_type}_{query.launcher_id}' if result else '' + debug_notice=f'根据访问控制忽略消息: {query.launcher_type}_{query.launcher_id}' if result else '' ) diff --git a/pkg/pipeline/cntfilter/cntfilter.py b/pkg/pipeline/cntfilter/cntfilter.py index 7841245..9982a51 100644 --- a/pkg/pipeline/cntfilter/cntfilter.py +++ b/pkg/pipeline/cntfilter/cntfilter.py @@ -24,10 +24,10 @@ class ContentFilterStage(stage.PipelineStage): async def initialize(self): self.filter_chain.append(cntignore.ContentIgnore(self.ap)) - if self.ap.cfg_mgr.data['sensitive_word_filter']: + if self.ap.pipeline_cfg.data['check-sensitive-words']: self.filter_chain.append(banwords.BanWordFilter(self.ap)) - if self.ap.cfg_mgr.data['baidu_check']: + if self.ap.pipeline_cfg.data['baidu-cloud-examine']['enable']: self.filter_chain.append(baiduexamine.BaiduCloudExamine(self.ap)) for filter in self.filter_chain: @@ -41,7 +41,7 @@ class ContentFilterStage(stage.PipelineStage): """请求llm前处理消息 只要有一个不通过就不放行,只放行 PASS 的消息 """ - if not self.ap.cfg_mgr.data['income_msg_check']: + if not self.ap.pipeline_cfg.data['income-msg-check']: return entities.StageProcessResult( result_type=entities.ResultType.CONTINUE, new_query=query diff --git a/pkg/pipeline/cntfilter/filters/baiduexamine.py b/pkg/pipeline/cntfilter/filters/baiduexamine.py index a658897..f72fe96 100644 --- a/pkg/pipeline/cntfilter/filters/baiduexamine.py +++ b/pkg/pipeline/cntfilter/filters/baiduexamine.py @@ -19,8 +19,8 @@ class BaiduCloudExamine(filter_model.ContentFilter): BAIDU_EXAMINE_TOKEN_URL, params={ "grant_type": "client_credentials", - "client_id": self.ap.cfg_mgr.data['baidu_api_key'], - "client_secret": self.ap.cfg_mgr.data['baidu_secret_key'] + "client_id": self.ap.pipeline_cfg.data['baidu-cloud-examine']['api-key'], + "client_secret": self.ap.pipeline_cfg.data['baidu-cloud-examine']['api-secret'] } ) as resp: return (await resp.json())['access_token'] @@ -56,6 +56,6 @@ class BaiduCloudExamine(filter_model.ContentFilter): return entities.FilterResult( level=entities.ResultLevel.BLOCK, replacement=message, - user_notice=self.ap.cfg_mgr.data['inappropriate_message_tips'], + user_notice="消息中存在不合适的内容, 请修改", console_notice=f"百度云判定结果:{conclusion}" - ) \ No newline at end of file + ) diff --git a/pkg/pipeline/cntfilter/filters/banwords.py b/pkg/pipeline/cntfilter/filters/banwords.py index 9451c7b..587f81c 100644 --- a/pkg/pipeline/cntfilter/filters/banwords.py +++ b/pkg/pipeline/cntfilter/filters/banwords.py @@ -13,8 +13,8 @@ class BanWordFilter(filter_model.ContentFilter): async def initialize(self): self.sensitive = await cfg_mgr.load_json_config( - "sensitive.json", - "res/templates/sensitive-template.json" + "data/config/sensitive-words.json", + "templates/sensitive-words.json" ) async def process(self, message: str) -> entities.FilterResult: @@ -39,6 +39,6 @@ class BanWordFilter(filter_model.ContentFilter): return entities.FilterResult( level=entities.ResultLevel.MASKED if found else entities.ResultLevel.PASS, replacement=message, - user_notice='[bot] 消息中存在不合适的内容, 请更换措辞' if found else '', + user_notice='消息中存在不合适的内容, 请修改' if found else '', console_notice='' ) \ No newline at end of file diff --git a/pkg/pipeline/cntfilter/filters/cntignore.py b/pkg/pipeline/cntfilter/filters/cntignore.py index 8140886..92fe94e 100644 --- a/pkg/pipeline/cntfilter/filters/cntignore.py +++ b/pkg/pipeline/cntfilter/filters/cntignore.py @@ -15,8 +15,8 @@ class ContentIgnore(filter_model.ContentFilter): ] async def process(self, message: str) -> entities.FilterResult: - if 'prefix' in self.ap.cfg_mgr.data['ignore_rules']: - for rule in self.ap.cfg_mgr.data['ignore_rules']['prefix']: + if 'prefix' in self.ap.pipeline_cfg.data['ignore-rules']: + for rule in self.ap.pipeline_cfg.data['ignore-rules']['prefix']: if message.startswith(rule): return entities.FilterResult( level=entities.ResultLevel.BLOCK, @@ -25,8 +25,8 @@ class ContentIgnore(filter_model.ContentFilter): console_notice='根据 ignore_rules 中的 prefix 规则,忽略消息' ) - if 'regexp' in self.ap.cfg_mgr.data['ignore_rules']: - for rule in self.ap.cfg_mgr.data['ignore_rules']['regexp']: + if 'regexp' in self.ap.pipeline_cfg.data['ignore-rules']: + for rule in self.ap.pipeline_cfg.data['ignore-rules']['regexp']: if re.search(rule, message): return entities.FilterResult( level=entities.ResultLevel.BLOCK, diff --git a/pkg/pipeline/longtext/longtext.py b/pkg/pipeline/longtext/longtext.py index 72c36cd..85de47c 100644 --- a/pkg/pipeline/longtext/longtext.py +++ b/pkg/pipeline/longtext/longtext.py @@ -19,9 +19,9 @@ class LongTextProcessStage(stage.PipelineStage): strategy_impl: strategy.LongTextStrategy async def initialize(self): - config = self.ap.cfg_mgr.data - if self.ap.cfg_mgr.data['blob_message_strategy'] == 'image': - use_font = config['font_path'] + config = self.ap.platform_cfg.data['long-text-process'] + if config['strategy'] == 'image': + use_font = config['font-path'] try: # 检查是否存在 if not os.path.exists(use_font): @@ -33,23 +33,25 @@ class LongTextProcessStage(stage.PipelineStage): config['blob_message_strategy'] = "forward" else: self.ap.logger.info("使用Windows自带字体:" + use_font) - self.ap.cfg_mgr.data['font_path'] = use_font + config['font-path'] = use_font else: self.ap.logger.warn("未找到字体文件,且无法使用系统自带字体,更换为转发消息组件以发送长消息,您可以在config.py中调整相关设置。") - self.ap.cfg_mgr.data['blob_message_strategy'] = "forward" + + self.ap.platform_cfg.data['long-text-process']['strategy'] = "forward" except: traceback.print_exc() self.ap.logger.error("加载字体文件失败({}),更换为转发消息组件以发送长消息,您可以在config.py中调整相关设置。".format(use_font)) - self.ap.cfg_mgr.data['blob_message_strategy'] = "forward" + + self.ap.platform_cfg.data['long-text-process']['strategy'] = "forward" - if self.ap.cfg_mgr.data['blob_message_strategy'] == 'image': + if config['strategy'] == 'image': self.strategy_impl = image.Text2ImageStrategy(self.ap) - elif self.ap.cfg_mgr.data['blob_message_strategy'] == 'forward': + elif config['strategy'] == 'forward': self.strategy_impl = forward.ForwardComponentStrategy(self.ap) await self.strategy_impl.initialize() async def process(self, query: core_entities.Query, stage_inst_name: str) -> entities.StageProcessResult: - if len(str(query.resp_message_chain)) > self.ap.cfg_mgr.data['blob_message_threshold']: + if len(str(query.resp_message_chain)) > self.ap.platform_cfg.data['long-text-process']['threshold']: query.resp_message_chain = MessageChain(await self.strategy_impl.process(str(query.resp_message_chain))) return entities.StageProcessResult( result_type=entities.ResultType.CONTINUE, diff --git a/pkg/pipeline/longtext/strategies/image.py b/pkg/pipeline/longtext/strategies/image.py index 4f78909..1d350f6 100644 --- a/pkg/pipeline/longtext/strategies/image.py +++ b/pkg/pipeline/longtext/strategies/image.py @@ -19,7 +19,7 @@ class Text2ImageStrategy(strategy_model.LongTextStrategy): text_render_font: ImageFont.FreeTypeFont async def initialize(self): - self.text_render_font = ImageFont.truetype(self.ap.cfg_mgr.data['font_path'], 32, encoding="utf-8") + self.text_render_font = ImageFont.truetype(self.ap.platform_cfg.data['long-text-process']['font-path'], 32, encoding="utf-8") async def process(self, message: str) -> list[MessageComponent]: img_path = self.text_to_image( diff --git a/pkg/pipeline/preproc/preproc.py b/pkg/pipeline/preproc/preproc.py index 1df5daf..ad0d7ff 100644 --- a/pkg/pipeline/preproc/preproc.py +++ b/pkg/pipeline/preproc/preproc.py @@ -52,7 +52,7 @@ class PreProcessor(stage.PipelineStage): query.messages = event_ctx.event.prompt # 根据模型max_tokens剪裁 - max_tokens = min(query.use_model.max_tokens, self.ap.cfg_mgr.data['prompt_submit_length']) + max_tokens = min(query.use_model.max_tokens, self.ap.pipeline_cfg.data['submit-messages-tokens']) test_messages = query.prompt.messages + query.messages + [query.user_message] @@ -63,7 +63,7 @@ class PreProcessor(stage.PipelineStage): result_type=entities.ResultType.INTERRUPT, new_query=query, user_notice='输入内容过长,请减少情景预设或者输入内容长度', - console_notice='输入内容过长,请减少情景预设或者输入内容长度,或者增大配置文件中的 prompt_submit_length 项(但不能超过所用模型最大tokens数)' + console_notice='输入内容过长,请减少情景预设或者输入内容长度,或者增大配置文件中的 submit-messages-tokens 项(但不能超过所用模型最大tokens数)' ) query.messages.pop(0) # pop第一个肯定是role=user的 diff --git a/pkg/pipeline/process/handlers/chat.py b/pkg/pipeline/process/handlers/chat.py index c34f229..26c99a2 100644 --- a/pkg/pipeline/process/handlers/chat.py +++ b/pkg/pipeline/process/handlers/chat.py @@ -54,6 +54,12 @@ class ChatMessageHandler(handler.MessageHandler): ) else: + if not self.ap.provider_cfg.data['enable-chat']: + yield entities.StageProcessResult( + result_type=entities.ResultType.INTERRUPT, + new_query=query, + ) + if event_ctx.event.alter is not None: query.message_chain = mirai.MessageChain([ mirai.Plain(event_ctx.event.alter) @@ -83,7 +89,7 @@ class ChatMessageHandler(handler.MessageHandler): yield entities.StageProcessResult( result_type=entities.ResultType.INTERRUPT, new_query=query, - user_notice=self.ap.tips_mgr.data['alter_tip_message'] if self.ap.cfg_mgr.data['hide_exce_info_to_user'] else f'{e}', + user_notice='请求失败' if self.ap.platform_cfg.data['hide-exception-info'] else f'{e}', error_notice=f'{e}', debug_notice=traceback.format_exc() ) diff --git a/pkg/pipeline/process/handlers/command.py b/pkg/pipeline/process/handlers/command.py index 50600a3..d5873e3 100644 --- a/pkg/pipeline/process/handlers/command.py +++ b/pkg/pipeline/process/handlers/command.py @@ -23,8 +23,8 @@ class CommandHandler(handler.MessageHandler): privilege = 1 - if query.sender_id == self.ap.cfg_mgr.data['admin_qq'] \ - or query.sender_id in self.ap.cfg_mgr['admin_qq']: + + if f'{query.launcher_type.value}_{query.launcher_id}' in self.ap.system_cfg.data['admin-sessions']: privilege = 2 spt = str(query.message_chain).strip().split(' ') diff --git a/pkg/pipeline/ratelimit/algos/fixedwin.py b/pkg/pipeline/ratelimit/algos/fixedwin.py index 4996fba..bb69b0d 100644 --- a/pkg/pipeline/ratelimit/algos/fixedwin.py +++ b/pkg/pipeline/ratelimit/algos/fixedwin.py @@ -55,16 +55,16 @@ class FixedWindowAlgo(algo.ReteLimitAlgo): # 获取当前分钟的访问次数 count = container.records.get(now, 0) - limitation = self.ap.cfg_mgr.data['rate_limitation']['default'] + limitation = self.ap.pipeline_cfg.data['rate-limit']['fixwin']['default'] - if session_name in self.ap.cfg_mgr.data['rate_limitation']: - limitation = self.ap.cfg_mgr.data['rate_limitation'][session_name] + if session_name in self.ap.pipeline_cfg.data['rate-limit']['fixwin']: + limitation = self.ap.pipeline_cfg.data['rate-limit']['fixwin'][session_name] # 如果访问次数超过了限制 if count >= limitation: - if self.ap.cfg_mgr.data['rate_limit_strategy'] == 'drop': + if self.ap.pipeline_cfg.data['rate-limit']['strategy'] == 'drop': return False - elif self.ap.cfg_mgr.data['rate_limit_strategy'] == 'wait': + elif self.ap.pipeline_cfg.data['rate-limit']['strategy'] == 'wait': # 等待下一分钟 await asyncio.sleep(60 - time.time() % 60) diff --git a/pkg/pipeline/ratelimit/ratelimit.py b/pkg/pipeline/ratelimit/ratelimit.py index 2e3c670..cc8e4ac 100644 --- a/pkg/pipeline/ratelimit/ratelimit.py +++ b/pkg/pipeline/ratelimit/ratelimit.py @@ -42,7 +42,7 @@ class RateLimit(stage.PipelineStage): result_type=entities.ResultType.INTERRUPT, new_query=query, console_notice=f"根据限速规则忽略 {query.launcher_type.value}:{query.launcher_id} 消息", - user_notice=self.ap.tips_mgr.data['rate_limit_drop_tip'] + user_notice=f"请求数超过限速器设定值,已丢弃本消息。" ) elif stage_inst_name == "ReleaseRateLimitOccupancy": await self.algo.release_access( diff --git a/pkg/pipeline/respback/respback.py b/pkg/pipeline/respback/respback.py index 0a60a79..52b23ab 100644 --- a/pkg/pipeline/respback/respback.py +++ b/pkg/pipeline/respback/respback.py @@ -20,7 +20,7 @@ class SendResponseBackStage(stage.PipelineStage): async def process(self, query: core_entities.Query, stage_inst_name: str) -> entities.StageProcessResult: """处理 """ - random_delay = random.uniform(*self.ap.cfg_mgr.data['force_delay_range']) + random_delay = random.uniform(*self.ap.platform_cfg.data['force-delay']) self.ap.logger.debug( "根据规则强制延迟回复: %s s", diff --git a/pkg/pipeline/resprule/resprule.py b/pkg/pipeline/resprule/resprule.py index 6335a7d..894bbce 100644 --- a/pkg/pipeline/resprule/resprule.py +++ b/pkg/pipeline/resprule/resprule.py @@ -33,13 +33,13 @@ class GroupRespondRuleCheckStage(stage.PipelineStage): async def process(self, query: core_entities.Query, stage_inst_name: str) -> entities.StageProcessResult: - if query.launcher_type != 'group': + if query.launcher_type.value != 'group': return entities.StageProcessResult( result_type=entities.ResultType.CONTINUE, new_query=query ) - rules = self.ap.cfg_mgr.data['response_rules'] + rules = self.ap.pipeline_cfg.data['respond-rules'] use_rule = rules['default'] diff --git a/pkg/pipeline/resprule/rules/prefix.py b/pkg/pipeline/resprule/rules/prefix.py index 31ead5a..b23f5f8 100644 --- a/pkg/pipeline/resprule/rules/prefix.py +++ b/pkg/pipeline/resprule/rules/prefix.py @@ -16,6 +16,7 @@ class PrefixRule(rule_model.GroupRespondRule): for prefix in prefixes: if message_text.startswith(prefix): + return entities.RuleJudgeResult( matching=True, replacement=mirai.MessageChain([ diff --git a/pkg/pipeline/resprule/rules/random.py b/pkg/pipeline/resprule/rules/random.py index 1e8354b..932d00b 100644 --- a/pkg/pipeline/resprule/rules/random.py +++ b/pkg/pipeline/resprule/rules/random.py @@ -14,7 +14,7 @@ class RandomRespRule(rule_model.GroupRespondRule): message_chain: mirai.MessageChain, rule_dict: dict ) -> entities.RuleJudgeResult: - random_rate = rule_dict['random_rate'] + random_rate = rule_dict['random'] return entities.RuleJudgeResult( matching=random.random() < random_rate, diff --git a/pkg/pipeline/wrapper/wrapper.py b/pkg/pipeline/wrapper/wrapper.py index 0625a3d..0278b60 100644 --- a/pkg/pipeline/wrapper/wrapper.py +++ b/pkg/pipeline/wrapper/wrapper.py @@ -85,7 +85,7 @@ class ResponseWrapper(stage.PipelineStage): query.resp_message_chain = mirai.MessageChain([mirai.Plain(reply_text)]) - if self.ap.cfg_mgr.data['trace_function_calls']: + if self.ap.platform_cfg.data['track-function-calls']: event_ctx = await self.ap.plugin_mgr.emit_event( event=events.NormalMessageResponded( diff --git a/pkg/platform/manager.py b/pkg/platform/manager.py index 277d8ff..931c590 100644 --- a/pkg/platform/manager.py +++ b/pkg/platform/manager.py @@ -31,19 +31,16 @@ class PlatformManager: async def initialize(self): - config = self.ap.cfg_mgr.data - - logging.debug("Use adapter:" + config['msg_source_adapter']) - if config['msg_source_adapter'] == 'yirimirai': + if self.ap.platform_cfg.data['platform-adapter'] == 'yiri-mirai': from pkg.platform.sources.yirimirai import YiriMiraiAdapter - mirai_http_api_config = config['mirai_http_api_config'] - self.bot_account_id = config['mirai_http_api_config']['qq'] + mirai_http_api_config = self.ap.platform_cfg.data['yiri-mirai-config'] + self.bot_account_id = mirai_http_api_config['qq'] self.adapter = YiriMiraiAdapter(mirai_http_api_config) - elif config['msg_source_adapter'] == 'nakuru': - from pkg.platform.sources.nakuru import NakuruProjectAdapter - self.adapter = NakuruProjectAdapter(config['nakuru_config']) - self.bot_account_id = self.adapter.bot_account_id + # elif config['msg_source_adapter'] == 'nakuru': + # from pkg.platform.sources.nakuru import NakuruProjectAdapter + # self.adapter = NakuruProjectAdapter(config['nakuru_config']) + # self.bot_account_id = self.adapter.bot_account_id # 保存 account_id 到审计模块 from ..audit.center import apigroup @@ -99,7 +96,7 @@ class PlatformManager: ) # nakuru不区分好友和陌生人,故仅为yirimirai注册陌生人事件 - if config['msg_source_adapter'] == 'yirimirai': + if self.ap.platform_cfg.data['platform-adapter'] == 'yiri-mirai': self.adapter.register_listener( StrangerMessage, on_stranger_message @@ -133,27 +130,26 @@ class PlatformManager: ) async def send(self, event, msg, check_quote=True, check_at_sender=True): - config = self.ap.cfg_mgr.data - if check_at_sender and config['at_sender']: + if check_at_sender and self.ap.platform_cfg.data['at-sender']: msg.insert( 0, Plain(" \n") ) # 当回复的正文中包含换行时,quote可能会自带at,此时就不再单独添加at,只添加换行 - if "\n" not in str(msg[1]) or config['msg_source_adapter'] == 'nakuru': - msg.insert( - 0, - At( - event.sender.id - ) + # if "\n" not in str(msg[1]) or self.ap.platform_cfg.data['platform-adapter'] == 'nakuru': + msg.insert( + 0, + At( + event.sender.id ) + ) await self.adapter.reply_message( event, msg, - quote_origin=True if config['quote_origin'] and check_quote else False + quote_origin=True if self.ap.platform_cfg.data['quote-origin'] and check_quote else False ) # 通知系统管理员 @@ -161,19 +157,16 @@ class PlatformManager: await self.notify_admin_message_chain(MessageChain([Plain("[bot]{}".format(message))])) async def notify_admin_message_chain(self, message: mirai.MessageChain): - config = self.ap.cfg_mgr.data - if config['admin_qq'] != 0 and config['admin_qq'] != []: - logging.info("通知管理员:{}".format(message)) + if self.ap.system_cfg.data['admin-sessions'] != []: admin_list = [] - - if type(config['admin_qq']) == int: - admin_list.append(config['admin_qq']) + for admin in self.ap.system_cfg.data['admin-sessions']: + admin_list.append(admin) for adm in admin_list: self.adapter.send_message( - "person", - adm, + adm.split("_")[0], + adm.split("_")[1], message ) diff --git a/pkg/provider/requester/apis/chatcmpl.py b/pkg/provider/requester/apis/chatcmpl.py index d82152b..8630d4e 100644 --- a/pkg/provider/requester/apis/chatcmpl.py +++ b/pkg/provider/requester/apis/chatcmpl.py @@ -22,8 +22,8 @@ class OpenAIChatCompletion(api.LLMAPIRequester): async def initialize(self): self.client = openai.AsyncClient( api_key="", - base_url=self.ap.cfg_mgr.data["openai_config"]["reverse_proxy"], - timeout=self.ap.cfg_mgr.data["process_message_timeout"], + base_url=self.ap.provider_cfg.data['openai-config']['base_url'], + timeout=self.ap.provider_cfg.data['openai-config']['request-timeout'], ) async def _req( @@ -51,7 +51,7 @@ class OpenAIChatCompletion(api.LLMAPIRequester): ) -> llm_entities.Message: self.client.api_key = use_model.token_mgr.get_token() - args = self.ap.cfg_mgr.data["completion_api_params"].copy() + args = self.ap.provider_cfg.data['openai-config']['chat-completions-params'].copy() args["model"] = use_model.name if use_model.model_name is None else use_model.model_name if use_model.tool_call_supported: diff --git a/pkg/provider/requester/modelmgr.py b/pkg/provider/requester/modelmgr.py index ab5ff9e..dd94297 100644 --- a/pkg/provider/requester/modelmgr.py +++ b/pkg/provider/requester/modelmgr.py @@ -29,7 +29,7 @@ class ModelManager: async def initialize(self): openai_chat_completion = chatcmpl.OpenAIChatCompletion(self.ap) await openai_chat_completion.initialize() - openai_token_mgr = token.TokenManager(self.ap, list(self.ap.cfg_mgr.data['openai_config']['api_key'].values())) + openai_token_mgr = token.TokenManager(self.ap, list(self.ap.provider_cfg.data['openai-config']['api-keys'])) tiktoken_tokenizer = tiktoken.Tiktoken(self.ap) diff --git a/pkg/provider/session/sessionmgr.py b/pkg/provider/session/sessionmgr.py index a20e2b5..a781250 100644 --- a/pkg/provider/session/sessionmgr.py +++ b/pkg/provider/session/sessionmgr.py @@ -25,10 +25,15 @@ class SessionManager: if query.launcher_type == session.launcher_type and query.launcher_id == session.launcher_id: return session + session_concurrency = self.ap.system_cfg.data['session-concurrency']['default'] + + if f'{query.launcher_type.value}_{query.launcher_id}' in self.ap.system_cfg.data['session-concurrency']: + session_concurrency = self.ap.system_cfg.data['session-concurrency'][f'{query.launcher_type.value}_{query.launcher_id}'] + session = core_entities.Session( launcher_type=query.launcher_type, launcher_id=query.launcher_id, - semaphore=asyncio.Semaphore(1) if self.ap.cfg_mgr.data['wait_last_done'] else asyncio.Semaphore(10000), + semaphore=asyncio.Semaphore(session_concurrency), ) self.session_list.append(session) return session @@ -41,7 +46,7 @@ class SessionManager: conversation = core_entities.Conversation( prompt=await self.ap.prompt_mgr.get_prompt(session.use_prompt_name), messages=[], - use_model=await self.ap.model_mgr.get_model_by_name(self.ap.cfg_mgr.data['completion_api_params']['model']), + use_model=await self.ap.model_mgr.get_model_by_name(self.ap.provider_cfg.data['openai-config']['chat-completions-params']['model']), use_funcs=await self.ap.tool_mgr.get_all_functions(), ) session.conversations.append(conversation) diff --git a/pkg/provider/sysprompt/loaders/scenario.py b/pkg/provider/sysprompt/loaders/scenario.py index 917de48..a559ff7 100644 --- a/pkg/provider/sysprompt/loaders/scenario.py +++ b/pkg/provider/sysprompt/loaders/scenario.py @@ -14,8 +14,8 @@ class ScenarioPromptLoader(loader.PromptLoader): async def load(self): """加载Prompt """ - for file in os.listdir("scenarios"): - with open("scenarios/{}".format(file), "r", encoding="utf-8") as f: + for file in os.listdir("data/scenarios"): + with open("data/scenarios/{}".format(file), "r", encoding="utf-8") as f: file_str = f.read() file_name = file.split(".")[0] file_json = json.loads(file_str) diff --git a/pkg/provider/sysprompt/loaders/single.py b/pkg/provider/sysprompt/loaders/single.py index 0b10963..57e06ed 100644 --- a/pkg/provider/sysprompt/loaders/single.py +++ b/pkg/provider/sysprompt/loaders/single.py @@ -14,7 +14,7 @@ class SingleSystemPromptLoader(loader.PromptLoader): """加载Prompt """ - for name, cnt in self.ap.cfg_mgr.data['default_prompt'].items(): + for name, cnt in self.ap.provider_cfg.data['prompt'].items(): prompt = entities.Prompt( name=name, messages=[ @@ -26,8 +26,8 @@ class SingleSystemPromptLoader(loader.PromptLoader): ) self.prompts.append(prompt) - for file in os.listdir("prompts"): - with open("prompts/{}".format(file), "r", encoding="utf-8") as f: + for file in os.listdir("data/prompts"): + with open("data/prompts/{}".format(file), "r", encoding="utf-8") as f: file_str = f.read() file_name = file.split(".")[0] prompt = entities.Prompt( diff --git a/pkg/provider/sysprompt/sysprompt.py b/pkg/provider/sysprompt/sysprompt.py index 5df28ee..5500bb1 100644 --- a/pkg/provider/sysprompt/sysprompt.py +++ b/pkg/provider/sysprompt/sysprompt.py @@ -23,7 +23,7 @@ class PromptManager: "full_scenario": scenario.ScenarioPromptLoader } - loader_cls = loader_map[self.ap.cfg_mgr.data['preset_mode']] + loader_cls = loader_map[self.ap.provider_cfg.data['prompt-mode']] self.loader_inst: loader.PromptLoader = loader_cls(self.ap) diff --git a/pkg/utils/proxy.py b/pkg/utils/proxy.py index 1c5ee18..74228b8 100644 --- a/pkg/utils/proxy.py +++ b/pkg/utils/proxy.py @@ -1,5 +1,8 @@ from __future__ import annotations +import os +import sys + from ..core import app @@ -14,17 +17,15 @@ class ProxyManager: self.forward_proxies = {} async def initialize(self): - config = self.ap.cfg_mgr.data + self.forward_proxies = { + "http": os.getenv("HTTP_PROXY") or os.getenv("http_proxy"), + "https": os.getenv("HTTPS_PROXY") or os.getenv("https_proxy"), + } - return ( - { - "http": config["openai_config"]["proxy"], - "https": config["openai_config"]["proxy"], - } - if "proxy" in config["openai_config"] - and (config["openai_config"]["proxy"] is not None) - else None - ) + if 'http' in self.ap.system_cfg.data['network-proxies']: + self.forward_proxies['http'] = self.ap.system_cfg.data['network-proxies']['http'] + if 'https' in self.ap.system_cfg.data['network-proxies']: + self.forward_proxies['https'] = self.ap.system_cfg.data['network-proxies']['https'] - def get_forward_proxies(self) -> str: + def get_forward_proxies(self) -> dict: return self.forward_proxies diff --git a/templates/__init__.py b/templates/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/templates/command.json b/templates/command.json new file mode 100644 index 0000000..55360fc --- /dev/null +++ b/templates/command.json @@ -0,0 +1,3 @@ +{ + "privilege": {} +} \ No newline at end of file diff --git a/templates/pipeline.json b/templates/pipeline.json new file mode 100644 index 0000000..7284fda --- /dev/null +++ b/templates/pipeline.json @@ -0,0 +1,36 @@ +{ + "access-control":{ + "mode": "blacklist", + "blacklist": [], + "whitelist": [] + }, + "respond-rules": { + "default": { + "at": true, + "prefix": [ + "/ai", "!ai", "!ai", "ai" + ], + "regexp": [], + "random": 0.0 + } + }, + "income-msg-check": true, + "ignore-rules": { + "prefix": ["/"], + "regexp": [] + }, + "check-sensitive-words": true, + "baidu-cloud-examine": { + "enable": false, + "api-key": "", + "api-secret": "" + }, + "submit-messages-tokens": 3072, + "rate-limit": { + "strategy": "drop", + "algo": "fixwin", + "fixwin": { + "default": 60 + } + } +} \ No newline at end of file diff --git a/templates/platform.json b/templates/platform.json new file mode 100644 index 0000000..a25ba3e --- /dev/null +++ b/templates/platform.json @@ -0,0 +1,20 @@ +{ + "platform-adapter": "yiri-mirai", + "yiri-mirai-config": { + "adapter": "WebSocketAdapter", + "host": "localhost", + "port": 8080, + "verifyKey": "yirimirai", + "qq": 123456789 + }, + "track-function-calls": true, + "quote-origin": false, + "at-sender": false, + "force-delay": [0, 0], + "long-text-process": { + "threshold": 256, + "strategy": "forward", + "font-path": "" + }, + "hide-exception-info": true +} \ No newline at end of file diff --git a/templates/plugin-settings.json b/templates/plugin-settings.json new file mode 100644 index 0000000..1d807ed --- /dev/null +++ b/templates/plugin-settings.json @@ -0,0 +1,3 @@ +{ + "plugins": [] +} \ No newline at end of file diff --git a/templates/provider.json b/templates/provider.json new file mode 100644 index 0000000..2336027 --- /dev/null +++ b/templates/provider.json @@ -0,0 +1,17 @@ +{ + "enable-chat": true, + "openai-config": { + "api-keys": [ + "sk-1234567890" + ], + "base_url": "https://api.openai.com/v1", + "chat-completions-params": { + "model": "gpt-3.5-turbo" + }, + "request-timeout": 120 + }, + "prompt-mode": "normal", + "prompt": { + "default": "如果用户之后想获取帮助,请你说”输入!help获取帮助“。" + } +} \ No newline at end of file diff --git a/templates/scenario-template.json b/templates/scenario-template.json new file mode 100644 index 0000000..d9b7267 --- /dev/null +++ b/templates/scenario-template.json @@ -0,0 +1,12 @@ +{ + "prompt": [ + { + "role": "system", + "content": "You are a helpful assistant. 如果我需要帮助,你要说“输入!help获得帮助”" + }, + { + "role": "assistant", + "content": "好的,我是一个能干的AI助手。 如果你需要帮助,我会说“输入!help获得帮助”" + } + ] +} \ No newline at end of file diff --git a/templates/sensitive-words.json b/templates/sensitive-words.json new file mode 100644 index 0000000..61d15ff --- /dev/null +++ b/templates/sensitive-words.json @@ -0,0 +1,78 @@ +{ + "说明": "mask将替换敏感词中的每一个字,若mask_word值不为空,则将敏感词整个替换为mask_word的值", + "mask": "*", + "mask_word": "", + "words": [ + "习近平", + "胡锦涛", + "江泽民", + "温家宝", + "李克强", + "李长春", + "毛泽东", + "邓小平", + "周恩来", + "马克思", + "社会主义", + "共产党", + "共产主义", + "大陆官方", + "北京政权", + "中华帝国", + "中国政府", + "共狗", + "六四事件", + "天安门", + "六四", + "政治局常委", + "两会", + "共青团", + "学潮", + "八九", + "二十大", + "民进党", + "台独", + "台湾独立", + "台湾国", + "国民党", + "台湾民国", + "中华民国", + "pornhub", + "Pornhub", + "[Yy]ou[Pp]orn", + "porn", + "Porn", + "[Xx][Vv]ideos", + "[Rr]ed[Tt]ube", + "[Xx][Hh]amster", + "[Ss]pank[Ww]ire", + "[Ss]pank[Bb]ang", + "[Tt]ube8", + "[Yy]ou[Jj]izz", + "[Bb]razzers", + "[Nn]aughty[ ]?[Aa]merica", + "作爱", + "做爱", + "性交", + "性爱", + "自慰", + "阴茎", + "淫妇", + "肛交", + "交配", + "性关系", + "性活动", + "色情", + "色图", + "涩图", + "裸体", + "小穴", + "淫荡", + "性爱", + "翻墙", + "VPN", + "科学上网", + "挂梯子", + "GFW" + ] +} \ No newline at end of file diff --git a/templates/system.json b/templates/system.json new file mode 100644 index 0000000..c7b0118 --- /dev/null +++ b/templates/system.json @@ -0,0 +1,11 @@ +{ + "admin-sessions": [], + "network-proxies": {}, + "report-usage": true, + "logging-level": "info", + "session-concurrency": { + "default": 1 + }, + "pipeline-concurrency": 20, + "help-message": "QChatGPT - 😎高稳定性、🧩支持插件、🌏实时联网的 ChatGPT QQ 机器人🤖\n链接:https://q.rkcn.top" +} \ No newline at end of file