mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-06-02 20:14:36 +00:00
Compare commits
60 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
14b9f814c7 | ||
|
|
b11e5d99b0 | ||
|
|
9590718da4 | ||
|
|
8c2b53cffb | ||
|
|
5a85c073a8 | ||
|
|
2d2fbd0a8b | ||
|
|
1b25a05122 | ||
|
|
709cc1140b | ||
|
|
1730962636 | ||
|
|
a1de4f6f7a | ||
|
|
a5ccda5ed6 | ||
|
|
f035e654ba | ||
|
|
151d3e9f66 | ||
|
|
c79207e197 | ||
|
|
f9d461d9a1 | ||
|
|
3e17bbb90f | ||
|
|
549a7eff7f | ||
|
|
db2e366014 | ||
|
|
26e4215054 | ||
|
|
5f07ff8145 | ||
|
|
e396ba4649 | ||
|
|
d1dff6dedd | ||
|
|
419354cb07 | ||
|
|
7708eaa82c | ||
|
|
9fccf84987 | ||
|
|
0f59788184 | ||
|
|
0ad52bcd3f | ||
|
|
d7d710ec07 | ||
|
|
75a9a3e9af | ||
|
|
70503bedb7 | ||
|
|
7890eac3f8 | ||
|
|
e15f3595b3 | ||
|
|
eebd6a6ba3 | ||
|
|
0407f3e4ac | ||
|
|
5abca84437 | ||
|
|
d2776cc1e6 | ||
|
|
9fe0ee2b77 | ||
|
|
b68daac323 | ||
|
|
665de5dc43 | ||
|
|
e3b280758c | ||
|
|
374ae25d9c | ||
|
|
c86529ac99 | ||
|
|
6309f1fb78 | ||
|
|
c246fb6d8e | ||
|
|
ec6c041bcf | ||
|
|
2da5a9f3c7 | ||
|
|
4e0df52d7c | ||
|
|
71b8bf13e4 | ||
|
|
a8b1e6ce91 | ||
|
|
1419d7611d | ||
|
|
89c83ebf20 | ||
|
|
76d7db88ea | ||
|
|
67a208bc90 | ||
|
|
acbd55ded2 | ||
|
|
11a240a6d1 | ||
|
|
97c85abbe7 | ||
|
|
06a0cd2a3d | ||
|
|
572b215df8 | ||
|
|
2c542bf412 | ||
|
|
1576ba7a01 |
2
.github/dependabot.yml
vendored
2
.github/dependabot.yml
vendored
@@ -10,6 +10,6 @@ updates:
|
|||||||
schedule:
|
schedule:
|
||||||
interval: "weekly"
|
interval: "weekly"
|
||||||
allow:
|
allow:
|
||||||
- dependency-name: "yiri-mirai"
|
- dependency-name: "yiri-mirai-rc"
|
||||||
- dependency-name: "dulwich"
|
- dependency-name: "dulwich"
|
||||||
- dependency-name: "openai"
|
- dependency-name: "openai"
|
||||||
|
|||||||
@@ -21,12 +21,12 @@ jobs:
|
|||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v2
|
uses: actions/setup-python@v2
|
||||||
with:
|
with:
|
||||||
python-version: 3.x
|
python-version: 3.10.13
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade yiri-mirai-rc openai>=1.0.0 colorlog func_timeout dulwich Pillow CallingGPT tiktoken
|
||||||
python -m pip install --upgrade yiri-mirai openai colorlog func_timeout dulwich Pillow CallingGPT tiktoken
|
python -m pip install -U openai>=1.0.0
|
||||||
|
|
||||||
- name: Copy Scripts
|
- name: Copy Scripts
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
1
.github/workflows/update-override-all.yml
vendored
1
.github/workflows/update-override-all.yml
vendored
@@ -29,7 +29,6 @@ jobs:
|
|||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
# 在此处添加您的项目所需的其他依赖
|
|
||||||
|
|
||||||
- name: Copy Scripts
|
- name: Copy Scripts
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,4 +1,4 @@
|
|||||||
config.py
|
/config.py
|
||||||
.idea/
|
.idea/
|
||||||
__pycache__/
|
__pycache__/
|
||||||
database.db
|
database.db
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
FROM python:3.10.13-alpine3.18
|
FROM python:3.10.13-bullseye
|
||||||
WORKDIR /QChatGPT
|
WORKDIR /QChatGPT
|
||||||
|
|
||||||
COPY . /QChatGPT/
|
COPY . /QChatGPT/
|
||||||
|
|
||||||
RUN ls
|
RUN ls
|
||||||
|
|
||||||
RUN pip install -r requirements.txt
|
RUN python -m pip install -r requirements.txt && \
|
||||||
RUN pip install -U websockets==10.0
|
python -m pip install -U websockets==10.0 && \
|
||||||
|
python -m pip install -U httpcore httpx openai
|
||||||
|
|
||||||
# 生成配置文件
|
# 生成配置文件
|
||||||
RUN python main.py
|
RUN python main.py
|
||||||
|
|||||||
12
README.md
12
README.md
@@ -7,9 +7,6 @@
|
|||||||
|
|
||||||
# QChatGPT
|
# QChatGPT
|
||||||
|
|
||||||
<!-- 高稳定性/持续迭代/架构清晰/支持插件/高可自定义的 ChatGPT QQ机器人框架 -->
|
|
||||||
<!-- “当然!下面是一个使用Java编写的快速排序算法的示例代码” -->
|
|
||||||
|
|
||||||
[](https://github.com/RockChinQ/QChatGPT/releases/latest)
|
[](https://github.com/RockChinQ/QChatGPT/releases/latest)
|
||||||
<a href="https://hub.docker.com/repository/docker/rockchin/qchatgpt">
|
<a href="https://hub.docker.com/repository/docker/rockchin/qchatgpt">
|
||||||
<img src="https://img.shields.io/docker/pulls/rockchin/qchatgpt?color=blue" alt="docker pull">
|
<img src="https://img.shields.io/docker/pulls/rockchin/qchatgpt?color=blue" alt="docker pull">
|
||||||
@@ -35,6 +32,7 @@
|
|||||||
<img alt="Static Badge" src="https://img.shields.io/badge/Linux%E9%83%A8%E7%BD%B2%E8%A7%86%E9%A2%91-208647">
|
<img alt="Static Badge" src="https://img.shields.io/badge/Linux%E9%83%A8%E7%BD%B2%E8%A7%86%E9%A2%91-208647">
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
<blockquote> 🥳 QChatGPT 一周年啦,感谢大家的支持!欢迎前往<a href="https://github.com/RockChinQ/QChatGPT/discussions/627">讨论</a>。</blockquote>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>回复效果演示(带有联网插件)</summary>
|
<summary>回复效果演示(带有联网插件)</summary>
|
||||||
@@ -42,7 +40,7 @@
|
|||||||
</details>
|
</details>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
> **NOTE**
|
> [!NOTE]
|
||||||
> 2023/9/13 现已支持通过[One API](https://github.com/songquanpeng/one-api)接入 Azure、Anthropic Claude、Google PaLM 2、智谱 ChatGLM、百度文心一言、讯飞星火认知、阿里通义千问以及 360 智脑等模型,欢迎测试并反馈。
|
> 2023/9/13 现已支持通过[One API](https://github.com/songquanpeng/one-api)接入 Azure、Anthropic Claude、Google PaLM 2、智谱 ChatGLM、百度文心一言、讯飞星火认知、阿里通义千问以及 360 智脑等模型,欢迎测试并反馈。
|
||||||
> 2023/8/29 [逆向库插件](https://github.com/RockChinQ/revLibs)已支持 gpt4free
|
> 2023/8/29 [逆向库插件](https://github.com/RockChinQ/revLibs)已支持 gpt4free
|
||||||
> 2023/8/14 [逆向库插件](https://github.com/RockChinQ/revLibs)已支持Claude和Bard
|
> 2023/8/14 [逆向库插件](https://github.com/RockChinQ/revLibs)已支持Claude和Bard
|
||||||
@@ -197,7 +195,7 @@
|
|||||||
|
|
||||||
</summary>
|
</summary>
|
||||||
|
|
||||||
> **NOTE**
|
> **NOTE**
|
||||||
> - 部署过程中遇到任何问题,请先在[QChatGPT](https://github.com/RockChinQ/QChatGPT/issues)或[qcg-installer](https://github.com/RockChinQ/qcg-installer/issues)的issue里进行搜索
|
> - 部署过程中遇到任何问题,请先在[QChatGPT](https://github.com/RockChinQ/QChatGPT/issues)或[qcg-installer](https://github.com/RockChinQ/qcg-installer/issues)的issue里进行搜索
|
||||||
> - QChatGPT需要Python版本>=3.9
|
> - QChatGPT需要Python版本>=3.9
|
||||||
> - 官方群和社区群群号请见文档顶部
|
> - 官方群和社区群群号请见文档顶部
|
||||||
@@ -278,7 +276,7 @@ cd QChatGPT
|
|||||||
2. 安装依赖
|
2. 安装依赖
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pip3 install requests yiri-mirai openai colorlog func_timeout dulwich Pillow nakuru-project-idk CallingGPT tiktoken
|
pip3 install requests -r requirements.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
3. 运行一次主程序,生成配置文件
|
3. 运行一次主程序,生成配置文件
|
||||||
@@ -346,6 +344,8 @@ python3 main.py
|
|||||||
- [oliverkirk-sudo/QChatWeather](https://github.com/oliverkirk-sudo/QChatWeather) - 生成好看的天气图片,基于和风天气
|
- [oliverkirk-sudo/QChatWeather](https://github.com/oliverkirk-sudo/QChatWeather) - 生成好看的天气图片,基于和风天气
|
||||||
- [oliverkirk-sudo/QChatMarkdown](https://github.com/oliverkirk-sudo/QChatMarkdown) - 将机器人输出的markdown转换为图片,基于[playwright](https://playwright.dev/python/docs/intro)
|
- [oliverkirk-sudo/QChatMarkdown](https://github.com/oliverkirk-sudo/QChatMarkdown) - 将机器人输出的markdown转换为图片,基于[playwright](https://playwright.dev/python/docs/intro)
|
||||||
- [ruuuux/WikipediaSearch](https://github.com/ruuuux/WikipediaSearch) - Wikipedia 搜索插件
|
- [ruuuux/WikipediaSearch](https://github.com/ruuuux/WikipediaSearch) - Wikipedia 搜索插件
|
||||||
|
- [zuo-shi-yun/discountAssistant](https://github.com/zuo-shi-yun/discountAssistant) - 自动筛选并发送羊毛群内的优惠券
|
||||||
|
- [zuo-shi-yun/Gatekeeper ](https://github.com/zuo-shi-yun/Gatekeeper) - QChatGPT的看门狗,包含黑白名单、临时用户机制
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ English | [简体中文](README.md)
|
|||||||
|
|
||||||
Install this [plugin](https://github.com/RockChinQ/Switcher) to switch between different models.
|
Install this [plugin](https://github.com/RockChinQ/Switcher) to switch between different models.
|
||||||
|
|
||||||
## ✅Function Points
|
## ✅Features
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>Details</summary>
|
<summary>Details</summary>
|
||||||
@@ -141,7 +141,7 @@ cd QChatGPT
|
|||||||
2. Install dependencies
|
2. Install dependencies
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pip3 install requests yiri-mirai openai colorlog func_timeout dulwich Pillow nakuru-project-idk
|
pip3 install requests yiri-mirai-rc openai colorlog func_timeout dulwich Pillow nakuru-project-idk
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Generate `config.py`
|
3. Generate `config.py`
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ admin_qq = 0
|
|||||||
#
|
#
|
||||||
# 还可以加载文件中的预设文字,使用方法请查看:https://github.com/RockChinQ/QChatGPT/wiki/%E5%8A%9F%E8%83%BD%E4%BD%BF%E7%94%A8#%E9%A2%84%E8%AE%BE%E6%96%87%E5%AD%97
|
# 还可以加载文件中的预设文字,使用方法请查看:https://github.com/RockChinQ/QChatGPT/wiki/%E5%8A%9F%E8%83%BD%E4%BD%BF%E7%94%A8#%E9%A2%84%E8%AE%BE%E6%96%87%E5%AD%97
|
||||||
default_prompt = {
|
default_prompt = {
|
||||||
"default": "如果我之后想获取帮助,请你说“输入!help获取帮助”",
|
"default": "如果用户之后想获取帮助,请你说“输入!help获取帮助”。",
|
||||||
}
|
}
|
||||||
|
|
||||||
# 情景预设格式
|
# 情景预设格式
|
||||||
@@ -208,38 +208,65 @@ auto_reset = True
|
|||||||
|
|
||||||
# OpenAI补全API的参数
|
# OpenAI补全API的参数
|
||||||
# 请在下方填写模型,程序自动选择接口
|
# 请在下方填写模型,程序自动选择接口
|
||||||
|
# 模型文档:https://platform.openai.com/docs/models
|
||||||
# 现已支持的模型有:
|
# 现已支持的模型有:
|
||||||
#
|
#
|
||||||
# 'gpt-4'
|
# ChatCompletions 接口:
|
||||||
# 'gpt-4-0613'
|
# # GPT 4 系列
|
||||||
# 'gpt-4-32k'
|
# "gpt-4-1106-preview",
|
||||||
# 'gpt-4-32k-0613'
|
# "gpt-4-vision-preview",
|
||||||
# 'gpt-3.5-turbo'
|
# "gpt-4",
|
||||||
# 'gpt-3.5-turbo-16k'
|
# "gpt-4-32k",
|
||||||
# 'gpt-3.5-turbo-0613'
|
# "gpt-4-0613",
|
||||||
# 'gpt-3.5-turbo-16k-0613'
|
# "gpt-4-32k-0613",
|
||||||
# 'text-davinci-003'
|
# "gpt-4-0314", # legacy
|
||||||
# 'text-davinci-002'
|
# "gpt-4-32k-0314", # legacy
|
||||||
# 'code-davinci-002'
|
# # GPT 3.5 系列
|
||||||
# 'code-cushman-001'
|
# "gpt-3.5-turbo-1106",
|
||||||
# 'text-curie-001'
|
# "gpt-3.5-turbo",
|
||||||
# 'text-babbage-001'
|
# "gpt-3.5-turbo-16k",
|
||||||
# 'text-ada-001'
|
# "gpt-3.5-turbo-0613", # legacy
|
||||||
|
# "gpt-3.5-turbo-16k-0613", # legacy
|
||||||
|
# "gpt-3.5-turbo-0301", # legacy
|
||||||
|
#
|
||||||
|
# Completions接口:
|
||||||
|
# "text-davinci-003", # legacy
|
||||||
|
# "text-davinci-002", # legacy
|
||||||
|
# "code-davinci-002", # legacy
|
||||||
|
# "code-cushman-001", # legacy
|
||||||
|
# "text-curie-001", # legacy
|
||||||
|
# "text-babbage-001", # legacy
|
||||||
|
# "text-ada-001", # legacy
|
||||||
|
# "gpt-3.5-turbo-instruct",
|
||||||
#
|
#
|
||||||
# 具体请查看OpenAI的文档: https://beta.openai.com/docs/api-reference/completions/create
|
# 具体请查看OpenAI的文档: https://beta.openai.com/docs/api-reference/completions/create
|
||||||
# 请将内容修改到config.py中,请勿修改config-template.py
|
# 请将内容修改到config.py中,请勿修改config-template.py
|
||||||
#
|
#
|
||||||
# 支持通过 One API 接入多种模型,请在上方的openai_config中设置One API的代理地址,
|
# 支持通过 One API 接入多种模型,请在上方的openai_config中设置One API的代理地址,
|
||||||
# 并在此填写您要使用的模型名称,详细请参考:https://github.com/songquanpeng/one-api
|
# 并在此填写您要使用的模型名称,详细请参考:https://github.com/songquanpeng/one-api
|
||||||
|
#
|
||||||
|
# 支持的 One API 模型:
|
||||||
|
# "SparkDesk",
|
||||||
|
# "chatglm_pro",
|
||||||
|
# "chatglm_std",
|
||||||
|
# "chatglm_lite",
|
||||||
|
# "qwen-v1",
|
||||||
|
# "qwen-plus-v1",
|
||||||
|
# "ERNIE-Bot",
|
||||||
|
# "ERNIE-Bot-turbo",
|
||||||
completion_api_params = {
|
completion_api_params = {
|
||||||
"model": "gpt-3.5-turbo",
|
"model": "gpt-3.5-turbo",
|
||||||
"temperature": 0.9, # 数值越低得到的回答越理性,取值范围[0, 1]
|
"temperature": 0.9, # 数值越低得到的回答越理性,取值范围[0, 1]
|
||||||
}
|
}
|
||||||
|
|
||||||
# OpenAI的Image API的参数
|
# OpenAI的Image API的参数
|
||||||
# 具体请查看OpenAI的文档: https://beta.openai.com/docs/api-reference/images/create
|
# 具体请查看OpenAI的文档: https://platform.openai.com/docs/api-reference/images/create
|
||||||
image_api_params = {
|
image_api_params = {
|
||||||
"size": "256x256", # 图片尺寸,支持256x256, 512x512, 1024x1024
|
"model": "dall-e-2", # 默认使用 dall-e-2 模型,也可以改为 dall-e-3
|
||||||
|
# 图片尺寸
|
||||||
|
# dall-e-2 模型支持 256x256, 512x512, 1024x1024
|
||||||
|
# dall-e-3 模型支持 1024x1024, 1792x1024, 1024x1792
|
||||||
|
"size": "256x256",
|
||||||
}
|
}
|
||||||
|
|
||||||
# 跟踪函数调用
|
# 跟踪函数调用
|
||||||
@@ -268,7 +295,7 @@ show_prefix = False
|
|||||||
# 当此次消息处理时间低于此秒数时,将会强制延迟至此秒数
|
# 当此次消息处理时间低于此秒数时,将会强制延迟至此秒数
|
||||||
# 例如:[1.5, 3],则每次处理时会随机取一个1.5-3秒的随机数,若处理时间低于此随机数,则强制延迟至此随机秒数
|
# 例如:[1.5, 3],则每次处理时会随机取一个1.5-3秒的随机数,若处理时间低于此随机数,则强制延迟至此随机秒数
|
||||||
# 若您不需要此功能,请将force_delay_range设置为[0, 0]
|
# 若您不需要此功能,请将force_delay_range设置为[0, 0]
|
||||||
force_delay_range = [1.5, 3]
|
force_delay_range = [0, 0]
|
||||||
|
|
||||||
# 应用长消息处理策略的阈值
|
# 应用长消息处理策略的阈值
|
||||||
# 当回复消息长度超过此值时,将使用长消息处理策略
|
# 当回复消息长度超过此值时,将使用长消息处理策略
|
||||||
@@ -299,19 +326,6 @@ retry_times = 3
|
|||||||
# 设置为False时,向用户及管理员发送错误详细信息
|
# 设置为False时,向用户及管理员发送错误详细信息
|
||||||
hide_exce_info_to_user = False
|
hide_exce_info_to_user = False
|
||||||
|
|
||||||
# 线程池相关配置
|
|
||||||
# 该参数决定机器人可以同时处理几个人的消息,超出线程池数量的请求会被阻塞,不会被丢弃
|
|
||||||
# 如果你不清楚该参数的意义,请不要更改
|
|
||||||
# 程序运行本身线程池,无代码层面修改请勿更改
|
|
||||||
sys_pool_num = 8
|
|
||||||
|
|
||||||
# 执行管理员请求和指令的线程池并行线程数量,一般和管理员数量相等
|
|
||||||
admin_pool_num = 4
|
|
||||||
|
|
||||||
# 执行用户请求和指令的线程池并行线程数量
|
|
||||||
# 如需要更高的并发,可以增大该值
|
|
||||||
user_pool_num = 8
|
|
||||||
|
|
||||||
# 每个会话的过期时间,单位为秒
|
# 每个会话的过期时间,单位为秒
|
||||||
# 默认值20分钟
|
# 默认值20分钟
|
||||||
session_expire_time = 1200
|
session_expire_time = 1200
|
||||||
@@ -351,7 +365,7 @@ rate_limitation = {
|
|||||||
rate_limit_strategy = "drop"
|
rate_limit_strategy = "drop"
|
||||||
|
|
||||||
# 是否在启动时进行依赖库更新
|
# 是否在启动时进行依赖库更新
|
||||||
upgrade_dependencies = True
|
upgrade_dependencies = False
|
||||||
|
|
||||||
# 是否上报统计信息
|
# 是否上报统计信息
|
||||||
# 用于统计机器人的使用情况,不会收集任何用户信息
|
# 用于统计机器人的使用情况,不会收集任何用户信息
|
||||||
|
|||||||
220
main.py
220
main.py
@@ -8,10 +8,56 @@ import time
|
|||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
|
import asyncio
|
||||||
|
|
||||||
sys.path.append(".")
|
sys.path.append(".")
|
||||||
|
|
||||||
|
|
||||||
|
def check_file():
|
||||||
|
# 检查是否有banlist.py,如果没有就把banlist-template.py复制一份
|
||||||
|
if not os.path.exists('banlist.py'):
|
||||||
|
shutil.copy('res/templates/banlist-template.py', 'banlist.py')
|
||||||
|
|
||||||
|
# 检查是否有sensitive.json
|
||||||
|
if not os.path.exists("sensitive.json"):
|
||||||
|
shutil.copy("res/templates/sensitive-template.json", "sensitive.json")
|
||||||
|
|
||||||
|
# 检查是否有scenario/default.json
|
||||||
|
if not os.path.exists("scenario/default.json"):
|
||||||
|
shutil.copy("scenario/default-template.json", "scenario/default.json")
|
||||||
|
|
||||||
|
# 检查cmdpriv.json
|
||||||
|
if not os.path.exists("cmdpriv.json"):
|
||||||
|
shutil.copy("res/templates/cmdpriv-template.json", "cmdpriv.json")
|
||||||
|
|
||||||
|
# 检查tips_custom
|
||||||
|
if not os.path.exists("tips.py"):
|
||||||
|
shutil.copy("tips-custom-template.py", "tips.py")
|
||||||
|
|
||||||
|
# 检查temp目录
|
||||||
|
if not os.path.exists("temp/"):
|
||||||
|
os.mkdir("temp/")
|
||||||
|
|
||||||
|
# 检查并创建plugins、prompts目录
|
||||||
|
check_path = ["plugins", "prompts"]
|
||||||
|
for path in check_path:
|
||||||
|
if not os.path.exists(path):
|
||||||
|
os.mkdir(path)
|
||||||
|
|
||||||
|
# 配置文件存在性校验
|
||||||
|
if not os.path.exists('config.py'):
|
||||||
|
shutil.copy('config-template.py', 'config.py')
|
||||||
|
print('请先在config.py中填写配置')
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
|
# 初始化相关文件
|
||||||
|
check_file()
|
||||||
|
|
||||||
from pkg.utils.log import init_runtime_log_file, reset_logging
|
from pkg.utils.log import init_runtime_log_file, reset_logging
|
||||||
|
from pkg.config import manager as config_mgr
|
||||||
|
from pkg.config.impls import pymodule as pymodule_cfg
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import colorlog
|
import colorlog
|
||||||
@@ -20,7 +66,6 @@ except ImportError:
|
|||||||
import pkg.utils.pkgmgr as pkgmgr
|
import pkg.utils.pkgmgr as pkgmgr
|
||||||
try:
|
try:
|
||||||
pkgmgr.install_requirements("requirements.txt")
|
pkgmgr.install_requirements("requirements.txt")
|
||||||
pkgmgr.install_upgrade("websockets")
|
|
||||||
import colorlog
|
import colorlog
|
||||||
except ImportError:
|
except ImportError:
|
||||||
print("依赖不满足,请查看 https://github.com/RockChinQ/qcg-installer/issues/15")
|
print("依赖不满足,请查看 https://github.com/RockChinQ/qcg-installer/issues/15")
|
||||||
@@ -55,15 +100,15 @@ def ensure_dependencies():
|
|||||||
known_exception_caught = False
|
known_exception_caught = False
|
||||||
|
|
||||||
|
|
||||||
def override_config():
|
def override_config_manager():
|
||||||
import config
|
config = pkg.utils.context.get_config_manager().data
|
||||||
# 检查override.json覆盖
|
|
||||||
if os.path.exists("override.json") and use_override:
|
if os.path.exists("override.json") and use_override:
|
||||||
override_json = json.load(open("override.json", "r", encoding="utf-8"))
|
override_json = json.load(open("override.json", "r", encoding="utf-8"))
|
||||||
overrided = []
|
overrided = []
|
||||||
for key in override_json:
|
for key in override_json:
|
||||||
if hasattr(config, key):
|
if key in config:
|
||||||
setattr(config, key, override_json[key])
|
config[key] = override_json[key]
|
||||||
# logging.info("覆写配置[{}]为[{}]".format(key, override_json[key]))
|
# logging.info("覆写配置[{}]为[{}]".format(key, override_json[key]))
|
||||||
overrided.append(key)
|
overrided.append(key)
|
||||||
else:
|
else:
|
||||||
@@ -72,36 +117,6 @@ def override_config():
|
|||||||
logging.info("已根据override.json覆写配置项: {}".format(", ".join(overrided)))
|
logging.info("已根据override.json覆写配置项: {}".format(", ".join(overrided)))
|
||||||
|
|
||||||
|
|
||||||
# 临时函数,用于加载config和上下文,未来统一放在config类
|
|
||||||
def load_config():
|
|
||||||
logging.info("检查config模块完整性.")
|
|
||||||
# 完整性校验
|
|
||||||
non_exist_keys = []
|
|
||||||
|
|
||||||
is_integrity = True
|
|
||||||
config_template = importlib.import_module('config-template')
|
|
||||||
config = importlib.import_module('config')
|
|
||||||
for key in dir(config_template):
|
|
||||||
if not key.startswith("__") and not hasattr(config, key):
|
|
||||||
setattr(config, key, getattr(config_template, key))
|
|
||||||
# logging.warning("[{}]不存在".format(key))
|
|
||||||
non_exist_keys.append(key)
|
|
||||||
is_integrity = False
|
|
||||||
|
|
||||||
if not is_integrity:
|
|
||||||
logging.warning("以下配置字段不存在: {}".format(", ".join(non_exist_keys)))
|
|
||||||
|
|
||||||
# 检查override.json覆盖
|
|
||||||
override_config()
|
|
||||||
|
|
||||||
if not is_integrity:
|
|
||||||
logging.warning("以上不存在的配置已被设为默认值,您可以依据config-template.py检查config.py,将在3秒后继续启动... ")
|
|
||||||
time.sleep(3)
|
|
||||||
|
|
||||||
# 存进上下文
|
|
||||||
pkg.utils.context.set_config(config)
|
|
||||||
|
|
||||||
|
|
||||||
def complete_tips():
|
def complete_tips():
|
||||||
"""根据tips-custom-template模块补全tips模块的属性"""
|
"""根据tips-custom-template模块补全tips模块的属性"""
|
||||||
non_exist_keys = []
|
non_exist_keys = []
|
||||||
@@ -124,17 +139,29 @@ def complete_tips():
|
|||||||
time.sleep(3)
|
time.sleep(3)
|
||||||
|
|
||||||
|
|
||||||
def start(first_time_init=False):
|
async def start_process(first_time_init=False):
|
||||||
"""启动流程,reload之后会被执行"""
|
"""启动流程,reload之后会被执行"""
|
||||||
|
|
||||||
global known_exception_caught
|
global known_exception_caught
|
||||||
import pkg.utils.context
|
import pkg.utils.context
|
||||||
|
|
||||||
config = pkg.utils.context.get_config()
|
# 加载配置
|
||||||
|
cfg_inst: pymodule_cfg.PythonModuleConfigFile = pymodule_cfg.PythonModuleConfigFile(
|
||||||
|
'config.py',
|
||||||
|
'config-template.py'
|
||||||
|
)
|
||||||
|
await config_mgr.ConfigManager(cfg_inst).load_config()
|
||||||
|
|
||||||
|
override_config_manager()
|
||||||
|
|
||||||
|
# 检查tips模块
|
||||||
|
complete_tips()
|
||||||
|
|
||||||
|
cfg = pkg.utils.context.get_config_manager().data
|
||||||
# 更新openai库到最新版本
|
# 更新openai库到最新版本
|
||||||
if not hasattr(config, 'upgrade_dependencies') or config.upgrade_dependencies:
|
if 'upgrade_dependencies' not in cfg or cfg['upgrade_dependencies']:
|
||||||
print("正在更新依赖库,请等待...")
|
print("正在更新依赖库,请等待...")
|
||||||
if not hasattr(config, 'upgrade_dependencies'):
|
if 'upgrade_dependencies' not in cfg:
|
||||||
print("这个操作不是必须的,如果不想更新,请在config.py中添加upgrade_dependencies=False")
|
print("这个操作不是必须的,如果不想更新,请在config.py中添加upgrade_dependencies=False")
|
||||||
else:
|
else:
|
||||||
print("这个操作不是必须的,如果不想更新,请在config.py中将upgrade_dependencies设置为False")
|
print("这个操作不是必须的,如果不想更新,请在config.py中将upgrade_dependencies设置为False")
|
||||||
@@ -143,6 +170,10 @@ def start(first_time_init=False):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("更新openai库失败:{}, 请忽略或自行更新".format(e))
|
print("更新openai库失败:{}, 请忽略或自行更新".format(e))
|
||||||
|
|
||||||
|
# 初始化文字转图片
|
||||||
|
from pkg.utils import text2img
|
||||||
|
text2img.initialize()
|
||||||
|
|
||||||
known_exception_caught = False
|
known_exception_caught = False
|
||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
@@ -151,11 +182,11 @@ def start(first_time_init=False):
|
|||||||
pkg.utils.context.context['logger_handler'] = sh
|
pkg.utils.context.context['logger_handler'] = sh
|
||||||
|
|
||||||
# 检查是否设置了管理员
|
# 检查是否设置了管理员
|
||||||
if not (hasattr(config, 'admin_qq') and config.admin_qq != 0):
|
if cfg['admin_qq'] == 0:
|
||||||
# logging.warning("未设置管理员QQ,管理员权限指令及运行告警将无法使用,如需设置请修改config.py中的admin_qq字段")
|
# logging.warning("未设置管理员QQ,管理员权限指令及运行告警将无法使用,如需设置请修改config.py中的admin_qq字段")
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
config.admin_qq = int(input("未设置管理员QQ,管理员权限指令及运行告警将无法使用,请输入管理员QQ号: "))
|
cfg['admin_qq'] = int(input("未设置管理员QQ,管理员权限指令及运行告警将无法使用,请输入管理员QQ号: "))
|
||||||
# 写入到文件
|
# 写入到文件
|
||||||
|
|
||||||
# 读取文件
|
# 读取文件
|
||||||
@@ -163,7 +194,7 @@ def start(first_time_init=False):
|
|||||||
with open("config.py", "r", encoding="utf-8") as f:
|
with open("config.py", "r", encoding="utf-8") as f:
|
||||||
config_file_str = f.read()
|
config_file_str = f.read()
|
||||||
# 替换
|
# 替换
|
||||||
config_file_str = config_file_str.replace("admin_qq = 0", "admin_qq = " + str(config.admin_qq))
|
config_file_str = config_file_str.replace("admin_qq = 0", "admin_qq = " + str(cfg['admin_qq']))
|
||||||
# 写入
|
# 写入
|
||||||
with open("config.py", "w", encoding="utf-8") as f:
|
with open("config.py", "w", encoding="utf-8") as f:
|
||||||
f.write(config_file_str)
|
f.write(config_file_str)
|
||||||
@@ -192,22 +223,23 @@ def start(first_time_init=False):
|
|||||||
# 配置OpenAI proxy
|
# 配置OpenAI proxy
|
||||||
import openai
|
import openai
|
||||||
openai.proxies = None # 先重置,因为重载后可能需要清除proxy
|
openai.proxies = None # 先重置,因为重载后可能需要清除proxy
|
||||||
if "http_proxy" in config.openai_config and config.openai_config["http_proxy"] is not None:
|
if "http_proxy" in cfg['openai_config'] and cfg['openai_config']["http_proxy"] is not None:
|
||||||
openai.proxies = {
|
openai.proxies = {
|
||||||
"http": config.openai_config["http_proxy"],
|
"http": cfg['openai_config']["http_proxy"],
|
||||||
"https": config.openai_config["http_proxy"]
|
"https": cfg['openai_config']["http_proxy"]
|
||||||
}
|
}
|
||||||
|
|
||||||
# 配置openai api_base
|
# 配置openai api_base
|
||||||
if "reverse_proxy" in config.openai_config and config.openai_config["reverse_proxy"] is not None:
|
if "reverse_proxy" in cfg['openai_config'] and cfg['openai_config']["reverse_proxy"] is not None:
|
||||||
openai.base_url = config.openai_config["reverse_proxy"]
|
logging.debug("设置反向代理: "+cfg['openai_config']['reverse_proxy'])
|
||||||
|
openai.base_url = cfg['openai_config']["reverse_proxy"]
|
||||||
|
|
||||||
# 主启动流程
|
# 主启动流程
|
||||||
database = pkg.database.manager.DatabaseManager()
|
database = pkg.database.manager.DatabaseManager()
|
||||||
|
|
||||||
database.initialize_database()
|
database.initialize_database()
|
||||||
|
|
||||||
openai_interact = pkg.openai.manager.OpenAIInteract(config.openai_config['api_key'])
|
openai_interact = pkg.openai.manager.OpenAIInteract(cfg['openai_config']['api_key'])
|
||||||
|
|
||||||
# 加载所有未超时的session
|
# 加载所有未超时的session
|
||||||
pkg.openai.session.load_sessions()
|
pkg.openai.session.load_sessions()
|
||||||
@@ -296,13 +328,12 @@ def start(first_time_init=False):
|
|||||||
|
|
||||||
if first_time_init:
|
if first_time_init:
|
||||||
if not known_exception_caught:
|
if not known_exception_caught:
|
||||||
import config
|
if cfg['msg_source_adapter'] == "yirimirai":
|
||||||
if config.msg_source_adapter == "yirimirai":
|
logging.info("QQ: {}, MAH: {}".format(cfg['mirai_http_api_config']['qq'], cfg['mirai_http_api_config']['host']+":"+str(cfg['mirai_http_api_config']['port'])))
|
||||||
logging.info("QQ: {}, MAH: {}".format(config.mirai_http_api_config['qq'], config.mirai_http_api_config['host']+":"+str(config.mirai_http_api_config['port'])))
|
|
||||||
logging.critical('程序启动完成,如长时间未显示 "成功登录到账号xxxxx" ,并且不回复消息,解决办法(请勿到群里问): '
|
logging.critical('程序启动完成,如长时间未显示 "成功登录到账号xxxxx" ,并且不回复消息,解决办法(请勿到群里问): '
|
||||||
'https://github.com/RockChinQ/QChatGPT/issues/37')
|
'https://github.com/RockChinQ/QChatGPT/issues/37')
|
||||||
elif config.msg_source_adapter == 'nakuru':
|
elif cfg['msg_source_adapter'] == 'nakuru':
|
||||||
logging.info("host: {}, port: {}, http_port: {}".format(config.nakuru_config['host'], config.nakuru_config['port'], config.nakuru_config['http_port']))
|
logging.info("host: {}, port: {}, http_port: {}".format(cfg['nakuru_config']['host'], cfg['nakuru_config']['port'], cfg['nakuru_config']['http_port']))
|
||||||
logging.critical('程序启动完成,如长时间未显示 "Protocol: connected" ,并且不回复消息,请检查config.py中的nakuru_config是否正确')
|
logging.critical('程序启动完成,如长时间未显示 "Protocol: connected" ,并且不回复消息,请检查config.py中的nakuru_config是否正确')
|
||||||
else:
|
else:
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
@@ -310,7 +341,7 @@ def start(first_time_init=False):
|
|||||||
logging.info('热重载完成')
|
logging.info('热重载完成')
|
||||||
|
|
||||||
# 发送赞赏码
|
# 发送赞赏码
|
||||||
if config.encourage_sponsor_at_start \
|
if cfg['encourage_sponsor_at_start'] \
|
||||||
and pkg.utils.context.get_openai_manager().audit_mgr.get_total_text_length() >= 2048:
|
and pkg.utils.context.get_openai_manager().audit_mgr.get_total_text_length() >= 2048:
|
||||||
|
|
||||||
logging.info("发送赞赏码")
|
logging.info("发送赞赏码")
|
||||||
@@ -368,70 +399,22 @@ def stop():
|
|||||||
raise e
|
raise e
|
||||||
|
|
||||||
|
|
||||||
def check_file():
|
|
||||||
# 检查是否有banlist.py,如果没有就把banlist-template.py复制一份
|
|
||||||
if not os.path.exists('banlist.py'):
|
|
||||||
shutil.copy('res/templates/banlist-template.py', 'banlist.py')
|
|
||||||
|
|
||||||
# 检查是否有sensitive.json
|
|
||||||
if not os.path.exists("sensitive.json"):
|
|
||||||
shutil.copy("res/templates/sensitive-template.json", "sensitive.json")
|
|
||||||
|
|
||||||
# 检查是否有scenario/default.json
|
|
||||||
if not os.path.exists("scenario/default.json"):
|
|
||||||
shutil.copy("scenario/default-template.json", "scenario/default.json")
|
|
||||||
|
|
||||||
# 检查cmdpriv.json
|
|
||||||
if not os.path.exists("cmdpriv.json"):
|
|
||||||
shutil.copy("res/templates/cmdpriv-template.json", "cmdpriv.json")
|
|
||||||
|
|
||||||
# 检查tips_custom
|
|
||||||
if not os.path.exists("tips.py"):
|
|
||||||
shutil.copy("tips-custom-template.py", "tips.py")
|
|
||||||
|
|
||||||
# 检查temp目录
|
|
||||||
if not os.path.exists("temp/"):
|
|
||||||
os.mkdir("temp/")
|
|
||||||
|
|
||||||
# 检查并创建plugins、prompts目录
|
|
||||||
check_path = ["plugins", "prompts"]
|
|
||||||
for path in check_path:
|
|
||||||
if not os.path.exists(path):
|
|
||||||
os.mkdir(path)
|
|
||||||
|
|
||||||
# 配置文件存在性校验
|
|
||||||
if not os.path.exists('config.py'):
|
|
||||||
shutil.copy('config-template.py', 'config.py')
|
|
||||||
print('请先在config.py中填写配置')
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
global use_override
|
global use_override
|
||||||
# 检查是否携带了 --override 或 -r 参数
|
# 检查是否携带了 --override 或 -r 参数
|
||||||
if '--override' in sys.argv or '-r' in sys.argv:
|
if '--override' in sys.argv or '-r' in sys.argv:
|
||||||
use_override = True
|
use_override = True
|
||||||
|
|
||||||
# 初始化相关文件
|
|
||||||
check_file()
|
|
||||||
|
|
||||||
# 初始化logging
|
# 初始化logging
|
||||||
init_runtime_log_file()
|
init_runtime_log_file()
|
||||||
pkg.utils.context.context['logger_handler'] = reset_logging()
|
pkg.utils.context.context['logger_handler'] = reset_logging()
|
||||||
|
|
||||||
# 加载配置
|
|
||||||
load_config()
|
|
||||||
config = pkg.utils.context.get_config()
|
|
||||||
|
|
||||||
# 检查tips模块
|
|
||||||
complete_tips()
|
|
||||||
|
|
||||||
# 配置线程池
|
# 配置线程池
|
||||||
from pkg.utils import ThreadCtl
|
from pkg.utils import ThreadCtl
|
||||||
thread_ctl = ThreadCtl(
|
thread_ctl = ThreadCtl(
|
||||||
sys_pool_num=config.sys_pool_num,
|
sys_pool_num=8,
|
||||||
admin_pool_num=config.admin_pool_num,
|
admin_pool_num=4,
|
||||||
user_pool_num=config.user_pool_num
|
user_pool_num=8
|
||||||
)
|
)
|
||||||
# 存进上下文
|
# 存进上下文
|
||||||
pkg.utils.context.set_thread_ctl(thread_ctl)
|
pkg.utils.context.set_thread_ctl(thread_ctl)
|
||||||
@@ -450,9 +433,11 @@ def main():
|
|||||||
# 关闭urllib的http警告
|
# 关闭urllib的http警告
|
||||||
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
|
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
|
||||||
|
|
||||||
|
def run_wrapper():
|
||||||
|
asyncio.run(start_process(True))
|
||||||
|
|
||||||
pkg.utils.context.get_thread_ctl().submit_sys_task(
|
pkg.utils.context.get_thread_ctl().submit_sys_task(
|
||||||
start,
|
run_wrapper
|
||||||
True
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# 主线程循环
|
# 主线程循环
|
||||||
@@ -462,12 +447,19 @@ def main():
|
|||||||
except:
|
except:
|
||||||
stop()
|
stop()
|
||||||
pkg.utils.context.get_thread_ctl().shutdown()
|
pkg.utils.context.get_thread_ctl().shutdown()
|
||||||
import platform
|
|
||||||
if platform.system() == 'Windows':
|
launch_args = sys.argv.copy()
|
||||||
cmd = "taskkill /F /PID {}".format(os.getpid())
|
|
||||||
elif platform.system() in ['Linux', 'Darwin']:
|
if "--cov-report" not in launch_args:
|
||||||
cmd = "kill -9 {}".format(os.getpid())
|
import platform
|
||||||
os.system(cmd)
|
if platform.system() == 'Windows':
|
||||||
|
cmd = "taskkill /F /PID {}".format(os.getpid())
|
||||||
|
elif platform.system() in ['Linux', 'Darwin']:
|
||||||
|
cmd = "kill -9 {}".format(os.getpid())
|
||||||
|
os.system(cmd)
|
||||||
|
else:
|
||||||
|
print("正常退出以生成覆盖率报告")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
"switch_strategy": "active",
|
"switch_strategy": "active",
|
||||||
"admin_qq": 0,
|
"admin_qq": 0,
|
||||||
"default_prompt": {
|
"default_prompt": {
|
||||||
"default": "如果我之后想获取帮助,请你说“输入!help获取帮助”"
|
"default": "如果用户之后想获取帮助,请你说“输入!help获取帮助”。"
|
||||||
},
|
},
|
||||||
"preset_mode": "normal",
|
"preset_mode": "normal",
|
||||||
"response_rules": {
|
"response_rules": {
|
||||||
@@ -60,6 +60,7 @@
|
|||||||
"temperature": 0.9
|
"temperature": 0.9
|
||||||
},
|
},
|
||||||
"image_api_params": {
|
"image_api_params": {
|
||||||
|
"model": "dall-e-2",
|
||||||
"size": "256x256"
|
"size": "256x256"
|
||||||
},
|
},
|
||||||
"trace_function_calls": false,
|
"trace_function_calls": false,
|
||||||
@@ -69,8 +70,8 @@
|
|||||||
"process_message_timeout": 120,
|
"process_message_timeout": 120,
|
||||||
"show_prefix": false,
|
"show_prefix": false,
|
||||||
"force_delay_range": [
|
"force_delay_range": [
|
||||||
1.5,
|
0,
|
||||||
3
|
0
|
||||||
],
|
],
|
||||||
"blob_message_threshold": 256,
|
"blob_message_threshold": 256,
|
||||||
"blob_message_strategy": "forward",
|
"blob_message_strategy": "forward",
|
||||||
@@ -78,15 +79,12 @@
|
|||||||
"font_path": "",
|
"font_path": "",
|
||||||
"retry_times": 3,
|
"retry_times": 3,
|
||||||
"hide_exce_info_to_user": false,
|
"hide_exce_info_to_user": false,
|
||||||
"sys_pool_num": 8,
|
|
||||||
"admin_pool_num": 4,
|
|
||||||
"user_pool_num": 8,
|
|
||||||
"session_expire_time": 1200,
|
"session_expire_time": 1200,
|
||||||
"rate_limitation": {
|
"rate_limitation": {
|
||||||
"default": 60
|
"default": 60
|
||||||
},
|
},
|
||||||
"rate_limit_strategy": "drop",
|
"rate_limit_strategy": "drop",
|
||||||
"upgrade_dependencies": true,
|
"upgrade_dependencies": false,
|
||||||
"report_usage": true,
|
"report_usage": true,
|
||||||
"logging_level": 20
|
"logging_level": 20
|
||||||
}
|
}
|
||||||
@@ -9,8 +9,8 @@ import threading
|
|||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
import pkg.utils.context
|
from ..utils import context
|
||||||
import pkg.utils.updater
|
from ..utils import updater
|
||||||
|
|
||||||
|
|
||||||
class DataGatherer:
|
class DataGatherer:
|
||||||
@@ -33,7 +33,7 @@ class DataGatherer:
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.load_from_db()
|
self.load_from_db()
|
||||||
try:
|
try:
|
||||||
self.version_str = pkg.utils.updater.get_current_tag() # 从updater模块获取版本号
|
self.version_str = updater.get_current_tag() # 从updater模块获取版本号
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -47,10 +47,10 @@ class DataGatherer:
|
|||||||
def thread_func():
|
def thread_func():
|
||||||
|
|
||||||
try:
|
try:
|
||||||
config = pkg.utils.context.get_config()
|
config = context.get_config_manager().data
|
||||||
if not config.report_usage:
|
if not config['report_usage']:
|
||||||
return
|
return
|
||||||
res = requests.get("http://reports.rockchin.top:18989/usage?service_name=qchatgpt.{}&version={}&count={}&msg_source={}".format(subservice_name, self.version_str, count, config.msg_source_adapter))
|
res = requests.get("http://reports.rockchin.top:18989/usage?service_name=qchatgpt.{}&version={}&count={}&msg_source={}".format(subservice_name, self.version_str, count, config['msg_source_adapter']))
|
||||||
if res.status_code != 200 or res.text != "ok":
|
if res.status_code != 200 or res.text != "ok":
|
||||||
logging.warning("report to server failed, status_code: {}, text: {}".format(res.status_code, res.text))
|
logging.warning("report to server failed, status_code: {}, text: {}".format(res.status_code, res.text))
|
||||||
except:
|
except:
|
||||||
@@ -64,7 +64,7 @@ class DataGatherer:
|
|||||||
def report_text_model_usage(self, model, total_tokens):
|
def report_text_model_usage(self, model, total_tokens):
|
||||||
"""调用方报告文字模型请求文字使用量"""
|
"""调用方报告文字模型请求文字使用量"""
|
||||||
|
|
||||||
key_md5 = pkg.utils.context.get_openai_manager().key_mgr.get_using_key_md5() # 以key的md5进行储存
|
key_md5 = context.get_openai_manager().key_mgr.get_using_key_md5() # 以key的md5进行储存
|
||||||
|
|
||||||
if key_md5 not in self.usage:
|
if key_md5 not in self.usage:
|
||||||
self.usage[key_md5] = {}
|
self.usage[key_md5] = {}
|
||||||
@@ -84,7 +84,7 @@ class DataGatherer:
|
|||||||
def report_image_model_usage(self, size):
|
def report_image_model_usage(self, size):
|
||||||
"""调用方报告图片模型请求图片使用量"""
|
"""调用方报告图片模型请求图片使用量"""
|
||||||
|
|
||||||
key_md5 = pkg.utils.context.get_openai_manager().key_mgr.get_using_key_md5()
|
key_md5 = context.get_openai_manager().key_mgr.get_using_key_md5()
|
||||||
|
|
||||||
if key_md5 not in self.usage:
|
if key_md5 not in self.usage:
|
||||||
self.usage[key_md5] = {}
|
self.usage[key_md5] = {}
|
||||||
@@ -131,9 +131,9 @@ class DataGatherer:
|
|||||||
return total
|
return total
|
||||||
|
|
||||||
def dump_to_db(self):
|
def dump_to_db(self):
|
||||||
pkg.utils.context.get_database_manager().dump_usage_json(self.usage)
|
context.get_database_manager().dump_usage_json(self.usage)
|
||||||
|
|
||||||
def load_from_db(self):
|
def load_from_db(self):
|
||||||
json_str = pkg.utils.context.get_database_manager().load_usage_json()
|
json_str = context.get_database_manager().load_usage_json()
|
||||||
if json_str is not None:
|
if json_str is not None:
|
||||||
self.usage = json.loads(json_str)
|
self.usage = json.loads(json_str)
|
||||||
|
|||||||
0
pkg/config/__init__.py
Normal file
0
pkg/config/__init__.py
Normal file
62
pkg/config/impls/pymodule.py
Normal file
62
pkg/config/impls/pymodule.py
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import importlib
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from .. import model as file_model
|
||||||
|
|
||||||
|
|
||||||
|
class PythonModuleConfigFile(file_model.ConfigFile):
|
||||||
|
"""Python模块配置文件"""
|
||||||
|
|
||||||
|
config_file_name: str = None
|
||||||
|
"""配置文件名"""
|
||||||
|
|
||||||
|
template_file_name: str = None
|
||||||
|
"""模板文件名"""
|
||||||
|
|
||||||
|
def __init__(self, config_file_name: str, template_file_name: str) -> None:
|
||||||
|
self.config_file_name = config_file_name
|
||||||
|
self.template_file_name = template_file_name
|
||||||
|
|
||||||
|
def exists(self) -> bool:
|
||||||
|
return os.path.exists(self.config_file_name)
|
||||||
|
|
||||||
|
async def create(self):
|
||||||
|
shutil.copyfile(self.template_file_name, self.config_file_name)
|
||||||
|
|
||||||
|
async def load(self) -> dict:
|
||||||
|
module_name = os.path.splitext(os.path.basename(self.config_file_name))[0]
|
||||||
|
module = importlib.import_module(module_name)
|
||||||
|
|
||||||
|
cfg = {}
|
||||||
|
|
||||||
|
allowed_types = (int, float, str, bool, list, dict)
|
||||||
|
|
||||||
|
for key in dir(module):
|
||||||
|
if key.startswith('__'):
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not isinstance(getattr(module, key), allowed_types):
|
||||||
|
continue
|
||||||
|
|
||||||
|
cfg[key] = getattr(module, key)
|
||||||
|
|
||||||
|
# 从模板模块文件中进行补全
|
||||||
|
module_name = os.path.splitext(os.path.basename(self.template_file_name))[0]
|
||||||
|
module = importlib.import_module(module_name)
|
||||||
|
|
||||||
|
for key in dir(module):
|
||||||
|
if key.startswith('__'):
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not isinstance(getattr(module, key), allowed_types):
|
||||||
|
continue
|
||||||
|
|
||||||
|
if key not in cfg:
|
||||||
|
cfg[key] = getattr(module, key)
|
||||||
|
|
||||||
|
return cfg
|
||||||
|
|
||||||
|
async def save(self, data: dict):
|
||||||
|
logging.warning('Python模块配置文件不支持保存')
|
||||||
23
pkg/config/manager.py
Normal file
23
pkg/config/manager.py
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
from . import model as file_model
|
||||||
|
from ..utils import context
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigManager:
|
||||||
|
"""配置文件管理器"""
|
||||||
|
|
||||||
|
file: file_model.ConfigFile = None
|
||||||
|
"""配置文件实例"""
|
||||||
|
|
||||||
|
data: dict = None
|
||||||
|
"""配置数据"""
|
||||||
|
|
||||||
|
def __init__(self, cfg_file: file_model.ConfigFile) -> None:
|
||||||
|
self.file = cfg_file
|
||||||
|
self.data = {}
|
||||||
|
context.set_config_manager(self)
|
||||||
|
|
||||||
|
async def load_config(self):
|
||||||
|
self.data = await self.file.load()
|
||||||
|
|
||||||
|
async def dump_config(self):
|
||||||
|
await self.file.save(self.data)
|
||||||
27
pkg/config/model.py
Normal file
27
pkg/config/model.py
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import abc
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigFile(metaclass=abc.ABCMeta):
|
||||||
|
"""配置文件抽象类"""
|
||||||
|
|
||||||
|
config_file_name: str = None
|
||||||
|
"""配置文件名"""
|
||||||
|
|
||||||
|
template_file_name: str = None
|
||||||
|
"""模板文件名"""
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def exists(self) -> bool:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
async def create(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
async def load(self) -> dict:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
async def save(self, data: dict):
|
||||||
|
pass
|
||||||
@@ -5,11 +5,10 @@ import hashlib
|
|||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
from sqlite3 import Cursor
|
|
||||||
|
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
|
||||||
import pkg.utils.context
|
from ..utils import context
|
||||||
|
|
||||||
|
|
||||||
class DatabaseManager:
|
class DatabaseManager:
|
||||||
@@ -22,7 +21,7 @@ class DatabaseManager:
|
|||||||
|
|
||||||
self.reconnect()
|
self.reconnect()
|
||||||
|
|
||||||
pkg.utils.context.set_database_manager(self)
|
context.set_database_manager(self)
|
||||||
|
|
||||||
# 连接到数据库文件
|
# 连接到数据库文件
|
||||||
def reconnect(self):
|
def reconnect(self):
|
||||||
@@ -33,7 +32,7 @@ class DatabaseManager:
|
|||||||
def close(self):
|
def close(self):
|
||||||
self.conn.close()
|
self.conn.close()
|
||||||
|
|
||||||
def __execute__(self, *args, **kwargs) -> Cursor:
|
def __execute__(self, *args, **kwargs) -> sqlite3.Cursor:
|
||||||
# logging.debug('SQL: {}'.format(sql))
|
# logging.debug('SQL: {}'.format(sql))
|
||||||
logging.debug('SQL: {}'.format(args))
|
logging.debug('SQL: {}'.format(args))
|
||||||
c = self.cursor.execute(*args, **kwargs)
|
c = self.cursor.execute(*args, **kwargs)
|
||||||
@@ -145,11 +144,11 @@ class DatabaseManager:
|
|||||||
# 从数据库加载还没过期的session数据
|
# 从数据库加载还没过期的session数据
|
||||||
def load_valid_sessions(self) -> dict:
|
def load_valid_sessions(self) -> dict:
|
||||||
# 从数据库中加载所有还没过期的session
|
# 从数据库中加载所有还没过期的session
|
||||||
config = pkg.utils.context.get_config()
|
config = context.get_config_manager().data
|
||||||
self.__execute__("""
|
self.__execute__("""
|
||||||
select `name`, `type`, `number`, `create_timestamp`, `last_interact_timestamp`, `prompt`, `status`, `default_prompt`, `token_counts`
|
select `name`, `type`, `number`, `create_timestamp`, `last_interact_timestamp`, `prompt`, `status`, `default_prompt`, `token_counts`
|
||||||
from `sessions` where `last_interact_timestamp` > {}
|
from `sessions` where `last_interact_timestamp` > {}
|
||||||
""".format(int(time.time()) - config.session_expire_time))
|
""".format(int(time.time()) - config['session_expire_time']))
|
||||||
results = self.cursor.fetchall()
|
results = self.cursor.fetchall()
|
||||||
sessions = {}
|
sessions = {}
|
||||||
for result in results:
|
for result in results:
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import openai
|
|
||||||
from openai.types.chat import chat_completion_message
|
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from .model import RequestBase
|
import openai
|
||||||
|
from openai.types.chat import chat_completion_message
|
||||||
|
|
||||||
from ..funcmgr import get_func_schema_list, execute_function, get_func, get_func_schema, ContentFunctionNotFoundError
|
from .model import RequestBase
|
||||||
|
from .. import funcmgr
|
||||||
|
|
||||||
|
|
||||||
class ChatCompletionRequest(RequestBase):
|
class ChatCompletionRequest(RequestBase):
|
||||||
@@ -81,7 +81,7 @@ class ChatCompletionRequest(RequestBase):
|
|||||||
"messages": self.messages,
|
"messages": self.messages,
|
||||||
}
|
}
|
||||||
|
|
||||||
funcs = get_func_schema_list()
|
funcs = funcmgr.get_func_schema_list()
|
||||||
|
|
||||||
if len(funcs) > 0:
|
if len(funcs) > 0:
|
||||||
args['functions'] = funcs
|
args['functions'] = funcs
|
||||||
@@ -171,7 +171,7 @@ class ChatCompletionRequest(RequestBase):
|
|||||||
# 若不是json格式的异常处理
|
# 若不是json格式的异常处理
|
||||||
except json.decoder.JSONDecodeError:
|
except json.decoder.JSONDecodeError:
|
||||||
# 获取函数的参数列表
|
# 获取函数的参数列表
|
||||||
func_schema = get_func_schema(func_name)
|
func_schema = funcmgr.get_func_schema(func_name)
|
||||||
|
|
||||||
arguments = {
|
arguments = {
|
||||||
func_schema['parameters']['required'][0]: cp_pending_func_call.arguments
|
func_schema['parameters']['required'][0]: cp_pending_func_call.arguments
|
||||||
@@ -182,7 +182,7 @@ class ChatCompletionRequest(RequestBase):
|
|||||||
# 执行函数调用
|
# 执行函数调用
|
||||||
ret = ""
|
ret = ""
|
||||||
try:
|
try:
|
||||||
ret = execute_function(func_name, arguments)
|
ret = funcmgr.execute_function(func_name, arguments)
|
||||||
|
|
||||||
logging.info("函数执行完成。")
|
logging.info("函数执行完成。")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -216,6 +216,5 @@ class ChatCompletionRequest(RequestBase):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
except ContentFunctionNotFoundError:
|
except funcmgr.ContentFunctionNotFoundError:
|
||||||
raise Exception("没有找到函数: {}".format(func_name))
|
raise Exception("没有找到函数: {}".format(func_name))
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import openai
|
import openai
|
||||||
from openai.types import completion, completion_choice
|
from openai.types import completion, completion_choice
|
||||||
|
|
||||||
from .model import RequestBase
|
from . import model
|
||||||
|
|
||||||
|
|
||||||
class CompletionRequest(RequestBase):
|
class CompletionRequest(model.RequestBase):
|
||||||
"""调用Completion接口的请求类。
|
"""调用Completion接口的请求类。
|
||||||
|
|
||||||
调用方可以一直next completion直到finish_reason为stop。
|
调用方可以一直next completion直到finish_reason为stop。
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
# 定义不同接口请求的模型
|
# 定义不同接口请求的模型
|
||||||
import threading
|
|
||||||
import asyncio
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import openai
|
import openai
|
||||||
|
|
||||||
|
from ...utils import context
|
||||||
|
|
||||||
|
|
||||||
class RequestBase:
|
class RequestBase:
|
||||||
|
|
||||||
@@ -16,19 +16,19 @@ class RequestBase:
|
|||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def _next_key(self):
|
def _next_key(self):
|
||||||
import pkg.utils.context as context
|
|
||||||
switched, name = context.get_openai_manager().key_mgr.auto_switch()
|
switched, name = context.get_openai_manager().key_mgr.auto_switch()
|
||||||
logging.debug("切换api-key: switched={}, name={}".format(switched, name))
|
logging.debug("切换api-key: switched={}, name={}".format(switched, name))
|
||||||
self.client.api_key = context.get_openai_manager().key_mgr.get_using_key()
|
self.client.api_key = context.get_openai_manager().key_mgr.get_using_key()
|
||||||
|
|
||||||
def _req(self, **kwargs):
|
def _req(self, **kwargs):
|
||||||
"""处理代理问题"""
|
"""处理代理问题"""
|
||||||
import config
|
logging.debug("请求接口参数: %s", str(kwargs))
|
||||||
|
config = context.get_config_manager().data
|
||||||
|
|
||||||
ret = self.req_func(**kwargs)
|
ret = self.req_func(**kwargs)
|
||||||
logging.debug("接口请求返回:%s", str(ret))
|
logging.debug("接口请求返回:%s", str(ret))
|
||||||
|
|
||||||
if config.switch_strategy == 'active':
|
if config['switch_strategy'] == 'active':
|
||||||
self._next_key()
|
self._next_key()
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
# 多情景预设值管理
|
# 多情景预设值管理
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import config
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
from ..utils import context
|
||||||
|
|
||||||
# __current__ = "default"
|
# __current__ = "default"
|
||||||
# """当前默认使用的情景预设的名称
|
# """当前默认使用的情景预设的名称
|
||||||
|
|
||||||
@@ -16,10 +17,6 @@ import os
|
|||||||
# __scenario_from_files__ = {}
|
# __scenario_from_files__ = {}
|
||||||
|
|
||||||
|
|
||||||
__universal_first_reply__ = "ok, I'll follow your commands."
|
|
||||||
"""通用首次回复"""
|
|
||||||
|
|
||||||
|
|
||||||
class ScenarioMode:
|
class ScenarioMode:
|
||||||
"""情景预设模式抽象类"""
|
"""情景预设模式抽象类"""
|
||||||
|
|
||||||
@@ -66,29 +63,24 @@ class NormalScenarioMode(ScenarioMode):
|
|||||||
"""普通情景预设模式"""
|
"""普通情景预设模式"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
global __universal_first_reply__
|
config = context.get_config_manager().data
|
||||||
|
|
||||||
# 加载config中的default_prompt值
|
# 加载config中的default_prompt值
|
||||||
if type(config.default_prompt) == str:
|
if type(config['default_prompt']) == str:
|
||||||
self.using_prompt_name = "default"
|
self.using_prompt_name = "default"
|
||||||
self.prompts = {"default": [
|
self.prompts = {"default": [
|
||||||
{
|
{
|
||||||
"role": "user",
|
"role": "system",
|
||||||
"content": config.default_prompt
|
"content": config['default_prompt']
|
||||||
},{
|
|
||||||
"role": "assistant",
|
|
||||||
"content": __universal_first_reply__
|
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
|
|
||||||
elif type(config.default_prompt) == dict:
|
elif type(config['default_prompt']) == dict:
|
||||||
for key in config.default_prompt:
|
for key in config['default_prompt']:
|
||||||
self.prompts[key] = [
|
self.prompts[key] = [
|
||||||
{
|
{
|
||||||
"role": "user",
|
"role": "system",
|
||||||
"content": config.default_prompt[key]
|
"content": config['default_prompt'][key]
|
||||||
},{
|
|
||||||
"role": "assistant",
|
|
||||||
"content": __universal_first_reply__
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -98,11 +90,8 @@ class NormalScenarioMode(ScenarioMode):
|
|||||||
with open(os.path.join("prompts", file), encoding="utf-8") as f:
|
with open(os.path.join("prompts", file), encoding="utf-8") as f:
|
||||||
self.prompts[file] = [
|
self.prompts[file] = [
|
||||||
{
|
{
|
||||||
"role": "user",
|
"role": "system",
|
||||||
"content": f.read()
|
"content": f.read()
|
||||||
},{
|
|
||||||
"role": "assistant",
|
|
||||||
"content": __universal_first_reply__
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -137,9 +126,9 @@ def register_all():
|
|||||||
|
|
||||||
def mode_inst() -> ScenarioMode:
|
def mode_inst() -> ScenarioMode:
|
||||||
"""获取指定名称的情景预设模式对象"""
|
"""获取指定名称的情景预设模式对象"""
|
||||||
import config
|
config = context.get_config_manager().data
|
||||||
|
|
||||||
if config.preset_mode == "default":
|
if config['preset_mode'] == "default":
|
||||||
config.preset_mode = "normal"
|
config['preset_mode'] = "normal"
|
||||||
|
|
||||||
return scenario_mode_mapping[config.preset_mode]
|
return scenario_mode_mapping[config['preset_mode']]
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
# 封装了function calling的一些支持函数
|
# 封装了function calling的一些支持函数
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from ..plugin import host
|
||||||
from pkg.plugin import host
|
|
||||||
|
|
||||||
|
|
||||||
class ContentFunctionNotFoundError(Exception):
|
class ContentFunctionNotFoundError(Exception):
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
import hashlib
|
import hashlib
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import pkg.plugin.host as plugin_host
|
from ..plugin import host as plugin_host
|
||||||
import pkg.plugin.models as plugin_models
|
from ..plugin import models as plugin_models
|
||||||
|
|
||||||
|
|
||||||
class KeysManager:
|
class KeysManager:
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
import openai
|
import openai
|
||||||
|
from openai.types import images_response
|
||||||
|
|
||||||
import pkg.openai.keymgr
|
from ..openai import keymgr
|
||||||
import pkg.utils.context
|
from ..utils import context
|
||||||
import pkg.audit.gatherer
|
from ..audit import gatherer
|
||||||
from pkg.openai.modelmgr import select_request_cls
|
from ..openai import modelmgr
|
||||||
|
from ..openai.api import model as api_model
|
||||||
from pkg.openai.api.model import RequestBase
|
|
||||||
|
|
||||||
|
|
||||||
class OpenAIInteract:
|
class OpenAIInteract:
|
||||||
@@ -16,9 +16,9 @@ class OpenAIInteract:
|
|||||||
将文字接口和图片接口封装供调用方使用
|
将文字接口和图片接口封装供调用方使用
|
||||||
"""
|
"""
|
||||||
|
|
||||||
key_mgr: pkg.openai.keymgr.KeysManager = None
|
key_mgr: keymgr.KeysManager = None
|
||||||
|
|
||||||
audit_mgr: pkg.audit.gatherer.DataGatherer = None
|
audit_mgr: gatherer.DataGatherer = None
|
||||||
|
|
||||||
default_image_api_params = {
|
default_image_api_params = {
|
||||||
"size": "256x256",
|
"size": "256x256",
|
||||||
@@ -28,31 +28,32 @@ class OpenAIInteract:
|
|||||||
|
|
||||||
def __init__(self, api_key: str):
|
def __init__(self, api_key: str):
|
||||||
|
|
||||||
self.key_mgr = pkg.openai.keymgr.KeysManager(api_key)
|
self.key_mgr = keymgr.KeysManager(api_key)
|
||||||
self.audit_mgr = pkg.audit.gatherer.DataGatherer()
|
self.audit_mgr = gatherer.DataGatherer()
|
||||||
|
|
||||||
# logging.info("文字总使用量:%d", self.audit_mgr.get_total_text_length())
|
# logging.info("文字总使用量:%d", self.audit_mgr.get_total_text_length())
|
||||||
|
|
||||||
self.client = openai.Client(
|
self.client = openai.Client(
|
||||||
api_key=self.key_mgr.get_using_key()
|
api_key=self.key_mgr.get_using_key(),
|
||||||
|
base_url=openai.base_url
|
||||||
)
|
)
|
||||||
|
|
||||||
pkg.utils.context.set_openai_manager(self)
|
context.set_openai_manager(self)
|
||||||
|
|
||||||
def request_completion(self, messages: list):
|
def request_completion(self, messages: list):
|
||||||
"""请求补全接口回复=
|
"""请求补全接口回复=
|
||||||
"""
|
"""
|
||||||
# 选择接口请求类
|
# 选择接口请求类
|
||||||
config = pkg.utils.context.get_config()
|
config = context.get_config_manager().data
|
||||||
|
|
||||||
request: RequestBase
|
request: api_model.RequestBase
|
||||||
|
|
||||||
model: str = config.completion_api_params['model']
|
model: str = config['completion_api_params']['model']
|
||||||
|
|
||||||
cp_parmas = config.completion_api_params.copy()
|
cp_parmas = config['completion_api_params'].copy()
|
||||||
del cp_parmas['model']
|
del cp_parmas['model']
|
||||||
|
|
||||||
request = select_request_cls(self.client, model, messages, cp_parmas)
|
request = modelmgr.select_request_cls(self.client, model, messages, cp_parmas)
|
||||||
|
|
||||||
# 请求接口
|
# 请求接口
|
||||||
for resp in request:
|
for resp in request:
|
||||||
@@ -65,7 +66,7 @@ class OpenAIInteract:
|
|||||||
|
|
||||||
yield resp
|
yield resp
|
||||||
|
|
||||||
def request_image(self, prompt) -> dict:
|
def request_image(self, prompt) -> images_response.ImagesResponse:
|
||||||
"""请求图片接口回复
|
"""请求图片接口回复
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
@@ -74,10 +75,10 @@ class OpenAIInteract:
|
|||||||
Returns:
|
Returns:
|
||||||
dict: 响应
|
dict: 响应
|
||||||
"""
|
"""
|
||||||
config = pkg.utils.context.get_config()
|
config = context.get_config_manager().data
|
||||||
params = config.image_api_params
|
params = config['image_api_params']
|
||||||
|
|
||||||
response = openai.Image.create(
|
response = self.client.images.generate(
|
||||||
prompt=prompt,
|
prompt=prompt,
|
||||||
n=1,
|
n=1,
|
||||||
**params
|
**params
|
||||||
|
|||||||
@@ -8,39 +8,47 @@ Completion - text-davinci-003 等模型
|
|||||||
import tiktoken
|
import tiktoken
|
||||||
import openai
|
import openai
|
||||||
|
|
||||||
from pkg.openai.api.model import RequestBase
|
from ..openai.api import model as api_model
|
||||||
from pkg.openai.api.completion import CompletionRequest
|
from ..openai.api import completion as api_completion
|
||||||
from pkg.openai.api.chat_completion import ChatCompletionRequest
|
from ..openai.api import chat_completion as api_chat_completion
|
||||||
|
|
||||||
COMPLETION_MODELS = {
|
COMPLETION_MODELS = {
|
||||||
'text-davinci-003',
|
"text-davinci-003", # legacy
|
||||||
'text-davinci-002',
|
"text-davinci-002", # legacy
|
||||||
'code-davinci-002',
|
"code-davinci-002", # legacy
|
||||||
'code-cushman-001',
|
"code-cushman-001", # legacy
|
||||||
'text-curie-001',
|
"text-curie-001", # legacy
|
||||||
'text-babbage-001',
|
"text-babbage-001", # legacy
|
||||||
'text-ada-001',
|
"text-ada-001", # legacy
|
||||||
|
"gpt-3.5-turbo-instruct",
|
||||||
}
|
}
|
||||||
|
|
||||||
CHAT_COMPLETION_MODELS = {
|
CHAT_COMPLETION_MODELS = {
|
||||||
'gpt-3.5-turbo',
|
# GPT 4 系列
|
||||||
'gpt-3.5-turbo-16k',
|
"gpt-4-1106-preview",
|
||||||
'gpt-3.5-turbo-0613',
|
"gpt-4-vision-preview",
|
||||||
'gpt-3.5-turbo-16k-0613',
|
"gpt-4",
|
||||||
# 'gpt-3.5-turbo-0301',
|
"gpt-4-32k",
|
||||||
'gpt-4',
|
"gpt-4-0613",
|
||||||
'gpt-4-0613',
|
"gpt-4-32k-0613",
|
||||||
'gpt-4-32k',
|
"gpt-4-0314", # legacy
|
||||||
'gpt-4-32k-0613',
|
"gpt-4-32k-0314", # legacy
|
||||||
|
# GPT 3.5 系列
|
||||||
|
"gpt-3.5-turbo-1106",
|
||||||
|
"gpt-3.5-turbo",
|
||||||
|
"gpt-3.5-turbo-16k",
|
||||||
|
"gpt-3.5-turbo-0613", # legacy
|
||||||
|
"gpt-3.5-turbo-16k-0613", # legacy
|
||||||
|
"gpt-3.5-turbo-0301", # legacy
|
||||||
# One-API 接入
|
# One-API 接入
|
||||||
'SparkDesk',
|
"SparkDesk",
|
||||||
'chatglm_pro',
|
"chatglm_pro",
|
||||||
'chatglm_std',
|
"chatglm_std",
|
||||||
'chatglm_lite',
|
"chatglm_lite",
|
||||||
'qwen-v1',
|
"qwen-v1",
|
||||||
'qwen-plus-v1',
|
"qwen-plus-v1",
|
||||||
'ERNIE-Bot',
|
"ERNIE-Bot",
|
||||||
'ERNIE-Bot-turbo',
|
"ERNIE-Bot-turbo",
|
||||||
}
|
}
|
||||||
|
|
||||||
EDIT_MODELS = {
|
EDIT_MODELS = {
|
||||||
@@ -52,11 +60,11 @@ IMAGE_MODELS = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def select_request_cls(client: openai.Client, model_name: str, messages: list, args: dict) -> RequestBase:
|
def select_request_cls(client: openai.Client, model_name: str, messages: list, args: dict) -> api_model.RequestBase:
|
||||||
if model_name in CHAT_COMPLETION_MODELS:
|
if model_name in CHAT_COMPLETION_MODELS:
|
||||||
return ChatCompletionRequest(client, model_name, messages, **args)
|
return api_chat_completion.ChatCompletionRequest(client, model_name, messages, **args)
|
||||||
elif model_name in COMPLETION_MODELS:
|
elif model_name in COMPLETION_MODELS:
|
||||||
return CompletionRequest(client, model_name, messages, **args)
|
return api_completion.CompletionRequest(client, model_name, messages, **args)
|
||||||
raise ValueError("不支持模型[{}],请检查配置文件".format(model_name))
|
raise ValueError("不支持模型[{}],请检查配置文件".format(model_name))
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,15 +8,13 @@ import threading
|
|||||||
import time
|
import time
|
||||||
import json
|
import json
|
||||||
|
|
||||||
import pkg.openai.manager
|
from ..openai import manager as openai_manager
|
||||||
import pkg.openai.modelmgr
|
from ..openai import modelmgr as openai_modelmgr
|
||||||
import pkg.database.manager
|
from ..database import manager as database_manager
|
||||||
import pkg.utils.context
|
from ..utils import context as context
|
||||||
|
|
||||||
import pkg.plugin.host as plugin_host
|
from ..plugin import host as plugin_host
|
||||||
import pkg.plugin.models as plugin_models
|
from ..plugin import models as plugin_models
|
||||||
|
|
||||||
from pkg.openai.modelmgr import count_tokens
|
|
||||||
|
|
||||||
# 运行时保存的所有session
|
# 运行时保存的所有session
|
||||||
sessions = {}
|
sessions = {}
|
||||||
@@ -38,11 +36,11 @@ def reset_session_prompt(session_name, prompt):
|
|||||||
f.write(prompt)
|
f.write(prompt)
|
||||||
f.close()
|
f.close()
|
||||||
# 生成新数据
|
# 生成新数据
|
||||||
config = pkg.utils.context.get_config()
|
config = context.get_config_manager().data
|
||||||
prompt = [
|
prompt = [
|
||||||
{
|
{
|
||||||
'role': 'system',
|
'role': 'system',
|
||||||
'content': config.default_prompt['default'] if type(config.default_prompt) == dict else config.default_prompt
|
'content': config['default_prompt']['default'] if type(config['default_prompt']) == dict else config['default_prompt']
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
# 警告
|
# 警告
|
||||||
@@ -61,7 +59,7 @@ def load_sessions():
|
|||||||
|
|
||||||
global sessions
|
global sessions
|
||||||
|
|
||||||
db_inst = pkg.utils.context.get_database_manager()
|
db_inst = context.get_database_manager()
|
||||||
|
|
||||||
session_data = db_inst.load_valid_sessions()
|
session_data = db_inst.load_valid_sessions()
|
||||||
|
|
||||||
@@ -172,17 +170,17 @@ class Session:
|
|||||||
if self.create_timestamp != create_timestamp or self not in sessions.values():
|
if self.create_timestamp != create_timestamp or self not in sessions.values():
|
||||||
return
|
return
|
||||||
|
|
||||||
config = pkg.utils.context.get_config()
|
config = context.get_config_manager().data
|
||||||
if int(time.time()) - self.last_interact_timestamp > config.session_expire_time:
|
if int(time.time()) - self.last_interact_timestamp > config['session_expire_time']:
|
||||||
logging.info('session {} 已过期'.format(self.name))
|
logging.info('session {} 已过期'.format(self.name))
|
||||||
|
|
||||||
# 触发插件事件
|
# 触发插件事件
|
||||||
args = {
|
args = {
|
||||||
'session_name': self.name,
|
'session_name': self.name,
|
||||||
'session': self,
|
'session': self,
|
||||||
'session_expire_time': config.session_expire_time
|
'session_expire_time': config['session_expire_time']
|
||||||
}
|
}
|
||||||
event = pkg.plugin.host.emit(plugin_models.SessionExpired, **args)
|
event = plugin_host.emit(plugin_models.SessionExpired, **args)
|
||||||
if event.is_prevented_default():
|
if event.is_prevented_default():
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -214,12 +212,12 @@ class Session:
|
|||||||
'default_prompt': self.default_prompt,
|
'default_prompt': self.default_prompt,
|
||||||
}
|
}
|
||||||
|
|
||||||
event = pkg.plugin.host.emit(plugin_models.SessionFirstMessageReceived, **args)
|
event = plugin_host.emit(plugin_models.SessionFirstMessageReceived, **args)
|
||||||
if event.is_prevented_default():
|
if event.is_prevented_default():
|
||||||
return None, None, None
|
return None, None, None
|
||||||
|
|
||||||
config = pkg.utils.context.get_config()
|
config = context.get_config_manager().data
|
||||||
max_length = config.prompt_submit_length
|
max_length = config['prompt_submit_length']
|
||||||
|
|
||||||
local_default_prompt = self.default_prompt.copy()
|
local_default_prompt = self.default_prompt.copy()
|
||||||
local_prompt = self.prompt.copy()
|
local_prompt = self.prompt.copy()
|
||||||
@@ -232,7 +230,7 @@ class Session:
|
|||||||
'text_message': text,
|
'text_message': text,
|
||||||
}
|
}
|
||||||
|
|
||||||
event = pkg.plugin.host.emit(plugin_models.PromptPreProcessing, **args)
|
event = plugin_host.emit(plugin_models.PromptPreProcessing, **args)
|
||||||
|
|
||||||
if event.get_return_value('default_prompt') is not None:
|
if event.get_return_value('default_prompt') is not None:
|
||||||
local_default_prompt = event.get_return_value('default_prompt')
|
local_default_prompt = event.get_return_value('default_prompt')
|
||||||
@@ -243,6 +241,7 @@ class Session:
|
|||||||
if event.get_return_value('text_message') is not None:
|
if event.get_return_value('text_message') is not None:
|
||||||
text = event.get_return_value('text_message')
|
text = event.get_return_value('text_message')
|
||||||
|
|
||||||
|
# 裁剪messages到合适长度
|
||||||
prompts, _ = self.cut_out(text, max_length, local_default_prompt, local_prompt)
|
prompts, _ = self.cut_out(text, max_length, local_default_prompt, local_prompt)
|
||||||
|
|
||||||
res_text = ""
|
res_text = ""
|
||||||
@@ -255,15 +254,15 @@ class Session:
|
|||||||
|
|
||||||
funcs = []
|
funcs = []
|
||||||
|
|
||||||
trace_func_calls = config.trace_function_calls
|
trace_func_calls = config['trace_function_calls']
|
||||||
botmgr = pkg.utils.context.get_qqbot_manager()
|
botmgr = context.get_qqbot_manager()
|
||||||
|
|
||||||
session_name_spt: list[str] = self.name.split("_")
|
session_name_spt: list[str] = self.name.split("_")
|
||||||
|
|
||||||
pending_res_text = ""
|
pending_res_text = ""
|
||||||
|
|
||||||
# TODO 对不起,我知道这样非常非常屎山,但我之后会重构的
|
# TODO 对不起,我知道这样非常非常屎山,但我之后会重构的
|
||||||
for resp in pkg.utils.context.get_openai_manager().request_completion(prompts):
|
for resp in context.get_openai_manager().request_completion(prompts):
|
||||||
|
|
||||||
if pending_res_text != "":
|
if pending_res_text != "":
|
||||||
botmgr.adapter.send_message(
|
botmgr.adapter.send_message(
|
||||||
@@ -325,7 +324,6 @@ class Session:
|
|||||||
)
|
)
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
# 向API请求补全
|
# 向API请求补全
|
||||||
# message, total_token = pkg.utils.context.get_openai_manager().request_completion(
|
# message, total_token = pkg.utils.context.get_openai_manager().request_completion(
|
||||||
# prompts,
|
# prompts,
|
||||||
@@ -383,13 +381,13 @@ class Session:
|
|||||||
# 包装目前的对话回合内容
|
# 包装目前的对话回合内容
|
||||||
changable_prompts = []
|
changable_prompts = []
|
||||||
|
|
||||||
use_model = pkg.utils.context.get_config().completion_api_params['model']
|
use_model = context.get_config_manager().data['completion_api_params']['model']
|
||||||
|
|
||||||
ptr = len(prompt) - 1
|
ptr = len(prompt) - 1
|
||||||
|
|
||||||
# 直接从后向前扫描拼接,不管是否是整回合
|
# 直接从后向前扫描拼接,不管是否是整回合
|
||||||
while ptr >= 0:
|
while ptr >= 0:
|
||||||
if count_tokens(prompt[ptr:ptr+1]+changable_prompts, use_model) > max_tokens:
|
if openai_modelmgr.count_tokens(prompt[ptr:ptr+1]+changable_prompts, use_model) > max_tokens:
|
||||||
break
|
break
|
||||||
|
|
||||||
changable_prompts.insert(0, prompt[ptr])
|
changable_prompts.insert(0, prompt[ptr])
|
||||||
@@ -410,14 +408,14 @@ class Session:
|
|||||||
|
|
||||||
logging.debug("cut_out: {}".format(json.dumps(result_prompt, ensure_ascii=False, indent=4)))
|
logging.debug("cut_out: {}".format(json.dumps(result_prompt, ensure_ascii=False, indent=4)))
|
||||||
|
|
||||||
return result_prompt, count_tokens(changable_prompts, use_model)
|
return result_prompt, openai_modelmgr.count_tokens(changable_prompts, use_model)
|
||||||
|
|
||||||
# 持久化session
|
# 持久化session
|
||||||
def persistence(self):
|
def persistence(self):
|
||||||
if self.prompt == self.get_default_prompt():
|
if self.prompt == self.get_default_prompt():
|
||||||
return
|
return
|
||||||
|
|
||||||
db_inst = pkg.utils.context.get_database_manager()
|
db_inst = context.get_database_manager()
|
||||||
|
|
||||||
name_spt = self.name.split('_')
|
name_spt = self.name.split('_')
|
||||||
|
|
||||||
@@ -439,12 +437,12 @@ class Session:
|
|||||||
}
|
}
|
||||||
|
|
||||||
# 此事件不支持阻止默认行为
|
# 此事件不支持阻止默认行为
|
||||||
_ = pkg.plugin.host.emit(plugin_models.SessionExplicitReset, **args)
|
_ = plugin_host.emit(plugin_models.SessionExplicitReset, **args)
|
||||||
|
|
||||||
pkg.utils.context.get_database_manager().explicit_close_session(self.name, self.create_timestamp)
|
context.get_database_manager().explicit_close_session(self.name, self.create_timestamp)
|
||||||
|
|
||||||
if expired:
|
if expired:
|
||||||
pkg.utils.context.get_database_manager().set_session_expired(self.name, self.create_timestamp)
|
context.get_database_manager().set_session_expired(self.name, self.create_timestamp)
|
||||||
|
|
||||||
if not persist: # 不要求保持default prompt
|
if not persist: # 不要求保持default prompt
|
||||||
self.default_prompt = self.get_default_prompt(use_prompt)
|
self.default_prompt = self.get_default_prompt(use_prompt)
|
||||||
@@ -461,11 +459,11 @@ class Session:
|
|||||||
|
|
||||||
# 将本session的数据库状态设置为on_going
|
# 将本session的数据库状态设置为on_going
|
||||||
def set_ongoing(self):
|
def set_ongoing(self):
|
||||||
pkg.utils.context.get_database_manager().set_session_ongoing(self.name, self.create_timestamp)
|
context.get_database_manager().set_session_ongoing(self.name, self.create_timestamp)
|
||||||
|
|
||||||
# 切换到上一个session
|
# 切换到上一个session
|
||||||
def last_session(self):
|
def last_session(self):
|
||||||
last_one = pkg.utils.context.get_database_manager().last_session(self.name, self.last_interact_timestamp)
|
last_one = context.get_database_manager().last_session(self.name, self.last_interact_timestamp)
|
||||||
if last_one is None:
|
if last_one is None:
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
@@ -486,7 +484,7 @@ class Session:
|
|||||||
|
|
||||||
# 切换到下一个session
|
# 切换到下一个session
|
||||||
def next_session(self):
|
def next_session(self):
|
||||||
next_one = pkg.utils.context.get_database_manager().next_session(self.name, self.last_interact_timestamp)
|
next_one = context.get_database_manager().next_session(self.name, self.last_interact_timestamp)
|
||||||
if next_one is None:
|
if next_one is None:
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
@@ -506,13 +504,13 @@ class Session:
|
|||||||
return self
|
return self
|
||||||
|
|
||||||
def list_history(self, capacity: int = 10, page: int = 0):
|
def list_history(self, capacity: int = 10, page: int = 0):
|
||||||
return pkg.utils.context.get_database_manager().list_history(self.name, capacity, page)
|
return context.get_database_manager().list_history(self.name, capacity, page)
|
||||||
|
|
||||||
def delete_history(self, index: int) -> bool:
|
def delete_history(self, index: int) -> bool:
|
||||||
return pkg.utils.context.get_database_manager().delete_history(self.name, index)
|
return context.get_database_manager().delete_history(self.name, index)
|
||||||
|
|
||||||
def delete_all_history(self) -> bool:
|
def delete_all_history(self) -> bool:
|
||||||
return pkg.utils.context.get_database_manager().delete_all_history(self.name)
|
return context.get_database_manager().delete_all_history(self.name)
|
||||||
|
|
||||||
def draw_image(self, prompt: str):
|
def draw_image(self, prompt: str):
|
||||||
return pkg.utils.context.get_openai_manager().request_image(prompt)
|
return context.get_openai_manager().request_image(prompt)
|
||||||
|
|||||||
@@ -7,14 +7,19 @@ import pkgutil
|
|||||||
import sys
|
import sys
|
||||||
import shutil
|
import shutil
|
||||||
import traceback
|
import traceback
|
||||||
|
import time
|
||||||
|
import re
|
||||||
|
|
||||||
import pkg.utils.updater as updater
|
from ..utils import updater as updater
|
||||||
import pkg.utils.context as context
|
from ..utils import network as network
|
||||||
import pkg.plugin.switch as switch
|
from ..utils import context as context
|
||||||
import pkg.plugin.settings as settings
|
from ..plugin import switch as switch
|
||||||
import pkg.qqbot.adapter as msadapter
|
from ..plugin import settings as settings
|
||||||
|
from ..qqbot import adapter as msadapter
|
||||||
|
from ..plugin import metadata as metadata
|
||||||
|
|
||||||
from mirai import Mirai
|
from mirai import Mirai
|
||||||
|
import requests
|
||||||
|
|
||||||
from CallingGPT.session.session import Session
|
from CallingGPT.session.session import Session
|
||||||
|
|
||||||
@@ -65,6 +70,8 @@ def generate_plugin_order():
|
|||||||
def iter_plugins():
|
def iter_plugins():
|
||||||
"""按照顺序迭代插件"""
|
"""按照顺序迭代插件"""
|
||||||
for plugin_name in __plugins_order__:
|
for plugin_name in __plugins_order__:
|
||||||
|
if plugin_name not in __plugins__:
|
||||||
|
continue
|
||||||
yield __plugins__[plugin_name]
|
yield __plugins__[plugin_name]
|
||||||
|
|
||||||
|
|
||||||
@@ -113,10 +120,15 @@ def load_plugins():
|
|||||||
# 加载插件顺序
|
# 加载插件顺序
|
||||||
settings.load_settings()
|
settings.load_settings()
|
||||||
|
|
||||||
|
logging.debug("registered plugins: {}".format(__plugins__))
|
||||||
|
|
||||||
# 输出已注册的内容函数列表
|
# 输出已注册的内容函数列表
|
||||||
logging.debug("registered content functions: {}".format(__callable_functions__))
|
logging.debug("registered content functions: {}".format(__callable_functions__))
|
||||||
logging.debug("function instance map: {}".format(__function_inst_map__))
|
logging.debug("function instance map: {}".format(__function_inst_map__))
|
||||||
|
|
||||||
|
# 迁移插件源地址记录
|
||||||
|
metadata.do_plugin_git_repo_migrate()
|
||||||
|
|
||||||
|
|
||||||
def initialize_plugins():
|
def initialize_plugins():
|
||||||
"""初始化插件"""
|
"""初始化插件"""
|
||||||
@@ -135,6 +147,7 @@ def initialize_plugins():
|
|||||||
successfully_initialized_plugins.append(plugin['name'])
|
successfully_initialized_plugins.append(plugin['name'])
|
||||||
except:
|
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)))
|
logging.info("以下插件已初始化: {}".format(", ".join(successfully_initialized_plugins)))
|
||||||
|
|
||||||
@@ -155,34 +168,100 @@ def unload_plugins():
|
|||||||
# logging.error("插件{}卸载时发生错误: {}".format(plugin['name'], sys.exc_info()))
|
# logging.error("插件{}卸载时发生错误: {}".format(plugin['name'], sys.exc_info()))
|
||||||
|
|
||||||
|
|
||||||
def install_plugin(repo_url: str):
|
def get_github_plugin_repo_label(repo_url: str) -> list[str]:
|
||||||
"""安装插件,从git储存库获取并解决依赖"""
|
"""获取username, repo"""
|
||||||
try:
|
|
||||||
import pkg.utils.pkgmgr
|
|
||||||
pkg.utils.pkgmgr.ensure_dulwich()
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
try:
|
# 提取 username/repo , 正则表达式
|
||||||
import dulwich
|
repo = re.findall(r'(?:https?://github\.com/|git@github\.com:)([^/]+/[^/]+?)(?:\.git|/|$)', repo_url)
|
||||||
except ModuleNotFoundError:
|
|
||||||
raise Exception("dulwich模块未安装,请查看 https://github.com/RockChinQ/QChatGPT/issues/77")
|
|
||||||
|
|
||||||
from dulwich import porcelain
|
if len(repo) > 0: # github
|
||||||
|
return repo[0].split("/")
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
logging.info("克隆插件储存库: {}".format(repo_url))
|
|
||||||
repo = porcelain.clone(repo_url, "plugins/"+repo_url.split(".git")[0].split("/")[-1]+"/", checkout=True)
|
|
||||||
|
|
||||||
|
def download_plugin_source_code(repo_url: str, target_path: str) -> str:
|
||||||
|
"""下载插件源码"""
|
||||||
|
# 检查源类型
|
||||||
|
|
||||||
|
# 提取 username/repo , 正则表达式
|
||||||
|
repo = get_github_plugin_repo_label(repo_url)
|
||||||
|
|
||||||
|
target_path += repo[1]
|
||||||
|
|
||||||
|
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
|
||||||
|
)
|
||||||
|
|
||||||
|
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(target_path):
|
||||||
|
shutil.rmtree(target_path)
|
||||||
|
|
||||||
|
os.makedirs("temp/"+target_path)
|
||||||
|
|
||||||
|
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")
|
||||||
|
|
||||||
|
# 目标是 username-repo-hash , 用正则表达式提取完整的文件夹名,复制到 plugins/repo
|
||||||
|
import glob
|
||||||
|
|
||||||
|
# 获取解压后的文件夹名
|
||||||
|
unzip_dir = glob.glob("temp/"+target_path+"/*")[0]
|
||||||
|
|
||||||
|
# 复制到 plugins/repo
|
||||||
|
shutil.copytree(unzip_dir, target_path+"/")
|
||||||
|
|
||||||
|
# 删除解压后的文件夹
|
||||||
|
shutil.rmtree(unzip_dir)
|
||||||
|
|
||||||
|
logging.info("解压完成")
|
||||||
|
else:
|
||||||
|
raise Exception("暂不支持的源类型,请使用 GitHub 仓库发行插件。")
|
||||||
|
|
||||||
|
return repo[1]
|
||||||
|
|
||||||
|
|
||||||
|
def check_requirements(path: str):
|
||||||
# 检查此目录是否包含requirements.txt
|
# 检查此目录是否包含requirements.txt
|
||||||
if os.path.exists("plugins/"+repo_url.split(".git")[0].split("/")[-1]+"/requirements.txt"):
|
if os.path.exists(path+"/requirements.txt"):
|
||||||
logging.info("检测到requirements.txt,正在安装依赖")
|
logging.info("检测到requirements.txt,正在安装依赖")
|
||||||
import pkg.utils.pkgmgr
|
import pkg.utils.pkgmgr
|
||||||
pkg.utils.pkgmgr.install_requirements("plugins/"+repo_url.split(".git")[0].split("/")[-1]+"/requirements.txt")
|
pkg.utils.pkgmgr.install_requirements(path+"/requirements.txt")
|
||||||
|
|
||||||
import pkg.utils.log as log
|
import pkg.utils.log as log
|
||||||
log.reset_logging()
|
log.reset_logging()
|
||||||
|
|
||||||
|
|
||||||
|
def install_plugin(repo_url: str):
|
||||||
|
"""安装插件,从git储存库获取并解决依赖"""
|
||||||
|
|
||||||
|
repo_label = download_plugin_source_code(repo_url, "plugins/")
|
||||||
|
|
||||||
|
check_requirements("plugins/"+repo_label)
|
||||||
|
|
||||||
|
metadata.set_plugin_metadata(repo_label, repo_url, int(time.time()), "HEAD")
|
||||||
|
|
||||||
|
|
||||||
def uninstall_plugin(plugin_name: str) -> str:
|
def uninstall_plugin(plugin_name: str) -> str:
|
||||||
"""卸载插件"""
|
"""卸载插件"""
|
||||||
if plugin_name not in __plugins__:
|
if plugin_name not in __plugins__:
|
||||||
@@ -202,39 +281,43 @@ def uninstall_plugin(plugin_name: str) -> str:
|
|||||||
def update_plugin(plugin_name: str):
|
def update_plugin(plugin_name: str):
|
||||||
"""更新插件"""
|
"""更新插件"""
|
||||||
# 检查是否有远程地址记录
|
# 检查是否有远程地址记录
|
||||||
target_plugin_dir = "plugins/" + __plugins__[plugin_name]['path'].replace("\\", "/").split("plugins/")[1].split("/")[0]
|
plugin_path_name = get_plugin_path_name_by_plugin_name(plugin_name)
|
||||||
|
|
||||||
remote_url = updater.get_remote_url(target_plugin_dir)
|
meta = metadata.get_plugin_metadata(plugin_path_name)
|
||||||
|
|
||||||
|
if meta == {}:
|
||||||
|
raise Exception("没有此插件元数据信息,无法更新")
|
||||||
|
|
||||||
|
remote_url = meta['source']
|
||||||
if remote_url == "https://github.com/RockChinQ/QChatGPT" or remote_url == "https://gitee.com/RockChin/QChatGPT" \
|
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":
|
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("插件没有远程地址记录,无法更新")
|
raise Exception("插件没有远程地址记录,无法更新")
|
||||||
|
|
||||||
# 把远程clone到temp/plugins/update/插件名
|
# 重新安装插件
|
||||||
logging.info("克隆插件储存库: {}".format(remote_url))
|
logging.info("正在重新安装插件以进行更新...")
|
||||||
|
|
||||||
from dulwich import porcelain
|
install_plugin(remote_url)
|
||||||
clone_target_dir = "temp/plugins/update/"+target_plugin_dir.split("/")[-1]+"/"
|
|
||||||
|
|
||||||
if os.path.exists(clone_target_dir):
|
|
||||||
shutil.rmtree(clone_target_dir)
|
|
||||||
|
|
||||||
if not os.path.exists(clone_target_dir):
|
def get_plugin_name_by_path_name(plugin_path_name: str) -> str:
|
||||||
os.makedirs(clone_target_dir)
|
for k, v in __plugins__.items():
|
||||||
repo = porcelain.clone(remote_url, clone_target_dir, checkout=True)
|
if v['path'] == "plugins/"+plugin_path_name+"/main.py":
|
||||||
|
return k
|
||||||
|
return None
|
||||||
|
|
||||||
# 检查此目录是否包含requirements.txt
|
|
||||||
if os.path.exists(clone_target_dir+"requirements.txt"):
|
|
||||||
logging.info("检测到requirements.txt,正在安装依赖")
|
|
||||||
import pkg.utils.pkgmgr
|
|
||||||
pkg.utils.pkgmgr.install_requirements(clone_target_dir+"requirements.txt")
|
|
||||||
|
|
||||||
import pkg.utils.log as log
|
def get_plugin_path_name_by_plugin_name(plugin_name: str) -> str:
|
||||||
log.reset_logging()
|
if plugin_name not in __plugins__:
|
||||||
|
return None
|
||||||
|
|
||||||
|
plugin_main_module_path = __plugins__[plugin_name]['path']
|
||||||
|
|
||||||
# 将temp/plugins/update/插件名 覆盖到 plugins/插件名
|
plugin_main_module_path = plugin_main_module_path.replace("\\", "/")
|
||||||
shutil.rmtree(target_plugin_dir)
|
|
||||||
|
spt = plugin_main_module_path.split("/")
|
||||||
|
|
||||||
|
return spt[1]
|
||||||
|
|
||||||
shutil.copytree(clone_target_dir, target_plugin_dir)
|
|
||||||
|
|
||||||
class EventContext:
|
class EventContext:
|
||||||
"""事件上下文"""
|
"""事件上下文"""
|
||||||
|
|||||||
87
pkg/plugin/metadata.py
Normal file
87
pkg/plugin/metadata.py
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import json
|
||||||
|
import time
|
||||||
|
|
||||||
|
import dulwich.errors as dulwich_err
|
||||||
|
|
||||||
|
from ..utils import updater
|
||||||
|
|
||||||
|
|
||||||
|
def read_metadata_file() -> dict:
|
||||||
|
# 读取 plugins/metadata.json 文件
|
||||||
|
if not os.path.exists('plugins/metadata.json'):
|
||||||
|
return {}
|
||||||
|
with open('plugins/metadata.json', 'r') as f:
|
||||||
|
return json.load(f)
|
||||||
|
|
||||||
|
|
||||||
|
def write_metadata_file(metadata: dict):
|
||||||
|
if not os.path.exists('plugins'):
|
||||||
|
os.mkdir('plugins')
|
||||||
|
|
||||||
|
with open('plugins/metadata.json', 'w') as f:
|
||||||
|
json.dump(metadata, f, indent=4, ensure_ascii=False)
|
||||||
|
|
||||||
|
|
||||||
|
def do_plugin_git_repo_migrate():
|
||||||
|
# 仅在 plugins/metadata.json 不存在时执行
|
||||||
|
if os.path.exists('plugins/metadata.json'):
|
||||||
|
return
|
||||||
|
|
||||||
|
metadata = read_metadata_file()
|
||||||
|
|
||||||
|
# 遍历 plugins 下所有目录,获取目录的git远程地址
|
||||||
|
for plugin_name in os.listdir('plugins'):
|
||||||
|
plugin_path = os.path.join('plugins', plugin_name)
|
||||||
|
if not os.path.isdir(plugin_path):
|
||||||
|
continue
|
||||||
|
|
||||||
|
remote_url = None
|
||||||
|
try:
|
||||||
|
remote_url = updater.get_remote_url(plugin_path)
|
||||||
|
except dulwich_err.NotGitRepository:
|
||||||
|
continue
|
||||||
|
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":
|
||||||
|
continue
|
||||||
|
|
||||||
|
from . import host
|
||||||
|
|
||||||
|
if plugin_name not in metadata:
|
||||||
|
metadata[plugin_name] = {
|
||||||
|
'source': remote_url,
|
||||||
|
'install_timestamp': int(time.time()),
|
||||||
|
'ref': 'HEAD',
|
||||||
|
}
|
||||||
|
|
||||||
|
write_metadata_file(metadata)
|
||||||
|
|
||||||
|
|
||||||
|
def set_plugin_metadata(
|
||||||
|
plugin_name: str,
|
||||||
|
source: str,
|
||||||
|
install_timestamp: int,
|
||||||
|
ref: str,
|
||||||
|
):
|
||||||
|
metadata = read_metadata_file()
|
||||||
|
metadata[plugin_name] = {
|
||||||
|
'source': source,
|
||||||
|
'install_timestamp': install_timestamp,
|
||||||
|
'ref': ref,
|
||||||
|
}
|
||||||
|
write_metadata_file(metadata)
|
||||||
|
|
||||||
|
|
||||||
|
def remove_plugin_metadata(plugin_name: str):
|
||||||
|
metadata = read_metadata_file()
|
||||||
|
if plugin_name in metadata:
|
||||||
|
del metadata[plugin_name]
|
||||||
|
write_metadata_file(metadata)
|
||||||
|
|
||||||
|
|
||||||
|
def get_plugin_metadata(plugin_name: str) -> dict:
|
||||||
|
metadata = read_metadata_file()
|
||||||
|
if plugin_name in metadata:
|
||||||
|
return metadata[plugin_name]
|
||||||
|
return {}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
import pkg.plugin.host as host
|
from ..plugin import host
|
||||||
import pkg.utils.context
|
from ..utils import context
|
||||||
|
|
||||||
PersonMessageReceived = "person_message_received"
|
PersonMessageReceived = "person_message_received"
|
||||||
"""收到私聊消息时,在判断是否应该响应前触发
|
"""收到私聊消息时,在判断是否应该响应前触发
|
||||||
@@ -285,7 +285,7 @@ def register(name: str, description: str, version: str, author: str):
|
|||||||
cls.description = description
|
cls.description = description
|
||||||
cls.version = version
|
cls.version = version
|
||||||
cls.author = author
|
cls.author = author
|
||||||
cls.host = pkg.utils.context.get_plugin_host()
|
cls.host = context.get_plugin_host()
|
||||||
cls.enabled = True
|
cls.enabled = True
|
||||||
cls.path = host.__current_module_path__
|
cls.path = host.__current_module_path__
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import pkg.plugin.host as host
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from ..plugin import host
|
||||||
|
|
||||||
def wrapper_dict_from_runtime_context() -> dict:
|
def wrapper_dict_from_runtime_context() -> dict:
|
||||||
"""从变量中包装settings.json的数据字典"""
|
"""从变量中包装settings.json的数据字典"""
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import json
|
|||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import pkg.plugin.host as host
|
from ..plugin import host
|
||||||
|
|
||||||
|
|
||||||
def wrapper_dict_from_plugin_list() -> dict:
|
def wrapper_dict_from_plugin_list() -> dict:
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import mirai
|
|||||||
|
|
||||||
|
|
||||||
class MessageSourceAdapter:
|
class MessageSourceAdapter:
|
||||||
|
bot_account_id: int
|
||||||
def __init__(self, config: dict):
|
def __init__(self, config: dict):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
import pkg.utils.context
|
from ..utils import context
|
||||||
|
|
||||||
|
|
||||||
def is_banned(launcher_type: str, launcher_id: int, sender_id: int) -> bool:
|
def is_banned(launcher_type: str, launcher_id: int, sender_id: int) -> bool:
|
||||||
if not pkg.utils.context.get_qqbot_manager().enable_banlist:
|
if not context.get_qqbot_manager().enable_banlist:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
result = False
|
result = False
|
||||||
|
|
||||||
if launcher_type == 'group':
|
if launcher_type == 'group':
|
||||||
# 检查是否显式声明发起人QQ要被person忽略
|
# 检查是否显式声明发起人QQ要被person忽略
|
||||||
if sender_id in pkg.utils.context.get_qqbot_manager().ban_person:
|
if sender_id in context.get_qqbot_manager().ban_person:
|
||||||
result = True
|
result = True
|
||||||
else:
|
else:
|
||||||
for group_rule in pkg.utils.context.get_qqbot_manager().ban_group:
|
for group_rule in context.get_qqbot_manager().ban_group:
|
||||||
if type(group_rule) == int:
|
if type(group_rule) == int:
|
||||||
if group_rule == launcher_id: # 此群群号被禁用
|
if group_rule == launcher_id: # 此群群号被禁用
|
||||||
result = True
|
result = True
|
||||||
@@ -32,7 +32,7 @@ def is_banned(launcher_type: str, launcher_id: int, sender_id: int) -> bool:
|
|||||||
|
|
||||||
else:
|
else:
|
||||||
# ban_person, 与群规则相同
|
# ban_person, 与群规则相同
|
||||||
for person_rule in pkg.utils.context.get_qqbot_manager().ban_person:
|
for person_rule in context.get_qqbot_manager().ban_person:
|
||||||
if type(person_rule) == int:
|
if type(person_rule) == int:
|
||||||
if person_rule == launcher_id:
|
if person_rule == launcher_id:
|
||||||
result = True
|
result = True
|
||||||
|
|||||||
@@ -2,21 +2,21 @@
|
|||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
import base64
|
import base64
|
||||||
|
import typing
|
||||||
|
|
||||||
import config
|
|
||||||
from mirai.models.message import MessageComponent, MessageChain, Image
|
from mirai.models.message import MessageComponent, MessageChain, Image
|
||||||
from mirai.models.message import ForwardMessageNode
|
from mirai.models.message import ForwardMessageNode
|
||||||
from mirai.models.base import MiraiBaseModel
|
from mirai.models.base import MiraiBaseModel
|
||||||
from typing import List
|
|
||||||
import pkg.utils.context as context
|
from ..utils import text2img
|
||||||
import pkg.utils.text2img as text2img
|
from ..utils import context
|
||||||
|
|
||||||
|
|
||||||
class ForwardMessageDiaplay(MiraiBaseModel):
|
class ForwardMessageDiaplay(MiraiBaseModel):
|
||||||
title: str = "群聊的聊天记录"
|
title: str = "群聊的聊天记录"
|
||||||
brief: str = "[聊天记录]"
|
brief: str = "[聊天记录]"
|
||||||
source: str = "聊天记录"
|
source: str = "聊天记录"
|
||||||
preview: List[str] = []
|
preview: typing.List[str] = []
|
||||||
summary: str = "查看x条转发消息"
|
summary: str = "查看x条转发消息"
|
||||||
|
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ class Forward(MessageComponent):
|
|||||||
"""消息组件类型。"""
|
"""消息组件类型。"""
|
||||||
display: ForwardMessageDiaplay
|
display: ForwardMessageDiaplay
|
||||||
"""显示信息"""
|
"""显示信息"""
|
||||||
node_list: List[ForwardMessageNode]
|
node_list: typing.List[ForwardMessageNode]
|
||||||
"""转发消息节点列表。"""
|
"""转发消息节点列表。"""
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
if len(args) == 1:
|
if len(args) == 1:
|
||||||
@@ -64,13 +64,16 @@ def text_to_image(text: str) -> MessageComponent:
|
|||||||
|
|
||||||
def check_text(text: str) -> list:
|
def check_text(text: str) -> list:
|
||||||
"""检查文本是否为长消息,并转换成该使用的消息链组件"""
|
"""检查文本是否为长消息,并转换成该使用的消息链组件"""
|
||||||
if len(text) > config.blob_message_threshold:
|
|
||||||
|
config = context.get_config_manager().data
|
||||||
|
|
||||||
|
if len(text) > config['blob_message_threshold']:
|
||||||
|
|
||||||
# logging.info("长消息: {}".format(text))
|
# logging.info("长消息: {}".format(text))
|
||||||
if config.blob_message_strategy == 'image':
|
if config['blob_message_strategy'] == 'image':
|
||||||
# 转换成图片
|
# 转换成图片
|
||||||
return [text_to_image(text)]
|
return [text_to_image(text)]
|
||||||
elif config.blob_message_strategy == 'forward':
|
elif config['blob_message_strategy'] == 'forward':
|
||||||
|
|
||||||
# 包装转发消息
|
# 包装转发消息
|
||||||
display = ForwardMessageDiaplay(
|
display = ForwardMessageDiaplay(
|
||||||
@@ -82,7 +85,7 @@ def check_text(text: str) -> list:
|
|||||||
)
|
)
|
||||||
|
|
||||||
node = ForwardMessageNode(
|
node = ForwardMessageNode(
|
||||||
sender_id=config.mirai_http_api_config['qq'],
|
sender_id=config['mirai_http_api_config']['qq'],
|
||||||
sender_name='bot',
|
sender_name='bot',
|
||||||
message_chain=MessageChain([text])
|
message_chain=MessageChain([text])
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
import importlib
|
|
||||||
import inspect
|
|
||||||
import logging
|
import logging
|
||||||
import copy
|
import copy
|
||||||
import pkgutil
|
import pkgutil
|
||||||
import traceback
|
import traceback
|
||||||
import types
|
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
from ..aamgr import AbstractCommandNode, Context
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from mirai import Image
|
import mirai
|
||||||
import config
|
|
||||||
|
from .. import aamgr
|
||||||
|
from ....utils import context
|
||||||
|
|
||||||
|
|
||||||
@AbstractCommandNode.register(
|
@aamgr.AbstractCommandNode.register(
|
||||||
parent=None,
|
parent=None,
|
||||||
name="draw",
|
name="draw",
|
||||||
description="使用DALL·E生成图片",
|
description="使用DALL·E生成图片",
|
||||||
@@ -13,9 +14,9 @@ import config
|
|||||||
aliases=[],
|
aliases=[],
|
||||||
privilege=1
|
privilege=1
|
||||||
)
|
)
|
||||||
class DrawCommand(AbstractCommandNode):
|
class DrawCommand(aamgr.AbstractCommandNode):
|
||||||
@classmethod
|
@classmethod
|
||||||
def process(cls, ctx: Context) -> tuple[bool, list]:
|
def process(cls, ctx: aamgr.Context) -> tuple[bool, list]:
|
||||||
import pkg.openai.session
|
import pkg.openai.session
|
||||||
|
|
||||||
reply = []
|
reply = []
|
||||||
@@ -28,9 +29,9 @@ class DrawCommand(AbstractCommandNode):
|
|||||||
res = session.draw_image(" ".join(ctx.params))
|
res = session.draw_image(" ".join(ctx.params))
|
||||||
|
|
||||||
logging.debug("draw_image result:{}".format(res))
|
logging.debug("draw_image result:{}".format(res))
|
||||||
reply = [Image(url=res['data'][0]['url'])]
|
reply = [mirai.Image(url=res.data[0].url)]
|
||||||
if not (hasattr(config, 'include_image_description')
|
config = context.get_config_manager().data
|
||||||
and not config.include_image_description):
|
if config['include_image_description']:
|
||||||
reply.append(" ".join(ctx.params))
|
reply.append(" ".join(ctx.params))
|
||||||
|
|
||||||
return True, reply
|
return True, reply
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
from ..aamgr import AbstractCommandNode, Context
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
from .. import aamgr
|
||||||
|
|
||||||
@AbstractCommandNode.register(
|
@aamgr.AbstractCommandNode.register(
|
||||||
parent=None,
|
parent=None,
|
||||||
name="func",
|
name="func",
|
||||||
description="管理内容函数",
|
description="管理内容函数",
|
||||||
@@ -12,9 +11,9 @@ import json
|
|||||||
aliases=[],
|
aliases=[],
|
||||||
privilege=1
|
privilege=1
|
||||||
)
|
)
|
||||||
class FuncCommand(AbstractCommandNode):
|
class FuncCommand(aamgr.AbstractCommandNode):
|
||||||
@classmethod
|
@classmethod
|
||||||
def process(cls, ctx: Context) -> tuple[bool, list]:
|
def process(cls, ctx: aamgr.Context) -> tuple[bool, list]:
|
||||||
from pkg.plugin.models import host
|
from pkg.plugin.models import host
|
||||||
|
|
||||||
reply = []
|
reply = []
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
from ..aamgr import AbstractCommandNode, Context
|
from ....plugin import host as plugin_host
|
||||||
|
from ....utils import updater
|
||||||
import os
|
from .. import aamgr
|
||||||
|
|
||||||
import pkg.plugin.host as plugin_host
|
|
||||||
import pkg.utils.updater as updater
|
|
||||||
|
|
||||||
|
|
||||||
@AbstractCommandNode.register(
|
@aamgr.AbstractCommandNode.register(
|
||||||
parent=None,
|
parent=None,
|
||||||
name="plugin",
|
name="plugin",
|
||||||
description="插件管理",
|
description="插件管理",
|
||||||
@@ -14,9 +11,9 @@ import pkg.utils.updater as updater
|
|||||||
aliases=[],
|
aliases=[],
|
||||||
privilege=1
|
privilege=1
|
||||||
)
|
)
|
||||||
class PluginCommand(AbstractCommandNode):
|
class PluginCommand(aamgr.AbstractCommandNode):
|
||||||
@classmethod
|
@classmethod
|
||||||
def process(cls, ctx: Context) -> tuple[bool, list]:
|
def process(cls, ctx: aamgr.Context) -> tuple[bool, list]:
|
||||||
reply = []
|
reply = []
|
||||||
plugin_list = plugin_host.__plugins__
|
plugin_list = plugin_host.__plugins__
|
||||||
if len(ctx.params) == 0:
|
if len(ctx.params) == 0:
|
||||||
@@ -48,7 +45,7 @@ class PluginCommand(AbstractCommandNode):
|
|||||||
return False, []
|
return False, []
|
||||||
|
|
||||||
|
|
||||||
@AbstractCommandNode.register(
|
@aamgr.AbstractCommandNode.register(
|
||||||
parent=PluginCommand,
|
parent=PluginCommand,
|
||||||
name="get",
|
name="get",
|
||||||
description="安装插件",
|
description="安装插件",
|
||||||
@@ -56,9 +53,9 @@ class PluginCommand(AbstractCommandNode):
|
|||||||
aliases=[],
|
aliases=[],
|
||||||
privilege=2
|
privilege=2
|
||||||
)
|
)
|
||||||
class PluginGetCommand(AbstractCommandNode):
|
class PluginGetCommand(aamgr.AbstractCommandNode):
|
||||||
@classmethod
|
@classmethod
|
||||||
def process(cls, ctx: Context) -> tuple[bool, list]:
|
def process(cls, ctx: aamgr.Context) -> tuple[bool, list]:
|
||||||
import threading
|
import threading
|
||||||
import logging
|
import logging
|
||||||
import pkg.utils.context
|
import pkg.utils.context
|
||||||
@@ -81,17 +78,17 @@ class PluginGetCommand(AbstractCommandNode):
|
|||||||
return True, reply
|
return True, reply
|
||||||
|
|
||||||
|
|
||||||
@AbstractCommandNode.register(
|
@aamgr.AbstractCommandNode.register(
|
||||||
parent=PluginCommand,
|
parent=PluginCommand,
|
||||||
name="update",
|
name="update",
|
||||||
description="更新所有插件",
|
description="更新指定插件或全部插件",
|
||||||
usage="!plugin update",
|
usage="!plugin update",
|
||||||
aliases=[],
|
aliases=[],
|
||||||
privilege=2
|
privilege=2
|
||||||
)
|
)
|
||||||
class PluginUpdateCommand(AbstractCommandNode):
|
class PluginUpdateCommand(aamgr.AbstractCommandNode):
|
||||||
@classmethod
|
@classmethod
|
||||||
def process(cls, ctx: Context) -> tuple[bool, list]:
|
def process(cls, ctx: aamgr.Context) -> tuple[bool, list]:
|
||||||
import threading
|
import threading
|
||||||
import logging
|
import logging
|
||||||
plugin_list = plugin_host.__plugins__
|
plugin_list = plugin_host.__plugins__
|
||||||
@@ -110,7 +107,9 @@ class PluginUpdateCommand(AbstractCommandNode):
|
|||||||
plugin_host.update_plugin(key)
|
plugin_host.update_plugin(key)
|
||||||
updated.append(key)
|
updated.append(key)
|
||||||
else:
|
else:
|
||||||
if ctx.crt_params[0] in plugin_list:
|
plugin_path_name = plugin_host.get_plugin_path_name_by_plugin_name(ctx.crt_params[0])
|
||||||
|
|
||||||
|
if plugin_path_name is not None:
|
||||||
plugin_host.update_plugin(ctx.crt_params[0])
|
plugin_host.update_plugin(ctx.crt_params[0])
|
||||||
updated.append(ctx.crt_params[0])
|
updated.append(ctx.crt_params[0])
|
||||||
else:
|
else:
|
||||||
@@ -119,7 +118,7 @@ class PluginUpdateCommand(AbstractCommandNode):
|
|||||||
pkg.utils.context.get_qqbot_manager().notify_admin("已更新插件: {}, 请发送 !reload 重载插件".format(", ".join(updated)))
|
pkg.utils.context.get_qqbot_manager().notify_admin("已更新插件: {}, 请发送 !reload 重载插件".format(", ".join(updated)))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error("插件更新失败:{}".format(e))
|
logging.error("插件更新失败:{}".format(e))
|
||||||
pkg.utils.context.get_qqbot_manager().notify_admin("插件更新失败:{} 请尝试手动更新插件".format(e))
|
pkg.utils.context.get_qqbot_manager().notify_admin("插件更新失败:{} 请使用 !plugin 命令确认插件名称或尝试手动更新插件".format(e))
|
||||||
|
|
||||||
reply = ["[bot]正在更新插件,请勿重复发起..."]
|
reply = ["[bot]正在更新插件,请勿重复发起..."]
|
||||||
threading.Thread(target=closure).start()
|
threading.Thread(target=closure).start()
|
||||||
@@ -128,7 +127,7 @@ class PluginUpdateCommand(AbstractCommandNode):
|
|||||||
return True, reply
|
return True, reply
|
||||||
|
|
||||||
|
|
||||||
@AbstractCommandNode.register(
|
@aamgr.AbstractCommandNode.register(
|
||||||
parent=PluginCommand,
|
parent=PluginCommand,
|
||||||
name="del",
|
name="del",
|
||||||
description="删除插件",
|
description="删除插件",
|
||||||
@@ -136,9 +135,9 @@ class PluginUpdateCommand(AbstractCommandNode):
|
|||||||
aliases=[],
|
aliases=[],
|
||||||
privilege=2
|
privilege=2
|
||||||
)
|
)
|
||||||
class PluginDelCommand(AbstractCommandNode):
|
class PluginDelCommand(aamgr.AbstractCommandNode):
|
||||||
@classmethod
|
@classmethod
|
||||||
def process(cls, ctx: Context) -> tuple[bool, list]:
|
def process(cls, ctx: aamgr.Context) -> tuple[bool, list]:
|
||||||
plugin_list = plugin_host.__plugins__
|
plugin_list = plugin_host.__plugins__
|
||||||
reply = []
|
reply = []
|
||||||
|
|
||||||
@@ -155,7 +154,7 @@ class PluginDelCommand(AbstractCommandNode):
|
|||||||
return True, reply
|
return True, reply
|
||||||
|
|
||||||
|
|
||||||
@AbstractCommandNode.register(
|
@aamgr.AbstractCommandNode.register(
|
||||||
parent=PluginCommand,
|
parent=PluginCommand,
|
||||||
name="on",
|
name="on",
|
||||||
description="启用指定插件",
|
description="启用指定插件",
|
||||||
@@ -163,7 +162,7 @@ class PluginDelCommand(AbstractCommandNode):
|
|||||||
aliases=[],
|
aliases=[],
|
||||||
privilege=2
|
privilege=2
|
||||||
)
|
)
|
||||||
@AbstractCommandNode.register(
|
@aamgr.AbstractCommandNode.register(
|
||||||
parent=PluginCommand,
|
parent=PluginCommand,
|
||||||
name="off",
|
name="off",
|
||||||
description="禁用指定插件",
|
description="禁用指定插件",
|
||||||
@@ -171,9 +170,9 @@ class PluginDelCommand(AbstractCommandNode):
|
|||||||
aliases=[],
|
aliases=[],
|
||||||
privilege=2
|
privilege=2
|
||||||
)
|
)
|
||||||
class PluginOnOffCommand(AbstractCommandNode):
|
class PluginOnOffCommand(aamgr.AbstractCommandNode):
|
||||||
@classmethod
|
@classmethod
|
||||||
def process(cls, ctx: Context) -> tuple[bool, list]:
|
def process(cls, ctx: aamgr.Context) -> tuple[bool, list]:
|
||||||
import pkg.plugin.switch as plugin_switch
|
import pkg.plugin.switch as plugin_switch
|
||||||
|
|
||||||
plugin_list = plugin_host.__plugins__
|
plugin_list = plugin_host.__plugins__
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
from ..aamgr import AbstractCommandNode, Context
|
from .. import aamgr
|
||||||
|
from ....utils import context
|
||||||
|
|
||||||
|
|
||||||
@AbstractCommandNode.register(
|
@aamgr.AbstractCommandNode.register(
|
||||||
parent=None,
|
parent=None,
|
||||||
name="default",
|
name="default",
|
||||||
description="操作情景预设",
|
description="操作情景预设",
|
||||||
@@ -9,19 +10,20 @@ from ..aamgr import AbstractCommandNode, Context
|
|||||||
aliases=[],
|
aliases=[],
|
||||||
privilege=1
|
privilege=1
|
||||||
)
|
)
|
||||||
class DefaultCommand(AbstractCommandNode):
|
class DefaultCommand(aamgr.AbstractCommandNode):
|
||||||
@classmethod
|
@classmethod
|
||||||
def process(cls, ctx: Context) -> tuple[bool, list]:
|
def process(cls, ctx: aamgr.Context) -> tuple[bool, list]:
|
||||||
import pkg.openai.session
|
import pkg.openai.session
|
||||||
session_name = ctx.session_name
|
session_name = ctx.session_name
|
||||||
params = ctx.params
|
params = ctx.params
|
||||||
reply = []
|
reply = []
|
||||||
import config
|
|
||||||
|
config = context.get_config_manager().data
|
||||||
|
|
||||||
if len(params) == 0:
|
if len(params) == 0:
|
||||||
# 输出目前所有情景预设
|
# 输出目前所有情景预设
|
||||||
import pkg.openai.dprompt as dprompt
|
import pkg.openai.dprompt as dprompt
|
||||||
reply_str = "[bot]当前所有情景预设({}模式):\n\n".format(config.preset_mode)
|
reply_str = "[bot]当前所有情景预设({}模式):\n\n".format(config['preset_mode'])
|
||||||
|
|
||||||
prompts = dprompt.mode_inst().list()
|
prompts = dprompt.mode_inst().list()
|
||||||
|
|
||||||
@@ -45,7 +47,7 @@ class DefaultCommand(AbstractCommandNode):
|
|||||||
return True, reply
|
return True, reply
|
||||||
|
|
||||||
|
|
||||||
@AbstractCommandNode.register(
|
@aamgr.AbstractCommandNode.register(
|
||||||
parent=DefaultCommand,
|
parent=DefaultCommand,
|
||||||
name="set",
|
name="set",
|
||||||
description="设置默认情景预设",
|
description="设置默认情景预设",
|
||||||
@@ -53,9 +55,9 @@ class DefaultCommand(AbstractCommandNode):
|
|||||||
aliases=[],
|
aliases=[],
|
||||||
privilege=2
|
privilege=2
|
||||||
)
|
)
|
||||||
class DefaultSetCommand(AbstractCommandNode):
|
class DefaultSetCommand(aamgr.AbstractCommandNode):
|
||||||
@classmethod
|
@classmethod
|
||||||
def process(cls, ctx: Context) -> tuple[bool, list]:
|
def process(cls, ctx: aamgr.Context) -> tuple[bool, list]:
|
||||||
reply = []
|
reply = []
|
||||||
|
|
||||||
if len(ctx.crt_params) == 0:
|
if len(ctx.crt_params) == 0:
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
from ..aamgr import AbstractCommandNode, Context
|
from .. import aamgr
|
||||||
import datetime
|
|
||||||
|
|
||||||
|
|
||||||
@AbstractCommandNode.register(
|
@aamgr.AbstractCommandNode.register(
|
||||||
parent=None,
|
parent=None,
|
||||||
name="del",
|
name="del",
|
||||||
description="删除当前会话的历史记录",
|
description="删除当前会话的历史记录",
|
||||||
@@ -10,9 +9,9 @@ import datetime
|
|||||||
aliases=[],
|
aliases=[],
|
||||||
privilege=1
|
privilege=1
|
||||||
)
|
)
|
||||||
class DelCommand(AbstractCommandNode):
|
class DelCommand(aamgr.AbstractCommandNode):
|
||||||
@classmethod
|
@classmethod
|
||||||
def process(cls, ctx: Context) -> tuple[bool, list]:
|
def process(cls, ctx: aamgr.Context) -> tuple[bool, list]:
|
||||||
import pkg.openai.session
|
import pkg.openai.session
|
||||||
session_name = ctx.session_name
|
session_name = ctx.session_name
|
||||||
params = ctx.params
|
params = ctx.params
|
||||||
@@ -33,7 +32,7 @@ class DelCommand(AbstractCommandNode):
|
|||||||
return True, reply
|
return True, reply
|
||||||
|
|
||||||
|
|
||||||
@AbstractCommandNode.register(
|
@aamgr.AbstractCommandNode.register(
|
||||||
parent=DelCommand,
|
parent=DelCommand,
|
||||||
name="all",
|
name="all",
|
||||||
description="删除当前会话的全部历史记录",
|
description="删除当前会话的全部历史记录",
|
||||||
@@ -41,9 +40,9 @@ class DelCommand(AbstractCommandNode):
|
|||||||
aliases=[],
|
aliases=[],
|
||||||
privilege=1
|
privilege=1
|
||||||
)
|
)
|
||||||
class DelAllCommand(AbstractCommandNode):
|
class DelAllCommand(aamgr.AbstractCommandNode):
|
||||||
@classmethod
|
@classmethod
|
||||||
def process(cls, ctx: Context) -> tuple[bool, list]:
|
def process(cls, ctx: aamgr.Context) -> tuple[bool, list]:
|
||||||
import pkg.openai.session
|
import pkg.openai.session
|
||||||
session_name = ctx.session_name
|
session_name = ctx.session_name
|
||||||
reply = []
|
reply = []
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from ..aamgr import AbstractCommandNode, Context
|
from .. import aamgr
|
||||||
|
|
||||||
|
|
||||||
@AbstractCommandNode.register(
|
@aamgr.AbstractCommandNode.register(
|
||||||
parent=None,
|
parent=None,
|
||||||
name="delhst",
|
name="delhst",
|
||||||
description="删除指定会话的所有历史记录",
|
description="删除指定会话的所有历史记录",
|
||||||
@@ -9,9 +9,9 @@ from ..aamgr import AbstractCommandNode, Context
|
|||||||
aliases=[],
|
aliases=[],
|
||||||
privilege=2
|
privilege=2
|
||||||
)
|
)
|
||||||
class DelHistoryCommand(AbstractCommandNode):
|
class DelHistoryCommand(aamgr.AbstractCommandNode):
|
||||||
@classmethod
|
@classmethod
|
||||||
def process(cls, ctx: Context) -> tuple[bool, list]:
|
def process(cls, ctx: aamgr.Context) -> tuple[bool, list]:
|
||||||
import pkg.openai.session
|
import pkg.openai.session
|
||||||
import pkg.utils.context
|
import pkg.utils.context
|
||||||
params = ctx.params
|
params = ctx.params
|
||||||
@@ -31,7 +31,7 @@ class DelHistoryCommand(AbstractCommandNode):
|
|||||||
return True, reply
|
return True, reply
|
||||||
|
|
||||||
|
|
||||||
@AbstractCommandNode.register(
|
@aamgr.AbstractCommandNode.register(
|
||||||
parent=DelHistoryCommand,
|
parent=DelHistoryCommand,
|
||||||
name="all",
|
name="all",
|
||||||
description="删除所有会话的全部历史记录",
|
description="删除所有会话的全部历史记录",
|
||||||
@@ -39,9 +39,9 @@ class DelHistoryCommand(AbstractCommandNode):
|
|||||||
aliases=[],
|
aliases=[],
|
||||||
privilege=2
|
privilege=2
|
||||||
)
|
)
|
||||||
class DelAllHistoryCommand(AbstractCommandNode):
|
class DelAllHistoryCommand(aamgr.AbstractCommandNode):
|
||||||
@classmethod
|
@classmethod
|
||||||
def process(cls, ctx: Context) -> tuple[bool, list]:
|
def process(cls, ctx: aamgr.Context) -> tuple[bool, list]:
|
||||||
import pkg.utils.context
|
import pkg.utils.context
|
||||||
reply = []
|
reply = []
|
||||||
pkg.utils.context.get_database_manager().delete_all_session_history()
|
pkg.utils.context.get_database_manager().delete_all_session_history()
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
from ..aamgr import AbstractCommandNode, Context
|
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
|
from .. import aamgr
|
||||||
|
|
||||||
@AbstractCommandNode.register(
|
|
||||||
|
@aamgr.AbstractCommandNode.register(
|
||||||
parent=None,
|
parent=None,
|
||||||
name="last",
|
name="last",
|
||||||
description="切换前一次对话",
|
description="切换前一次对话",
|
||||||
@@ -10,9 +11,9 @@ import datetime
|
|||||||
aliases=[],
|
aliases=[],
|
||||||
privilege=1
|
privilege=1
|
||||||
)
|
)
|
||||||
class LastCommand(AbstractCommandNode):
|
class LastCommand(aamgr.AbstractCommandNode):
|
||||||
@classmethod
|
@classmethod
|
||||||
def process(cls, ctx: Context) -> tuple[bool, list]:
|
def process(cls, ctx: aamgr.Context) -> tuple[bool, list]:
|
||||||
import pkg.openai.session
|
import pkg.openai.session
|
||||||
session_name = ctx.session_name
|
session_name = ctx.session_name
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
from ..aamgr import AbstractCommandNode, Context
|
|
||||||
import datetime
|
import datetime
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
from .. import aamgr
|
||||||
|
|
||||||
@AbstractCommandNode.register(
|
|
||||||
|
@aamgr.AbstractCommandNode.register(
|
||||||
parent=None,
|
parent=None,
|
||||||
name='list',
|
name='list',
|
||||||
description='列出当前会话的所有历史记录',
|
description='列出当前会话的所有历史记录',
|
||||||
@@ -11,9 +12,9 @@ import json
|
|||||||
aliases=[],
|
aliases=[],
|
||||||
privilege=1
|
privilege=1
|
||||||
)
|
)
|
||||||
class ListCommand(AbstractCommandNode):
|
class ListCommand(aamgr.AbstractCommandNode):
|
||||||
@classmethod
|
@classmethod
|
||||||
def process(cls, ctx: Context) -> tuple[bool, list]:
|
def process(cls, ctx: aamgr.Context) -> tuple[bool, list]:
|
||||||
import pkg.openai.session
|
import pkg.openai.session
|
||||||
session_name = ctx.session_name
|
session_name = ctx.session_name
|
||||||
params = ctx.params
|
params = ctx.params
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
from ..aamgr import AbstractCommandNode, Context
|
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
|
from .. import aamgr
|
||||||
|
|
||||||
@AbstractCommandNode.register(
|
|
||||||
|
@aamgr.AbstractCommandNode.register(
|
||||||
parent=None,
|
parent=None,
|
||||||
name="next",
|
name="next",
|
||||||
description="切换后一次对话",
|
description="切换后一次对话",
|
||||||
@@ -10,9 +11,9 @@ import datetime
|
|||||||
aliases=[],
|
aliases=[],
|
||||||
privilege=1
|
privilege=1
|
||||||
)
|
)
|
||||||
class NextCommand(AbstractCommandNode):
|
class NextCommand(aamgr.AbstractCommandNode):
|
||||||
@classmethod
|
@classmethod
|
||||||
def process(cls, ctx: Context) -> tuple[bool, list]:
|
def process(cls, ctx: aamgr.Context) -> tuple[bool, list]:
|
||||||
import pkg.openai.session
|
import pkg.openai.session
|
||||||
session_name = ctx.session_name
|
session_name = ctx.session_name
|
||||||
reply = []
|
reply = []
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
from ..aamgr import AbstractCommandNode, Context
|
from .. import aamgr
|
||||||
import datetime
|
|
||||||
|
|
||||||
|
|
||||||
@AbstractCommandNode.register(
|
@aamgr.AbstractCommandNode.register(
|
||||||
parent=None,
|
parent=None,
|
||||||
name="prompt",
|
name="prompt",
|
||||||
description="获取当前会话的前文",
|
description="获取当前会话的前文",
|
||||||
@@ -10,9 +9,9 @@ import datetime
|
|||||||
aliases=[],
|
aliases=[],
|
||||||
privilege=1
|
privilege=1
|
||||||
)
|
)
|
||||||
class PromptCommand(AbstractCommandNode):
|
class PromptCommand(aamgr.AbstractCommandNode):
|
||||||
@classmethod
|
@classmethod
|
||||||
def process(cls, ctx: Context) -> tuple[bool, list]:
|
def process(cls, ctx: aamgr.Context) -> tuple[bool, list]:
|
||||||
import pkg.openai.session
|
import pkg.openai.session
|
||||||
session_name = ctx.session_name
|
session_name = ctx.session_name
|
||||||
params = ctx.params
|
params = ctx.params
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
from ..aamgr import AbstractCommandNode, Context
|
from .. import aamgr
|
||||||
import datetime
|
|
||||||
|
|
||||||
|
|
||||||
@AbstractCommandNode.register(
|
@aamgr.AbstractCommandNode.register(
|
||||||
parent=None,
|
parent=None,
|
||||||
name="resend",
|
name="resend",
|
||||||
description="重新获取上一次问题的回复",
|
description="重新获取上一次问题的回复",
|
||||||
@@ -10,20 +9,22 @@ import datetime
|
|||||||
aliases=[],
|
aliases=[],
|
||||||
privilege=1
|
privilege=1
|
||||||
)
|
)
|
||||||
class ResendCommand(AbstractCommandNode):
|
class ResendCommand(aamgr.AbstractCommandNode):
|
||||||
@classmethod
|
@classmethod
|
||||||
def process(cls, ctx: Context) -> tuple[bool, list]:
|
def process(cls, ctx: aamgr.Context) -> tuple[bool, list]:
|
||||||
import pkg.openai.session
|
from ....openai import session as openai_session
|
||||||
|
from ....utils import context
|
||||||
|
from ....qqbot import message
|
||||||
import config
|
import config
|
||||||
session_name = ctx.session_name
|
session_name = ctx.session_name
|
||||||
reply = []
|
reply = []
|
||||||
|
|
||||||
session = pkg.openai.session.get_session(session_name)
|
session = openai_session.get_session(session_name)
|
||||||
to_send = session.undo()
|
to_send = session.undo()
|
||||||
|
|
||||||
mgr = pkg.utils.context.get_qqbot_manager()
|
mgr = context.get_qqbot_manager()
|
||||||
|
|
||||||
reply = pkg.qqbot.message.process_normal_message(to_send, mgr, config,
|
reply = message.process_normal_message(to_send, mgr, config,
|
||||||
ctx.launcher_type, ctx.launcher_id,
|
ctx.launcher_type, ctx.launcher_id,
|
||||||
ctx.sender_id)
|
ctx.sender_id)
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
from ..aamgr import AbstractCommandNode, Context
|
|
||||||
import tips as tips_custom
|
import tips as tips_custom
|
||||||
|
|
||||||
import pkg.openai.session
|
from .. import aamgr
|
||||||
import pkg.utils.context
|
from ....openai import session
|
||||||
|
from ....utils import context
|
||||||
|
|
||||||
|
|
||||||
@AbstractCommandNode.register(
|
@aamgr.AbstractCommandNode.register(
|
||||||
parent=None,
|
parent=None,
|
||||||
name='reset',
|
name='reset',
|
||||||
description='重置当前会话',
|
description='重置当前会话',
|
||||||
@@ -13,21 +13,21 @@ import pkg.utils.context
|
|||||||
aliases=[],
|
aliases=[],
|
||||||
privilege=1
|
privilege=1
|
||||||
)
|
)
|
||||||
class ResetCommand(AbstractCommandNode):
|
class ResetCommand(aamgr.AbstractCommandNode):
|
||||||
@classmethod
|
@classmethod
|
||||||
def process(cls, ctx: Context) -> tuple[bool, list]:
|
def process(cls, ctx: aamgr.Context) -> tuple[bool, list]:
|
||||||
params = ctx.params
|
params = ctx.params
|
||||||
session_name = ctx.session_name
|
session_name = ctx.session_name
|
||||||
|
|
||||||
reply = ""
|
reply = ""
|
||||||
|
|
||||||
if len(params) == 0:
|
if len(params) == 0:
|
||||||
pkg.openai.session.get_session(session_name).reset(explicit=True)
|
session.get_session(session_name).reset(explicit=True)
|
||||||
reply = [tips_custom.command_reset_message]
|
reply = [tips_custom.command_reset_message]
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
import pkg.openai.dprompt as dprompt
|
import pkg.openai.dprompt as dprompt
|
||||||
pkg.openai.session.get_session(session_name).reset(explicit=True, use_prompt=params[0])
|
session.get_session(session_name).reset(explicit=True, use_prompt=params[0])
|
||||||
reply = [tips_custom.command_reset_name_message+"{}".format(dprompt.mode_inst().get_full_name(params[0]))]
|
reply = [tips_custom.command_reset_name_message+"{}".format(dprompt.mode_inst().get_full_name(params[0]))]
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
reply = ["[bot]会话重置失败:{}".format(e)]
|
reply = ["[bot]会话重置失败:{}".format(e)]
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from ..aamgr import AbstractCommandNode, Context
|
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
from .. import aamgr
|
||||||
|
|
||||||
|
|
||||||
def config_operation(cmd, params):
|
def config_operation(cmd, params):
|
||||||
reply = []
|
reply = []
|
||||||
@@ -85,7 +86,7 @@ def config_operation(cmd, params):
|
|||||||
return reply
|
return reply
|
||||||
|
|
||||||
|
|
||||||
@AbstractCommandNode.register(
|
@aamgr.AbstractCommandNode.register(
|
||||||
parent=None,
|
parent=None,
|
||||||
name="cfg",
|
name="cfg",
|
||||||
description="配置项管理",
|
description="配置项管理",
|
||||||
@@ -93,8 +94,8 @@ def config_operation(cmd, params):
|
|||||||
aliases=[],
|
aliases=[],
|
||||||
privilege=2
|
privilege=2
|
||||||
)
|
)
|
||||||
class CfgCommand(AbstractCommandNode):
|
class CfgCommand(aamgr.AbstractCommandNode):
|
||||||
@classmethod
|
@classmethod
|
||||||
def process(cls, ctx: Context) -> tuple[bool, list]:
|
def process(cls, ctx: aamgr.Context) -> tuple[bool, list]:
|
||||||
return True, config_operation(ctx.command, ctx.params)
|
return True, config_operation(ctx.command, ctx.params)
|
||||||
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
from ..aamgr import AbstractCommandNode, Context, __command_list__
|
from .. import aamgr
|
||||||
|
|
||||||
|
|
||||||
@AbstractCommandNode.register(
|
@aamgr.AbstractCommandNode.register(
|
||||||
parent=None,
|
parent=None,
|
||||||
name="cmd",
|
name="cmd",
|
||||||
description="显示指令列表",
|
description="显示指令列表",
|
||||||
@@ -9,10 +9,10 @@ from ..aamgr import AbstractCommandNode, Context, __command_list__
|
|||||||
aliases=[],
|
aliases=[],
|
||||||
privilege=1
|
privilege=1
|
||||||
)
|
)
|
||||||
class CmdCommand(AbstractCommandNode):
|
class CmdCommand(aamgr.AbstractCommandNode):
|
||||||
@classmethod
|
@classmethod
|
||||||
def process(cls, ctx: Context) -> tuple[bool, list]:
|
def process(cls, ctx: aamgr.Context) -> tuple[bool, list]:
|
||||||
command_list = __command_list__
|
command_list = aamgr.__command_list__
|
||||||
|
|
||||||
reply = []
|
reply = []
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from ..aamgr import AbstractCommandNode, Context
|
from .. import aamgr
|
||||||
|
|
||||||
|
|
||||||
@AbstractCommandNode.register(
|
@aamgr.AbstractCommandNode.register(
|
||||||
parent=None,
|
parent=None,
|
||||||
name="help",
|
name="help",
|
||||||
description="显示自定义的帮助信息",
|
description="显示自定义的帮助信息",
|
||||||
@@ -9,9 +9,9 @@ from ..aamgr import AbstractCommandNode, Context
|
|||||||
aliases=[],
|
aliases=[],
|
||||||
privilege=1
|
privilege=1
|
||||||
)
|
)
|
||||||
class HelpCommand(AbstractCommandNode):
|
class HelpCommand(aamgr.AbstractCommandNode):
|
||||||
@classmethod
|
@classmethod
|
||||||
def process(cls, ctx: Context) -> tuple[bool, list]:
|
def process(cls, ctx: aamgr.Context) -> tuple[bool, list]:
|
||||||
import tips
|
import tips
|
||||||
reply = ["[bot] "+tips.help_message + "\n请输入 !cmd 查看指令列表"]
|
reply = ["[bot] "+tips.help_message + "\n请输入 !cmd 查看指令列表"]
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
from ..aamgr import AbstractCommandNode, Context
|
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
@AbstractCommandNode.register(
|
from .. import aamgr
|
||||||
|
|
||||||
|
|
||||||
|
@aamgr.AbstractCommandNode.register(
|
||||||
parent=None,
|
parent=None,
|
||||||
name="reload",
|
name="reload",
|
||||||
description="执行热重载",
|
description="执行热重载",
|
||||||
@@ -9,9 +11,9 @@ import threading
|
|||||||
aliases=[],
|
aliases=[],
|
||||||
privilege=2
|
privilege=2
|
||||||
)
|
)
|
||||||
class ReloadCommand(AbstractCommandNode):
|
class ReloadCommand(aamgr.AbstractCommandNode):
|
||||||
@classmethod
|
@classmethod
|
||||||
def process(cls, ctx: Context) -> tuple[bool, list]:
|
def process(cls, ctx: aamgr.Context) -> tuple[bool, list]:
|
||||||
reply = []
|
reply = []
|
||||||
|
|
||||||
import pkg.utils.reloader
|
import pkg.utils.reloader
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
from ..aamgr import AbstractCommandNode, Context
|
|
||||||
import threading
|
import threading
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
|
from .. import aamgr
|
||||||
|
|
||||||
@AbstractCommandNode.register(
|
|
||||||
|
@aamgr.AbstractCommandNode.register(
|
||||||
parent=None,
|
parent=None,
|
||||||
name="update",
|
name="update",
|
||||||
description="更新程序",
|
description="更新程序",
|
||||||
@@ -11,9 +12,9 @@ import traceback
|
|||||||
aliases=[],
|
aliases=[],
|
||||||
privilege=2
|
privilege=2
|
||||||
)
|
)
|
||||||
class UpdateCommand(AbstractCommandNode):
|
class UpdateCommand(aamgr.AbstractCommandNode):
|
||||||
@classmethod
|
@classmethod
|
||||||
def process(cls, ctx: Context) -> tuple[bool, list]:
|
def process(cls, ctx: aamgr.Context) -> tuple[bool, list]:
|
||||||
reply = []
|
reply = []
|
||||||
import pkg.utils.updater
|
import pkg.utils.updater
|
||||||
import pkg.utils.reloader
|
import pkg.utils.reloader
|
||||||
@@ -22,8 +23,7 @@ class UpdateCommand(AbstractCommandNode):
|
|||||||
def update_task():
|
def update_task():
|
||||||
try:
|
try:
|
||||||
if pkg.utils.updater.update_all():
|
if pkg.utils.updater.update_all():
|
||||||
pkg.utils.reloader.reload_all(notify=False)
|
pkg.utils.context.get_qqbot_manager().notify_admin("更新完成, 请手动重启程序。")
|
||||||
pkg.utils.context.get_qqbot_manager().notify_admin("更新完成")
|
|
||||||
else:
|
else:
|
||||||
pkg.utils.context.get_qqbot_manager().notify_admin("无新版本")
|
pkg.utils.context.get_qqbot_manager().notify_admin("无新版本")
|
||||||
except Exception as e0:
|
except Exception as e0:
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
from ..aamgr import AbstractCommandNode, Context
|
from .. import aamgr
|
||||||
import logging
|
|
||||||
|
|
||||||
|
|
||||||
@AbstractCommandNode.register(
|
@aamgr.AbstractCommandNode.register(
|
||||||
parent=None,
|
parent=None,
|
||||||
name="usage",
|
name="usage",
|
||||||
description="获取使用情况",
|
description="获取使用情况",
|
||||||
@@ -10,9 +9,9 @@ import logging
|
|||||||
aliases=[],
|
aliases=[],
|
||||||
privilege=1
|
privilege=1
|
||||||
)
|
)
|
||||||
class UsageCommand(AbstractCommandNode):
|
class UsageCommand(aamgr.AbstractCommandNode):
|
||||||
@classmethod
|
@classmethod
|
||||||
def process(cls, ctx: Context) -> tuple[bool, list]:
|
def process(cls, ctx: aamgr.Context) -> tuple[bool, list]:
|
||||||
import config
|
import config
|
||||||
import pkg.utils.credit as credit
|
import pkg.utils.credit as credit
|
||||||
import pkg.utils.context
|
import pkg.utils.context
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from ..aamgr import AbstractCommandNode, Context
|
from .. import aamgr
|
||||||
|
|
||||||
|
|
||||||
@AbstractCommandNode.register(
|
@aamgr.AbstractCommandNode.register(
|
||||||
parent=None,
|
parent=None,
|
||||||
name="version",
|
name="version",
|
||||||
description="查看版本信息",
|
description="查看版本信息",
|
||||||
@@ -9,9 +9,9 @@ from ..aamgr import AbstractCommandNode, Context
|
|||||||
aliases=[],
|
aliases=[],
|
||||||
privilege=1
|
privilege=1
|
||||||
)
|
)
|
||||||
class VersionCommand(AbstractCommandNode):
|
class VersionCommand(aamgr.AbstractCommandNode):
|
||||||
@classmethod
|
@classmethod
|
||||||
def process(cls, ctx: Context) -> tuple[bool, list]:
|
def process(cls, ctx: aamgr.Context) -> tuple[bool, list]:
|
||||||
reply = []
|
reply = []
|
||||||
import pkg.utils.updater
|
import pkg.utils.updater
|
||||||
|
|
||||||
|
|||||||
@@ -1,26 +1,10 @@
|
|||||||
# 指令处理模块
|
# 指令处理模块
|
||||||
import logging
|
import logging
|
||||||
import json
|
|
||||||
import datetime
|
|
||||||
import os
|
|
||||||
import threading
|
|
||||||
import traceback
|
|
||||||
|
|
||||||
import pkg.openai.session
|
from ..qqbot.cmds import aamgr as cmdmgr
|
||||||
import pkg.openai.manager
|
|
||||||
import pkg.utils.reloader
|
|
||||||
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.aamgr as cmdmgr
|
|
||||||
|
|
||||||
from mirai import Image
|
|
||||||
|
|
||||||
|
|
||||||
|
def process_command(session_name: str, text_message: str, mgr, config: dict,
|
||||||
def process_command(session_name: str, text_message: str, mgr, config,
|
|
||||||
launcher_type: str, launcher_id: int, sender_id: int, is_admin: bool) -> list:
|
launcher_type: str, launcher_id: int, sender_id: int, is_admin: bool) -> list:
|
||||||
reply = []
|
reply = []
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import requests
|
|||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from ..utils import context
|
||||||
|
|
||||||
|
|
||||||
class ReplyFilter:
|
class ReplyFilter:
|
||||||
sensitive_words = []
|
sensitive_words = []
|
||||||
@@ -20,12 +22,13 @@ class ReplyFilter:
|
|||||||
self.sensitive_words = sensitive_words
|
self.sensitive_words = sensitive_words
|
||||||
self.mask = mask
|
self.mask = mask
|
||||||
self.mask_word = mask_word
|
self.mask_word = mask_word
|
||||||
import config
|
|
||||||
|
|
||||||
self.baidu_check = config.baidu_check
|
config = context.get_config_manager().data
|
||||||
self.baidu_api_key = config.baidu_api_key
|
|
||||||
self.baidu_secret_key = config.baidu_secret_key
|
self.baidu_check = config['baidu_check']
|
||||||
self.inappropriate_message_tips = config.inappropriate_message_tips
|
self.baidu_api_key = config['baidu_api_key']
|
||||||
|
self.baidu_secret_key = config['baidu_secret_key']
|
||||||
|
self.inappropriate_message_tips = config['inappropriate_message_tips']
|
||||||
|
|
||||||
def is_illegal(self, message: str) -> bool:
|
def is_illegal(self, message: str) -> bool:
|
||||||
processed = self.process(message)
|
processed = self.process(message)
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
|
from ..utils import context
|
||||||
|
|
||||||
|
|
||||||
def ignore(msg: str) -> bool:
|
def ignore(msg: str) -> bool:
|
||||||
"""检查消息是否应该被忽略"""
|
"""检查消息是否应该被忽略"""
|
||||||
import config
|
config = context.get_config_manager().data
|
||||||
|
|
||||||
if 'prefix' in config.ignore_rules:
|
if 'prefix' in config['ignore_rules']:
|
||||||
for rule in config.ignore_rules['prefix']:
|
for rule in config['ignore_rules']['prefix']:
|
||||||
if msg.startswith(rule):
|
if msg.startswith(rule):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if 'regexp' in config.ignore_rules:
|
if 'regexp' in config['ignore_rules']:
|
||||||
for rule in config.ignore_rules['regexp']:
|
for rule in config['ignore_rules']['regexp']:
|
||||||
if re.search(rule, msg):
|
if re.search(rule, msg):
|
||||||
return True
|
return True
|
||||||
|
|||||||
@@ -1,41 +1,34 @@
|
|||||||
import asyncio
|
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import threading
|
|
||||||
|
|
||||||
|
|
||||||
from mirai import At, GroupMessage, MessageEvent, Mirai, StrangerMessage, WebSocketAdapter, HTTPAdapter, \
|
|
||||||
FriendMessage, Image, MessageChain, Plain
|
|
||||||
from func_timeout import func_set_timeout
|
|
||||||
|
|
||||||
import pkg.openai.session
|
|
||||||
import pkg.openai.manager
|
|
||||||
from func_timeout import FunctionTimedOut
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import pkg.qqbot.filter
|
from mirai import At, GroupMessage, MessageEvent, StrangerMessage, \
|
||||||
import pkg.qqbot.process as processor
|
FriendMessage, Image, MessageChain, Plain
|
||||||
import pkg.utils.context
|
import func_timeout
|
||||||
|
|
||||||
import pkg.plugin.host as plugin_host
|
from ..openai import session as openai_session
|
||||||
import pkg.plugin.models as plugin_models
|
|
||||||
|
from ..qqbot import filter as qqbot_filter
|
||||||
|
from ..qqbot import process as processor
|
||||||
|
from ..utils import context
|
||||||
|
from ..plugin import host as plugin_host
|
||||||
|
from ..plugin import models as plugin_models
|
||||||
import tips as tips_custom
|
import tips as tips_custom
|
||||||
|
from ..qqbot import adapter as msadapter
|
||||||
import pkg.qqbot.adapter as msadapter
|
|
||||||
|
|
||||||
|
|
||||||
# 检查消息是否符合泛响应匹配机制
|
# 检查消息是否符合泛响应匹配机制
|
||||||
def check_response_rule(group_id:int, text: str):
|
def check_response_rule(group_id:int, text: str):
|
||||||
config = pkg.utils.context.get_config()
|
config = context.get_config_manager().data
|
||||||
|
|
||||||
rules = config.response_rules
|
rules = config['response_rules']
|
||||||
|
|
||||||
# 检查是否有特定规则
|
# 检查是否有特定规则
|
||||||
if 'prefix' not in config.response_rules:
|
if 'prefix' not in config['response_rules']:
|
||||||
if str(group_id) in config.response_rules:
|
if str(group_id) in config['response_rules']:
|
||||||
rules = config.response_rules[str(group_id)]
|
rules = config['response_rules'][str(group_id)]
|
||||||
else:
|
else:
|
||||||
rules = config.response_rules['default']
|
rules = config['response_rules']['default']
|
||||||
|
|
||||||
# 检查前缀匹配
|
# 检查前缀匹配
|
||||||
if 'prefix' in rules:
|
if 'prefix' in rules:
|
||||||
@@ -55,16 +48,16 @@ def check_response_rule(group_id:int, text: str):
|
|||||||
|
|
||||||
|
|
||||||
def response_at(group_id: int):
|
def response_at(group_id: int):
|
||||||
config = pkg.utils.context.get_config()
|
config = context.get_config_manager().data
|
||||||
|
|
||||||
use_response_rule = config.response_rules
|
use_response_rule = config['response_rules']
|
||||||
|
|
||||||
# 检查是否有特定规则
|
# 检查是否有特定规则
|
||||||
if 'prefix' not in config.response_rules:
|
if 'prefix' not in config['response_rules']:
|
||||||
if str(group_id) in config.response_rules:
|
if str(group_id) in config['response_rules']:
|
||||||
use_response_rule = config.response_rules[str(group_id)]
|
use_response_rule = config['response_rules'][str(group_id)]
|
||||||
else:
|
else:
|
||||||
use_response_rule = config.response_rules['default']
|
use_response_rule = config['response_rules']['default']
|
||||||
|
|
||||||
if 'at' not in use_response_rule:
|
if 'at' not in use_response_rule:
|
||||||
return True
|
return True
|
||||||
@@ -73,16 +66,16 @@ def response_at(group_id: int):
|
|||||||
|
|
||||||
|
|
||||||
def random_responding(group_id):
|
def random_responding(group_id):
|
||||||
config = pkg.utils.context.get_config()
|
config = context.get_config_manager().data
|
||||||
|
|
||||||
use_response_rule = config.response_rules
|
use_response_rule = config['response_rules']
|
||||||
|
|
||||||
# 检查是否有特定规则
|
# 检查是否有特定规则
|
||||||
if 'prefix' not in config.response_rules:
|
if 'prefix' not in config['response_rules']:
|
||||||
if str(group_id) in config.response_rules:
|
if str(group_id) in config['response_rules']:
|
||||||
use_response_rule = config.response_rules[str(group_id)]
|
use_response_rule = config['response_rules'][str(group_id)]
|
||||||
else:
|
else:
|
||||||
use_response_rule = config.response_rules['default']
|
use_response_rule = config['response_rules']['default']
|
||||||
|
|
||||||
if 'random_rate' in use_response_rule:
|
if 'random_rate' in use_response_rule:
|
||||||
import random
|
import random
|
||||||
@@ -109,31 +102,31 @@ class QQBotManager:
|
|||||||
ban_group = []
|
ban_group = []
|
||||||
|
|
||||||
def __init__(self, first_time_init=True):
|
def __init__(self, first_time_init=True):
|
||||||
import config
|
config = context.get_config_manager().data
|
||||||
|
|
||||||
self.timeout = config.process_message_timeout
|
self.timeout = config['process_message_timeout']
|
||||||
self.retry = config.retry_times
|
self.retry = config['retry_times']
|
||||||
|
|
||||||
# 由于YiriMirai的bot对象是单例的,且shutdown方法暂时无法使用
|
# 由于YiriMirai的bot对象是单例的,且shutdown方法暂时无法使用
|
||||||
# 故只在第一次初始化时创建bot对象,重载之后使用原bot对象
|
# 故只在第一次初始化时创建bot对象,重载之后使用原bot对象
|
||||||
# 因此,bot的配置不支持热重载
|
# 因此,bot的配置不支持热重载
|
||||||
if first_time_init:
|
if first_time_init:
|
||||||
logging.debug("Use adapter:" + config.msg_source_adapter)
|
logging.debug("Use adapter:" + config['msg_source_adapter'])
|
||||||
if config.msg_source_adapter == 'yirimirai':
|
if config['msg_source_adapter'] == 'yirimirai':
|
||||||
from pkg.qqbot.sources.yirimirai import YiriMiraiAdapter
|
from pkg.qqbot.sources.yirimirai import YiriMiraiAdapter
|
||||||
|
|
||||||
mirai_http_api_config = config.mirai_http_api_config
|
mirai_http_api_config = config['mirai_http_api_config']
|
||||||
self.bot_account_id = config.mirai_http_api_config['qq']
|
self.bot_account_id = config['mirai_http_api_config']['qq']
|
||||||
self.adapter = YiriMiraiAdapter(mirai_http_api_config)
|
self.adapter = YiriMiraiAdapter(mirai_http_api_config)
|
||||||
elif config.msg_source_adapter == 'nakuru':
|
elif config['msg_source_adapter'] == 'nakuru':
|
||||||
from pkg.qqbot.sources.nakuru import NakuruProjectAdapter
|
from pkg.qqbot.sources.nakuru import NakuruProjectAdapter
|
||||||
self.adapter = NakuruProjectAdapter(config.nakuru_config)
|
self.adapter = NakuruProjectAdapter(config['nakuru_config'])
|
||||||
self.bot_account_id = self.adapter.bot_account_id
|
self.bot_account_id = self.adapter.bot_account_id
|
||||||
else:
|
else:
|
||||||
self.adapter = pkg.utils.context.get_qqbot_manager().adapter
|
self.adapter = context.get_qqbot_manager().adapter
|
||||||
self.bot_account_id = pkg.utils.context.get_qqbot_manager().bot_account_id
|
self.bot_account_id = context.get_qqbot_manager().bot_account_id
|
||||||
|
|
||||||
pkg.utils.context.set_qqbot_manager(self)
|
context.set_qqbot_manager(self)
|
||||||
|
|
||||||
# 注册诸事件
|
# 注册诸事件
|
||||||
# Caution: 注册新的事件处理器之后,请务必在unsubscribe_all中编写相应的取消订阅代码
|
# Caution: 注册新的事件处理器之后,请务必在unsubscribe_all中编写相应的取消订阅代码
|
||||||
@@ -154,7 +147,7 @@ class QQBotManager:
|
|||||||
|
|
||||||
self.on_person_message(event)
|
self.on_person_message(event)
|
||||||
|
|
||||||
pkg.utils.context.get_thread_ctl().submit_user_task(
|
context.get_thread_ctl().submit_user_task(
|
||||||
friend_message_handler,
|
friend_message_handler,
|
||||||
)
|
)
|
||||||
self.adapter.register_listener(
|
self.adapter.register_listener(
|
||||||
@@ -179,11 +172,11 @@ class QQBotManager:
|
|||||||
|
|
||||||
self.on_person_message(event)
|
self.on_person_message(event)
|
||||||
|
|
||||||
pkg.utils.context.get_thread_ctl().submit_user_task(
|
context.get_thread_ctl().submit_user_task(
|
||||||
stranger_message_handler,
|
stranger_message_handler,
|
||||||
)
|
)
|
||||||
# nakuru不区分好友和陌生人,故仅为yirimirai注册陌生人事件
|
# nakuru不区分好友和陌生人,故仅为yirimirai注册陌生人事件
|
||||||
if config.msg_source_adapter == 'yirimirai':
|
if config['msg_source_adapter'] == 'yirimirai':
|
||||||
self.adapter.register_listener(
|
self.adapter.register_listener(
|
||||||
StrangerMessage,
|
StrangerMessage,
|
||||||
on_stranger_message
|
on_stranger_message
|
||||||
@@ -206,7 +199,7 @@ class QQBotManager:
|
|||||||
|
|
||||||
self.on_group_message(event)
|
self.on_group_message(event)
|
||||||
|
|
||||||
pkg.utils.context.get_thread_ctl().submit_user_task(
|
context.get_thread_ctl().submit_user_task(
|
||||||
group_message_handler,
|
group_message_handler,
|
||||||
event
|
event
|
||||||
)
|
)
|
||||||
@@ -220,12 +213,11 @@ class QQBotManager:
|
|||||||
|
|
||||||
用于在热重载流程中卸载所有事件处理器
|
用于在热重载流程中卸载所有事件处理器
|
||||||
"""
|
"""
|
||||||
import config
|
|
||||||
self.adapter.unregister_listener(
|
self.adapter.unregister_listener(
|
||||||
FriendMessage,
|
FriendMessage,
|
||||||
on_friend_message
|
on_friend_message
|
||||||
)
|
)
|
||||||
if config.msg_source_adapter == 'yirimirai':
|
if config['msg_source_adapter'] == 'yirimirai':
|
||||||
self.adapter.unregister_listener(
|
self.adapter.unregister_listener(
|
||||||
StrangerMessage,
|
StrangerMessage,
|
||||||
on_stranger_message
|
on_stranger_message
|
||||||
@@ -250,31 +242,31 @@ class QQBotManager:
|
|||||||
if hasattr(banlist, "enable_group"):
|
if hasattr(banlist, "enable_group"):
|
||||||
self.enable_group = banlist.enable_group
|
self.enable_group = banlist.enable_group
|
||||||
|
|
||||||
config = pkg.utils.context.get_config()
|
config = context.get_config_manager().data
|
||||||
if os.path.exists("sensitive.json") \
|
if os.path.exists("sensitive.json") \
|
||||||
and config.sensitive_word_filter is not None \
|
and config['sensitive_word_filter'] is not None \
|
||||||
and config.sensitive_word_filter:
|
and config['sensitive_word_filter']:
|
||||||
with open("sensitive.json", "r", encoding="utf-8") as f:
|
with open("sensitive.json", "r", encoding="utf-8") as f:
|
||||||
sensitive_json = json.load(f)
|
sensitive_json = json.load(f)
|
||||||
self.reply_filter = pkg.qqbot.filter.ReplyFilter(
|
self.reply_filter = qqbot_filter.ReplyFilter(
|
||||||
sensitive_words=sensitive_json['words'],
|
sensitive_words=sensitive_json['words'],
|
||||||
mask=sensitive_json['mask'] if 'mask' in sensitive_json else '*',
|
mask=sensitive_json['mask'] if 'mask' in sensitive_json else '*',
|
||||||
mask_word=sensitive_json['mask_word'] if 'mask_word' in sensitive_json else ''
|
mask_word=sensitive_json['mask_word'] if 'mask_word' in sensitive_json else ''
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.reply_filter = pkg.qqbot.filter.ReplyFilter([])
|
self.reply_filter = qqbot_filter.ReplyFilter([])
|
||||||
|
|
||||||
def send(self, event, msg, check_quote=True, check_at_sender=True):
|
def send(self, event, msg, check_quote=True, check_at_sender=True):
|
||||||
config = pkg.utils.context.get_config()
|
config = context.get_config_manager().data
|
||||||
|
|
||||||
if check_at_sender and config.at_sender:
|
if check_at_sender and config['at_sender']:
|
||||||
msg.insert(
|
msg.insert(
|
||||||
0,
|
0,
|
||||||
Plain(" \n")
|
Plain(" \n")
|
||||||
)
|
)
|
||||||
|
|
||||||
# 当回复的正文中包含换行时,quote可能会自带at,此时就不再单独添加at,只添加换行
|
# 当回复的正文中包含换行时,quote可能会自带at,此时就不再单独添加at,只添加换行
|
||||||
if "\n" not in str(msg[1]) or config.msg_source_adapter == 'nakuru':
|
if "\n" not in str(msg[1]) or config['msg_source_adapter'] == 'nakuru':
|
||||||
msg.insert(
|
msg.insert(
|
||||||
0,
|
0,
|
||||||
At(
|
At(
|
||||||
@@ -285,14 +277,15 @@ class QQBotManager:
|
|||||||
self.adapter.reply_message(
|
self.adapter.reply_message(
|
||||||
event,
|
event,
|
||||||
msg,
|
msg,
|
||||||
quote_origin=True if config.quote_origin and check_quote else False
|
quote_origin=True if config['quote_origin'] and check_quote else False
|
||||||
)
|
)
|
||||||
|
|
||||||
# 私聊消息处理
|
# 私聊消息处理
|
||||||
def on_person_message(self, event: MessageEvent):
|
def on_person_message(self, event: MessageEvent):
|
||||||
import config
|
|
||||||
reply = ''
|
reply = ''
|
||||||
|
|
||||||
|
config = context.get_config_manager().data
|
||||||
|
|
||||||
if not self.enable_private:
|
if not self.enable_private:
|
||||||
logging.debug("已在banlist.py中禁用所有私聊")
|
logging.debug("已在banlist.py中禁用所有私聊")
|
||||||
elif event.sender.id == self.bot_account_id:
|
elif event.sender.id == self.bot_account_id:
|
||||||
@@ -306,7 +299,7 @@ class QQBotManager:
|
|||||||
for i in range(self.retry):
|
for i in range(self.retry):
|
||||||
try:
|
try:
|
||||||
|
|
||||||
@func_set_timeout(config.process_message_timeout)
|
@func_timeout.func_set_timeout(config['process_message_timeout'])
|
||||||
def time_ctrl_wrapper():
|
def time_ctrl_wrapper():
|
||||||
reply = processor.process_message('person', event.sender.id, str(event.message_chain),
|
reply = processor.process_message('person', event.sender.id, str(event.message_chain),
|
||||||
event.message_chain,
|
event.message_chain,
|
||||||
@@ -315,16 +308,16 @@ class QQBotManager:
|
|||||||
|
|
||||||
reply = time_ctrl_wrapper()
|
reply = time_ctrl_wrapper()
|
||||||
break
|
break
|
||||||
except FunctionTimedOut:
|
except func_timeout.FunctionTimedOut:
|
||||||
logging.warning("person_{}: 超时,重试中({})".format(event.sender.id, i))
|
logging.warning("person_{}: 超时,重试中({})".format(event.sender.id, i))
|
||||||
pkg.openai.session.get_session('person_{}'.format(event.sender.id)).release_response_lock()
|
openai_session.get_session('person_{}'.format(event.sender.id)).release_response_lock()
|
||||||
if "person_{}".format(event.sender.id) in pkg.qqbot.process.processing:
|
if "person_{}".format(event.sender.id) in processor.processing:
|
||||||
pkg.qqbot.process.processing.remove('person_{}'.format(event.sender.id))
|
processor.processing.remove('person_{}'.format(event.sender.id))
|
||||||
failed += 1
|
failed += 1
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if failed == self.retry:
|
if failed == self.retry:
|
||||||
pkg.openai.session.get_session('person_{}'.format(event.sender.id)).release_response_lock()
|
openai_session.get_session('person_{}'.format(event.sender.id)).release_response_lock()
|
||||||
self.notify_admin("{} 请求超时".format("person_{}".format(event.sender.id)))
|
self.notify_admin("{} 请求超时".format("person_{}".format(event.sender.id)))
|
||||||
reply = [tips_custom.reply_message]
|
reply = [tips_custom.reply_message]
|
||||||
|
|
||||||
@@ -333,8 +326,10 @@ class QQBotManager:
|
|||||||
|
|
||||||
# 群消息处理
|
# 群消息处理
|
||||||
def on_group_message(self, event: GroupMessage):
|
def on_group_message(self, event: GroupMessage):
|
||||||
import config
|
|
||||||
reply = ''
|
reply = ''
|
||||||
|
|
||||||
|
config = context.get_config_manager().data
|
||||||
|
|
||||||
def process(text=None) -> str:
|
def process(text=None) -> str:
|
||||||
replys = ""
|
replys = ""
|
||||||
if At(self.bot_account_id) in event.message_chain:
|
if At(self.bot_account_id) in event.message_chain:
|
||||||
@@ -344,7 +339,7 @@ class QQBotManager:
|
|||||||
failed = 0
|
failed = 0
|
||||||
for i in range(self.retry):
|
for i in range(self.retry):
|
||||||
try:
|
try:
|
||||||
@func_set_timeout(config.process_message_timeout)
|
@func_timeout.func_set_timeout(config['process_message_timeout'])
|
||||||
def time_ctrl_wrapper():
|
def time_ctrl_wrapper():
|
||||||
replys = processor.process_message('group', event.group.id,
|
replys = processor.process_message('group', event.group.id,
|
||||||
str(event.message_chain).strip() if text is None else text,
|
str(event.message_chain).strip() if text is None else text,
|
||||||
@@ -354,16 +349,16 @@ class QQBotManager:
|
|||||||
|
|
||||||
replys = time_ctrl_wrapper()
|
replys = time_ctrl_wrapper()
|
||||||
break
|
break
|
||||||
except FunctionTimedOut:
|
except func_timeout.FunctionTimedOut:
|
||||||
logging.warning("group_{}: 超时,重试中({})".format(event.group.id, i))
|
logging.warning("group_{}: 超时,重试中({})".format(event.group.id, i))
|
||||||
pkg.openai.session.get_session('group_{}'.format(event.group.id)).release_response_lock()
|
openai_session.get_session('group_{}'.format(event.group.id)).release_response_lock()
|
||||||
if "group_{}".format(event.group.id) in pkg.qqbot.process.processing:
|
if "group_{}".format(event.group.id) in processor.processing:
|
||||||
pkg.qqbot.process.processing.remove('group_{}'.format(event.group.id))
|
processor.processing.remove('group_{}'.format(event.group.id))
|
||||||
failed += 1
|
failed += 1
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if failed == self.retry:
|
if failed == self.retry:
|
||||||
pkg.openai.session.get_session('group_{}'.format(event.group.id)).release_response_lock()
|
openai_session.get_session('group_{}'.format(event.group.id)).release_response_lock()
|
||||||
self.notify_admin("{} 请求超时".format("group_{}".format(event.group.id)))
|
self.notify_admin("{} 请求超时".format("group_{}".format(event.group.id)))
|
||||||
replys = [tips_custom.replys_message]
|
replys = [tips_custom.replys_message]
|
||||||
|
|
||||||
@@ -392,17 +387,17 @@ class QQBotManager:
|
|||||||
|
|
||||||
# 通知系统管理员
|
# 通知系统管理员
|
||||||
def notify_admin(self, message: str):
|
def notify_admin(self, message: str):
|
||||||
config = pkg.utils.context.get_config()
|
config = context.get_config_manager().data
|
||||||
if config.admin_qq != 0 and config.admin_qq != []:
|
if config['admin_qq'] != 0 and config['admin_qq'] != []:
|
||||||
logging.info("通知管理员:{}".format(message))
|
logging.info("通知管理员:{}".format(message))
|
||||||
if type(config.admin_qq) == int:
|
if type(config['admin_qq']) == int:
|
||||||
self.adapter.send_message(
|
self.adapter.send_message(
|
||||||
"person",
|
"person",
|
||||||
config.admin_qq,
|
config['admin_qq'],
|
||||||
MessageChain([Plain("[bot]{}".format(message))])
|
MessageChain([Plain("[bot]{}".format(message))])
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
for adm in config.admin_qq:
|
for adm in config['admin_qq']:
|
||||||
self.adapter.send_message(
|
self.adapter.send_message(
|
||||||
"person",
|
"person",
|
||||||
adm,
|
adm,
|
||||||
@@ -410,17 +405,17 @@ class QQBotManager:
|
|||||||
)
|
)
|
||||||
|
|
||||||
def notify_admin_message_chain(self, message):
|
def notify_admin_message_chain(self, message):
|
||||||
config = pkg.utils.context.get_config()
|
config = context.get_config_manager().data
|
||||||
if config.admin_qq != 0 and config.admin_qq != []:
|
if config['admin_qq'] != 0 and config['admin_qq'] != []:
|
||||||
logging.info("通知管理员:{}".format(message))
|
logging.info("通知管理员:{}".format(message))
|
||||||
if type(config.admin_qq) == int:
|
if type(config['admin_qq']) == int:
|
||||||
self.adapter.send_message(
|
self.adapter.send_message(
|
||||||
"person",
|
"person",
|
||||||
config.admin_qq,
|
config['admin_qq'],
|
||||||
message
|
message
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
for adm in config.admin_qq:
|
for adm in config['admin_qq']:
|
||||||
self.adapter.send_message(
|
self.adapter.send_message(
|
||||||
"person",
|
"person",
|
||||||
adm,
|
adm,
|
||||||
|
|||||||
@@ -1,32 +1,33 @@
|
|||||||
# 普通消息处理模块
|
# 普通消息处理模块
|
||||||
import logging
|
import logging
|
||||||
import openai
|
|
||||||
import pkg.utils.context
|
|
||||||
import pkg.openai.session
|
|
||||||
|
|
||||||
import pkg.plugin.host as plugin_host
|
import openai
|
||||||
import pkg.plugin.models as plugin_models
|
|
||||||
import pkg.qqbot.blob as blob
|
from ..utils import context
|
||||||
|
from ..openai import session as openai_session
|
||||||
|
|
||||||
|
from ..plugin import host as plugin_host
|
||||||
|
from ..plugin import models as plugin_models
|
||||||
import tips as tips_custom
|
import tips as tips_custom
|
||||||
|
|
||||||
|
|
||||||
def handle_exception(notify_admin: str = "", set_reply: str = "") -> list:
|
def handle_exception(notify_admin: str = "", set_reply: str = "") -> list:
|
||||||
"""处理异常,当notify_admin不为空时,会通知管理员,返回通知用户的消息"""
|
"""处理异常,当notify_admin不为空时,会通知管理员,返回通知用户的消息"""
|
||||||
import config
|
config = context.get_config_manager().data
|
||||||
pkg.utils.context.get_qqbot_manager().notify_admin(notify_admin)
|
context.get_qqbot_manager().notify_admin(notify_admin)
|
||||||
if config.hide_exce_info_to_user:
|
if config['hide_exce_info_to_user']:
|
||||||
return [tips_custom.alter_tip_message] if tips_custom.alter_tip_message else []
|
return [tips_custom.alter_tip_message] if tips_custom.alter_tip_message else []
|
||||||
else:
|
else:
|
||||||
return [set_reply]
|
return [set_reply]
|
||||||
|
|
||||||
|
|
||||||
def process_normal_message(text_message: str, mgr, config, launcher_type: str,
|
def process_normal_message(text_message: str, mgr, config: dict, launcher_type: str,
|
||||||
launcher_id: int, sender_id: int) -> list:
|
launcher_id: int, sender_id: int) -> list:
|
||||||
session_name = f"{launcher_type}_{launcher_id}"
|
session_name = f"{launcher_type}_{launcher_id}"
|
||||||
logging.info("[{}]发送消息:{}".format(session_name, text_message[:min(20, len(text_message))] + (
|
logging.info("[{}]发送消息:{}".format(session_name, text_message[:min(20, len(text_message))] + (
|
||||||
"..." if len(text_message) > 20 else "")))
|
"..." if len(text_message) > 20 else "")))
|
||||||
|
|
||||||
session = pkg.openai.session.get_session(session_name)
|
session = openai_session.get_session(session_name)
|
||||||
|
|
||||||
unexpected_exception_times = 0
|
unexpected_exception_times = 0
|
||||||
|
|
||||||
@@ -38,7 +39,7 @@ def process_normal_message(text_message: str, mgr, config, launcher_type: str,
|
|||||||
reply = handle_exception(notify_admin=f"{session_name},多次尝试失败。", set_reply=f"[bot]多次尝试失败,请重试或联系管理员")
|
reply = handle_exception(notify_admin=f"{session_name},多次尝试失败。", set_reply=f"[bot]多次尝试失败,请重试或联系管理员")
|
||||||
break
|
break
|
||||||
try:
|
try:
|
||||||
prefix = "[GPT]" if config.show_prefix else ""
|
prefix = "[GPT]" if config['show_prefix'] else ""
|
||||||
|
|
||||||
text, finish_reason, funcs = session.query(text_message)
|
text, finish_reason, funcs = session.query(text_message)
|
||||||
|
|
||||||
@@ -54,7 +55,7 @@ def process_normal_message(text_message: str, mgr, config, launcher_type: str,
|
|||||||
"funcs_called": funcs,
|
"funcs_called": funcs,
|
||||||
}
|
}
|
||||||
|
|
||||||
event = pkg.plugin.host.emit(plugin_models.NormalMessageResponded, **args)
|
event = plugin_host.emit(plugin_models.NormalMessageResponded, **args)
|
||||||
|
|
||||||
if event.get_return_value("prefix") is not None:
|
if event.get_return_value("prefix") is not None:
|
||||||
prefix = event.get_return_value("prefix")
|
prefix = event.get_return_value("prefix")
|
||||||
@@ -78,29 +79,29 @@ def process_normal_message(text_message: str, mgr, config, launcher_type: str,
|
|||||||
|
|
||||||
if 'message' in e.error and e.error['message'].__contains__('You exceeded your current quota'):
|
if 'message' in e.error and e.error['message'].__contains__('You exceeded your current quota'):
|
||||||
# 尝试切换api-key
|
# 尝试切换api-key
|
||||||
current_key_name = pkg.utils.context.get_openai_manager().key_mgr.get_key_name(
|
current_key_name = context.get_openai_manager().key_mgr.get_key_name(
|
||||||
pkg.utils.context.get_openai_manager().key_mgr.using_key
|
context.get_openai_manager().key_mgr.using_key
|
||||||
)
|
)
|
||||||
pkg.utils.context.get_openai_manager().key_mgr.set_current_exceeded()
|
context.get_openai_manager().key_mgr.set_current_exceeded()
|
||||||
|
|
||||||
# 触发插件事件
|
# 触发插件事件
|
||||||
args = {
|
args = {
|
||||||
'key_name': current_key_name,
|
'key_name': current_key_name,
|
||||||
'usage': pkg.utils.context.get_openai_manager().audit_mgr
|
'usage': context.get_openai_manager().audit_mgr
|
||||||
.get_usage(pkg.utils.context.get_openai_manager().key_mgr.get_using_key_md5()),
|
.get_usage(context.get_openai_manager().key_mgr.get_using_key_md5()),
|
||||||
'exceeded_keys': pkg.utils.context.get_openai_manager().key_mgr.exceeded,
|
'exceeded_keys': context.get_openai_manager().key_mgr.exceeded,
|
||||||
}
|
}
|
||||||
event = plugin_host.emit(plugin_models.KeyExceeded, **args)
|
event = plugin_host.emit(plugin_models.KeyExceeded, **args)
|
||||||
|
|
||||||
if not event.is_prevented_default():
|
if not event.is_prevented_default():
|
||||||
switched, name = pkg.utils.context.get_openai_manager().key_mgr.auto_switch()
|
switched, name = context.get_openai_manager().key_mgr.auto_switch()
|
||||||
|
|
||||||
if not switched:
|
if not switched:
|
||||||
reply = handle_exception(
|
reply = handle_exception(
|
||||||
"api-key调用额度超限({}),无可用api_key,请向OpenAI账户充值或在config.py中更换api_key;如果你认为这是误判,请尝试重启程序。".format(
|
"api-key调用额度超限({}),无可用api_key,请向OpenAI账户充值或在config.py中更换api_key;如果你认为这是误判,请尝试重启程序。".format(
|
||||||
current_key_name), "[bot]err:API调用额度超额,请联系管理员,或等待修复")
|
current_key_name), "[bot]err:API调用额度超额,请联系管理员,或等待修复")
|
||||||
else:
|
else:
|
||||||
openai.api_key = pkg.utils.context.get_openai_manager().key_mgr.get_using_key()
|
openai.api_key = context.get_openai_manager().key_mgr.get_using_key()
|
||||||
mgr.notify_admin("api-key调用额度超限({}),接口报错,已切换到{}".format(current_key_name, name))
|
mgr.notify_admin("api-key调用额度超限({}),接口报错,已切换到{}".format(current_key_name, name))
|
||||||
reply = ["[bot]err:API调用额度超额,已自动切换,请重新发送消息"]
|
reply = ["[bot]err:API调用额度超额,已自动切换,请重新发送消息"]
|
||||||
continue
|
continue
|
||||||
@@ -117,7 +118,7 @@ def process_normal_message(text_message: str, mgr, config, launcher_type: str,
|
|||||||
reply = handle_exception("{}会话调用API失败:{}".format(session_name, e),
|
reply = handle_exception("{}会话调用API失败:{}".format(session_name, e),
|
||||||
"[bot]err:RateLimitError,请重试或联系作者,或等待修复")
|
"[bot]err:RateLimitError,请重试或联系作者,或等待修复")
|
||||||
except openai.BadRequestError as e:
|
except openai.BadRequestError as e:
|
||||||
if config.auto_reset and "This model's maximum context length is" in str(e):
|
if config['auto_reset'] and "This model's maximum context length is" in str(e):
|
||||||
session.reset(persist=True)
|
session.reset(persist=True)
|
||||||
reply = [tips_custom.session_auto_reset_message]
|
reply = [tips_custom.session_auto_reset_message]
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -1,32 +1,27 @@
|
|||||||
# 此模块提供了消息处理的具体逻辑的接口
|
# 此模块提供了消息处理的具体逻辑的接口
|
||||||
import asyncio
|
import asyncio
|
||||||
import time
|
import time
|
||||||
|
import traceback
|
||||||
|
|
||||||
import mirai
|
import mirai
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from mirai import MessageChain, Plain
|
|
||||||
|
|
||||||
# 这里不使用动态引入config
|
# 这里不使用动态引入config
|
||||||
# 因为在这里动态引入会卡死程序
|
# 因为在这里动态引入会卡死程序
|
||||||
# 而此模块静态引用config与动态引入的表现一致
|
# 而此模块静态引用config与动态引入的表现一致
|
||||||
# 已弃用,由于超时时间现已动态使用
|
# 已弃用,由于超时时间现已动态使用
|
||||||
# import config as config_init_import
|
# import config as config_init_import
|
||||||
|
|
||||||
import pkg.openai.session
|
from ..qqbot import ratelimit
|
||||||
import pkg.openai.manager
|
from ..qqbot import command, message
|
||||||
import pkg.utils.reloader
|
from ..openai import session as openai_session
|
||||||
import pkg.utils.updater
|
from ..utils import context
|
||||||
import pkg.utils.context
|
|
||||||
import pkg.qqbot.message
|
|
||||||
import pkg.qqbot.command
|
|
||||||
import pkg.qqbot.ratelimit as ratelimit
|
|
||||||
|
|
||||||
import pkg.plugin.host as plugin_host
|
from ..plugin import host as plugin_host
|
||||||
import pkg.plugin.models as plugin_models
|
from ..plugin import models as plugin_models
|
||||||
import pkg.qqbot.ignore as ignore
|
from ..qqbot import ignore
|
||||||
import pkg.qqbot.banlist as banlist
|
from ..qqbot import banlist
|
||||||
import pkg.qqbot.blob as blob
|
from ..qqbot import blob
|
||||||
import tips as tips_custom
|
import tips as tips_custom
|
||||||
|
|
||||||
processing = []
|
processing = []
|
||||||
@@ -34,18 +29,18 @@ processing = []
|
|||||||
|
|
||||||
def is_admin(qq: int) -> bool:
|
def is_admin(qq: int) -> bool:
|
||||||
"""兼容list和int类型的管理员判断"""
|
"""兼容list和int类型的管理员判断"""
|
||||||
import config
|
config = context.get_config_manager().data
|
||||||
if type(config.admin_qq) == list:
|
if type(config['admin_qq']) == list:
|
||||||
return qq in config.admin_qq
|
return qq in config['admin_qq']
|
||||||
else:
|
else:
|
||||||
return qq == config.admin_qq
|
return qq == config['admin_qq']
|
||||||
|
|
||||||
|
|
||||||
def process_message(launcher_type: str, launcher_id: int, text_message: str, message_chain: MessageChain,
|
def process_message(launcher_type: str, launcher_id: int, text_message: str, message_chain: mirai.MessageChain,
|
||||||
sender_id: int) -> MessageChain:
|
sender_id: int) -> mirai.MessageChain:
|
||||||
global processing
|
global processing
|
||||||
|
|
||||||
mgr = pkg.utils.context.get_qqbot_manager()
|
mgr = context.get_qqbot_manager()
|
||||||
|
|
||||||
reply = []
|
reply = []
|
||||||
session_name = "{}_{}".format(launcher_type, launcher_id)
|
session_name = "{}_{}".format(launcher_type, launcher_id)
|
||||||
@@ -59,10 +54,10 @@ def process_message(launcher_type: str, launcher_id: int, text_message: str, mes
|
|||||||
logging.info("根据忽略规则忽略消息: {}".format(text_message))
|
logging.info("根据忽略规则忽略消息: {}".format(text_message))
|
||||||
return []
|
return []
|
||||||
|
|
||||||
import config
|
config = context.get_config_manager().data
|
||||||
|
|
||||||
if not config.wait_last_done and session_name in processing:
|
if not config['wait_last_done'] and session_name in processing:
|
||||||
return MessageChain([Plain(tips_custom.message_drop_tip)])
|
return mirai.MessageChain([mirai.Plain(tips_custom.message_drop_tip)])
|
||||||
|
|
||||||
# 检查是否被禁言
|
# 检查是否被禁言
|
||||||
if launcher_type == 'group':
|
if launcher_type == 'group':
|
||||||
@@ -71,12 +66,11 @@ def process_message(launcher_type: str, launcher_id: int, text_message: str, mes
|
|||||||
logging.info("机器人被禁言,跳过消息处理(group_{})".format(launcher_id))
|
logging.info("机器人被禁言,跳过消息处理(group_{})".format(launcher_id))
|
||||||
return reply
|
return reply
|
||||||
|
|
||||||
import config
|
if config['income_msg_check']:
|
||||||
if config.income_msg_check:
|
|
||||||
if mgr.reply_filter.is_illegal(text_message):
|
if mgr.reply_filter.is_illegal(text_message):
|
||||||
return MessageChain(Plain("[bot] 消息中存在不合适的内容, 请更换措辞"))
|
return mirai.MessageChain(mirai.Plain("[bot] 消息中存在不合适的内容, 请更换措辞"))
|
||||||
|
|
||||||
pkg.openai.session.get_session(session_name).acquire_response_lock()
|
openai_session.get_session(session_name).acquire_response_lock()
|
||||||
|
|
||||||
text_message = text_message.strip()
|
text_message = text_message.strip()
|
||||||
|
|
||||||
@@ -87,8 +81,6 @@ def process_message(launcher_type: str, launcher_id: int, text_message: str, mes
|
|||||||
# 处理消息
|
# 处理消息
|
||||||
try:
|
try:
|
||||||
|
|
||||||
config = pkg.utils.context.get_config()
|
|
||||||
|
|
||||||
processing.append(session_name)
|
processing.append(session_name)
|
||||||
try:
|
try:
|
||||||
if text_message.startswith('!') or text_message.startswith("!"): # 指令
|
if text_message.startswith('!') or text_message.startswith("!"): # 指令
|
||||||
@@ -114,17 +106,17 @@ def process_message(launcher_type: str, launcher_id: int, text_message: str, mes
|
|||||||
reply = event.get_return_value("reply")
|
reply = event.get_return_value("reply")
|
||||||
|
|
||||||
if not event.is_prevented_default():
|
if not event.is_prevented_default():
|
||||||
reply = pkg.qqbot.command.process_command(session_name, text_message,
|
reply = command.process_command(session_name, text_message,
|
||||||
mgr, config, launcher_type, launcher_id, sender_id, is_admin(sender_id))
|
mgr, config, launcher_type, launcher_id, sender_id, is_admin(sender_id))
|
||||||
|
|
||||||
else: # 消息
|
else: # 消息
|
||||||
# 限速丢弃检查
|
# 限速丢弃检查
|
||||||
# print(ratelimit.__crt_minute_usage__[session_name])
|
# print(ratelimit.__crt_minute_usage__[session_name])
|
||||||
if config.rate_limit_strategy == "drop":
|
if config['rate_limit_strategy'] == "drop":
|
||||||
if ratelimit.is_reach_limit(session_name):
|
if ratelimit.is_reach_limit(session_name):
|
||||||
logging.info("根据限速策略丢弃[{}]消息: {}".format(session_name, text_message))
|
logging.info("根据限速策略丢弃[{}]消息: {}".format(session_name, text_message))
|
||||||
|
|
||||||
return MessageChain(["[bot]"+tips_custom.rate_limit_drop_tip]) if tips_custom.rate_limit_drop_tip != "" else []
|
return mirai.MessageChain(["[bot]"+tips_custom.rate_limit_drop_tip]) if tips_custom.rate_limit_drop_tip != "" else []
|
||||||
|
|
||||||
before = time.time()
|
before = time.time()
|
||||||
# 触发插件事件
|
# 触发插件事件
|
||||||
@@ -146,11 +138,11 @@ def process_message(launcher_type: str, launcher_id: int, text_message: str, mes
|
|||||||
reply = event.get_return_value("reply")
|
reply = event.get_return_value("reply")
|
||||||
|
|
||||||
if not event.is_prevented_default():
|
if not event.is_prevented_default():
|
||||||
reply = pkg.qqbot.message.process_normal_message(text_message,
|
reply = message.process_normal_message(text_message,
|
||||||
mgr, config, launcher_type, launcher_id, sender_id)
|
mgr, config, launcher_type, launcher_id, sender_id)
|
||||||
|
|
||||||
# 限速等待时间
|
# 限速等待时间
|
||||||
if config.rate_limit_strategy == "wait":
|
if config['rate_limit_strategy'] == "wait":
|
||||||
time.sleep(ratelimit.get_rest_wait_time(session_name, time.time() - before))
|
time.sleep(ratelimit.get_rest_wait_time(session_name, time.time() - before))
|
||||||
|
|
||||||
ratelimit.add_usage(session_name)
|
ratelimit.add_usage(session_name)
|
||||||
@@ -170,16 +162,16 @@ def process_message(launcher_type: str, launcher_id: int, text_message: str, mes
|
|||||||
finally:
|
finally:
|
||||||
processing.remove(session_name)
|
processing.remove(session_name)
|
||||||
finally:
|
finally:
|
||||||
pkg.openai.session.get_session(session_name).release_response_lock()
|
openai_session.get_session(session_name).release_response_lock()
|
||||||
|
|
||||||
# 检查延迟时间
|
# 检查延迟时间
|
||||||
if config.force_delay_range[1] == 0:
|
if config['force_delay_range'][1] == 0:
|
||||||
delay_time = 0
|
delay_time = 0
|
||||||
else:
|
else:
|
||||||
import random
|
import random
|
||||||
|
|
||||||
# 从延迟范围中随机取一个值(浮点)
|
# 从延迟范围中随机取一个值(浮点)
|
||||||
rdm = random.uniform(config.force_delay_range[0], config.force_delay_range[1])
|
rdm = random.uniform(config['force_delay_range'][0], config['force_delay_range'][1])
|
||||||
|
|
||||||
spent = time.time() - start_time
|
spent = time.time() - start_time
|
||||||
|
|
||||||
@@ -191,4 +183,4 @@ def process_message(launcher_type: str, launcher_id: int, text_message: str, mes
|
|||||||
logging.info("[风控] 强制延迟{:.2f}秒(如需关闭,请到config.py修改force_delay_range字段)".format(delay_time))
|
logging.info("[风控] 强制延迟{:.2f}秒(如需关闭,请到config.py修改force_delay_range字段)".format(delay_time))
|
||||||
time.sleep(delay_time)
|
time.sleep(delay_time)
|
||||||
|
|
||||||
return MessageChain(reply)
|
return mirai.MessageChain(reply)
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ import time
|
|||||||
import logging
|
import logging
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
|
from ..utils import context
|
||||||
|
|
||||||
|
|
||||||
__crt_minute_usage__ = {}
|
__crt_minute_usage__ = {}
|
||||||
"""当前分钟每个会话的对话次数"""
|
"""当前分钟每个会话的对话次数"""
|
||||||
|
|
||||||
@@ -12,16 +15,16 @@ __timer_thr__: threading.Thread = None
|
|||||||
|
|
||||||
def get_limitation(session_name: str) -> int:
|
def get_limitation(session_name: str) -> int:
|
||||||
"""获取会话的限制次数"""
|
"""获取会话的限制次数"""
|
||||||
import config
|
config = context.get_config_manager().data
|
||||||
|
|
||||||
if type(config.rate_limitation) == dict:
|
if type(config['rate_limitation']) == dict:
|
||||||
# 如果被指定了
|
# 如果被指定了
|
||||||
if session_name in config.rate_limitation:
|
if session_name in config['rate_limitation']:
|
||||||
return config.rate_limitation[session_name]
|
return config['rate_limitation'][session_name]
|
||||||
else:
|
else:
|
||||||
return config.rate_limitation["default"]
|
return config['rate_limitation']["default"]
|
||||||
elif type(config.rate_limitation) == int:
|
elif type(config['rate_limitation']) == int:
|
||||||
return config.rate_limitation
|
return config['rate_limitation']
|
||||||
|
|
||||||
|
|
||||||
def add_usage(session_name: str):
|
def add_usage(session_name: str):
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
import mirai
|
|
||||||
|
|
||||||
from ..adapter import MessageSourceAdapter, MessageConverter, EventConverter
|
|
||||||
import nakuru
|
|
||||||
import nakuru.entities.components as nkc
|
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import typing
|
import typing
|
||||||
import traceback
|
import traceback
|
||||||
import logging
|
import logging
|
||||||
import json
|
|
||||||
|
|
||||||
from pkg.qqbot.blob import Forward, ForwardMessageNode, ForwardMessageDiaplay
|
import mirai
|
||||||
|
|
||||||
|
import nakuru
|
||||||
|
import nakuru.entities.components as nkc
|
||||||
|
|
||||||
|
from .. import adapter as adapter_model
|
||||||
|
from ...qqbot import blob
|
||||||
|
from ...utils import context
|
||||||
|
|
||||||
|
|
||||||
class NakuruProjectMessageConverter(MessageConverter):
|
class NakuruProjectMessageConverter(adapter_model.MessageConverter):
|
||||||
"""消息转换器"""
|
"""消息转换器"""
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def yiri2target(message_chain: mirai.MessageChain) -> list:
|
def yiri2target(message_chain: mirai.MessageChain) -> list:
|
||||||
@@ -49,7 +49,7 @@ class NakuruProjectMessageConverter(MessageConverter):
|
|||||||
nakuru_msg_list.append(nkc.Record.fromURL(component.url))
|
nakuru_msg_list.append(nkc.Record.fromURL(component.url))
|
||||||
elif component.path is not None:
|
elif component.path is not None:
|
||||||
nakuru_msg_list.append(nkc.Record.fromFileSystem(component.path))
|
nakuru_msg_list.append(nkc.Record.fromFileSystem(component.path))
|
||||||
elif type(component) is Forward:
|
elif type(component) is blob.Forward:
|
||||||
# 转发消息
|
# 转发消息
|
||||||
yiri_forward_node_list = component.node_list
|
yiri_forward_node_list = component.node_list
|
||||||
nakuru_forward_node_list = []
|
nakuru_forward_node_list = []
|
||||||
@@ -102,7 +102,7 @@ class NakuruProjectMessageConverter(MessageConverter):
|
|||||||
return chain
|
return chain
|
||||||
|
|
||||||
|
|
||||||
class NakuruProjectEventConverter(EventConverter):
|
class NakuruProjectEventConverter(adapter_model.EventConverter):
|
||||||
"""事件转换器"""
|
"""事件转换器"""
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def yiri2target(event: typing.Type[mirai.Event]):
|
def yiri2target(event: typing.Type[mirai.Event]):
|
||||||
@@ -157,7 +157,7 @@ class NakuruProjectEventConverter(EventConverter):
|
|||||||
raise Exception("未支持转换的事件类型: " + str(event))
|
raise Exception("未支持转换的事件类型: " + str(event))
|
||||||
|
|
||||||
|
|
||||||
class NakuruProjectAdapter(MessageSourceAdapter):
|
class NakuruProjectAdapter(adapter_model.MessageSourceAdapter):
|
||||||
"""nakuru-project适配器"""
|
"""nakuru-project适配器"""
|
||||||
bot: nakuru.CQHTTP
|
bot: nakuru.CQHTTP
|
||||||
bot_account_id: int
|
bot_account_id: int
|
||||||
@@ -173,19 +173,25 @@ class NakuruProjectAdapter(MessageSourceAdapter):
|
|||||||
self.listener_list = []
|
self.listener_list = []
|
||||||
# nakuru库有bug,这个接口没法带access_token,会失败
|
# nakuru库有bug,这个接口没法带access_token,会失败
|
||||||
# 所以目前自行发请求
|
# 所以目前自行发请求
|
||||||
import config
|
|
||||||
|
config = context.get_config_manager().data
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
resp = requests.get(
|
resp = requests.get(
|
||||||
url="http://{}:{}/get_login_info".format(config.nakuru_config['host'], config.nakuru_config['http_port']),
|
url="http://{}:{}/get_login_info".format(config['nakuru_config']['host'], config['nakuru_config']['http_port']),
|
||||||
headers={
|
headers={
|
||||||
'Authorization': "Bearer " + config.nakuru_config['token'] if 'token' in config.nakuru_config else ""
|
'Authorization': "Bearer " + config['nakuru_config']['token'] if 'token' in config['nakuru_config']else ""
|
||||||
},
|
},
|
||||||
timeout=5
|
timeout=5
|
||||||
)
|
)
|
||||||
if resp.status_code == 403:
|
if resp.status_code == 403:
|
||||||
logging.error("go-cqhttp拒绝访问,请检查config.py中nakuru_config的token是否与go-cqhttp设置的access-token匹配")
|
logging.error("go-cqhttp拒绝访问,请检查config.py中nakuru_config的token是否与go-cqhttp设置的access-token匹配")
|
||||||
raise Exception("go-cqhttp拒绝访问,请检查config.py中nakuru_config的token是否与go-cqhttp设置的access-token匹配")
|
raise Exception("go-cqhttp拒绝访问,请检查config.py中nakuru_config的token是否与go-cqhttp设置的access-token匹配")
|
||||||
self.bot_account_id = int(resp.json()['data']['user_id'])
|
try:
|
||||||
|
self.bot_account_id = int(resp.json()['data']['user_id'])
|
||||||
|
except Exception as e:
|
||||||
|
logging.error("获取go-cqhttp账号信息失败: {}, 请检查是否已启动go-cqhttp并配置正确".format(e))
|
||||||
|
raise Exception("获取go-cqhttp账号信息失败: {}, 请检查是否已启动go-cqhttp并配置正确".format(e))
|
||||||
|
|
||||||
def send_message(
|
def send_message(
|
||||||
self,
|
self,
|
||||||
@@ -267,7 +273,7 @@ class NakuruProjectAdapter(MessageSourceAdapter):
|
|||||||
logging.debug("注册监听器: " + str(event_type) + " -> " + str(callback))
|
logging.debug("注册监听器: " + str(event_type) + " -> " + str(callback))
|
||||||
|
|
||||||
# 包装函数
|
# 包装函数
|
||||||
async def listener_wrapper(app: nakuru.CQHTTP, source: self.event_converter.yiri2target(event_type)):
|
async def listener_wrapper(app: nakuru.CQHTTP, source: NakuruProjectAdapter.event_converter.yiri2target(event_type)):
|
||||||
callback(self.event_converter.target2yiri(source))
|
callback(self.event_converter.target2yiri(source))
|
||||||
|
|
||||||
# 将包装函数和原函数的对应关系存入列表
|
# 将包装函数和原函数的对应关系存入列表
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
from ..adapter import MessageSourceAdapter
|
import asyncio
|
||||||
|
import typing
|
||||||
|
|
||||||
import mirai
|
import mirai
|
||||||
import mirai.models.bus
|
import mirai.models.bus
|
||||||
from mirai.bot import MiraiRunner
|
from mirai.bot import MiraiRunner
|
||||||
|
|
||||||
import asyncio
|
from .. import adapter as adapter_model
|
||||||
import typing
|
|
||||||
|
|
||||||
|
|
||||||
class YiriMiraiAdapter(MessageSourceAdapter):
|
class YiriMiraiAdapter(adapter_model.MessageSourceAdapter):
|
||||||
"""YiriMirai适配器"""
|
"""YiriMirai适配器"""
|
||||||
bot: mirai.Mirai
|
bot: mirai.Mirai
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -1,5 +1,13 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import threading
|
import threading
|
||||||
from pkg.utils import ThreadCtl
|
from . import threadctl
|
||||||
|
|
||||||
|
from ..database import manager as db_mgr
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
@@ -7,6 +15,7 @@ context = {
|
|||||||
'database.manager.DatabaseManager': None,
|
'database.manager.DatabaseManager': None,
|
||||||
'openai.manager.OpenAIInteract': None,
|
'openai.manager.OpenAIInteract': None,
|
||||||
'qqbot.manager.QQBotManager': None,
|
'qqbot.manager.QQBotManager': None,
|
||||||
|
'config.manager.ConfigManager': None,
|
||||||
},
|
},
|
||||||
'pool_ctl': None,
|
'pool_ctl': None,
|
||||||
'logger_handler': None,
|
'logger_handler': None,
|
||||||
@@ -29,66 +38,79 @@ def get_config():
|
|||||||
return t
|
return t
|
||||||
|
|
||||||
|
|
||||||
def set_database_manager(inst):
|
def set_database_manager(inst: db_mgr.DatabaseManager):
|
||||||
context_lock.acquire()
|
context_lock.acquire()
|
||||||
context['inst']['database.manager.DatabaseManager'] = inst
|
context['inst']['database.manager.DatabaseManager'] = inst
|
||||||
context_lock.release()
|
context_lock.release()
|
||||||
|
|
||||||
|
|
||||||
def get_database_manager():
|
def get_database_manager() -> db_mgr.DatabaseManager:
|
||||||
context_lock.acquire()
|
context_lock.acquire()
|
||||||
t = context['inst']['database.manager.DatabaseManager']
|
t = context['inst']['database.manager.DatabaseManager']
|
||||||
context_lock.release()
|
context_lock.release()
|
||||||
return t
|
return t
|
||||||
|
|
||||||
|
|
||||||
def set_openai_manager(inst):
|
def set_openai_manager(inst: openai_mgr.OpenAIInteract):
|
||||||
context_lock.acquire()
|
context_lock.acquire()
|
||||||
context['inst']['openai.manager.OpenAIInteract'] = inst
|
context['inst']['openai.manager.OpenAIInteract'] = inst
|
||||||
context_lock.release()
|
context_lock.release()
|
||||||
|
|
||||||
|
|
||||||
def get_openai_manager():
|
def get_openai_manager() -> openai_mgr.OpenAIInteract:
|
||||||
context_lock.acquire()
|
context_lock.acquire()
|
||||||
t = context['inst']['openai.manager.OpenAIInteract']
|
t = context['inst']['openai.manager.OpenAIInteract']
|
||||||
context_lock.release()
|
context_lock.release()
|
||||||
return t
|
return t
|
||||||
|
|
||||||
|
|
||||||
def set_qqbot_manager(inst):
|
def set_qqbot_manager(inst: qqbot_mgr.QQBotManager):
|
||||||
context_lock.acquire()
|
context_lock.acquire()
|
||||||
context['inst']['qqbot.manager.QQBotManager'] = inst
|
context['inst']['qqbot.manager.QQBotManager'] = inst
|
||||||
context_lock.release()
|
context_lock.release()
|
||||||
|
|
||||||
|
|
||||||
def get_qqbot_manager():
|
def get_qqbot_manager() -> qqbot_mgr.QQBotManager:
|
||||||
context_lock.acquire()
|
context_lock.acquire()
|
||||||
t = context['inst']['qqbot.manager.QQBotManager']
|
t = context['inst']['qqbot.manager.QQBotManager']
|
||||||
context_lock.release()
|
context_lock.release()
|
||||||
return t
|
return t
|
||||||
|
|
||||||
|
|
||||||
def set_plugin_host(inst):
|
def set_config_manager(inst: config_mgr.ConfigManager):
|
||||||
|
context_lock.acquire()
|
||||||
|
context['inst']['config.manager.ConfigManager'] = inst
|
||||||
|
context_lock.release()
|
||||||
|
|
||||||
|
|
||||||
|
def get_config_manager() -> config_mgr.ConfigManager:
|
||||||
|
context_lock.acquire()
|
||||||
|
t = context['inst']['config.manager.ConfigManager']
|
||||||
|
context_lock.release()
|
||||||
|
return t
|
||||||
|
|
||||||
|
|
||||||
|
def set_plugin_host(inst: plugin_host.PluginHost):
|
||||||
context_lock.acquire()
|
context_lock.acquire()
|
||||||
context['plugin_host'] = inst
|
context['plugin_host'] = inst
|
||||||
context_lock.release()
|
context_lock.release()
|
||||||
|
|
||||||
|
|
||||||
def get_plugin_host():
|
def get_plugin_host() -> plugin_host.PluginHost:
|
||||||
context_lock.acquire()
|
context_lock.acquire()
|
||||||
t = context['plugin_host']
|
t = context['plugin_host']
|
||||||
context_lock.release()
|
context_lock.release()
|
||||||
return t
|
return t
|
||||||
|
|
||||||
|
|
||||||
def set_thread_ctl(inst):
|
def set_thread_ctl(inst: threadctl.ThreadCtl):
|
||||||
context_lock.acquire()
|
context_lock.acquire()
|
||||||
context['pool_ctl'] = inst
|
context['pool_ctl'] = inst
|
||||||
context_lock.release()
|
context_lock.release()
|
||||||
|
|
||||||
|
|
||||||
def get_thread_ctl() -> ThreadCtl:
|
def get_thread_ctl() -> threadctl.ThreadCtl:
|
||||||
context_lock.acquire()
|
context_lock.acquire()
|
||||||
t: ThreadCtl = context['pool_ctl']
|
t: threadctl.ThreadCtl = context['pool_ctl']
|
||||||
context_lock.release()
|
context_lock.release()
|
||||||
return t
|
return t
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ import time
|
|||||||
import logging
|
import logging
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
|
from . import context
|
||||||
|
|
||||||
|
|
||||||
log_file_name = "qchatgpt.log"
|
log_file_name = "qchatgpt.log"
|
||||||
|
|
||||||
@@ -36,7 +38,6 @@ def init_runtime_log_file():
|
|||||||
def reset_logging():
|
def reset_logging():
|
||||||
global log_file_name
|
global log_file_name
|
||||||
|
|
||||||
import config
|
|
||||||
import pkg.utils.context
|
import pkg.utils.context
|
||||||
import colorlog
|
import colorlog
|
||||||
|
|
||||||
@@ -46,7 +47,11 @@ def reset_logging():
|
|||||||
for handler in logging.getLogger().handlers:
|
for handler in logging.getLogger().handlers:
|
||||||
logging.getLogger().removeHandler(handler)
|
logging.getLogger().removeHandler(handler)
|
||||||
|
|
||||||
logging.basicConfig(level=config.logging_level, # 设置日志输出格式
|
config_mgr = context.get_config_manager()
|
||||||
|
|
||||||
|
logging_level = logging.INFO if config_mgr is None else config_mgr.data['logging_level']
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging_level, # 设置日志输出格式
|
||||||
filename=log_file_name, # log日志输出的文件位置和文件名
|
filename=log_file_name, # log日志输出的文件位置和文件名
|
||||||
format="[%(asctime)s.%(msecs)03d] %(pathname)s (%(lineno)d) - [%(levelname)s] :\n%(message)s",
|
format="[%(asctime)s.%(msecs)03d] %(pathname)s (%(lineno)d) - [%(levelname)s] :\n%(message)s",
|
||||||
# 日志输出的格式
|
# 日志输出的格式
|
||||||
@@ -54,7 +59,7 @@ def reset_logging():
|
|||||||
datefmt="%Y-%m-%d %H:%M:%S" # 时间输出的格式
|
datefmt="%Y-%m-%d %H:%M:%S" # 时间输出的格式
|
||||||
)
|
)
|
||||||
sh = logging.StreamHandler()
|
sh = logging.StreamHandler()
|
||||||
sh.setLevel(config.logging_level)
|
sh.setLevel(logging_level)
|
||||||
sh.setFormatter(colorlog.ColoredFormatter(
|
sh.setFormatter(colorlog.ColoredFormatter(
|
||||||
fmt="%(log_color)s[%(asctime)s.%(msecs)03d] %(filename)s (%(lineno)d) - [%(levelname)s] : "
|
fmt="%(log_color)s[%(asctime)s.%(msecs)03d] %(filename)s (%(lineno)d) - [%(levelname)s] : "
|
||||||
"%(message)s",
|
"%(message)s",
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
|
from . import context
|
||||||
|
|
||||||
|
|
||||||
def wrapper_proxies() -> dict:
|
def wrapper_proxies() -> dict:
|
||||||
"""获取代理"""
|
"""获取代理"""
|
||||||
import config
|
config = context.get_config_manager().data
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"http": config.openai_config['proxy'],
|
"http": config['openai_config']['proxy'],
|
||||||
"https": 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 'proxy' in config['openai_config'] and (config['openai_config']['proxy'] is not None) else None
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from pip._internal import main as pipmain
|
from pip._internal import main as pipmain
|
||||||
|
|
||||||
import pkg.utils.log as log
|
from . import log
|
||||||
|
|
||||||
|
|
||||||
def install(package):
|
def install(package):
|
||||||
@@ -19,7 +19,7 @@ def run_pip(params: list):
|
|||||||
|
|
||||||
|
|
||||||
def install_requirements(file):
|
def install_requirements(file):
|
||||||
pipmain(['install', '-r', file, "--upgrade", "-i", "https://pypi.tuna.tsinghua.edu.cn/simple",
|
pipmain(['install', '-r', file, "-i", "https://pypi.tuna.tsinghua.edu.cn/simple",
|
||||||
"--trusted-host", "pypi.tuna.tsinghua.edu.cn"])
|
"--trusted-host", "pypi.tuna.tsinghua.edu.cn"])
|
||||||
log.reset_logging()
|
log.reset_logging()
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import logging
|
import logging
|
||||||
import threading
|
|
||||||
|
|
||||||
import importlib
|
import importlib
|
||||||
import pkgutil
|
import pkgutil
|
||||||
import pkg.utils.context as context
|
import asyncio
|
||||||
import pkg.plugin.host
|
|
||||||
|
from . import context
|
||||||
|
from ..plugin import host as plugin_host
|
||||||
|
|
||||||
|
|
||||||
def walk(module, prefix='', path_prefix=''):
|
def walk(module, prefix='', path_prefix=''):
|
||||||
@@ -15,7 +15,7 @@ def walk(module, prefix='', path_prefix=''):
|
|||||||
walk(__import__(module.__name__ + '.' + item.name, fromlist=['']), prefix + item.name + '.', path_prefix + item.name + '/')
|
walk(__import__(module.__name__ + '.' + item.name, fromlist=['']), prefix + item.name + '.', path_prefix + item.name + '/')
|
||||||
else:
|
else:
|
||||||
logging.info('reload module: {}, path: {}'.format(prefix + item.name, path_prefix + item.name + '.py'))
|
logging.info('reload module: {}, path: {}'.format(prefix + item.name, path_prefix + item.name + '.py'))
|
||||||
pkg.plugin.host.__current_module_path__ = "plugins/" + path_prefix + item.name + '.py'
|
plugin_host.__current_module_path__ = "plugins/" + path_prefix + item.name + '.py'
|
||||||
importlib.reload(__import__(module.__name__ + '.' + item.name, fromlist=['']))
|
importlib.reload(__import__(module.__name__ + '.' + item.name, fromlist=['']))
|
||||||
|
|
||||||
|
|
||||||
@@ -53,15 +53,17 @@ def reload_all(notify=True):
|
|||||||
|
|
||||||
# 执行启动流程
|
# 执行启动流程
|
||||||
logging.info("执行程序启动流程")
|
logging.info("执行程序启动流程")
|
||||||
main.load_config()
|
|
||||||
main.complete_tips()
|
|
||||||
context.get_thread_ctl().reload(
|
context.get_thread_ctl().reload(
|
||||||
admin_pool_num=context.get_config().admin_pool_num,
|
admin_pool_num=4,
|
||||||
user_pool_num=context.get_config().user_pool_num
|
user_pool_num=8
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def run_wrapper():
|
||||||
|
asyncio.run(main.start_process(False))
|
||||||
|
|
||||||
context.get_thread_ctl().submit_sys_task(
|
context.get_thread_ctl().submit_sys_task(
|
||||||
main.start,
|
run_wrapper
|
||||||
False
|
|
||||||
)
|
)
|
||||||
|
|
||||||
logging.info('程序启动完成')
|
logging.info('程序启动完成')
|
||||||
|
|||||||
@@ -1,37 +1,42 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from PIL import Image, ImageDraw, ImageFont
|
|
||||||
import re
|
import re
|
||||||
import os
|
import os
|
||||||
import config
|
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
|
from PIL import Image, ImageDraw, ImageFont
|
||||||
|
|
||||||
|
from ..utils import context
|
||||||
|
|
||||||
|
|
||||||
text_render_font: ImageFont = None
|
text_render_font: ImageFont = None
|
||||||
|
|
||||||
if config.blob_message_strategy == "image": # 仅在启用了image时才加载字体
|
def initialize():
|
||||||
use_font = config.font_path
|
config = context.get_config_manager().data
|
||||||
try:
|
|
||||||
|
|
||||||
# 检查是否存在
|
if config['blob_message_strategy'] == "image": # 仅在启用了image时才加载字体
|
||||||
if not os.path.exists(use_font):
|
use_font = config['font_path']
|
||||||
# 若是windows系统,使用微软雅黑
|
try:
|
||||||
if os.name == "nt":
|
|
||||||
use_font = "C:/Windows/Fonts/msyh.ttc"
|
# 检查是否存在
|
||||||
if not os.path.exists(use_font):
|
if not os.path.exists(use_font):
|
||||||
logging.warn("未找到字体文件,且无法使用Windows自带字体,更换为转发消息组件以发送长消息,您可以在config.py中调整相关设置。")
|
# 若是windows系统,使用微软雅黑
|
||||||
config.blob_message_strategy = "forward"
|
if os.name == "nt":
|
||||||
|
use_font = "C:/Windows/Fonts/msyh.ttc"
|
||||||
|
if not os.path.exists(use_font):
|
||||||
|
logging.warn("未找到字体文件,且无法使用Windows自带字体,更换为转发消息组件以发送长消息,您可以在config.py中调整相关设置。")
|
||||||
|
config['blob_message_strategy'] = "forward"
|
||||||
|
else:
|
||||||
|
logging.info("使用Windows自带字体:" + use_font)
|
||||||
|
text_render_font = ImageFont.truetype(use_font, 32, encoding="utf-8")
|
||||||
else:
|
else:
|
||||||
logging.info("使用Windows自带字体:" + use_font)
|
logging.warn("未找到字体文件,且无法使用Windows自带字体,更换为转发消息组件以发送长消息,您可以在config.py中调整相关设置。")
|
||||||
text_render_font = ImageFont.truetype(use_font, 32, encoding="utf-8")
|
config['blob_message_strategy'] = "forward"
|
||||||
else:
|
else:
|
||||||
logging.warn("未找到字体文件,且无法使用Windows自带字体,更换为转发消息组件以发送长消息,您可以在config.py中调整相关设置。")
|
text_render_font = ImageFont.truetype(use_font, 32, encoding="utf-8")
|
||||||
config.blob_message_strategy = "forward"
|
except:
|
||||||
else:
|
traceback.print_exc()
|
||||||
text_render_font = ImageFont.truetype(use_font, 32, encoding="utf-8")
|
logging.error("加载字体文件失败({}),更换为转发消息组件以发送长消息,您可以在config.py中调整相关设置。".format(use_font))
|
||||||
except:
|
config['blob_message_strategy'] = "forward"
|
||||||
traceback.print_exc()
|
|
||||||
logging.error("加载字体文件失败({}),更换为转发消息组件以发送长消息,您可以在config.py中调整相关设置。".format(use_font))
|
|
||||||
config.blob_message_strategy = "forward"
|
|
||||||
|
|
||||||
|
|
||||||
def indexNumber(path=''):
|
def indexNumber(path=''):
|
||||||
|
|||||||
@@ -3,10 +3,9 @@ import logging
|
|||||||
import os.path
|
import os.path
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
import json
|
|
||||||
|
|
||||||
import pkg.utils.constants
|
from . import constants
|
||||||
import pkg.utils.network as network
|
from . import network
|
||||||
|
|
||||||
|
|
||||||
def check_dulwich_closure():
|
def check_dulwich_closure():
|
||||||
@@ -70,7 +69,7 @@ def get_release_list() -> list:
|
|||||||
|
|
||||||
def get_current_tag() -> str:
|
def get_current_tag() -> str:
|
||||||
"""获取当前tag"""
|
"""获取当前tag"""
|
||||||
current_tag = pkg.utils.constants.semantic_version
|
current_tag = constants.semantic_version
|
||||||
if os.path.exists("current_tag"):
|
if os.path.exists("current_tag"):
|
||||||
with open("current_tag", "r") as f:
|
with open("current_tag", "r") as f:
|
||||||
current_tag = f.read()
|
current_tag = f.read()
|
||||||
@@ -204,9 +203,9 @@ def update_all(cli: bool = False) -> bool:
|
|||||||
# 通知管理员
|
# 通知管理员
|
||||||
if not cli:
|
if not cli:
|
||||||
import pkg.utils.context
|
import pkg.utils.context
|
||||||
pkg.utils.context.get_qqbot_manager().notify_admin("已更新到最新版本: {}\n更新日志:\n{}\n完整的更新日志请前往 https://github.com/RockChinQ/QChatGPT/releases 查看".format(current_tag, "\n".join(rls_notes[:-1])))
|
pkg.utils.context.get_qqbot_manager().notify_admin("已更新到最新版本: {}\n更新日志:\n{}\n完整的更新日志请前往 https://github.com/RockChinQ/QChatGPT/releases 查看。\n请手动重启程序以使用新版本。".format(current_tag, "\n".join(rls_notes[:-1])))
|
||||||
else:
|
else:
|
||||||
print("已更新到最新版本: {}\n更新日志:\n{}\n完整的更新日志请前往 https://github.com/RockChinQ/QChatGPT/releases 查看".format(current_tag, "\n".join(rls_notes[:-1])))
|
print("已更新到最新版本: {}\n更新日志:\n{}\n完整的更新日志请前往 https://github.com/RockChinQ/QChatGPT/releases 查看。请手动重启程序以使用新版本。".format(current_tag, "\n".join(rls_notes[:-1])))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ requests
|
|||||||
openai
|
openai
|
||||||
dulwich~=0.21.6
|
dulwich~=0.21.6
|
||||||
colorlog~=6.6.0
|
colorlog~=6.6.0
|
||||||
yiri-mirai
|
yiri-mirai-rc
|
||||||
websockets
|
websockets
|
||||||
urllib3
|
urllib3
|
||||||
func_timeout~=4.3.5
|
func_timeout~=4.3.5
|
||||||
|
|||||||
@@ -4,5 +4,23 @@
|
|||||||
"time": "2023-08-01 10:49:26",
|
"time": "2023-08-01 10:49:26",
|
||||||
"timestamp": 1690858166,
|
"timestamp": 1690858166,
|
||||||
"content": "现已支持GPT函数调用功能,欢迎了解:https://github.com/RockChinQ/QChatGPT/wiki/%E6%8F%92%E4%BB%B6%E4%BD%BF%E7%94%A8-%E5%86%85%E5%AE%B9%E5%87%BD%E6%95%B0"
|
"content": "现已支持GPT函数调用功能,欢迎了解:https://github.com/RockChinQ/QChatGPT/wiki/%E6%8F%92%E4%BB%B6%E4%BD%BF%E7%94%A8-%E5%86%85%E5%AE%B9%E5%87%BD%E6%95%B0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"time": "2023-11-10 12:20:09",
|
||||||
|
"timestamp": 1699590009,
|
||||||
|
"content": "OpenAI 库1.0版本已发行,若出现 OpenAI 调用问题,请更新 QChatGPT 版本。详见项目主页:https://github.com/RockChinQ/QChatGPT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 4,
|
||||||
|
"time": "2023-11-13 18:02:39",
|
||||||
|
"timestamp": 1699869759,
|
||||||
|
"content": "近期 OpenAI 接口改动频繁,正在积极适配并添加新功能,请尽快更新到最新版本,更新方式:https://github.com/RockChinQ/QChatGPT/discussions/595"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 5,
|
||||||
|
"time": "2023-12-07 9:20:00",
|
||||||
|
"timestamp": 1701912000,
|
||||||
|
"content": "QChatGPT 一周年啦!感谢大家的选择和支持,RockChinQ 在此衷心感谢素未谋面但又至关重要的你们每一个人,愿 AI 与我们同在!欢迎前往:https://github.com/RockChinQ/QChatGPT/discussions/627 参与讨论。"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
> **Warning**
|
> [!WARNING]
|
||||||
> 此文档已过时,请查看[QChatGPT 容器化部署指南](docker_deployment.md)
|
> 此文档已过时,请查看[QChatGPT 容器化部署指南](docker_deployment.md)
|
||||||
|
|
||||||
## 操作步骤
|
## 操作步骤
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# QChatGPT 容器化部署指南
|
# QChatGPT 容器化部署指南
|
||||||
|
|
||||||
> **Warning**
|
> [!WARNING]
|
||||||
> 请您确保您**确实**需要 Docker 部署,您**必须**具有以下能力:
|
> 请您确保您**确实**需要 Docker 部署,您**必须**具有以下能力:
|
||||||
> - 了解 `Docker` 和 `Docker Compose` 的使用
|
> - 了解 `Docker` 和 `Docker Compose` 的使用
|
||||||
> - 了解容器间网络通信配置方式
|
> - 了解容器间网络通信配置方式
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
QChatGPT 主程序需要连接`QQ登录框架`以与QQ通信,您可以选择 [Mirai](https://github.com/mamoe/mirai)(还需要配置mirai-api-http,请查看此仓库README中手动部署部分) 或 [go-cqhttp](https://github.com/Mrs4s/go-cqhttp),我们仅发布 QChatGPT主程序 的镜像,您需要自行配置QQ登录框架(可以参考[README.md](https://github.com/RockChinQ/QChatGPT#-%E9%85%8D%E7%BD%AEqq%E7%99%BB%E5%BD%95%E6%A1%86%E6%9E%B6)中的教程,或自行寻找其镜像)并在 QChatGPT 的配置文件中设置连接地址。
|
QChatGPT 主程序需要连接`QQ登录框架`以与QQ通信,您可以选择 [Mirai](https://github.com/mamoe/mirai)(还需要配置mirai-api-http,请查看此仓库README中手动部署部分) 或 [go-cqhttp](https://github.com/Mrs4s/go-cqhttp),我们仅发布 QChatGPT主程序 的镜像,您需要自行配置QQ登录框架(可以参考[README.md](https://github.com/RockChinQ/QChatGPT#-%E9%85%8D%E7%BD%AEqq%E7%99%BB%E5%BD%95%E6%A1%86%E6%9E%B6)中的教程,或自行寻找其镜像)并在 QChatGPT 的配置文件中设置连接地址。
|
||||||
|
|
||||||
> **Note**
|
> [!NOTE]
|
||||||
> 请先确保 Docker 和 Docker Compose 已安装
|
> 请先确保 Docker 和 Docker Compose 已安装
|
||||||
|
|
||||||
## 准备文件
|
## 准备文件
|
||||||
|
|||||||
7
tests/repo_regexp_test.py
Normal file
7
tests/repo_regexp_test.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import re
|
||||||
|
|
||||||
|
repo_url = "git@github.com:RockChinQ/WebwlkrPlugin.git"
|
||||||
|
|
||||||
|
repo = re.findall(r'(?:https?://github\.com/|git@github\.com:)([^/]+/[^/]+?)(?:\.git|/|$)', repo_url)
|
||||||
|
|
||||||
|
print(repo)
|
||||||
Reference in New Issue
Block a user