From e14dece206b26bfba0d7fc89ad866edc9d069332 Mon Sep 17 00:00:00 2001 From: Soulter <905617992@qq.com> Date: Tue, 6 Feb 2024 17:45:02 +0800 Subject: [PATCH] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + model/command/command.py | 87 +++++--------------- model/command/openai_official.py | 25 +++--- model/platform/qq_official.py | 1 - util/updator.py | 132 +++++++++++++++++++++++++++++++ 5 files changed, 164 insertions(+), 82 deletions(-) create mode 100644 util/updator.py diff --git a/.gitignore b/.gitignore index 70b9165d5..99fe286ff 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ configs/config.yaml **/.DS_Store temp cmd_config.json +addons/plugins/ \ No newline at end of file diff --git a/model/command/command.py b/model/command/command.py index 6fcdede6f..1c0cb6c79 100644 --- a/model/command/command.py +++ b/model/command/command.py @@ -14,6 +14,7 @@ import json import util.plugin_util as putil from util.cmd_config import CmdConfig as cc from util.general_utils import Logger +import util.updator from nakuru.entities.components import ( Plain, Image @@ -112,8 +113,8 @@ class Command: elif platform == PLATFORM_GOCQ: user_id = str(message_obj.user_id) - return True, f"你的ID:{user_id}", "plugin" - + return True, f"你在此平台上的ID:{user_id}", "plugin" + def get_new_conf(self, message, role): if role != "admin": return False, f"你的身份组{role}没有权限使用此指令。", "newconf" @@ -200,14 +201,12 @@ class Command: return { "help": "帮助", "keyword": "设置关键词/关键指令回复", - "update": "更新面板", - "update latest": "更新到最新版本", - "update r": "重启机器人", - "reset": "重置会话", + "update": "更新项目", "nick": "设置机器人昵称", "plugin": "插件安装、卸载和重载", - "web on/off": "启动或关闭网页搜索能力", - "/gpt": "切换到OpenAI ChatGPT API", + "web on/off": "LLM 网页搜索能力", + "reset": "重置 LLM 对话", + "/gpt": "切换到 OpenAI 官方接口", "/revgpt": "切换到网页版ChatGPT", } @@ -238,8 +237,10 @@ class Command: finally: return msg - # 接受可变参数 def command_start_with(self, message: str, *args): + ''' + 当消息以指定的指令开头时返回True + ''' for arg in args: if message.startswith(arg) or message.startswith('/'+arg): return True @@ -273,8 +274,7 @@ class Command: 3. keyword d hi 删除hi关键词的回复 4. keyword hi <图片> -当发送hi时会回复图片 -""", "keyword" +当发送hi时会回复图片""", "keyword" del_mode = False if l[1] == "d": @@ -321,68 +321,23 @@ class Command: if role != "admin": return True, "你没有权限使用该指令", "keyword" l = message.split(" ") - try: - repo = Repo() - except git.exc.InvalidGitRepositoryError: - try: - repo = Repo(path="QQChannelChatGPT") - except git.exc.InvalidGitRepositoryError: - repo = Repo(path="AstrBot") if len(l) == 1: - curr_branch = repo.active_branch.name - # 得到本地版本号和最新版本号 - now_commit = repo.head.commit - # 得到远程3条commit列表, 包含commit信息 - origin = repo.remotes.origin - origin.fetch() - commits = list(repo.iter_commits(curr_branch, max_count=3)) - commits_log = '' - index = 1 - for commit in commits: - if commit.message.endswith("\n"): - commits_log += f"[{index}] {commit.message}-----------\n" - else: - commits_log += f"[{index}] {commit.message}\n-----------\n" - index+=1 - # remote_commit_hash = origin.refs.master.commit.hexsha[:6] - remote_commit_hash = origin.refs[curr_branch].commit.hexsha[:6] - return True, f"当前分支: {curr_branch}\n当前版本: {now_commit.hexsha[:6]}\n最新版本: {remote_commit_hash}\n\n3条commit(非最新):\n{str(commits_log)}\nTips:\n1. 使用 update latest 更新至最新版本;\n2. 使用 update checkout <分支名> 切换代码分支。", "update" + try: + update_info = util.updator.check_update() + update_info += "\nTips:\n输入「update latest」更新到最新版本\n输入「update r」重启机器人\n" + return True, update_info, "update" + except BaseException as e: + return False, "检查更新失败: "+str(e), "update" else: if l[1] == "latest": try: - curr_branch = repo.active_branch.name - origin = repo.remotes.origin - repo.git.pull("origin", curr_branch, "-f") - commits = list(repo.iter_commits(curr_branch, max_count=1)) - commit_log = commits[0].message - tag = "update" - if len(l) == 3 and l[2] == "r": - tag = "update latest r" - return True, f"更新成功。新版本内容: \n{commit_log}\nps:重启后生效。输入update r重启(重启指令不返回任何确认信息)。", tag + release_data = util.updator.request_release_info() + util.updator.update_project(release_data) + return True, "更新成功,重启生效。可输入「update r」重启", "update" except BaseException as e: return False, "更新失败: "+str(e), "update" if l[1] == "r": - py = sys.executable - os.execl(py, py, *sys.argv) - if l[1] == 'checkout': - # 切换分支 - if len(l) < 3: - return False, "请提供分支名,如 /update checkout dev_dashboard", "update" - try: - origin = repo.remotes.origin - origin.fetch() - repo.git.checkout(l[2]) - - # 更新分支(强制) - repo.git.pull("origin", l[2], "-f") - - # 获得最新的 commit - commits = list(repo.iter_commits(max_count=1)) - commit_log = commits[0].message - - return True, f"切换分支成功,机器人将在 5 秒内重新启动以应用新的功能。\n当前分支: {l[2]}\n此分支最近更新: \n{commit_log}", "update latest r" - except BaseException as e: - return False, f"切换分支失败。原因: {str(e)}", "update" + util.updator._reboot() def reset(self): return False diff --git a/model/command/openai_official.py b/model/command/openai_official.py index fd036650b..c90603c9b 100644 --- a/model/command/openai_official.py +++ b/model/command/openai_official.py @@ -68,7 +68,7 @@ class CommandOpenAIOfficial(Command): def reset(self, session_id: str, message: str = "reset"): if self.provider is None: - return False, "未启动OpenAI ChatGPT语言模型.", "reset" + return False, "未启用 OpenAI 官方 API", "reset" l = message.split(" ") if len(l) == 1: self.provider.forget(session_id) @@ -81,7 +81,7 @@ class CommandOpenAIOfficial(Command): def his(self, message: str, session_id: str): if self.provider is None: - return False, "未启动OpenAI ChatGPT语言模型.", "his" + return False, "未启用 OpenAI 官方 API", "his" #分页,每页5条 msg = '' size_per_page = 3 @@ -99,17 +99,17 @@ class CommandOpenAIOfficial(Command): def token(self, session_id: str): if self.provider is None: - return False, "未启动OpenAI ChatGPT语言模型.", "token" + return False, "未启用 OpenAI 官方 API", "token" return True, f"会话的token数: {self.provider.get_user_usage_tokens(self.provider.session_dict[session_id])}\n系统最大缓存token数: {self.provider.max_tokens}", "token" def gpt(self): if self.provider is None: - return False, "未启动OpenAI ChatGPT语言模型.", "gpt" + return False, "未启用 OpenAI 官方 API", "gpt" return True, f"OpenAI GPT配置:\n {self.provider.chatGPT_configs}", "gpt" def status(self): if self.provider is None: - return False, "未启动OpenAI ChatGPT语言模型.", "status" + return False, "未启用 OpenAI 官方 API", "status" chatgpt_cfg_str = "" key_stat = self.provider.get_key_stat() index = 1 @@ -131,7 +131,7 @@ class CommandOpenAIOfficial(Command): def key(self, message: str): if self.provider is None: - return False, "未启动OpenAI ChatGPT语言模型.", "reset" + return False, "未启用 OpenAI 官方 API", "reset" l = message.split(" ") if len(l) == 1: msg = "感谢您赞助key,key为官方API使用,请以以下格式赞助:\n/key xxxxx" @@ -177,14 +177,14 @@ class CommandOpenAIOfficial(Command): def unset(self, session_id: str): if self.provider is None: - return False, "未启动OpenAI ChatGPT语言模型.", "unset" + return False, "未启用 OpenAI 官方 API", "unset" self.provider.curr_personality = {} self.provider.forget(session_id) return True, "已清除人格并重置历史记录。", "unset" def set(self, message: str, session_id: str): if self.provider is None: - return False, "未启动OpenAI ChatGPT语言模型.", "set" + return False, "未启用 OpenAI 官方 API", "set" l = message.split(" ") if len(l) == 1: return True, f"【人格文本由PlexPt开源项目awesome-chatgpt-pr \ @@ -256,7 +256,7 @@ class CommandOpenAIOfficial(Command): def draw(self, message): if self.provider is None: - return False, "未启动OpenAI ChatGPT语言模型.", "draw" + return False, "未启用 OpenAI 官方 API", "draw" if message.startswith("/画"): message = message[2:] elif message.startswith("画"): @@ -268,9 +268,4 @@ class CommandOpenAIOfficial(Command): except Exception as e: if 'exceeded' in str(e): return f"OpenAI API错误。原因:\n{str(e)} \n超额了。可自己搭建一个机器人(Github仓库:QQChannelChatGPT)" - return False, f"图片生成失败: {e}", "draw" - - - - - \ No newline at end of file + return False, f"图片生成失败: {e}", "draw" \ No newline at end of file diff --git a/model/platform/qq_official.py b/model/platform/qq_official.py index ce8100ab7..cd3f40f62 100644 --- a/model/platform/qq_official.py +++ b/model/platform/qq_official.py @@ -207,7 +207,6 @@ class QQOfficial(Platform): self._send_wrapper(**data) def _send_wrapper(self, **kwargs): - print(kwargs) if 'channel_id' in kwargs: asyncio.run_coroutine_threadsafe(self.client.api.post_message(**kwargs), self.loop).result() else: diff --git a/util/updator.py b/util/updator.py new file mode 100644 index 000000000..7564301f0 --- /dev/null +++ b/util/updator.py @@ -0,0 +1,132 @@ +has_git = True +try: + import git.exc + from git.repo import Repo +except BaseException as e: + has_git = False +import sys, os +import requests + +def _reboot(): + py = sys.executable + os.execl(py, py, *sys.argv) + +def find_repo() -> Repo: + if not has_git: + raise Exception("未安装 GitPython 库,无法进行更新。") + repo = None + + # 由于项目更名过,因此这里需要多次尝试。 + try: + repo = Repo() + except git.exc.InvalidGitRepositoryError: + try: + repo = Repo(path="QQChannelChatGPT") + except git.exc.InvalidGitRepositoryError: + repo = Repo(path="AstrBot") + if not repo: + raise Exception("在已知的目录下未找到项目位置。请联系项目维护者。") + return repo + +def request_release_info(latest: bool = True) -> list: + ''' + 请求版本信息。 + 返回一个列表,每个元素是一个字典,包含版本号、发布时间、更新内容、commit hash等信息。 + ''' + api_url = "https://api.github.com/repos/Soulter/AstrBot/releases" + result = requests.get(api_url).json() + if latest: + ret = github_api_release_parser([result[0]]) + else: + ret = github_api_release_parser(result) + return ret + +def github_api_release_parser(releases: list) -> list: + ''' + 解析 GitHub API 返回的 releases 信息。 + 返回一个列表,每个元素是一个字典,包含版本号、发布时间、更新内容、commit hash等信息。 + ''' + ret = [] + for release in releases: + version = release['tag_name'] + commit_hash = '' + # 规范是: v3.0.7.xxxxxx,其中xxxxxx为 commit hash + _t = version.split(".") + if len(_t) == 4: + commit_hash = _t[3] + ret.append({ + "version": release['name'], + "published_at": release['published_at'], + "body": release['body'], + "commit_hash": commit_hash + }) + return ret + +def check_update() -> str: + repo = find_repo() + curr_commit = repo.head.commit.hexsha[:6] + update_data = request_release_info() + new_commit = update_data[0]['commit_hash'] + if curr_commit == new_commit: + return "当前已经是最新版本。" + else: + update_info = f"""有新版本可用。 +=== 新版本 === +{update_data[0]['version']} + +=== 发布时间 === +{update_data[0]['published_at']} + +=== 更新内容 === +{update_data[0]['body']}""" + return update_info + +def update_project(update_data: list, + reboot: bool = False, + latest: bool = True, + version: str = ''): + repo = find_repo() + # update_data = request_release_info(latest) + if latest: + # 检查本地commit和最新commit是否一致 + curr_commit = repo.head.commit.hexsha[:6] + new_commit = update_data[0]['commit_hash'] + if curr_commit == '': + raise Exception("无法获取当前版本号对应的版本位置。请联系项目维护者。") + if curr_commit == new_commit: + raise Exception("当前已经是最新版本。") + else: + # 更新到最新版本对应的commit + try: + repo.remotes.origin.fetch() + repo.git.checkout(new_commit) + if reboot: _reboot() + except BaseException as e: + raise e + else: + # 更新到指定版本 + flag = False + for data in update_data: + if data['version'] == version: + try: + repo.remotes.origin.fetch() + repo.git.checkout(data['commit_hash']) + flag = True + if reboot: _reboot() + except BaseException as e: + raise e + else: + continue + if not flag: + raise Exception("未找到指定版本。") + +def checkout_branch(branch_name: str): + repo = find_repo() + try: + origin = repo.remotes.origin + origin.fetch() + repo.git.checkout(branch_name) + repo.git.pull("origin", branch_name, "-f") + return True + except BaseException as e: + raise e \ No newline at end of file