Merge branch 'master' into dev
This commit is contained in:
@@ -26,14 +26,14 @@
|
||||
|
||||
🌍支持的AI语言模型一览:
|
||||
|
||||
**文字模型**
|
||||
**文字模型/图片理解**
|
||||
|
||||
- OpenAI GPT-3模型(原生支持)
|
||||
- OpenAI GPT-3.5模型(原生支持)
|
||||
- OpenAI GPT-4模型(原生支持)
|
||||
- ChatGPT网页版 GPT-3.5模型(免费,原生支持)
|
||||
- ChatGPT网页版 GPT-4模型(需订阅Plus账户,原生支持)
|
||||
- Bing(免费,原生支持)
|
||||
- ~~ChatGPT网页版 GPT-3.5模型(免费,原生支持)~~
|
||||
- ~~ChatGPT网页版 GPT-4模型(需订阅Plus账户,原生支持)~~
|
||||
- ~~Bing(免费,原生支持)~~
|
||||
- Claude模型(免费,由[LLMs插件](https://github.com/Soulter/llms)支持)
|
||||
- HuggingChat模型(免费,由[LLMs插件](https://github.com/Soulter/llms)支持)
|
||||
- Google Bard(免费,由[LLMs插件](https://github.com/Soulter/llms)支持)
|
||||
@@ -44,9 +44,9 @@
|
||||
|
||||
|
||||
🌍机器人支持的能力一览:
|
||||
- 同时部署机器人到QQ和QQ频道
|
||||
- 同时部署机器人到 QQ 和 QQ 频道
|
||||
- 大模型对话
|
||||
- 大模型网页搜索能力 **(目前仅支持OpenAI系的模型,最新版本下使用web on指令打开)**
|
||||
- 大模型网页搜索能力 **(目前仅支持OpenAI系的模型,最新版本下使用 web on 指令打开)**
|
||||
- 插件安装(在QQ或QQ频道聊天框内输入`plugin`了解详情)
|
||||
- 回复文字图片渲染(以图片markdown格式回复,**大幅度降低被风控概率**,需手动在`cmd_config.json`内开启qq_pic_mode)
|
||||
- 人格设置
|
||||
|
||||
@@ -50,6 +50,11 @@ class HelloWorldPlugin:
|
||||
return True, tuple([True, "Hello World!!", "helloworld"])
|
||||
else:
|
||||
return False, None
|
||||
else:
|
||||
"""
|
||||
其他平台处理逻辑
|
||||
"""
|
||||
return False, None
|
||||
"""
|
||||
帮助函数,当用户输入 plugin v 插件名称 时,会调用此函数,返回帮助信息
|
||||
返回参数要求(必填):dict{
|
||||
|
||||
+233
-136
@@ -13,6 +13,13 @@ from cores.qqbot.personality import personalities
|
||||
from addons.baidu_aip_judge import BaiduJudge
|
||||
from model.platform.qqchan import QQChan, NakuruGuildMember, NakuruGuildMessage
|
||||
from model.platform.qq import QQ
|
||||
from model.platform.qqgroup import (
|
||||
UnofficialQQBotSDK,
|
||||
Event as QQEvent,
|
||||
Message as QQMessage,
|
||||
MessageChain,
|
||||
PlainText
|
||||
)
|
||||
from nakuru import (
|
||||
CQHTTP,
|
||||
GroupMessage,
|
||||
@@ -86,25 +93,35 @@ PLATFORM_QQCHAN = 'qqchan'
|
||||
qqchan_loop = None
|
||||
client = None
|
||||
|
||||
# 配置
|
||||
cc.init_attributes(["qq_forward_threshold"], 200)
|
||||
cc.init_attributes(["qq_welcome"], "欢迎加入本群!\n欢迎给https://github.com/Soulter/QQChannelChatGPT项目一个Star😊~\n输入help查看帮助~\n")
|
||||
cc.init_attributes(["bing_proxy"], "")
|
||||
cc.init_attributes(["qq_pic_mode"], False)
|
||||
cc.init_attributes(["rev_chatgpt_model"], "")
|
||||
cc.init_attributes(["rev_chatgpt_plugin_ids"], [])
|
||||
cc.init_attributes(["rev_chatgpt_PUID"], "")
|
||||
cc.init_attributes(["rev_chatgpt_unverified_plugin_domains"], [])
|
||||
cc.init_attributes(["gocq_host"], "127.0.0.1")
|
||||
cc.init_attributes(["gocq_http_port"], 5700)
|
||||
cc.init_attributes(["gocq_websocket_port"], 6700)
|
||||
cc.init_attributes(["gocq_react_group"], True)
|
||||
cc.init_attributes(["gocq_react_guild"], True)
|
||||
cc.init_attributes(["gocq_react_friend"], True)
|
||||
cc.init_attributes(["gocq_react_group_increase"], True)
|
||||
cc.init_attributes(["gocq_qqchan_admin"], "")
|
||||
cc.init_attributes(["other_admins"], [])
|
||||
cc.init_attributes(["CHATGPT_BASE_URL"], "")
|
||||
# QQ群机器人
|
||||
PLATFROM_QQBOT = 'qqbot'
|
||||
|
||||
# CLI
|
||||
PLATFORM_CLI = 'cli'
|
||||
|
||||
# 加载默认配置
|
||||
cc.init_attributes("qq_forward_threshold", 200)
|
||||
cc.init_attributes("qq_welcome", "欢迎加入本群!\n欢迎给https://github.com/Soulter/QQChannelChatGPT项目一个Star😊~\n输入help查看帮助~\n")
|
||||
cc.init_attributes("bing_proxy", "")
|
||||
cc.init_attributes("qq_pic_mode", False)
|
||||
cc.init_attributes("rev_chatgpt_model", "")
|
||||
cc.init_attributes("rev_chatgpt_plugin_ids", [])
|
||||
cc.init_attributes("rev_chatgpt_PUID", "")
|
||||
cc.init_attributes("rev_chatgpt_unverified_plugin_domains", [])
|
||||
cc.init_attributes("gocq_host", "127.0.0.1")
|
||||
cc.init_attributes("gocq_http_port", 5700)
|
||||
cc.init_attributes("gocq_websocket_port", 6700)
|
||||
cc.init_attributes("gocq_react_group", True)
|
||||
cc.init_attributes("gocq_react_guild", True)
|
||||
cc.init_attributes("gocq_react_friend", True)
|
||||
cc.init_attributes("gocq_react_group_increase", True)
|
||||
cc.init_attributes("gocq_qqchan_admin", "")
|
||||
cc.init_attributes("other_admins", [])
|
||||
cc.init_attributes("CHATGPT_BASE_URL", "")
|
||||
cc.init_attributes("qqbot_appid", "")
|
||||
cc.init_attributes("qqbot_secret", "")
|
||||
cc.init_attributes("llm_env_prompt", "> hint: 末尾根据内容和心情添加 1-2 个emoji")
|
||||
cc.init_attributes("default_personality_str", "")
|
||||
# cc.init_attributes(["qq_forward_mode"], False)
|
||||
|
||||
# QQ机器人
|
||||
@@ -115,8 +132,14 @@ gocq_app = CQHTTP(
|
||||
port=cc.get("gocq_websocket_port", 6700),
|
||||
http_port=cc.get("gocq_http_port", 5700),
|
||||
)
|
||||
qq_bot: UnofficialQQBotSDK = UnofficialQQBotSDK(
|
||||
cc.get("qqbot_appid", None),
|
||||
cc.get("qqbot_secret", None)
|
||||
)
|
||||
|
||||
gocq_loop: asyncio.AbstractEventLoop = None
|
||||
qqbot_loop: asyncio.AbstractEventLoop = None
|
||||
|
||||
gocq_loop = None
|
||||
|
||||
# 全局对象
|
||||
_global_object: GlobalObject = None
|
||||
@@ -200,6 +223,9 @@ def initBot(cfg, prov):
|
||||
global frequency_count, frequency_time, announcement, direct_message_mode, version
|
||||
global keywords, _global_object
|
||||
|
||||
_event_loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(_event_loop)
|
||||
|
||||
# 初始化 global_object
|
||||
_global_object = GlobalObject()
|
||||
_global_object.base_config = cfg
|
||||
@@ -207,61 +233,6 @@ def initBot(cfg, prov):
|
||||
if 'reply_prefix' in cfg:
|
||||
_global_object.reply_prefix = cfg['reply_prefix']
|
||||
|
||||
|
||||
gu.log("--------加载机器人平台--------", gu.LEVEL_INFO, fg=gu.FG_COLORS['yellow'])
|
||||
thread_inst = None
|
||||
admin_qq = cc.get('admin_qq', None)
|
||||
admin_qqchan = cc.get('admin_qqchan', None)
|
||||
if admin_qq == None:
|
||||
gu.log("未设置管理者QQ号(管理者才能使用update/plugin等指令)", gu.LEVEL_WARNING)
|
||||
admin_qq = input("请输入管理者QQ号(必须设置): ")
|
||||
gu.log("管理者QQ号设置为: " + admin_qq, gu.LEVEL_INFO, fg=gu.FG_COLORS['yellow'])
|
||||
cc.put('admin_qq', admin_qq)
|
||||
if admin_qqchan == None:
|
||||
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'])
|
||||
cc.put('admin_qqchan', admin_qqchan)
|
||||
|
||||
gu.log("管理者QQ: " + admin_qq, gu.LEVEL_INFO)
|
||||
gu.log("管理者频道用户号: " + admin_qqchan, gu.LEVEL_INFO)
|
||||
_global_object.admin_qq = admin_qq
|
||||
_global_object.admin_qqchan = admin_qqchan
|
||||
|
||||
# GOCQ
|
||||
global gocq_bot
|
||||
|
||||
if 'gocqbot' in cfg and cfg['gocqbot']['enable']:
|
||||
gu.log("- 启用QQ机器人 -", gu.LEVEL_INFO)
|
||||
|
||||
global gocq_app, gocq_loop
|
||||
gocq_loop = asyncio.new_event_loop()
|
||||
gocq_bot = QQ(True, cc, gocq_loop)
|
||||
thread_inst = threading.Thread(target=run_gocq_bot, args=(gocq_loop, gocq_bot, gocq_app), daemon=False)
|
||||
thread_inst.start()
|
||||
else:
|
||||
gocq_bot = QQ(False)
|
||||
|
||||
_global_object.platform_qq = gocq_bot
|
||||
|
||||
gu.log("机器人部署教程: https://github.com/Soulter/QQChannelChatGPT/wiki/", gu.LEVEL_INFO, fg=gu.FG_COLORS['yellow'])
|
||||
gu.log("如果有任何问题, 请在 https://github.com/Soulter/QQChannelChatGPT 上提交issue或加群322154837", 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频道
|
||||
if 'qqbot' in cfg and cfg['qqbot']['enable']:
|
||||
gu.log("- 启用QQ频道机器人 -", gu.LEVEL_INFO)
|
||||
global qqchannel_bot, qqchan_loop
|
||||
qqchannel_bot = QQChan()
|
||||
qqchan_loop = asyncio.new_event_loop()
|
||||
_global_object.platform_qqchan = qqchannel_bot
|
||||
thread_inst = threading.Thread(target=run_qqchan_bot, args=(cfg, qqchan_loop, qqchannel_bot), daemon=False)
|
||||
thread_inst.start()
|
||||
# thread.join()
|
||||
|
||||
# 语言模型提供商
|
||||
gu.log("--------加载语言模型--------", gu.LEVEL_INFO, fg=gu.FG_COLORS['yellow'])
|
||||
|
||||
@@ -380,12 +351,110 @@ def initBot(cfg, prov):
|
||||
llm_command_instance[NONE_LLM] = _command
|
||||
chosen_provider = NONE_LLM
|
||||
|
||||
gu.log("--------加载机器人平台--------", gu.LEVEL_INFO, fg=gu.FG_COLORS['yellow'])
|
||||
|
||||
admin_qq = cc.get('admin_qq', None)
|
||||
admin_qqchan = cc.get('admin_qqchan', None)
|
||||
if admin_qq == None:
|
||||
gu.log("未设置管理者QQ号(管理者才能使用update/plugin等指令)", gu.LEVEL_WARNING)
|
||||
admin_qq = input("请输入管理者QQ号(必须设置): ")
|
||||
gu.log("管理者QQ号设置为: " + admin_qq, gu.LEVEL_INFO, fg=gu.FG_COLORS['yellow'])
|
||||
cc.put('admin_qq', admin_qq)
|
||||
if admin_qqchan == None:
|
||||
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'])
|
||||
cc.put('admin_qqchan', admin_qqchan)
|
||||
|
||||
gu.log("管理者QQ: " + admin_qq, gu.LEVEL_INFO)
|
||||
gu.log("管理者频道用户号: " + admin_qqchan, gu.LEVEL_INFO)
|
||||
_global_object.admin_qq = admin_qq
|
||||
_global_object.admin_qqchan = admin_qqchan
|
||||
|
||||
|
||||
global qq_bot, qqbot_loop
|
||||
qqbot_loop = asyncio.new_event_loop()
|
||||
if cc.get("qqbot_appid", '') != '' and cc.get("qqbot_secret", '') != '':
|
||||
gu.log("- 启用QQ群机器人 -", gu.LEVEL_INFO)
|
||||
thread_inst = threading.Thread(target=run_qqbot, args=(qqbot_loop, qq_bot,), daemon=True)
|
||||
thread_inst.start()
|
||||
|
||||
|
||||
# GOCQ
|
||||
global gocq_bot
|
||||
if 'gocqbot' in cfg and cfg['gocqbot']['enable']:
|
||||
gu.log("- 启用QQ机器人 -", gu.LEVEL_INFO)
|
||||
|
||||
global gocq_app, gocq_loop
|
||||
gocq_loop = asyncio.new_event_loop()
|
||||
gocq_bot = QQ(True, cc, gocq_loop)
|
||||
thread_inst = threading.Thread(target=run_gocq_bot, args=(gocq_loop, gocq_bot, gocq_app), daemon=True)
|
||||
thread_inst.start()
|
||||
else:
|
||||
gocq_bot = QQ(False)
|
||||
|
||||
_global_object.platform_qq = gocq_bot
|
||||
|
||||
gu.log("机器人部署教程: https://github.com/Soulter/QQChannelChatGPT/wiki/", gu.LEVEL_INFO, fg=gu.FG_COLORS['yellow'])
|
||||
gu.log("如果有任何问题, 请在 https://github.com/Soulter/QQChannelChatGPT 上提交issue或加群322154837", 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频道
|
||||
if 'qqbot' in cfg and cfg['qqbot']['enable']:
|
||||
gu.log("- 启用QQ频道机器人 -", gu.LEVEL_INFO)
|
||||
global qqchannel_bot, qqchan_loop
|
||||
qqchannel_bot = QQChan()
|
||||
qqchan_loop = asyncio.new_event_loop()
|
||||
_global_object.platform_qqchan = qqchannel_bot
|
||||
thread_inst = threading.Thread(target=run_qqchan_bot, args=(cfg, qqchan_loop, qqchannel_bot), daemon=True)
|
||||
thread_inst.start()
|
||||
# thread.join()
|
||||
|
||||
if thread_inst == None:
|
||||
input("[System-Error] 没有启用/成功启用任何机器人,程序退出")
|
||||
exit()
|
||||
|
||||
thread_inst.join()
|
||||
default_personality_str = cc.get("default_personality_str", "")
|
||||
if default_personality_str == "":
|
||||
_global_object.default_personality = None
|
||||
else:
|
||||
_global_object.default_personality = {
|
||||
"name": "default",
|
||||
"prompt": default_personality_str,
|
||||
}
|
||||
|
||||
gu.log("🎉 项目启动完成。")
|
||||
|
||||
# thread_inst.join()
|
||||
asyncio.get_event_loop().run_until_complete(cli())
|
||||
|
||||
async def cli():
|
||||
time.sleep(1)
|
||||
while True:
|
||||
prompt = input(">>> ")
|
||||
if prompt == "":
|
||||
continue
|
||||
ngm = await cli_pack_message(prompt)
|
||||
await oper_msg(ngm, True, PLATFORM_CLI)
|
||||
|
||||
async def cli_pack_message(prompt: str) -> NakuruGuildMessage:
|
||||
ngm = NakuruGuildMessage()
|
||||
ngm.channel_id = 6180
|
||||
ngm.user_id = 6180
|
||||
ngm.message = [Plain(prompt)]
|
||||
ngm.type = "GuildMessage"
|
||||
ngm.self_id = 6180
|
||||
ngm.self_tiny_id = 6180
|
||||
ngm.guild_id = 6180
|
||||
ngm.sender = NakuruGuildMember()
|
||||
ngm.sender.tiny_id = 6180
|
||||
ngm.sender.user_id = 6180
|
||||
ngm.sender.nickname = "CLI"
|
||||
ngm.sender.role = 0
|
||||
return ngm
|
||||
|
||||
'''
|
||||
运行QQ频道机器人
|
||||
@@ -416,9 +485,12 @@ def run_qqchan_bot(cfg, loop, qqchannel_bot: QQChan):
|
||||
def run_gocq_bot(loop, gocq_bot, gocq_app):
|
||||
asyncio.set_event_loop(loop)
|
||||
gu.log("正在检查本地GO-CQHTTP连接...端口5700, 6700", tag="QQ")
|
||||
noticed = False
|
||||
while True:
|
||||
if not gu.port_checker(5700, cc.get("gocq_host", "127.0.0.1")) or not gu.port_checker(6700, cc.get("gocq_host", "127.0.0.1")):
|
||||
gu.log("与GO-CQHTTP通信失败, 请检查GO-CQHTTP是否启动并正确配置。5秒后自动重试。", gu.LEVEL_CRITICAL, tag="QQ")
|
||||
if not noticed:
|
||||
noticed = True
|
||||
gu.log("与GO-CQHTTP通信失败, 请检查GO-CQHTTP是否启动并正确配置。程序会每隔 5s 自动重试。", gu.LEVEL_CRITICAL, tag="QQ")
|
||||
time.sleep(5)
|
||||
else:
|
||||
gu.log("检查完毕,未发现问题。", tag="QQ")
|
||||
@@ -431,6 +503,15 @@ def run_gocq_bot(loop, gocq_bot, gocq_app):
|
||||
except BaseException as e:
|
||||
input("启动QQ机器人出现错误"+str(e))
|
||||
|
||||
'''
|
||||
启动QQ群机器人(官方接口)
|
||||
'''
|
||||
def run_qqbot(loop: asyncio.AbstractEventLoop, qq_bot: UnofficialQQBotSDK):
|
||||
asyncio.set_event_loop(loop)
|
||||
QQBotClient()
|
||||
qq_bot.run_bot()
|
||||
|
||||
|
||||
'''
|
||||
检查发言频率
|
||||
'''
|
||||
@@ -469,6 +550,12 @@ async def send_message(platform, message, res, session_id = None):
|
||||
qqchannel_bot.send_qq_msg(message, res)
|
||||
if platform == PLATFORM_GOCQ:
|
||||
await gocq_bot.send_qq_msg(message, res)
|
||||
if platform == PLATFROM_QQBOT:
|
||||
message_chain = MessageChain()
|
||||
message_chain.parse_from_nakuru(res)
|
||||
await qq_bot.send(message, message_chain)
|
||||
if platform == PLATFORM_CLI:
|
||||
print(res)
|
||||
|
||||
async def oper_msg(message: Union[GroupMessage, FriendMessage, GuildMessage, NakuruGuildMessage],
|
||||
group: bool=False,
|
||||
@@ -493,61 +580,60 @@ async def oper_msg(message: Union[GroupMessage, FriendMessage, GuildMessage, Nak
|
||||
|
||||
with_tag = False # 是否带有昵称
|
||||
|
||||
if platform == PLATFORM_GOCQ or platform == PLATFORM_QQCHAN:
|
||||
_len = 0
|
||||
for i in message.message:
|
||||
if isinstance(i, Plain):
|
||||
qq_msg += str(i.text).strip()
|
||||
if isinstance(i, At):
|
||||
# @机器人
|
||||
if message.type == "GuildMessage":
|
||||
if i.qq == message.user_id or i.qq == message.self_tiny_id:
|
||||
with_tag = True
|
||||
if message.type == "FriendMessage":
|
||||
if i.qq == message.self_id:
|
||||
with_tag = True
|
||||
if message.type == "GroupMessage":
|
||||
if i.qq == message.self_id:
|
||||
with_tag = True
|
||||
|
||||
for i in _global_object.nick:
|
||||
if i != '' and qq_msg.startswith(i):
|
||||
_len = len(i)
|
||||
with_tag = True
|
||||
break
|
||||
qq_msg = qq_msg[_len:].strip()
|
||||
|
||||
gu.log(f"收到消息:{qq_msg}", gu.LEVEL_INFO, tag="QQ")
|
||||
user_id = message.user_id
|
||||
|
||||
if group:
|
||||
# 适配GO-CQHTTP的频道功能
|
||||
if message.type == "GuildMessage":
|
||||
session_id = message.channel_id
|
||||
else:
|
||||
session_id = message.group_id
|
||||
else:
|
||||
with_tag = True
|
||||
session_id = message.user_id
|
||||
role = "member"
|
||||
|
||||
if message.type == "GuildMessage":
|
||||
sender_id = str(message.sender.tiny_id)
|
||||
else:
|
||||
sender_id = str(message.sender.user_id)
|
||||
if sender_id == _global_object.admin_qq or \
|
||||
sender_id == _global_object.admin_qqchan or \
|
||||
sender_id in cc.get("other_admins", []) or \
|
||||
sender_id == cc.get("gocq_qqchan_admin", ""):
|
||||
# gu.log("检测到管理员身份", gu.LEVEL_INFO, tag="GOCQ")
|
||||
role = "admin"
|
||||
if _global_object.uniqueSession:
|
||||
# 独立会话时,一个用户一个session
|
||||
session_id = sender_id
|
||||
|
||||
if platform == PLATFORM_QQCHAN:
|
||||
if platform == PLATFORM_QQCHAN or platform == PLATFROM_QQBOT or platform == PLATFORM_CLI:
|
||||
with_tag = True
|
||||
|
||||
_len = 0
|
||||
for i in message.message:
|
||||
if isinstance(i, Plain) or isinstance(i, PlainText):
|
||||
qq_msg += str(i.text).strip()
|
||||
if isinstance(i, At):
|
||||
if message.type == "GuildMessage":
|
||||
if i.qq == message.user_id or i.qq == message.self_tiny_id:
|
||||
with_tag = True
|
||||
if message.type == "FriendMessage":
|
||||
if i.qq == message.self_id:
|
||||
with_tag = True
|
||||
if message.type == "GroupMessage":
|
||||
if i.qq == message.self_id:
|
||||
with_tag = True
|
||||
|
||||
for i in _global_object.nick:
|
||||
if i != '' and qq_msg.startswith(i):
|
||||
_len = len(i)
|
||||
with_tag = True
|
||||
break
|
||||
qq_msg = qq_msg[_len:].strip()
|
||||
|
||||
gu.log(f"收到消息:{qq_msg}", gu.LEVEL_INFO, tag="QQ")
|
||||
user_id = message.user_id
|
||||
|
||||
if group:
|
||||
# 适配GO-CQHTTP的频道功能
|
||||
if message.type == "GuildMessage":
|
||||
session_id = message.channel_id
|
||||
else:
|
||||
session_id = message.group_id
|
||||
else:
|
||||
with_tag = True
|
||||
session_id = message.user_id
|
||||
|
||||
if message.type == "GuildMessage":
|
||||
sender_id = str(message.sender.tiny_id)
|
||||
else:
|
||||
sender_id = str(message.sender.user_id)
|
||||
if sender_id == _global_object.admin_qq or \
|
||||
sender_id == _global_object.admin_qqchan or \
|
||||
sender_id in cc.get("other_admins", []) or \
|
||||
sender_id == cc.get("gocq_qqchan_admin", "") or \
|
||||
platform == PLATFORM_CLI:
|
||||
role = "admin"
|
||||
|
||||
if _global_object.uniqueSession:
|
||||
# 独立会话时,一个用户一个 session
|
||||
session_id = sender_id
|
||||
|
||||
|
||||
if qq_msg == "":
|
||||
await send_message(platform, message, f"Hi~", session_id=session_id)
|
||||
return
|
||||
@@ -603,10 +689,15 @@ async def oper_msg(message: Union[GroupMessage, FriendMessage, GuildMessage, Nak
|
||||
|
||||
chatgpt_res = ""
|
||||
|
||||
if session_id in gocq_bot.waiting and gocq_bot.waiting[session_id] == '':
|
||||
gocq_bot.waiting[session_id] = qq_msg
|
||||
# 如果是等待回复的消息
|
||||
if platform == PLATFORM_GOCQ and session_id in gocq_bot.waiting and gocq_bot.waiting[session_id] == '':
|
||||
gocq_bot.waiting[session_id] = message
|
||||
return
|
||||
hit, command_result = await llm_command_instance[chosen_provider].check_command(
|
||||
if platform == PLATFORM_QQCHAN and session_id in qqchannel_bot.waiting and qqchannel_bot.waiting[session_id] == '':
|
||||
qqchannel_bot.waiting[session_id] = message
|
||||
return
|
||||
|
||||
hit, command_result = llm_command_instance[chosen_provider].check_command(
|
||||
qq_msg,
|
||||
session_id,
|
||||
role,
|
||||
@@ -649,13 +740,13 @@ async def oper_msg(message: Union[GroupMessage, FriendMessage, GuildMessage, Nak
|
||||
qq_msg = qq_msg[3:]
|
||||
web_sch_flag = True
|
||||
else:
|
||||
qq_msg += "> hint: 末尾根据内容和心情添加1-2个emoji"
|
||||
qq_msg += " " + cc.get("llm_env_prompt", "")
|
||||
if chosen_provider == REV_CHATGPT or chosen_provider == OPENAI_OFFICIAL:
|
||||
if _global_object.web_search or web_sch_flag:
|
||||
official_fc = chosen_provider == OPENAI_OFFICIAL
|
||||
chatgpt_res = gplugin.web_search(qq_msg, llm_instance[chosen_provider], session_id, official_fc)
|
||||
else:
|
||||
chatgpt_res = str(llm_instance[chosen_provider].text_chat(qq_msg, session_id, image_url))
|
||||
chatgpt_res = str(llm_instance[chosen_provider].text_chat(qq_msg, session_id, image_url, default_personality = _global_object.default_personality))
|
||||
elif chosen_provider == REV_EDGEGPT:
|
||||
res, res_code = await llm_instance[chosen_provider].text_chat(qq_msg, platform)
|
||||
if res_code == 0: # bing不想继续话题,重置会话后重试。
|
||||
@@ -811,4 +902,10 @@ class gocqClient():
|
||||
if source.message[0].qq == source.self_tiny_id:
|
||||
new_sub_thread(oper_msg, (source, True, PLATFORM_GOCQ))
|
||||
else:
|
||||
return
|
||||
return
|
||||
|
||||
class QQBotClient():
|
||||
@qq_bot.on('GroupMessage')
|
||||
async def _(bot: UnofficialQQBotSDK, message: QQMessage):
|
||||
print(message)
|
||||
new_sub_thread(oper_msg, (message, True, PLATFROM_QQBOT))
|
||||
@@ -26,6 +26,7 @@ class GlobalObject:
|
||||
cnt_total: int
|
||||
platform_qq: QQ
|
||||
platform_qqchan: QQChan
|
||||
default_personality: dict
|
||||
|
||||
def __init__(self):
|
||||
self.nick = None # gocq 的昵称
|
||||
@@ -39,6 +40,7 @@ class GlobalObject:
|
||||
self.cnt_total = 0
|
||||
self.platform_qq = None
|
||||
self.platform_qqchan = None
|
||||
self.default_personality = None
|
||||
|
||||
class AstrMessageEvent():
|
||||
message_str: str # 纯消息字符串
|
||||
@@ -48,6 +50,7 @@ class AstrMessageEvent():
|
||||
platform: str # `gocq` 或 `qqchan`
|
||||
role: str # `admin` 或 `member`
|
||||
global_object: GlobalObject # 一些公用数据
|
||||
session_id: int # 会话id (可能是群id,也可能是某个user的id。取决于是否开启了 uniqueSession)
|
||||
|
||||
def __init__(self, message_str: str,
|
||||
message_obj: Union[GroupMessage, FriendMessage, GuildMessage, NakuruGuildMessage],
|
||||
@@ -56,7 +59,8 @@ class AstrMessageEvent():
|
||||
platform: str,
|
||||
role: str,
|
||||
global_object: GlobalObject,
|
||||
llm_provider: Provider = None):
|
||||
llm_provider: Provider = None,
|
||||
session_id: int = None):
|
||||
self.message_str = message_str
|
||||
self.message_obj = message_obj
|
||||
self.gocq_platform = gocq_platform
|
||||
@@ -64,4 +68,5 @@ class AstrMessageEvent():
|
||||
self.platform = platform
|
||||
self.role = role
|
||||
self.global_object = global_object
|
||||
self.llm_provider = llm_provider
|
||||
self.llm_provider = llm_provider
|
||||
self.session_id = session_id
|
||||
@@ -2,6 +2,7 @@ import os, sys
|
||||
from pip._internal import main as pipmain
|
||||
import warnings
|
||||
import traceback
|
||||
import threading
|
||||
|
||||
warnings.filterwarnings("ignore")
|
||||
abs_path = os.path.dirname(os.path.realpath(sys.argv[0])) + '/'
|
||||
@@ -128,4 +129,7 @@ if __name__ == "__main__":
|
||||
except BaseException as e:
|
||||
print(e)
|
||||
print(f"[System-err] Replit Web保活服务启动失败:{str(e)}")
|
||||
main()
|
||||
|
||||
t = threading.Thread(target=main, daemon=False)
|
||||
t.start()
|
||||
t.join()
|
||||
|
||||
@@ -58,7 +58,8 @@ class Command:
|
||||
qq_sdk_platform=self.global_object.platform_qqchan,
|
||||
platform=platform,
|
||||
role=role,
|
||||
global_object=self.global_object
|
||||
global_object=self.global_object,
|
||||
session_id = session_id
|
||||
)
|
||||
for k, v in cached_plugins.items():
|
||||
try:
|
||||
@@ -131,8 +132,6 @@ class Command:
|
||||
fail_rec = ""
|
||||
if plugins is None:
|
||||
return False, "未找到任何插件模块"
|
||||
|
||||
print(plugins)
|
||||
|
||||
for plugin in plugins:
|
||||
try:
|
||||
|
||||
@@ -191,7 +191,7 @@ class CommandOpenAIOfficial(Command):
|
||||
def unset(self, session_id: str):
|
||||
if self.provider is None:
|
||||
return False, "未启动OpenAI ChatGPT语言模型.", "unset"
|
||||
self.provider.now_personality = {}
|
||||
self.provider.curr_personality = {}
|
||||
self.provider.forget(session_id)
|
||||
return True, "已清除人格并重置历史记录。", "unset"
|
||||
|
||||
@@ -202,7 +202,7 @@ class CommandOpenAIOfficial(Command):
|
||||
if len(l) == 1:
|
||||
return True, f"【人格文本由PlexPt开源项目awesome-chatgpt-pr \
|
||||
ompts-zh提供】\n设置人格: \n/set 人格名。例如/set 编剧\n人格列表: /set list\n人格详细信息: \
|
||||
/set view 人格名\n自定义人格: /set 人格文本\n重置会话(清除人格): /reset\n重置会话(保留人格): /reset p\n【当前人格】: {str(self.provider.now_personality)}", "set"
|
||||
/set view 人格名\n自定义人格: /set 人格文本\n重置会话(清除人格): /reset\n重置会话(保留人格): /reset p\n【当前人格】: {str(self.provider.curr_personality)}", "set"
|
||||
elif l[1] == "list":
|
||||
msg = "人格列表:\n"
|
||||
for key in personalities.keys():
|
||||
@@ -223,7 +223,7 @@ class CommandOpenAIOfficial(Command):
|
||||
else:
|
||||
ps = l[1].strip()
|
||||
if ps in personalities:
|
||||
self.provider.now_personality = {
|
||||
self.provider.curr_personality = {
|
||||
'name': ps,
|
||||
'prompt': personalities[ps]
|
||||
}
|
||||
@@ -245,7 +245,7 @@ class CommandOpenAIOfficial(Command):
|
||||
self.personality_str = message
|
||||
return True, f"人格{ps}已设置。", "set"
|
||||
else:
|
||||
self.provider.now_personality = {
|
||||
self.provider.curr_personality = {
|
||||
'name': '自定义人格',
|
||||
'prompt': ps
|
||||
}
|
||||
|
||||
+11
-4
@@ -4,8 +4,11 @@ from util.cmd_config import CmdConfig
|
||||
import asyncio
|
||||
from nakuru import (
|
||||
CQHTTP,
|
||||
GuildMessage
|
||||
GuildMessage,
|
||||
GroupMessage,
|
||||
FriendMessage
|
||||
)
|
||||
from typing import Union
|
||||
import time
|
||||
|
||||
|
||||
@@ -155,18 +158,22 @@ class QQ:
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
def wait_for_message(self, group_id):
|
||||
def wait_for_message(self, group_id) -> Union[GroupMessage, FriendMessage, GuildMessage]:
|
||||
'''
|
||||
等待下一条消息
|
||||
等待下一条消息,超时 300s 后抛出异常
|
||||
'''
|
||||
self.waiting[group_id] = ''
|
||||
cnt = 0
|
||||
while True:
|
||||
if group_id in self.waiting and self.waiting[group_id] != '':
|
||||
# 去掉
|
||||
ret = self.waiting[group_id]
|
||||
del self.waiting[group_id]
|
||||
return ret
|
||||
time.sleep(0.5)
|
||||
cnt += 1
|
||||
if cnt > 300:
|
||||
raise Exception("等待消息超时。")
|
||||
time.sleep(1)
|
||||
|
||||
def get_client(self):
|
||||
return self.client
|
||||
|
||||
+31
-10
@@ -10,6 +10,7 @@ from util import general_utils as gu
|
||||
from nakuru.entities.components import Plain, At, Image
|
||||
from botpy.types.message import Reference
|
||||
from botpy import Client
|
||||
import time
|
||||
|
||||
class NakuruGuildMember():
|
||||
tiny_id: int # 发送者识别号
|
||||
@@ -38,6 +39,7 @@ class NakuruGuildMessage():
|
||||
class QQChan():
|
||||
def __init__(self, cnt: dict = None) -> None:
|
||||
self.qqchan_cnt = 0
|
||||
self.waiting: dict = {}
|
||||
|
||||
def get_cnt(self):
|
||||
return self.qqchan_cnt
|
||||
@@ -133,7 +135,7 @@ class QQChan():
|
||||
|
||||
try:
|
||||
# reply_res = asyncio.run_coroutine_threadsafe(message.raw_message.reply(content=str(plain_text), message_reference = msg_ref, file_image=image_path), self.client.loop)
|
||||
reply_res = asyncio.run_coroutine_threadsafe(self.client.api.post_message(channel_id=message.channel_id,
|
||||
reply_res = asyncio.run_coroutine_threadsafe(self.client.api.post_message(channel_id=str(message.channel_id),
|
||||
content=str(plain_text),
|
||||
msg_id=message.message_id,
|
||||
file_image=image_path,
|
||||
@@ -146,7 +148,7 @@ class QQChan():
|
||||
split_res.append(plain_text[:len(plain_text)//2])
|
||||
split_res.append(plain_text[len(plain_text)//2:])
|
||||
for i in split_res:
|
||||
reply_res = asyncio.run_coroutine_threadsafe(self.client.api.post_message(channel_id=message.channel_id,
|
||||
reply_res = asyncio.run_coroutine_threadsafe(self.client.api.post_message(channel_id=str(message.channel_id),
|
||||
content=str(i),
|
||||
msg_id=message.message_id,
|
||||
file_image=image_path,
|
||||
@@ -157,7 +159,7 @@ class QQChan():
|
||||
try:
|
||||
# 防止被qq频道过滤消息
|
||||
plain_text = plain_text.replace(".", " . ")
|
||||
reply_res = asyncio.run_coroutine_threadsafe(self.client.api.post_message(channel_id=message.channel_id,
|
||||
reply_res = asyncio.run_coroutine_threadsafe(self.client.api.post_message(channel_id=str(message.channel_id),
|
||||
content=str(plain_text),
|
||||
msg_id=message.message_id,
|
||||
file_image=image_path,
|
||||
@@ -166,7 +168,7 @@ class QQChan():
|
||||
print("QQ频道API错误: \n"+str(e))
|
||||
try:
|
||||
# reply_res = asyncio.run_coroutine_threadsafe(message.raw_message.reply(content=str(str.join(" ", plain_text)), message_reference = msg_ref, file_image=image_path), self.client.loop)
|
||||
reply_res = asyncio.run_coroutine_threadsafe(self.client.api.post_message(channel_id=message.channel_id,
|
||||
reply_res = asyncio.run_coroutine_threadsafe(self.client.api.post_message(channel_id=str(message.channel_id),
|
||||
content=str(str.join(" ", plain_text)),
|
||||
msg_id=message.message_id,
|
||||
file_image=image_path,
|
||||
@@ -174,23 +176,42 @@ class QQChan():
|
||||
except BaseException as e:
|
||||
plain_text = re.sub(r'(https|http)?:\/\/(\w|\.|\/|\?|\=|\&|\%)*\b', '[被隐藏的链接]', str(e), flags=re.MULTILINE)
|
||||
plain_text = plain_text.replace(".", "·")
|
||||
reply_res = asyncio.run_coroutine_threadsafe(self.client.api.post_message(channel_id=message.channel_id,
|
||||
reply_res = asyncio.run_coroutine_threadsafe(self.client.api.post_message(channel_id=str(message.channel_id),
|
||||
content=plain_text,
|
||||
msg_id=message.message_id,
|
||||
file_image=image_path,
|
||||
message_reference=msg_ref), self.client.loop).result()
|
||||
# send(message, f"QQ频道API错误:{str(e)}\n下面是格式化后的回答:\n{f_res}")
|
||||
|
||||
def push_message(self, channel_id: int, message_chain: list):
|
||||
def push_message(self, channel_id: int, message_chain: list, message_id: int = None):
|
||||
'''
|
||||
推送消息
|
||||
推送消息, 如果有 message_id,那么就是回复消息。
|
||||
'''
|
||||
_n = NakuruGuildMessage()
|
||||
_n.channel_id = channel_id
|
||||
_n.message_id = message_id
|
||||
self.send_qq_msg(_n, message_chain)
|
||||
|
||||
def send(self, message: NakuruGuildMessage, res: list):
|
||||
def send(self, message_obj, message_chain: list):
|
||||
'''
|
||||
同 send_qq_msg。回复频道消息
|
||||
发送信息
|
||||
'''
|
||||
self.send_qq_msg(message, res)
|
||||
self.send_qq_msg(message_obj, message_chain)
|
||||
|
||||
def wait_for_message(self, channel_id: int) -> NakuruGuildMessage:
|
||||
'''
|
||||
等待指定 channel_id 的下一条信息,超时 300s 后抛出异常
|
||||
'''
|
||||
self.waiting[channel_id] = ''
|
||||
cnt = 0
|
||||
while True:
|
||||
if channel_id in self.waiting and self.waiting[channel_id] != '':
|
||||
# 去掉
|
||||
ret = self.waiting[channel_id]
|
||||
del self.waiting[channel_id]
|
||||
return ret
|
||||
cnt += 1
|
||||
if cnt > 300:
|
||||
raise Exception("等待消息超时。")
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
+120
-44
@@ -2,8 +2,64 @@ import requests
|
||||
import asyncio
|
||||
import websockets
|
||||
from websockets import WebSocketClientProtocol
|
||||
import threading
|
||||
import json
|
||||
import inspect
|
||||
from typing import Callable, Awaitable, Union
|
||||
from pydantic import BaseModel
|
||||
import datetime
|
||||
|
||||
class Event(BaseModel):
|
||||
GroupMessage: str = "GuildMessage"
|
||||
|
||||
class Sender(BaseModel):
|
||||
user_id: str
|
||||
member_openid: str
|
||||
|
||||
|
||||
class MessageComponent(BaseModel):
|
||||
type: str
|
||||
|
||||
class PlainText(MessageComponent):
|
||||
text: str
|
||||
|
||||
class Image(MessageComponent):
|
||||
path: str
|
||||
file: str
|
||||
url: str
|
||||
|
||||
class MessageChain(list):
|
||||
|
||||
def append(self, __object: MessageComponent) -> None:
|
||||
if not isinstance(__object, MessageComponent):
|
||||
raise TypeError("不受支持的消息链元素类型。回复的消息链必须是 MessageComponent 的子类。")
|
||||
return super().append(__object)
|
||||
|
||||
def insert(self, __index: int, __object: MessageComponent) -> None:
|
||||
if not isinstance(__object, MessageComponent):
|
||||
raise TypeError("不受支持的消息链元素类型。回复的消息链必须是 MessageComponent 的子类。")
|
||||
return super().insert(__index, __object)
|
||||
|
||||
def parse_from_nakuru(self, nakuru_message_chain: Union[list, str]) -> None:
|
||||
if isinstance(nakuru_message_chain, str):
|
||||
self.append(PlainText(type='Plain', text=nakuru_message_chain))
|
||||
else:
|
||||
for i in nakuru_message_chain:
|
||||
if i['type'] == 'Plain':
|
||||
self.append(PlainText(type='Plain', text=i['text']))
|
||||
elif i['type'] == 'Image':
|
||||
self.append(Image(path=i['path'], file=i['file'], url=i['url']))
|
||||
|
||||
class Message(BaseModel):
|
||||
type: str
|
||||
user_id: str
|
||||
member_openid: str
|
||||
message_id: str
|
||||
group_id: str
|
||||
group_openid: str
|
||||
content: str
|
||||
message: MessageChain
|
||||
time: int
|
||||
sender: Sender
|
||||
|
||||
class UnofficialQQBotSDK:
|
||||
|
||||
@@ -13,43 +69,42 @@ class UnofficialQQBotSDK:
|
||||
def __init__(self, appid: str, client_secret: str) -> None:
|
||||
self.appid = appid
|
||||
self.client_secret = client_secret
|
||||
self.get_access_token()
|
||||
self.get_wss_endpoint()
|
||||
asyncio.get_event_loop().run_until_complete(self.ws_client())
|
||||
self.events: dict[str, Awaitable] = {}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def get_access_token(self) -> None:
|
||||
def run_bot(self) -> None:
|
||||
self.__get_access_token()
|
||||
self.__get_wss_endpoint()
|
||||
asyncio.get_event_loop().run_until_complete(self.__ws_client())
|
||||
|
||||
def __get_access_token(self) -> None:
|
||||
res = requests.post(self.GET_APP_ACCESS_TOKEN_URL, json={
|
||||
"appId": self.appid,
|
||||
"clientSecret": self.client_secret
|
||||
}, headers={
|
||||
"Content-Type": "application/json"
|
||||
})
|
||||
print(res.text)
|
||||
self.access_token = 'QQBot ' + res.json()['access_token']
|
||||
print("access_token: " + self.access_token)
|
||||
res = res.json()
|
||||
code = res['code'] if 'code' in res else 1
|
||||
if 'access_token' not in res:
|
||||
raise Exception(f"获取 access_token 失败。原因:{res['message'] if 'message' in res else '未知'}")
|
||||
self.access_token = 'QQBot ' + res['access_token']
|
||||
|
||||
def auth_header(self) -> str:
|
||||
def __auth_header(self) -> str:
|
||||
return {
|
||||
'Authorization': self.access_token,
|
||||
'X-Union-Appid': self.appid,
|
||||
}
|
||||
|
||||
def get_wss_endpoint(self):
|
||||
# self.wss_endpoint = requests.get(self.OPENAPI_BASE_URL + "/gateway", headers=self.auth_header()).json()['url']
|
||||
res = requests.get(self.OPENAPI_BASE_URL + "/gateway", headers=self.auth_header())
|
||||
print(res.text)
|
||||
def __get_wss_endpoint(self):
|
||||
res = requests.get(self.OPENAPI_BASE_URL + "/gateway", headers=self.__auth_header())
|
||||
self.wss_endpoint = res.json()['url']
|
||||
print("wss_endpoint: " + self.wss_endpoint)
|
||||
# print("wss_endpoint: " + self.wss_endpoint)
|
||||
|
||||
async def behav_heartbeat(self, ws: WebSocketClientProtocol, t: int):
|
||||
async def __behav_heartbeat(self, ws: WebSocketClientProtocol, t: int):
|
||||
while True:
|
||||
await asyncio.sleep(t - 1)
|
||||
try:
|
||||
print("heartbeat., s: " + str(self.s))
|
||||
await ws.send(json.dumps({
|
||||
"op": 1,
|
||||
"d": self.s
|
||||
@@ -57,12 +112,9 @@ class UnofficialQQBotSDK:
|
||||
except:
|
||||
print("heartbeat error.")
|
||||
|
||||
async def handle_msg(self, ws: WebSocketClientProtocol, msg: dict):
|
||||
async def __handle_msg(self, ws: WebSocketClientProtocol, msg: dict):
|
||||
if msg['op'] == 10:
|
||||
# hello
|
||||
# 创建心跳任务
|
||||
print("hello.")
|
||||
asyncio.get_event_loop().create_task(self.behav_heartbeat(ws, msg['d']['heartbeat_interval'] / 1000))
|
||||
asyncio.get_event_loop().create_task(self.__behav_heartbeat(ws, msg['d']['heartbeat_interval'] / 1000))
|
||||
# 鉴权,获得session
|
||||
await ws.send(json.dumps({
|
||||
"op": 2,
|
||||
@@ -77,36 +129,60 @@ class UnofficialQQBotSDK:
|
||||
}
|
||||
}
|
||||
}))
|
||||
|
||||
if msg['op'] == 0:
|
||||
# ready
|
||||
print("ready.")
|
||||
data = msg['d']
|
||||
print(data)
|
||||
event_typ: str = msg['t'] if 't' in msg else None
|
||||
if event_typ == 'GROUP_AT_MESSAGE_CREATE':
|
||||
if 'GroupMessage' in self.events:
|
||||
coro = self.events['GroupMessage']
|
||||
else:
|
||||
return
|
||||
message_chain = MessageChain()
|
||||
message_chain.append(PlainText(type="Plain", text=data['content']))
|
||||
group_message = Message(
|
||||
type='GroupMessage',
|
||||
user_id=data['author']['id'],
|
||||
member_openid=data['author']['member_openid'],
|
||||
message_id=data['id'],
|
||||
group_id=data['group_id'],
|
||||
group_openid=data['group_openid'],
|
||||
content=data['content'],
|
||||
# 2023-11-24T19:51:11+08:00
|
||||
time=int(datetime.datetime.strptime(data['timestamp'], "%Y-%m-%dT%H:%M:%S%z").timestamp()),
|
||||
sender=Sender(
|
||||
user_id=data['author']['id'],
|
||||
member_openid=data['author']['member_openid']
|
||||
),
|
||||
message=message_chain
|
||||
)
|
||||
await coro(self, group_message)
|
||||
|
||||
if 'group_openid' in data:
|
||||
group_openid = data['group_openid']
|
||||
message_str = data['content'].strip()
|
||||
message_id = data['id']
|
||||
# 发送消息
|
||||
requests.post(self.OPENAPI_BASE_URL + f"/v2/groups/{group_openid}/messages", headers=self.auth_header(), json={
|
||||
"content": message_str,
|
||||
"message_type": 0,
|
||||
"msg_id": message_id
|
||||
})
|
||||
async def send(self, message: Message, message_chain: MessageChain) -> None:
|
||||
# todo: 消息链转换支持更多类型。
|
||||
plain_text = ""
|
||||
for i in message_chain:
|
||||
if isinstance(i, PlainText):
|
||||
plain_text += i.text
|
||||
requests.post(self.OPENAPI_BASE_URL + f"/v2/groups/{message.group_openid}/messages", headers=self.__auth_header(), json={
|
||||
"content": plain_text,
|
||||
"message_type": 0,
|
||||
"msg_id": message.message_id
|
||||
})
|
||||
|
||||
async def ws_client(self):
|
||||
async def __ws_client(self):
|
||||
self.s = 0
|
||||
async with websockets.connect(self.wss_endpoint) as websocket:
|
||||
print("ws connected.")
|
||||
while True:
|
||||
msg = await websocket.recv()
|
||||
msg = json.loads(msg)
|
||||
if 's' in msg:
|
||||
self.s = msg['s']
|
||||
print("recv: " + str(msg))
|
||||
await self.handle_msg(websocket, msg)
|
||||
|
||||
await self.__handle_msg(websocket, msg)
|
||||
|
||||
if __name__ == "__main__":
|
||||
UnofficialQQBotSDK("", "")
|
||||
def on(self, event: str) -> None:
|
||||
def wrapper(func: Awaitable):
|
||||
if inspect.iscoroutinefunction(func) == False:
|
||||
raise TypeError("func must be a coroutine function")
|
||||
self.events[event] = func
|
||||
return wrapper
|
||||
@@ -35,7 +35,7 @@ class ProviderOpenAIOfficial(Provider):
|
||||
self.api_base = None
|
||||
if 'api_base' in cfg and cfg['api_base'] != 'none' and cfg['api_base'] != '':
|
||||
self.api_base = cfg['api_base']
|
||||
print(f"设置 api_base 为: {self.api_base}")
|
||||
gu.log(f"设置 api_base 为: {self.api_base}")
|
||||
# openai client
|
||||
self.client = OpenAI(
|
||||
api_key=self.key_list[0],
|
||||
@@ -83,7 +83,7 @@ class ProviderOpenAIOfficial(Provider):
|
||||
threading.Thread(target=self.dump_history, daemon=True).start()
|
||||
|
||||
# 人格
|
||||
self.now_personality = {}
|
||||
self.curr_personality = {}
|
||||
|
||||
|
||||
# 转储历史记录
|
||||
@@ -109,11 +109,30 @@ class ProviderOpenAIOfficial(Provider):
|
||||
# 每隔10分钟转储一次
|
||||
time.sleep(10*self.history_dump_interval)
|
||||
|
||||
def personality_set(self, default_personality: dict, session_id: str):
|
||||
self.curr_personality = default_personality
|
||||
new_record = {
|
||||
"user": {
|
||||
"role": "user",
|
||||
"content": default_personality['prompt'],
|
||||
},
|
||||
"AI": {
|
||||
"role": "assistant",
|
||||
"content": "好的,接下来我会扮演这个角色。"
|
||||
},
|
||||
'type': "personality",
|
||||
'usage_tokens': 0,
|
||||
'single-tokens': 0
|
||||
}
|
||||
self.session_dict[session_id].append(new_record)
|
||||
|
||||
|
||||
def text_chat(self, prompt,
|
||||
session_id = None,
|
||||
image_url = None,
|
||||
function_call=None,
|
||||
extra_conf: dict = None):
|
||||
extra_conf: dict = None,
|
||||
default_personality: dict = None):
|
||||
if session_id is None:
|
||||
session_id = "unknown"
|
||||
if "unknown" in self.session_dict:
|
||||
@@ -136,6 +155,12 @@ class ProviderOpenAIOfficial(Provider):
|
||||
f.flush()
|
||||
f.close()
|
||||
|
||||
if len(self.session_dict[session_id]) == 0:
|
||||
# 设置默认人格
|
||||
if default_personality is not None:
|
||||
self.personality_set(default_personality, session_id)
|
||||
|
||||
|
||||
# 使用 tictoken 截断消息
|
||||
_encoded_prompt = self.enc.encode(prompt)
|
||||
if self.openai_model_configs['max_tokens'] < len(_encoded_prompt):
|
||||
|
||||
@@ -5,7 +5,7 @@ class Provider:
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def text_chat(self, prompt, session_id, image_url: None, function_call: None):
|
||||
def text_chat(self, prompt, session_id, image_url: None, function_call: None, extra_conf: dict = None, default_personality: dict = None) -> str:
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
|
||||
@@ -101,7 +101,12 @@ class ProviderRevChatGPT(Provider):
|
||||
# print("[RevChatGPT] "+str(resp))
|
||||
return resp
|
||||
|
||||
def text_chat(self, prompt, session_id = None, image_url = None, function_call=None) -> str:
|
||||
def text_chat(self, prompt,
|
||||
session_id = None,
|
||||
image_url = None,
|
||||
function_call=None,
|
||||
extra_conf: dict = None,
|
||||
default_personality: dict = None) -> str:
|
||||
|
||||
# 选择一个人少的账号。
|
||||
selected_revstat = None
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
from model.provider.provider import Provider
|
||||
# from EdgeGPT import Chatbot, ConversationStyle
|
||||
from EdgeGPT import Chatbot, ConversationStyle
|
||||
import json
|
||||
import os
|
||||
from util import general_utils as gu
|
||||
from util.cmd_config import CmdConfig as cc
|
||||
import time
|
||||
from EdgeGPT.EdgeUtils import Query, Cookie
|
||||
from EdgeGPT.EdgeGPT import Chatbot as EdgeChatbot, ConversationStyle, NotAllowedToAccess
|
||||
|
||||
class ProviderRevEdgeGPT(Provider):
|
||||
def __init__(self):
|
||||
raise Exception("Bing 逆向已停止维护,不可用,请使用 ChatGPT 官方 API。")
|
||||
|
||||
self.busy = False
|
||||
self.wait_stack = []
|
||||
with open('./cookies.json', 'r') as f:
|
||||
|
||||
+27
-16
@@ -1,12 +1,13 @@
|
||||
import os
|
||||
import json
|
||||
from typing import Union
|
||||
|
||||
cpath = "cmd_config.json"
|
||||
|
||||
def check_exist():
|
||||
if not os.path.exists(cpath):
|
||||
with open(cpath, "w", encoding="utf-8") as f:
|
||||
json.dump({}, f, indent=4)
|
||||
with open(cpath, "w", encoding="utf-8-sig") as f:
|
||||
json.dump({}, f, indent=4, ensure_ascii=False)
|
||||
f.flush()
|
||||
|
||||
class CmdConfig():
|
||||
@@ -14,7 +15,7 @@ class CmdConfig():
|
||||
@staticmethod
|
||||
def get(key, default=None):
|
||||
check_exist()
|
||||
with open(cpath, "r", encoding="utf-8") as f:
|
||||
with open(cpath, "r", encoding="utf-8-sig") as f:
|
||||
d = json.load(f)
|
||||
if key in d:
|
||||
return d[key]
|
||||
@@ -24,30 +25,40 @@ class CmdConfig():
|
||||
@staticmethod
|
||||
def get_all():
|
||||
check_exist()
|
||||
with open(cpath, "r", encoding="utf-8") as f:
|
||||
with open(cpath, "r", encoding="utf-8-sig") as f:
|
||||
return json.load(f)
|
||||
|
||||
@staticmethod
|
||||
def put(key, value):
|
||||
check_exist()
|
||||
with open(cpath, "r", encoding="utf-8") as f:
|
||||
with open(cpath, "r", encoding="utf-8-sig") as f:
|
||||
d = json.load(f)
|
||||
d[key] = value
|
||||
with open(cpath, "w", encoding="utf-8") as f:
|
||||
json.dump(d, f, indent=4)
|
||||
with open(cpath, "w", encoding="utf-8-sig") as f:
|
||||
json.dump(d, f, indent=4, ensure_ascii=False)
|
||||
f.flush()
|
||||
|
||||
@staticmethod
|
||||
def init_attributes(keys: list, init_val = ""):
|
||||
def init_attributes(key: Union[str, list], init_val = ""):
|
||||
check_exist()
|
||||
with open(cpath, "r", encoding="utf-8") as f:
|
||||
d = json.load(f)
|
||||
_tag = False
|
||||
for k in keys:
|
||||
conf_str = ''
|
||||
with open(cpath, "r", encoding="utf-8-sig") as f:
|
||||
conf_str = f.read()
|
||||
if conf_str.startswith(u'/ufeff'):
|
||||
conf_str = conf_str.encode('utf8')[3:].decode('utf8')
|
||||
d = json.loads(conf_str)
|
||||
_tag = False
|
||||
|
||||
if isinstance(key, str):
|
||||
if key not in d:
|
||||
d[key] = init_val
|
||||
_tag = True
|
||||
elif isinstance(key, list):
|
||||
for k in key:
|
||||
if k not in d:
|
||||
d[k] = init_val
|
||||
_tag = True
|
||||
if _tag:
|
||||
with open(cpath, "w", encoding="utf-8") as f:
|
||||
json.dump(d, f, indent=4)
|
||||
f.flush()
|
||||
if _tag:
|
||||
with open(cpath, "w", encoding="utf-8-sig") as f:
|
||||
json.dump(d, f, indent=4, ensure_ascii=False)
|
||||
f.flush()
|
||||
+1
-1
@@ -259,7 +259,7 @@ def web_search(question, provider: Provider, session_id, official_fc=False):
|
||||
|
||||
if has_func:
|
||||
provider.forget(session_id)
|
||||
question3 = f"""请你用活泼的语气回答`{question}`问题。\n以下是相关材料,请直接拿此材料针对问题进行总结回答。在引文末加上参考链接的标号,如` [1]`;在文章末尾加上各参考链接,如`[1] <标题> <网址>`;不要提到任何函数调用的信息;在总结的末尾加上 1-2 个相关的emoji。```\n{function_invoked_ret}\n```\n"""
|
||||
question3 = f"""请你用活泼的语气回答`{question}`问题。\n以下是相关材料,请直接拿此材料针对问题进行总结回答。在文章末尾加上各参考链接,如`[1] <title> <url>`;不要提到任何函数调用的信息;在总结的末尾加上1或2个相关的emoji。```\n{function_invoked_ret}\n```\n"""
|
||||
gu.log(f"web_search: {question3}", tag="web_search", level=gu.LEVEL_DEBUG, max_len=99999)
|
||||
_c = 0
|
||||
while _c < 3:
|
||||
|
||||
Reference in New Issue
Block a user