From e228225178b8c492afab79dbd1551970abe34bde Mon Sep 17 00:00:00 2001 From: Rock Chin <1010553892@qq.com> Date: Tue, 28 Mar 2023 03:12:19 +0000 Subject: [PATCH] =?UTF-8?q?refactor:=20=E6=8C=87=E4=BB=A4=E6=B3=A8?= =?UTF-8?q?=E5=86=8C=E6=9E=B6=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.py | 13 +++++--- pkg/qqbot/cmds/__init__.py | 0 pkg/qqbot/cmds/mgr.py | 53 ++++++++++++++++++++++++++++-- pkg/qqbot/cmds/session/__init__.py | 0 pkg/qqbot/cmds/session/reset.py | 2 ++ pkg/qqbot/command.py | 37 +++++++++++---------- 6 files changed, 81 insertions(+), 24 deletions(-) create mode 100644 pkg/qqbot/cmds/__init__.py create mode 100644 pkg/qqbot/cmds/session/__init__.py diff --git a/main.py b/main.py index 807b1e11..ac69286e 100644 --- a/main.py +++ b/main.py @@ -7,6 +7,7 @@ import time import logging import sys +import traceback try: import colorlog @@ -191,8 +192,14 @@ def start(first_time_init=False): import pkg.openai.session import pkg.qqbot.manager import pkg.openai.dprompt + import pkg.qqbot.cmds.mgr - pkg.openai.dprompt.register_all() + try: + pkg.openai.dprompt.register_all() + pkg.qqbot.cmds.mgr.register_all() + except Exception as e: + logging.error(e) + traceback.print_exc() # 配置openai api_base if "reverse_proxy" in config.openai_config and config.openai_config["reverse_proxy"] is not None: @@ -271,10 +278,6 @@ def start(first_time_init=False): threading.Thread( target=run_bot_wrapper ).start() - # 机器人暂时不能放在线程池中 - # pkg.utils.context.get_thread_ctl().submit_sys_task( - # run_bot_wrapper - # ) finally: # 判断若是Windows,输出选择模式可能会暂停程序的警告 if os.name == 'nt': diff --git a/pkg/qqbot/cmds/__init__.py b/pkg/qqbot/cmds/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pkg/qqbot/cmds/mgr.py b/pkg/qqbot/cmds/mgr.py index b04b8ee0..16376cee 100644 --- a/pkg/qqbot/cmds/mgr.py +++ b/pkg/qqbot/cmds/mgr.py @@ -1,5 +1,10 @@ +import importlib +import inspect import logging import copy +import pkgutil +import traceback +import types __commands_tree__ = {} @@ -109,6 +114,9 @@ class Context: class AbstractCommand: """指令抽象类""" + parent: type + """父指令类""" + name: str """指令名""" @@ -179,6 +187,12 @@ class AbstractCommand: # 更新索引 __tree_index__[cls.__module__ + '.' + cls.__name__] = path + [name] + +class CommandPrivilegeError(Exception): + """指令权限不足或不存在异常""" + pass + + # 传入Context对象,广搜命令树,返回执行结果 # 若命令被处理,返回reply列表 # 若命令未被处理,继续执行下一级指令 @@ -198,12 +212,15 @@ def execute(context: Context) -> list: # 从树取出顶级指令 node = __commands_tree__ + path = "" + # 搜当前顶层,找不到则报错 while True: + path = path + ctx.crt_command + "." try: # 检查权限 if ctx.privilege < node[ctx.crt_command]['privilege']: - raise ValueError('权限不足') + raise CommandPrivilegeError('权限不足: {}'.format(path[:-1])) # 执行 execed, reply, ctx.crt_command = node[ctx.crt_command]['cls'].process(ctx) if execed: @@ -214,4 +231,36 @@ def execute(context: Context) -> list: # 删除crt_params第一个参数 ctx.crt_params.pop(0) except KeyError: - raise ValueError('找不到指令: {}'.format(ctx.command)) + raise CommandPrivilegeError('找不到指令: {}'.format(path[:-1])) + + +def register_all(): + """启动时调用此函数注册所有指令 + + 递归处理pkg.qqbot.cmds包下及其子包下所有模块的所有继承于AbstractCommand的类 + """ + # 模块:遍历其中的继承于AbstractCommand的类,进行注册 + # 包:递归处理包下的模块 + # 排除__开头的属性 + import pkg.qqbot.cmds + + def walk(module, prefix, path_prefix): + # 排除不处于pkg.qqbot.cmds中的包 + if not module.__name__.startswith('pkg.qqbot.cmds'): + return + for item in pkgutil.iter_modules(module.__path__): + if item.name.startswith('__'): + continue + + if item.ispkg: + walk(__import__(module.__name__ + '.' + item.name, fromlist=['']), prefix + item.name + '.', path_prefix + item.name + '/') + else: + m = __import__(module.__name__ + '.' + item.name, fromlist=['']) + for name, cls in inspect.getmembers(m, inspect.isclass): + # 检查是否为指令类 + if cls.__module__ == m.__name__ and issubclass(cls, AbstractCommand) and cls != AbstractCommand: + cls.register(cls, cls.name, cls.parent) + + walk(pkg.qqbot.cmds, '', '') + logging.debug(__commands_tree__) + diff --git a/pkg/qqbot/cmds/session/__init__.py b/pkg/qqbot/cmds/session/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pkg/qqbot/cmds/session/reset.py b/pkg/qqbot/cmds/session/reset.py index 8247e0f2..9e888958 100644 --- a/pkg/qqbot/cmds/session/reset.py +++ b/pkg/qqbot/cmds/session/reset.py @@ -4,6 +4,8 @@ import pkg.openai.session import pkg.utils.context class ResetCommand(AbstractCommand): + parent: type = None + name = 'reset' description = '重置当前会话' usage = 'reset' diff --git a/pkg/qqbot/command.py b/pkg/qqbot/command.py index dddc43e1..42ddd7f6 100644 --- a/pkg/qqbot/command.py +++ b/pkg/qqbot/command.py @@ -13,7 +13,8 @@ import pkg.utils.updater import pkg.utils.context import pkg.qqbot.message import pkg.utils.credit as credit -import pkg.qqbot.cmds.model as cmdmodel +# import pkg.qqbot.cmds.model as cmdmodel +import pkg.qqbot.cmds.mgr as cmdmgr from mirai import Image @@ -36,22 +37,24 @@ def process_command(session_name: str, text_message: str, mgr, config, params = [cmd[1:]] + params cmd = 'cfg' - # 选择指令处理函数 - cmd_obj = cmdmodel.search(cmd) - if cmd_obj is not None and (cmd_obj['admin_only'] is False or is_admin): - cmd_func = cmd_obj['func'] - reply = cmd_func( - cmd=cmd, - params=params, - session_name=session_name, - text_message=text_message, - launcher_type=launcher_type, - launcher_id=launcher_id, - sender_id=sender_id, - is_admin=is_admin, - ) - else: - reply = ["[bot]err:未知的指令或权限不足: " + cmd] + # 包装参数 + context = cmdmgr.Context( + command=cmd, + crt_command=cmd, + params=params, + crt_params=params, + session_name=session_name, + text_message=text_message, + launcher_type=launcher_type, + launcher_id=launcher_id, + sender_id=sender_id, + is_admin=is_admin, + privilege=2 if is_admin else 1, # 普通用户1,管理员2 + ) + try: + reply = cmdmgr.execute(context) + except cmdmgr.CommandPrivilegeError as e: + reply = ["[bot]err:{}".format(e)] return reply except Exception as e: