Compare commits

...

80 Commits

Author SHA1 Message Date
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
Soulter 43dc0f96ff Update README.md 2023-04-10 00:46:16 +08:00
Soulter e02a82fa72 Update README.md 2023-04-10 00:45:53 +08:00
Soulter 9f91b0c92b feat: 1.接入QQ,可以同时在QQ和频道上使用 (beta)
2. 支持临时使用其他语言模型回复(如 /bing hello) #79
perf: 😊1. 优化代码结构,降低耦合度
2. 启动前检查依赖库安装情况
fix: 🤔修复bing模型死锁(正忙)的问题
2023-04-10 00:43:30 +08:00
Soulter d14d6364a3 fix: 修复切换模型造成的数组超限问题 2023-04-09 00:28:03 +08:00
Soulter 15c8f0b6f7 feat: update和切换模型指令以及keyword指令现在仅可管理员使用;
fix: 修复keyword指令在使用官方模型的时候会被识别为“赞助key”的指令的问题 #80
2023-04-08 23:58:46 +08:00
Soulter 9bca158174 feat: 新增自定义指令/keyword 2023-04-08 20:21:49 +08:00
Soulter 45bb30692d fix: 修复bing自动重置会话的一些问题 2023-04-08 19:21:45 +08:00
Soulter 5bf73caba7 perf: 更新后自动更新第三方库 2023-04-08 11:05:12 +08:00
Soulter e5389f620a Merge branch 'master' of https://github.com/Soulter/QQChannelChatGPT 2023-04-06 23:42:20 +08:00
Soulter 61fd52ff61 fix: 修复无法重置后无法回信的问题 2023-04-06 23:42:14 +08:00
Soulter 73c46bd812 Update README.md 2023-04-06 22:52:59 +08:00
Soulter d8173122e0 fix: 修复bing text_chat报too many values to unpack的问题 2023-04-06 22:39:37 +08:00
Soulter 1af6e77dd1 perf: bing不想继续话题后自动重置并重试 2023-04-06 22:28:31 +08:00
Soulter ce476ca163 fix: 修复update指令commit数量显示的问题 2023-04-05 20:32:15 +08:00
Soulter 0a1df90a83 fix: 修复重启程序后设置的语言模型选择偏好重置的问题 2023-04-05 20:26:35 +08:00
Soulter 762f5ea30f fix: 修复官方语言模型切换key时可能发生的死循环问题;修复切换语言模型时前缀不更新的问题 2023-04-05 19:10:31 +08:00
Soulter 06e7753797 Merge pull request #75 from Soulter/55-multi-models-switch
fix: 修复官方api模型画画指令和更新指令接反的问题
2023-04-05 12:11:46 +08:00
Soulter c5e1f8d3e9 fix: 修复官方api模型画画指令和更新指令接反的问题 2023-04-05 12:11:21 +08:00
Soulter 221433725b Update README.md 2023-04-05 12:08:05 +08:00
Soulter 28864cd066 Update README.md 2023-04-05 10:56:25 +08:00
Soulter 854f70dc8b Merge pull request #73 from Soulter/55-multi-models-switch
55 multi models switch
2023-04-05 10:51:50 +08:00
Soulter 435b988223 feat: 支持切换语言模型 #55 2023-04-05 10:50:27 +08:00
Soulter ecc119b296 Merge pull request #72 from Soulter/master
fix: launcher add hints
2023-04-05 09:43:08 +08:00
Soulter 209f3aa136 fix: launcher add hints 2023-04-05 09:41:39 +08:00
Soulter 8935859934 perf: main.py install requierment.txt 2023-04-04 12:26:07 +08:00
Soulter 6c77ec3534 fix: remove main.py request module 2023-04-04 12:24:41 +08:00
Soulter 291d3ebae8 perf: 支持使用/开头的指令 2023-04-03 21:44:46 +08:00
Soulter 5b97fd2e6f fix: 增加baidu_aip_judge文件 2023-04-03 21:33:54 +08:00
Soulter 4de8c5ed7d fix: 修复某些语言模型下update指令无法发送的问题 2023-04-03 21:13:42 +08:00
Soulter 09333d1604 delete: delete some files 2023-04-03 20:05:44 +08:00
Soulter 60240ca9a1 update redeme.md 2023-04-03 18:46:28 +08:00
Soulter 3e45ec0a08 fix: 修复热更新的一些问题 2023-04-03 18:44:47 +08:00
Soulter 4aad04b31a fix: 修复安装器的一些问题 2023-04-03 18:35:20 +08:00
Soulter 99ff3f8d42 fix: 修复热更新的一些问题 2023-04-03 18:02:52 +08:00
20 changed files with 1229 additions and 920 deletions
+128
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.
+180 -19
View File
@@ -1,23 +1,34 @@
## ⭐体验
<div align="center">
使用手机QQ扫码加入QQ频道(频道名: GPT机器人 | 频道号: x42d56aki2)
# QQChannelChatGPT
在QQ和QQ频道上使用ChatGPT、NewBing等语言模型,稳定,一次部署,同时使用。
<img src="https://user-images.githubusercontent.com/37870767/227197121-4f1e02a4-92fd-4497-8768-9d6977a291b7.jpg" width="200"></img>
教程:https://soulter.top/posts/qpdg.html
**Windows用户推荐Windows一键安装,请前往Release下载最新版本**
欢迎体验😊(频道名: GPT机器人 | 频道号: x42d56aki2) | QQ群号:322154837):
详细部署教程链接:https://soulter.top/posts/qpdg.html
<img src="https://user-images.githubusercontent.com/37870767/230417115-9dd3c9d5-6b6b-4928-8fe3-82f559208aab.JPG" width="300"></img>
有网络问题报错的请先看issue,解决不了再加频道反馈
</div>
## ⭐功能:
通知:部署好后,如果使用的是bing或者逆向ChatGPT库,需要使用切换模型指令`/bing`或者'/revgpt'
近期新功能:
- 支持一键切换语言模型(使用/bing /revgpt /gpt分别可以切换newbing、逆向ChatGPT、官方ChatGPT模型)
- 热更新
- 接入QQ,支持在QQ上和QQ频道上同时聊天!https://github.com/Soulter/QQChannelChatGPT/issues/82
支持的AI语言模型(请在`configs/config.yaml`下配置):
- 逆向ChatGPT库
- 官方ChatGPT AI
- 文心一言(即将支持,链接https://github.com/Soulter/ERNIEBot 欢迎Star
- NewBing
- Bard (即将支持)
部署QQ频道机器人教程链接:https://soulter.top/posts/qpdg.html
### 基本功能
<details>
<summary>✅ 回复符合上下文</summary>
@@ -76,7 +87,9 @@
> 关于token:token就相当于是AI中的单词数(但是不等于单词数),`text-davinci-003`模型中最大可以支持`4097`个token。在发送信息时,这个机器人会将用户的历史聊天记录打包发送给ChatGPT,因此,`token`也会相应的累加,为了保证聊天的上下文的逻辑性,就有了缓存token。
### 指令功能
需要先`@`机器人之后再输入指令
#### OpenAI官方API
在频道内需要先`@`机器人之后再输入指令;在QQ中暂时需要在消息前加上`ai `,不需要@
- `/reset`重置prompt
- `/his`查看历史记录(每个用户都有独立的会话)
- `/his [页码数]`查看不同页码的历史记录。例如`/his 2`查看第2页
@@ -86,33 +99,181 @@
- `/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://soulter.top/posts/qpdg.html
### 启动
- 启动main.py
## DEMO
![1.jpg](screenshots/1.jpg)
![3.jpg](screenshots/3.jpg)
![2.jpg](screenshots/2.jpg)
## ⚙配置文件说明:
```yaml
# 如果你不知道怎么部署,请查看https://soulter.top/posts/qpdg.html
# 不一定需要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:
```
-45
View File
@@ -1,45 +0,0 @@
from revChatGPT.V1 import Chatbot
class revChatGPT:
def __init__(self, config):
if 'password' in config:
config['password'] = str(config['password'])
self.chatbot = Chatbot(config=config)
def chat(self, prompt):
resp = ''
"""
Base class for exceptions in this module.
Error codes:
-1: User error
0: Unknown error
1: Server error
2: Rate limit error
3: Invalid request error
4: Expired access token error
5: Invalid access token error
6: Prohibited concurrent query error
"""
err_count = 0
retry_count = 5
while err_count < retry_count:
try:
for data in self.chatbot.ask(prompt):
resp = data["message"]
break
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
print("[RevChatGPT] "+str(resp))
return resp
-47
View File
@@ -1,47 +0,0 @@
import asyncio
from EdgeGPT import Chatbot, ConversationStyle
import json
class revEdgeGPT:
def __init__(self):
self.busy = False
self.wait_stack = []
with open('./cookies.json', 'r') as f:
cookies = json.load(f)
self.bot = Chatbot(cookies=cookies)
def is_busy(self):
return self.busy
async def reset(self):
try:
await self.bot.reset()
return False
except BaseException:
return True
async def chat(self, prompt):
if self.busy:
return
self.busy = True
resp = 'err'
err_count = 0
retry_count = 5
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 resp == prompt:
resp += '\n\n如果你没有让我复述你的话,那代表我可能不想和你继续这个话题了,请输入/reset重置会话😶'
break
except BaseException as e:
print(e.with_traceback)
err_count += 1
if err_count >= retry_count:
raise e
print("[RevEdgeGPT] 请求出现了一些问题, 正在重试。次数"+str(err_count))
self.busy = False
print("[RevEdgeGPT] "+str(resp))
return resp
+66 -35
View File
@@ -1,43 +1,42 @@
# 如果你不知道怎么部署,请务必查看https://soulter.top/posts/qpdg.html
# 如果你不知道怎么部署,请查看https://soulter.top/posts/qpdg.html
# 不一定需要key了,如果你没有key但有openAI账号或者必应账号,可以考虑使用下面的逆向库
# 不一定需要key了,如果你没有key但有openAI账号或者必应账号,也可以使用下面的逆向库
# 注意:已支持多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
###############平台设置#################
# 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: 2.8
version: 3.0
# [Beta] 转储历史记录时间间隔(分钟)
dump_history_interval: 10
# 一个用户只能在time秒内发送count条消息
@@ -61,9 +60,6 @@ reply_prefix:
rev_chatgpt: "[Rev]"
rev_edgegpt: "[RevBing]"
################外带程序(插件)################
# 百度内容审核服务
# 新用户免费5万次调用。https://cloud.baidu.com/doc/ANTIPORN/index.html
baidu_aip:
@@ -72,10 +68,45 @@ baidu_aip:
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
@@ -93,14 +124,14 @@ rev_edgegpt:
# - email: 第2个账户
# password: 第2个账户密码
# - ....
# 支持使用session_token\access_token登录
# 支持使用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:
- email:
password:
- access_token:
-191
View File
@@ -1,191 +0,0 @@
import openai
import yaml
from util.errors.errors import PromptExceededError
import json
import time
import os
import sys
inst = None
# 适配pyinstaller
abs_path = os.path.dirname(os.path.realpath(sys.argv[0])) + '/'
key_record_path = abs_path+'chatgpt_key_record'
class ChatGPT:
def __init__(self, cfg):
self.key_list = []
if 'api_base' in cfg and cfg['api_base'] != 'none' and cfg['api_base'] != '':
openai.api_base = cfg['api_base']
if cfg['key'] != '' and cfg['key'] != None:
print("[System] 读取ChatGPT Key成功")
self.key_list = cfg['key']
# openai.api_key = cfg['key']
else:
input("[System] 请先去完善ChatGPT的Key。详情请前往https://beta.openai.com/account/api-keys")
# init key record
self.init_key_record()
chatGPT_configs = cfg['chatGPTConfigs']
print(f'[System] 加载ChatGPTConfigs: {chatGPT_configs}')
self.chatGPT_configs = chatGPT_configs
self.openai_configs = cfg
def chat(self, req, image_mode = False, img_num = 1, img_size = "1024x1024"):
# ChatGPT API 2023/3/2
# messages = [{"role": "user", "content": prompt}]
if not image_mode:
try:
response = openai.ChatCompletion.create(
messages=req,
**self.chatGPT_configs
)
except Exception as e:
print(e)
if 'You exceeded' in str(e) or 'Billing hard limit has been reached' in str(e) or 'No API key provided' in str(e) or 'Incorrect API key provided' in str(e):
print("[System] 当前Key已超额或者不正常,正在切换")
self.key_stat[openai.api_key]['exceed'] = True
self.save_key_record()
response, is_switched = self.handle_switch_key(req)
if not is_switched:
# 所有Key都超额或不正常
raise e
else:
response = openai.ChatCompletion.create(
messages=req,
**self.chatGPT_configs
)
self.key_stat[openai.api_key]['used'] += response['usage']['total_tokens']
self.save_key_record()
print("[ChatGPT] "+str(response["choices"][0]["message"]["content"]))
return str(response["choices"][0]["message"]["content"]).strip(), response['usage']['total_tokens']
else:
try:
# print("test1")
response = openai.Image.create(
prompt=req[0]['content'],
n=img_num,
size=img_size
)
# print("test2")
image_url = []
for i in range(img_num):
image_url.append(response['data'][i]['url'])
print(image_url)
except Exception as e:
print(e)
if 'You exceeded' in str(e) or 'Billing hard limit has been reached' in str(
e) or 'No API key provided' in str(e) or 'Incorrect API key provided' in str(e):
print("[System] 当前Key已超额或者不正常,正在切换")
self.key_stat[openai.api_key]['exceed'] = True
self.save_key_record()
response, is_switched = self.handle_switch_key(req)
if not is_switched:
# 所有Key都超额或不正常
raise e
else:
response = openai.Image.create(
prompt=req[0]['content'],
n=img_num,
size=img_size
)
image_url = []
for i in range(img_num):
image_url.append(response['data'][i]['url'])
return image_url
def handle_switch_key(self, req):
# messages = [{"role": "user", "content": prompt}]
while True:
is_all_exceed = True
for key in self.key_stat:
if not self.key_stat[key]['exceed']:
is_all_exceed = False
openai.api_key = key
print(f"[System] 切换到Key: {key}, 已使用token: {self.key_stat[key]['used']}")
if len(req) > 0:
try:
response = openai.ChatCompletion.create(
messages=req,
**self.chatGPT_configs
)
return response, True
except Exception as e:
print(e)
if 'You exceeded' in str(e):
print("[System] 当前Key已超额,正在切换")
self.key_stat[openai.api_key]['exceed'] = True
self.save_key_record()
time.sleep(1)
continue
else:
return True
if is_all_exceed:
print("[System] 所有Key已超额")
return None, False
def getConfigs(self):
return self.openai_configs
def save_key_record(self):
with open(key_record_path, 'w', encoding='utf-8') as f:
json.dump(self.key_stat, f)
def get_key_stat(self):
return self.key_stat
def get_key_list(self):
return self.key_list
# 添加key
def append_key(self, key, sponsor):
self.key_list.append(key)
self.key_stat[key] = {'exceed': False, 'used': 0, 'sponsor': sponsor}
self.save_key_record()
self.init_key_record()
# 检查key是否可用
def check_key(self, key):
pre_key = openai.api_key
openai.api_key = key
messages = [{"role": "user", "content": "1"}]
try:
response = openai.ChatCompletion.create(
messages=messages,
**self.chatGPT_configs
)
openai.api_key = pre_key
return True
except Exception as e:
pass
openai.api_key = pre_key
return False
#将key_list的key转储到key_record中,并记录相关数据
def init_key_record(self):
if not os.path.exists(key_record_path):
with open(key_record_path, 'w', encoding='utf-8') as f:
json.dump({}, f)
with open(key_record_path, 'r', encoding='utf-8') as keyfile:
try:
self.key_stat = json.load(keyfile)
except Exception as e:
print(e)
self.key_stat = {}
finally:
for key in self.key_list:
if key not in self.key_stat:
self.key_stat[key] = {'exceed': False, 'used': 0}
# if openai.api_key is None:
# openai.api_key = key
else:
# if self.key_stat[key]['exceed']:
# print(f"Key: {key} 已超额")
# continue
# else:
# if openai.api_key is None:
# openai.api_key = key
# print(f"使用Key: {key}, 已使用token: {self.key_stat[key]['used']}")
pass
if openai.api_key == None:
self.handle_switch_key("")
self.save_key_record()
+390 -292
View File
@@ -1,12 +1,7 @@
import io
import botpy
from PIL import Image
from botpy.message import Message
from botpy.types.message import Reference
import yaml
import re
from util.errors.errors import PromptExceededError
from botpy.message import DirectMessage
import json
import threading
@@ -18,7 +13,15 @@ import os
import sys
from cores.qqbot.personality import personalities
from addons.baidu_aip_judge import BaiduJudge
from model.platform.qqchan import QQChan
from model.platform.qq import QQ
from nakuru import (
CQHTTP,
GroupMessage,
GroupMemberIncrease,
FriendMessage
)
from nakuru.entities.components import Plain,At
# QQBotClient实例
client = ''
@@ -59,48 +62,49 @@ direct_message_mode = True
abs_path = os.path.dirname(os.path.realpath(sys.argv[0])) + '/'
# 版本
version = '2.9'
version = '3.0'
# 语言模型提供商
# 语言模型
REV_CHATGPT = 'rev_chatgpt'
OPENAI_OFFICIAL = 'openai_official'
REV_ERNIE = 'rev_ernie'
REV_EDGEGPT = 'rev_edgegpt'
provider = ''
# 逆向库对象及负载均衡
rev_chatgpt = []
provider = None
chosen_provider = None
# 逆向库对象
rev_chatgpt = None
# gpt配置信息
gpt_config = {}
# 百度内容审核实例
baidu_judge = None
# 回复前缀
reply_prefix = ''
reply_prefix = {}
# 关键词回复
keywords = {}
# QQ频道机器人
qqchannel_bot = None
PLATFORM_QQCHAN = 'qqchan'
qqchan_loop = None
# QQ机器人
gocq_bot = None
PLATFORM_GOCQ = 'gocq'
gocq_app = CQHTTP(
host="127.0.0.1",
port=6700,
http_port=5700,
)
gocq_loop = None
nick_qq = "ai "
bing_cache_loop = None
def new_sub_thread(func, args=()):
thread = threading.Thread(target=func, args=args, daemon=True)
thread.start()
class botClient(botpy.Client):
# 收到At消息
async def on_at_message_create(self, message: Message):
toggle_count(at=True, message=message)
message_reference = Reference(message_id=message.id, ignore_get_message_error=False)
# executor.submit(oper_msg, message, True)
new_sub_thread(oper_msg, (message, True, message_reference))
# await oper_msg(message=message, at=True)
# 收到私聊消息
async def on_direct_message_create(self, message: DirectMessage):
if direct_message_mode:
toggle_count(at=False, message=message)
# executor.submit(oper_msg, message, True)
# await oper_msg(message=message, at=False)
new_sub_thread(oper_msg, (message, False))
thread.start()
# 写入统计信息
def toggle_count(at: bool, message):
@@ -162,55 +166,53 @@ def upload():
初始化机器人
'''
def initBot(cfg, prov):
global chatgpt, provider, rev_chatgpt, baidu_judge, rev_ernie, rev_edgegpt
global reply_prefix, now_personality, gpt_config, config, uniqueSession, frequency_count, frequency_time,announcement, direct_message_mode, version
global command_openai_official, command_rev_chatgpt, command_rev_edgegpt
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
provider = prov
config = cfg
reply_prefix_config = None
if 'reply_prefix' in cfg:
reply_prefix_config = cfg['reply_prefix']
reply_prefix = cfg['reply_prefix']
# 语言模型提供商
if prov == REV_CHATGPT:
if 'account' in cfg['rev_ChatGPT']:
from model.provider.provider_rev_chatgpt import ProviderRevChatGPT
from model.command.command_rev_chatgpt import CommandRevChatGPT
for i in range(0, len(cfg['rev_ChatGPT']['account'])):
try:
print(f"[System] 创建rev_ChatGPT负载{str(i)}: " + str(cfg['rev_ChatGPT']['account'][i]))
revstat = {
'obj': ProviderRevChatGPT(cfg['rev_ChatGPT']['account'][i]),
'busy': False
}
rev_chatgpt.append(revstat)
except:
print("[System] 创建rev_ChatGPT负载失败")
command_rev_chatgpt = CommandRevChatGPT(rev_chatgpt)
if REV_CHATGPT in prov:
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:
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:
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
if REV_CHATGPT in reply_prefix_config:
reply_prefix = reply_prefix_config[REV_CHATGPT]
else:
input("[System-err] 请退出本程序, 然后在配置文件中填写rev_ChatGPT相关配置")
elif prov == OPENAI_OFFICIAL:
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)
if OPENAI_OFFICIAL in reply_prefix_config:
reply_prefix = reply_prefix_config[OPENAI_OFFICIAL]
# elif prov == REV_ERNIE:
# from addons.revERNIE import revernie
# rev_ernie = revernie.wx
elif prov == REV_EDGEGPT:
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)
if REV_EDGEGPT in reply_prefix_config:
reply_prefix = reply_prefix_config[REV_EDGEGPT]
# 得到关键词
if os.path.exists("keyword.json"):
with open("keyword.json", 'r', encoding='utf-8') as f:
keywords = json.load(f)
# 检查provider设置偏好
if os.path.exists("provider_preference.txt"):
with open("provider_preference.txt", 'r', encoding='utf-8') as f:
res = f.read()
if res in prov:
chosen_provider = res
# 百度内容审核
if 'baidu_aip' in cfg and 'enable' in cfg['baidu_aip'] and cfg['baidu_aip']['enable']:
try:
@@ -238,16 +240,6 @@ def initBot(cfg, prov):
direct_message_mode = cfg['direct_message_mode']
print("[System] 私聊功能: "+str(direct_message_mode))
# 得到版本
if 'version' in cfg:
try:
f = open(abs_path+"version.txt", 'r', encoding='utf-8')
version = f.read()
except:
print('[System-Err] 读取更新记录文件失败')
# version = 'Unknown'
# print("[System] QQChannelChatGPT版本: "+str(version))
# 得到发言频率配置
if 'limit' in cfg:
print('[System] 发言频率配置: '+str(cfg['limit']))
@@ -256,7 +248,6 @@ def initBot(cfg, prov):
if 'time' in cfg['limit']:
frequency_time = cfg['limit']['time']
announcement += '[QQChannelChatGPT项目,觉得好用的话欢迎前往Github给Star]\n所有回答与腾讯公司无关。出现问题请前往[GPT机器人]官方频道\n\n'
# 得到公告配置
if 'notice' in cfg:
print('[System] 公告配置: '+cfg['notice'])
@@ -274,91 +265,67 @@ def initBot(cfg, prov):
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!")
print("[System] 请给https://github.com/Soulter/QQChannelChatGPT点个star!")
# input("\n仔细阅读完以上信息后,输入任意信息并回车以继续")
try:
run_bot(cfg['qqbot']['appid'], cfg['qqbot']['token'])
except BaseException as e:
input(f"\n[System-Error] 启动QQ机器人时出现错误,原因如下:{e}\n可能是没有填写QQBOT appid和token?请在config中完善你的appid和token\n配置教程:https://soulter.top/posts/qpdg.html\n")
print("\n[System] 如果有任何问题, 请在 https://github.com/Soulter/QQChannelChatGPT 上提交issue说明问题!或者添加QQ905617992")
print("[System] 请给 https://github.com/Soulter/QQChannelChatGPT 点个star!")
'''
启动机器人
'''
def run_bot(appid, token):
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
# QQ频道
if 'qqbot' in cfg and cfg['qqbot']['enable']:
print("[System] 启用QQ频道机器人")
global qqchannel_bot, qqchan_loop
qqchannel_bot = QQChan()
qqchan_loop = asyncio.new_event_loop()
thread_inst = threading.Thread(target=run_qqchan_bot, args=(cfg, qqchan_loop, qqchannel_bot), daemon=False)
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()
thread_inst.join()
def run_qqchan_bot(cfg, loop, qqchannel_bot):
asyncio.set_event_loop(loop)
intents = botpy.Intents(public_guild_messages=True, direct_message=True)
global client
client = botClient(intents=intents)
client.run(appid=appid, token=token)
'''
负载均衡,得到逆向ChatGPT回复
'''
def get_rev_ChatGPT_response(prompts_str):
res = ''
print("[Debug] "+str(rev_chatgpt))
for revstat in rev_chatgpt:
if not revstat['busy']:
try:
revstat['busy'] = True
print("[Debug] 使用逆向ChatGPT回复ing", end='', flush=True)
res = revstat['obj'].text_chat(prompts_str)
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
'''
回复QQ消息
'''
def send_qq_msg(message, res, image_mode=False, msg_ref = None):
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), client.loop)
else:
reply_res = asyncio.run_coroutine_threadsafe(message.reply(content=res), client.loop)
reply_res.result()
except BaseException as e:
if "msg over length" in str(e):
split_res = []
split_res.append(res[:len(res)//2])
split_res.append(res[len(res)//2:])
for i in split_res:
if msg_ref is not None:
reply_res = asyncio.run_coroutine_threadsafe(message.reply(content=i, message_reference = msg_ref), client.loop)
else:
reply_res = asyncio.run_coroutine_threadsafe(message.reply(content=i), client.loop)
reply_res.result()
else:
print("[System-Error] 回复QQ消息失败")
raise e
else:
pic_res = requests.get(str(res), stream=True)
if pic_res.status_code == 200:
# 将二进制数据转换成图片对象
image = Image.open(io.BytesIO(pic_res.content))
# 保存图片到本地
image.save('tmp_image.jpg')
asyncio.run_coroutine_threadsafe(message.reply(file_image='tmp_image.jpg', content=""), client.loop)
try:
qqchannel_bot.run_bot(client, cfg['qqbot']['appid'], cfg['qqbot']['token'])
except BaseException as e:
input(f"\n[System-Error] 启动QQ频道机器人时出现错误,原因如下:{e}\n可能是没有填写QQBOT appid和token?请在config中完善你的appid和token\n配置教程:https://soulter.top/posts/qpdg.html\n")
def run_gocq_bot(loop, gocq_bot, gocq_app):
asyncio.set_event_loop(loop)
global gocq_client
gocq_client = gocqClient()
try:
gocq_bot.run_bot(gocq_app)
except BaseException as e:
input("启动QQ机器人出现错误"+str(e))
'''
检查发言频率
@@ -381,139 +348,238 @@ def check_frequency(id) -> bool:
user_frequency[id] = t
return True
def save_provider_preference(chosen_provider):
with open('provider_preference.txt', 'w') as f:
f.write(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)
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()
'''
处理消息
group: 群聊模式
'''
def oper_msg(message, at=False, msg_ref = None):
def oper_msg(message, group=False, msg_ref = None, platform = None):
global session_dict, provider
print("[QQBOT] 接收到消息:"+ str(message.content))
qq_msg = ''
session_id = ''
name = ''
user_id = message.author.id
user_name = message.author.username
user_id = ''
user_name = ''
global chosen_provider, reply_prefix, keywords, qqchannel_bot, gocq_bot, gocq_loop, bing_cache_loop
role = "member" # 角色
hit = False # 是否命中指令
command_result = () # 调用指令返回的结果
if platform == PLATFORM_QQCHAN:
print("[QQCHAN-BOT] 接收到消息:"+ str(message.content))
user_id = message.author.id
user_name = message.author.username
global qqchan_loop
if platform == PLATFORM_GOCQ:
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):
send_qq_msg(message, f'{user_name}的发言超过频率限制(╯▔皿▔)╯。\n{frequency_time}秒内只能提问{frequency_count}次。')
qqchannel_bot.send_qq_msg(message, f'{user_name}的发言超过频率限制(╯▔皿▔)╯。\n{frequency_time}秒内只能提问{frequency_count}次。')
return
logf.write("[QQBOT] "+ str(message.content)+'\n')
if platform == PLATFORM_QQCHAN:
if group:
# 频道内
# 过滤@
qq_msg = message.content
lines = qq_msg.splitlines()
for i in range(len(lines)):
lines[i] = re.sub(r"<@!\d+>", "", lines[i])
qq_msg = "\n".join(lines).lstrip().strip()
if uniqueSession:
session_id = user_id
else:
session_id = message.channel_id
# 得到身份
if "2" in message.member.roles or "4" in message.member.roles or "5" in message.member.roles:
print("[QQCHAN-BOT] 检测到管理员身份")
role = "admin"
else:
role = "member"
else:
# 私信
qq_msg = message.content
session_id = user_id
if platform == PLATFORM_GOCQ:
if group:
if isinstance(message.message[0], Plain):
qq_msg = str(message.message[0].text)
elif isinstance(message.message[0], At):
qq_msg = str(message.message[1].text).strip()
else:
return
session_id = message.group_id
else:
qq_msg = message.message[0].text
session_id = message.user_id
# todo: 暂时将所有人设为管理员
role = "admin"
logf.write("[QQBOT] "+ qq_msg+'\n')
logf.flush()
if at:
qq_msg = message.content
lines = qq_msg.splitlines()
for i in range(len(lines)):
lines[i] = re.sub(r"<@!\d+>", "", lines[i])
qq_msg = "\n".join(lines).lstrip().strip()
# 关键词回复
for k in keywords:
if qq_msg == k:
send_message(platform, message, keywords[k], msg_ref=msg_ref, gocq_loop=gocq_loop, qqchannel_bot=qqchannel_bot, gocq_bot=gocq_bot)
return
if uniqueSession:
session_id = user_id
else:
session_id = message.channel_id
else:
qq_msg = message.content
session_id = user_id
if uniqueSession:
name = user_name
else:
name = "频道"
# 这里是预设
if qq_msg.strip() == 'hello' or qq_msg.strip() == '你好' or qq_msg.strip() == '':
send_qq_msg(message, f"你好呀🥰,输入/help查看指令噢", msg_ref=msg_ref)
return
# 关键词拦截器
for i in uw.unfit_words_q:
matches = re.match(i, qq_msg.strip(), re.I | re.M)
if matches:
send_qq_msg(message, f"你的提问得到的回复未通过【自有关键词拦截】服务不予回复。", msg_ref=msg_ref)
send_message(platform, message, f"你的提问得到的回复未通过【自有关键词拦截】服务, 不予回复。", msg_ref=msg_ref, gocq_loop=gocq_loop, qqchannel_bot=qqchannel_bot, gocq_bot=gocq_bot)
return
if baidu_judge != None:
check, msg = baidu_judge.judge(qq_msg)
if not check:
send_qq_msg(message, f"你的提问得到的回复未通过【百度AI内容审核】服务不予回复。\n\n{msg}", msg_ref=msg_ref)
send_message(platform, message, f"你的提问得到的回复未通过【百度AI内容审核】服务, 不予回复。\n\n{msg}", msg_ref=msg_ref, gocq_loop=gocq_loop, qqchannel_bot=qqchannel_bot, gocq_bot=gocq_bot)
return
# 检查是否是更换语言模型的请求
temp_switch = ""
if qq_msg.startswith('/bing') or qq_msg.startswith('/gpt') or qq_msg.startswith('/revgpt'):
target = chosen_provider
if qq_msg.startswith('/bing'):
target = REV_EDGEGPT
elif qq_msg.startswith('/gpt'):
target = OPENAI_OFFICIAL
elif qq_msg.startswith('/revgpt'):
target = REV_CHATGPT
l = qq_msg.split(' ')
if len(l) > 1 and l[1] != "":
# 临时对话模式,先记录下之前的语言模型,回答完毕后再切回
temp_switch = chosen_provider
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
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)
return
chatgpt_res = ""
if provider == OPENAI_OFFICIAL:
# 检查指令
hit, command_result = command_openai_official.check_command(qq_msg, session_id, user_name)
print(f"{hit} {command_result}")
# hit: 是否触发指令
if hit:
if command_result != None and command_result[0]:
# 是否是画图模式
if len(command_result) == 3 and command_result[2] == 'image':
if chosen_provider == OPENAI_OFFICIAL:
hit, command_result = command_openai_official.check_command(qq_msg, session_id, user_name, role, platform=platform)
# hit: 是否触发了指令.
if not hit:
# 请求ChatGPT获得结果
try:
chatgpt_res = chatgpt.text_chat(qq_msg, session_id)
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)
elif chosen_provider == REV_CHATGPT:
hit, command_result = command_rev_chatgpt.check_command(qq_msg, role, platform=platform)
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)
elif chosen_provider == REV_EDGEGPT:
if bing_cache_loop == None:
if platform == PLATFORM_GOCQ:
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, platform=platform)
if not hit:
try:
while rev_edgegpt.is_busy():
time.sleep(1)
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)
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还是不想继续话题,大概率说明提问有问题。
asyncio.run_coroutine_threadsafe(rev_edgegpt.forget(), bing_cache_loop).result()
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)
# 切换回原来的语言模型
if temp_switch != "":
chosen_provider = temp_switch
# 指令回复
if hit:
# 检查指令. command_result是一个元组:(指令调用是否成功, 指令返回的文本结果, 指令类型)
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)
# 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':
for i in command_result[1]:
send_qq_msg(message, i, image_mode=True, msg_ref=command_result[2])
else: send_qq_msg(message, command_result[1], msg_ref=msg_ref)
send_message(platform, message, i, msg_ref=msg_ref, 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)
else:
send_qq_msg(message, f"指令调用错误: \n{command_result[1]}", msg_ref=msg_ref)
return
# 请求chatGPT获得结果
try:
chatgpt_res = reply_prefix + chatgpt.text_chat(qq_msg, session_id)
except (BaseException) as e:
print("[System-Err] OpenAI API错误。原因如下:\n"+str(e))
if 'exceeded' in str(e):
send_qq_msg(message, f"OpenAI API错误。原因:\n{str(e)} \n超额了。可自己搭建一个机器人(Github仓库:QQChannelChatGPT)")
return
else:
f_res = re.sub(r'(https|http)?:\/\/(\w|\.|\/|\?|\=|\&|\%)*\b', '[被隐藏的链接]', str(e), flags=re.MULTILINE)
f_res = f_res.replace(".", "·")
send_qq_msg(message, f"OpenAI API错误。原因如下:\n{f_res} \n前往官方频道反馈~")
return
elif provider == REV_CHATGPT:
hit, command_result = command_rev_chatgpt.check_command(qq_msg)
if hit:
if command_result != None and command_result[0]:
send_qq_msg(message, command_result[1], msg_ref=msg_ref)
else:
send_qq_msg(message, f"指令调用错误: \n{command_result[1]}", msg_ref=msg_ref)
return
try:
chatgpt_res = reply_prefix+str(get_rev_ChatGPT_response(qq_msg))
except BaseException as e:
print("[System-Err] Rev ChatGPT API错误。原因如下:\n"+str(e))
send_qq_msg(message, f"Rev ChatGPT API错误。原因如下:\n{str(e)} \n前往官方频道反馈~")
return
# elif provider == REV_ERNIE:
# try:
# chatgpt_res = reply_prefix+str(rev_ernie.chatViaSelenium(qq_msg))
# except BaseException as e:
# print("[System-Err] Rev ERNIE API错误。原因如下:\n"+str(e))
# send_qq_msg(message, f"Rev ERNIE API错误。原因如下:\n{str(e)} \n前往官方频道反馈~")
# return
elif provider == REV_EDGEGPT:
hit, command_result = command_rev_edgegpt.check_command(qq_msg, client.loop)
if hit:
if command_result != None and command_result[0]:
try:
send_qq_msg(message, command_result[1], msg_ref=msg_ref)
except BaseException as e:
t = command_result[1].replace(".", " . ")
send_qq_msg(message, t, msg_ref=msg_ref)
else:
send_qq_msg(message, f"指令调用错误: \n{command_result[1]}", msg_ref=msg_ref)
return
try:
if rev_edgegpt.is_busy():
send_qq_msg(message, f"[RevBing] 正忙,请稍后再试",msg_ref=msg_ref)
return
else:
chatgpt_res = reply_prefix
chatgpt_res += str(asyncio.run_coroutine_threadsafe(rev_edgegpt.text_chat(qq_msg), client.loop).result())
except BaseException as e:
print("[System-Err] Rev NewBing API错误。原因如下:\n"+str(e))
send_qq_msg(message, f"Rev NewBing API错误。原因如下:\n{str(e)} \n前往官方频道反馈~")
return
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)
return
if chatgpt_res == "":
return
# 记录日志
logf.write(f"{reply_prefix} {str(chatgpt_res)}\n")
logf.flush()
@@ -527,34 +593,20 @@ def oper_msg(message, at=False, msg_ref = None):
if baidu_judge != None:
check, msg = baidu_judge.judge(judged_res)
if not check:
send_qq_msg(message, f"你的提问得到的回复【百度内容审核】未通过,不予回复。\n\n{msg}", msg_ref=msg_ref)
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
# 发送qq信息
try:
# 防止被qq频道过滤消息
gap_chatgpt_res = judged_res.replace(".", " . ")
send_qq_msg(message, ''+gap_chatgpt_res, msg_ref=msg_ref)
# 发送信息
send_message(platform, message, chatgpt_res, msg_ref=msg_ref, gocq_loop=gocq_loop, qqchannel_bot=qqchannel_bot, gocq_bot=gocq_bot)
except BaseException as e:
print("QQ频道API错误: \n"+str(e))
f_res = ""
for t in chatgpt_res:
f_res += t + ' '
try:
send_qq_msg(message, ''+f_res, msg_ref=msg_ref)
# send(message, f"QQ频道API错误:{str(e)}\n下面是格式化后的回答:\n{f_res}")
except BaseException as e:
# 如果还是不行则过滤url
f_res = re.sub(r'(https|http)?:\/\/(\w|\.|\/|\?|\=|\&|\%)*\b', '[被隐藏的链接]', str(e), flags=re.MULTILINE)
f_res = f_res.replace(".", "·")
send_qq_msg(message, ''+f_res, msg_ref=msg_ref)
# send(message, f"QQ频道API错误:{str(e)}\n下面是格式化后的回答:\n{f_res}")
print("回复消息错误: \n"+str(e))
'''
获取统计信息
'''
def get_stat(self):
try:
f = open(abs_path+"configs/stat", "r", encoding="utf-8")
fjson = json.loads(f.read())
@@ -577,4 +629,50 @@ def get_stat(self):
session_count += 1
return guild_count, guild_msg_count, guild_direct_msg_count, session_count
except:
return -1, -1, -1, -1
return -1, -1, -1, -1
# QQ频道机器人
class botClient(botpy.Client):
# 收到频道消息
async def on_at_message_create(self, message: Message):
toggle_count(at=True, message=message)
message_reference = Reference(message_id=message.id, ignore_get_message_error=False)
new_sub_thread(oper_msg, (message, True, message_reference, PLATFORM_QQCHAN))
# 收到私聊消息
async def on_direct_message_create(self, message: DirectMessage):
if direct_message_mode:
toggle_count(at=False, message=message)
new_sub_thread(oper_msg, (message, False, None, PLATFORM_QQCHAN))
# QQ机器人
class gocqClient():
# 收到群聊消息
@gocq_app.receiver("GroupMessage")
async def _(app: CQHTTP, source: GroupMessage):
global nick_qq
if isinstance(source.message[0], Plain):
if source.message[0].text.startswith(nick_qq):
source.message[0].text = source.message[0].text[len(nick_qq):]
new_sub_thread(oper_msg, (source, True, None, PLATFORM_GOCQ))
if isinstance(source.message[0], At):
if source.message[0].qq == source.self_id:
if source.message[1].text.startswith(nick_qq):
source.message[1].text = source.message[0].text[len(nick_qq):]
new_sub_thread(oper_msg, (source, True, None, PLATFORM_GOCQ))
else:
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")
])
+46 -59
View File
@@ -1,69 +1,56 @@
# -*- coding: utf-8 -*-
from git.repo import Repo
import git
import os
# import zipfile
if __name__ == "__main__":
# 检测文件夹
if not os.path.exists('QQChannelChatGPT'):
os.mkdir('QQChannelChatGPT')
# if not os.path.exists('pythonemb'):
# os.mkdir('pythonemb')
# python_path = os.path.join('pythonemb', 'python.zip')
# 检测Python环境
# if not os.path.exists('pythonemb/python.exe'):
# print("正在从https://www.python.org/ftp/python/3.10.10/python-3.10.10-embed-amd64.zip安装Python环境...")
# os.system('curl -o pythonemb/python.zip https://www.python.org/ftp/python/3.10.10/python-3.10.10-embed-amd64.zip')
# print("解压中...")
# file=zipfile.ZipFile(python_path)
# for name in file.namelist():
# file.extract(name, path='pythonemb')
# print("解压完毕, 创建pip...")
# os.system('curl https://bootstrap.pypa.io/get-pip.py -o pythonemb\get-pip.py')
# os.system('pythonemb\python.exe pythonemb\get-pip.py')
# print("pip创建完毕")
# print("Python环境安装完成")
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
# 检测文件夹
if not os.path.exists('QQChannelChatGPT'):
os.mkdir('QQChannelChatGPT')
project_path = os.path.join('QQChannelChatGPT')
try:
origin.fetch()
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:
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='61-enhancement-重构代码增强稳定性')
print("项目拉取完毕")
print("【重要提醒】如果你没有Python环境, 请先安装Python环境! 否则接下来的操作会造成闪退。")
print("【重要提醒】Python3.9淘宝下载地址: https://npm.taobao.org/mirrors/python/3.9.7/python-3.9.7-amd64.exe ")
print("【重要提醒】安装时, 请务必勾选“Add Python 3.9 to PATH”选项。")
print("【重要提醒】QQ: 905617992")
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')
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("程序出错。可以截图发给QQ:905617992.按下回车键退出...")
+24 -129
View File
@@ -1,18 +1,12 @@
import threading
import time
import asyncio
import os, sys
import signal
import requests,json
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)
@@ -22,134 +16,43 @@ def main(loop, event):
os.environ['HTTPS_PROXY'] = cfg['https_proxy']
provider = privider_chooser(cfg)
print('[System] 当前语言模型提供商: ' + provider)
print('[System] 当前语言模型提供商: ' + str(provider))
# 执行Bot
qqBot.initBot(cfg, provider)
# 语言模型提供商选择器
# 目前有:OpenAI官方API、逆向库
def privider_chooser(cfg):
l = []
if 'rev_ChatGPT' in cfg and cfg['rev_ChatGPT']['enable']:
return 'rev_chatgpt'
elif 'rev_ernie' in cfg and cfg['rev_ernie']['enable']:
return 'rev_ernie'
elif 'rev_edgegpt' in cfg and cfg['rev_edgegpt']['enable']:
return 'rev_edgegpt'
else:
return 'openai_official'
# 仅支持linux
def hot_update():
target = 'target.tar'
time.sleep(5)
while(True):
if os.path.exists('version.txt'):
version_file = open('version.txt', 'r', encoding='utf-8')
vs = version_file.read()
version = float(vs)
else:
version = 0
if not os.path.exists(target):
version = 0
try:
res = requests.get("https://soulter.top/channelbot/update.json")
res_obj = json.loads(res.text)
ol_version = float(res_obj['version'])
if ol_version > version:
print('发现新版本: ' + str(ol_version))
res = requests.get(res_obj['linux-url'], stream=True)
filesize = res.headers["Content-Length"]
print('文件大小: ' + str(int(filesize) / 1024 / 1024) + 'MB')
print('正在更新文件...')
chunk_size = 1024
times = int(filesize) // chunk_size
show = 1 / times
show2 = 1 / times
start = 1
with open(target, "wb") as pyFile:
for chunk in res.iter_content(chunk_size=chunk_size):
if chunk:
pyFile.write(chunk)
if start <= times:
print(f"\r下载进度: {show:.2%}",end="",flush=True)
start += 1
show += show2
else:
sys.stdout.write(f"下载进度: 100%\n")
print('更新完成')
print('解压覆盖')
os.system(f"tar -zxvf {target}")
version = ol_version
version_file = open('version.txt', 'w+', encoding='utf-8')
version_file.write(str(res_obj['version']))
version_file.flush()
version_file.close()
try:
update_version(version)
except BaseException as e:
print(e)
print('自启动')
py = sys.executable
os.execl(py, py, *sys.argv)
time.sleep(60*60*3)
except BaseException as e:
print(e)
print("upd出现异常, 请联系QQ905617992")
time.sleep(60*60*3)
def update_version(ver):
if not os.path.exists('update_record'):
object_id = ''
else:
object_id = open("update_record", 'r', encoding='utf-8').read()
addr = 'unknown'
try:
addr = requests.get('http://myip.ipip.net', timeout=5).text
except BaseException:
pass
try:
ts = str(time.time())
# md = hashlib.md5((ts+'QAZ1rQLY1ZufHrZlpuUiNff7').encode())
headers = {
'X-LC-Id': 'UqfXTWW15nB7iMT0OHvYrDFb-gzGzoHsz',
'X-LC-Key': 'QAZ1rQLY1ZufHrZlpuUiNff7',
'Content-Type': 'application/json'
}
d = {"data": {'version':'win-hot-update'+str(ver), 'addr': addr}}
d = json.dumps(d).encode("utf-8")
res = requests.put(f'https://uqfxtww1.lc-cn-n1-shared.com/1.1/classes/version_record/{object_id}', headers = headers, data = d)
if json.loads(res.text)['code'] == 1:
res = requests.post(f'https://uqfxtww1.lc-cn-n1-shared.com/1.1/classes/version_record', headers = headers, data = d)
object_id = json.loads(res.text)['objectId']
object_id_file = open("update_record", 'w+', encoding='utf-8')
object_id_file.write(str(object_id))
object_id_file.flush()
object_id_file.close()
except BaseException as e:
print(e)
l.append('rev_chatgpt')
if 'rev_ernie' in cfg and cfg['rev_ernie']['enable']:
l.append('rev_ernie')
if 'rev_edgegpt' in cfg and cfg['rev_edgegpt']['enable']:
l.append('rev_edgegpt')
if 'openai' in cfg and cfg['openai']['key'] != None and len(cfg['openai']['key'])>0:
l.append('openai_official')
return l
def check_env():
if not (sys.version_info.major == 3 and sys.version_info.minor >= 8):
print("请使用Python3.8运行本项目")
input("按任意键退出...")
exit()
try:
import openai
import botpy
import yaml
except Exception as e:
# print(e)
try:
print("安装依赖库...")
os.system("pip3 install openai")
os.system("pip3 install qq-botpy")
os.system("pip3 install pyyaml")
print("安装依赖库完毕...")
except BaseException:
print("\n安装第三方库异常.请自行安装或者联系QQ905617992.")
# 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('pip3 install -r QQChannelChatGPT'+ os.sep +'requirements.txt')
# os.system("clear")
# print("安装依赖库完毕...")
# except BaseException as e:
# print("安装依赖库失败,请手动安装依赖库。")
# print(e)
# input("按任意键退出...")
# exit()
# 检查key
with open(abs_path+"configs/config.yaml", 'r', encoding='utf-8') as ymlfile:
import yaml
@@ -172,15 +75,7 @@ def get_platform():
print("other")
if __name__ == "__main__":
global pid
pid = os.getpid()
global ma_type
print("程序PID:"+str(pid))
check_env()
bot_event = threading.Event()
loop = asyncio.get_event_loop()
# ma_type = get_platform()
# if ma_type == 'linux':
# threading.Thread(target=hot_update).start()
main(loop, bot_event)
+132 -25
View File
@@ -1,53 +1,168 @@
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
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 check_command(self, message, role, platform):
if self.command_start_with(message, "nick"):
return True, self.set_nick(message, platform)
return False, None
'''
存储机器人的昵称
'''
def set_nick(self, message: str, platform: str):
if platform == PLATFORM_GOCQ:
nick = message.split(" ")[1]
self.general_command_storer("nick_qq", nick)
return True, f"设置成功!现在你可以叫我{nick}来提问我啦~", "nick"
elif platform == PLATFORM_QQCHAN:
nick = message.split(" ")[2]
return False, "QQ频道平台不支持为机器人设置昵称。", "nick"
def update(self, message: str):
"""
存储指令结果到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进行会话",
}
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:
if message.startswith(arg) or message.startswith('/'+arg):
return True
return False
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(" ")
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]
else:
keyword = {l[1]: l[2]}
with open("keyword.json", "w", encoding="utf-8") as f:
json.dump(keyword, f, ensure_ascii=False, indent=4)
f.flush()
return True, "设置成功: "+l[1]+" -> "+l[2], "keyword"
except BaseException as e:
return False, "设置失败: "+str(e), "keyword"
def update(self, message: str, role: str):
if role != "admin":
return True, "你没有权限使用该指令", "keyword"
l = message.split(" ")
if len(l) == 1:
# 得到本地版本号和最新版本号
repo = Repo()
try:
repo = Repo()
except git.exc.InvalidGitRepositoryError:
repo = Repo(path="QQChannelChatGPT")
now_commit = repo.head.commit
# 得到最新的5条commit列表, 包含commit信息
# 得到远程3条commit列表, 包含commit信息
origin = repo.remotes.origin
origin.fetch()
commits = list(repo.iter_commits('master', max_count=5))
commits = list(repo.iter_commits('master', max_count=3))
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]
return True, f"当前版本: {now_commit.hexsha[:6]}\n最新版本: {remote_commit_hash}\n\n最新5条commit:\n{str(commits_log)}\n使用update latest更新至最新版本\n"
return True, f"当前版本: {now_commit.hexsha[:6]}\n最新版本: {remote_commit_hash}\n\n3条commit(非最新):\n{str(commits_log)}\n使用update latest更新至最新版本\n", "update"
else:
if l[1] == "latest":
pash_tag = ""
try:
repo = Repo()
try:
repo = Repo()
except git.exc.InvalidGitRepositoryError:
repo = Repo(path="QQChannelChatGPT")
pash_tag = "QQChannelChatGPT"+os.sep
repo.remotes.origin.pull()
py = sys.executable
os.execl(py, py, *sys.argv)
return True, "更新成功"
# 检查是否是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)
return False, "更新失败: "+str(e), "update"
if l[1] == "r":
py = sys.executable
os.execl(py, py, *sys.argv)
def reset(self):
return False
@@ -62,15 +177,7 @@ class Command:
return False
def help(self):
# ol_version = 'Unknown'
# try:
# res = requests.get("https://soulter.top/channelbot/update.json")
# res_obj = json.loads(res.text)
# ol_version = res_obj['version']
# except BaseException:
# pass
return True, f"[Github项目名: QQChannelChatGPT,有问题请前往提交issue,欢迎Star此项目~]\n\n指令面板:\nstatus 查看机器人key状态\ncount 查看机器人统计信息\nreset 重置会话\nhis 查看历史记录\ntoken 查看会话token数\nhelp 查看帮助\nset 人格指令菜单\nkey 动态添加key"
return False
def status(self):
return False
+52 -33
View File
@@ -6,37 +6,56 @@ class CommandOpenAIOfficial(Command):
def __init__(self, provider: ProviderOpenAIOfficial):
self.provider = provider
def check_command(self, message: str, session_id: str, user_name: str):
if message.startswith("reset") or message.startswith("重置"):
def check_command(self, message: str, session_id: str, user_name: str, role, platform: str):
hit, res = super().check_command(message, role, platform)
if hit:
return True, res
if self.command_start_with(message, "reset", "重置"):
return True, self.reset(session_id)
elif message.startswith("his") or message.startswith("历史"):
elif self.command_start_with(message, "his", "历史"):
return True, self.his(message, session_id, user_name)
elif message.startswith("token"):
elif self.command_start_with(message, "token"):
return True, self.token(session_id)
elif message.startswith("gpt"):
elif self.command_start_with(message, "gpt"):
return True, self.gpt()
elif message.startswith("status") or message.startswith("状态"):
elif self.command_start_with(message, "status"):
return True, self.status()
elif message.startswith("count") or message.startswith("统计"):
elif self.command_start_with(message, "count"):
return True, self.count()
elif message.startswith("help") or message.startswith("帮助"):
elif self.command_start_with(message, "help", "帮助"):
return True, self.help()
elif message.startswith("key") or message.startswith("动态添加key"):
return True, self.key(message, user_name)
elif message.startswith("unset"):
elif self.command_start_with(message, "unset"):
return True, self.unset(session_id)
elif message.startswith("set"):
elif self.command_start_with(message, "set"):
return True, self.set(message, session_id)
elif message.startswith(""):
elif self.command_start_with(message, "update"):
return True, self.update(message, role)
elif self.command_start_with(message, ""):
return True, self.draw(message)
elif message.startswith("update"):
return True, self.update(message)
elif self.command_start_with(message, "keyword"):
return True, self.keyword(message, role)
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):
self.provider.forget(session_id)
return True, "重置成功"
return True, "重置成功", "reset"
def his(self, message: str, session_id: str, name: str):
#分页,每页5条
@@ -48,17 +67,17 @@ class CommandOpenAIOfficial(Command):
# 检查是否有过历史记录
if session_id not in self.provider.session_dict:
msg = f"历史记录为空"
return True, msg
return True, msg, "his"
l = self.provider.session_dict[session_id]
max_page = len(l)//size_per_page + 1 if len(l)%size_per_page != 0 else len(l)//size_per_page
p = self.provider.get_prompts_by_cache_list(self.provider.session_dict[session_id], divide=True, paging=True, size=size_per_page, page=page)
return True, f"历史记录如下:\n{p}\n{page}页 | 共{max_page}\n*输入/his 2跳转到第2页"
return True, f"历史记录如下:\n{p}\n{page}页 | 共{max_page}\n*输入/his 2跳转到第2页", "his"
def token(self, session_id: str):
return True, f"会话的token数: {self.provider.get_user_usage_tokens(self.provider.session_dict[session_id])}\n系统最大缓存token数: {self.provider.max_tokens}"
return True, f"会话的token数: {self.provider.get_user_usage_tokens(self.provider.session_dict[session_id])}\n系统最大缓存token数: {self.provider.max_tokens}", "token"
def gpt(self):
return True, f"OpenAI GPT配置:\n {self.provider.chatGPT_configs}"
return True, f"OpenAI GPT配置:\n {self.provider.chatGPT_configs}", "gpt"
def status(self):
chatgpt_cfg_str = ""
@@ -78,52 +97,52 @@ class CommandOpenAIOfficial(Command):
sponsor = key_stat[key]['sponsor']
chatgpt_cfg_str += f" |-{index}: {key_stat[key]['used']}/{max} {sponsor}赞助{tag}\n"
index += 1
return True, f"⭐使用情况({str(gg_count)}个已用):\n{chatgpt_cfg_str}⏰全频道已用{total}tokens"
return True, f"⭐使用情况({str(gg_count)}个已用):\n{chatgpt_cfg_str}⏰全频道已用{total}tokens", "status"
def count(self):
guild_count, guild_msg_count, guild_direct_msg_count, session_count = self.provider.get_stat()
return True, f"当前会话数: {len(self.provider.session_dict)}\n共有频道数: {guild_count} \n共有消息数: {guild_msg_count}\n私信数: {guild_direct_msg_count}\n历史会话数: {session_count}"
return True, f"当前会话数: {len(self.provider.session_dict)}\n共有频道数: {guild_count} \n共有消息数: {guild_msg_count}\n私信数: {guild_direct_msg_count}\n历史会话数: {session_count}", "count"
def key(self, message: str, user_name: str):
l = message.split(" ")
if len(l) == 1:
msg = "感谢您赞助key,key为官方API使用,请以以下格式赞助:\n/key xxxxx"
return True, msg
return True, msg, "key"
key = l[1]
if self.provider.check_key(key):
self.provider.append_key(key, user_name)
return True, f"*★,°*:.☆( ̄▽ ̄)/$:*.°★* 。\n该Key被验证为有效。感谢{user_name}赞助~"
else:
return True, "该Key被验证为无效。也许是输入错误了,或者重试。"
return True, "该Key被验证为无效。也许是输入错误了,或者重试。", "key"
def unset(self, session_id: str):
self.provider.now_personality = {}
self.provider.forget(session_id)
return True, "已清除人格并重置历史记录。"
return True, "已清除人格并重置历史记录。", "unset"
def set(self, message: str, session_id: str):
l = message.split(" ")
if len(l) == 1:
return True, f"【由Github项目QQChannelChatGPT支持】\n\n【人格文本由PlexPt开源项目awesome-chatgpt-pr \
ompts-zh提供】\n\n这个是人格设置指令。\n设置人格: \n/set 人格名。例如/set 编剧\n人格列表: /set list\n人格详细信息: \
/set view 人格名\n自定义人格: /set 人格文本\n清除人格: /unset\n【当前人格】: {str(self.provider.now_personality)}"
/set view 人格名\n自定义人格: /set 人格文本\n清除人格: /unset\n【当前人格】: {str(self.provider.now_personality)}", "set"
elif l[1] == "list":
msg = "人格列表:\n"
for key in personalities.keys():
msg += f" |-{key}\n"
msg += '\n\n*输入/set view 人格名查看人格详细信息'
msg += '\n*不定时更新人格库,请及时更新本项目。'
return True, msg
return True, msg, "set"
elif l[1] == "view":
if len(l) == 2:
return True, "请输入/set view 人格名"
return True, "请输入/set view 人格名", "set"
ps = l[2].strip()
if ps in personalities:
msg = f"人格{ps}的详细信息:\n"
msg += f"{personalities[ps]}\n"
else:
msg = f"人格{ps}不存在"
return True, msg
return True, msg, "set"
else:
ps = l[1].strip()
if ps in personalities:
@@ -141,7 +160,7 @@ class CommandOpenAIOfficial(Command):
'single-tokens': 0
}
self.provider.session_dict[session_id].append(new_record)
return True, f"人格{ps}已设置."
return True, f"人格{ps}已设置.", "set"
else:
self.provider.now_personality = {
'name': '自定义人格',
@@ -157,17 +176,17 @@ class CommandOpenAIOfficial(Command):
}
self.provider.session_dict[session_id] = []
self.provider.session_dict[session_id].append(new_record)
return True, f"自定义人格已设置。 \n人格信息: {ps}"
return True, f"自定义人格已设置。 \n人格信息: {ps}", "set"
def draw(self, message):
try:
# 画图模式传回3个参数
img_url = self.provider.image_chat(message)
return True, img_url, "image"
return True, img_url, "draw"
except Exception as e:
if 'exceeded' in str(e):
return f"OpenAI API错误。原因:\n{str(e)} \n超额了。可自己搭建一个机器人(Github仓库:QQChannelChatGPT)"
return False, f"图片生成失败: {e}"
return False, f"图片生成失败: {e}", "draw"
+18 -11
View File
@@ -5,18 +5,25 @@ class CommandRevChatGPT(Command):
def __init__(self, provider: ProviderRevChatGPT):
self.provider = provider
def check_command(self, message: str):
# hit, res = super().check_command(message)
# if hit:
# return res
# if message.startswith("reset") or message.startswith("重置"):
# return True, self.reset()
if message.startswith("help") or message.startswith("帮助"):
def check_command(self, message: str, role, platform: str):
hit, res = super().check_command(message, role, platform)
if hit:
return True, res
if self.command_start_with(message, "help", "帮助"):
return True, self.help()
elif message.startswith("update"):
return True, self.update(message)
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"
return True, super().help_messager(super().general_commands()), "help"
+16 -8
View File
@@ -5,23 +5,31 @@ class CommandRevEdgeGPT(Command):
def __init__(self, provider: ProviderRevEdgeGPT):
self.provider = provider
def check_command(self, message: str, loop):
if message.startswith("reset") or message.startswith("重置"):
def check_command(self, message: str, loop, role, platform: str):
hit, res = super().check_command(message, role, platform)
if hit:
return True, res
if self.command_start_with(message, "reset"):
return True, self.reset(loop)
elif message.startswith("help") or message.startswith("帮助"):
elif self.command_start_with(message, "help"):
return True, self.help()
elif message.startswith("update"):
return True, self.update(message)
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, loop):
res = asyncio.run_coroutine_threadsafe(self.provider.forget(), loop).result()
print(res)
if res:
return res, "重置成功"
return res, "重置成功", "reset"
else:
return res, "重置失败"
return res, "重置失败", "reset"
def help(self):
return True, "[Github项目名: QQChannelChatGPT,有问题请前往提交issue,欢迎Star此项目~]\n\nRevBing指令面板:\nreset: 重置\nhelp: 帮助"
return True, super().help_messager(super().general_commands()), "help"
View File
+18
View File
@@ -0,0 +1,18 @@
from nakuru.entities.components import Plain
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)
# 通过消息链处理
if source.type == "GroupMessage":
await self.client.sendGroupMessage(source.group_id, [
Plain(text=res)
])
elif source.type == "FriendMessage":
await self.client.sendFriendMessage(source.user_id, [
Plain(text=res)
])
+63
View File
@@ -0,0 +1,63 @@
import io
import botpy
from PIL import Image
import re
import asyncio
import requests
from cores.qqbot.personality import personalities
class QQChan():
def run_bot(self, botclient, appid, token):
intents = botpy.Intents(public_guild_messages=True, direct_message=True)
self.client = botclient
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)
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)
else:
reply_res = asyncio.run_coroutine_threadsafe(message.reply(content=res), self.client.loop)
reply_res.result()
except BaseException as e:
# 分割过长的消息
if "msg over length" in str(e):
split_res = []
split_res.append(res[:len(res)//2])
split_res.append(res[len(res)//2:])
for i in split_res:
if msg_ref is not None:
reply_res = asyncio.run_coroutine_threadsafe(message.reply(content=i, message_reference = msg_ref), self.client.loop)
else:
reply_res = asyncio.run_coroutine_threadsafe(message.reply(content=i), self.client.loop)
reply_res.result()
else:
# 发送qq信息
try:
# 防止被qq频道过滤消息
res = res.replace(".", " . ")
asyncio.run_coroutine_threadsafe(message.reply(content=res), self.client.loop).result()
# 发送信息
except BaseException as e:
print("QQ频道API错误: \n"+str(e))
res = str.join(" ", res)
try:
asyncio.run_coroutine_threadsafe(message.reply(content=res), self.client.loop).result()
except BaseException as e:
# 如果还是不行则报出错误
res = re.sub(r'(https|http)?:\/\/(\w|\.|\/|\?|\=|\&|\%)*\b', '[被隐藏的链接]', str(e), flags=re.MULTILINE)
res = res.replace(".", "·")
asyncio.run_coroutine_threadsafe(message.reply(content=res), self.client.loop).result()
# send(message, f"QQ频道API错误:{str(e)}\n下面是格式化后的回答:\n{f_res}")
else:
pic_res = requests.get(str(res), stream=True)
if pic_res.status_code == 200:
# 将二进制数据转换成图片对象
image = Image.open(io.BytesIO(pic_res.content))
# 保存图片到本地
image.save('tmp_image.jpg')
asyncio.run_coroutine_threadsafe(message.reply(file_image='tmp_image.jpg', content=""), self.client.loop)
+5 -2
View File
@@ -1,6 +1,4 @@
import openai
import yaml
from util.errors.errors import PromptExceededError
import json
import time
import os
@@ -302,6 +300,8 @@ class ProviderOpenAIOfficial(Provider):
while True:
is_all_exceed = True
for key in self.key_stat:
if key == None:
continue
if not self.key_stat[key]['exceed']:
is_all_exceed = False
openai.api_key = key
@@ -326,6 +326,9 @@ class ProviderOpenAIOfficial(Provider):
if is_all_exceed:
print("[System] 所有Key已超额")
return None, False
else:
print("[System] 在切换key时程序异常。")
return None, False
def getConfigs(self):
return self.openai_configs
+45 -9
View File
@@ -3,22 +3,31 @@ from model.provider.provider import Provider
class ProviderRevChatGPT(Provider):
def __init__(self, config):
if 'password' in config:
config['password'] = str(config['password'])
self.bot = Chatbot(config=config)
self.rev_chatgpt = []
for i in range(0, len(config['account'])):
try:
print(f"[System] 创建rev_ChatGPT负载{str(i)}: " + str(config['account'][i]))
if 'password' in config['account'][i]:
config['account'][i]['password'] = str(config['account'][i]['password'])
revstat = {
'obj': Chatbot(config=config['account'][i]),
'busy': False
}
self.rev_chatgpt.append(revstat)
except BaseException as e:
print(f"[System] 创建rev_ChatGPT负载失败: {str(e)}")
def forget(self) -> bool:
self.bot.reset_chat()
return True
def text_chat(self, prompt):
return False
def request_text(self, prompt: str, bot) -> str:
resp = ''
err_count = 0
retry_count = 5
while err_count < retry_count:
try:
for data in self.bot.ask(prompt):
for data in bot.ask(prompt):
resp = data["message"]
break
except BaseException as e:
@@ -31,4 +40,31 @@ class ProviderRevChatGPT(Provider):
err_count += 1
print("[RevChatGPT] "+str(resp))
return resp
return resp
def text_chat(self, prompt):
res = ''
print("[Debug] "+str(self.rev_chatgpt))
for revstat in self.rev_chatgpt:
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账号都有负载, 请稍后再试~'
+37 -7
View File
@@ -1,4 +1,3 @@
import asyncio
from model.provider.provider import Provider
from EdgeGPT import Chatbot, ConversationStyle
import json
@@ -32,18 +31,49 @@ 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 resp == prompt:
resp += '\n\n如果你没有让我复述你的话,那代表我可能不想和你继续这个话题了,请输入reset重置会话😶'
# print("[RevEdgeGPT] "+str(resp))
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 resp:
self.busy = False
return '', 0
if reply_msg == prompt:
# resp += '\n\n如果你没有让我复述你的话,那代表我可能不想和你继续这个话题了,请输入reset重置会话😶'
await self.forget()
err_count += 1
continue
if reply_msg is None:
# 不想答复
return '', 0
else:
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:
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
raise e
print("[RevEdgeGPT] 请求出现了一些问题, 正在重试。次数"+str(err_count))
self.busy = False
print("[RevEdgeGPT] "+str(resp))
return resp
print("[RevEdgeGPT] "+str(reply_msg))
return reply_msg, 1
+9 -8
View File
@@ -1,9 +1,10 @@
requests
openai
qq-botpy
requests~=2.28.1
openai~=0.27.4
qq-botpy~=1.1.2
revChatGPT~=4.0.8
baidu-aip
EdgeGPT~=0.1.2
chardet
Pillow
GitPython
baidu-aip~=4.16.9
EdgeGPT~=0.1.22.1
chardet~=5.1.0
Pillow~=9.4.0
GitPython~=3.1.31
git+https://github.com/Lxns-Network/nakuru-project.git