Compare commits

...

23 Commits

Author SHA1 Message Date
Soulter a833812738 feat: 逆向ChatGPT 2023-03-03 12:20:11 +08:00
Soulter b557bc1ec7 Merge branch 'master' of https://github.com/Soulter/QQChannelChatGPT 2023-03-03 12:09:41 +08:00
Soulter 8ea7f42a1f merge 2023-03-03 12:03:30 +08:00
Soulter 9d553145ca feat: 接入逆向ChatGPT库。
perf: 重构部分代码,提高代码鲁棒性。
2023-03-03 11:58:52 +08:00
Soulter a38f3b9c28 fix: 修复缓存超限报错的问题 2023-03-03 10:08:14 +08:00
Soulter 9122b33fd0 Update README.md 2023-03-02 18:15:17 +08:00
Soulter 91c6767522 Update README.md 2023-03-02 16:58:21 +08:00
Soulter 5cf8df572a Merge pull request #19 from slippersheepig/patch-1
fix: change engine to model to use chatgpt
2023-03-02 11:41:09 +08:00
sheepgreen 64bfac00a9 fix: change engine to model to use chatgpt 2023-03-02 11:39:34 +08:00
Soulter 7407ac0ce1 Update requirements.txt 2023-03-02 10:47:50 +08:00
Soulter 0bb0493404 fix: 修复了一些bug 2023-03-02 10:44:44 +08:00
Soulter 4366572675 feat: 支持/gpt指令,查看配置 2023-03-02 09:46:29 +08:00
Soulter ffa3a0be3f Merge branch 'master' of https://github.com/Soulter/QQChannelChatGPT 2023-03-02 09:34:31 +08:00
Soulter 4dc214b27e feat: 支持官方ChatGPT API
perf: 提高了稳定性
2023-03-02 09:34:16 +08:00
Soulter 2baee48ff7 Update README.md 2023-02-28 19:26:57 +08:00
Soulter 74ae9cbee0 Update README.md 2023-02-28 08:46:52 +08:00
Soulter 1bfa325a6c Update README.md 2023-02-28 08:46:02 +08:00
Soulter 5663c1d6b2 Merge branch 'master' of https://github.com/Soulter/QQChannelChatGPT 2023-02-28 08:35:26 +08:00
Soulter 9a96456a77 feat: 支持在配置文件设置是否关闭私聊功能
fix: 修复了人格功能的一些bug
2023-02-28 08:35:05 +08:00
Soulter 9f741ef749 feat: 支持在配置文件设置是否关闭私聊功能
fix: 修复人格功能的一些bug
2023-02-28 08:34:04 +08:00
Soulter dadaa10924 Update requirements.txt 2023-02-27 09:18:23 +08:00
Soulter 6b596dcb12 Update README.md 2023-02-18 22:59:42 +08:00
Soulter 6d4d2bf84d fix: 修复了/help菜单的一些bug 2023-02-18 22:53:40 +08:00
9 changed files with 458 additions and 223 deletions
+15 -14
View File
@@ -1,23 +1,18 @@
# 2023/3/2 已支持官方ChatGPT API,性能更好!!!
## ⭐体验
扫码加入QQ频道
![image](https://user-images.githubusercontent.com/37870767/217891261-7f2bbede-a70a-4e9b-9cdd-15dcb06f6629.png)
使用手机QQ扫码加入QQ频道(频道名: GPT机器人 | 频道号: x42d56aki2)
![image](https://user-images.githubusercontent.com/37870767/221722540-d4b0fada-4fa2-4063-bb00-1bb3e4d9a747.png)
**推荐Windows一键安装(版本更新更及时)!!**
**请前往Release下载最新版本**
**详细部署教程链接**https://soulter.top/posts/qpdg.html
**详细部署教程链接**https://soulter.top/posts/qpdg.html
**Windows用户推荐Windows一键安装,请前往Release下载最新版本**
**详细部署教程链接**https://soulter.top/posts/qpdg.html
有任何问题请加频道反馈。
## ⭐功能:
### 基本功能
@@ -61,7 +56,7 @@
</details>
<details>
<summary>✅ 支持指令控制</summary>
<summary>✅ 支持多种指令控制</summary>
- 详见下方`指令功能`
@@ -85,18 +80,24 @@
- `/token`查看当前缓存的总token数
- `/count` 查看统计
- `/status` 查看chatGPT的配置
- `/help` 查看帮助
- `/key` 动态添加key
- `/set` 人格设置面板
## 📰使用方法:
**详细部署教程链接**https://soulter.top/posts/qpdg.html
### 安装第三方库
使用Python的pip工具安装
- `qq-botpy` QQ频道官方Python SDK
- `openai` (OpenAI )
- `openai` (OpenAI Python SDK)
```shell
pip install -r requirements.txt
```
> ⚠注意,由于qq-botpy需要运行在`Python 3.8+`的版本上,因此本项目也需要在此之上运行
> ⚠注意,由于qq-botpy需要运行在`Python 3.8+`的版本上,因此本项目也需要在此之上运行
### 配置
+29
View File
@@ -0,0 +1,29 @@
from revChatGPT.V1 import Chatbot
class revChatGPT:
def __init__(self, config):
print("[RevChatGPT] 逆向库初始化:"+str(config))
config['password'] = str(config['password'])
self.chatbot = Chatbot(config=config)
def chat(self, prompt):
resp = ''
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:
print(e)
print("[RevChatGPT] 请求出现了一些问题, 正在重试。次数"+str(err_count))
err_count += 1
if err_count == retry_count:
raise e
print("[RevChatGPT] "+str(resp))
return resp
+42
View File
@@ -0,0 +1,42 @@
openai:
# 注意:在1.7版本已支持多key自动切换,方法:
# key:
# - xxxxx
# - xxxxxx
# 在下方非注释的地方使用以上格式
key:
-
chatGPTConfigs:
engine: "gpt-3.5-turbo"
max_tokens: 1000
temperature: 0.9
top_p: 1
frequency_penalty: 0
presence_penalty: 0
total_tokens_limit: 2000
qqbot:
appid:
token:
# 设置是否一个人一个会话
uniqueSessionMode: false
# QChannelBot 的版本,请勿修改此字段,否则可能产生一些bug
version: 2.4 RealChatGPT Ver.
# [Beta] 转储历史记录时间间隔(分钟)
dump_history_interval: 10
# 一个用户只能在time秒内发送count条消息
limit:
time: 60
count: 5
# 公告
notice: "此机器人由Github项目QQChannelChatGPT驱动。"
# 是否打开私信功能
# 设置为true则频道成员可以私聊机器人。
# 设置为false则频道成员不能私聊机器人。
direct_message_mode: true
+61 -14
View File
@@ -1,20 +1,27 @@
# 如果你不知道怎么部署,请务必查看https://soulter.top/posts/qpdg.html
# 注意:已支持多key自动切换,方法:
# key:
# - sk-xxxxxx
# - sk-xxxxxx
# 在下方非注释的地方使用以上格式
openai:
# 注意:在1.7版本已支持多key自动切换,方法:
# key:
# - xxxxx
# - xxxxxx
# 在下方非注释的地方使用以上格式
key:
-
# 这里是GPT配置,语言模型默认使用gpt-3.5-turbo
chatGPTConfigs:
engine: text-davinci-003
max_tokens: 800
temperature: 0.8
model: gpt-3.5-turbo
max_tokens: 1500
temperature: 0.9
top_p: 1
frequency_penalty: 0.4
presence_penalty: 0.3
total_tokens_limit: 700
frequency_penalty: 0
presence_penalty: 0
total_tokens_limit: 2800
# QQ开放平台的appid和令牌
# q.qq.com
qqbot:
appid:
token:
@@ -22,8 +29,48 @@ qqbot:
# 设置是否一个人一个会话
uniqueSessionMode: false
# QChannelBot 的版本
version: 1.7 beta
# QChannelBot 的版本,请勿修改此字段,否则可能产生一些bug
version: 2.5 RealChatGPT Ver.
# [Beta] 转储历史记录时间间隔(分钟)
dump_history_interval: 10
dump_history_interval: 10
# 一个用户只能在time秒内发送count条消息
limit:
time: 60
count: 5
# 公告
notice: "此机器人由Github项目QQChannelChatGPT驱动。"
# 是否打开私信功能
# 设置为true则频道成员可以私聊机器人。
# 设置为false则频道成员不能私聊机器人。
direct_message_mode: true
################外带程序(插件)################
# 逆向ChatGPT库
# https://github.com/acheong08/ChatGPT
# 优点:免费(无免费额度限制);
# 缺点:速度相对慢。OpenAI 速率限制:免费帐户每小时 50 个请求。您可以通过多帐户循环来绕过它
# enable设置为true后,将会停止使用上面正常的官方API调用而使用本逆向项目
#
# 多账户可以保证每个请求都能得到及时的回复。
# 关于account的格式
# account:
# - email: 第1个账户
# password: 第1个账户密码
# - email: 第2个账户
# password: 第2个账户密码
# - ....
# 请严格按照上面这个格式填写。
# 这里我免费提供2个账号给大家,不过用的人一定会很多的,所以会造成一些bug,因此还是用自己的账号好一点。
# 需要账号可以联系我。QQ905617992
rev_ChatGPT:
enable: false
account:
- email: d.o.m.her.ry61.7@gmail.com
password: 11111111
- email: ca.it.li.nal.o.i.si.o91@gmail.com
password: 11111111
+29 -26
View File
@@ -12,30 +12,31 @@ abs_path = os.path.dirname(os.path.realpath(sys.argv[0])) + '/'
key_record_path = abs_path+'chatgpt_key_record'
class ChatGPT:
def __init__(self):
def __init__(self, cfg):
self.key_list = []
with open(abs_path+"configs/config.yaml", 'r', encoding='utf-8') as ymlfile:
cfg = yaml.safe_load(ymlfile)
if cfg['openai']['key'] != '' or cfg['openai']['key'] != '修改我!!':
print("读取ChatGPT Key成功")
self.key_list = cfg['openai']['key']
# openai.api_key = cfg['openai']['key']
else:
input("请先去完善ChatGPT的Key。详情请前往https://beta.openai.com/account/api-keys")
if cfg['key'] != '' or cfg['key'] != '修改我!!':
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['openai']['chatGPTConfigs']
print(f'加载ChatGPTConfigs: {chatGPT_configs}')
chatGPT_configs = cfg['chatGPTConfigs']
print(f'[System] 加载ChatGPTConfigs: {chatGPT_configs}')
self.chatGPT_configs = chatGPT_configs
self.openai_configs = cfg['openai']
self.openai_configs = cfg
def chat(self, prompt, image_mode = False):
# ChatGPT API 2023/3/2
messages = [{"role": "user", "content": prompt}]
try:
if not image_mode:
response = openai.Completion.create(
prompt=prompt,
response = openai.ChatCompletion.create(
messages=messages,
**self.chatGPT_configs
)
else:
@@ -47,7 +48,7 @@ class ChatGPT:
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):
print("当前Key已超额,正在切换")
print("[System] 当前Key已超额,正在切换")
self.key_stat[openai.api_key]['exceed'] = True
self.save_key_record()
@@ -57,8 +58,8 @@ class ChatGPT:
raise e
else:
if not image_mode:
response = openai.Completion.create(
prompt=prompt,
response = openai.ChatCompletion.create(
messages=messages,
**self.chatGPT_configs
)
else:
@@ -70,30 +71,31 @@ class ChatGPT:
if not image_mode:
self.key_stat[openai.api_key]['used'] += response['usage']['total_tokens']
self.save_key_record()
print("[ChatGPT] "+str(response["choices"][0]["text"]))
return str(response["choices"][0]["text"]).strip(), response['usage']['total_tokens']
print("[ChatGPT] "+str(response["choices"][0]["message"]["content"]))
return str(response["choices"][0]["message"]["content"]).strip(), response['usage']['total_tokens']
else:
return response['data'][0]['url']
def handle_switch_key(self, prompt):
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"切换到Key: {key}, 已使用token: {self.key_stat[key]['used']}")
print(f"[System] 切换到Key: {key}, 已使用token: {self.key_stat[key]['used']}")
if prompt != '':
try:
response = openai.Completion.create(
prompt=prompt,
response = openai.ChatCompletion.create(
messages=messages,
**self.chatGPT_configs
)
return response, True
except Exception as e:
print(e)
if 'You exceeded' in str(e):
print("当前Key已超额,正在切换")
print("[System] 当前Key已超额,正在切换")
self.key_stat[openai.api_key]['exceed'] = True
self.save_key_record()
time.sleep(1)
@@ -101,7 +103,7 @@ class ChatGPT:
else:
return True
if is_all_exceed:
print("所有Key已超额")
print("[System] 所有Key已超额")
return None, False
def getConfigs(self):
@@ -126,9 +128,10 @@ class ChatGPT:
def check_key(self, key):
pre_key = openai.api_key
openai.api_key = key
messages = [{"role": "user", "content": "1"}]
try:
openai.Completion.create(
prompt="1",
response = openai.ChatCompletion.create(
messages=messages,
**self.chatGPT_configs
)
openai.api_key = pre_key
+250 -155
View File
@@ -15,6 +15,7 @@ import os
import sys
from cores.qqbot.personality import personalities
history_dump_interval = 10
# QQBotClient实例
client = ''
@@ -51,9 +52,26 @@ announcement = ""
# 人格信息
now_personality = {}
# 机器人私聊模式
direct_message_mode = True
# 适配pyinstaller
abs_path = os.path.dirname(os.path.realpath(sys.argv[0])) + '/'
# 版本
version = '2.4 RealChatGPT Ver.'
# 语言模型提供商
REV_CHATGPT = 'rev_chatgpt'
OPENAI_OFFICIAL = 'openai_official'
provider = ''
# 逆向库对象及负载均衡
rev_chatgpt = []
# gpt配置信息
gpt_config = {}
def new_sub_thread(func, args=()):
thread = threading.Thread(target=func, args=args, daemon=True)
thread.start()
@@ -68,10 +86,11 @@ class botClient(botpy.Client):
# 收到私聊消息
async def on_direct_message_create(self, message: DirectMessage):
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))
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))
# 写入统计信息
def toggle_count(at: bool, message):
@@ -117,9 +136,10 @@ def dump_history():
# 每隔10分钟转储一次
time.sleep(10*history_dump_interval)
# 上传统计信息
# 上传统计信息并检查更新
def upload():
global object_id
global version
while True:
addr = ''
try:
@@ -136,7 +156,7 @@ def upload():
'Content-Type': 'application/json'
}
key_stat = chatgpt.get_key_stat()
d = {"data": {"guild_count": guild_count, "guild_msg_count": guild_msg_count, "guild_direct_msg_count": guild_direct_msg_count, "session_count": session_count, 'addr': addr, 'winver': '2.21', 'key_stat':key_stat}}
d = {"data": {"guild_count": guild_count, "guild_msg_count": guild_msg_count, "guild_direct_msg_count": guild_direct_msg_count, "session_count": session_count, 'addr': addr, 'winver': version, 'key_stat':key_stat}}
d = json.dumps(d).encode("utf-8")
res = requests.put(f'https://uqfxtww1.lc-cn-n1-shared.com/1.1/classes/bot_record/{object_id}', headers = headers, data = d)
if json.loads(res.text)['code'] == 1:
@@ -155,41 +175,64 @@ def upload():
'''
初始化机器人
'''
def initBot(chatgpt_inst):
global chatgpt
chatgpt = chatgpt_inst
global max_tokens
max_tokens = int(chatgpt_inst.getConfigs()['total_tokens_limit'])
global now_personality
def initBot(cfg, prov):
global chatgpt, provider, rev_chatgpt
global now_personality, gpt_config, config, uniqueSession, history_dump_interval, frequency_count, frequency_time,announcement, direct_message_mode, version
provider = prov
config = cfg
# 读取历史记录 Soulter
try:
db1 = dbConn()
for session in db1.get_all_session():
session_dict[session[0]] = json.loads(session[1])['data']
print("[System] 历史记录读取成功喵")
except BaseException as e:
print("[System] 历史记录读取失败: " + str(e))
# 语言模型提供商
if prov == REV_CHATGPT:
if 'account' in cfg['rev_ChatGPT']:
from addons.revChatGPT.revchatgpt import revChatGPT
for i in range(0, len(cfg['rev_ChatGPT']['account'])):
print(f"[System] 正在创建rev_ChatGPT负载{str(i)}: " + cfg['rev_ChatGPT']['account'][i]['email'])
revstat = {
'obj': revChatGPT(cfg['rev_ChatGPT']['account'][i]),
'busy': False
}
rev_chatgpt.append(revstat)
else:
input("[System-err] 请退出本程序, 然后在配置文件中填写rev_ChatGPT的email和password")
elif prov == OPENAI_OFFICIAL:
from cores.openai.core import ChatGPT
chatgpt = ChatGPT(cfg['openai'])
global max_tokens
max_tokens = int(chatgpt.getConfigs()['total_tokens_limit'])
# 读取历史记录 Soulter
try:
db1 = dbConn()
for session in db1.get_all_session():
session_dict[session[0]] = json.loads(session[1])['data']
print("[System] 历史记录读取成功喵")
except BaseException as e:
print("[System] 历史记录读取失败: " + str(e))
# 读统计信息
global stat_file
if not os.path.exists(abs_path+"configs/stat"):
with open(abs_path+"configs/stat", 'w', encoding='utf-8') as f:
json.dump({}, f)
stat_file = open(abs_path+"configs/stat", 'r', encoding='utf-8')
global count
res = stat_file.read()
if res == '':
count = {}
else:
try:
count = json.loads(res)
except BaseException:
pass
# 创建转储定时器线程
threading.Thread(target=dump_history, daemon=True).start()
# 读统计信息
global stat_file
if not os.path.exists(abs_path+"configs/stat"):
with open(abs_path+"configs/stat", 'w', encoding='utf-8') as f:
json.dump({}, f)
stat_file = open(abs_path+"configs/stat", 'r', encoding='utf-8')
global count
res = stat_file.read()
if res == '':
count = {}
else:
try:
count = json.loads(res)
except BaseException:
pass
# 创建转储定时器线程
threading.Thread(target=dump_history, daemon=True).start()
# 得到GPT配置信息
if 'openai' in cfg and 'chatGPTConfigs' in cfg['openai']:
gpt_config = cfg['openai']['chatGPTConfigs']
# 统计上传
if is_upload_log:
# 读取object_id
global object_id
@@ -201,44 +244,53 @@ def initBot(chatgpt_inst):
object_id_file.close()
# 创建上传定时器线程
threading.Thread(target=upload, daemon=True).start()
# 得到私聊模式配置
if 'direct_message_mode' in cfg:
direct_message_mode = cfg['direct_message_mode']
print("[System] 私聊功能: "+str(direct_message_mode))
global config, uniqueSession, history_dump_interval, frequency_count, frequency_time,announcement
with open(abs_path+"configs/config.yaml", 'r', encoding='utf-8') as ymlfile:
cfg = yaml.safe_load(ymlfile)
config = cfg
# 得到版本
if 'version' in cfg:
version = cfg['version']
print("[System] QQChannelChatGPT版本: "+str(version))
# 得到发言频率配置
if 'limit' in cfg:
print('[System] 发言频率配置: '+str(cfg['limit']))
if 'count' in cfg['limit']:
frequency_count = cfg['limit']['count']
if 'time' in cfg['limit']:
frequency_time = cfg['limit']['time']
announcement += '[QQChannelChatGPT项目]\n所有回答与腾讯公司无关。出现问题请前往[ChatGPT机器人]官方频道\n\n'
# 得到公告配置
if 'notice' in cfg:
print('[System] 公告配置: '+cfg['notice'])
announcement += cfg['notice']
try:
if 'uniqueSessionMode' in cfg and cfg['uniqueSessionMode']:
uniqueSession = True
else:
uniqueSession = False
print("[System] 独立会话: " + str(uniqueSession))
if 'dump_history_interval' in cfg:
history_dump_interval = int(cfg['dump_history_interval'])
print("[System] 历史记录转储时间周期: " + str(history_dump_interval) + "分钟")
except BaseException:
print("[System-Error] 读取uniqueSessionMode/version/dump_history_interval配置文件失败, 使用默认值。")
# 得到发言频率配置
if 'limit' in cfg:
print('[System] 发言频率配置: '+str(cfg['limit']))
if 'count' in cfg['limit']:
frequency_count = cfg['limit']['count']
if 'time' in cfg['limit']:
frequency_time = cfg['limit']['time']
announcement += '[QQChannelChatGPT项目]\n所有回答与腾讯公司无关。出现问题请前往[GPT机器人]官方频道\n\n'
# 得到公告配置
if 'notice' in cfg:
print('[System] 公告配置: '+cfg['notice'])
announcement += cfg['notice']
try:
if 'uniqueSessionMode' in cfg and cfg['uniqueSessionMode']:
uniqueSession = True
else:
uniqueSession = False
print("[System] 独立会话: " + str(uniqueSession))
if 'dump_history_interval' in cfg:
history_dump_interval = int(cfg['dump_history_interval'])
print("[System] 历史记录转储时间周期: " + str(history_dump_interval) + "分钟")
except BaseException:
print("[System-Error] 读取uniqueSessionMode/version/dump_history_interval配置文件失败, 使用默认值。")
print(f"[System] QQ开放平台AppID: {cfg['qqbot']['appid']} 令牌: {cfg['qqbot']['token']}")
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("[System] 如果有任何问题,请在https://github.com/Soulter/QQChannelChatGPT上提交issue说明问题!或者添加QQ905617992\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")
'''
启动机器人
@@ -250,7 +302,7 @@ def run_bot(appid, token):
client.run(appid=appid, token=token)
'''
得到OpenAI的回复
得到OpenAI官方API的回复
'''
def get_chatGPT_response(prompts_str, image_mode=False):
res = ''
@@ -264,6 +316,26 @@ def get_chatGPT_response(prompts_str, image_mode=False):
res = chatgpt.chat(prompts_str, image_mode = True)
return res
'''
负载均衡,得到逆向ChatGPT回复
'''
def get_rev_ChatGPT_response(prompts_str):
res = ''
print("[Debug] "+str(rev_chatgpt))
for revstat in rev_chatgpt:
if not revstat['busy']:
revstat['busy'] = True
print("[Debug] 使用逆向ChatGPT回复ing", end='')
res = revstat['obj'].chat(prompts_str)
print("OK")
revstat['busy'] = False
# 处理结果文本
chatgpt_res = res.strip()
return res
res = '所有的OpenAI账号都有负载, 请稍后再试~'
return res
'''
回复QQ消息
'''
@@ -271,8 +343,8 @@ def send_qq_msg(message, res, image_mode=False):
if not image_mode:
try:
asyncio.run_coroutine_threadsafe(message.reply(content=res), client.loop)
except BaseException as e:
raise e
except:
raise
else:
asyncio.run_coroutine_threadsafe(message.reply(image=res, content=""), client.loop)
@@ -327,7 +399,7 @@ def check_frequency(id) -> bool:
处理消息
'''
def oper_msg(message, at=False, loop=None):
global session_dict
global session_dict, provider
print("[QQBOT] 接收到消息:"+ str(message.content))
qq_msg = ''
session_id = ''
@@ -378,20 +450,6 @@ def oper_msg(message, at=False, loop=None):
if command_type == 1 and 'prompt' in now_personality:
# 设置人格
qq_msg = now_personality['prompt']
# if qq_msg[0:6] == '/draw ':
# # TODO 未完全实现
# prompt = qq_msg[6:]
# url = get_chatGPT_response(prompt, image_mode = True)
# resp = requests.get(url)
# filename = './images/' + str(int(time.time())) + '.jpg'
# print(url)
# with open(filename, 'wb') as f:
# f.write(resp.content)
# qiniu_url = cores.database.qiniu.put_img(filename)
# print(qiniu_url)
# send_qq_msg(message, qiniu_url, image_mode=True)
# return
# 这里是预设,你可以按需更改
if qq_msg.strip() == 'hello' or qq_msg.strip() == '你好' or qq_msg.strip() == '':
@@ -405,12 +463,13 @@ def oper_msg(message, at=False, loop=None):
# if '台湾' in qq_msg or 'taiwan' in qq_msg:
# send_qq_msg(message, "声明:台湾是中国的一部分。\n下面否定此结论的回答全部无效。")
# 拦截不合适的词请求
# 关键词拦截器
for i in uw.unfit_words_q:
if i in qq_msg.strip():
send_qq_msg(message, f"你的提问中有不太合适的内容😭\n请更换措辞~")
return
# 会话机制
if session_id not in session_dict:
session_dict[session_id] = []
@@ -427,31 +486,78 @@ def oper_msg(message, at=False, loop=None):
f.write(json.dumps(fjson))
f.flush()
f.close()
chatgpt_res = "[Error] 占位符"
if provider == OPENAI_OFFICIAL:
# 获取缓存
cache_prompt = ''
cache_data_list = session_dict[session_id]
cache_prompt = get_prompts_by_cache_list(cache_data_list)
cache_prompt += "\nHuman: "+ qq_msg + "\nAI: "
# 请求chatGPT获得结果
try:
chatgpt_res, current_usage_tokens = get_chatGPT_response(prompts_str=cache_prompt)
except (PromptExceededError) as e:
print("token超限, 清空对应缓存")
session_dict[session_id] = []
cache_data_list = []
cache_prompt = "Human: "+ qq_msg + "\nAI: "
chatgpt_res, current_usage_tokens = get_chatGPT_response(prompts_str=cache_prompt)
except (BaseException) as e:
print("OpenAI API错误:(")
if 'exceeded' in str(e):
send_qq_msg(message, f"OpenAI API错误。原因:\n{str(e)} \n超额了。您可自己搭建一个机器人(Github仓库:QQChannelChatGPT)")
else:
send_qq_msg(message, f"OpenAI API错误。原因如下:\n{str(e)} \n前往官方频道反馈~")
return
# 获取缓存
cache_prompt = ''
cache_data_list = session_dict[session_id]
cache_prompt = get_prompts_by_cache_list(cache_data_list)
cache_prompt += "\nHuman: "+ qq_msg + "\nAI: "
logf.write("[GPT] "+ str(chatgpt_res)+'\n')
logf.flush()
# 请求chatGPT获得结果
try:
chatgpt_res, current_usage_tokens = get_chatGPT_response(prompts_str=cache_prompt)
except (BaseException) as e:
print("[System-Err] OpenAI API错误。原因如下:\n"+str(e))
if 'maximum context length' in str(e):
print("token超限, 清空对应缓存")
session_dict[session_id] = []
cache_data_list = []
cache_prompt = "Human: "+ qq_msg + "\nAI: "
chatgpt_res, current_usage_tokens = get_chatGPT_response(prompts_str=cache_prompt)
elif 'exceeded' in str(e):
send_qq_msg(message, f"OpenAI API错误。原因:\n{str(e)} \n超额了。可自己搭建一个机器人(Github仓库:QQChannelChatGPT)")
else:
send_qq_msg(message, f"OpenAI API错误。原因如下:\n{str(e)} \n前往官方频道反馈~")
return
# 超过指定tokens, 尽可能的保留最多的条目,直到小于max_tokens
if current_usage_tokens > max_tokens:
t = current_usage_tokens
index = 0
while t > max_tokens:
if index >= len(cache_data_list):
break
if 'level' in cache_data_list[index] and cache_data_list[index]['level'] != 'max':
t -= int(cache_data_list[index]['single_tokens'])
del cache_data_list[index]
else:
index += 1
# 删除完后更新相关字段
session_dict[session_id] = cache_data_list
cache_prompt = get_prompts_by_cache_list(cache_data_list)
# 添加新条目进入缓存的prompt
if command_type == 1:
level = 'max'
else:
level = 'normal'
if len(cache_data_list) > 0:
single_record = {
"prompt": f'Human: {qq_msg}\nAI: {chatgpt_res}\n',
"usage_tokens": current_usage_tokens,
"single_tokens": current_usage_tokens - int(cache_data_list[-1]['usage_tokens']),
"level": level
}
else:
single_record = {
"prompt": f'Human: {qq_msg}\nAI: {chatgpt_res}\n',
"usage_tokens": current_usage_tokens,
"single_tokens": current_usage_tokens,
"level": level
}
cache_data_list.append(single_record)
session_dict[session_id] = cache_data_list
elif provider == REV_CHATGPT:
try:
chatgpt_res = "[Rev]"+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
# 发送qq信息
try:
@@ -465,7 +571,7 @@ def oper_msg(message, at=False, loop=None):
gap_chatgpt_res = gap_chatgpt_res.replace(i, "***")
# 发送信息
send_qq_msg(message, ''+gap_chatgpt_res)
except BaseException as e:
except:
print("QQ频道API错误: \n"+str(e))
f_res = ""
for t in chatgpt_res:
@@ -473,50 +579,17 @@ def oper_msg(message, at=False, loop=None):
try:
send_qq_msg(message, ''+f_res)
# send(message, f"QQ频道API错误:{str(e)}\n下面是格式化后的回答:\n{f_res}")
except BaseException as e:
except:
# 如果还是不行则过滤url
f_res = re.sub(r'(https|http)?:\/\/(\w|\.|\/|\?|\=|\&|\%)*\b', '', f_res, flags=re.MULTILINE)
f_res = f_res.replace(".", "·")
send_qq_msg(message, ''+f_res)
# send(message, f"QQ频道API错误:{str(e)}\n下面是格式化后的回答:\n{f_res}")
# 超过指定tokens, 尽可能的保留最多的条目,直到小于max_tokens
if current_usage_tokens > max_tokens:
t = current_usage_tokens
index = 0
while t > max_tokens:
if index >= len(cache_data_list):
break
if cache_data_list[index]['level'] != 'max':
t -= int(cache_data_list[index]['single_tokens'])
del cache_data_list[index]
else:
index += 1
# 删除完后更新相关字段
session_dict[session_id] = cache_data_list
cache_prompt = get_prompts_by_cache_list(cache_data_list)
# 记录日志
logf.write("[GPT] "+ str(chatgpt_res)+'\n')
logf.flush()
# 添加新条目进入缓存的prompt
if command_type == 1:
level = 'max'
else:
level = 'normal'
if len(cache_data_list) > 0:
single_record = {
"prompt": f'Human: {qq_msg}\nAI: {chatgpt_res}\n',
"usage_tokens": current_usage_tokens,
"single_tokens": current_usage_tokens - int(cache_data_list[-1]['usage_tokens']),
"level": level
}
else:
single_record = {
"prompt": f'Human: {qq_msg}\nAI: {chatgpt_res}\n',
"usage_tokens": current_usage_tokens,
"single_tokens": current_usage_tokens,
"level": level
}
cache_data_list.append(single_record)
session_dict[session_id] = cache_data_list
'''
获取统计信息
@@ -552,7 +625,7 @@ def get_stat():
def command_oper(qq_msg, message, session_id, name, user_id, user_name, at):
go = False # 是否处理完指令后继续执行msg_oper后面的代码
msg = ''
global session_dict, now_personality
global session_dict, now_personality, provider
# 指令返回值,/set设置人格是1
type = -1
@@ -567,6 +640,9 @@ def command_oper(qq_msg, message, session_id, name, user_id, user_name, at):
msg = f"你的历史记录重置成功"
if qq_msg[:4] == "/his":
if provider == REV_CHATGPT:
msg = "[QQChannelChatGPT]当前使用的语言模型提供商是Rev_ChatGPT, 不支持查看历史记录"
return msg, go, type
#分页,每页5条
msg = ''
size_per_page = 3
@@ -585,13 +661,24 @@ def command_oper(qq_msg, message, session_id, name, user_id, user_name, at):
msg=f"历史记录如下:\n{p}\n{page}页 | 共{max_page}\n*输入/his 2跳转到第2页\n\n{announcement}"
if qq_msg == "/token":
if provider == REV_CHATGPT:
msg = "[QQChannelChatGPT]当前使用的语言模型提供商是Rev_ChatGPT, 不支持使用此指令"
return msg, go, type
msg = ''
if at:
msg=f"{name} 会话的token数: {get_user_usage_tokens(session_dict[session_id])}\n系统最大缓存token数: {max_tokens}"
else:
msg=f"会话的token数: {get_user_usage_tokens(session_dict[session_id])}\n系统最大缓存token数: {max_tokens}"
if qq_msg == '/gpt':
if provider == REV_CHATGPT:
msg = "[QQChannelChatGPT]当前使用的语言模型提供商是Rev_ChatGPT, 不支持使用此指令"
return msg, go, type
global gpt_config
msg=f"OpenAI GPT配置:\n {gpt_config}"
if qq_msg == "/status" or qq_msg == "/状态":
chatgpt_cfg_str = ""
key_stat = chatgpt.get_key_stat()
key_list = chatgpt.get_key_list()
@@ -616,11 +703,19 @@ def command_oper(qq_msg, message, session_id, name, user_id, user_name, at):
msg = f"当前会话数: {len(session_dict)}\n共有频道数: {guild_count} \n共有消息数: {guild_msg_count}\n私信数: {guild_direct_msg_count}\n历史会话数: {session_count}"
if qq_msg == "/help":
msg = "[Github项目名: QQChannelChatGPT,有问题请前往提交issue,欢迎赞助支持我!]\n\n指令面板:\n/status 查看机器人key状态\n/count 查看机器人统计信息\n/reset 重置会话\n/his 查看历史记录\n/token 查看会话token数\n/help 查看帮助\n/key 人格指令菜单"
ol_version = 'Unknown'
try:
global version
res = requests.get("https://soulter.top/channelbot/update.json")
res_obj = json.loads(res.text)
ol_version = res_obj['version']
except BaseException:
pass
msg = f"[Github项目名: QQChannelChatGPT,有问题请前往提交issue,欢迎Star此项目~]\n\n当前版本:{version}\n最新版本:{str(ol_version)}\n请及时更新!\n\n指令面板:\n/status 查看机器人key状态\n/count 查看机器人统计信息\n/reset 重置会话\n/his 查看历史记录\n/token 查看会话token数\n/help 查看帮助\n/set 人格指令菜单\n/key 动态添加key"
if qq_msg[:4] == "/key":
if len(qq_msg) == 4:
msg = "感谢您赞助key请以以下格式赞助:\n/key xxxxx"
msg = "感谢您赞助keykey为官方API使用,请以以下格式赞助:\n/key xxxxx"
key = qq_msg[5:]
send_qq_msg(message, "收到!正在核验...")
if chatgpt.check_key(key):
+18 -11
View File
@@ -5,19 +5,27 @@ import os, sys
import signal
import requests,json
# 是否是windows打包。一般人不需要改这个,这个只是我为了方便加上的。
win_compile_mode = False
abs_path = os.path.dirname(os.path.realpath(sys.argv[0])) + '/'
def main(loop, event):
import cores.qqbot.core as qqBot
from cores.openai.core import ChatGPT
#实例化ChatGPT
chatgpt = ChatGPT()
# #执行qqBot
qqBot.initBot(chatgpt)
import yaml
ymlfile = open(abs_path+"configs/config.yaml", 'r', encoding='utf-8')
cfg = yaml.safe_load(ymlfile)
provider = privider_chooser(cfg)
print('[System] 当前语言模型提供商: ' + provider)
# 执行Bot
qqBot.initBot(cfg, provider)
# 语言模型提供商选择器
# 目前有:OpenAI官方API、逆向库
def privider_chooser(cfg):
if 'rev_ChatGPT' in cfg and cfg['rev_ChatGPT']['enable']:
return 'rev_chatgpt'
else:
return 'openai_official'
# 仅支持linux
def hot_update(ver):
@@ -77,10 +85,9 @@ def hot_update(ver):
time.sleep(60*20)
except BaseException as e:
print(e)
print("upd出现异常请联系QQ905617992")
print("upd出现异常, 请联系QQ905617992")
time.sleep(60*20)
def update_version(ver):
if not os.path.exists('update_record'):
object_id = ''
+3 -3
View File
@@ -1,3 +1,3 @@
requests~=2.28.2
openai~=0.26.5
qq-botpy~=1.1.2
requests~=2.27.1
openai~=0.27.0
qq-botpy~=1.1.2
+11
View File
@@ -0,0 +1,11 @@
import logging
from logging.handlers import RotatingFileHandler
import colorlog
logger = logging.getLogger("QQChannelChatGPT")
logger.setLevel(logging.DEBUG)
handler = colorlog.StreamHandler()
fmt = "%(log_color)s[%(name)s] %(message)s"
handler.setFormatter(colorlog.ColoredFormatter(
fmt))
logger.addHandler(handler)