Compare commits

...

24 Commits

Author SHA1 Message Date
Soulter 4b158a1c89 feat: GOCQ适配QQ频道 2023-05-20 15:30:07 +08:00
Soulter 6894900e46 fix: 修复画画指令得到的图片风格像油画的问题 2023-05-20 14:27:02 +08:00
Soulter 2e11d6e007 perf: log perf 2023-05-18 22:21:29 +08:00
Soulter 348381be15 Merge branch 'master' of https://github.com/Soulter/QQChannelChatGPT 2023-05-18 22:15:07 +08:00
Soulter 9024c28e70 perf: fix some logs 2023-05-18 22:15:01 +08:00
Soulter ae1702901b Update README.md 2023-05-18 08:34:41 +08:00
Soulter c1c0df85e6 Update README.md 2023-05-17 20:36:54 +08:00
Soulter f3c6d9c02b fix: draw command 2023-05-16 15:06:39 +08:00
Soulter 811a885411 fix: draw command 2023-05-16 15:04:55 +08:00
Soulter b4ec28b71c Merge branch 'master' of https://github.com/Soulter/QQChannelChatGPT 2023-05-16 11:57:04 +08:00
Soulter cdf4a5321b perf: 1.逆向ChatGPT库支持消息等待,不会回复忙碌。
2. 优化模型加载流程
2023-05-16 11:56:59 +08:00
Soulter d83f155f80 Update README.md 2023-05-15 20:54:19 +08:00
Soulter 4c402ed5bd perf: 优化插件鉴别 2023-05-15 20:43:42 +08:00
Soulter ec5aff8d0b fix: update helloworld default plugin 2023-05-15 20:14:14 +08:00
Soulter eae0d6c422 fix: 修复一些奇怪的地方 2023-05-15 20:09:01 +08:00
Soulter 9c284b84b1 perf: 升级插件协议簇 2023-05-15 20:03:17 +08:00
Soulter 9f36e5ae05 perf: 在连接到go-cqhttp之前添加连接检测 2023-05-15 18:33:07 +08:00
Soulter 7caa380e54 perf: 优化控制台输出的长度限制 2023-05-14 21:58:45 +08:00
Soulter 41d81bb60e perf: 简化控制台字数 2023-05-14 20:54:56 +08:00
Soulter 454a74f4e1 perf: 颜色日志-优化控制台显示 2023-05-14 20:51:39 +08:00
Soulter c5bdad02e5 fix: 修复ChatGPT逆向库回答报错的问题 2023-05-14 20:39:15 +08:00
Soulter f46de3d518 perf: 颜色日志-美化控制台显示 2023-05-14 20:38:28 +08:00
Soulter a3e21bea1a perf: 删除不必要的控制台信息显示 2023-05-14 19:54:47 +08:00
Soulter d7e4707d5d perf: 简化控制台输出信息 2023-05-14 19:43:12 +08:00
15 changed files with 402 additions and 139 deletions
+7 -3
View File
@@ -12,7 +12,7 @@ _✨教程:https://github.com/Soulter/QQChannelChatGPT/wiki ✨_
_✨插件开发教程:https://github.com/Soulter/QQChannelChatGPT/wiki/%E5%9B%9B%E3%80%81%E5%BC%80%E5%8F%91%E6%8F%92%E4%BB%B6 ✨_ _✨插件开发教程:https://github.com/Soulter/QQChannelChatGPT/wiki/%E5%9B%9B%E3%80%81%E5%BC%80%E5%8F%91%E6%8F%92%E4%BB%B6 ✨_
_✨欢迎体验😊(频道名: GPT机器人 | 频道号: x42d56aki2) | QQ群号:322154837)✨_ _✨欢迎体验(频道名: GPT机器人 | 频道号: x42d56aki2) | QQ群号:322154837)✨_
<!-- <img src="https://user-images.githubusercontent.com/37870767/230417115-9dd3c9d5-6b6b-4928-8fe3-82f559208aab.JPG" width="300"></img> --> <!-- <img src="https://user-images.githubusercontent.com/37870767/230417115-9dd3c9d5-6b6b-4928-8fe3-82f559208aab.JPG" width="300"></img> -->
@@ -25,7 +25,7 @@ _✨欢迎体验😊(频道名: GPT机器人 | 频道号: x42d56aki2) | QQ群
- 支持一键切换语言模型(使用/bing /revgpt /gpt分别可以切换newbing、逆向ChatGPT、官方ChatGPT模型) - 支持一键切换语言模型(使用/bing /revgpt /gpt分别可以切换newbing、逆向ChatGPT、官方ChatGPT模型)
- 热更新 - 热更新
- 接入QQ,支持在QQ上和QQ频道上同时聊天!https://github.com/Soulter/QQChannelChatGPT/issues/82 - 接入QQ,支持在QQ上和QQ频道上同时聊天!https://github.com/Soulter/QQChannelChatGPT/issues/82
- 更强大的Windows启动器,环境配置自动搞定。链接:https://github.com/Soulter/QQChatGPTLauncher/releases/latest - Windows启动器。链接:https://github.com/Soulter/QQChatGPTLauncher/releases/latest
支持的AI语言模型(请在`configs/config.yaml`下配置): 支持的AI语言模型(请在`configs/config.yaml`下配置):
- 逆向ChatGPT库 - 逆向ChatGPT库
@@ -102,7 +102,11 @@ _✨欢迎体验😊(频道名: GPT机器人 | 频道号: x42d56aki2) | QQ群
部分好用的插件: 部分好用的插件:
`HuggingChat`: https://github.com/Soulter/HuggingChatForQQBot - `HuggingChat`: https://github.com/Soulter/HuggingChatForQQBot | HuggingChat模型接入
- `GoodPlugins`: https://github.com/Soulter/goodplugins | 随机动漫图片等等
- `sysstat`: https://github.com/Soulter/sysstatqcbot | 查看系统状态
### 指令功能 ### 指令功能
+17 -3
View File
@@ -4,24 +4,28 @@ from nakuru import (
FriendMessage FriendMessage
) )
from botpy.message import Message, DirectMessage from botpy.message import Message, DirectMessage
from model.platform.qq import QQ
import time
import threading
class HelloWorldPlugin: class HelloWorldPlugin:
""" """
初始化函数, 可以选择直接pass 初始化函数, 可以选择直接pass
""" """
def __init__(self) -> None: def __init__(self) -> None:
self.myThread = None # 线程对象,如果要使用线程,需要在此处定义。在run处定义会被释放掉
print("这是HelloWorld测试插件, 发送 helloworld 即可触发此插件。") print("这是HelloWorld测试插件, 发送 helloworld 即可触发此插件。")
""" """
入口函数,机器人会调用此函数。 入口函数,机器人会调用此函数。
参数规范: message: 消息文本; role: 身份; platform: 消息平台; message_obj: 消息对象 参数规范: message: 消息文本; role: 身份; platform: 消息平台; message_obj: 消息对象; qq_platform: QQ平台对象,可以通过调用qq_platform.send()直接发送消息。详见Helloworld插件示例
参数详情: role为admin或者member; platform为qqchan或者gocq; message_obj为nakuru的GroupMessage对象或者FriendMessage对象或者频道的Message, DirectMessage对象。 参数详情: role为admin或者member; platform为qqchan或者gocq; message_obj为nakuru的GroupMessage对象或者FriendMessage对象或者频道的Message, DirectMessage对象。
返回规范: bool: 是否hit到此插件(所有的消息均会调用每一个载入的插件, 如果没有hit到, 则应返回False) 返回规范: bool: 是否hit到此插件(所有的消息均会调用每一个载入的插件, 如果没有hit到, 则应返回False)
Tuple: None或者长度为3的元组。当没有hit到时, 返回None. hit到时, 第1个参数为指令是否调用成功, 第2个参数为返回的消息文本或者gocq的消息链列表, 第3个参数为指令名称 Tuple: None或者长度为3的元组。当没有hit到时, 返回None. hit到时, 第1个参数为指令是否调用成功, 第2个参数为返回的消息文本或者gocq的消息链列表, 第3个参数为指令名称
例子:做一个名为"yuanshen"的插件;当接收到消息为“原神 可莉”, 如果不想要处理此消息,则返回False, None;如果想要处理,但是执行失败了,返回True, tuple([False, "请求失败啦~", "yuanshen"]) 例子:做一个名为"yuanshen"的插件;当接收到消息为“原神 可莉”, 如果不想要处理此消息,则返回False, None;如果想要处理,但是执行失败了,返回True, tuple([False, "请求失败啦~", "yuanshen"])
;执行成功了,返回True, tuple([True, "结果文本", "yuanshen"]) ;执行成功了,返回True, tuple([True, "结果文本", "yuanshen"])
""" """
def run(self, message: str, role: str, platform: str, message_obj): def run(self, message: str, role: str, platform: str, message_obj, qq_platform: QQ):
if platform == "gocq": if platform == "gocq":
""" """
@@ -30,6 +34,11 @@ class HelloWorldPlugin:
img_url = "https://gchat.qpic.cn/gchatpic_new/905617992/720871955-2246763964-C6EE1A52CC668EC982453065C4FA8747/0?term=2&amp;is_origin=0" img_url = "https://gchat.qpic.cn/gchatpic_new/905617992/720871955-2246763964-C6EE1A52CC668EC982453065C4FA8747/0?term=2&amp;is_origin=0"
if message == "helloworld": if message == "helloworld":
return True, tuple([True, [Plain("Hello World!!"), Image.fromURL(url=img_url)], "helloworld"]) return True, tuple([True, [Plain("Hello World!!"), Image.fromURL(url=img_url)], "helloworld"])
elif message == "hiloop":
if self.myThread is None:
self.myThread = threading.Thread(target=self.helloworldThread, args=(message_obj, qq_platform))
self.myThread.start()
return True, tuple([True, [Plain("A lot of Helloworlds!!"), Image.fromURL(url=img_url)], "helloworld"])
else: else:
return False, None return False, None
elif platform == "qqchan": elif platform == "qqchan":
@@ -58,7 +67,12 @@ class HelloWorldPlugin:
"version": "v1.0.1 beta", "version": "v1.0.1 beta",
"author": "Soulter" "author": "Soulter"
} }
def helloworldThread(self, meseage_obj, qq_platform: QQ):
while True:
qq_platform.send(meseage_obj, [Plain("Hello World!!")]) # 第一个参数可以是message_obj, 也可以是qq群号
time.sleep(3) # 睡眠3秒。 用while True一定要记得sleep,不然会卡死
# 热知识:检测消息开头指令,使用以下方法 # 热知识:检测消息开头指令,使用以下方法
# if message.startswith("原神"): # if message.startswith("原神"):
+129 -70
View File
@@ -18,10 +18,12 @@ from nakuru import (
CQHTTP, CQHTTP,
GroupMessage, GroupMessage,
GroupMemberIncrease, GroupMemberIncrease,
FriendMessage FriendMessage,
GuildMessage
) )
from nakuru.entities.components import Plain,At from nakuru.entities.components import Plain,At
from model.command.command import Command from model.command.command import Command
from util import general_utils as gu
# QQBotClient实例 # QQBotClient实例
client = '' client = ''
@@ -91,7 +93,11 @@ qqchan_loop = None
# QQ机器人 # QQ机器人
gocq_bot = None gocq_bot = None
PLATFORM_GOCQ = 'gocq' PLATFORM_GOCQ = 'gocq'
gocq_app = None gocq_app = CQHTTP(
host="127.0.0.1",
port=6700,
http_port=5700,
)
admin_qq = "123456" admin_qq = "123456"
gocq_loop = None gocq_loop = None
@@ -103,23 +109,6 @@ bing_cache_loop = None
cached_plugins = {} cached_plugins = {}
def gocq_runner():
global gocq_app
ok = False
while not ok:
try:
gocq_app = CQHTTP(
host="127.0.0.1",
port=6700,
http_port=5700,
)
ok = True
except BaseException as e:
print("[System-err] 连接到go-cqhttp异常, 5秒后重试。"+str(e))
threading.Thread(target=gocq_runner, daemon=True).start()
def new_sub_thread(func, args=()): def new_sub_thread(func, args=()):
thread = threading.Thread(target=func, args=args, daemon=True) thread = threading.Thread(target=func, args=args, daemon=True)
thread.start() thread.start()
@@ -169,7 +158,6 @@ def upload():
d = json.dumps(d).encode("utf-8") 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) 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: if json.loads(res.text)['code'] == 1:
print("[System] New User.")
res = requests.post(f'https://uqfxtww1.lc-cn-n1-shared.com/1.1/classes/bot_record', headers = headers, data = d) res = requests.post(f'https://uqfxtww1.lc-cn-n1-shared.com/1.1/classes/bot_record', headers = headers, data = d)
object_id = json.loads(res.text)['objectId'] object_id = json.loads(res.text)['objectId']
object_id_file = open(abs_path+"configs/object_id", 'w+', encoding='utf-8') object_id_file = open(abs_path+"configs/object_id", 'w+', encoding='utf-8')
@@ -194,9 +182,9 @@ def initBot(cfg, prov):
reply_prefix = cfg['reply_prefix'] reply_prefix = cfg['reply_prefix']
# 语言模型提供商 # 语言模型提供商
print("--------------------加载语言模型--------------------") gu.log("--------加载语言模型--------", gu.LEVEL_INFO, fg=gu.FG_COLORS['yellow'])
if REV_CHATGPT in prov: if REV_CHATGPT in prov:
print("- 逆向ChatGPT库 -") gu.log("- 逆向ChatGPT库 -", gu.LEVEL_INFO)
if cfg['rev_ChatGPT']['enable']: if cfg['rev_ChatGPT']['enable']:
if 'account' in cfg['rev_ChatGPT']: if 'account' in cfg['rev_ChatGPT']:
from model.provider.provider_rev_chatgpt import ProviderRevChatGPT from model.provider.provider_rev_chatgpt import ProviderRevChatGPT
@@ -208,7 +196,8 @@ def initBot(cfg, prov):
input("[System-err] 请退出本程序, 然后在配置文件中填写rev_ChatGPT相关配置") input("[System-err] 请退出本程序, 然后在配置文件中填写rev_ChatGPT相关配置")
if REV_EDGEGPT in prov: if REV_EDGEGPT in prov:
print("- New Bing -") gu.log("- New Bing -", gu.LEVEL_INFO)
if not os.path.exists('./cookies.json'): if not os.path.exists('./cookies.json'):
input("[System-err] 导入Bing模型时发生错误, 没有找到cookies文件或者cookies文件放置位置错误。windows启动器启动的用户请把cookies.json文件放到和启动器相同的目录下。\n如何获取请看https://github.com/Soulter/QQChannelChatGPT仓库介绍。") input("[System-err] 导入Bing模型时发生错误, 没有找到cookies文件或者cookies文件放置位置错误。windows启动器启动的用户请把cookies.json文件放到和启动器相同的目录下。\n如何获取请看https://github.com/Soulter/QQChannelChatGPT仓库介绍。")
else: else:
@@ -219,7 +208,7 @@ def initBot(cfg, prov):
command_rev_edgegpt = CommandRevEdgeGPT(rev_edgegpt) command_rev_edgegpt = CommandRevEdgeGPT(rev_edgegpt)
chosen_provider = REV_EDGEGPT chosen_provider = REV_EDGEGPT
if OPENAI_OFFICIAL in prov: if OPENAI_OFFICIAL in prov:
print("- OpenAI ChatGPT官方API -") gu.log("- OpenAI官方 -", gu.LEVEL_INFO)
if cfg['openai']['key'] is not None: if cfg['openai']['key'] is not None:
from model.provider.provider_openai_official import ProviderOpenAIOfficial from model.provider.provider_openai_official import ProviderOpenAIOfficial
from model.command.command_openai_official import CommandOpenAIOfficial from model.command.command_openai_official import CommandOpenAIOfficial
@@ -227,7 +216,7 @@ def initBot(cfg, prov):
command_openai_official = CommandOpenAIOfficial(chatgpt) command_openai_official = CommandOpenAIOfficial(chatgpt)
chosen_provider = OPENAI_OFFICIAL chosen_provider = OPENAI_OFFICIAL
print("--------------------加载个性化配置--------------------") gu.log("--------加载个性化配置--------", gu.LEVEL_INFO, fg=gu.FG_COLORS['yellow'])
# 得到关键词 # 得到关键词
if os.path.exists("keyword.json"): if os.path.exists("keyword.json"):
with open("keyword.json", 'r', encoding='utf-8') as f: with open("keyword.json", 'r', encoding='utf-8') as f:
@@ -244,10 +233,9 @@ def initBot(cfg, prov):
if 'baidu_aip' in cfg and 'enable' in cfg['baidu_aip'] and cfg['baidu_aip']['enable']: if 'baidu_aip' in cfg and 'enable' in cfg['baidu_aip'] and cfg['baidu_aip']['enable']:
try: try:
baidu_judge = BaiduJudge(cfg['baidu_aip']) baidu_judge = BaiduJudge(cfg['baidu_aip'])
print("[System] 百度内容审核初始化成功") gu.log("百度内容审核初始化成功", gu.LEVEL_INFO)
except BaseException as e: except BaseException as e:
input("[System] 百度内容审核初始化失败: " + str(e)) gu.log("百度内容审核初始化失败", gu.LEVEL_ERROR)
exit()
# 统计上传 # 统计上传
if is_upload_log: if is_upload_log:
@@ -265,11 +253,11 @@ def initBot(cfg, prov):
# 得到私聊模式配置 # 得到私聊模式配置
if 'direct_message_mode' in cfg: if 'direct_message_mode' in cfg:
direct_message_mode = cfg['direct_message_mode'] direct_message_mode = cfg['direct_message_mode']
print("[System] 私聊功能: "+str(direct_message_mode)) gu.log("私聊功能: "+str(direct_message_mode), gu.LEVEL_INFO)
# 得到发言频率配置 # 得到发言频率配置
if 'limit' in cfg: if 'limit' in cfg:
print('[System] 发言频率配置: '+str(cfg['limit'])) gu.log("发言频率配置: "+str(cfg['limit']), gu.LEVEL_INFO)
if 'count' in cfg['limit']: if 'count' in cfg['limit']:
frequency_count = cfg['limit']['count'] frequency_count = cfg['limit']['count']
if 'time' in cfg['limit']: if 'time' in cfg['limit']:
@@ -277,26 +265,24 @@ def initBot(cfg, prov):
# 得到公告配置 # 得到公告配置
if 'notice' in cfg: if 'notice' in cfg:
print('[System] 公告配置: '+cfg['notice']) gu.log("公告配置: "+cfg['notice'], gu.LEVEL_INFO)
announcement += cfg['notice'] announcement += cfg['notice']
try: try:
if 'uniqueSessionMode' in cfg and cfg['uniqueSessionMode']: if 'uniqueSessionMode' in cfg and cfg['uniqueSessionMode']:
uniqueSession = True uniqueSession = True
else: else:
uniqueSession = False uniqueSession = False
print("[System] 独立会话: " + str(uniqueSession)) gu.log("独立会话: "+str(uniqueSession), gu.LEVEL_INFO)
if 'dump_history_interval' in cfg: if 'dump_history_interval' in cfg:
print("[System] 历史记录转储时间周期: " + cfg['dump_history_interval'] + "分钟") gu.log("历史记录保存间隔: "+str(cfg['dump_history_interval']), gu.LEVEL_INFO)
except BaseException: except BaseException:
pass pass
print(f"[System] QQ开放平台AppID: {cfg['qqbot']['appid']} 令牌: {cfg['qqbot']['token']}")
gu.log(f"QQ开放平台AppID: {cfg['qqbot']['appid']} 令牌: {cfg['qqbot']['token']}")
print("\n[System] 如果有任何问题, 请在 https://github.com/Soulter/QQChannelChatGPT 上提交issue说明问题!或者添加QQ905617992")
print("[System] 请给 https://github.com/Soulter/QQChannelChatGPT 点个star!")
if chosen_provider is None: if chosen_provider is None:
print("[System-Warning] 检测到没有启动任何一个语言模型。请至少在配置文件中启用一个语言模型。") gu.log("检测到没有启动任何一个语言模型。请至少在配置文件中启用一个语言模型。", gu.LEVEL_CRITICAL)
# 得到指令设置(cmd_config.json) # 得到指令设置(cmd_config.json)
if os.path.exists("cmd_config.json"): if os.path.exists("cmd_config.json"):
@@ -310,42 +296,65 @@ def initBot(cfg, prov):
thread_inst = None thread_inst = None
print("--------------------加载插件--------------------") gu.log("--------加载插件--------", gu.LEVEL_INFO, fg=gu.FG_COLORS['yellow'])
# 加载插件 # 加载插件
_command = Command(None) _command = Command(None)
ok, err = _command.plugin_reload(cached_plugins) ok, err = _command.plugin_reload(cached_plugins)
if ok: if ok:
print("加载插件完成") gu.log("加载插件完成", gu.LEVEL_INFO)
else: else:
print(err) gu.log(err, gu.LEVEL_ERROR)
print("--------------------加载平台--------------------") gu.log("--------加载平台--------", gu.LEVEL_INFO, fg=gu.FG_COLORS['yellow'])
# GOCQ # GOCQ
global gocq_bot
if 'gocqbot' in cfg and cfg['gocqbot']['enable']: if 'gocqbot' in cfg and cfg['gocqbot']['enable']:
print("- 启用QQ机器人 -") gu.log("- 启用QQ机器人 -", gu.LEVEL_INFO)
if os.path.exists("cmd_config.json"): if os.path.exists("cmd_config.json"):
with open("cmd_config.json", 'r', encoding='utf-8') as f: with open("cmd_config.json", 'r', encoding='utf-8') as f:
cmd_config = json.load(f) cmd_config = json.load(f)
global admin_qq global admin_qq, admin_qqchan
if "admin_qq" in cmd_config: if "admin_qq" in cmd_config:
admin_qq = cmd_config['admin_qq'] admin_qq = cmd_config['admin_qq']
print("[System] 管理者QQ号: " + admin_qq) gu.log("管理者QQ号: " + admin_qq, gu.LEVEL_INFO)
else: else:
admin_qq = input("[System] 请输入管理者QQ号(管理者QQ号才能使用update/plugin等指令): ") gu.log("未设置管理者QQ号(管理者才能使用update/plugin等指令)", gu.LEVEL_WARNING)
print("[System] 管理者QQ号设置: " + admin_qq) admin_qq = input("请输入管理者QQ号(必须设置): ")
gu.log("管理者QQ号设置为: " + admin_qq, gu.LEVEL_INFO, fg=gu.FG_COLORS['yellow'])
cmd_config['admin_qq'] = admin_qq cmd_config['admin_qq'] = admin_qq
with open("cmd_config.json", 'w', encoding='utf-8') as f: with open("cmd_config.json", 'w', encoding='utf-8') as f:
json.dump(cmd_config, f, indent=4) json.dump(cmd_config, f, indent=4)
f.flush() f.flush()
global gocq_app, gocq_bot, gocq_loop if "admin_qqchan" in cmd_config:
gocq_bot = QQ() admin_qqchan = cmd_config['admin_qqchan']
gu.log("管理者频道用户号: " + admin_qqchan, gu.LEVEL_INFO)
else:
gu.log("未设置管理者QQ频道用户号(管理者才能使用update/plugin等指令)", gu.LEVEL_WARNING)
admin_qqchan = input("请输入管理者频道用户号(不是QQ号, 可以先回车跳过然后在频道发送指令!myid获取): ")
if admin_qqchan == "":
gu.log("跳过设置管理者频道用户号", gu.LEVEL_INFO, fg=gu.FG_COLORS['yellow'])
else:
gu.log("管理者频道用户号设置为: " + admin_qqchan, gu.LEVEL_INFO, fg=gu.FG_COLORS['yellow'])
cmd_config['admin_qqchan'] = admin_qqchan
with open("cmd_config.json", 'w', encoding='utf-8') as f:
json.dump(cmd_config, f, indent=4)
f.flush()
global gocq_app, gocq_loop
gocq_loop = asyncio.new_event_loop() gocq_loop = asyncio.new_event_loop()
gocq_bot = QQ(True, gocq_loop)
thread_inst = threading.Thread(target=run_gocq_bot, args=(gocq_loop, gocq_bot, gocq_app), daemon=False) thread_inst = threading.Thread(target=run_gocq_bot, args=(gocq_loop, gocq_bot, gocq_app), daemon=False)
thread_inst.start() thread_inst.start()
else:
gocq_bot = QQ(False)
gu.log("机器人部署教程: https://github.com/Soulter/QQChannelChatGPT/wiki/", gu.LEVEL_INFO, fg=gu.FG_COLORS['yellow'])
gu.log("如果有任何问题, 请在 https://github.com/Soulter/QQChannelChatGPT 上提交issue说明问题!", gu.LEVEL_INFO, fg=gu.FG_COLORS['yellow'])
gu.log("请给 https://github.com/Soulter/QQChannelChatGPT 点个star!", gu.LEVEL_INFO, fg=gu.FG_COLORS['yellow'])
# QQ频道 # QQ频道
if 'qqbot' in cfg and cfg['qqbot']['enable']: if 'qqbot' in cfg and cfg['qqbot']['enable']:
print("- 启用QQ频道机器人 -") gu.log("- 启用QQ频道机器人 -", gu.LEVEL_INFO)
global qqchannel_bot, qqchan_loop global qqchannel_bot, qqchan_loop
qqchannel_bot = QQChan() qqchannel_bot = QQChan()
qqchan_loop = asyncio.new_event_loop() qqchan_loop = asyncio.new_event_loop()
@@ -371,6 +380,16 @@ def run_qqchan_bot(cfg, loop, qqchannel_bot):
def run_gocq_bot(loop, gocq_bot, gocq_app): def run_gocq_bot(loop, gocq_bot, gocq_app):
asyncio.set_event_loop(loop) asyncio.set_event_loop(loop)
gu.log("正在检查本地GO-CQHTTP连接...端口5700, 6700", tag="QQ")
while True:
if not gu.port_checker(5700) or not gu.port_checker(6700):
gu.log("与GO-CQHTTP通信失败, 请检查GO-CQHTTP是否启动并正确配置。5秒后自动重试。", gu.LEVEL_CRITICAL, tag="QQ")
time.sleep(5)
else:
gu.log("检查完毕,未发现问题。", tag="QQ")
break
global gocq_client global gocq_client
gocq_client = gocqClient() gocq_client = gocqClient()
try: try:
@@ -438,18 +457,18 @@ def oper_msg(message,
role = "member" # 角色 role = "member" # 角色
hit = False # 是否命中指令 hit = False # 是否命中指令
command_result = () # 调用指令返回的结果 command_result = () # 调用指令返回的结果
global admin_qq, cached_plugins global admin_qq, admin_qqchan, cached_plugins, gocq_bot
if platform == PLATFORM_QQCHAN: if platform == PLATFORM_QQCHAN:
print("[QQCHAN-BOT] 接收到消息:"+ str(message.content)) gu.log(f"接收到消息:{message.content}", gu.LEVEL_INFO, tag="QQ频道")
user_id = message.author.id user_id = message.author.id
user_name = message.author.username user_name = message.author.username
global qqchan_loop global qqchan_loop
if platform == PLATFORM_GOCQ: if platform == PLATFORM_GOCQ:
if isinstance(message.message[0], Plain): if isinstance(message.message[0], Plain):
print("[GOCQ-BOT] 接收到消息:"+ str(message.message[0].text)) gu.log(f"接收到消息:{message.message[0].text}", gu.LEVEL_INFO, tag="GOCQ")
elif isinstance(message.message[0], At): elif isinstance(message.message[0], At):
print("[GOCQ-BOT] 接收到消息:"+ str(message.message[1].text)) gu.log(f"接收到消息:{message.message[1].text}", gu.LEVEL_INFO, tag="GOCQ")
user_id = message.user_id user_id = message.user_id
user_name = message.user_id user_name = message.user_id
@@ -478,7 +497,7 @@ def oper_msg(message,
session_id = message.channel_id session_id = message.channel_id
# 得到身份 # 得到身份
if "2" in message.member.roles or "4" in message.member.roles or "5" in message.member.roles: if "2" in message.member.roles or "4" in message.member.roles or "5" in message.member.roles:
print("[QQCHAN-BOT] 检测到管理员身份") gu.log(f"检测到管理员身份", gu.LEVEL_INFO, tag="QQ频道")
role = "admin" role = "admin"
else: else:
role = "member" role = "member"
@@ -495,20 +514,24 @@ def oper_msg(message,
qq_msg = str(message.message[1].text).strip() qq_msg = str(message.message[1].text).strip()
else: else:
return return
session_id = message.group_id # 适配GO-CQHTTP的频道功能
if message.type == "GuildMessage":
session_id = message.channel_id
else:
session_id = message.group_id
else: else:
qq_msg = message.message[0].text qq_msg = message.message[0].text
session_id = message.user_id session_id = message.user_id
role = "member" role = "member"
if str(message.sender.user_id) == admin_qq: if str(message.sender.user_id) == admin_qq or str(message.sender.tiny_id) == admin_qqchan:
print("[GOCQ-BOT] 检测到管理员身份") gu.log("检测到管理员身份", gu.LEVEL_INFO, tag="GOCQ")
role = "admin" role = "admin"
if qq_msg == "": if qq_msg == "":
send_message(platform, message, f"Hi~", msg_ref=msg_ref, gocq_loop=gocq_loop, qqchannel_bot=qqchannel_bot, gocq_bot=gocq_bot) send_message(platform, message, f"Hi~", msg_ref=msg_ref, gocq_loop=gocq_loop, qqchannel_bot=qqchannel_bot, gocq_bot=gocq_bot)
return return
logf.write("[QQBOT] "+ qq_msg+'\n') logf.write("[GOCQBOT] "+ qq_msg+'\n')
logf.flush() logf.flush()
# 关键词回复 # 关键词回复
@@ -557,7 +580,10 @@ def oper_msg(message,
chatgpt_res = "" chatgpt_res = ""
if chosen_provider == OPENAI_OFFICIAL: if chosen_provider == OPENAI_OFFICIAL:
hit, command_result = command_openai_official.check_command(qq_msg, session_id, user_name, role, platform=platform, message_obj=message, cached_plugins=cached_plugins) if chatgpt == None:
send_message(platform, message, f"管理员未启动此模型或者此模型初始化时失败。", msg_ref=msg_ref, gocq_loop=gocq_loop, qqchannel_bot=qqchannel_bot, gocq_bot=gocq_bot)
return
hit, command_result = command_openai_official.check_command(qq_msg, session_id, user_name, role, platform=platform, message_obj=message, cached_plugins=cached_plugins, qq_platform=gocq_bot)
# hit: 是否触发了指令 # hit: 是否触发了指令
if not hit: if not hit:
# 请求ChatGPT获得结果 # 请求ChatGPT获得结果
@@ -566,27 +592,35 @@ def oper_msg(message,
if OPENAI_OFFICIAL in reply_prefix: if OPENAI_OFFICIAL in reply_prefix:
chatgpt_res = reply_prefix[OPENAI_OFFICIAL] + chatgpt_res chatgpt_res = reply_prefix[OPENAI_OFFICIAL] + chatgpt_res
except (BaseException) as e: except (BaseException) as e:
print("[System-Err] OpenAI API请求错误, 原因: "+str(e)) gu.log("OpenAI API请求错误, 原因: "+str(e), gu.LEVEL_ERROR)
send_message(platform, message, f"OpenAI API错误, 原因: {str(e)}", msg_ref=msg_ref, gocq_loop=gocq_loop, qqchannel_bot=qqchannel_bot, gocq_bot=gocq_bot) send_message(platform, message, f"OpenAI API错误, 原因: {str(e)}", msg_ref=msg_ref, gocq_loop=gocq_loop, qqchannel_bot=qqchannel_bot, gocq_bot=gocq_bot)
elif chosen_provider == REV_CHATGPT: elif chosen_provider == REV_CHATGPT:
hit, command_result = command_rev_chatgpt.check_command(qq_msg, role, platform=platform, message=message, cached_plugins=cached_plugins) if rev_chatgpt == None:
send_message(platform, message, f"管理员未启动此模型或者此模型初始化时失败。", msg_ref=msg_ref, gocq_loop=gocq_loop, qqchannel_bot=qqchannel_bot, gocq_bot=gocq_bot)
return
hit, command_result = command_rev_chatgpt.check_command(qq_msg, role, platform=platform, message_obj=message, cached_plugins=cached_plugins, qq_platform=gocq_bot)
if not hit: if not hit:
try: try:
while rev_chatgpt.is_all_busy():
time.sleep(1)
chatgpt_res = str(rev_chatgpt.text_chat(qq_msg)) chatgpt_res = str(rev_chatgpt.text_chat(qq_msg))
if REV_CHATGPT in reply_prefix: if REV_CHATGPT in reply_prefix:
chatgpt_res = reply_prefix[REV_CHATGPT] + chatgpt_res chatgpt_res = reply_prefix[REV_CHATGPT] + chatgpt_res
except BaseException as e: except BaseException as e:
print("[System-Err] RevChatGPT请求错误, 原因: "+str(e)) gu.log("逆向ChatGPT请求错误, 原因: "+str(e), gu.LEVEL_ERROR)
send_message(platform, message, f"RevChatGPT错误, 原因: \n{str(e)}", msg_ref=msg_ref, gocq_loop=gocq_loop, qqchannel_bot=qqchannel_bot, gocq_bot=gocq_bot) send_message(platform, message, f"RevChatGPT错误, 原因: \n{str(e)}", msg_ref=msg_ref, gocq_loop=gocq_loop, qqchannel_bot=qqchannel_bot, gocq_bot=gocq_bot)
elif chosen_provider == REV_EDGEGPT: elif chosen_provider == REV_EDGEGPT:
if rev_edgegpt == None:
send_message(platform, message, f"管理员未启动此模型或者此模型初始化时失败。", msg_ref=msg_ref, gocq_loop=gocq_loop, qqchannel_bot=qqchannel_bot, gocq_bot=gocq_bot)
return
if bing_cache_loop == None: if bing_cache_loop == None:
if platform == PLATFORM_GOCQ: if platform == PLATFORM_GOCQ:
bing_cache_loop = gocq_loop bing_cache_loop = gocq_loop
elif platform == PLATFORM_QQCHAN: elif platform == PLATFORM_QQCHAN:
bing_cache_loop = qqchan_loop bing_cache_loop = qqchan_loop
hit, command_result = command_rev_edgegpt.check_command(qq_msg, bing_cache_loop, role, platform=platform, message_obj=message, cached_plugins=cached_plugins) hit, command_result = command_rev_edgegpt.check_command(qq_msg, bing_cache_loop, role, platform=platform, message_obj=message, cached_plugins=cached_plugins, qq_platform=gocq_bot)
if not hit: if not hit:
try: try:
while rev_edgegpt.is_busy(): while rev_edgegpt.is_busy():
@@ -605,7 +639,7 @@ def oper_msg(message,
if REV_EDGEGPT in reply_prefix: if REV_EDGEGPT in reply_prefix:
chatgpt_res = reply_prefix[REV_EDGEGPT] + chatgpt_res chatgpt_res = reply_prefix[REV_EDGEGPT] + chatgpt_res
except BaseException as e: except BaseException as e:
print("[System-Err] Rev NewBing API错误。原因如下:\n"+str(e)) gu.log("NewBing请求错误, 原因: "+str(e), gu.LEVEL_ERROR)
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) 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)
# 切换回原来的语言模型 # 切换回原来的语言模型
@@ -622,7 +656,7 @@ def oper_msg(message,
with open("keyword.json", "r", encoding="utf-8") as f: with open("keyword.json", "r", encoding="utf-8") as f:
keywords = json.load(f) keywords = json.load(f)
# QQ昵称 # 昵称
if command == "nick": if command == "nick":
with open("cmd_config.json", "r", encoding="utf-8") as f: with open("cmd_config.json", "r", encoding="utf-8") as f:
global nick_qq global nick_qq
@@ -630,7 +664,7 @@ def oper_msg(message,
if command_result[0]: if command_result[0]:
# 是否是画图指令 # 是否是画图指令
if isinstance(command_result, list) and len(command_result) == 3 and command_result[2] == 'draw': if isinstance(command_result[1], list) and len(command_result) == 3 and command_result[2] == 'draw':
for i in command_result[1]: for i in command_result[1]:
send_message(platform, message, i, msg_ref=msg_ref, image=i, gocq_loop=gocq_loop, qqchannel_bot=qqchannel_bot, gocq_bot=gocq_bot) send_message(platform, message, i, msg_ref=msg_ref, image=i, gocq_loop=gocq_loop, qqchannel_bot=qqchannel_bot, gocq_bot=gocq_bot)
else: else:
@@ -666,7 +700,7 @@ def oper_msg(message,
try: try:
send_message(platform, message, chatgpt_res, msg_ref=msg_ref, gocq_loop=gocq_loop, qqchannel_bot=qqchannel_bot, gocq_bot=gocq_bot) 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: except BaseException as e:
print("回复消息错误: \n"+str(e)) gu.log("回复消息错误: \n"+str(e), gu.LEVEL_ERROR)
''' '''
获取统计信息 获取统计信息
@@ -719,7 +753,7 @@ class gocqClient():
global nick_qq global nick_qq
# 将nick_qq转换为元组 # 将nick_qq转换为元组
if nick_qq == None: if nick_qq == None:
nick_qq = ("ai",) nick_qq = ("ai","!","")
if isinstance(nick_qq, str): if isinstance(nick_qq, str):
nick_qq = (nick_qq,) nick_qq = (nick_qq,)
if isinstance(nick_qq, list): if isinstance(nick_qq, list):
@@ -751,4 +785,29 @@ class gocqClient():
global nick_qq global nick_qq
await app.sendGroupMessage(source.group_id, [ await app.sendGroupMessage(source.group_id, [
Plain(text=f"欢迎加入本群!\n欢迎给https://github.com/Soulter/QQChannelChatGPT项目一个Star😊~\n@我输入help查看帮助~\n我叫{nick_qq}, 你也可以以【{nick_qq}+问题】的格式来提醒我并问我问题哦~\n") Plain(text=f"欢迎加入本群!\n欢迎给https://github.com/Soulter/QQChannelChatGPT项目一个Star😊~\n@我输入help查看帮助~\n我叫{nick_qq}, 你也可以以【{nick_qq}+问题】的格式来提醒我并问我问题哦~\n")
]) ])
@gocq_app.receiver("GuildMessage")
async def _(app: CQHTTP, source: GuildMessage):
# gu.log(str(source), gu.LEVEL_INFO, max_len=9999)
global nick_qq
if nick_qq == None:
nick_qq = ("ai","!","")
if isinstance(nick_qq, str):
nick_qq = (nick_qq,)
if isinstance(nick_qq, list):
nick_qq = tuple(nick_qq)
if isinstance(source.message[0], Plain):
if source.message[0].text.startswith(nick_qq):
_len = 0
for i in nick_qq:
if source.message[0].text.startswith(i):
_len = len(i)
source.message[0].text = source.message[0].text[_len:].strip()
new_sub_thread(oper_msg, (source, True, None, PLATFORM_GOCQ))
if isinstance(source.message[0], At):
if source.message[0].tiny_id == source.self_tiny_id:
new_sub_thread(oper_msg, (source, True, None, PLATFORM_GOCQ))
else:
return
+9 -9
View File
@@ -1,11 +1,10 @@
import threading
import asyncio
import os, sys import os, sys
from pip._internal import main as pipmain from pip._internal import main as pipmain
import util.general_utils as gu
abs_path = os.path.dirname(os.path.realpath(sys.argv[0])) + '/' abs_path = os.path.dirname(os.path.realpath(sys.argv[0])) + '/'
def main(loop, event): def main():
try: try:
import cores.qqbot.core as qqBot import cores.qqbot.core as qqBot
import yaml import yaml
@@ -13,7 +12,7 @@ def main(loop, event):
cfg = yaml.safe_load(ymlfile) cfg = yaml.safe_load(ymlfile)
except BaseException as e: except BaseException as e:
print(e) print(e)
input("yaml库未导入或者配置文件格式错误,请退出程序重试。") input("第三方依赖库未完全安装完毕,请退出程序重试。")
exit() exit()
if 'http_proxy' in cfg: if 'http_proxy' in cfg:
@@ -22,7 +21,11 @@ def main(loop, event):
os.environ['HTTPS_PROXY'] = cfg['https_proxy'] os.environ['HTTPS_PROXY'] = cfg['https_proxy']
provider = privider_chooser(cfg) provider = privider_chooser(cfg)
print('[System] 当前语言模型提供商: ' + str(provider)) if len(provider) == 0:
gu.log("未开启任何语言模型, 请在configs/config.yaml下选择开启相应语言模型。", gu.LEVEL_CRITICAL)
input("按任意键退出...")
exit()
print('[System] 开启的语言模型: ' + str(provider))
# 执行Bot # 执行Bot
qqBot.initBot(cfg, provider) qqBot.initBot(cfg, provider)
@@ -116,7 +119,4 @@ if __name__ == "__main__":
except BaseException as e: except BaseException as e:
print(e) print(e)
print(f"[System-err] Replit Web保活服务启动失败:{str(e)}") print(f"[System-err] Replit Web保活服务启动失败:{str(e)}")
main()
bot_event = threading.Event()
loop = asyncio.get_event_loop()
main(loop, bot_event)
+16 -5
View File
@@ -9,6 +9,8 @@ import json
import util.plugin_util as putil import util.plugin_util as putil
import shutil import shutil
import importlib import importlib
from util import general_utils as gu
from model.platform.qq import QQ
PLATFORM_QQCHAN = 'qqchan' PLATFORM_QQCHAN = 'qqchan'
PLATFORM_GOCQ = 'gocq' PLATFORM_GOCQ = 'gocq'
@@ -32,16 +34,16 @@ class Command:
except BaseException as e: except BaseException as e:
raise e raise e
def check_command(self, message, role, platform, message_obj, cached_plugins: dict): def check_command(self, message, role, platform, message_obj, cached_plugins: dict, qq_platform: QQ):
# 插件 # 插件
for k, v in cached_plugins.items(): for k, v in cached_plugins.items():
try: try:
hit, res = v["clsobj"].run(message, role, platform, message_obj) hit, res = v["clsobj"].run(message, role, platform, message_obj, qq_platform)
if hit: if hit:
return True, res return True, res
except BaseException as e: except BaseException as e:
print(f"[Debug] {k}插件加载出现问题,原因: {str(e)}\n已安装插件: {cached_plugins.keys}\n如果你没有相关装插件的想法, 请直接忽略此报错, 不影响其他功能的运行。") gu.log(f"{k}插件加载出现问题,原因: {str(e)}\n已安装插件: {cached_plugins.keys}\n如果你没有相关装插件的想法, 请直接忽略此报错, 不影响其他功能的运行。", level=gu.LEVEL_WARNING)
if self.command_start_with(message, "nick"): if self.command_start_with(message, "nick"):
return True, self.set_nick(message, platform) return True, self.set_nick(message, platform)
@@ -49,8 +51,19 @@ class Command:
if self.command_start_with(message, "plugin"): if self.command_start_with(message, "plugin"):
return True, self.plugin_oper(message, role, cached_plugins) return True, self.plugin_oper(message, role, cached_plugins)
if self.command_start_with(message, "myid"):
return True, self.get_my_id(message_obj, platform)
return False, None return False, None
def get_my_id(self, message_obj, platform):
if platform == "gocq":
if message_obj.type == "GuildMessage":
return True, f"你的频道id是{str(message_obj.sender.tiny_id)}", "plugin"
else:
return True, f"你的QQ是{str(message_obj.sender.user_id)}", "plugin"
def plugin_reload(self, cached_plugins: dict, target: str = None, all: bool = False): def plugin_reload(self, cached_plugins: dict, target: str = None, all: bool = False):
plugins = self.get_plugin_modules() plugins = self.get_plugin_modules()
fail_rec = "" fail_rec = ""
@@ -272,7 +285,6 @@ class Command:
del_mode = False del_mode = False
if l[1] == "d": if l[1] == "d":
print("删除关键词: "+l[2])
del_mode = True del_mode = True
try: try:
@@ -291,7 +303,6 @@ class Command:
return False, "该关键词不存在", "keyword" return False, "该关键词不存在", "keyword"
keyword = {l[1]: l[2]} keyword = {l[1]: l[2]}
with open("keyword.json", "w", encoding="utf-8") as f: with open("keyword.json", "w", encoding="utf-8") as f:
print("设置指令: "+l[1]+" -> "+l[2])
json.dump(keyword, f, ensure_ascii=False, indent=4) json.dump(keyword, f, ensure_ascii=False, indent=4)
f.flush() f.flush()
if del_mode: if del_mode:
+8 -2
View File
@@ -1,6 +1,7 @@
from model.command.command import Command from model.command.command import Command
from model.provider.provider_openai_official import ProviderOpenAIOfficial from model.provider.provider_openai_official import ProviderOpenAIOfficial
from cores.qqbot.personality import personalities from cores.qqbot.personality import personalities
from model.platform.qq import QQ
class CommandOpenAIOfficial(Command): class CommandOpenAIOfficial(Command):
def __init__(self, provider: ProviderOpenAIOfficial): def __init__(self, provider: ProviderOpenAIOfficial):
@@ -14,8 +15,9 @@ class CommandOpenAIOfficial(Command):
role: str, role: str,
platform: str, platform: str,
message_obj, message_obj,
cached_plugins: dict): cached_plugins: dict,
hit, res = super().check_command(message, role, platform, message_obj=message_obj, cached_plugins=cached_plugins) qq_platform: QQ):
hit, res = super().check_command(message, role, platform, message_obj=message_obj, cached_plugins=cached_plugins, qq_platform=qq_platform)
if hit: if hit:
return True, res return True, res
if self.command_start_with(message, "reset", "重置"): if self.command_start_with(message, "reset", "重置"):
@@ -187,6 +189,10 @@ class CommandOpenAIOfficial(Command):
return True, f"自定义人格已设置。 \n人格信息: {ps}", "set" return True, f"自定义人格已设置。 \n人格信息: {ps}", "set"
def draw(self, message): def draw(self, message):
if message.startswith("/画"):
message = message[2:]
elif message.startswith(""):
message = message[1:]
try: try:
# 画图模式传回3个参数 # 画图模式传回3个参数
img_url = self.provider.image_chat(message) img_url = self.provider.image_chat(message)
+4 -2
View File
@@ -1,5 +1,6 @@
from model.command.command import Command from model.command.command import Command
from model.provider.provider_rev_chatgpt import ProviderRevChatGPT from model.provider.provider_rev_chatgpt import ProviderRevChatGPT
from model.platform.qq import QQ
class CommandRevChatGPT(Command): class CommandRevChatGPT(Command):
def __init__(self, provider: ProviderRevChatGPT): def __init__(self, provider: ProviderRevChatGPT):
@@ -11,8 +12,9 @@ class CommandRevChatGPT(Command):
role: str, role: str,
platform: str, platform: str,
message_obj, message_obj,
cached_plugins: dict): cached_plugins: dict,
hit, res = super().check_command(message, role, platform, message_obj=message_obj, cached_plugins=cached_plugins) qq_platform: QQ):
hit, res = super().check_command(message, role, platform, message_obj=message_obj, cached_plugins=cached_plugins, qq_platform=qq_platform)
if hit: if hit:
return True, res return True, res
if self.command_start_with(message, "help", "帮助"): if self.command_start_with(message, "help", "帮助"):
+5 -2
View File
@@ -1,6 +1,8 @@
from model.command.command import Command from model.command.command import Command
from model.provider.provider_rev_edgegpt import ProviderRevEdgeGPT from model.provider.provider_rev_edgegpt import ProviderRevEdgeGPT
import asyncio import asyncio
from model.platform.qq import QQ
class CommandRevEdgeGPT(Command): class CommandRevEdgeGPT(Command):
def __init__(self, provider: ProviderRevEdgeGPT): def __init__(self, provider: ProviderRevEdgeGPT):
self.provider = provider self.provider = provider
@@ -13,8 +15,9 @@ class CommandRevEdgeGPT(Command):
role: str, role: str,
platform: str, platform: str,
message_obj, message_obj,
cached_plugins: dict): cached_plugins: dict,
hit, res = super().check_command(message, role, platform, message_obj=message_obj, cached_plugins=cached_plugins) qq_platform: QQ):
hit, res = super().check_command(message, role, platform, message_obj=message_obj, cached_plugins=cached_plugins, qq_platform=qq_platform)
if hit: if hit:
return True, res return True, res
if self.command_start_with(message, "reset"): if self.command_start_with(message, "reset"):
+68 -8
View File
@@ -1,24 +1,49 @@
from nakuru.entities.components import Plain, At, Image from nakuru.entities.components import Plain, At, Image
from util import general_utils as gu
import asyncio
from nakuru import (
CQHTTP,
GuildMessage
)
class QQ: class QQ:
def __init__(self, is_start: bool, gocq_loop = None) -> None:
self.is_start = is_start
self.gocq_loop = gocq_loop
def run_bot(self, gocq): def run_bot(self, gocq):
self.client = gocq self.client: CQHTTP = gocq
self.client.run() self.client.run()
def get_msg_loop(self):
return self.gocq_loop
async def send_qq_msg(self, async def send_qq_msg(self,
source, source,
res, res,
image_mode: bool = False): image_mode: bool = False):
if not self.is_start:
raise Exception("管理员未启动GOCQ平台")
""" """
res可以是一个数组也就是gocq的消息链. res可以是一个数组, 也就是gocq的消息链
插件开发者请使用send方法, 可以不用直接调用这个方法。
""" """
gu.log("回复GOCQ消息: "+str(res), level=gu.LEVEL_INFO, tag="GOCQ", max_len=40)
# print(res) if isinstance(source, int):
print("[System-Info] 回复QQ消息中..."+str(res)) source = {
"type": "GroupMessage",
"group_id": source
}
# 回复消息链
if isinstance(res, list) and len(res) > 0: if isinstance(res, list) and len(res) > 0:
await self.client.sendGroupMessage(source.group_id, res) if source.type == "GuildMessage":
return await self.client.sendGuildChannelMessage(source.guild_id, source.channel_id, res)
return
else:
await self.client.sendGroupMessage(source.group_id, res)
return
# 通过消息链处理 # 通过消息链处理
if not image_mode: if not image_mode:
if source.type == "GroupMessage": if source.type == "GroupMessage":
@@ -30,6 +55,10 @@ class QQ:
await self.client.sendFriendMessage(source.user_id, [ await self.client.sendFriendMessage(source.user_id, [
Plain(text=res) Plain(text=res)
]) ])
elif source.type == "GuildMessage":
await self.client.sendGuildChannelMessage(source.guild_id, source.channel_id, [
Plain(text=res)
])
else: else:
if source.type == "GroupMessage": if source.type == "GroupMessage":
await self.client.sendGroupMessage(source.group_id, [ await self.client.sendGroupMessage(source.group_id, [
@@ -41,4 +70,35 @@ class QQ:
await self.client.sendFriendMessage(source.user_id, [ await self.client.sendFriendMessage(source.user_id, [
Plain(text="好的,我根据你的需要为你生成了一张图片😊"), Plain(text="好的,我根据你的需要为你生成了一张图片😊"),
Image.fromURL(url=res) Image.fromURL(url=res)
]) ])
elif source.type == "GuildMessage":
await self.client.sendGuildChannelMessage(source.guild_id, source.channel_id, [
Plain(text="好的,我根据你的需要为你生成了一张图片😊"),
Image.fromURL(url=res)
])
def send(self,
to,
res,
):
'''
提供给插件的发送QQ消息接口, 不用在外部await。
参数说明:第一个参数可以是消息对象,也可以是QQ群号。第二个参数是消息内容(消息内容可以是消息链列表,也可以是纯文字信息)。
'''
try:
asyncio.run_coroutine_threadsafe(self.send_qq_msg(to, res), self.gocq_loop).result()
except BaseException as e:
raise e
def send_guild(self,
message_obj,
res,
):
'''
提供给插件的发送GOCQ QQ频道消息接口, 不用在外部await。
参数说明:第一个参数必须是消息对象, 第二个参数是消息内容(消息内容可以是消息链列表,也可以是纯文字信息)。
'''
try:
asyncio.run_coroutine_threadsafe(self.send_qq_msg(message_obj, res), self.gocq_loop).result()
except BaseException as e:
raise e
+3 -2
View File
@@ -6,7 +6,7 @@ import re
import asyncio import asyncio
import requests import requests
from cores.qqbot.personality import personalities from cores.qqbot.personality import personalities
from util import general_utils as gu
class QQChan(): class QQChan():
@@ -16,7 +16,8 @@ class QQChan():
self.client.run(appid=appid, token=token) self.client.run(appid=appid, token=token)
def send_qq_msg(self, message, res, image_mode=False, msg_ref = None): def send_qq_msg(self, message, res, image_mode=False, msg_ref = None):
print("[System-Info] 回复QQ频道消息中..."+str(res)) gu.log("回复QQ频道消息: "+str(res), level=gu.LEVEL_INFO, tag="QQ频道", max_len=30)
if not image_mode: if not image_mode:
try: try:
if msg_ref is not None: if msg_ref is not None:
+23 -19
View File
@@ -6,6 +6,7 @@ import sys
from cores.database.conn import dbConn from cores.database.conn import dbConn
from model.provider.provider import Provider from model.provider.provider import Provider
import threading import threading
from util import general_utils as gu
abs_path = os.path.dirname(os.path.realpath(sys.argv[0])) + '/' abs_path = os.path.dirname(os.path.realpath(sys.argv[0])) + '/'
key_record_path = abs_path+'chatgpt_key_record' key_record_path = abs_path+'chatgpt_key_record'
@@ -16,7 +17,7 @@ class ProviderOpenAIOfficial(Provider):
if 'api_base' in cfg and cfg['api_base'] != 'none' and cfg['api_base'] != '': if 'api_base' in cfg and cfg['api_base'] != 'none' and cfg['api_base'] != '':
openai.api_base = cfg['api_base'] openai.api_base = cfg['api_base']
if cfg['key'] != '' and cfg['key'] != None: if cfg['key'] != '' and cfg['key'] != None:
print("[System] 读取ChatGPT Key成功") gu.log("读取ChatGPT Key成功")
self.key_list = cfg['key'] self.key_list = cfg['key']
else: else:
input("[System] 请先去完善ChatGPT的Key。详情请前往https://beta.openai.com/account/api-keys") input("[System] 请先去完善ChatGPT的Key。详情请前往https://beta.openai.com/account/api-keys")
@@ -25,7 +26,7 @@ class ProviderOpenAIOfficial(Provider):
self.init_key_record() self.init_key_record()
self.chatGPT_configs = cfg['chatGPTConfigs'] self.chatGPT_configs = cfg['chatGPTConfigs']
print(f'[System] 加载ChatGPTConfigs: {self.chatGPT_configs}') gu.log(f'加载ChatGPTConfigs: {self.chatGPT_configs}')
self.openai_configs = cfg self.openai_configs = cfg
# 会话缓存 # 会话缓存
self.session_dict = {} self.session_dict = {}
@@ -39,9 +40,10 @@ class ProviderOpenAIOfficial(Provider):
db1 = dbConn() db1 = dbConn()
for session in db1.get_all_session(): for session in db1.get_all_session():
self.session_dict[session[0]] = json.loads(session[1])['data'] self.session_dict[session[0]] = json.loads(session[1])['data']
print("[System] 历史记录读取成功喵") gu.log("历史记录读取成功喵")
except BaseException as e: except BaseException as e:
print("[System] 历史记录读取失败: " + str(e)) gu.log("历史记录读取失败", level=gu.LEVEL_ERROR)
# 读取统计信息 # 读取统计信息
if not os.path.exists(abs_path+"configs/stat"): if not os.path.exists(abs_path+"configs/stat"):
@@ -118,9 +120,8 @@ class ProviderOpenAIOfficial(Provider):
) )
break break
except Exception as e: 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): 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已超额或者不正常,正在切换") gu.log("当前Key已超额或者不正常,正在切换", level=gu.LEVEL_WARNING)
self.key_stat[openai.api_key]['exceed'] = True self.key_stat[openai.api_key]['exceed'] = True
self.save_key_record() self.save_key_record()
@@ -130,17 +131,20 @@ class ProviderOpenAIOfficial(Provider):
raise e raise e
else: else:
break break
if 'maximum context length' in str(e): elif 'maximum context length' in str(e):
print("token超限, 清空对应缓存") gu.log("token超限, 清空对应缓存")
self.session_dict[session_id] = [] self.session_dict[session_id] = []
cache_data_list, new_record, req = self.wrap(prompt, session_id) cache_data_list, new_record, req = self.wrap(prompt, session_id)
else:
gu.log(str(e), level=gu.LEVEL_ERROR)
retry+=1 retry+=1
if retry >= 5: if retry >= 5:
raise BaseException("连接超时") gu.log(r"如果报错, 且您的机器在中国大陆内, 请确保您的电脑已经设置好代理软件(梯子), 并在配置文件设置了系统代理地址。详见https://github.com/Soulter/QQChannelChatGPT/wiki/%E4%BA%8C%E3%80%81%E9%A1%B9%E7%9B%AE%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6%E9%85%8D%E7%BD%AE", max_len=999)
raise BaseException("连接出错")
self.key_stat[openai.api_key]['used'] += response['usage']['total_tokens'] self.key_stat[openai.api_key]['used'] += response['usage']['total_tokens']
self.save_key_record() self.save_key_record()
print("[ChatGPT] "+str(response["choices"][0]["message"]["content"])) # print("[ChatGPT] "+str(response["choices"][0]["message"]["content"]))
chatgpt_res = str(response["choices"][0]["message"]["content"]).strip() chatgpt_res = str(response["choices"][0]["message"]["content"]).strip()
current_usage_tokens = response['usage']['total_tokens'] current_usage_tokens = response['usage']['total_tokens']
@@ -192,13 +196,12 @@ class ProviderOpenAIOfficial(Provider):
image_url = [] image_url = []
for i in range(img_num): for i in range(img_num):
image_url.append(response['data'][i]['url']) image_url.append(response['data'][i]['url'])
print(image_url)
break break
except Exception as e: except Exception as e:
print(e) gu.log(str(e), level=gu.LEVEL_ERROR)
if 'You exceeded' in str(e) or 'Billing hard limit has been reached' in str( 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): e) or 'No API key provided' in str(e) or 'Incorrect API key provided' in str(e):
print("[System] 当前Key已超额或者不正常,正在切换") gu.log("当前Key已超额或者不正常, 正在切换", level=gu.LEVEL_WARNING)
self.key_stat[openai.api_key]['exceed'] = True self.key_stat[openai.api_key]['exceed'] = True
self.save_key_record() self.save_key_record()
@@ -305,7 +308,7 @@ class ProviderOpenAIOfficial(Provider):
if not self.key_stat[key]['exceed']: if not self.key_stat[key]['exceed']:
is_all_exceed = False is_all_exceed = False
openai.api_key = key openai.api_key = key
print(f"[System] 切换到Key: {key}, 已使用token: {self.key_stat[key]['used']}") gu.log(f"切换到Key: {key}, 已使用token: {self.key_stat[key]['used']}", level=gu.LEVEL_INFO)
if len(req) > 0: if len(req) > 0:
try: try:
response = openai.ChatCompletion.create( response = openai.ChatCompletion.create(
@@ -314,20 +317,21 @@ class ProviderOpenAIOfficial(Provider):
) )
return response, True return response, True
except Exception as e: except Exception as e:
print(e)
if 'You exceeded' in str(e): if 'You exceeded' in str(e):
print("[System] 当前Key已超额,正在切换") gu.log("当前Key已超额, 正在切换")
self.key_stat[openai.api_key]['exceed'] = True self.key_stat[openai.api_key]['exceed'] = True
self.save_key_record() self.save_key_record()
time.sleep(1) time.sleep(1)
continue continue
else:
gu.log(str(e), level=gu.LEVEL_ERROR)
else: else:
return True return True
if is_all_exceed: if is_all_exceed:
print("[System] 所有Key已超额") gu.log("所有Key已超额", level=gu.LEVEL_CRITICAL)
return None, False return None, False
else: else:
print("[System] 在切换key时程序异常。") gu.log("在切换key时程序异常。", level=gu.LEVEL_ERROR)
return None, False return None, False
def getConfigs(self): def getConfigs(self):
@@ -375,7 +379,7 @@ class ProviderOpenAIOfficial(Provider):
try: try:
self.key_stat = json.load(keyfile) self.key_stat = json.load(keyfile)
except Exception as e: except Exception as e:
print(e) gu.log(str(e), level=gu.LEVEL_ERROR)
self.key_stat = {} self.key_stat = {}
finally: finally:
for key in self.key_list: for key in self.key_list:
+19 -9
View File
@@ -1,13 +1,16 @@
from revChatGPT.V1 import Chatbot from revChatGPT.V1 import Chatbot
from revChatGPT import typings from revChatGPT import typings
from model.provider.provider import Provider from model.provider.provider import Provider
from util import general_utils as gu
class ProviderRevChatGPT(Provider): class ProviderRevChatGPT(Provider):
def __init__(self, config): def __init__(self, config):
self.rev_chatgpt = [] self.rev_chatgpt = []
for i in range(0, len(config['account'])): for i in range(0, len(config['account'])):
try: try:
print(f"[System] 创建rev_ChatGPT负载{str(i)}中...") gu.log(f"创建rev_ChatGPT负载{str(i)}中...", level=gu.LEVEL_INFO, tag="RevChatGPT")
if 'password' in config['account'][i]: if 'password' in config['account'][i]:
config['account'][i]['password'] = str(config['account'][i]['password']) config['account'][i]['password'] = str(config['account'][i]['password'])
revstat = { revstat = {
@@ -16,7 +19,7 @@ class ProviderRevChatGPT(Provider):
} }
self.rev_chatgpt.append(revstat) self.rev_chatgpt.append(revstat)
except BaseException as e: except BaseException as e:
print(f"[System] 创建rev_ChatGPT负载失败: {str(e)}") gu.log(f"创建rev_ChatGPT负载{str(i)}失败: {str(e)}", level=gu.LEVEL_ERROR, tag="RevChatGPT")
def forget(self) -> bool: def forget(self) -> bool:
return False return False
@@ -42,21 +45,22 @@ class ProviderRevChatGPT(Provider):
raise e raise e
err_count += 1 err_count += 1
print(f"[RevChatGPT] 请求出现问题: {str(e)} | 正在重试: {str(err_count)}") gu.log(f"请求出现问题: {str(e)} | 正在重试: {str(err_count)}", level=gu.LEVEL_WARNING, tag="RevChatGPT")
if err_count >= retry_count: if err_count >= retry_count:
raise e raise e
except BaseException as e: except BaseException as e:
err_count += 1 err_count += 1
print(f"[RevChatGPT] 请求出现问题: {str(e)} | 正在重试: {str(err_count)}") gu.log(f"请求出现问题: {str(e)} | 正在重试: {str(err_count)}", level=gu.LEVEL_WARNING, tag="RevChatGPT")
if err_count >= retry_count: if err_count >= retry_count:
raise e raise e
if resp == '':
resp = "RevChatGPT出现故障."
print("[RevChatGPT] "+str(resp)) # print("[RevChatGPT] "+str(resp))
return resp return resp
def text_chat(self, prompt): def text_chat(self, prompt) -> str:
res = '' res = ''
print("[Debug] "+str(self.rev_chatgpt))
err_msg = '' err_msg = ''
cursor = 0 cursor = 0
for revstat in self.rev_chatgpt: for revstat in self.rev_chatgpt:
@@ -70,11 +74,17 @@ class ProviderRevChatGPT(Provider):
# todo: 细化错误管理 # todo: 细化错误管理
except BaseException as e: except BaseException as e:
revstat['busy'] = False revstat['busy'] = False
print(f"请求出现问题: {str(e)}") gu.log(f"请求出现问题: {str(e)}", level=gu.LEVEL_WARNING, tag="RevChatGPT")
err_msg += f"账号{cursor} - 错误原因: {str(e)}" err_msg += f"账号{cursor} - 错误原因: {str(e)}"
continue continue
else: else:
err_msg += f"账号{cursor} - 错误原因: 忙碌" err_msg += f"账号{cursor} - 错误原因: 忙碌"
continue continue
res = f'回复失败。错误跟踪:{err_msg}' res = f'回复失败。错误跟踪:{err_msg}'
return res return res
def is_all_busy(self) -> bool:
for revstat in self.rev_chatgpt:
if not revstat['busy']:
return False
return True
+6 -4
View File
@@ -2,6 +2,8 @@ from model.provider.provider import Provider
from EdgeGPT import Chatbot, ConversationStyle from EdgeGPT import Chatbot, ConversationStyle
import json import json
import os import os
from util import general_utils as gu
class ProviderRevEdgeGPT(Provider): class ProviderRevEdgeGPT(Provider):
def __init__(self): def __init__(self):
@@ -76,15 +78,15 @@ class ProviderRevEdgeGPT(Provider):
reply_msg += f"\n{throttling['numUserMessagesInConversation']}/{throttling['maxNumUserMessagesInConversation']}" reply_msg += f"\n{throttling['numUserMessagesInConversation']}/{throttling['maxNumUserMessagesInConversation']}"
break break
except BaseException as e: except BaseException as e:
# raise e gu.log(str(e), level=gu.LEVEL_WARNING, tag="RevEdgeGPT")
print(e)
err_count += 1 err_count += 1
if err_count >= retry_count: if err_count >= retry_count:
gu.log(r"如果报错, 且您的机器在中国大陆内, 请确保您的电脑已经设置好代理软件(梯子), 并在配置文件设置了系统代理地址。详见https://github.com/Soulter/QQChannelChatGPT/wiki/%E4%BA%8C%E3%80%81%E9%A1%B9%E7%9B%AE%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6%E9%85%8D%E7%BD%AE", max_len=999)
self.busy = False self.busy = False
raise e raise e
print("[RevEdgeGPT] 请求出现了一些问题, 正在重试。次数"+str(err_count)) gu.log("请求出现了一些问题, 正在重试。次数"+str(err_count), level=gu.LEVEL_WARNING, tag="RevEdgeGPT")
self.busy = False self.busy = False
print("[RevEdgeGPT] "+str(reply_msg)) # print("[RevEdgeGPT] "+str(reply_msg))
return reply_msg, 1 return reply_msg, 1
+87
View File
@@ -0,0 +1,87 @@
import datetime
import socket
FG_COLORS = {
"black": "30",
"red": "31",
"green": "32",
"yellow": "33",
"blue": "34",
"purple": "35",
"cyan": "36",
"white": "37",
"default": "39",
}
BG_COLORS = {
"black": "40",
"red": "41",
"green": "42",
"yellow": "43",
"blue": "44",
"purple": "45",
"cyan": "46",
"white": "47",
"default": "49",
}
LEVEL_INFO = "INFO"
LEVEL_WARNING = "WARNING"
LEVEL_ERROR = "ERROR"
LEVEL_CRITICAL = "CRITICAL"
level_colors = {
"INFO": "green",
"WARNING": "yellow",
"ERROR": "red",
"CRITICAL": "purple",
}
def log(
msg: str,
level: str = "INFO",
tag: str = "System",
fg: str = None,
bg: str = None,
max_len: int = 100):
"""
日志记录函数
"""
if len(msg) > max_len:
msg = msg[:max_len] + "..."
now = datetime.datetime.now().strftime("%m-%d %H:%M:%S")
pre = f"[{now}] [{level}] [{tag}]: {msg}"
if level == "INFO":
if fg is None:
fg = FG_COLORS["green"]
if bg is None:
bg = BG_COLORS["default"]
elif level == "WARNING":
if fg is None:
fg = FG_COLORS["yellow"]
if bg is None:
bg = BG_COLORS["default"]
elif level == "ERROR":
if fg is None:
fg = FG_COLORS["red"]
if bg is None:
bg = BG_COLORS["default"]
elif level == "CRITICAL":
if fg is None:
fg = FG_COLORS["purple"]
if bg is None:
bg = BG_COLORS["default"]
print(f"\033[{fg};{bg}m{pre}\033[0m")
def port_checker(port: int, host: str = "localhost"):
sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sk.settimeout(1)
try:
sk.connect((host, port))
sk.close()
return True
except Exception:
sk.close()
return False
+1 -1
View File
@@ -7,7 +7,7 @@ def get_classes(p_name, arg):
clsmembers = inspect.getmembers(arg, inspect.isclass) clsmembers = inspect.getmembers(arg, inspect.isclass)
for (name, _) in clsmembers: for (name, _) in clsmembers:
# print(name, p_name) # print(name, p_name)
if p_name.lower() == name.lower().replace("plugin", ""): if p_name.lower() == name.lower()[:-6]:
classes.append(name) classes.append(name)
break break
return classes return classes