From 3f29464dbdfa6563ad8e2dae950abc9961738bf6 Mon Sep 17 00:00:00 2001
From: RockChinQ <1010553892@qq.com>
Date: Wed, 13 Dec 2023 14:53:39 +0800
Subject: [PATCH 01/15] =?UTF-8?q?feat:=20=E6=A0=87=E8=AF=86=E7=AC=A6?=
=?UTF-8?q?=E7=94=9F=E6=88=90=E5=99=A8=E6=A8=A1=E5=9D=97?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.gitignore | 3 +-
main.py | 5 ++
pkg/audit/identifier.py | 83 ++++++++++++++++++++++++
pkg/database/manager.py | 2 +-
tests/identifier_test/host_identifier.py | 43 ++++++++++++
5 files changed, 134 insertions(+), 2 deletions(-)
create mode 100644 pkg/audit/identifier.py
create mode 100644 tests/identifier_test/host_identifier.py
diff --git a/.gitignore b/.gitignore
index 9d4595de..d671df3a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,4 +30,5 @@ qcapi
claude.json
bard.json
/*yaml
-!/docker-compose.yaml
\ No newline at end of file
+!/docker-compose.yaml
+res/instance_id.json
\ No newline at end of file
diff --git a/main.py b/main.py
index 043500ef..155620a5 100644
--- a/main.py
+++ b/main.py
@@ -145,6 +145,11 @@ async def start_process(first_time_init=False):
global known_exception_caught
import pkg.utils.context
+ # 计算host和instance标识符
+ import pkg.audit.identifier
+ pkg.audit.identifier.init()
+ pkg.audit.identifier.print_out()
+
# 加载配置
cfg_inst: pymodule_cfg.PythonModuleConfigFile = pymodule_cfg.PythonModuleConfigFile(
'config.py',
diff --git a/pkg/audit/identifier.py b/pkg/audit/identifier.py
new file mode 100644
index 00000000..334b7e3b
--- /dev/null
+++ b/pkg/audit/identifier.py
@@ -0,0 +1,83 @@
+import os
+import uuid
+import json
+import time
+
+
+identifier = {
+ 'host_id': '',
+ 'instance_id': '',
+ 'host_create_ts': 0,
+ 'instance_create_ts': 0,
+}
+
+HOST_ID_FILE = os.path.expanduser('~/.qchatgpt/host_id.json')
+INSTANCE_ID_FILE = 'res/instance_id.json'
+
+def init():
+ global identifier
+
+ if not os.path.exists(os.path.expanduser('~/.qchatgpt')):
+ os.mkdir(os.path.expanduser('~/.qchatgpt'))
+
+ if not os.path.exists(HOST_ID_FILE):
+ new_host_id = 'host_'+str(uuid.uuid4())
+ new_host_create_ts = int(time.time())
+
+ with open(HOST_ID_FILE, 'w') as f:
+ json.dump({
+ 'host_id': new_host_id,
+ 'host_create_ts': new_host_create_ts
+ }, f)
+
+ identifier['host_id'] = new_host_id
+ identifier['host_create_ts'] = new_host_create_ts
+ else:
+ loaded_host_id = ''
+ loaded_host_create_ts = 0
+
+ with open(HOST_ID_FILE, 'r') as f:
+ file_content = json.load(f)
+ loaded_host_id = file_content['host_id']
+ loaded_host_create_ts = file_content['host_create_ts']
+
+ identifier['host_id'] = loaded_host_id
+ identifier['host_create_ts'] = loaded_host_create_ts
+
+ # 检查实例 id
+ if os.path.exists(INSTANCE_ID_FILE):
+ instance_id = {}
+ with open(INSTANCE_ID_FILE, 'r') as f:
+ instance_id = json.load(f)
+
+ if instance_id['host_id'] != identifier['host_id']: # 如果实例 id 不是当前主机的,删除
+ os.remove(INSTANCE_ID_FILE)
+
+ if not os.path.exists(INSTANCE_ID_FILE):
+ new_instance_id = 'instance_'+str(uuid.uuid4())
+ new_instance_create_ts = int(time.time())
+
+ with open(INSTANCE_ID_FILE, 'w') as f:
+ json.dump({
+ 'host_id': identifier['host_id'],
+ 'instance_id': new_instance_id,
+ 'instance_create_ts': new_instance_create_ts
+ }, f)
+
+ identifier['instance_id'] = new_instance_id
+ identifier['instance_create_ts'] = new_instance_create_ts
+ else:
+ loaded_instance_id = ''
+ loaded_instance_create_ts = 0
+
+ with open(INSTANCE_ID_FILE, 'r') as f:
+ file_content = json.load(f)
+ loaded_instance_id = file_content['instance_id']
+ loaded_instance_create_ts = file_content['instance_create_ts']
+
+ identifier['instance_id'] = loaded_instance_id
+ identifier['instance_create_ts'] = loaded_instance_create_ts
+
+def print_out():
+ global identifier
+ print(identifier)
diff --git a/pkg/database/manager.py b/pkg/database/manager.py
index f410b418..ad44d512 100644
--- a/pkg/database/manager.py
+++ b/pkg/database/manager.py
@@ -91,7 +91,7 @@ class DatabaseManager:
`json` text not null
)
""")
- print('Database initialized.')
+ # print('Database initialized.')
# session持久化
def persistence_session(self, subject_type: str, subject_number: int, create_timestamp: int,
diff --git a/tests/identifier_test/host_identifier.py b/tests/identifier_test/host_identifier.py
new file mode 100644
index 00000000..64834931
--- /dev/null
+++ b/tests/identifier_test/host_identifier.py
@@ -0,0 +1,43 @@
+import os
+import uuid
+import json
+
+# 向 ~/.qchatgpt 写入一个 标识符
+
+if not os.path.exists(os.path.expanduser('~/.qchatgpt')):
+ os.mkdir(os.path.expanduser('~/.qchatgpt'))
+
+identifier = {
+ "host_id": "host_"+str(uuid.uuid4()),
+}
+
+if not os.path.exists(os.path.expanduser('~/.qchatgpt/host.json')):
+ print('create ~/.qchatgpt/host.json')
+ with open(os.path.expanduser('~/.qchatgpt/host.json'), 'w') as f:
+ json.dump(identifier, f)
+else:
+ print('load ~/.qchatgpt/host.json')
+ with open(os.path.expanduser('~/.qchatgpt/host.json'), 'r') as f:
+ identifier = json.load(f)
+
+print(identifier)
+
+instance_id = {
+ "host_id": identifier['host_id'],
+ "instance_id": "instance_"+str(uuid.uuid4()),
+}
+
+# 实例 id
+if os.path.exists("res/instance_id.json"):
+ with open("res/instance_id.json", 'r') as f:
+ instance_id = json.load(f)
+
+ if instance_id['host_id'] != identifier['host_id']:
+ os.remove("res/instance_id.json")
+
+if not os.path.exists("res/instance_id.json"):
+ print('create res/instance_id.json')
+ with open("res/instance_id.json", 'w') as f:
+ json.dump(instance_id, f)
+
+print(instance_id)
\ No newline at end of file
From d1c24533102453032dc76bdf65ed45e5c9cf851f Mon Sep 17 00:00:00 2001
From: RockChinQ <1010553892@qq.com>
Date: Thu, 21 Dec 2023 16:21:24 +0800
Subject: [PATCH 02/15] =?UTF-8?q?feat:=20=E5=90=AF=E5=8A=A8=E6=97=B6?=
=?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96=E4=B8=AD=E5=A4=AE=E6=9C=8D=E5=8A=A1?=
=?UTF-8?q?=E5=99=A8=20API=20=E4=BA=A4=E4=BA=92=E7=B1=BB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 3 --
main.py | 19 +++++++
pkg/utils/center/__init__.py | 0
pkg/utils/center/apigroup.py | 60 +++++++++++++++++++++
pkg/utils/center/groups/__init__.py | 0
pkg/utils/center/groups/main.py | 48 +++++++++++++++++
pkg/utils/center/groups/plugin.py | 58 +++++++++++++++++++++
pkg/utils/center/groups/usage.py | 81 +++++++++++++++++++++++++++++
pkg/utils/center/v2.py | 33 ++++++++++++
pkg/utils/context.py | 14 +++++
pkg/utils/platform.py | 7 +++
requirements.txt | 1 +
12 files changed, 321 insertions(+), 3 deletions(-)
create mode 100644 pkg/utils/center/__init__.py
create mode 100644 pkg/utils/center/apigroup.py
create mode 100644 pkg/utils/center/groups/__init__.py
create mode 100644 pkg/utils/center/groups/main.py
create mode 100644 pkg/utils/center/groups/plugin.py
create mode 100644 pkg/utils/center/groups/usage.py
create mode 100644 pkg/utils/center/v2.py
create mode 100644 pkg/utils/platform.py
diff --git a/README.md b/README.md
index 5c84f766..df8b7991 100644
--- a/README.md
+++ b/README.md
@@ -29,7 +29,6 @@
-
项目主页 |
部署文档 |
功能介绍 |
@@ -37,6 +36,4 @@
插件介绍
-
-
diff --git a/main.py b/main.py
index 155620a5..00af25fb 100644
--- a/main.py
+++ b/main.py
@@ -163,6 +163,7 @@ async def start_process(first_time_init=False):
complete_tips()
cfg = pkg.utils.context.get_config_manager().data
+
# 更新openai库到最新版本
if 'upgrade_dependencies' not in cfg or cfg['upgrade_dependencies']:
print("正在更新依赖库,请等待...")
@@ -209,6 +210,24 @@ async def start_process(first_time_init=False):
break
except ValueError:
print("请输入数字")
+
+ # 初始化中央服务器 API 交互实例
+ from pkg.utils.center import apigroup
+ from pkg.utils.center import v2 as center_v2
+
+ center_v2_api = center_v2.V2CenterAPI(
+ basic_info={
+ "host_id": pkg.audit.identifier.identifier['host_id'],
+ "instance_id": pkg.audit.identifier.identifier['instance_id'],
+ "semantic_version": pkg.utils.updater.get_current_tag(),
+ "platform": sys.platform,
+ },
+ runtime_info={
+ "admin_qq": cfg['admin_qq'],
+ "msg_source": cfg['msg_source_adapter'],
+ }
+ )
+ pkg.utils.context.set_center_v2_api(center_v2_api)
import pkg.openai.manager
import pkg.database.manager
diff --git a/pkg/utils/center/__init__.py b/pkg/utils/center/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/pkg/utils/center/apigroup.py b/pkg/utils/center/apigroup.py
new file mode 100644
index 00000000..5938fd60
--- /dev/null
+++ b/pkg/utils/center/apigroup.py
@@ -0,0 +1,60 @@
+import abc
+import uuid
+import json
+
+import aiohttp
+
+
+class APIGroup(metaclass=abc.ABCMeta):
+ """API 组抽象类"""
+ _basic_info: dict = None
+ _runtime_info: dict = None
+
+ prefix = None
+
+ def __init__(self, prefix: str):
+ self.prefix = prefix
+
+ async def do(
+ self,
+ method: str,
+ path: str,
+ data: dict = None,
+ params: dict = None,
+ headers: dict = None,
+ **kwargs
+ ):
+ """执行一个请求"""
+ url = self.prefix + path
+ data = json.dumps(data)
+ headers['Content-Type'] = 'application/json'
+ async with aiohttp.ClientSession() as session:
+ async with session.request(
+ method,
+ url,
+ data=data,
+ params=params,
+ headers=headers,
+ **kwargs
+ ) as resp:
+ return await resp.json()
+
+ def gen_rid(
+ self
+ ):
+ """生成一个请求 ID"""
+ return str(uuid.uuid4())
+
+ def basic_info(
+ self
+ ):
+ """获取基本信息"""
+ basic_info = APIGroup._basic_info.copy()
+ basic_info['rid'] = self.gen_rid()
+ return APIGroup._basic_info
+
+ def runtime_info(
+ self
+ ):
+ """获取运行时信息"""
+ return APIGroup._runtime_info
diff --git a/pkg/utils/center/groups/__init__.py b/pkg/utils/center/groups/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/pkg/utils/center/groups/main.py b/pkg/utils/center/groups/main.py
new file mode 100644
index 00000000..71bef9b2
--- /dev/null
+++ b/pkg/utils/center/groups/main.py
@@ -0,0 +1,48 @@
+from __future__ import annotations
+
+from .. import apigroup
+
+
+class V2MainDataAPI(apigroup.APIGroup):
+ """主程序相关 数据API"""
+
+ def __init__(self, prefix: str):
+ super().__init__(prefix+"/main")
+
+ async def post_update_record(
+ self,
+ spent_seconds: int,
+ infer_reason: str,
+ old_version: str,
+ new_version: str,
+ ):
+ """提交更新记录"""
+ return await self.do(
+ "POST",
+ "/update",
+ data={
+ "basic": self.basic_info(),
+ "update_info": {
+ "spent_seconds": spent_seconds,
+ "infer_reason": infer_reason,
+ "old_version": old_version,
+ "new_version": new_version,
+ }
+ }
+ )
+
+ async def post_announcement_showed(
+ self,
+ ids: list[int],
+ ):
+ """提交公告已阅"""
+ return await self.do(
+ "POST",
+ "/announcement",
+ data={
+ "basic": self.basic_info(),
+ "announcement_info": {
+ "ids": ids,
+ }
+ }
+ )
diff --git a/pkg/utils/center/groups/plugin.py b/pkg/utils/center/groups/plugin.py
new file mode 100644
index 00000000..beff8714
--- /dev/null
+++ b/pkg/utils/center/groups/plugin.py
@@ -0,0 +1,58 @@
+from __future__ import annotations
+
+from .. import apigroup
+
+
+class V2PluginDataAPI(apigroup.APIGroup):
+ """插件数据相关 API"""
+
+ def __init__(self, prefix: str):
+ super().__init__(prefix+"/plugin")
+
+ async def post_install_record(
+ self,
+ plugin: dict
+ ):
+ """提交插件安装记录"""
+ return await self.do(
+ "POST",
+ "/install",
+ data={
+ "basic": self.basic_info(),
+ "plugin": plugin,
+ }
+ )
+
+ async def post_remove_record(
+ self,
+ plugin: dict
+ ):
+ """提交插件卸载记录"""
+ return await self.do(
+ "POST",
+ "/remove",
+ data={
+ "basic": self.basic_info(),
+ "plugin": plugin,
+ }
+ )
+
+ async def post_update_record(
+ self,
+ plugin: dict,
+ old_version: str,
+ new_version: str,
+ ):
+ """提交插件更新记录"""
+ return await self.do(
+ "POST",
+ "/update",
+ data={
+ "basic": self.basic_info(),
+ "plugin": plugin,
+ "update_info": {
+ "old_version": old_version,
+ "new_version": new_version,
+ }
+ }
+ )
diff --git a/pkg/utils/center/groups/usage.py b/pkg/utils/center/groups/usage.py
new file mode 100644
index 00000000..338b869a
--- /dev/null
+++ b/pkg/utils/center/groups/usage.py
@@ -0,0 +1,81 @@
+from __future__ import annotations
+
+from .. import apigroup
+
+
+class V2UsageDataAPI(apigroup.APIGroup):
+ """使用量数据相关 API"""
+
+ def __init__(self, prefix: str):
+ super().__init__(prefix+"/usage")
+
+ async def post_query_record(
+ self,
+ session_type: str,
+ session_id: str,
+ query_ability_provider: str,
+ usage: int,
+ model_name: str,
+ response_seconds: int,
+ retry_times: int,
+ ):
+ """提交请求记录"""
+ return await self.do(
+ "POST",
+ "/query",
+ data={
+ "basic": self.basic_info(),
+ "runtime": self.runtime_info(),
+ "session_info": {
+ "type": session_type,
+ "id": session_id,
+ },
+ "query_info": {
+ "ability_provider": query_ability_provider,
+ "usage": usage,
+ "model_name": model_name,
+ "response_seconds": response_seconds,
+ "retry_times": retry_times,
+ }
+ }
+ )
+
+ async def post_event_record(
+ self,
+ plugins: list[dict],
+ event_name: str,
+ ):
+ """提交事件触发记录"""
+ return await self.do(
+ "POST",
+ "/event",
+ data={
+ "basic": self.basic_info(),
+ "runtime": self.runtime_info(),
+ "plugins": plugins,
+ "event_info": {
+ "name": event_name,
+ }
+ }
+ )
+
+ async def post_function_record(
+ self,
+ plugin: dict,
+ function_name: str,
+ function_description: str,
+ ):
+ """提交内容函数使用记录"""
+ return await self.do(
+ "POST",
+ "/function",
+ data={
+ "basic": self.basic_info(),
+ "plugin": plugin,
+ "function_info": {
+ "name": function_name,
+ "description": function_description,
+ }
+ }
+ )
+
diff --git a/pkg/utils/center/v2.py b/pkg/utils/center/v2.py
new file mode 100644
index 00000000..6cda4093
--- /dev/null
+++ b/pkg/utils/center/v2.py
@@ -0,0 +1,33 @@
+from __future__ import annotations
+
+from . import apigroup
+from .groups import main
+from .groups import usage
+from .groups import plugin
+
+
+BACKEND_URL = "https://api.qchatgpt.rockchin.top/api/v2"
+
+class V2CenterAPI:
+ """中央服务器 v2 API 交互类"""
+
+ main: main.V2MainDataAPI = None
+ """主 API 组"""
+
+ usage: usage.V2UsageDataAPI = None
+ """使用量 API 组"""
+
+ plugin: plugin.V2PluginDataAPI = None
+ """插件 API 组"""
+
+ def __init__(self, basic_info: dict = None, runtime_info: dict = None):
+ """初始化"""
+
+ print("basic_info:", basic_info)
+ print("runtime_info:", runtime_info)
+ apigroup.APIGroup._basic_info = basic_info
+ apigroup.APIGroup._runtime_info = runtime_info
+
+ self.main = main.V2MainDataAPI(BACKEND_URL)
+ self.usage = usage.V2UsageDataAPI(BACKEND_URL)
+ self.plugin = plugin.V2PluginDataAPI(BACKEND_URL)
diff --git a/pkg/utils/context.py b/pkg/utils/context.py
index e26c702b..e6a2734a 100644
--- a/pkg/utils/context.py
+++ b/pkg/utils/context.py
@@ -8,6 +8,7 @@ from ..openai import manager as openai_mgr
from ..qqbot import manager as qqbot_mgr
from ..config import manager as config_mgr
from ..plugin import host as plugin_host
+from .center import v2 as center_v2
context = {
@@ -114,3 +115,16 @@ def get_thread_ctl() -> threadctl.ThreadCtl:
t: threadctl.ThreadCtl = context['pool_ctl']
context_lock.release()
return t
+
+
+def set_center_v2_api(inst: center_v2.V2CenterAPI):
+ context_lock.acquire()
+ context['center_v2_api'] = inst
+ context_lock.release()
+
+
+def get_center_v2_api() -> center_v2.V2CenterAPI:
+ context_lock.acquire()
+ t: center_v2.V2CenterAPI = context['center_v2_api']
+ context_lock.release()
+ return t
\ No newline at end of file
diff --git a/pkg/utils/platform.py b/pkg/utils/platform.py
new file mode 100644
index 00000000..b280b071
--- /dev/null
+++ b/pkg/utils/platform.py
@@ -0,0 +1,7 @@
+import os
+import sys
+
+
+def get_platform() -> str:
+ """获取当前平台"""
+ return sys.platform
diff --git a/requirements.txt b/requirements.txt
index 7047d580..c3e29401 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -11,3 +11,4 @@ nakuru-project-idk
CallingGPT
tiktoken
PyYaml
+aiohttp
\ No newline at end of file
From 38357dd68db39fa6f38d59c8f7240723c841ef19 Mon Sep 17 00:00:00 2001
From: RockChinQ <1010553892@qq.com>
Date: Thu, 21 Dec 2023 16:28:45 +0800
Subject: [PATCH 03/15] =?UTF-8?q?perf:=20=E7=AE=80=E5=8C=96=E5=90=AF?=
=?UTF-8?q?=E5=8A=A8=E8=BE=93=E5=87=BA?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
main.py | 3 +--
pkg/openai/keymgr.py | 2 +-
pkg/plugin/host.py | 2 +-
pkg/utils/center/v2.py | 6 ++++--
4 files changed, 7 insertions(+), 6 deletions(-)
diff --git a/main.py b/main.py
index 00af25fb..5fe42013 100644
--- a/main.py
+++ b/main.py
@@ -122,7 +122,7 @@ def complete_tips():
non_exist_keys = []
is_integrity = True
- logging.info("检查tips模块完整性.")
+ logging.debug("检查tips模块完整性.")
tips_template = importlib.import_module('tips-custom-template')
tips = importlib.import_module('tips')
for key in dir(tips_template):
@@ -148,7 +148,6 @@ async def start_process(first_time_init=False):
# 计算host和instance标识符
import pkg.audit.identifier
pkg.audit.identifier.init()
- pkg.audit.identifier.print_out()
# 加载配置
cfg_inst: pymodule_cfg.PythonModuleConfigFile = pymodule_cfg.PythonModuleConfigFile(
diff --git a/pkg/openai/keymgr.py b/pkg/openai/keymgr.py
index ea9c292b..be6d728f 100644
--- a/pkg/openai/keymgr.py
+++ b/pkg/openai/keymgr.py
@@ -75,7 +75,7 @@ class KeysManager:
if self.api_key[key_name] not in self.exceeded:
self.using_key = self.api_key[key_name]
- logging.info("使用api-key:" + key_name)
+ logging.debug("使用api-key:" + key_name)
# 触发插件事件
args = {
diff --git a/pkg/plugin/host.py b/pkg/plugin/host.py
index 27806845..889e57f6 100644
--- a/pkg/plugin/host.py
+++ b/pkg/plugin/host.py
@@ -132,7 +132,7 @@ def load_plugins():
def initialize_plugins():
"""初始化插件"""
- logging.info("初始化插件")
+ logging.debug("初始化插件")
import pkg.plugin.models as models
successfully_initialized_plugins = []
diff --git a/pkg/utils/center/v2.py b/pkg/utils/center/v2.py
index 6cda4093..b1c0a3e6 100644
--- a/pkg/utils/center/v2.py
+++ b/pkg/utils/center/v2.py
@@ -1,5 +1,7 @@
from __future__ import annotations
+import logging
+
from . import apigroup
from .groups import main
from .groups import usage
@@ -23,8 +25,8 @@ class V2CenterAPI:
def __init__(self, basic_info: dict = None, runtime_info: dict = None):
"""初始化"""
- print("basic_info:", basic_info)
- print("runtime_info:", runtime_info)
+ logging.debug("basic_info: %s, runtime_info: %s", basic_info, runtime_info)
+
apigroup.APIGroup._basic_info = basic_info
apigroup.APIGroup._runtime_info = runtime_info
From b8776fba6579064bcc5a424a358b878b7c812a5f Mon Sep 17 00:00:00 2001
From: RockChinQ <1010553892@qq.com>
Date: Thu, 21 Dec 2023 16:44:21 +0800
Subject: [PATCH 04/15] chore: stash
---
main.py | 6 +++---
pkg/qqbot/cmds/system/update.py | 10 +++++++---
pkg/utils/updater.py | 16 +++++++++++++++-
3 files changed, 25 insertions(+), 7 deletions(-)
diff --git a/main.py b/main.py
index 5fe42013..ebb98e92 100644
--- a/main.py
+++ b/main.py
@@ -422,7 +422,7 @@ def stop():
raise e
-def main():
+async def main():
global use_override
# 检查是否携带了 --override 或 -r 参数
if '--override' in sys.argv or '-r' in sys.argv:
@@ -450,7 +450,7 @@ def main():
elif len(sys.argv) > 1 and sys.argv[1] == 'update':
print("正在进行程序更新...")
import pkg.utils.updater as updater
- updater.update_all(cli=True)
+ await updater.update_all(cli=True)
sys.exit(0)
# 关闭urllib的http警告
@@ -486,5 +486,5 @@ def main():
if __name__ == '__main__':
- main()
+ asyncio.run(main())
diff --git a/pkg/qqbot/cmds/system/update.py b/pkg/qqbot/cmds/system/update.py
index d4cca3f3..6d97cd9e 100644
--- a/pkg/qqbot/cmds/system/update.py
+++ b/pkg/qqbot/cmds/system/update.py
@@ -1,4 +1,5 @@
import threading
+import asyncio
import traceback
from .. import aamgr
@@ -20,9 +21,9 @@ class UpdateCommand(aamgr.AbstractCommandNode):
import pkg.utils.reloader
import pkg.utils.context
- def update_task():
+ async def update_task():
try:
- if pkg.utils.updater.update_all():
+ if await pkg.utils.updater.update_all():
pkg.utils.context.get_qqbot_manager().notify_admin("更新完成, 请手动重启程序。")
else:
pkg.utils.context.get_qqbot_manager().notify_admin("无新版本")
@@ -31,7 +32,10 @@ class UpdateCommand(aamgr.AbstractCommandNode):
pkg.utils.context.get_qqbot_manager().notify_admin("更新失败:{}".format(e0))
return
- threading.Thread(target=update_task, daemon=True).start()
+ def wrapper():
+ asyncio.run(update_task())
+
+ threading.Thread(target=wrapper).start()
reply = ["[bot]正在更新,请耐心等待,请勿重复发起更新..."]
diff --git a/pkg/utils/updater.py b/pkg/utils/updater.py
index 767f3621..abbc696a 100644
--- a/pkg/utils/updater.py
+++ b/pkg/utils/updater.py
@@ -1,11 +1,15 @@
+from __future__ import annotations
+
import datetime
import logging
import os.path
+import time
import requests
from . import constants
from . import network
+from . import context
def check_dulwich_closure():
@@ -105,9 +109,12 @@ def compare_version_str(v0: str, v1: str) -> int:
return 0
-def update_all(cli: bool = False) -> bool:
+async def update_all(cli: bool = False) -> bool:
"""检查更新并下载源码"""
+ start_time = time.time()
+
current_tag = get_current_tag()
+ old_tag = current_tag
rls_list = get_release_list()
@@ -200,6 +207,13 @@ def update_all(cli: bool = False) -> bool:
with open("current_tag", "w") as f:
f.write(current_tag)
+ await context.get_center_v2_api().main.post_update_record(
+ spent_seconds=int(time.time()-start_time),
+ infer_reason="update",
+ old_version=old_tag,
+ new_version=current_tag,
+ )
+
# 通知管理员
if not cli:
import pkg.utils.context
From 7c6526d1ea6904c65a3135570f8c8626faea8d2b Mon Sep 17 00:00:00 2001
From: RockChinQ <1010553892@qq.com>
Date: Thu, 21 Dec 2023 16:48:50 +0800
Subject: [PATCH 05/15] =?UTF-8?q?feat:=20=E6=94=B9=E4=B8=BA=E5=90=8C?=
=?UTF-8?q?=E6=AD=A5?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
main.py | 6 +++---
pkg/qqbot/cmds/system/update.py | 10 +++-------
pkg/utils/center/apigroup.py | 21 ++++++++++-----------
pkg/utils/center/groups/main.py | 8 ++++----
pkg/utils/center/groups/plugin.py | 12 ++++++------
pkg/utils/center/groups/usage.py | 12 ++++++------
pkg/utils/updater.py | 4 ++--
7 files changed, 34 insertions(+), 39 deletions(-)
diff --git a/main.py b/main.py
index ebb98e92..5fe42013 100644
--- a/main.py
+++ b/main.py
@@ -422,7 +422,7 @@ def stop():
raise e
-async def main():
+def main():
global use_override
# 检查是否携带了 --override 或 -r 参数
if '--override' in sys.argv or '-r' in sys.argv:
@@ -450,7 +450,7 @@ async def main():
elif len(sys.argv) > 1 and sys.argv[1] == 'update':
print("正在进行程序更新...")
import pkg.utils.updater as updater
- await updater.update_all(cli=True)
+ updater.update_all(cli=True)
sys.exit(0)
# 关闭urllib的http警告
@@ -486,5 +486,5 @@ async def main():
if __name__ == '__main__':
- asyncio.run(main())
+ main()
diff --git a/pkg/qqbot/cmds/system/update.py b/pkg/qqbot/cmds/system/update.py
index 6d97cd9e..d4cca3f3 100644
--- a/pkg/qqbot/cmds/system/update.py
+++ b/pkg/qqbot/cmds/system/update.py
@@ -1,5 +1,4 @@
import threading
-import asyncio
import traceback
from .. import aamgr
@@ -21,9 +20,9 @@ class UpdateCommand(aamgr.AbstractCommandNode):
import pkg.utils.reloader
import pkg.utils.context
- async def update_task():
+ def update_task():
try:
- if await pkg.utils.updater.update_all():
+ if pkg.utils.updater.update_all():
pkg.utils.context.get_qqbot_manager().notify_admin("更新完成, 请手动重启程序。")
else:
pkg.utils.context.get_qqbot_manager().notify_admin("无新版本")
@@ -32,10 +31,7 @@ class UpdateCommand(aamgr.AbstractCommandNode):
pkg.utils.context.get_qqbot_manager().notify_admin("更新失败:{}".format(e0))
return
- def wrapper():
- asyncio.run(update_task())
-
- threading.Thread(target=wrapper).start()
+ threading.Thread(target=update_task, daemon=True).start()
reply = ["[bot]正在更新,请耐心等待,请勿重复发起更新..."]
diff --git a/pkg/utils/center/apigroup.py b/pkg/utils/center/apigroup.py
index 5938fd60..1324ab76 100644
--- a/pkg/utils/center/apigroup.py
+++ b/pkg/utils/center/apigroup.py
@@ -2,7 +2,7 @@ import abc
import uuid
import json
-import aiohttp
+import requests
class APIGroup(metaclass=abc.ABCMeta):
@@ -28,16 +28,15 @@ class APIGroup(metaclass=abc.ABCMeta):
url = self.prefix + path
data = json.dumps(data)
headers['Content-Type'] = 'application/json'
- async with aiohttp.ClientSession() as session:
- async with session.request(
- method,
- url,
- data=data,
- params=params,
- headers=headers,
- **kwargs
- ) as resp:
- return await resp.json()
+
+ return requests.request(
+ method,
+ url,
+ data=data,
+ params=params,
+ headers=headers,
+ **kwargs
+ )
def gen_rid(
self
diff --git a/pkg/utils/center/groups/main.py b/pkg/utils/center/groups/main.py
index 71bef9b2..f158cd79 100644
--- a/pkg/utils/center/groups/main.py
+++ b/pkg/utils/center/groups/main.py
@@ -9,7 +9,7 @@ class V2MainDataAPI(apigroup.APIGroup):
def __init__(self, prefix: str):
super().__init__(prefix+"/main")
- async def post_update_record(
+ def post_update_record(
self,
spent_seconds: int,
infer_reason: str,
@@ -17,7 +17,7 @@ class V2MainDataAPI(apigroup.APIGroup):
new_version: str,
):
"""提交更新记录"""
- return await self.do(
+ return self.do(
"POST",
"/update",
data={
@@ -31,12 +31,12 @@ class V2MainDataAPI(apigroup.APIGroup):
}
)
- async def post_announcement_showed(
+ def post_announcement_showed(
self,
ids: list[int],
):
"""提交公告已阅"""
- return await self.do(
+ return self.do(
"POST",
"/announcement",
data={
diff --git a/pkg/utils/center/groups/plugin.py b/pkg/utils/center/groups/plugin.py
index beff8714..b3ac423c 100644
--- a/pkg/utils/center/groups/plugin.py
+++ b/pkg/utils/center/groups/plugin.py
@@ -9,12 +9,12 @@ class V2PluginDataAPI(apigroup.APIGroup):
def __init__(self, prefix: str):
super().__init__(prefix+"/plugin")
- async def post_install_record(
+ def post_install_record(
self,
plugin: dict
):
"""提交插件安装记录"""
- return await self.do(
+ return self.do(
"POST",
"/install",
data={
@@ -23,12 +23,12 @@ class V2PluginDataAPI(apigroup.APIGroup):
}
)
- async def post_remove_record(
+ def post_remove_record(
self,
plugin: dict
):
"""提交插件卸载记录"""
- return await self.do(
+ return self.do(
"POST",
"/remove",
data={
@@ -37,14 +37,14 @@ class V2PluginDataAPI(apigroup.APIGroup):
}
)
- async def post_update_record(
+ def post_update_record(
self,
plugin: dict,
old_version: str,
new_version: str,
):
"""提交插件更新记录"""
- return await self.do(
+ return self.do(
"POST",
"/update",
data={
diff --git a/pkg/utils/center/groups/usage.py b/pkg/utils/center/groups/usage.py
index 338b869a..6e383a35 100644
--- a/pkg/utils/center/groups/usage.py
+++ b/pkg/utils/center/groups/usage.py
@@ -9,7 +9,7 @@ class V2UsageDataAPI(apigroup.APIGroup):
def __init__(self, prefix: str):
super().__init__(prefix+"/usage")
- async def post_query_record(
+ def post_query_record(
self,
session_type: str,
session_id: str,
@@ -20,7 +20,7 @@ class V2UsageDataAPI(apigroup.APIGroup):
retry_times: int,
):
"""提交请求记录"""
- return await self.do(
+ return self.do(
"POST",
"/query",
data={
@@ -40,13 +40,13 @@ class V2UsageDataAPI(apigroup.APIGroup):
}
)
- async def post_event_record(
+ def post_event_record(
self,
plugins: list[dict],
event_name: str,
):
"""提交事件触发记录"""
- return await self.do(
+ return self.do(
"POST",
"/event",
data={
@@ -59,14 +59,14 @@ class V2UsageDataAPI(apigroup.APIGroup):
}
)
- async def post_function_record(
+ def post_function_record(
self,
plugin: dict,
function_name: str,
function_description: str,
):
"""提交内容函数使用记录"""
- return await self.do(
+ return self.do(
"POST",
"/function",
data={
diff --git a/pkg/utils/updater.py b/pkg/utils/updater.py
index abbc696a..3881298c 100644
--- a/pkg/utils/updater.py
+++ b/pkg/utils/updater.py
@@ -109,7 +109,7 @@ def compare_version_str(v0: str, v1: str) -> int:
return 0
-async def update_all(cli: bool = False) -> bool:
+def update_all(cli: bool = False) -> bool:
"""检查更新并下载源码"""
start_time = time.time()
@@ -207,7 +207,7 @@ async def update_all(cli: bool = False) -> bool:
with open("current_tag", "w") as f:
f.write(current_tag)
- await context.get_center_v2_api().main.post_update_record(
+ context.get_center_v2_api().main.post_update_record(
spent_seconds=int(time.time()-start_time),
infer_reason="update",
old_version=old_tag,
From b69f193a3ef51d6fda85fb37aaec91bb376e882d Mon Sep 17 00:00:00 2001
From: RockChinQ <1010553892@qq.com>
Date: Thu, 21 Dec 2023 17:03:58 +0800
Subject: [PATCH 06/15] =?UTF-8?q?feat:=20main.update=20=E6=8E=A5=E5=8F=A3?=
=?UTF-8?q?=E5=AE=8C=E6=88=90?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
pkg/utils/center/apigroup.py | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/pkg/utils/center/apigroup.py b/pkg/utils/center/apigroup.py
index 1324ab76..ccce0988 100644
--- a/pkg/utils/center/apigroup.py
+++ b/pkg/utils/center/apigroup.py
@@ -1,6 +1,7 @@
import abc
import uuid
import json
+import logging
import requests
@@ -15,13 +16,13 @@ class APIGroup(metaclass=abc.ABCMeta):
def __init__(self, prefix: str):
self.prefix = prefix
- async def do(
+ def do(
self,
method: str,
path: str,
data: dict = None,
params: dict = None,
- headers: dict = None,
+ headers: dict = {},
**kwargs
):
"""执行一个请求"""
@@ -29,7 +30,7 @@ class APIGroup(metaclass=abc.ABCMeta):
data = json.dumps(data)
headers['Content-Type'] = 'application/json'
- return requests.request(
+ ret = requests.request(
method,
url,
data=data,
@@ -37,6 +38,11 @@ class APIGroup(metaclass=abc.ABCMeta):
headers=headers,
**kwargs
)
+
+ logging.debug("data: %s", data)
+
+ logging.debug("ret: %s", ret.json())
+ return ret
def gen_rid(
self
@@ -50,7 +56,7 @@ class APIGroup(metaclass=abc.ABCMeta):
"""获取基本信息"""
basic_info = APIGroup._basic_info.copy()
basic_info['rid'] = self.gen_rid()
- return APIGroup._basic_info
+ return basic_info
def runtime_info(
self
From e02765bf9503b757cb978c58521a7ac732776b5a Mon Sep 17 00:00:00 2001
From: RockChinQ <1010553892@qq.com>
Date: Thu, 21 Dec 2023 17:11:45 +0800
Subject: [PATCH 07/15] =?UTF-8?q?feat:=20main.announcement=20=E6=8E=A5?=
=?UTF-8?q?=E5=8F=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
main.py | 6 +++++
pkg/utils/center/apigroup.py | 51 ++++++++++++++++++++++++++----------
2 files changed, 43 insertions(+), 14 deletions(-)
diff --git a/main.py b/main.py
index 5fe42013..48971941 100644
--- a/main.py
+++ b/main.py
@@ -398,6 +398,12 @@ async def start_process(first_time_init=False):
if len(new_announcement) > 0:
for announcement in new_announcement:
logging.critical("[公告]<{}> {}".format(announcement['time'], announcement['content']))
+
+ # 发送统计数据
+ pkg.utils.context.get_center_v2_api().main.post_announcement_showed(
+ [announcement['id'] for announcement in new_announcement]
+ )
+
except Exception as e:
logging.warning("获取公告失败:{}".format(e))
diff --git a/pkg/utils/center/apigroup.py b/pkg/utils/center/apigroup.py
index ccce0988..94812d59 100644
--- a/pkg/utils/center/apigroup.py
+++ b/pkg/utils/center/apigroup.py
@@ -2,6 +2,7 @@ import abc
import uuid
import json
import logging
+import threading
import requests
@@ -26,23 +27,45 @@ class APIGroup(metaclass=abc.ABCMeta):
**kwargs
):
"""执行一个请求"""
- url = self.prefix + path
- data = json.dumps(data)
- headers['Content-Type'] = 'application/json'
-
- ret = requests.request(
- method,
- url,
- data=data,
- params=params,
- headers=headers,
+ def thr_wrapper(
+ self,
+ method: str,
+ path: str,
+ data: dict = None,
+ params: dict = None,
+ headers: dict = {},
**kwargs
- )
+ ):
+ try:
+ url = self.prefix + path
+ data = json.dumps(data)
+ headers['Content-Type'] = 'application/json'
+
+ ret = requests.request(
+ method,
+ url,
+ data=data,
+ params=params,
+ headers=headers,
+ **kwargs
+ )
- logging.debug("data: %s", data)
+ logging.debug("data: %s", data)
+
+ logging.debug("ret: %s", ret.json())
+ except Exception as e:
+ logging.debug("上报数据失败: %s", e)
+
+ thr = threading.Thread(target=thr_wrapper, args=(
+ self,
+ method,
+ path,
+ data,
+ params,
+ headers,
+ ), kwargs=kwargs)
+ thr.start()
- logging.debug("ret: %s", ret.json())
- return ret
def gen_rid(
self
From 61a47808c8198010c548e4e25e8c075f5f503d0f Mon Sep 17 00:00:00 2001
From: RockChinQ <1010553892@qq.com>
Date: Thu, 21 Dec 2023 17:35:20 +0800
Subject: [PATCH 08/15] chore: typo
---
pkg/qqbot/cmds/aamgr.py | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/pkg/qqbot/cmds/aamgr.py b/pkg/qqbot/cmds/aamgr.py
index 27596c4c..f761063c 100644
--- a/pkg/qqbot/cmds/aamgr.py
+++ b/pkg/qqbot/cmds/aamgr.py
@@ -4,11 +4,10 @@ import pkgutil
import traceback
import json
-
-__command_list__ = {}
-
import tips as tips_custom
+
+__command_list__ = {}
"""命令树
结构:
From 6f6c3af302719256899fa15ca60e16a043f63cf6 Mon Sep 17 00:00:00 2001
From: RockChinQ <1010553892@qq.com>
Date: Thu, 21 Dec 2023 18:04:16 +0800
Subject: [PATCH 09/15] =?UTF-8?q?feat:=20=E6=8F=92=E4=BB=B6=E4=BA=8B?=
=?UTF-8?q?=E4=BB=B6=E8=A7=A6=E5=8F=91=E6=8A=A5=E5=91=8A?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
pkg/plugin/host.py | 29 +++++++++++++++++++++++++++++
1 file changed, 29 insertions(+)
diff --git a/pkg/plugin/host.py b/pkg/plugin/host.py
index 889e57f6..d65a0916 100644
--- a/pkg/plugin/host.py
+++ b/pkg/plugin/host.py
@@ -443,6 +443,8 @@ class PluginHost:
event_context = EventContext(event_name)
logging.debug("触发事件: {} ({})".format(event_name, event_context.eid))
+
+ emitted_plugins = []
for plugin in iter_plugins():
if not plugin['enabled']:
@@ -460,6 +462,8 @@ class PluginHost:
if 'hooks' not in plugin or event_name not in plugin['hooks']:
continue
+ emitted_plugins.append(plugin)
+
hooks = []
if event_name in plugin["hooks"]:
hooks = plugin["hooks"][event_name]
@@ -487,6 +491,31 @@ class PluginHost:
logging.debug("事件 {} ({}) 处理完毕,返回值: {}".format(event_name, event_context.eid,
event_context.__return_value__))
+ if len(emitted_plugins) > 0:
+
+ plugins_info = []
+
+ for plugin in emitted_plugins:
+ name = plugin['name']
+ meta = metadata.get_plugin_metadata(get_plugin_path_name_by_plugin_name(name))
+ remote = meta['source'] if meta != {} else ""
+ author = plugin['author']
+ version = plugin['version']
+
+ plugins_info.append(
+ {
+ "name": name,
+ "remote": remote,
+ "author": author,
+ "version": version,
+ }
+ )
+
+ context.get_center_v2_api().usage.post_event_record(
+ plugins=plugins_info,
+ event_name=event_name,
+ )
+
return event_context
if __name__ == "__main__":
From af8c21f3d4ee42be074b08ca791f8fb34618ebfe Mon Sep 17 00:00:00 2001
From: RockChinQ <1010553892@qq.com>
Date: Thu, 21 Dec 2023 18:19:04 +0800
Subject: [PATCH 10/15] =?UTF-8?q?feat:=20=E5=AE=8C=E5=96=84=20=E6=8F=92?=
=?UTF-8?q?=E4=BB=B6=E4=BA=8B=E4=BB=B6=E8=B0=83=E7=94=A8=E6=8A=A5=E5=91=8A?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
pkg/plugin/host.py | 191 +++++++++++++++++++++++++++------------------
1 file changed, 113 insertions(+), 78 deletions(-)
diff --git a/pkg/plugin/host.py b/pkg/plugin/host.py
index d65a0916..631012a2 100644
--- a/pkg/plugin/host.py
+++ b/pkg/plugin/host.py
@@ -84,23 +84,34 @@ def iter_plugins_name():
__current_module_path__ = ""
-def walk_plugin_path(module, prefix='', path_prefix=''):
+def walk_plugin_path(module, prefix="", path_prefix=""):
global __current_module_path__
"""遍历插件路径"""
for item in pkgutil.iter_modules(module.__path__):
if item.ispkg:
logging.debug("扫描插件包: plugins/{}".format(path_prefix + item.name))
- walk_plugin_path(__import__(module.__name__ + '.' + item.name, fromlist=['']),
- prefix + item.name + '.', path_prefix + item.name + '/')
+ walk_plugin_path(
+ __import__(module.__name__ + "." + item.name, fromlist=[""]),
+ prefix + item.name + ".",
+ path_prefix + item.name + "/",
+ )
else:
try:
- logging.debug("扫描插件模块: plugins/{}".format(path_prefix + item.name + '.py'))
- __current_module_path__ = "plugins/"+path_prefix + item.name + '.py'
+ logging.debug(
+ "扫描插件模块: plugins/{}".format(path_prefix + item.name + ".py")
+ )
+ __current_module_path__ = "plugins/" + path_prefix + item.name + ".py"
- importlib.import_module(module.__name__ + '.' + item.name)
- logging.debug('加载模块: plugins/{} 成功'.format(path_prefix + item.name + '.py'))
+ importlib.import_module(module.__name__ + "." + item.name)
+ logging.debug(
+ "加载模块: plugins/{} 成功".format(path_prefix + item.name + ".py")
+ )
except:
- logging.error('加载模块: plugins/{} 失败: {}'.format(path_prefix + item.name + '.py', sys.exc_info()))
+ logging.error(
+ "加载模块: plugins/{} 失败: {}".format(
+ path_prefix + item.name + ".py", sys.exc_info()
+ )
+ )
traceback.print_exc()
@@ -108,7 +119,7 @@ def load_plugins():
"""加载插件"""
logging.debug("加载插件")
PluginHost()
- walk_plugin_path(__import__('plugins'))
+ walk_plugin_path(__import__("plugins"))
logging.debug(__plugins__)
@@ -141,14 +152,14 @@ def initialize_plugins():
# if not plugin['enabled']:
# continue
try:
- models.__current_registering_plugin__ = plugin['name']
- plugin['instance'] = plugin["class"](plugin_host=context.get_plugin_host())
+ models.__current_registering_plugin__ = plugin["name"]
+ plugin["instance"] = plugin["class"](plugin_host=context.get_plugin_host())
# logging.info("插件 {} 已初始化".format(plugin['name']))
- successfully_initialized_plugins.append(plugin['name'])
+ successfully_initialized_plugins.append(plugin["name"])
except:
- logging.error("插件{}初始化时发生错误: {}".format(plugin['name'], sys.exc_info()))
+ logging.error("插件{}初始化时发生错误: {}".format(plugin["name"], sys.exc_info()))
logging.debug(traceback.format_exc())
-
+
logging.info("以下插件已初始化: {}".format(", ".join(successfully_initialized_plugins)))
@@ -172,9 +183,12 @@ def get_github_plugin_repo_label(repo_url: str) -> list[str]:
"""获取username, repo"""
# 提取 username/repo , 正则表达式
- repo = re.findall(r'(?:https?://github\.com/|git@github\.com:)([^/]+/[^/]+?)(?:\.git|/|$)', repo_url)
+ repo = re.findall(
+ r"(?:https?://github\.com/|git@github\.com:)([^/]+/[^/]+?)(?:\.git|/|$)",
+ repo_url,
+ )
- if len(repo) > 0: # github
+ if len(repo) > 0: # github
return repo[0].split("/")
else:
return None
@@ -183,53 +197,52 @@ def get_github_plugin_repo_label(repo_url: str) -> list[str]:
def download_plugin_source_code(repo_url: str, target_path: str) -> str:
"""下载插件源码"""
# 检查源类型
-
+
# 提取 username/repo , 正则表达式
- repo = get_github_plugin_repo_label(repo_url)
+ repo = get_github_plugin_repo_label(repo_url)
target_path += repo[1]
- if repo is not None: # github
+ if repo is not None: # github
logging.info("从 GitHub 下载插件源码...")
zipball_url = f"https://api.github.com/repos/{'/'.join(repo)}/zipball/HEAD"
zip_resp = requests.get(
- url=zipball_url,
- proxies=network.wrapper_proxies(),
- stream=True
+ url=zipball_url, proxies=network.wrapper_proxies(), stream=True
)
if zip_resp.status_code != 200:
raise Exception("下载源码失败: {}".format(zip_resp.text))
-
- if os.path.exists("temp/"+target_path):
- shutil.rmtree("temp/"+target_path)
+
+ if os.path.exists("temp/" + target_path):
+ shutil.rmtree("temp/" + target_path)
if os.path.exists(target_path):
shutil.rmtree(target_path)
- os.makedirs("temp/"+target_path)
+ os.makedirs("temp/" + target_path)
- with open("temp/"+target_path+"/source.zip", "wb") as f:
+ with open("temp/" + target_path + "/source.zip", "wb") as f:
for chunk in zip_resp.iter_content(chunk_size=1024):
if chunk:
f.write(chunk)
logging.info("下载完成, 解压...")
import zipfile
- with zipfile.ZipFile("temp/"+target_path+"/source.zip", 'r') as zip_ref:
- zip_ref.extractall("temp/"+target_path)
- os.remove("temp/"+target_path+"/source.zip")
+
+ with zipfile.ZipFile("temp/" + target_path + "/source.zip", "r") as zip_ref:
+ zip_ref.extractall("temp/" + target_path)
+ os.remove("temp/" + target_path + "/source.zip")
# 目标是 username-repo-hash , 用正则表达式提取完整的文件夹名,复制到 plugins/repo
import glob
# 获取解压后的文件夹名
- unzip_dir = glob.glob("temp/"+target_path+"/*")[0]
+ unzip_dir = glob.glob("temp/" + target_path + "/*")[0]
# 复制到 plugins/repo
- shutil.copytree(unzip_dir, target_path+"/")
+ shutil.copytree(unzip_dir, target_path + "/")
# 删除解压后的文件夹
shutil.rmtree(unzip_dir)
@@ -237,18 +250,20 @@ def download_plugin_source_code(repo_url: str, target_path: str) -> str:
logging.info("解压完成")
else:
raise Exception("暂不支持的源类型,请使用 GitHub 仓库发行插件。")
-
+
return repo[1]
def check_requirements(path: str):
# 检查此目录是否包含requirements.txt
- if os.path.exists(path+"/requirements.txt"):
+ if os.path.exists(path + "/requirements.txt"):
logging.info("检测到requirements.txt,正在安装依赖")
import pkg.utils.pkgmgr
- pkg.utils.pkgmgr.install_requirements(path+"/requirements.txt")
+
+ pkg.utils.pkgmgr.install_requirements(path + "/requirements.txt")
import pkg.utils.log as log
+
log.reset_logging()
@@ -257,7 +272,7 @@ def install_plugin(repo_url: str):
repo_label = download_plugin_source_code(repo_url, "plugins/")
- check_requirements("plugins/"+repo_label)
+ check_requirements("plugins/" + repo_label)
metadata.set_plugin_metadata(repo_label, repo_url, int(time.time()), "HEAD")
@@ -266,16 +281,16 @@ def uninstall_plugin(plugin_name: str) -> str:
"""卸载插件"""
if plugin_name not in __plugins__:
raise Exception("插件不存在")
-
+
# 获取文件夹路径
- plugin_path = __plugins__[plugin_name]['path'].replace("\\", "/")
+ plugin_path = __plugins__[plugin_name]["path"].replace("\\", "/")
# 剪切路径为plugins/插件名
plugin_path = plugin_path.split("plugins/")[1].split("/")[0]
# 删除文件夹
- shutil.rmtree("plugins/"+plugin_path)
- return "plugins/"+plugin_path
+ shutil.rmtree("plugins/" + plugin_path)
+ return "plugins/" + plugin_path
def update_plugin(plugin_name: str):
@@ -288,11 +303,17 @@ def update_plugin(plugin_name: str):
if meta == {}:
raise Exception("没有此插件元数据信息,无法更新")
- remote_url = meta['source']
- if remote_url == "https://github.com/RockChinQ/QChatGPT" or remote_url == "https://gitee.com/RockChin/QChatGPT" \
- or remote_url == "" or remote_url is None or remote_url == "http://github.com/RockChinQ/QChatGPT" or remote_url == "http://gitee.com/RockChin/QChatGPT":
+ remote_url = meta["source"]
+ if (
+ remote_url == "https://github.com/RockChinQ/QChatGPT"
+ or remote_url == "https://gitee.com/RockChin/QChatGPT"
+ or remote_url == ""
+ or remote_url is None
+ or remote_url == "http://github.com/RockChinQ/QChatGPT"
+ or remote_url == "http://gitee.com/RockChin/QChatGPT"
+ ):
raise Exception("插件没有远程地址记录,无法更新")
-
+
# 重新安装插件
logging.info("正在重新安装插件以进行更新...")
@@ -301,7 +322,7 @@ def update_plugin(plugin_name: str):
def get_plugin_name_by_path_name(plugin_path_name: str) -> str:
for k, v in __plugins__.items():
- if v['path'] == "plugins/"+plugin_path_name+"/main.py":
+ if v["path"] == "plugins/" + plugin_path_name + "/main.py":
return k
return None
@@ -309,8 +330,8 @@ def get_plugin_name_by_path_name(plugin_path_name: str) -> str:
def get_plugin_path_name_by_plugin_name(plugin_name: str) -> str:
if plugin_name not in __plugins__:
return None
-
- plugin_main_module_path = __plugins__[plugin_name]['path']
+
+ plugin_main_module_path = __plugins__[plugin_name]["path"]
plugin_main_module_path = plugin_main_module_path.replace("\\", "/")
@@ -319,8 +340,29 @@ def get_plugin_path_name_by_plugin_name(plugin_name: str) -> str:
return spt[1]
+def get_plugin_info_for_audit(plugin_name: str) -> dict:
+ """获取插件信息"""
+ if plugin_name not in __plugins__:
+ return {}
+ plugin = __plugins__[plugin_name]
+
+ name = plugin["name"]
+ meta = metadata.get_plugin_metadata(get_plugin_path_name_by_plugin_name(name))
+ remote = meta["source"] if meta != {} else ""
+ author = plugin["author"]
+ version = plugin["version"]
+
+ return {
+ "name": name,
+ "remote": remote,
+ "author": author,
+ "version": version,
+ }
+
+
class EventContext:
"""事件上下文"""
+
eid = 0
"""事件编号"""
@@ -395,6 +437,7 @@ class EventContext:
def emit(event_name: str, **kwargs) -> EventContext:
"""触发事件"""
import pkg.utils.context as context
+
if context.get_plugin_host() is None:
return None
return context.get_plugin_host().emit(event_name, **kwargs)
@@ -446,8 +489,7 @@ class PluginHost:
emitted_plugins = []
for plugin in iter_plugins():
-
- if not plugin['enabled']:
+ if not plugin["enabled"]:
continue
# if plugin['instance'] is None:
@@ -459,10 +501,10 @@ class PluginHost:
# logging.error("插件 {} 初始化时发生错误: {}".format(plugin['name'], sys.exc_info()))
# continue
- if 'hooks' not in plugin or event_name not in plugin['hooks']:
+ if "hooks" not in plugin or event_name not in plugin["hooks"]:
continue
- emitted_plugins.append(plugin)
+ emitted_plugins.append(plugin['name'])
hooks = []
if event_name in plugin["hooks"]:
@@ -471,45 +513,37 @@ class PluginHost:
try:
already_prevented_default = event_context.is_prevented_default()
- kwargs['host'] = context.get_plugin_host()
- kwargs['event'] = event_context
+ kwargs["host"] = context.get_plugin_host()
+ kwargs["event"] = event_context
- hook(plugin['instance'], **kwargs)
+ hook(plugin["instance"], **kwargs)
- if event_context.is_prevented_default() and not already_prevented_default:
- logging.debug("插件 {} 已要求阻止事件 {} 的默认行为".format(plugin['name'], event_name))
+ if (
+ event_context.is_prevented_default()
+ and not already_prevented_default
+ ):
+ logging.debug(
+ "插件 {} 已要求阻止事件 {} 的默认行为".format(plugin["name"], event_name)
+ )
except Exception as e:
- logging.error("插件{}响应事件{}时发生错误".format(plugin['name'], event_name))
+ logging.error("插件{}响应事件{}时发生错误".format(plugin["name"], event_name))
logging.error(traceback.format_exc())
# print("done:{}".format(plugin['name']))
if event_context.is_prevented_postorder():
- logging.debug("插件 {} 阻止了后序插件的执行".format(plugin['name']))
+ logging.debug("插件 {} 阻止了后序插件的执行".format(plugin["name"]))
break
- logging.debug("事件 {} ({}) 处理完毕,返回值: {}".format(event_name, event_context.eid,
- event_context.__return_value__))
+ logging.debug(
+ "事件 {} ({}) 处理完毕,返回值: {}".format(
+ event_name, event_context.eid, event_context.__return_value__
+ )
+ )
+ print(emitted_plugins)
if len(emitted_plugins) > 0:
-
- plugins_info = []
-
- for plugin in emitted_plugins:
- name = plugin['name']
- meta = metadata.get_plugin_metadata(get_plugin_path_name_by_plugin_name(name))
- remote = meta['source'] if meta != {} else ""
- author = plugin['author']
- version = plugin['version']
-
- plugins_info.append(
- {
- "name": name,
- "remote": remote,
- "author": author,
- "version": version,
- }
- )
+ plugins_info = [get_plugin_info_for_audit(p) for p in emitted_plugins]
context.get_center_v2_api().usage.post_event_record(
plugins=plugins_info,
@@ -518,5 +552,6 @@ class PluginHost:
return event_context
+
if __name__ == "__main__":
pass
From c10f72cf4c35e3dcec4b46f93ad274db9fd9bb8a Mon Sep 17 00:00:00 2001
From: RockChinQ <1010553892@qq.com>
Date: Thu, 21 Dec 2023 18:36:02 +0800
Subject: [PATCH 11/15] =?UTF-8?q?feat:=20=E5=86=85=E5=AE=B9=E5=87=BD?=
=?UTF-8?q?=E6=95=B0=E8=B0=83=E7=94=A8=E6=8A=A5=E5=91=8A?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
pkg/openai/api/chat_completion.py | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/pkg/openai/api/chat_completion.py b/pkg/openai/api/chat_completion.py
index e308f17d..1e0e1bc5 100644
--- a/pkg/openai/api/chat_completion.py
+++ b/pkg/openai/api/chat_completion.py
@@ -6,6 +6,8 @@ from openai.types.chat import chat_completion_message
from .model import RequestBase
from .. import funcmgr
+from ...plugin import host
+from ...utils import context
class ChatCompletionRequest(RequestBase):
@@ -189,6 +191,16 @@ class ChatCompletionRequest(RequestBase):
ret = "error: execute function failed: {}".format(str(e))
logging.error("函数执行失败: {}".format(str(e)))
+ # 上报数据
+ plugin_info = host.get_plugin_info_for_audit(func_name.split('-')[0])
+ audit_func_name = func_name.split('-')[1]
+ audit_func_desc = funcmgr.get_func_schema(func_name)['description']
+ context.get_center_v2_api().usage.post_function_record(
+ plugin=plugin_info,
+ function_name=audit_func_name,
+ function_description=audit_func_desc,
+ )
+
self.append_message(
role="function",
content=json.dumps(ret, ensure_ascii=False),
From 565066bbcdd39651c4baf8c165fcce70f43a5efe Mon Sep 17 00:00:00 2001
From: RockChinQ <1010553892@qq.com>
Date: Thu, 21 Dec 2023 18:46:48 +0800
Subject: [PATCH 12/15] =?UTF-8?q?feat:=20=E6=8F=92=E4=BB=B6=E7=9B=B8?=
=?UTF-8?q?=E5=85=B3=E4=B8=8A=E6=8A=A5=20API?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
pkg/plugin/host.py | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/pkg/plugin/host.py b/pkg/plugin/host.py
index 631012a2..9128aa76 100644
--- a/pkg/plugin/host.py
+++ b/pkg/plugin/host.py
@@ -276,11 +276,23 @@ def install_plugin(repo_url: str):
metadata.set_plugin_metadata(repo_label, repo_url, int(time.time()), "HEAD")
+ # 上报安装记录
+ context.get_center_v2_api().plugin.post_install_record(
+ plugin={
+ "name": "unknown",
+ "remote": repo_url,
+ "author": "unknown",
+ "version": "HEAD",
+ }
+ )
+
def uninstall_plugin(plugin_name: str) -> str:
"""卸载插件"""
if plugin_name not in __plugins__:
raise Exception("插件不存在")
+
+ plugin_info = get_plugin_info_for_audit(plugin_name)
# 获取文件夹路径
plugin_path = __plugins__[plugin_name]["path"].replace("\\", "/")
@@ -290,6 +302,12 @@ def uninstall_plugin(plugin_name: str) -> str:
# 删除文件夹
shutil.rmtree("plugins/" + plugin_path)
+
+ # 上报卸载记录
+ context.get_center_v2_api().plugin.post_remove_record(
+ plugin=plugin_info
+ )
+
return "plugins/" + plugin_path
@@ -302,6 +320,14 @@ def update_plugin(plugin_name: str):
if meta == {}:
raise Exception("没有此插件元数据信息,无法更新")
+
+ old_plugin_info = get_plugin_info_for_audit(plugin_name)
+
+ context.get_center_v2_api().plugin.post_update_record(
+ plugin=old_plugin_info,
+ old_version=old_plugin_info['version'],
+ new_version='HEAD',
+ )
remote_url = meta["source"]
if (
From 3a63630068114d2e931565d64ad9065cd329c29c Mon Sep 17 00:00:00 2001
From: RockChinQ <1010553892@qq.com>
Date: Thu, 21 Dec 2023 18:51:10 +0800
Subject: [PATCH 13/15] =?UTF-8?q?feat:=20account=5Fid=20=E8=AE=BE=E7=BD=AE?=
=?UTF-8?q?=E9=80=BB=E8=BE=91?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
pkg/qqbot/manager.py | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/pkg/qqbot/manager.py b/pkg/qqbot/manager.py
index 588d6621..b8ba8a9c 100644
--- a/pkg/qqbot/manager.py
+++ b/pkg/qqbot/manager.py
@@ -125,6 +125,10 @@ class QQBotManager:
else:
self.adapter = context.get_qqbot_manager().adapter
self.bot_account_id = context.get_qqbot_manager().bot_account_id
+
+ # 保存 account_id 到审计模块
+ from ..utils.center import apigroup
+ apigroup.APIGroup._runtime_info['account_id'] = self.bot_account_id
context.set_qqbot_manager(self)
From bb12b488871051ab7d03a0134b12b6ab9d959e5a Mon Sep 17 00:00:00 2001
From: RockChinQ <1010553892@qq.com>
Date: Fri, 22 Dec 2023 12:38:27 +0800
Subject: [PATCH 14/15] =?UTF-8?q?feat:=20usage.query=E5=AE=8C=E6=88=90?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
main.py | 2 +-
pkg/openai/session.py | 22 ++++++++++++++++++++++
pkg/qqbot/manager.py | 2 +-
3 files changed, 24 insertions(+), 2 deletions(-)
diff --git a/main.py b/main.py
index 48971941..5ceb67f6 100644
--- a/main.py
+++ b/main.py
@@ -222,7 +222,7 @@ async def start_process(first_time_init=False):
"platform": sys.platform,
},
runtime_info={
- "admin_qq": cfg['admin_qq'],
+ "admin_id": "{}".format(cfg['admin_qq']),
"msg_source": cfg['msg_source_adapter'],
}
)
diff --git a/pkg/openai/session.py b/pkg/openai/session.py
index 197ccfd0..1f9c76de 100644
--- a/pkg/openai/session.py
+++ b/pkg/openai/session.py
@@ -261,6 +261,8 @@ class Session:
pending_res_text = ""
+ start_time = time.time()
+
# TODO 对不起,我知道这样非常非常屎山,但我之后会重构的
for resp in context.get_openai_manager().request_completion(prompts):
@@ -349,6 +351,26 @@ class Session:
self.just_switched_to_exist_session = False
self.set_ongoing()
+ # 上报使用量数据
+ session_type = session_name_spt[0]
+ session_id = session_name_spt[1]
+
+ ability_provider = "QChatGPT.Text"
+ usage = total_tokens
+ model_name = context.get_config_manager().data['completion_api_params']['model']
+ response_seconds = int(time.time() - start_time)
+ retry_times = -1 # 暂不记录
+
+ context.get_center_v2_api().usage.post_query_record(
+ session_type=session_type,
+ session_id=session_id,
+ query_ability_provider=ability_provider,
+ usage=usage,
+ model_name=model_name,
+ response_seconds=response_seconds,
+ retry_times=retry_times
+ )
+
return res_ans if res_ans[0] != '\n' else res_ans[1:], finish_reason, funcs
# 删除上一回合并返回上一回合的问题
diff --git a/pkg/qqbot/manager.py b/pkg/qqbot/manager.py
index b8ba8a9c..8cd663ff 100644
--- a/pkg/qqbot/manager.py
+++ b/pkg/qqbot/manager.py
@@ -128,7 +128,7 @@ class QQBotManager:
# 保存 account_id 到审计模块
from ..utils.center import apigroup
- apigroup.APIGroup._runtime_info['account_id'] = self.bot_account_id
+ apigroup.APIGroup._runtime_info['account_id'] = "{}".format(self.bot_account_id)
context.set_qqbot_manager(self)
From d2bd6e23b6cc5f5e27a9e556920461f1d243002e Mon Sep 17 00:00:00 2001
From: RockChinQ <1010553892@qq.com>
Date: Fri, 22 Dec 2023 14:36:52 +0800
Subject: [PATCH 15/15] =?UTF-8?q?chore:=20=E5=88=A0=E9=99=A4=E8=B0=83?=
=?UTF-8?q?=E8=AF=95=E8=BE=93=E5=87=BA?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
pkg/plugin/host.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/pkg/plugin/host.py b/pkg/plugin/host.py
index 9128aa76..50af1ec0 100644
--- a/pkg/plugin/host.py
+++ b/pkg/plugin/host.py
@@ -566,7 +566,6 @@ class PluginHost:
event_name, event_context.eid, event_context.__return_value__
)
)
- print(emitted_plugins)
if len(emitted_plugins) > 0:
plugins_info = [get_plugin_info_for_audit(p) for p in emitted_plugins]