Compare commits

..

116 Commits

Author SHA1 Message Date
Soulter
a78ebf2fd7 feat: plugin dev mode 2023-05-14 18:20:28 +08:00
Soulter
bd11541678 perf: 优化插件更新缓存策略 2023-05-14 18:16:12 +08:00
Soulter
0d99aa81e6 Merge branch 'master' of https://github.com/Soulter/QQChannelChatGPT 2023-05-14 17:40:16 +08:00
Soulter
f104d40d0a perf: 优化插件加载规则、更新插件接口规范
fix: 修复发言频率限制报错的问题
2023-05-14 17:40:13 +08:00
Soulter
0d69f8ab8a Update README.md 2023-05-13 17:03:32 +08:00
Soulter
66d1fc08b6 perf: 去除不必要的import 2023-05-13 14:25:16 +08:00
Soulter
e32fc27728 perf: 优化插件的删除逻辑 2023-05-13 14:23:05 +08:00
Soulter
eec890cd02 perf: 优化插件指令的卸载插件逻辑 2023-05-13 14:20:38 +08:00
Soulter
d30881e59b perf: 补充插件指令的帮助信息 2023-05-13 14:10:00 +08:00
Soulter
9afaf83368 perf: 优化插件指令的身份组鉴定 2023-05-13 14:07:40 +08:00
Soulter
33f9a9cfa0 feat: 支持显示插件列表和插件帮助 2023-05-13 14:04:15 +08:00
Soulter
bf72d5fa27 fix: 修复有依赖的插件拉取问题 2023-05-13 13:55:22 +08:00
Soulter
567c29bcd6 perf: 清除多余log 2023-05-13 13:45:04 +08:00
Soulter
dcdfe453fb perf: 优化插件类鉴定规则 2023-05-13 13:44:14 +08:00
Soulter
0d23c0900b perf: 优化插件卸载逻辑 2023-05-13 13:28:18 +08:00
Soulter
86eda7bdf8 perf: 优化插件缓存逻辑 2023-05-13 13:25:56 +08:00
Soulter
1e46525b0f perf: 优化pip更新逻辑 2023-05-13 13:02:24 +08:00
Soulter
8d41efea4d Update README.md 2023-05-13 11:43:03 +08:00
Soulter
f15d0eb0eb Update README.md 2023-05-13 11:10:56 +08:00
Soulter
1795362bcd perf: 优化插件调用逻辑 2023-05-13 11:03:16 +08:00
Soulter
2bf9c82617 perf: 更好的插件处理逻辑和更开放的插件功能 2023-05-13 10:54:57 +08:00
Soulter
33793a2053 chore: 更新版本号 2023-05-12 09:21:57 +08:00
Soulter
656fe14af4 perf: 优化身份组鉴定 2023-05-12 09:15:32 +08:00
Soulter
46197d49a4 perf: 调换语言模型启动顺序 2023-05-12 09:08:06 +08:00
Soulter
843ab56f50 perf: 优化身份组 2023-05-12 09:02:29 +08:00
Soulter
6b4b52f3c5 perf: 完善身份组功能 2023-05-11 22:56:38 +08:00
Soulter
392e5cd592 chore: 添加默认插件 2023-05-11 22:43:26 +08:00
Soulter
d273019830 chore: 删除一些没必要的文件 2023-05-11 22:39:55 +08:00
Soulter
fd59ec4b6c fix: 修复插件指令clone插件异常的问题 2023-05-11 22:12:39 +08:00
Soulter
bf33ccafca fix: 修复插件指令创建文件夹出错的问题 2023-05-11 22:06:38 +08:00
Soulter
425936872d fix: 修复插件指令结果显示异常的问题 2023-05-11 22:01:57 +08:00
Soulter
6627b2e1e5 fix: 修复插件指令报错的问题 2023-05-11 22:00:18 +08:00
Soulter
323c2cecf8 feat: 新增插件指令 2023-05-11 21:52:44 +08:00
Soulter
5b1dd3dce9 feat: 插件支持 2023-05-11 21:35:25 +08:00
Soulter
54af770dfb fix: 修复keyword指令的一些问题 2023-05-08 20:30:36 +08:00
Soulter
30a48fea6e feat: QQ群的免@唤醒支持多个前缀(nick指令) #92 2023-05-08 20:17:51 +08:00
Soulter
cfd5fb1452 perf: keyword指令支持删除关键词 2023-05-08 19:43:26 +08:00
Soulter
a78984376f perf: 优化与go-cqhttp的通信 2023-04-26 14:52:09 +08:00
Soulter
9887cae43c fix: replit web fix 2023-04-25 20:57:22 +08:00
Soulter
e63fe60f8d Merge branch 'master' of https://github.com/Soulter/QQChannelChatGPT 2023-04-25 20:48:08 +08:00
Soulter
b0ac2d676c feat: Replit平台支持 2023-04-25 20:48:04 +08:00
Soulter
5ef515165c Merge pull request #94 from RockChinQ/patch-1
chore: 更换使用nakuru-project-idk
2023-04-25 19:43:02 +08:00
Rock Chin
e21d43f920 chore: 更换使用nakuru-project-idk 2023-04-25 12:46:41 +08:00
Soulter
3a80ffad88 perf: 优化控制台信息显示 2023-04-25 10:42:03 +08:00
Soulter
47506d60cd perf: 优化pip检测 2023-04-25 10:29:16 +08:00
Soulter
b999b712b7 perf: 优化逆向库的错误管理 2023-04-25 10:21:12 +08:00
Soulter
6860ba3f05 Merge branch 'master' of https://github.com/Soulter/QQChannelChatGPT 2023-04-25 09:38:21 +08:00
Soulter
02594867c0 fix: 修复QQ平台昵称后的消息前导空格问题 2023-04-25 09:38:17 +08:00
Soulter
250435f3e7 Update requirements.txt 2023-04-25 09:29:35 +08:00
Soulter
3c593fb6f7 Update README.md 2023-04-24 19:34:11 +08:00
Soulter
807cad5c48 fix: 删除启动时对qq频道appid不应该的检查 2023-04-24 08:00:45 +00:00
Soulter
e92ecdd3f8 Update README.md 2023-04-23 17:16:31 +08:00
Soulter
1c91079d8f Merge branch 'master' of https://github.com/Soulter/QQChannelChatGPT 2023-04-23 09:05:32 +00:00
Soulter
376b2fef40 fix: 修复启动前检查依赖的问题 2023-04-23 09:05:30 +00:00
Soulter
300f3b6df8 Update README.md 2023-04-23 16:52:07 +08:00
Soulter
6e6f6d5cd4 Update README.md 2023-04-23 16:51:46 +08:00
Soulter
077e54d0f1 Merge branch 'master' of https://github.com/Soulter/QQChannelChatGPT 2023-04-23 08:35:02 +00:00
Soulter
18ffaa2b91 perf: 优化各种报错管理;
feat: 启动时检查依赖库
2023-04-23 08:31:22 +00:00
Soulter
a6555681a0 Update requirements.txt 2023-04-23 15:31:33 +08:00
Soulter
43ac0ef87c fix: remove judge_res 2023-04-22 08:09:22 +00:00
Soulter
754842be7c Update README.md 2023-04-22 14:35:22 +08:00
Soulter
5b3ee2dbe8 fix: 修复回复内容屏蔽词无效的问题 2023-04-22 06:07:33 +00:00
Soulter
ca5a1ddc0b perf: 优化bing模型达到单次会话上限后自动重置 2023-04-22 11:27:43 +08:00
Soulter
c9821132ad fix: QQ频道停止信息来源显示 2023-04-21 11:11:16 +08:00
Soulter
0641dca2a6 fix: 修复qqAt的一些问题 2023-04-21 01:04:57 +08:00
Soulter
fd983b9f5d fix: 修复了一些问题 2023-04-21 01:01:54 +08:00
Soulter
7e1e51c450 feat: QQ支持at发送方和画画指令支持 2023-04-21 01:00:31 +08:00
Soulter
d912b990e4 fix: 修复画画指令失效的问题 2023-04-21 00:45:59 +08:00
Soulter
8224aa87a5 fix: 修复bing模型不想继续会话自动重置的一些问题 2023-04-20 09:07:55 +08:00
Soulter
4cb5abc7b6 fix: 修复bing会话超时过期的问题 2023-04-20 08:59:58 +08:00
Soulter
743a800b0d perf: 删除一些不必要的log 2023-04-19 17:23:24 +08:00
Soulter
a5c43612bf feat: bing模型支持显示信息来源 #70 2023-04-19 17:22:59 +08:00
Soulter
e2bd612b8e perf: 优化语言模型载入流程 2023-04-19 17:01:13 +08:00
Soulter
3ddb65e399 fix: 修复bing获取消息数量报错的bug 2023-04-19 16:52:16 +08:00
Soulter
56775580fc feat: bing模型添加单次会话消息条数显示 2023-04-19 16:34:38 +08:00
Soulter
8f7703c158 perf: 强化超出会话限制后自动重置;其他优化 2023-04-19 16:25:07 +08:00
Soulter
7aba9ff3ff fix: 细化依赖库版本 2023-04-18 21:35:09 +08:00
Soulter
aea1271a94 perf: 取消程序启动多余的依赖库检查 2023-04-18 21:30:51 +08:00
Soulter
b575f195c9 Merge branch 'master' of https://github.com/Soulter/QQChannelChatGPT 2023-04-18 10:54:21 +08:00
Soulter
1eedf7b332 fix: 取消切换模型的权限限制 2023-04-18 10:54:16 +08:00
Soulter
d327a1041b Update README.md 2023-04-17 14:40:34 +08:00
Soulter
10a3ba7dd4 Update README.md 2023-04-15 21:44:11 +08:00
Soulter
deaa4ea910 Create CODE_OF_CONDUCT.md 2023-04-15 15:33:00 +08:00
Soulter
fbfceb3137 fix: 将除去昵称和@后,/开头的消息都视为指令 2023-04-14 22:43:15 +08:00
Soulter
e7b9d7cd54 feat: QQ平台支持自定义昵称指令。使用格式: nick 新昵称。默认是ai 2023-04-14 21:54:56 +08:00
Soulter
34aba58351 feat: 更新help说明 2023-04-14 21:10:39 +08:00
Soulter
e1639be6c3 Merge branch 'master' of https://github.com/Soulter/QQChannelChatGPT 2023-04-14 21:05:42 +08:00
Soulter
80975c5715 perf: 重做help指令模块 2023-04-14 21:05:39 +08:00
Soulter
c12a4f7353 Merge branch 'master' of https://github.com/Soulter/QQChannelChatGPT 2023-04-14 20:49:12 +08:00
Soulter
defab688e5 Merge branch 'master' of https://github.com/Soulter/QQChannelChatGPT 2023-04-14 20:32:51 +08:00
Soulter
2244386d33 feat: 针对QQ平台增加私聊模式 #86 2023-04-14 20:31:52 +08:00
Soulter
39244fa27f Update README.md 2023-04-11 19:40:14 +08:00
Soulter
20c19905ac fix: 修复update文本显示的问题 2023-04-11 17:54:24 +08:00
Soulter
8086d645f9 perf: 优化update指令:更新成功后不执行重启命令,需要使用update r重启 2023-04-11 17:50:09 +08:00
Soulter
3a3289bf04 perf: QQ平台@优化 2023-04-11 17:39:29 +08:00
Soulter
1711ff3bb5 fix: bugfixes 2023-04-11 10:53:48 +08:00
Soulter
b945913f88 fix: 修复了一些其他问题 2023-04-11 10:50:41 +08:00
Soulter
d31533ed82 fix: 修复QQ平台@时使用不了指令的问题 2023-04-11 10:49:10 +08:00
Soulter
0fb2ec2c76 fix: 修复QQ平台@的一些问题 2023-04-11 10:44:01 +08:00
Soulter
89847cbc83 fix: 修复@机器人的已知问题 2023-04-11 10:36:05 +08:00
Soulter
9d12bb23fd fix: 修复@机器人的已知问题 2023-04-11 10:33:46 +08:00
Soulter
79af4ce381 fix: 修复一些已知问题 2023-04-11 10:28:29 +08:00
Soulter
79f293e248 fix: 修复QQ平台@机器人不回复的问题 2023-04-11 10:26:52 +08:00
Soulter
e75a0fec01 Merge branch 'master' of https://github.com/Soulter/QQChannelChatGPT 2023-04-11 10:23:29 +08:00
Soulter
a935b085d4 perf: QQ平台支持@机器人时回复 2023-04-11 10:23:25 +08:00
Soulter
4ef0a14420 Update README.md 2023-04-11 09:40:38 +08:00
Soulter
8273154904 Update README.md 2023-04-11 09:40:09 +08:00
Soulter
71d6ef3b52 Update README.md 2023-04-11 09:37:14 +08:00
Soulter
119b3a090a Update README.md 2023-04-11 09:34:45 +08:00
Soulter
496df3347b Update README.md 2023-04-11 09:33:58 +08:00
Soulter
2b70eef35b fix: 修复启动时的依赖库更新流程;优化update latest指令 2023-04-10 22:40:26 +08:00
Soulter
c4071eedf8 perf: 优化update指令显示 2023-04-10 22:37:08 +08:00
Soulter
b6cc866113 Merge branch 'master' of https://github.com/Soulter/QQChannelChatGPT 2023-04-10 22:32:31 +08:00
Soulter
6aabcdeac7 perf: bing模型不再显示正忙,会自动逐个回复。 2023-04-10 22:32:24 +08:00
Soulter
72bccee9e2 Update README.md 2023-04-10 21:54:42 +08:00
Soulter
b54f934fcd Update README.md 2023-04-10 01:07:04 +08:00
18 changed files with 1218 additions and 287 deletions

128
CODE_OF_CONDUCT.md Normal file
View File

@@ -0,0 +1,128 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
SoulterL@outlook.com.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.

228
README.md
View File

@@ -1,27 +1,40 @@
## ⭐体验
<div align="center">
使用手机QQ扫码加入QQ频道(频道名: GPT机器人 | 频道号: x42d56aki2)
<img src="https://socialify.git.ci/Soulter/QQChannelChatGPT/image?description=1&forks=1&issues=1&language=1&name=1&owner=1&pattern=Circuit%20Board&stargazers=1&theme=Light" alt="QQChannelChatGPT" width="600" height="300" />
<img src="https://user-images.githubusercontent.com/37870767/230417115-9dd3c9d5-6b6b-4928-8fe3-82f559208aab.JPG" width="200"></img>
[![Language](https://img.shields.io/badge/language-python-green.svg?style=plastic)](https://www.python.org/)
[![License](https://img.shields.io/badge/license-AGPL3-orange.svg?style=plastic)](https://github.com/Soulter/QQChannelChatGPT/blob/master/LICENSE)
![Python](https://img.shields.io/badge/python-3.9+-blue)
_✨在QQ和QQ频道上使用ChatGPT、NewBing等语言模型稳定一次部署同时使用✨_
**Windows用户推荐Windows一键安装请前往Release下载最新版本Beta**
有报错请先看issue解决不了再在频道内反馈。
_✨教程https://github.com/Soulter/QQChannelChatGPT/wiki ✨_
_✨插件开发教程https://github.com/Soulter/QQChannelChatGPT/wiki/%E5%9B%9B%E3%80%81%E5%BC%80%E5%8F%91%E6%8F%92%E4%BB%B6 ✨_
_✨欢迎体验😊频道名: GPT机器人 | 频道号: x42d56aki2) | QQ群号322154837✨_
<!-- <img src="https://user-images.githubusercontent.com/37870767/230417115-9dd3c9d5-6b6b-4928-8fe3-82f559208aab.JPG" width="300"></img> -->
</div>
## ⭐功能:
近期新功能:
- 支持插件https://github.com/Soulter/QQChannelChatGPT/wiki/%E5%9B%9B%E3%80%81%E5%BC%80%E5%8F%91%E6%8F%92%E4%BB%B6
- 支持一键切换语言模型(使用/bing /revgpt /gpt分别可以切换newbing、逆向ChatGPT、官方ChatGPT模型
- 热更新
- 接入QQ支持在QQ上和QQ频道上同时聊天https://github.com/Soulter/QQChannelChatGPT/issues/82
- 更强大的Windows启动器环境配置自动搞定。链接https://github.com/Soulter/QQChatGPTLauncher/releases/latest
支持的AI语言模型请在`configs/config.yaml`下配置):
- 逆向ChatGPT库
- 官方ChatGPT AI
- 文心一言(即将支持链接https://github.com/Soulter/ERNIEBot 欢迎Star
- 文心一言(即将支持)
- NewBing
- Bard (即将支持)
部署QQ频道机器人教程链接https://soulter.top/posts/qpdg.html
部署此项目的教程链接https://github.com/Soulter/QQChannelChatGPT/wiki
### 基本功能
<details>
@@ -80,8 +93,22 @@
</details>
> 关于tokentoken就相当于是AI中的单词数但是不等于单词数`text-davinci-003`模型中最大可以支持`4097`个token。在发送信息时这个机器人会将用户的历史聊天记录打包发送给ChatGPT因此`token`也会相应的累加为了保证聊天的上下文的逻辑性就有了缓存token。
### 🛠️ 插件支持
本项目支持接入插件。
插件开发教程https://github.com/Soulter/QQChannelChatGPT/wiki/%E5%9B%9B%E3%80%81%E5%BC%80%E5%8F%91%E6%8F%92%E4%BB%B6
部分好用的插件:
`HuggingChat`: https://github.com/Soulter/HuggingChatForQQBot
### 指令功能
需要先`@`机器人之后再输入指令
#### OpenAI官方API
在频道内需要先`@`机器人之后再输入指令在QQ中暂时需要在消息前加上`ai `,不需要@
- `/reset`重置prompt
- `/his`查看历史记录(每个用户都有独立的会话)
- `/his [页码数]`查看不同页码的历史记录。例如`/his 2`查看第2页
@@ -91,33 +118,194 @@
- `/help` 查看帮助
- `/key` 动态添加key
- `/set` 人格设置面板
- `/keyword nihao 你好` 设置关键词回复。nihao->你好
- `/bing` 切换为bing
- `/revgpt` 切换为ChatGPT逆向库
- `/画` 画画
#### Bing语言模型
- `/reset`重置prompt
- `/gpt` 切换为OpenAI官方API
- `/revgpt` 切换为ChatGPT逆向库
#### 逆向ChatGPT库语言模型
- `/gpt` 切换为OpenAI官方API
- `/bing` 切换为bing
* 切换模型指令支持临时回复。如`/bing 你好`将会临时使用一次bing模型
## 📰使用方法:
**详细部署教程链接**https://soulter.top/posts/qpdg.html
### 安装第三方库
**Windows用户推荐Windows一键安装请前往Release下载最新版本Beta**
有报错请先看issue解决不了再在频道内反馈。
使用Python的pip工具安装
- `qq-botpy` QQ频道官方Python SDK
- `openai` (OpenAI Python SDK)
### 安装第三方库
```shell
pip install -r requirements.txt
```
> 注意由于qq-botpy库需要运行在`Python 3.8+`的版本上,因此本项目也需要在此之上运行
> Python版本应>=3.9
### 配置
- 获得 OpenAI的key [OpenAI](https://beta.openai.com/)
- 获得 QQ开放平台下QQ频道机器人的token和appid [QQ开放平台](https://q.qq.com/)一个QQ频道机器人很容易创建~
- 在configs/config.yaml下进行配置
**详细部署教程链接**https://github.com/Soulter/QQChannelChatGPT/wiki
### 启动
- 启动main.py
## DEMO
![1.jpg](screenshots/1.jpg)
![3.jpg](screenshots/3.jpg)
![2.jpg](screenshots/2.jpg)
## 感谢
本项目使用了一下项目:
[ChatGPT by acheong08](https://github.com/acheong08/ChatGPT)
[EdgeGPT by acheong08](https://github.com/acheong08/EdgeGPT)
[go-cqhttp by Mrs4s](https://github.com/Mrs4s/go-cqhttp)
[nakuru-project by Lxns-Network](https://github.com/Lxns-Network/nakuru-project)
## ⚙配置文件说明:
```yaml
# 如果你不知道怎么部署请查看https://github.com/Soulter/QQChannelChatGPT/wiki
# 不一定需要key了如果你没有key但有openAI账号或者必应账号可以考虑使用下面的逆向库
###############平台设置#################
# QQ频道机器人
# QQ开放平台的appid和令牌
# q.qq.com
# enable为true则启用false则不启用
qqbot:
enable: true
appid:
token:
# QQ机器人
# enable为true则启用false则不启用
# 需要安装GO-CQHTTP配合使用。
# 文档https://docs.go-cqhttp.org/
# 请将go-cqhttp的配置文件的sever部分粘贴为以下内容否则无法使用
# 请先启动go-cqhttp再启动本程序
#
# servers:
# - http:
# host: 127.0.0.1
# version: 0
# port: 5700
# timeout: 5
# - ws:
# address: 127.0.0.1:6700
# middlewares:
# <<: *default
gocqbot:
enable: false
# 设置是否一个人一个会话
uniqueSessionMode: false
# QChannelBot 的版本请勿修改此字段否则可能产生一些bug
version: 3.0
# [Beta] 转储历史记录时间间隔(分钟)
dump_history_interval: 10
# 一个用户只能在time秒内发送count条消息
limit:
time: 60
count: 5
# 公告
notice: "此机器人由Github项目QQChannelChatGPT驱动。"
# 是否打开私信功能
# 设置为true则频道成员可以私聊机器人。
# 设置为false则频道成员不能私聊机器人。
direct_message_mode: true
# 系统代理
# http_proxy: http://localhost:7890
# https_proxy: http://localhost:7890
# 自定义回复前缀,如[Rev]或其他务必加引号以防止不必要的bug。
reply_prefix:
openai_official: "[GPT]"
rev_chatgpt: "[Rev]"
rev_edgegpt: "[RevBing]"
# 百度内容审核服务
# 新用户免费5万次调用。https://cloud.baidu.com/doc/ANTIPORN/index.html
baidu_aip:
enable: false
app_id:
api_key:
secret_key:
###############语言模型设置#################
# OpenAI官方API
# 注意已支持多key自动切换方法
# key:
# - sk-xxxxxx
# - sk-xxxxxx
# 在下方非注释的地方使用以上格式
# 关于api_base可以使用一些云函数如腾讯、阿里来避免国内被墙的问题。
# 详见:
# https://github.com/Ice-Hazymoon/openai-scf-proxy
# https://github.com/Soulter/QQChannelChatGPT/issues/42
# 设置为none则表示使用官方默认api地址
openai:
key:
-
api_base: none
# 这里是GPT配置语言模型默认使用gpt-3.5-turbo
chatGPTConfigs:
model: gpt-3.5-turbo
max_tokens: 3000
temperature: 0.9
top_p: 1
frequency_penalty: 0
presence_penalty: 0
total_tokens_limit: 5000
# 逆向文心一言【暂时不可用,请勿使用】
rev_ernie:
enable: false
# 逆向New Bing
# 需要在项目根目录下创建cookies.json并粘贴cookies进去。
# 详见https://soulter.top/posts/qpdg.html
rev_edgegpt:
enable: false
# 逆向ChatGPT库
# https://github.com/acheong08/ChatGPT
# 优点:免费(无免费额度限制);
# 缺点速度相对慢。OpenAI 速率限制:免费帐户每小时 50 个请求。您可以通过多帐户循环来绕过它
# enable设置为true后将会停止使用上面正常的官方API调用而使用本逆向项目
#
# 多账户可以保证每个请求都能得到及时的回复。
# 关于account的格式
# account:
# - email: 第1个账户
# password: 第1个账户密码
# - email: 第2个账户
# password: 第2个账户密码
# - ....
# 支持使用access_token登录
# 例:
# - session_token: xxxxx
# - access_token: xxxx
# 请严格按照上面这个格式填写。
# 逆向ChatGPT库的email-password登录方式不工作建议使用access_token登录
# 获取access_token的方法详见https://soulter.top/posts/qpdg.html
rev_ChatGPT:
enable: false
account:
- access_token:
```

View File

@@ -0,0 +1,5 @@
# helloworld
QQChannelChatGPT项目的测试插件
A test plugin for QQChannelChatGPT plugin feature

View File

@@ -0,0 +1,65 @@
from nakuru.entities.components import *
from nakuru import (
GroupMessage,
FriendMessage
)
from botpy.message import Message, DirectMessage
class HelloWorldPlugin:
"""
初始化函数, 可以选择直接pass
"""
def __init__(self) -> None:
print("这是HelloWorld测试插件, 发送 helloworld 即可触发此插件。")
"""
入口函数,机器人会调用此函数。
参数规范: message: 消息文本; role: 身份; platform: 消息平台; message_obj: 消息对象
参数详情: role为admin或者member; platform为qqchan或者gocq; message_obj为nakuru的GroupMessage对象或者FriendMessage对象或者频道的Message, DirectMessage对象。
返回规范: bool: 是否hit到此插件(所有的消息均会调用每一个载入的插件, 如果没有hit到, 则应返回False)
Tuple: None或者长度为3的元组。当没有hit到时, 返回None. hit到时, 第1个参数为指令是否调用成功, 第2个参数为返回的消息文本或者gocq的消息链列表, 第3个参数为指令名称
例子:做一个名为"yuanshen"的插件;当接收到消息为“原神 可莉”, 如果不想要处理此消息则返回False, None如果想要处理但是执行失败了返回True, tuple([False, "请求失败啦~", "yuanshen"])
执行成功了返回True, tuple([True, "结果文本", "yuanshen"])
"""
def run(self, message: str, role: str, platform: str, message_obj):
if platform == "gocq":
"""
QQ平台指令处理逻辑
"""
img_url = "https://gchat.qpic.cn/gchatpic_new/905617992/720871955-2246763964-C6EE1A52CC668EC982453065C4FA8747/0?term=2&amp;is_origin=0"
if message == "helloworld":
return True, tuple([True, [Plain("Hello World!!"), Image.fromURL(url=img_url)], "helloworld"])
else:
return False, None
elif platform == "qqchan":
"""
频道处理逻辑(频道暂时只支持回复字符串类型的信息返回的信息都会被转成字符串如果不想处理某一个平台的信息直接返回False, None就行)
"""
if message == "helloworld":
return True, tuple([True, "Hello World!!", "helloworld"])
else:
return False, None
"""
帮助函数,当用户输入 plugin v 插件名称 时,会调用此函数,返回帮助信息
返回参数要求(必填)dict{
"name": str, # 插件名称
"desc": str, # 插件简短描述
"help": str, # 插件帮助信息
"version": str, # 插件版本
"author": str, # 插件作者
}
"""
def info(self):
return {
"name": "helloworld",
"desc": "测试插件",
"help": "测试插件, 回复helloworld即可触发",
"version": "v1.0.1 beta",
"author": "Soulter"
}
# 热知识:检测消息开头指令,使用以下方法
# if message.startswith("原神"):
# pass

View File

@@ -1,8 +1,7 @@
import botpy
from botpy.message import Message
from botpy.message import Message, DirectMessage
from botpy.types.message import Reference
import re
from botpy.message import DirectMessage
import json
import threading
import asyncio
@@ -18,8 +17,11 @@ from model.platform.qq import QQ
from nakuru import (
CQHTTP,
GroupMessage,
GroupMemberIncrease,
FriendMessage
)
from nakuru.entities.components import Plain
from nakuru.entities.components import Plain,At
from model.command.command import Command
# QQBotClient实例
client = ''
@@ -60,7 +62,7 @@ direct_message_mode = True
abs_path = os.path.dirname(os.path.realpath(sys.argv[0])) + '/'
# 版本
version = '3.0'
version = '3.0.2'
# 语言模型
REV_CHATGPT = 'rev_chatgpt'
@@ -89,20 +91,40 @@ qqchan_loop = None
# QQ机器人
gocq_bot = None
PLATFORM_GOCQ = 'gocq'
gocq_app = CQHTTP(
host="127.0.0.1",
port=6700,
http_port=5700,
)
gocq_app = None
admin_qq = "123456"
gocq_loop = None
nick_qq = "ai"
bing_cache_loop = None
# 插件
cached_plugins = {}
def gocq_runner():
global gocq_app
ok = False
while not ok:
try:
gocq_app = CQHTTP(
host="127.0.0.1",
port=6700,
http_port=5700,
)
ok = True
except BaseException as e:
print("[System-err] 连接到go-cqhttp异常, 5秒后重试。"+str(e))
threading.Thread(target=gocq_runner, daemon=True).start()
def new_sub_thread(func, args=()):
thread = threading.Thread(target=func, args=args, daemon=True)
thread.start()
# 写入统计信息
def toggle_count(at: bool, message):
global stat_file
@@ -165,36 +187,47 @@ def upload():
def initBot(cfg, prov):
global chatgpt, provider, rev_chatgpt, baidu_judge, rev_edgegpt, chosen_provider
global reply_prefix, gpt_config, config, uniqueSession, frequency_count, frequency_time,announcement, direct_message_mode, version
global command_openai_official, command_rev_chatgpt, command_rev_edgegpt,reply_prefix, keywords
global command_openai_official, command_rev_chatgpt, command_rev_edgegpt,reply_prefix, keywords, cached_plugins
provider = prov
config = cfg
if 'reply_prefix' in cfg:
reply_prefix = cfg['reply_prefix']
# 语言模型提供商
print("--------------------加载语言模型--------------------")
if REV_CHATGPT in prov:
if 'account' in cfg['rev_ChatGPT']:
from model.provider.provider_rev_chatgpt import ProviderRevChatGPT
from model.command.command_rev_chatgpt import CommandRevChatGPT
rev_chatgpt = ProviderRevChatGPT(cfg['rev_ChatGPT'])
command_rev_chatgpt = CommandRevChatGPT(cfg['rev_ChatGPT'])
chosen_provider = REV_CHATGPT
else:
input("[System-err] 请退出本程序, 然后在配置文件中填写rev_ChatGPT相关配置")
print("- 逆向ChatGPT库 -")
if cfg['rev_ChatGPT']['enable']:
if 'account' in cfg['rev_ChatGPT']:
from model.provider.provider_rev_chatgpt import ProviderRevChatGPT
from model.command.command_rev_chatgpt import CommandRevChatGPT
rev_chatgpt = ProviderRevChatGPT(cfg['rev_ChatGPT'])
command_rev_chatgpt = CommandRevChatGPT(cfg['rev_ChatGPT'])
chosen_provider = REV_CHATGPT
else:
input("[System-err] 请退出本程序, 然后在配置文件中填写rev_ChatGPT相关配置")
if REV_EDGEGPT in prov:
from model.provider.provider_rev_edgegpt import ProviderRevEdgeGPT
from model.command.command_rev_edgegpt import CommandRevEdgeGPT
rev_edgegpt = ProviderRevEdgeGPT()
command_rev_edgegpt = CommandRevEdgeGPT(rev_edgegpt)
chosen_provider = REV_EDGEGPT
print("- New Bing -")
if not os.path.exists('./cookies.json'):
input("[System-err] 导入Bing模型时发生错误, 没有找到cookies文件或者cookies文件放置位置错误。windows启动器启动的用户请把cookies.json文件放到和启动器相同的目录下。\n如何获取请看https://github.com/Soulter/QQChannelChatGPT仓库介绍。")
else:
if cfg['rev_edgegpt']['enable']:
from model.provider.provider_rev_edgegpt import ProviderRevEdgeGPT
from model.command.command_rev_edgegpt import CommandRevEdgeGPT
rev_edgegpt = ProviderRevEdgeGPT()
command_rev_edgegpt = CommandRevEdgeGPT(rev_edgegpt)
chosen_provider = REV_EDGEGPT
if OPENAI_OFFICIAL in prov:
from model.provider.provider_openai_official import ProviderOpenAIOfficial
from model.command.command_openai_official import CommandOpenAIOfficial
chatgpt = ProviderOpenAIOfficial(cfg['openai'])
command_openai_official = CommandOpenAIOfficial(chatgpt)
chosen_provider = OPENAI_OFFICIAL
print("- OpenAI ChatGPT官方API -")
if cfg['openai']['key'] is not None:
from model.provider.provider_openai_official import ProviderOpenAIOfficial
from model.command.command_openai_official import CommandOpenAIOfficial
chatgpt = ProviderOpenAIOfficial(cfg['openai'])
command_openai_official = CommandOpenAIOfficial(chatgpt)
chosen_provider = OPENAI_OFFICIAL
print("--------------------加载个性化配置--------------------")
# 得到关键词
if os.path.exists("keyword.json"):
with open("keyword.json", 'r', encoding='utf-8') as f:
@@ -255,18 +288,64 @@ def initBot(cfg, prov):
if 'dump_history_interval' in cfg:
print("[System] 历史记录转储时间周期: " + cfg['dump_history_interval'] + "分钟")
except BaseException:
print("[System-Error] 读取uniqueSessionMode/version/dump_history_interval配置文件失败, 使用默认值。")
pass
print(f"[System] QQ开放平台AppID: {cfg['qqbot']['appid']} 令牌: {cfg['qqbot']['token']}")
print("\n[System] 如果有任何问题, 请在 https://github.com/Soulter/QQChannelChatGPT 上提交issue说明问题或者添加QQ905617992")
print("[System] 请给 https://github.com/Soulter/QQChannelChatGPT 点个star!")
if chosen_provider is None:
print("[System-Warning] 检测到没有启动任何一个语言模型。请至少在配置文件中启用一个语言模型。")
# 得到指令设置(cmd_config.json)
if os.path.exists("cmd_config.json"):
with open("cmd_config.json", 'r', encoding='utf-8') as f:
cmd_config = json.load(f)
# QQ机器人昵称
if 'nick_qq' in cmd_config:
global nick_qq
nick_qq = cmd_config['nick_qq']
thread_inst = None
print("--------------------加载插件--------------------")
# 加载插件
_command = Command(None)
ok, err = _command.plugin_reload(cached_plugins)
if ok:
print("加载插件完成")
else:
print(err)
print("--------------------加载平台--------------------")
# GOCQ
if 'gocqbot' in cfg and cfg['gocqbot']['enable']:
print("- 启用QQ机器人 -")
if os.path.exists("cmd_config.json"):
with open("cmd_config.json", 'r', encoding='utf-8') as f:
cmd_config = json.load(f)
global admin_qq
if "admin_qq" in cmd_config:
admin_qq = cmd_config['admin_qq']
print("[System] 管理者QQ号: " + admin_qq)
else:
admin_qq = input("[System] 请输入管理者QQ号(管理者QQ号才能使用update/plugin等指令): ")
print("[System] 管理者QQ号设置为: " + admin_qq)
cmd_config['admin_qq'] = admin_qq
with open("cmd_config.json", 'w', encoding='utf-8') as f:
json.dump(cmd_config, f, indent=4)
f.flush()
global gocq_app, gocq_bot, gocq_loop
gocq_bot = QQ()
gocq_loop = asyncio.new_event_loop()
thread_inst = threading.Thread(target=run_gocq_bot, args=(gocq_loop, gocq_bot, gocq_app), daemon=False)
thread_inst.start()
# QQ频道
if 'qqbot' in cfg and cfg['qqbot']['enable']:
print("[System] 启用QQ频道机器人")
print("- 启用QQ频道机器人 -")
global qqchannel_bot, qqchan_loop
qqchannel_bot = QQChan()
qqchan_loop = asyncio.new_event_loop()
@@ -274,15 +353,6 @@ def initBot(cfg, prov):
thread_inst.start()
# thread.join()
# GOCQ
if 'gocqbot' in cfg and cfg['gocqbot']['enable']:
print("[System] 启用QQ机器人")
global gocq_app, gocq_bot, gocq_loop
gocq_bot = QQ()
gocq_loop = asyncio.new_event_loop()
thread_inst = threading.Thread(target=run_gocq_bot, args=(gocq_loop, gocq_bot, gocq_app), daemon=False)
thread_inst.start()
if thread_inst == None:
input("[System-Error] 没有启用任何机器人,程序退出")
exit()
@@ -340,15 +410,25 @@ def save_provider_preference(chosen_provider):
def send_message(platform, message, res, msg_ref = None, image = None, gocq_loop = None, qqchannel_bot = None, gocq_bot = None):
if platform == PLATFORM_QQCHAN:
if image != None:
qqchannel_bot.send_qq_msg(message, res, image_mode=True, msg_ref=msg_ref)
qqchannel_bot.send_qq_msg(message, str(res), image_mode=True, msg_ref=msg_ref)
else:
qqchannel_bot.send_qq_msg(message, res, msg_ref=msg_ref)
if platform == PLATFORM_GOCQ: asyncio.run_coroutine_threadsafe(gocq_bot.send_qq_msg(message, res), gocq_loop).result()
qqchannel_bot.send_qq_msg(message, str(res), msg_ref=msg_ref)
if platform == PLATFORM_GOCQ:
if image != None:
asyncio.run_coroutine_threadsafe(gocq_bot.send_qq_msg(message, image, image_mode=True), gocq_loop).result()
else:
asyncio.run_coroutine_threadsafe(gocq_bot.send_qq_msg(message, res, False, ), gocq_loop).result()
'''
处理消息
'''
def oper_msg(message, at=False, msg_ref = None, platform = None):
def oper_msg(message,
group: bool=False,
msg_ref: Reference = None,
platform: str = None):
"""
处理消息。
group: 群聊模式,
message: 频道是频道的消息对象, QQ是nakuru-gocq的消息对象
"""
global session_dict, provider
qq_msg = ''
session_id = ''
@@ -358,6 +438,7 @@ def oper_msg(message, at=False, msg_ref = None, platform = None):
role = "member" # 角色
hit = False # 是否命中指令
command_result = () # 调用指令返回的结果
global admin_qq, cached_plugins
if platform == PLATFORM_QQCHAN:
print("[QQCHAN-BOT] 接收到消息:"+ str(message.content))
@@ -365,18 +446,25 @@ def oper_msg(message, at=False, msg_ref = None, platform = None):
user_name = message.author.username
global qqchan_loop
if platform == PLATFORM_GOCQ:
print("[GOCQ-BOT] 接收到消息:"+ str(message.message[0].text))
user_id = message.group_id
user_name = message.group_id
if isinstance(message.message[0], Plain):
print("[GOCQ-BOT] 接收到消息:"+ str(message.message[0].text))
elif isinstance(message.message[0], At):
print("[GOCQ-BOT] 接收到消息:"+ str(message.message[1].text))
user_id = message.user_id
user_name = message.user_id
global gocq_loop
if chosen_provider is None:
send_message(platform, message, f"没有启动任何一个语言模型。请至少在配置文件中启用一个语言模型。", msg_ref=msg_ref, gocq_loop=gocq_loop, qqchannel_bot=qqchannel_bot, gocq_bot=gocq_bot)
# 检查发言频率
if not check_frequency(user_id):
qqchannel_bot.send_qq_msg(message, f'{user_name}的发言超过频率限制(╯▔皿▔)╯。\n{frequency_time}秒内只能提问{frequency_count}次。')
send_message(platform, message, f'的发言超过频率限制(╯▔皿▔)╯。\n管理员设置{frequency_time}秒内只能提问{frequency_count}次。', msg_ref=msg_ref, gocq_loop=gocq_loop, qqchannel_bot=qqchannel_bot, gocq_bot=gocq_bot)
return
if platform == PLATFORM_QQCHAN:
if at:
if group:
# 频道内
# 过滤@
qq_msg = message.content
@@ -400,14 +488,26 @@ def oper_msg(message, at=False, msg_ref = None, platform = None):
session_id = user_id
if platform == PLATFORM_GOCQ:
if isinstance(message.message[0], Plain):
qq_msg = message.message[0].text
if group:
if isinstance(message.message[0], Plain):
qq_msg = str(message.message[0].text).strip()
elif isinstance(message.message[0], At):
qq_msg = str(message.message[1].text).strip()
else:
return
session_id = message.group_id
# todo: 暂时将所有人设为管理员
role = "admin"
else:
return
qq_msg = message.message[0].text
session_id = message.user_id
role = "member"
if str(message.sender.user_id) == admin_qq:
print("[GOCQ-BOT] 检测到管理员身份")
role = "admin"
if qq_msg == "":
send_message(platform, message, f"Hi~", msg_ref=msg_ref, gocq_loop=gocq_loop, qqchannel_bot=qqchannel_bot, gocq_bot=gocq_bot)
return
logf.write("[QQBOT] "+ qq_msg+'\n')
logf.flush()
@@ -446,9 +546,9 @@ def oper_msg(message, at=False, msg_ref = None, platform = None):
chosen_provider = target
qq_msg = l[1]
else:
if role != "admin":
send_message(platform, message, "你没有权限更换语言模型。", msg_ref=msg_ref, gocq_loop=gocq_loop, qqchannel_bot=qqchannel_bot, gocq_bot=gocq_bot)
return
# if role != "admin":
# send_message(platform, message, "你没有权限更换语言模型。", msg_ref=msg_ref, gocq_loop=gocq_loop, qqchannel_bot=qqchannel_bot, gocq_bot=gocq_bot)
# return
chosen_provider = target
save_provider_preference(chosen_provider)
send_message(platform, message, f"已切换至【{chosen_provider}", msg_ref=msg_ref, gocq_loop=gocq_loop, qqchannel_bot=qqchannel_bot, gocq_bot=gocq_bot)
@@ -456,10 +556,9 @@ def oper_msg(message, at=False, msg_ref = None, platform = None):
chatgpt_res = ""
if chosen_provider == OPENAI_OFFICIAL:
hit, command_result = command_openai_official.check_command(qq_msg, session_id, user_name, role)
print(f"{hit} {command_result}")
# hit: 是否触发了指令.
if chosen_provider == OPENAI_OFFICIAL:
hit, command_result = command_openai_official.check_command(qq_msg, session_id, user_name, role, platform=platform, message_obj=message, cached_plugins=cached_plugins)
# hit: 是否触发了指令
if not hit:
# 请求ChatGPT获得结果
try:
@@ -467,19 +566,19 @@ def oper_msg(message, at=False, msg_ref = None, platform = None):
if OPENAI_OFFICIAL in reply_prefix:
chatgpt_res = reply_prefix[OPENAI_OFFICIAL] + chatgpt_res
except (BaseException) as e:
print("[System-Err] OpenAI API错误。原因如下:\n"+str(e))
send_message(platform, message, f"OpenAI API错误。原因如下:\n{str(e)} \n前往官方频道反馈~", msg_ref=msg_ref, gocq_loop=gocq_loop, qqchannel_bot=qqchannel_bot, gocq_bot=gocq_bot)
print("[System-Err] OpenAI API请求错误, 原因: "+str(e))
send_message(platform, message, f"OpenAI API错误, 原因: {str(e)}", msg_ref=msg_ref, gocq_loop=gocq_loop, qqchannel_bot=qqchannel_bot, gocq_bot=gocq_bot)
elif chosen_provider == REV_CHATGPT:
hit, command_result = command_rev_chatgpt.check_command(qq_msg, role)
hit, command_result = command_rev_chatgpt.check_command(qq_msg, role, platform=platform, message=message, cached_plugins=cached_plugins)
if not hit:
try:
chatgpt_res = str(rev_chatgpt.text_chat(qq_msg))
if REV_CHATGPT in reply_prefix:
chatgpt_res = reply_prefix[REV_CHATGPT] + chatgpt_res
except BaseException as e:
print("[System-Err] Rev ChatGPT API错误。原因如下:\n"+str(e))
send_message(platform, message, f"Rev ChatGPT API错误。原因如下\n{str(e)} \n前往官方频道反馈~", msg_ref=msg_ref, gocq_loop=gocq_loop, qqchannel_bot=qqchannel_bot, gocq_bot=gocq_bot)
print("[System-Err] RevChatGPT请求错误, 原因: "+str(e))
send_message(platform, message, f"RevChatGPT错误, 原因: \n{str(e)}", msg_ref=msg_ref, gocq_loop=gocq_loop, qqchannel_bot=qqchannel_bot, gocq_bot=gocq_bot)
elif chosen_provider == REV_EDGEGPT:
if bing_cache_loop == None:
@@ -487,23 +586,24 @@ def oper_msg(message, at=False, msg_ref = None, platform = None):
bing_cache_loop = gocq_loop
elif platform == PLATFORM_QQCHAN:
bing_cache_loop = qqchan_loop
hit, command_result = command_rev_edgegpt.check_command(qq_msg, bing_cache_loop, role)
hit, command_result = command_rev_edgegpt.check_command(qq_msg, bing_cache_loop, role, platform=platform, message_obj=message, cached_plugins=cached_plugins)
if not hit:
try:
if rev_edgegpt.is_busy():
send_message(platform, message, "[RevBing] 正忙,请稍后再试", msg_ref=msg_ref, gocq_loop=gocq_loop, qqchannel_bot=qqchannel_bot, gocq_bot=gocq_bot)
else:
res, res_code = asyncio.run_coroutine_threadsafe(rev_edgegpt.text_chat(qq_msg), bing_cache_loop).result()
if res_code == 0: # bing不想继续话题重置会话后重试。
send_message(platform, message, "Bing不想继续话题了, 正在自动重置会话并重试。", msg_ref=msg_ref, gocq_loop=gocq_loop, qqchannel_bot=qqchannel_bot, gocq_bot=gocq_bot)
while rev_edgegpt.is_busy():
time.sleep(1)
res, res_code = asyncio.run_coroutine_threadsafe(rev_edgegpt.text_chat(qq_msg, platform), bing_cache_loop).result()
if res_code == 0: # bing不想继续话题重置会话后重试。
send_message(platform, message, "Bing不想继续话题了, 正在自动重置会话并重试。", msg_ref=msg_ref, gocq_loop=gocq_loop, qqchannel_bot=qqchannel_bot, gocq_bot=gocq_bot)
asyncio.run_coroutine_threadsafe(rev_edgegpt.forget(), bing_cache_loop).result()
res, res_code = asyncio.run_coroutine_threadsafe(rev_edgegpt.text_chat(qq_msg, platform), bing_cache_loop).result()
if res_code == 0: # bing还是不想继续话题大概率说明提问有问题。
asyncio.run_coroutine_threadsafe(rev_edgegpt.forget(), bing_cache_loop).result()
res, res_code = asyncio.run_coroutine_threadsafe(rev_edgegpt.text_chat(qq_msg), bing_cache_loop).result()
if res_code == 0: # bing还是不想继续话题大概率说明提问有问题。
send_message(platform, message, "Bing仍然不想继续话题, 请检查您的提问。", msg_ref=msg_ref, gocq_loop=gocq_loop, qqchannel_bot=qqchannel_bot, gocq_bot=gocq_bot)
else:
chatgpt_res = str(res)
if REV_EDGEGPT in reply_prefix:
chatgpt_res = reply_prefix[REV_EDGEGPT] + chatgpt_res
send_message(platform, message, "Bing仍然不想继续话题, 会话已重置, 请检查您的提问后重试。", msg_ref=msg_ref, gocq_loop=gocq_loop, qqchannel_bot=qqchannel_bot, gocq_bot=gocq_bot)
res = ""
chatgpt_res = str(res)
if REV_EDGEGPT in reply_prefix:
chatgpt_res = reply_prefix[REV_EDGEGPT] + chatgpt_res
except BaseException as e:
print("[System-Err] Rev NewBing API错误。原因如下:\n"+str(e))
send_message(platform, message, f"Rev NewBing API错误。原因如下\n{str(e)} \n前往官方频道反馈~", msg_ref=msg_ref, gocq_loop=gocq_loop, qqchannel_bot=qqchannel_bot, gocq_bot=gocq_bot)
@@ -518,23 +618,29 @@ def oper_msg(message, at=False, msg_ref = None, platform = None):
if command_result != None:
command = command_result[2]
if command == "keyword":
with open("keyword.json", "r", encoding="utf-8") as f:
keywords = json.load(f)
if os.path.exists("keyword.json"):
with open("keyword.json", "r", encoding="utf-8") as f:
keywords = json.load(f)
# QQ昵称
if command == "nick":
with open("cmd_config.json", "r", encoding="utf-8") as f:
global nick_qq
nick_qq = json.load(f)["nick_qq"]
if command_result[0]:
# 是否是画图指令
if len(command_result) == 3 and command_result[2] == 'draw':
if isinstance(command_result, list) and len(command_result) == 3 and command_result[2] == 'draw':
for i in command_result[1]:
send_message(platform, message, i, msg_ref=msg_ref, gocq_loop=gocq_loop, qqchannel_bot=qqchannel_bot, gocq_bot=gocq_bot)
else:
send_message(platform, message, i, msg_ref=msg_ref, image=i, gocq_loop=gocq_loop, qqchannel_bot=qqchannel_bot, gocq_bot=gocq_bot)
else:
try:
send_message(platform, message, command_result[1], msg_ref=msg_ref, gocq_loop=gocq_loop, qqchannel_bot=qqchannel_bot, gocq_bot=gocq_bot)
except BaseException as e:
t = command_result[1].replace(".", " . ")
send_message(platform, message, t, msg_ref=msg_ref, gocq_loop=gocq_loop, qqchannel_bot=qqchannel_bot, gocq_bot=gocq_bot)
send_message(platform, message, f"回复消息出错: {str(e)}", msg_ref=msg_ref, gocq_loop=gocq_loop, qqchannel_bot=qqchannel_bot, gocq_bot=gocq_bot)
else:
send_message(platform, message, f"指令调用错误: \n{command_result[1]}", msg_ref=msg_ref, gocq_loop=gocq_loop, qqchannel_bot=qqchannel_bot, gocq_bot=gocq_bot)
send_message(platform, message, f"指令调用错误: \n{str(command_result[1])}", msg_ref=msg_ref, gocq_loop=gocq_loop, qqchannel_bot=qqchannel_bot, gocq_bot=gocq_bot)
return
@@ -547,12 +653,11 @@ def oper_msg(message, at=False, msg_ref = None, platform = None):
# 敏感过滤
# 过滤不合适的词
judged_res = chatgpt_res
for i in uw.unfit_words:
judged_res = re.sub(i, "***", judged_res)
chatgpt_res = re.sub(i, "***", chatgpt_res)
# 百度内容审核服务二次审核
if baidu_judge != None:
check, msg = baidu_judge.judge(judged_res)
check, msg = baidu_judge.judge(chatgpt_res)
if not check:
send_message(platform, message, f"你的提问得到的回复【百度内容审核】未通过,不予回复。\n\n{msg}", msg_ref=msg_ref, gocq_loop=gocq_loop, qqchannel_bot=qqchannel_bot, gocq_bot=gocq_bot)
return
@@ -611,9 +716,39 @@ class gocqClient():
# 收到群聊消息
@gocq_app.receiver("GroupMessage")
async def _(app: CQHTTP, source: GroupMessage):
global nick_qq
# 将nick_qq转换为元组
if nick_qq == None:
nick_qq = ("ai",)
if isinstance(nick_qq, str):
nick_qq = (nick_qq,)
if isinstance(nick_qq, list):
nick_qq = tuple(nick_qq)
if isinstance(source.message[0], Plain):
if source.message[0].text.startswith('ai '):
source.message[0].text = source.message[0].text[3:]
if source.message[0].text.startswith(nick_qq):
_len = 0
for i in nick_qq:
if source.message[0].text.startswith(i):
_len = len(i)
source.message[0].text = source.message[0].text[_len:].strip()
new_sub_thread(oper_msg, (source, True, None, PLATFORM_GOCQ))
if isinstance(source.message[0], At):
if source.message[0].qq == source.self_id:
new_sub_thread(oper_msg, (source, True, None, PLATFORM_GOCQ))
else:
return
return
@gocq_app.receiver("FriendMessage")
async def _(app: CQHTTP, source: FriendMessage):
if isinstance(source.message[0], Plain):
new_sub_thread(oper_msg, (source, False, None, PLATFORM_GOCQ))
else:
return
@gocq_app.receiver("GroupMemberIncrease")
async def _(app: CQHTTP, source: GroupMemberIncrease):
global nick_qq
await app.sendGroupMessage(source.group_id, [
Plain(text=f"欢迎加入本群!\n欢迎给https://github.com/Soulter/QQChannelChatGPT项目一个Star😊~\n@我输入help查看帮助~\n我叫{nick_qq}, 你也可以以【{nick_qq}+问题】的格式来提醒我并问我问题哦~\n")
])

View File

@@ -1,56 +0,0 @@
# -*- coding: utf-8 -*-
from git.repo import Repo
import git
import os
# import zipfile
if __name__ == "__main__":
try:
# 检测文件夹
if not os.path.exists('QQChannelChatGPT'):
os.mkdir('QQChannelChatGPT')
project_path = os.path.join('QQChannelChatGPT')
try:
repo = Repo(project_path)
# 检查当前commit的hash值
commit_hash = repo.head.object.hexsha
print("当前commit的hash值为: " + commit_hash)
# 得到远程仓库的origin的commit的列表
origin = repo.remotes.origin
try:
origin.fetch()
except:
pass
# 得到远程仓库的commit的hash值
remote_commit_hash = origin.refs.master.commit.hexsha
print("https://github.com/Soulter/QQChannelChatGPT的commit的hash值为: " + remote_commit_hash)
# 比较两个commit的hash值
if commit_hash != remote_commit_hash:
res = input("检测到项目有更新, 是否更新? (y/n): ")
if res == 'y':
repo.remotes.origin.pull()
print("项目更新完毕")
if res == 'n':
print("已取消更新")
except:
print("正在从https://github.com/Soulter/QQChannelChatGPT.git拉取项目...")
Repo.clone_from('https://github.com/Soulter/QQChannelChatGPT.git',to_path=project_path,branch='master')
print("项目拉取完毕")
print("【重要提醒】如果你没有Python版本>=3.8或者Git环境, 请先安装, 否则接下来的操作会造成闪退。")
print("【重要提醒】Python下载地址: https://npm.taobao.org/mirrors/python/3.9.7/python-3.9.7-amd64.exe ")
print("【重要提醒】Git下载地址: https://registry.npmmirror.com/-/binary/git-for-windows/v2.39.2.windows.1/Git-2.39.2-64-bit.exe")
print("【重要提醒】安装时, 请务必勾选“Add Python to PATH”选项。")
input("已确保安装了Python3.9+的版本,按下回车继续...")
print("正在安装依赖库")
os.system('python -m pip install -r QQChannelChatGPT\\requirements.txt')
print("依赖库安装完毕")
input("初次启动, 请先在QQChannelChatGPT/configs/config.yaml填写相关配置! 按任意键继续...")
finally:
print("正在启动项目...")
os.system('python QQChannelChatGPT\main.py')
except BaseException as e:
print(e)
input("程序出错。可以截图发给QQ905617992.按下回车键退出...")

91
main.py
View File

@@ -1,14 +1,20 @@
import threading
import asyncio
import os, sys
from pip._internal import main as pipmain
abs_path = os.path.dirname(os.path.realpath(sys.argv[0])) + '/'
def main(loop, event):
import cores.qqbot.core as qqBot
import yaml
ymlfile = open(abs_path+"configs/config.yaml", 'r', encoding='utf-8')
cfg = yaml.safe_load(ymlfile)
try:
import cores.qqbot.core as qqBot
import yaml
ymlfile = open(abs_path+"configs/config.yaml", 'r', encoding='utf-8')
cfg = yaml.safe_load(ymlfile)
except BaseException as e:
print(e)
input("yaml库未导入或者配置文件格式错误请退出程序重试。")
exit()
if 'http_proxy' in cfg:
os.environ['HTTP_PROXY'] = cfg['http_proxy']
@@ -39,28 +45,50 @@ def check_env():
print("请使用Python3.8运行本项目")
input("按任意键退出...")
exit()
try:
print("检查依赖库中...")
if os.path.exists('requirements.txt'):
os.system("pip3 install -r requirements.txt")
elif os.path.exists('QQChannelChatGPT'+ os.sep +'requirements.txt'):
os.system('QQChannelChatGPT'+ os.sep +'requirements.txt')
os.system("clear")
print("安装依赖库完毕...")
except BaseException as e:
print("安装依赖库失败,请手动安装依赖库。")
print(e)
input("按任意键退出...")
exit()
# 检查pip
# pip_tag = "pip"
# mm = os.system("pip -V")
# if mm != 0:
# mm1 = os.system("pip3 -V")
# if mm1 != 0:
# print("未检测到pip, 请安装Python(版本应>=3.9)")
# input("按任意键退出...")
# exit()
# else:
# pip_tag = "pip3"
# 检查key
with open(abs_path+"configs/config.yaml", 'r', encoding='utf-8') as ymlfile:
import yaml
cfg = yaml.safe_load(ymlfile)
if cfg['openai']['key'] == '' or cfg['openai']['key'] == None:
print("请先在configs/config.yaml下添加一个可用的OpenAI Key。详情请前往https://beta.openai.com/account/api-keys")
if cfg['qqbot']['appid'] == '' or cfg['qqbot']['token'] == '' or cfg['qqbot']['appid'] == None or cfg['qqbot']['token'] == None:
print("请先在configs/config.yaml下完善appid和token令牌(在https://q.qq.com/上注册一个QQ机器人即可获得)")
if os.path.exists('requirements.txt'):
pth = 'requirements.txt'
else:
pth = 'QQChannelChatGPT'+ os.sep +'requirements.txt'
print("正在更新三方依赖库...")
try:
pipmain(['install', '-r', pth])
print("依赖库安装完毕。")
except BaseException as e:
print(e)
while True:
res = input("依赖库可能安装失败了。\n如果是报错ValueError: check_hostname requires server_hostname请尝试先关闭代理后重试。\n输入y回车重试\n输入c回车使用国内镜像源下载\n输入其他按键回车继续往下执行。")
if res == "y":
try:
pipmain(['install', '-r', pth])
print("依赖库安装完毕。")
break
except BaseException as e:
print(e)
continue
elif res == "c":
try:
pipmain(['install', '-r', pth, '-i', 'https://mirrors.aliyun.com/pypi/simple/'])
print("依赖库安装完毕。")
break
except BaseException as e:
print(e)
continue
else:
break
def get_platform():
import platform
@@ -76,6 +104,19 @@ def get_platform():
if __name__ == "__main__":
check_env()
# 获取参数
args = sys.argv
if len(args) > 1:
if args[1] == '-replit':
print("[System] 启动Replit Web保活服务...")
try:
from webapp_replit import keep_alive
keep_alive()
except BaseException as e:
print(e)
print(f"[System-err] Replit Web保活服务启动失败:{str(e)}")
bot_event = threading.Event()
loop = asyncio.get_event_loop()
main(loop, bot_event)

View File

@@ -1,22 +1,258 @@
import abc
import json
import git.exc
from git.repo import Repo
import os
import sys
import requests
from model.provider.provider import Provider
import json
import util.plugin_util as putil
import shutil
import importlib
PLATFORM_QQCHAN = 'qqchan'
PLATFORM_GOCQ = 'gocq'
# 指令功能的基类,通用的(不区分语言模型)的指令就在这实现
class Command:
def __init__(self, provider: Provider):
self.provider = Provider
@abc.abstractmethod
def check_command(self, message):
if message.startswith("help") or message.startswith("帮助"):
return True, self.help()
def get_plugin_modules(self):
plugins = []
try:
if os.path.exists("addons/plugins"):
plugins = putil.get_modules("addons/plugins")
return plugins
elif os.path.exists("QQChannelChatGPT/addons/plugins"):
plugins = putil.get_modules("QQChannelChatGPT/addons/plugins")
return plugins
else:
return None
except BaseException as e:
raise e
def check_command(self, message, role, platform, message_obj, cached_plugins: dict):
# 插件
for k, v in cached_plugins.items():
try:
hit, res = v["clsobj"].run(message, role, platform, message_obj)
if hit:
return True, res
except BaseException as e:
print(f"[Debug] {k}插件加载出现问题,原因: {str(e)}\n已安装插件: {cached_plugins.keys}\n如果你没有相关装插件的想法, 请直接忽略此报错, 不影响其他功能的运行。")
if self.command_start_with(message, "nick"):
return True, self.set_nick(message, platform)
if self.command_start_with(message, "plugin"):
return True, self.plugin_oper(message, role, cached_plugins)
return False, None
def plugin_reload(self, cached_plugins: dict, target: str = None, all: bool = False):
plugins = self.get_plugin_modules()
fail_rec = ""
if plugins != None:
for p in plugins:
try:
if p not in cached_plugins or p == target or all:
module = __import__("addons.plugins." + p + "." + p, fromlist=[p])
if p in cached_plugins:
module = importlib.reload(module)
cls = putil.get_classes(p, module)
obj = getattr(module, cls[0])()
try:
info = obj.info()
if 'name' not in info or 'desc' not in info or 'version' not in info or 'author' not in info:
fail_rec += f"载入插件{p}失败,原因: 插件信息不完整\n"
continue
if isinstance(info, dict) == False:
fail_rec += f"载入插件{p}失败,原因: 插件信息格式不正确\n"
continue
except BaseException as e:
fail_rec += f"调用插件{p} info失败, 原因: {str(e)}\n"
continue
cached_plugins[p] = {
"module": module,
"clsobj": obj,
"info": info
}
except BaseException as e:
fail_rec += f"加载{p}插件出现问题,原因{str(e)}\n"
if fail_rec == "":
return True, None
else:
return False, fail_rec
else:
return False, "未找到任何插件模块"
'''
插件指令
'''
def plugin_oper(self, message: str, role: str, cached_plugins: dict):
l = message.split(" ")
if len(l) < 2:
return True, "\n=====插件指令面板=====\n安装插件: \nplugin i 插件Github地址\n卸载插件: \nplugin i 插件名 \n重载插件: \nplugin reload\n查看插件列表:\nplugin l\n更新插件: plugin u 插件名\n===============", "plugin"
else:
ppath = ""
if os.path.exists("addons/plugins"):
ppath = "addons/plugins"
elif os.path.exists("QQChannelChatGPT/addons/plugins"):
ppath = "QQChannelChatGPT/addons/plugins"
else:
return False, "未找到插件目录", "plugin"
if l[1] == "i":
if role != "admin":
return False, f"你的身份组{role}没有权限安装插件", "plugin"
try:
# 得到url的最后一段
d = l[2].split("/")[-1]
# 创建文件夹
plugin_path = os.path.join(ppath, d)
if os.path.exists(plugin_path):
shutil.rmtree(plugin_path)
os.mkdir(plugin_path)
Repo.clone_from(l[2],to_path=plugin_path,branch='master')
# 读取插件的requirements.txt
if os.path.exists(os.path.join(plugin_path, "requirements.txt")):
with open(os.path.join(plugin_path, "requirements.txt"), "r", encoding="utf-8") as f:
for line in f.readlines():
mm = os.system(f"pip3 install {line.strip()}")
if mm != 0:
return False, "插件依赖安装失败需要您手动pip安装对应插件的依赖。", "plugin"
# 加载没缓存的插件
ok, err = self.plugin_reload(cached_plugins, target=d)
if ok:
return True, "插件拉取并载入成功~", "plugin"
else:
# if os.path.exists(plugin_path):
# shutil.rmtree(plugin_path)
return False, f"插件拉取载入失败。\n跟踪: \n{err}", "plugin"
except BaseException as e:
return False, f"拉取插件失败,原因: {str(e)}", "plugin"
elif l[1] == "d":
if role != "admin":
return False, f"你的身份组{role}没有权限删除插件", "plugin"
try:
# 删除文件夹
shutil.rmtree(os.path.join(ppath, l[2]))
if l[2] in cached_plugins:
del cached_plugins[l[2]]
return True, "插件卸载成功~", "plugin"
except BaseException as e:
return False, f"卸载插件失败,原因: {str(e)}", "plugin"
elif l[1] == "u":
plugin_path = os.path.join(ppath, l[2])
try:
repo = Repo(path = plugin_path)
repo.remotes.origin.pull()
ok, err = self.plugin_reload(cached_plugins, target=l[2])
if ok:
return True, "\n更新插件成功!!", "plugin"
else:
return False, "更新插件成功,但是重载插件失败。\n问题跟踪: \n"+err, "plugin"
except BaseException as e:
return False, "更新插件失败, 请使用plugin i指令覆盖安装", "plugin"
elif l[1] == "l":
try:
plugin_list_info = "\n".join([f"{k}: \n名称: {v['info']['name']}\n简介: {v['info']['desc']}\n版本: {v['info']['version']}\n作者: {v['info']['author']}\n" for k, v in cached_plugins.items()])
return True, "\n=====已激活插件列表=====\n" + plugin_list_info + "\n使用plugin v 插件名 查看插件帮助\n=================", "plugin"
except BaseException as e:
return False, f"获取插件列表失败,原因: {str(e)}", "plugin"
elif l[1] == "v":
try:
if l[2] in cached_plugins:
info = cached_plugins[l[2]]["info"]
res = f"\n=====插件信息=====\n名称: {info['name']}\n{info['desc']}\n版本: {info['version']}作者: {info['author']}\n\n帮助:\n{info['help']}"
return True, res, "plugin"
else:
return False, "未找到该插件", "plugin"
except BaseException as e:
return False, f"获取插件信息失败,原因: {str(e)}", "plugin"
elif l[1] == "reload":
if role != "admin":
return False, f"你的身份组{role}没有权限重载插件", "plugin"
try:
ok, err = self.plugin_reload(cached_plugins, all = True)
if ok:
return True, "\n重载插件成功~", "plugin"
else:
# if os.path.exists(plugin_path):
# shutil.rmtree(plugin_path)
return False, f"插件重载失败。\n跟踪: \n{err}", "plugin"
except BaseException as e:
return False, f"插件重载失败,原因: {str(e)}", "plugin"
elif l[1] == "dev":
if role != "admin":
return False, f"你的身份组{role}没有权限开发者模式", "plugin"
return True, "cached_plugins: \n" + str(cached_plugins), "plugin"
'''
nick: 存储机器人的昵称
'''
def set_nick(self, message: str, platform: str):
if platform == PLATFORM_GOCQ:
l = message.split(" ")
if len(l) == 1:
return True, "【设置机器人昵称】示例:\n支持多昵称\nnick 昵称1 昵称2 昵称3", "nick"
nick = l[1:]
self.general_command_storer("nick_qq", nick)
return True, f"设置成功!现在你可以叫我这些昵称来提问我啦~", "nick"
elif platform == PLATFORM_QQCHAN:
nick = message.split(" ")[2]
return False, "QQ频道平台不支持为机器人设置昵称。", "nick"
"""
存储指令结果到cmd_config.json
"""
def general_command_storer(self, key, value):
if not os.path.exists("cmd_config.json"):
config = {}
else:
with open("cmd_config.json", "r", encoding="utf-8") as f:
config = json.load(f)
config[key] = value
with open("cmd_config.json", "w", encoding="utf-8") as f:
json.dump(config, f, indent=4, ensure_ascii=False)
f.flush()
def general_commands(self):
return {
"help": "帮助",
"keyword": "设置关键词/关键指令回复",
"update": "更新面板",
"update latest": "更新到最新版本",
"update r": "重启机器人",
"reset": "重置会话",
"nick": "设置机器人昵称",
"/bing": "切换到bing模型",
"/gpt": "切换到OpenAI ChatGPT API",
"/revgpt": "切换到网页版ChatGPT",
"/bing 问题": "临时使用一次bing模型进行会话",
"/gpt 问题": "临时使用一次OpenAI ChatGPT API进行会话",
"/revgpt 问题": "临时使用一次网页版ChatGPT进行会话",
"plugin": "插件安装、卸载和重载"
}
def help_messager(self, commands: dict):
try:
resp = requests.get("https://soulter.top/channelbot/notice.json").text
notice = json.loads(resp)["notice"]
except BaseException as e:
notice = ""
msg = "Github项目名QQChannelChatGPT, 有问题提交issue, 欢迎Star\n【指令列表】\n"
for key, value in commands.items():
msg += key + ": " + value + "\n"
msg += notice
return msg
# 接受可变参数
def command_start_with(self, message: str, *args):
for arg in args:
@@ -24,22 +260,42 @@ class Command:
return True
return False
# keyword: 关键字
def keyword(self, message: str, role: str):
if role != "admin":
return True, "你没有权限使用该指令", "keyword"
if len(message.split(" ")) != 3:
return True, "【设置关键词/关键指令回复】示例:\nkeyword hi 你好\n当发送hi的时候会回复你好\nkeyword /hi 你好\n当发送/hi时会回复你好", "keyword"
l = message.split(" ")
if len(l) < 3:
return True, "【设置关键词回复】示例:\nkeyword hi 你好\n当发送hi的时候会回复你好\nkeyword /hi 你好\n当发送/hi时会回复你好\n删除关键词: keyword d hi\n删除hi关键词的回复", "keyword"
del_mode = False
if l[1] == "d":
print("删除关键词: "+l[2])
del_mode = True
try:
if os.path.exists("keyword.json"):
with open("keyword.json", "r", encoding="utf-8") as f:
keyword = json.load(f)
keyword[l[1]] = l[2]
if del_mode:
# 删除关键词
if l[2] not in keyword:
return False, "该关键词不存在", "keyword"
else: del keyword[l[2]]
else:
keyword[l[1]] = l[2]
else:
if del_mode:
return False, "该关键词不存在", "keyword"
keyword = {l[1]: l[2]}
with open("keyword.json", "w", encoding="utf-8") as f:
print("设置指令: "+l[1]+" -> "+l[2])
json.dump(keyword, f, ensure_ascii=False, indent=4)
f.flush()
if del_mode:
return True, "删除成功: "+l[2], "keyword"
return True, "设置成功: "+l[1]+" -> "+l[2], "keyword"
except BaseException as e:
return False, "设置失败: "+str(e), "keyword"
@@ -63,7 +319,10 @@ class Command:
commits_log = ''
index = 1
for commit in commits:
commits_log += f"[{index}] {commit.message}\n-----------\n"
if commit.message.endswith("\n"):
commits_log += f"[{index}] {commit.message}-----------\n"
else:
commits_log += f"[{index}] {commit.message}\n-----------\n"
index+=1
remote_commit_hash = origin.refs.master.commit.hexsha[:6]
@@ -79,29 +338,14 @@ class Command:
pash_tag = "QQChannelChatGPT"+os.sep
repo.remotes.origin.pull()
try:
os.system("pip3 install -r "+pash_tag+"requirements.txt")
except BaseException as e:
print(str(e))
py = sys.executable
os.execl(py, py, *sys.argv)
# 检查是否是windows环境
# if platform.system().lower() == "windows":
# if os.path.exists("launcher.exe"):
# os.system("start launcher.exe")
# elif os.path.exists("QQChannelChatGPT\\main.py"):
# os.system("start python QQChannelChatGPT\\main.py")
# else:
# return True, "更新成功,未发现启动项,因此需要手动重启程序。"
# exit()
# else:
# py = sys.executable
# os.execl(py, py, *sys.argv)
return True, "更新成功~是否重启输入update r重启重启指令不返回任何确认信息", "update"
except BaseException as e:
return False, "更新失败: "+str(e), "update"
if l[1] == "r":
py = sys.executable
os.execl(py, py, *sys.argv)
def reset(self):
return False
@@ -116,7 +360,7 @@ class Command:
return False
def help(self):
return True, f"[Github项目名: QQChannelChatGPT有问题请前往提交issue欢迎Star此项目~]\n\n指令面板:\nstatus 查看机器人key状态\ncount 查看机器人统计信息\nreset 重置会话\nhis 查看历史记录\ntoken 查看会话token数\nhelp 查看帮助\nset 人格指令菜单\nkey 动态添加key", "help"
return False
def status(self):
return False

View File

@@ -5,8 +5,19 @@ from cores.qqbot.personality import personalities
class CommandOpenAIOfficial(Command):
def __init__(self, provider: ProviderOpenAIOfficial):
self.provider = provider
self.cached_plugins = {}
def check_command(self, message: str, session_id: str, user_name: str, role):
def check_command(self,
message: str,
session_id: str,
user_name: str,
role: str,
platform: str,
message_obj,
cached_plugins: dict):
hit, res = super().check_command(message, role, platform, message_obj=message_obj, cached_plugins=cached_plugins)
if hit:
return True, res
if self.command_start_with(message, "reset", "重置"):
return True, self.reset(session_id)
elif self.command_start_with(message, "his", "历史"):
@@ -34,7 +45,20 @@ class CommandOpenAIOfficial(Command):
elif self.command_start_with(message, "key"):
return True, self.key(message, user_name)
if self.command_start_with(message, "/"):
return True, (False, "未知指令", "unknown_command")
return False, None
def help(self):
commands = super().general_commands()
commands[''] = '画画'
commands['key'] = '添加OpenAI key'
commands['set'] = '人格设置面板'
commands['gpt'] = '查看gpt配置信息'
commands['status'] = '查看key使用状态'
commands['token'] = '查看本轮会话token'
return True, super().help_messager(commands), "help"
def reset(self, session_id: str):

View File

@@ -4,16 +4,32 @@ from model.provider.provider_rev_chatgpt import ProviderRevChatGPT
class CommandRevChatGPT(Command):
def __init__(self, provider: ProviderRevChatGPT):
self.provider = provider
def check_command(self, message: str, role):
self.cached_plugins = {}
def check_command(self,
message: str,
role: str,
platform: str,
message_obj,
cached_plugins: dict):
hit, res = super().check_command(message, role, platform, message_obj=message_obj, cached_plugins=cached_plugins)
if hit:
return True, res
if self.command_start_with(message, "help", "帮助"):
return True, self.help()
elif self.command_start_with(message, "reset"):
return True, self.reset()
elif self.command_start_with(message, "update"):
return True, self.update(message, role)
elif self.command_start_with(message, "keyword"):
return True, self.keyword(message, role)
if self.command_start_with(message, "/"):
return True, (False, "未知指令", "unknown_command")
return False, None
def reset(self):
return False, "此功能暂未开放", "reset"
def help(self):
return True, "[Github项目名: QQChannelChatGPT有问题请前往提交issue欢迎Star此项目~]\n\nRevChatGPT指令面板\n当前语言模型RevChatGPT未实现任何指令\n", "help"
return True, super().help_messager(super().general_commands()), "help"

View File

@@ -4,8 +4,19 @@ import asyncio
class CommandRevEdgeGPT(Command):
def __init__(self, provider: ProviderRevEdgeGPT):
self.provider = provider
self.cached_plugins = {}
def check_command(self, message: str, loop, role):
def check_command(self,
message: str,
loop,
role: str,
platform: str,
message_obj,
cached_plugins: dict):
hit, res = super().check_command(message, role, platform, message_obj=message_obj, cached_plugins=cached_plugins)
if hit:
return True, res
if self.command_start_with(message, "reset"):
return True, self.reset(loop)
elif self.command_start_with(message, "help"):
@@ -14,6 +25,9 @@ class CommandRevEdgeGPT(Command):
return True, self.update(message, role)
elif self.command_start_with(message, "keyword"):
return True, self.keyword(message, role)
if self.command_start_with(message, "/"):
return True, (False, "未知指令", "unknown_command")
return False, None
def reset(self, loop):
@@ -25,5 +39,5 @@ class CommandRevEdgeGPT(Command):
return res, "重置失败", "reset"
def help(self):
return True, "[Github项目名: QQChannelChatGPT有问题请前往提交issue欢迎Star此项目~]\n\nRevBing指令面板:\nreset: 重置\nhelp: 帮助", "help"
return True, super().help_messager(super().general_commands()), "help"

View File

@@ -1,13 +1,44 @@
from nakuru.entities.components import Plain
from nakuru.entities.components import Plain, At, Image
class QQ:
def run_bot(self, gocq):
self.client = gocq
self.client.run()
async def send_qq_msg(self, source, res):
print("[System-Info] 回复QQ消息中..."+res)
async def send_qq_msg(self,
source,
res,
image_mode: bool = False):
"""
res可以是一个数组也就是gocq的消息链.
"""
# print(res)
print("[System-Info] 回复QQ消息中..."+str(res))
if isinstance(res, list) and len(res) > 0:
await self.client.sendGroupMessage(source.group_id, res)
return
# 通过消息链处理
await self.client.sendGroupMessage(source.group_id, [
Plain(text=res)
])
if not image_mode:
if source.type == "GroupMessage":
await self.client.sendGroupMessage(source.group_id, [
At(qq=source.user_id),
Plain(text=res)
])
elif source.type == "FriendMessage":
await self.client.sendFriendMessage(source.user_id, [
Plain(text=res)
])
else:
if source.type == "GroupMessage":
await self.client.sendGroupMessage(source.group_id, [
At(qq=source.user_id),
Plain(text="好的,我根据你的需要为你生成了一张图片😊"),
Image.fromURL(url=res)
])
elif source.type == "FriendMessage":
await self.client.sendFriendMessage(source.user_id, [
Plain(text="好的,我根据你的需要为你生成了一张图片😊"),
Image.fromURL(url=res)
])

View File

@@ -1,6 +1,7 @@
import io
import botpy
from PIL import Image
from botpy.message import Message, DirectMessage
import re
import asyncio
import requests
@@ -15,13 +16,13 @@ class QQChan():
self.client.run(appid=appid, token=token)
def send_qq_msg(self, message, res, image_mode=False, msg_ref = None):
print("[System-Info] 回复QQ频道消息中..."+res)
print("[System-Info] 回复QQ频道消息中..."+str(res))
if not image_mode:
try:
if msg_ref is not None:
reply_res = asyncio.run_coroutine_threadsafe(message.reply(content=res, message_reference = msg_ref), self.client.loop)
reply_res = asyncio.run_coroutine_threadsafe(message.reply(content=str(res), message_reference = msg_ref), self.client.loop)
else:
reply_res = asyncio.run_coroutine_threadsafe(message.reply(content=res), self.client.loop)
reply_res = asyncio.run_coroutine_threadsafe(message.reply(content=str(res)), self.client.loop)
reply_res.result()
except BaseException as e:
# 分割过长的消息

View File

@@ -1,4 +1,5 @@
from revChatGPT.V1 import Chatbot
from revChatGPT import typings
from model.provider.provider import Provider
class ProviderRevChatGPT(Provider):
@@ -6,7 +7,7 @@ class ProviderRevChatGPT(Provider):
self.rev_chatgpt = []
for i in range(0, len(config['account'])):
try:
print(f"[System] 创建rev_ChatGPT负载{str(i)}: " + str(config['account'][i]))
print(f"[System] 创建rev_ChatGPT负载{str(i)}中...")
if 'password' in config['account'][i]:
config['account'][i]['password'] = str(config['account'][i]['password'])
revstat = {
@@ -30,14 +31,25 @@ class ProviderRevChatGPT(Provider):
for data in bot.ask(prompt):
resp = data["message"]
break
except typings.Error as e:
if e.code == typings.ErrorType.RATE_LIMIT_ERROR:
raise e
if e.code == typings.ErrorType.INVALID_ACCESS_TOKEN_ERROR:
raise e
if e.code == typings.ErrorType.EXPIRED_ACCESS_TOKEN_ERROR:
raise e
if e.code == typings.ErrorType.PROHIBITED_CONCURRENT_QUERY_ERROR:
raise e
err_count += 1
print(f"[RevChatGPT] 请求出现问题: {str(e)} | 正在重试: {str(err_count)}")
if err_count >= retry_count:
raise e
except BaseException as e:
try:
print("[RevChatGPT] 请求出现了一些问题, 正在重试。次数"+str(err_count))
err_count += 1
if err_count >= retry_count:
raise e
except BaseException:
err_count += 1
err_count += 1
print(f"[RevChatGPT] 请求出现问题: {str(e)} | 正在重试: {str(err_count)}")
if err_count >= retry_count:
raise e
print("[RevChatGPT] "+str(resp))
return resp
@@ -45,26 +57,24 @@ class ProviderRevChatGPT(Provider):
def text_chat(self, prompt):
res = ''
print("[Debug] "+str(self.rev_chatgpt))
err_msg = ''
cursor = 0
for revstat in self.rev_chatgpt:
cursor += 1
if not revstat['busy']:
try:
revstat['busy'] = True
print("[Debug] 使用逆向ChatGPT回复ing", end='', flush=True)
res = self.request_text(prompt, revstat['obj'])
print("OK")
revstat['busy'] = False
# 处理结果文本
chatgpt_res = res.strip()
return res
except Exception as e:
print("[System-Error] 逆向ChatGPT回复失败" + str(e))
try:
if e.code == 2:
print("[System-Error] 频率限制,正在切换账号。"+ str(e))
continue
else:
res = '所有的非忙碌OpenAI账号经过测试都暂时出现问题请稍后再试或者联系管理员~'
return res
except BaseException:
continue
res = '所有的OpenAI账号都有负载, 请稍后再试~'
return res.strip()
# todo: 细化错误管理
except BaseException as e:
revstat['busy'] = False
print(f"请求出现问题: {str(e)}")
err_msg += f"账号{cursor} - 错误原因: {str(e)}"
continue
else:
err_msg += f"账号{cursor} - 错误原因: 忙碌"
continue
res = f'回复失败。错误跟踪:{err_msg}'
return res

View File

@@ -1,6 +1,7 @@
from model.provider.provider import Provider
from EdgeGPT import Chatbot, ConversationStyle
import json
import os
class ProviderRevEdgeGPT(Provider):
def __init__(self):
@@ -20,7 +21,7 @@ class ProviderRevEdgeGPT(Provider):
except BaseException:
return False
async def text_chat(self, prompt):
async def text_chat(self, prompt, platform = 'none'):
if self.busy:
return
self.busy = True
@@ -31,15 +32,52 @@ class ProviderRevEdgeGPT(Provider):
while err_count < retry_count:
try:
resp = await self.bot.ask(prompt=prompt, conversation_style=ConversationStyle.creative)
resp = resp['item']['messages'][len(resp['item']['messages'])-1]['text']
if 'I\'m sorry but I prefer not to continue this conversation. I\'m still learning so I appreciate your understanding and patience.' in resp:
# print("[RevEdgeGPT] "+str(resp))
if 'messages' not in resp['item']:
await self.bot.reset()
msj_obj = resp['item']['messages'][len(resp['item']['messages'])-1]
reply_msg = msj_obj['text']
if 'sourceAttributions' in msj_obj:
reply_source = msj_obj['sourceAttributions']
else:
reply_source = []
if 'throttling' in resp['item']:
throttling = resp['item']['throttling']
# print(throttling)
else:
throttling = None
if 'I\'m sorry but I prefer not to continue this conversation. I\'m still learning so I appreciate your understanding and patience.' in reply_msg:
self.busy = False
return '', 0
if resp == prompt:
resp += '\n\n如果你没有让我复述你的话那代表我可能不想和你继续这个话题了请输入reset重置会话😶'
if reply_msg == prompt:
# resp += '\n\n如果你没有让我复述你的话那代表我可能不想和你继续这个话题了请输入reset重置会话😶'
await self.forget()
err_count += 1
continue
if reply_source is None:
# 不想答复
return '', 0
else:
if platform != 'qqchan':
index = 1
if len(reply_source) > 0:
reply_msg += "\n\n信息来源:\n"
for i in reply_source:
reply_msg += f"[{str(index)}]: {i['seeMoreUrl']} | {i['providerDisplayName']}\n"
index += 1
if throttling is not None:
if throttling['numUserMessagesInConversation'] == throttling['maxNumUserMessagesInConversation']:
# 达到上限,重置会话
await self.forget()
if throttling['numUserMessagesInConversation'] > throttling['maxNumUserMessagesInConversation']:
await self.forget()
err_count += 1
continue
reply_msg += f"\n{throttling['numUserMessagesInConversation']}/{throttling['maxNumUserMessagesInConversation']}"
break
except BaseException as e:
print(e.with_traceback)
# raise e
print(e)
err_count += 1
if err_count >= retry_count:
self.busy = False
@@ -47,6 +85,6 @@ class ProviderRevEdgeGPT(Provider):
print("[RevEdgeGPT] 请求出现了一些问题, 正在重试。次数"+str(err_count))
self.busy = False
print("[RevEdgeGPT] "+str(resp))
return resp, 1
print("[RevEdgeGPT] "+str(reply_msg))
return reply_msg, 1

View File

@@ -1,10 +1,10 @@
requests
requests~=2.28.1
openai~=0.27.4
qq-botpy
revChatGPT~=4.0.8
baidu-aip
qq-botpy~=1.1.2
revChatGPT~=5.0.0
baidu-aip~=4.16.9
EdgeGPT~=0.1.22.1
chardet
Pillow
GitPython
git+https://github.com/Lxns-Network/nakuru-project.git
chardet~=5.1.0
Pillow~=9.4.0
GitPython~=3.1.31
nakuru-project-idk~=0.0.2

22
util/plugin_util.py Normal file
View File

@@ -0,0 +1,22 @@
import os
import inspect
# 找出模块里所有的类名
def get_classes(p_name, arg):
classes = []
clsmembers = inspect.getmembers(arg, inspect.isclass)
for (name, _) in clsmembers:
# print(name, p_name)
if p_name.lower() == name.lower().replace("plugin", ""):
classes.append(name)
break
return classes
# 获取一个文件夹下所有的模块
def get_modules(path):
modules = []
for root, dirs, files in os.walk(path):
for file in files:
if file.endswith(".py") and not file.startswith("__"):
modules.append(file[:-3])
return modules

25
webapp_replit.py Normal file
View File

@@ -0,0 +1,25 @@
from flask import Flask
from threading import Thread
import datetime
app = Flask(__name__)
@app.route('/')
def main_func():
content = "<h1>QQChannelChatGPT Web APP</h1>"
content += "<p>" + "Online @ " + str(datetime.datetime.now()) + "</p>"
content += "<p>欢迎Star本项目</p>"
return content
def run():
app.run(host="0.0.0.0", port=8080)
def keep_alive():
server = Thread(target=run)
server.start()