Compare commits

..

71 Commits

Author SHA1 Message Date
Rock Chin
e8dbd426ae Release v2.3.9 2023-04-15 17:36:59 +08:00
Rock Chin
40d6e809a0 Merge pull request #417 from RockChinQ/354-feature-single-concurrency
[Feat] 支持设置单会话内同时仅处理一条消息
2023-04-15 17:35:36 +08:00
GitHub Actions
236c540d18 Update override-all.json 2023-04-15 09:34:16 +00:00
Rock Chin
d6ca059f6c feat: 支持设置单会话内同时仅处理一条消息 2023-04-15 17:33:57 +08:00
Rock Chin
52c06a60ca fix: 公告功能bug 2023-04-15 16:54:50 +08:00
Rock Chin
6353644ec3 test: 测试公告 2023-04-15 16:49:11 +08:00
Rock Chin
20df9ded3d Merge pull request #416 from RockChinQ/413-feature-json-format-anouns
[Feat] 支持JSON格式的公告
2023-04-15 16:47:03 +08:00
Rock Chin
7569b18a4c feat: 支持JSON格式的公告 2023-04-15 16:45:26 +08:00
Rock Chin
b9da4f4951 Merge pull request #415 from RockChinQ/413-feature-json-format-anouns
[Feat] 新增`announcement.json`文件
2023-04-15 16:33:03 +08:00
Rock Chin
89b9e29257 Update pull_request_template.md 2023-04-15 16:25:24 +08:00
Rock Chin
d605de9de4 feat: 添加公告模板及公告发布脚本 2023-04-15 09:38:46 +08:00
Rock Chin
d46c94d7c3 Release v2.3.8 2023-04-14 23:47:00 +08:00
Rock Chin
2db9c00530 Merge pull request #414 from RockChinQ/detailed-rate-limit
[Feat] 速度限制支持细化到单个人或群
2023-04-14 19:46:24 +08:00
GitHub Actions
66d8d159f9 Update override-all.json 2023-04-14 11:44:26 +00:00
Rock Chin
9fa1446284 feat: 支持细化到个人和群的限速 2023-04-14 19:44:03 +08:00
Rock Chin
b3e4cb48c7 Merge pull request #412 from RockChinQ/349-bugfix-auto-deps-solving-failure
[Fix] 循环依赖导致的依赖自动解决失败
2023-04-14 18:44:40 +08:00
Rock Chin
0bca7b2247 fix: 循环引用导致的依赖自动解决失败 2023-04-14 18:42:09 +08:00
Rock Chin
7812e03c9d chore: 删除requirements.txt中对websockets的版本要求以防冲突 2023-04-14 18:27:44 +08:00
Rock Chin
7a852ae5af Merge pull request #410 from 2675hujilo/tips
删除tips-custom-template.py中无用字段
2023-04-14 17:43:30 +08:00
26751
706d9e61c1 删除tips-custom-template.py中无用字段 2023-04-14 02:00:45 +08:00
Rock Chin
8f0ed4ff4b Merge branch 'master' of https://github.com/RockChinQ/QChatGPT 2023-04-12 15:28:59 +08:00
Rock Chin
3415b6f121 doc: 添加lieyanqzu/WeatherPlugin 2023-04-12 15:28:56 +08:00
Rock Chin
256ba6fb86 Merge pull request #406 from RockChinQ/dependabot/pip/openai-approx-eq-0.27.4
chore(deps): update openai requirement from ~=0.27.2 to ~=0.27.4
2023-04-10 18:31:39 +08:00
dependabot[bot]
d30b2b9afe chore(deps): update openai requirement from ~=0.27.2 to ~=0.27.4
Updates the requirements on [openai](https://github.com/openai/openai-python) to permit the latest version.
- [Release notes](https://github.com/openai/openai-python/releases)
- [Commits](https://github.com/openai/openai-python/compare/v0.27.2...v0.27.4)

---
updated-dependencies:
- dependency-name: openai
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-10 09:03:02 +00:00
Rock Chin
be943ca1fc doc: 链接文档 2023-04-08 19:39:42 +08:00
Rock Chin
1ddab2a97a doc: README.md in English 2023-04-08 19:32:31 +08:00
Rock Chin
e15fd4695c Merge branch 'master' of https://github.com/RockChinQ/QChatGPT 2023-04-08 18:26:10 +08:00
Rock Chin
ffa4b1b4a1 fix(modelmgr): 使用异步请求时的异常类型丢失 2023-04-08 18:26:08 +08:00
Rock Chin
f8eee3a2a6 Merge pull request #399 from RockChinQ/optional-config-override
[Feat] override.json可选应用
2023-04-08 16:26:56 +08:00
Rock Chin
eeee7a8343 feat: 仅在提供命令行参数时应用override.json的内容 2023-04-08 16:21:40 +08:00
Rock Chin
8447b73fcb doc(README.md): 删除ChatAPI2D插件 2023-04-08 16:15:11 +08:00
Rock Chin
2863945d5f feat(config-template): 更改为常量表示超时时间 2023-04-08 15:36:35 +08:00
Rock Chin
cb1f8ca6f7 doc(README.md): 添加wenyinos/ChatAPI2D插件 2023-04-08 00:33:47 +08:00
Rock Chin
1d9964bcb1 Release v2.3.7 2023-04-08 00:21:21 +08:00
GitHub Actions Bot
15cb8016d3 Update cmdpriv-template.json 2023-04-07 16:20:13 +00:00
Rock Chin
895cc0a2c5 ci: test 2023-04-08 00:19:37 +08:00
Rock Chin
20bf349e4e ci: cmdpriv模板脚本 2023-04-08 00:18:00 +08:00
Rock Chin
e297763da1 fix: !cfg指令失效 2023-04-08 00:13:19 +08:00
Rock Chin
e471970654 ci: test 2023-04-07 20:32:51 +08:00
Rock Chin
12faaaced8 ci: 仅在wiki文件更新时提交 2023-04-07 20:31:33 +08:00
Rock Chin
083cbc55cc Release v2.3.6 2023-04-07 17:15:17 +08:00
Rock Chin
8aa7a3273d Merge pull request #390 from RockChinQ/customizable-tips
[Feat] 支持自定义提示消息
2023-04-07 17:13:28 +08:00
Rock Chin
255e2c4385 doc: 添加自定义提示消息的说明 2023-04-07 17:12:33 +08:00
Rock Chin
9856306870 feat: 修改文件生成顺序 2023-04-07 17:10:44 +08:00
GitHub Actions
527ab8b8a7 Update override-all.json 2023-04-07 09:08:21 +00:00
Rock Chin
f8e19ba9b3 feat: 删除config-template.py中多余的属性 2023-04-07 17:07:43 +08:00
GitHub Actions
7649dbfbbc Update override-all.json 2023-04-07 09:00:40 +00:00
Rock Chin
81e734644d feat: 删除config-template.py中的help_message模板 2023-04-07 17:00:16 +08:00
Rock Chin
ae55cf5b1e feat: 适配help指令 2023-04-07 16:59:51 +08:00
Rock Chin
af539546ef Merge pull request #356 from 2675hujilo/tips
[Feat] 支持自定义提示消息
2023-04-07 16:43:58 +08:00
Rock Chin
0031ce57d0 Merge branch 'customizable-tips' into tips 2023-04-07 16:40:26 +08:00
Rock Chin
2f48a2ce57 Merge branch 'customizable-tips' of https://github.com/RockChinQ/QChatGPT into customizable-tips 2023-04-07 16:39:48 +08:00
Rock Chin
6068ab7100 feat: 修改help_message为主线的内容 2023-04-07 16:39:25 +08:00
GitHub Actions
29a7dccef4 Update override-all.json 2023-04-07 08:34:23 +00:00
Rock Chin
e2073da86e Merge branch '2675hujilo-tips' into customizable-tips 2023-04-07 16:32:32 +08:00
2675hujilo
ae079526f7 删除tips-customs-template.py中不必要注释 2023-04-07 16:29:09 +08:00
26751
947bae8e26 删除tips-customs-template.py中不必要注释
Signed-off-by: 26751 <2675174581@qq.com>
2023-04-07 16:22:22 +08:00
Rock Chin
a68e29dff6 feat: tips模块完整性检查 2023-04-07 16:02:22 +08:00
Rock Chin
a588d7f960 feat: 热重载加上tips模块 2023-04-07 13:28:07 +08:00
Rock Chin
66224e5a32 fix: 热重载后未检查配置文件存在性 2023-04-07 13:25:57 +08:00
Rock Chin
07abad6a14 feat: 将tips的值统一为str类型 2023-04-07 13:23:58 +08:00
Rock Chin
83d02aaaac chore: 修改配置文件名称 2023-04-07 13:20:57 +08:00
26751
5865ac017c 增加tips_custom.py提示
Signed-off-by: 26751 <2675174581@qq.com>
2023-04-02 13:46:15 +08:00
26751
4061a92f8e 删除override-all.json中无效的字段
Signed-off-by: 26751 <2675174581@qq.com>
2023-04-02 13:36:51 +08:00
2675hujilo
d37c31b31c Update tips_custom_template.py 2023-04-01 18:43:03 +08:00
2675hujilo
973ef0078f Delete tips_custom.py 2023-04-01 18:36:33 +08:00
26751
48dcd257da Signed-off-by: 26751 <2675174581@qq.com> 2023-04-01 18:33:37 +08:00
26751
da03911610 Signed-off-by: 26751 <2675174581@qq.com> 2023-04-01 16:39:02 +08:00
26751
b6f7f3b73f Signed-off-by: 26751 <2675174581@qq.com> 2023-04-01 02:35:27 +08:00
26751
2050d20ea7 Signed-off-by: 26751 <2675174581@qq.com> 2023-04-01 02:23:40 +08:00
26751
ac1fb4a63a 修改自定义提示语 2023-04-01 01:02:59 +08:00
35 changed files with 656 additions and 206 deletions

View File

@@ -4,7 +4,7 @@
### 事务
- [ ] 已阅读仓库[贡献指引](../CONTRIBUTING.md)
- [ ] 已阅读仓库[贡献指引](https://github.com/RockChinQ/QChatGPT/blob/master/CONTRIBUTING.md)
- [ ] 已与维护者在issues或其他平台沟通此PR大致内容
## 以下内容可在起草PR后、合并PR前逐步完成

View File

@@ -26,6 +26,8 @@ jobs:
- name: Commit and Push Changes
run: |
cd wiki
git add .
git commit -m "Update wiki"
git push
if git diff --name-only; then
git add .
git commit -m "Update wiki"
git push
fi

View File

@@ -42,7 +42,7 @@ jobs:
- name: Check for changes in cmdpriv-template.json
id: check_changes
run: |
if git diff --name-only | grep -q "cmdpriv-template.json"; then
if git diff --name-only | grep -q "res/templates/cmdpriv-template.json"; then
echo "::set-output name=changes_detected::true"
else
echo "::set-output name=changes_detected::false"
@@ -53,6 +53,6 @@ jobs:
run: |
git config --global user.name "GitHub Actions Bot"
git config --global user.email "<github-actions@github.com>"
git add cmdpriv-template.json
git add res/templates/cmdpriv-template.json
git commit -m "Update cmdpriv-template.json"
git push

4
.gitignore vendored
View File

@@ -17,4 +17,6 @@ scenario/
override.json
cookies.json
res/announcement_saved
cmdpriv.json
res/announcement_saved.json
cmdpriv.json
tips.py

View File

@@ -1,5 +1,9 @@
# QChatGPT🤖
[English](README_en.md) | 简体中文
[![GitHub release (latest by date)](https://img.shields.io/github/v/release/RockChinQ/QChatGPT?style=flat-square)](https://github.com/RockChinQ/QChatGPT/releases/latest)
> 2023/3/18 现已支持GPT-4 API内测请查看`config-template.py`中的`completion_api_params`
> 2023/3/15 逆向库已支持New Bing使用方法查看[插件文档](https://github.com/RockChinQ/revLibs)
@@ -126,6 +130,12 @@
- 目前已支持正向代理访问接口
- 详细请查看config.py中的`openai_config`的说明
</details>
<details>
<summary>✅支持自定义提示内容</summary>
- 允许用户自定义报错、帮助等提示信息
- 请查看`tips.py`
</details>
</details>
详情请查看[Wiki功能使用页](https://github.com/RockChinQ/QChatGPT/wiki/%E5%8A%9F%E8%83%BD%E4%BD%BF%E7%94%A8#%E5%8A%9F%E8%83%BD%E7%82%B9%E5%88%97%E4%B8%BE)
@@ -251,6 +261,7 @@ python3 main.py
- [RockChinQ/WaitYiYan](https://github.com/RockChinQ/WaitYiYan) - 实时获取百度`文心一言`等待列表人数
- [chordfish-k/QChartGPT_Emoticon_Plugin](https://github.com/chordfish-k/QChartGPT_Emoticon_Plugin) - 使机器人根据回复内容发送表情包
- [oliverkirk-sudo/ChatPoeBot](https://github.com/oliverkirk-sudo/ChatPoeBot) - 接入[Poe](https://poe.com/)上的机器人
- [lieyanqzu/WeatherPlugin](https://github.com/lieyanqzu/WeatherPlugin) - 天气查询插件
</details>
## 😘致谢

194
README_en.md Normal file
View File

@@ -0,0 +1,194 @@
# QChatGPT🤖
English | [简体中文](README.md)
[![GitHub release (latest by date)](https://img.shields.io/github/v/release/RockChinQ/QChatGPT?style=flat-square)](https://github.com/RockChinQ/QChatGPT/releases/latest)
- Refer to [Wiki](https://github.com/RockChinQ/QChatGPT/wiki) to get further information.
- Official QQ group: 656285629
- Community QQ group: 362515018
- QQ channel robot: [QQChannelChatGPT](https://github.com/Soulter/QQChannelChatGPT)
- Any contribution is welcome, please refer to [CONTRIBUTING.md](CONTRIBUTING.md)
## 🍺List of supported models
<details>
<summary>Details</summary>
### Chat
- OpenAI GPT-3.5 (ChatGPT API), default model
- OpenAI GPT-3, supported natively, switch to it in `config.py`
- OpenAI GPT-4, supported natively, qualification for internal testing required, switch to it in `config.py`
- ChatGPT website edition (GPT-3.5), see [revLibs plugin](https://github.com/RockChinQ/revLibs)
- ChatGPT website edition (GPT-4), ChatGPT plus subscription required, see [revLibs plugin](https://github.com/RockChinQ/revLibs)
- New Bing, see [revLibs plugin](https://github.com/RockChinQ/revLibs)
### Story
- NovelAI API, see [QCPNovelAi plugin](https://github.com/dominoar/QCPNovelAi)
### Image
- OpenAI DALL·E, supported natively, see [Wiki(cn)](https://github.com/RockChinQ/QChatGPT/wiki/%E5%8A%9F%E8%83%BD%E4%BD%BF%E7%94%A8#%E5%8A%9F%E8%83%BD%E7%82%B9%E5%88%97%E4%B8%BE)
- NovelAI API, see [QCPNovelAi plugin](https://github.com/dominoar/QCPNovelAi)
### Voice
- TTS+VITS, see [QChatPlugins](https://github.com/dominoar/QChatPlugins)
- Plachta/VITS-Umamusume-voice-synthesizer, see [chat_voice plugin](https://github.com/oliverkirk-sudo/chat_voice)
</details>
Install this [plugin](https://github.com/RockChinQ/Switcher) to switch between different models.
## ✅Function Points
<details>
<summary>Details</summary>
- ✅Sensitive word filtering, avoid being banned
- ✅Multiple responding rules, including regular expression matching
- ✅Multiple api-key management, automatic switching when exceeding
- ✅Support for customizing the preset prompt text
- ✅Chat, story, image, voice, etc. models are supported
- ✅Support for hot reloading and hot updating
- ✅Support for plugin loading
- ✅Blacklist mechanism for private chat and group chat
- ✅Excellent long message processing strategy
- ✅Reply rate limitation
- ✅Support for network proxy
- ✅Support for customizing the output format
</details>
More details, see [Wiki(cn)](https://github.com/RockChinQ/QChatGPT/wiki/%E5%8A%9F%E8%83%BD%E4%BD%BF%E7%94%A8#%E5%8A%9F%E8%83%BD%E7%82%B9%E5%88%97%E4%B8%BE)
## 🔩Deployment
**If you encounter any problems during deployment, please search in the issue of [QChatGPT](https://github.com/RockChinQ/QChatGPT/issues) or [qcg-installer](https://github.com/RockChinQ/qcg-installer/issues) first.**
### - Register OpenAI account
> If you want to use a model other than OpenAI (such as New Bing), you can skip this step and directly refer to following steps, and then configure it according to the relevant plugin documentation.
To register OpenAI account, please refer to the following articles(in Chinese):
> [国内注册ChatGPT的方法(100%可用)](https://www.pythonthree.com/register-openai-chatgpt/)
> [手把手教你如何注册ChatGPT超级详细](https://guxiaobei.com/51461)
Check your api-key in [personal center](https://beta.openai.com/account/api-keys) after registration, and then follow the following steps to deploy.
### - Deploy Automatically
<details>
<summary>Details</summary>
#### Docker
See [this document(cn)](res/docs/docker_deploy.md)
Contributed by [@mikumifa](https://github.com/mikumifa)
#### Installer
Use [this installer](https://github.com/RockChinQ/qcg-installer) to deploy.
- The installer currently only supports some platforms, please refer to the repository document for details, and manually deploy for other platforms
</details>
### - Deploy Manually
<details>
<summary>Manually deployment supports any platforms</summary>
- Python 3.9.x or higher
#### Configure Mirai
Follow [this tutorial(cn)](https://yiri-mirai.wybxc.cc/tutorials/01/configuration) to configure Mirai and YiriMirai.
After starting mirai-console, use the `login` command to log in to the QQ account, and keep the mirai-console running.
#### Configure QChatGPT
1. Clone the repository
```bash
git clone https://github.com/RockChinQ/QChatGPT
cd QChatGPT
```
2. Install dependencies
```bash
pip3 install requests yiri-mirai openai colorlog func_timeout dulwich Pillow
```
3. Generate `config.py`
```bash
python3 main.py
```
4. Edit `config.py`
5. Run
```bash
python3 main.py
```
Any problems, please refer to the issues page.
</details>
## 🚀Usage
**After deployment, please read: [Commands(cn)](https://github.com/RockChinQ/QChatGPT/wiki/%E5%8A%9F%E8%83%BD%E4%BD%BF%E7%94%A8#%E6%9C%BA%E5%99%A8%E4%BA%BA%E6%8C%87%E4%BB%A4)**
**For more details, please refer to the [Wiki(cn)](https://github.com/RockChinQ/QChatGPT/wiki/%E5%8A%9F%E8%83%BD%E4%BD%BF%E7%94%A8#%E4%BD%BF%E7%94%A8%E6%96%B9%E5%BC%8F)**
## 🧩Plugin Ecosystem
Plugin [usage](https://github.com/RockChinQ/QChatGPT/wiki/%E6%8F%92%E4%BB%B6%E4%BD%BF%E7%94%A8) and [development](https://github.com/RockChinQ/QChatGPT/wiki/%E6%8F%92%E4%BB%B6%E5%BC%80%E5%8F%91) are supported.
<details>
<summary>List of plugins (cn)</summary>
### Examples
`tests/plugin_examples`目录下,将其整个目录复制到`plugins`目录下即可使用
- `cmdcn` - 主程序指令中文形式
- `hello_plugin` - 在收到消息`hello`时回复相应消息
- `urlikethisijustsix` - 收到冒犯性消息时回复相应消息
### More Plugins
欢迎提交新的插件
- [revLibs](https://github.com/RockChinQ/revLibs) - 将ChatGPT网页版接入此项目关于[官方接口和网页版有什么区别](https://github.com/RockChinQ/QChatGPT/wiki/%E5%AE%98%E6%96%B9%E6%8E%A5%E5%8F%A3%E4%B8%8EChatGPT%E7%BD%91%E9%A1%B5%E7%89%88)
- [Switcher](https://github.com/RockChinQ/Switcher) - 支持通过指令切换使用的模型
- [hello_plugin](https://github.com/RockChinQ/hello_plugin) - `hello_plugin` 的储存库形式,插件开发模板
- [dominoar/QChatPlugins](https://github.com/dominoar/QchatPlugins) - dominoar编写的诸多新功能插件语音输出、Ranimg、屏蔽词规则等
- [dominoar/QCP-NovelAi](https://github.com/dominoar/QCP-NovelAi) - NovelAI 故事叙述与绘画
- [oliverkirk-sudo/chat_voice](https://github.com/oliverkirk-sudo/chat_voice) - 文字转语音输出使用HuggingFace上的[VITS-Umamusume-voice-synthesizer模型](https://huggingface.co/spaces/Plachta/VITS-Umamusume-voice-synthesizer)
- [RockChinQ/WaitYiYan](https://github.com/RockChinQ/WaitYiYan) - 实时获取百度`文心一言`等待列表人数
- [chordfish-k/QChartGPT_Emoticon_Plugin](https://github.com/chordfish-k/QChartGPT_Emoticon_Plugin) - 使机器人根据回复内容发送表情包
- [oliverkirk-sudo/ChatPoeBot](https://github.com/oliverkirk-sudo/ChatPoeBot) - 接入[Poe](https://poe.com/)上的机器人
- [lieyanqzu/WeatherPlugin](https://github.com/lieyanqzu/WeatherPlugin) - 天气查询插件
</details>
## 😘Thanks
- [@the-lazy-me](https://github.com/the-lazy-me) video tutorial creator
- [@mikumifa](https://github.com/mikumifa) Docker deployment
- [@dominoar](https://github.com/dominoar) Plugin development
- [@万神的星空](https://github.com/qq255204159) Packages publisher
- [@ljcduo](https://github.com/ljcduo) GPT-4 API internal test account
And all [contributors](https://github.com/RockChinQ/QChatGPT/graphs/contributors) and other friends who support this project.
<!-- ## 👍赞赏
<img alt="赞赏码" src="res/mm_reward_qrcode_1672840549070.png" width="400" height="400"/> -->

View File

@@ -33,9 +33,9 @@ mirai_http_api_config = {
# },
# "http_proxy": "http://127.0.0.1:12345"
# }
#
#
# 现已支持反向代理可以添加reverse_proxy字段以使用反向代理
# 使用反向代理可以在国内使用OpenAI的API反向代理的配置请参考
# 使用反向代理可以在国内使用OpenAI的API反向代理的配置请参考
# https://github.com/Ice-Hazymoon/openai-scf-proxy
#
# 反向代理填写示例:
@@ -63,7 +63,7 @@ admin_qq = 0
# 情景预设(机器人人格)
# 每个会话的预设信息,影响所有会话,无视指令重置
# 可以通过这个字段指定某些情况的回复,可直接用自然语言描述指令
# 例如:
# 例如:
# default_prompt = "如果我之后想获取帮助,请你说“输入!help获取帮助”"
# 这样用户在不知所措的时候机器人就会提示其输入!help获取帮助
# 可参考 https://github.com/PlexPt/awesome-chatgpt-prompts-zh
@@ -81,14 +81,14 @@ admin_qq = 0
# 例如:
# !reset linux-terminal
# 若不指定名称,则使用默认情景预设
#
#
# 也可以使用指令:
# !default <名称>
# 将指定的情景预设设置为默认情景预设
# 例如:
# !default linux-terminal
# 之后的会话重置时若不指定名称则使用linux-terminal情景预设
#
#
# 还可以加载文件中的预设文字使用方法请查看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": "如果我之后想获取帮助,请你说“输入!help获取帮助”",
@@ -160,7 +160,7 @@ prompt_submit_length = 2048
# OpenAI补全API的参数
# 请在下方填写模型,程序自动选择接口
# 现已支持的模型有:
#
#
# 'gpt-4'
# 'gpt-4-0314'
# 'gpt-4-32k'
@@ -212,6 +212,12 @@ blob_message_threshold = 256
# - "forward": 将长消息转换为转发消息组件发送
blob_message_strategy = "forward"
# 允许等待
# 同一会话内,是否等待上一条消息处理完成后再处理下一条消息
# 若设置为False若上一条未处理完时收到了新消息将会丢弃新消息
# 丢弃消息时的提示信息可以在tips.py中修改
wait_last_done = True
# 文字转图片时使用的字体文件路径
# 当策略为"image"时生效
# 若在Windows系统下程序会自动使用Windows自带的微软雅黑字体
@@ -226,11 +232,6 @@ retry_times = 3
# 设置为False时向用户及管理员发送错误详细信息
hide_exce_info_to_user = False
# 消息处理出错时向用户发送的提示信息
# 仅当hide_exce_info_to_user为True时生效
# 设置为空字符串时,不发送提示信息
alter_tip_message = '出错了,请稍后再试'
# 线程池相关配置
# 该参数决定机器人可以同时处理几个人的消息,超出线程池数量的请求会被阻塞,不会被丢弃
# 如果你不清楚该参数的意义,请不要更改
@@ -238,31 +239,49 @@ alter_tip_message = '出错了,请稍后再试'
sys_pool_num = 8
# 执行管理员请求和指令的线程池并行线程数量,一般和管理员数量相等
admin_pool_num = 2
admin_pool_num = 4
# 执行用户请求和指令的线程池并行线程数量
# 如需要更高的并发,可以增大该值
user_pool_num = 6
user_pool_num = 8
# 每个会话的过期时间,单位为秒
# 默认值20分钟
session_expire_time = 60 * 20
session_expire_time = 1200
# 会话限速
# 单会话内每分钟可进行的对话次数
# 若不需要限速,可以设置为一个很大的值
# 默认值60次基本上不会触发限速
rate_limitation = 60
#
# 若要设置针对某特定群的限速,请使用如下格式:
# {
# "group_<群号>": 60,
# "default": 60,
# }
# 若要设置针对某特定用户私聊的限速,请使用如下格式:
# {
# "person_<用户QQ>": 60,
# "default": 60,
# }
# 同时设置多个群和私聊的限速,示例:
# {
# "group_12345678": 60,
# "group_87654321": 60,
# "person_234567890": 60,
# "person_345678901": 60,
# "default": 60,
# }
#
# 注意: 未指定的都使用default的限速值default不可删除
rate_limitation = {
"default": 60,
}
# 会话限速策略
# - "wait": 每次对话获取到回复时,等待一定时间再发送回复,保证其不会超过限速均值
# - "drop": 此分钟内,若对话次数超过限速次数,则丢弃之后的对话,每自然分钟重置
rate_limit_strategy = "wait"
# drop策略时超过限速均值时丢弃的对话的提示信息
# 仅当rate_limitation_strategy为"drop"时生效
# 若设置为空字符串,则不发送提示信息
rate_limit_drop_tip = "本分钟对话次数超过限速次数,此对话被丢弃"
rate_limit_strategy = "drop"
# 是否在启动时进行依赖库更新
upgrade_dependencies = True
@@ -274,8 +293,3 @@ report_usage = True
# 日志级别
logging_level = logging.INFO
# 定制帮助消息
help_message = """此机器人通过调用大型语言模型生成回复,不具有情感。
你可以用自然语言与其交流,回复的消息中[GPT]开头的为模型生成的语言,[bot]开头的为程序提示。
欢迎到github.com/RockChinQ/QChatGPT 给个star"""

133
main.py
View File

@@ -11,6 +11,8 @@ import traceback
sys.path.append(".")
from pkg.utils.log import init_runtime_log_file, reset_logging
try:
import colorlog
except ImportError:
@@ -18,6 +20,7 @@ except ImportError:
import pkg.utils.pkgmgr as pkgmgr
try:
pkgmgr.install_requirements("requirements.txt")
pkgmgr.install_upgrade("websockets")
import colorlog
except ImportError:
print("依赖不满足,请查看 https://github.com/RockChinQ/qcg-installer/issues/15")
@@ -30,13 +33,9 @@ from urllib3.exceptions import InsecureRequestWarning
import pkg.utils.context
log_colors_config = {
'DEBUG': 'green', # cyan white
'INFO': 'white',
'WARNING': 'yellow',
'ERROR': 'red',
'CRITICAL': 'cyan',
}
# 是否使用override.json覆盖配置
# 仅在启动时提供 --override 或 -r 参数时生效
use_override = False
def init_db():
@@ -55,59 +54,23 @@ def ensure_dependencies():
known_exception_caught = False
log_file_name = "qchatgpt.log"
def init_runtime_log_file():
"""为此次运行生成日志文件
格式: qchatgpt-yyyy-MM-dd-HH-mm-ss.log
"""
global log_file_name
# 检查logs目录是否存在
if not os.path.exists("logs"):
os.mkdir("logs")
# 检查本目录是否有qchatgpt.log若有移动到logs目录
if os.path.exists("qchatgpt.log"):
shutil.move("qchatgpt.log", "logs/qchatgpt.legacy.log")
log_file_name = "logs/qchatgpt-%s.log" % time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime())
def reset_logging():
global log_file_name
def override_config():
import config
if pkg.utils.context.context['logger_handler'] is not None:
logging.getLogger().removeHandler(pkg.utils.context.context['logger_handler'])
for handler in logging.getLogger().handlers:
logging.getLogger().removeHandler(handler)
logging.basicConfig(level=config.logging_level, # 设置日志输出格式
filename=log_file_name, # log日志输出的文件位置和文件名
format="[%(asctime)s.%(msecs)03d] %(filename)s (%(lineno)d) - [%(levelname)s] : %(message)s",
# 日志输出的格式
# -8表示占位符让输出左对齐输出长度都为8位
datefmt="%Y-%m-%d %H:%M:%S" # 时间输出的格式
)
sh = logging.StreamHandler()
sh.setLevel(config.logging_level)
sh.setFormatter(colorlog.ColoredFormatter(
fmt="%(log_color)s[%(asctime)s.%(msecs)03d] %(filename)s (%(lineno)d) - [%(levelname)s] : "
"%(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
log_colors=log_colors_config
))
logging.getLogger().addHandler(sh)
pkg.utils.context.context['logger_handler'] = sh
return sh
# 检查override.json覆盖
if os.path.exists("override.json") and use_override:
override_json = json.load(open("override.json", "r", encoding="utf-8"))
for key in override_json:
if hasattr(config, key):
setattr(config, key, override_json[key])
logging.info("覆写配置[{}]为[{}]".format(key, override_json[key]))
else:
logging.error("无法覆写配置[{}]为[{}]该配置不存在请检查override.json是否正确".format(key, override_json[key]))
# 临时函数用于加载config和上下文未来统一放在config类
def load_config():
logging.info("检查config模块完整性.")
# 完整性校验
is_integrity = True
config_template = importlib.import_module('config-template')
@@ -119,26 +82,37 @@ def load_config():
is_integrity = False
if not is_integrity:
logging.warning("配置文件不完整,依据config-template.py检查config.py")
logging.warning("配置文件不完整,您可以依据config-template.py检查config.py")
# 检查override.json覆盖
if os.path.exists("override.json"):
override_json = json.load(open("override.json", "r", encoding="utf-8"))
for key in override_json:
if hasattr(config, key):
setattr(config, key, override_json[key])
logging.info("覆写配置[{}]为[{}]".format(key, override_json[key]))
else:
logging.error("无法覆写配置[{}]为[{}]该配置不存在请检查override.json是否正确".format(key, override_json[key]))
override_config()
if not is_integrity:
logging.warning("以上配置已被设为默认值,将在5秒后继续启动... ")
time.sleep(5)
logging.warning("以上不存在的配置已被设为默认值,将在3秒后继续启动... ")
time.sleep(3)
# 存进上下文
pkg.utils.context.set_config(config)
def complete_tips():
"""根据tips-custom-template模块补全tips模块的属性"""
is_integrity = True
logging.info("检查tips模块完整性.")
tips_template = importlib.import_module('tips-custom-template')
tips = importlib.import_module('tips')
for key in dir(tips_template):
if not key.startswith("__") and not hasattr(tips, key):
setattr(tips, key, getattr(tips_template, key))
logging.warning("[{}]不存在".format(key))
is_integrity = False
if not is_integrity:
logging.warning("tips模块不完整您可以依据tips-custom-template.py检查tips.py")
logging.warning("以上配置已被设为默认值将在3秒后继续启动... ")
time.sleep(3)
def start(first_time_init=False):
"""启动流程reload之后会被执行"""
@@ -329,8 +303,9 @@ def start(first_time_init=False):
try:
import pkg.utils.announcement as announcement
new_announcement = announcement.fetch_new()
if new_announcement != "":
logging.critical("[公告] {}".format(new_announcement))
if len(new_announcement) > 0:
for announcement in new_announcement:
logging.critical("[公告]<{}> {}".format(announcement['time'], announcement['content']))
except Exception as e:
logging.warning("获取公告失败:{}".format(e))
@@ -356,12 +331,6 @@ def stop():
def check_file():
# 配置文件存在性校验
if not os.path.exists('config.py'):
shutil.copy('config-template.py', 'config.py')
print('请先在config.py中填写配置')
sys.exit(0)
# 检查是否有banlist.py,如果没有就把banlist-template.py复制一份
if not os.path.exists('banlist.py'):
shutil.copy('res/templates/banlist-template.py', 'banlist.py')
@@ -378,6 +347,10 @@ def check_file():
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/")
@@ -388,8 +361,19 @@ def check_file():
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():
global use_override
# 检查是否携带了 --override 或 -r 参数
if '--override' in sys.argv or '-r' in sys.argv:
use_override = True
# 初始化相关文件
check_file()
@@ -401,6 +385,9 @@ def main():
load_config()
config = pkg.utils.context.get_config()
# 检查tips模块
complete_tips()
# 配置线程池
from pkg.utils import ThreadCtl
thread_ctl = ThreadCtl(

View File

@@ -60,19 +60,19 @@
"show_prefix": false,
"blob_message_threshold": 256,
"blob_message_strategy": "forward",
"wait_last_done": true,
"font_path": "",
"retry_times": 3,
"hide_exce_info_to_user": false,
"alter_tip_message": "出错了,请稍后再试",
"sys_pool_num": 8,
"admin_pool_num": 2,
"user_pool_num": 6,
"admin_pool_num": 4,
"user_pool_num": 8,
"session_expire_time": 1200,
"rate_limitation": 60,
"rate_limit_strategy": "wait",
"rate_limit_drop_tip": "本分钟对话次数超过限速次数,此对话被丢弃",
"rate_limitation": {
"default": 60
},
"rate_limit_strategy": "drop",
"upgrade_dependencies": true,
"report_usage": true,
"logging_level": 20,
"help_message": "此机器人通过调用大型语言模型生成回复,不具有情感。\n你可以用自然语言与其交流回复的消息中[GPT]开头的为模型生成的语言,[bot]开头的为程序提示。\n欢迎到github.com/RockChinQ/QChatGPT 给个star"
"logging_level": 20
}

View File

@@ -69,7 +69,7 @@ class ModelRequest:
self.error_info = "{}\n该错误可能是由于http_proxy格式设置错误引起的"
except Exception as e:
self.error_info = "{}\n由于请求异常产生的未知错误,请查看日志".format(e)
raise Exception(self.error_info)
raise type(e)(self.error_info)
def request(self, **kwargs):
"""向接口发起请求"""

View File

@@ -156,8 +156,8 @@ def install_plugin(repo_url: str):
import pkg.utils.pkgmgr
pkg.utils.pkgmgr.install_requirements("plugins/"+repo_url.split(".git")[0].split("/")[-1]+"/requirements.txt")
import main
main.reset_logging()
import pkg.utils.log as log
log.reset_logging()
def uninstall_plugin(plugin_name: str) -> str:

View File

@@ -9,6 +9,9 @@ import json
__command_list__ = {}
import tips as tips_custom
"""命令树
结构:
@@ -262,7 +265,7 @@ def execute(context: Context) -> list:
# 检查权限
if ctx.privilege < node['privilege']:
raise CommandPrivilegeError('权限不足: {}'.format(path))
raise CommandPrivilegeError(tips_custom.command_admin_message+"{}".format(path))
# 执行
execed, reply = node['cls'].process(ctx)
@@ -275,7 +278,7 @@ def execute(context: Context) -> list:
path = path + '.' + ctx.crt_command
except KeyError:
traceback.print_exc()
raise CommandPrivilegeError('找不到指令: {}'.format(path))
raise CommandPrivilegeError(tips_custom.command_err_message+"{}".format(path))
def register_all():

View File

@@ -117,8 +117,8 @@ class PluginUpdateCommand(AbstractCommandNode):
import pkg.utils.pkgmgr
pkg.utils.pkgmgr.install_requirements("/".join(plugin['path'].split('/')[:-1])+"/requirements.txt")
import main
main.reset_logging()
import pkg.utils.log as log
log.reset_logging()
pkg.utils.context.get_qqbot_manager().notify_admin("已更新插件: {}".format(", ".join(updated)))
except Exception as e:

View File

@@ -1,4 +1,5 @@
from ..aamgr import AbstractCommandNode, Context
import tips as tips_custom
import pkg.openai.session
import pkg.utils.context
@@ -22,12 +23,12 @@ class ResetCommand(AbstractCommandNode):
if len(params) == 0:
pkg.openai.session.get_session(session_name).reset(explicit=True)
reply = ["[bot]会话已重置"]
reply = [tips_custom.command_reset_message]
else:
try:
import pkg.openai.dprompt as dprompt
pkg.openai.session.get_session(session_name).reset(explicit=True, use_prompt=params[0])
reply = ["[bot]会话已重置,使用场景预设:{}".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:
reply = ["[bot]会话重置失败:{}".format(e)]

View File

@@ -0,0 +1,84 @@
from ..aamgr import AbstractCommandNode, Context
import json
def config_operation(cmd, params):
reply = []
import pkg.utils.context
config = pkg.utils.context.get_config()
reply_str = ""
if len(params) == 0:
reply = ["[bot]err:请输入配置项"]
else:
cfg_name = params[0]
if cfg_name == 'all':
reply_str = "[bot]所有配置项:\n\n"
for cfg in dir(config):
if not cfg.startswith('__') and not cfg == 'logging':
# 根据配置项类型进行格式化如果是字典则转换为json并格式化
if isinstance(getattr(config, cfg), str):
reply_str += "{}: \"{}\"\n".format(cfg, getattr(config, cfg))
elif isinstance(getattr(config, cfg), dict):
# 不进行unicode转义并格式化
reply_str += "{}: {}\n".format(cfg,
json.dumps(getattr(config, cfg),
ensure_ascii=False, indent=4))
else:
reply_str += "{}: {}\n".format(cfg, getattr(config, cfg))
reply = [reply_str]
elif cfg_name in dir(config):
if len(params) == 1:
# 按照配置项类型进行格式化
if isinstance(getattr(config, cfg_name), str):
reply_str = "[bot]配置项{}: \"{}\"\n".format(cfg_name, getattr(config, cfg_name))
elif isinstance(getattr(config, cfg_name), dict):
reply_str = "[bot]配置项{}: {}\n".format(cfg_name,
json.dumps(getattr(config, cfg_name),
ensure_ascii=False, indent=4))
else:
reply_str = "[bot]配置项{}: {}\n".format(cfg_name, getattr(config, cfg_name))
reply = [reply_str]
else:
cfg_value = " ".join(params[1:])
# 类型转换如果是json则转换为字典
if cfg_value == 'true':
cfg_value = True
elif cfg_value == 'false':
cfg_value = False
elif cfg_value.isdigit():
cfg_value = int(cfg_value)
elif cfg_value.startswith('{') and cfg_value.endswith('}'):
cfg_value = json.loads(cfg_value)
else:
try:
cfg_value = float(cfg_value)
except ValueError:
pass
# 检查类型是否匹配
if isinstance(getattr(config, cfg_name), type(cfg_value)):
setattr(config, cfg_name, cfg_value)
pkg.utils.context.set_config(config)
reply = ["[bot]配置项{}修改成功".format(cfg_name)]
else:
reply = ["[bot]err:配置项{}类型不匹配".format(cfg_name)]
else:
reply = ["[bot]err:未找到配置项 {}".format(cfg_name)]
return reply
@AbstractCommandNode.register(
parent=None,
name="cfg",
description="配置项管理",
usage="!cfg <配置项> [配置值]\n!cfg all",
aliases=[],
privilege=2
)
class CfgCommand(AbstractCommandNode):
@classmethod
def process(cls, ctx: Context) -> tuple[bool, list]:
return True, config_operation(ctx.command, ctx.params)

View File

@@ -1,39 +0,0 @@
from ..aamgr import AbstractCommandNode, Context, __command_list__
@AbstractCommandNode.register(
parent=None,
name="cmd",
description="显示指令列表",
usage="!help\n!help <指令名称>",
aliases=[],
privilege=1
)
class CmdCommand(AbstractCommandNode):
@classmethod
def process(cls, ctx: Context) -> tuple[bool, list]:
command_list = __command_list__
reply = []
if len(ctx.params) == 0:
reply_str = "[bot]当前所有指令:\n\n"
# 遍历顶级指令
for key in command_list:
command = command_list[key]
if command['parent'] is None:
reply_str += "!{} - {}\n".format(key, command['description'])
reply_str += "\n请使用 !cmd <指令名称> 来查看指令的详细信息"
reply = [reply_str]
else:
command_name = ctx.params[0]
if command_name in command_list:
reply = [command_list[command_name]['cls'].help()]
else:
reply = ["[bot]指令 {} 不存在".format(command_name)]
return True, reply

View File

@@ -12,8 +12,13 @@ from ..aamgr import AbstractCommandNode, Context
class HelpCommand(AbstractCommandNode):
@classmethod
def process(cls, ctx: Context) -> tuple[bool, list]:
import tips
reply = ["[bot] "+tips.help_message + "\n请输入 !cmd 查看指令列表"]
# 警告config.help_message过时
import config
reply = [(config.help_message) + "\n请输入 !cmds 查看指令列表"]
if hasattr(config, "help_message"):
reply[0] += "\n\n警告config.py中的help_message已过时不再生效请使用tips.py中的help_message替代"
return True, reply

View File

@@ -54,7 +54,7 @@ def process_command(session_name: str, text_message: str, mgr, config,
try:
reply = cmdmgr.execute(context)
except cmdmgr.CommandPrivilegeError as e:
reply = ["[bot]err:{}".format(e)]
reply = ["{}".format(e)]
return reply
except Exception as e:

View File

@@ -19,6 +19,7 @@ import pkg.utils.context
import pkg.plugin.host as plugin_host
import pkg.plugin.models as plugin_models
import tips as tips_custom
# 检查消息是否符合泛响应匹配机制
@@ -264,7 +265,7 @@ class QQBotManager:
if failed == self.retry:
pkg.openai.session.get_session('person_{}'.format(event.sender.id)).release_response_lock()
self.notify_admin("{} 请求超时".format("person_{}".format(event.sender.id)))
reply = ["[bot]err:请求超时"]
reply = [tips_custom.reply_message]
if reply:
return self.send(event, reply, check_quote=False)
@@ -304,7 +305,7 @@ class QQBotManager:
if failed == self.retry:
pkg.openai.session.get_session('group_{}'.format(event.group.id)).release_response_lock()
self.notify_admin("{} 请求超时".format("group_{}".format(event.group.id)))
replys = ["[bot]err:请求超时"]
replys = [tips_custom.replys_message]
return replys

View File

@@ -7,6 +7,7 @@ import pkg.openai.session
import pkg.plugin.host as plugin_host
import pkg.plugin.models as plugin_models
import pkg.qqbot.blob as blob
import tips as tips_custom
def handle_exception(notify_admin: str = "", set_reply: str = "") -> list:
@@ -14,7 +15,7 @@ def handle_exception(notify_admin: str = "", set_reply: str = "") -> list:
import config
pkg.utils.context.get_qqbot_manager().notify_admin(notify_admin)
if config.hide_exce_info_to_user:
return [config.alter_tip_message] if config.alter_tip_message else []
return [tips_custom.alter_tip_message] if tips_custom.alter_tip_message else []
else:
return [set_reply]

View File

@@ -27,6 +27,7 @@ import pkg.plugin.models as plugin_models
import pkg.qqbot.ignore as ignore
import pkg.qqbot.banlist as banlist
import pkg.qqbot.blob as blob
import tips as tips_custom
processing = []
@@ -58,6 +59,11 @@ def process_message(launcher_type: str, launcher_id: int, text_message: str, mes
logging.info("根据忽略规则忽略消息: {}".format(text_message))
return []
import config
if not config.wait_last_done and session_name in processing:
return MessageChain([Plain(tips_custom.message_drop_tip)])
# 检查是否被禁言
if launcher_type == 'group':
result = mgr.bot.member_info(target=launcher_id, member_id=mgr.bot.qq).get()
@@ -78,9 +84,6 @@ def process_message(launcher_type: str, launcher_id: int, text_message: str, mes
# 处理消息
try:
if session_name in processing:
pkg.openai.session.get_session(session_name).release_response_lock()
return MessageChain([Plain("[bot]err:正在处理中,请稍后再试")])
config = pkg.utils.context.get_config()
@@ -118,7 +121,8 @@ def process_message(launcher_type: str, launcher_id: int, text_message: str, mes
if config.rate_limit_strategy == "drop":
if ratelimit.is_reach_limit(session_name):
logging.info("根据限速策略丢弃[{}]消息: {}".format(session_name, text_message))
return MessageChain(["[bot]"+config.rate_limit_drop_tip]) if config.rate_limit_drop_tip != "" else []
return MessageChain(["[bot]"+tips_custom.rate_limit_drop_tip]) if tips_custom.rate_limit_drop_tip != "" else []
before = time.time()
# 触发插件事件

View File

@@ -10,6 +10,20 @@ __crt_minute_usage__ = {}
__timer_thr__: threading.Thread = None
def get_limitation(session_name: str) -> int:
"""获取会话的限制次数"""
import config
if type(config.rate_limitation) == dict:
# 如果被指定了
if session_name in config.rate_limitation:
return config.rate_limitation[session_name]
else:
return config.rate_limitation["default"]
elif type(config.rate_limitation) == int:
return config.rate_limitation
def add_usage(session_name: str):
"""增加会话的对话次数"""
global __crt_minute_usage__
@@ -56,9 +70,7 @@ def get_rest_wait_time(session_name: str, spent: float) -> float:
"""获取会话此回合的剩余等待时间"""
global __crt_minute_usage__
import config
min_seconds_per_round = 60.0 / config.rate_limitation
min_seconds_per_round = 60.0 / get_limitation(session_name)
if session_name in __crt_minute_usage__:
return max(0, min_seconds_per_round - spent)
@@ -70,10 +82,8 @@ def is_reach_limit(session_name: str) -> bool:
"""判断会话是否超过限制"""
global __crt_minute_usage__
import config
if session_name in __crt_minute_usage__:
return __crt_minute_usage__[session_name] >= config.rate_limitation
return __crt_minute_usage__[session_name] >= get_limitation(session_name)
else:
return False

View File

@@ -1,47 +1,68 @@
import base64
import os
import json
import requests
import pkg.utils.network as network
def read_latest() -> str:
def read_latest() -> list:
import pkg.utils.network as network
resp = requests.get(
url="https://api.github.com/repos/RockChinQ/QChatGPT/contents/res/announcement",
url="https://api.github.com/repos/RockChinQ/QChatGPT/contents/res/announcement.json",
proxies=network.wrapper_proxies()
)
obj_json = resp.json()
b64_content = obj_json["content"]
# 解码
content = base64.b64decode(b64_content).decode("utf-8")
return content
return json.loads(content)
def read_saved() -> str:
def read_saved() -> list:
# 已保存的在res/announcement_saved
# 检查是否存在
if not os.path.exists("res/announcement_saved"):
with open("res/announcement_saved", "w", encoding="utf-8") as f:
f.write("")
if not os.path.exists("res/announcement_saved.json"):
with open("res/announcement_saved.json", "w", encoding="utf-8") as f:
f.write("[]")
with open("res/announcement_saved", "r", encoding="utf-8") as f:
with open("res/announcement_saved.json", "r", encoding="utf-8") as f:
content = f.read()
return content
return json.loads(content)
def write_saved(content: str):
def write_saved(content: list):
# 已保存的在res/announcement_saved
with open("res/announcement_saved", "w", encoding="utf-8") as f:
f.write(content)
with open("res/announcement_saved.json", "w", encoding="utf-8") as f:
f.write(json.dumps(content, indent=4, ensure_ascii=False))
def fetch_new() -> str:
def fetch_new() -> list:
latest = read_latest()
saved = read_saved()
if latest.replace(saved, "").strip() == "":
return ""
else:
write_saved(latest)
return latest.replace(saved, "").strip()
to_show: list = []
for item in latest:
# 遍历saved检查是否有相同id的公告
for saved_item in saved:
if saved_item["id"] == item["id"]:
break
else:
# 没有相同id的公告
to_show.append(item)
write_saved(latest)
return to_show
if __name__ == '__main__':
resp = requests.get(
url="https://api.github.com/repos/RockChinQ/QChatGPT/contents/res/announcement.json",
)
obj_json = resp.json()
b64_content = obj_json["content"]
# 解码
content = base64.b64decode(b64_content).decode("utf-8")
print(json.dumps(json.loads(content), indent=4, ensure_ascii=False))

File diff suppressed because one or more lines are too long

66
pkg/utils/log.py Normal file
View File

@@ -0,0 +1,66 @@
import os
import time
import logging
import shutil
log_file_name = "qchatgpt.log"
log_colors_config = {
'DEBUG': 'green', # cyan white
'INFO': 'white',
'WARNING': 'yellow',
'ERROR': 'red',
'CRITICAL': 'cyan',
}
def init_runtime_log_file():
"""为此次运行生成日志文件
格式: qchatgpt-yyyy-MM-dd-HH-mm-ss.log
"""
global log_file_name
# 检查logs目录是否存在
if not os.path.exists("logs"):
os.mkdir("logs")
# 检查本目录是否有qchatgpt.log若有移动到logs目录
if os.path.exists("qchatgpt.log"):
shutil.move("qchatgpt.log", "logs/qchatgpt.legacy.log")
log_file_name = "logs/qchatgpt-%s.log" % time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime())
def reset_logging():
global log_file_name
import config
import pkg.utils.context
import colorlog
if pkg.utils.context.context['logger_handler'] is not None:
logging.getLogger().removeHandler(pkg.utils.context.context['logger_handler'])
for handler in logging.getLogger().handlers:
logging.getLogger().removeHandler(handler)
logging.basicConfig(level=config.logging_level, # 设置日志输出格式
filename=log_file_name, # log日志输出的文件位置和文件名
format="[%(asctime)s.%(msecs)03d] %(filename)s (%(lineno)d) - [%(levelname)s] : %(message)s",
# 日志输出的格式
# -8表示占位符让输出左对齐输出长度都为8位
datefmt="%Y-%m-%d %H:%M:%S" # 时间输出的格式
)
sh = logging.StreamHandler()
sh.setLevel(config.logging_level)
sh.setFormatter(colorlog.ColoredFormatter(
fmt="%(log_color)s[%(asctime)s.%(msecs)03d] %(filename)s (%(lineno)d) - [%(levelname)s] : "
"%(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
log_colors=log_colors_config
))
logging.getLogger().addHandler(sh)
pkg.utils.context.context['logger_handler'] = sh
return sh

View File

@@ -1,21 +1,25 @@
from pip._internal import main as pipmain
import main
import pkg.utils.log as log
def install(package):
pipmain(['install', package])
main.reset_logging()
log.reset_logging()
def install_upgrade(package):
pipmain(['install', '--upgrade', package])
log.reset_logging()
def run_pip(params: list):
pipmain(params)
main.reset_logging()
log.reset_logging()
def install_requirements(file):
pipmain(['install', '-r', file, "--upgrade"])
main.reset_logging()
log.reset_logging()
def ensure_dulwich():

View File

@@ -41,15 +41,20 @@ def reload_all(notify=True):
importlib.reload(__import__('config'))
importlib.reload(__import__('main'))
importlib.reload(__import__('banlist'))
importlib.reload(__import__('tips'))
context.context = this_context
# 重载插件
import plugins
walk(plugins)
# 初始化相关文件
main.check_file()
# 执行启动流程
logging.info("执行程序启动流程")
main.load_config()
main.complete_tips()
context.get_thread_ctl().reload(
admin_pool_num=context.get_config().admin_pool_num,
user_pool_num=context.get_config().user_pool_num

View File

@@ -1,9 +1,9 @@
requests~=2.28.1
openai~=0.27.2
openai~=0.27.4
dulwich~=0.21.3
colorlog~=6.6.0
yiri-mirai~=0.2.6.1
websockets~=10.4
websockets
urllib3~=1.26.10
func_timeout~=4.3.5
Pillow

1
res/announcement.json Normal file
View File

@@ -0,0 +1 @@
[]

View File

@@ -13,5 +13,5 @@ for key in cmdsmgr.__command_list__:
template[key] = cmdsmgr.__command_list__[key]['privilege']
# 写入cmdpriv-template.json
with open('cmdpriv-template.json', 'w') as f:
with open('res/templates/cmdpriv-template.json', 'w') as f:
f.write(json.dumps(template, indent=4, ensure_ascii=False))

View File

@@ -0,0 +1,32 @@
# 输出工作路径
import os
print("工作路径: " + os.getcwd())
announcement = input("请输入公告内容: ")
import json
# 读取现有的公告文件 res/announcement.json
with open("res/announcement.json", "r", encoding="utf-8") as f:
announcement_json = json.load(f)
# 将公告内容写入公告文件
# 当前自然时间
import time
now = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
# 获取最后一个公告的id
last_id = announcement_json[-1]["id"] if len(announcement_json) > 0 else -1
announcement = {
"id": last_id + 1,
"time": now,
"timestamp": int(time.time()),
"content": announcement
}
announcement_json.append(announcement)
# 将公告写入公告文件
with open("res/announcement.json", "w", encoding="utf-8") as f:
json.dump(announcement_json, f, indent=4, ensure_ascii=False)

View File

@@ -19,6 +19,7 @@
"prompt": 1,
"resend": 1,
"reset": 1,
"cfg": 2,
"cmd": 1,
"help": 1,
"reload": 2,

View File

@@ -2,7 +2,7 @@
## 简介
调用OpenAI官方提供的API接口结合mirai和YiriMirai框架将QQ消息与语言模型连接实现更加智能的对话机器人
调用OpenAI官方提供的API接口结合mirai和YiriMirai框架将QQ消息与语言模型连接实现更加智能的对话机器人
## 技术栈

View File

@@ -96,6 +96,12 @@
- “丢弃”策略:此分钟内对话次数达到限制时,丢弃之后的对话
- 详细请查看config.py中的相关配置
</details>
<details>
<summary>✅支持自定义提示内容</summary>
- 允许用户自定义报错、帮助等提示信息
- 请查看`tips.py`
</details>
## 限制

34
tips-custom-template.py Normal file
View File

@@ -0,0 +1,34 @@
import config
# ---------------------------------------------自定义提示语---------------------------------------------
# 消息处理出错时向用户发送的提示信息仅当config.py中hide_exce_info_to_user为True时生效
# 设置为空字符串时,不发送提示信息
alter_tip_message = '[bot]err:出错了,请稍后再试'
# drop策略时超过限速均值时丢弃的对话的提示信息仅当config.py中rate_limitation_strategy为"drop"时生效
# 若设置为空字符串,则不发送提示信息
rate_limit_drop_tip = "本分钟对话次数超过限速次数,此对话被丢弃"
# 只允许同时处理一条消息时,新消息被丢弃时的提示信息
# 当config.py中的wait_last_done为False时生效
# 若设置为空字符串,则不发送提示信息
message_drop_tip = "[bot]当前有一条消息正在处理,请等待处理完成"
# 指令help帮助消息
help_message = """此机器人通过调用大型语言模型生成回复,不具有情感。
你可以用自然语言与其交流,回复的消息中[GPT]开头的为模型生成的语言,[bot]开头的为程序提示。
欢迎到github.com/RockChinQ/QChatGPT 给个star"""
# 私聊消息超时提示
reply_message = "[bot]err:请求超时"
# 群聊消息超时提示
replys_message = "[bot]err:请求超时"
# 指令权限不足提示
command_admin_message = "[bot]err:权限不足: "
# 指令无效提示
command_err_message = "[bot]err:指令执行出错:"
# 会话重置提示
command_reset_message = "[bot]:会话已重置"
command_reset_name_message = "[bot]:会话已重置,使用场景预设:"