feat: 针对 OneBot 和 NoneBot 的消息兼容层和插件的初步适配
This commit is contained in:
+56
-57
@@ -207,6 +207,61 @@ 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'])
|
||||
|
||||
@@ -312,8 +367,6 @@ def initBot(cfg, prov):
|
||||
nick_qq = tuple(nick_qq)
|
||||
_global_object.nick = nick_qq
|
||||
|
||||
thread_inst = None
|
||||
|
||||
gu.log("--------加载插件--------", gu.LEVEL_INFO, fg=gu.FG_COLORS['yellow'])
|
||||
# 加载插件
|
||||
_command = Command(None, _global_object)
|
||||
@@ -327,59 +380,6 @@ 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
|
||||
|
||||
# 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()
|
||||
|
||||
if thread_inst == None:
|
||||
input("[System-Error] 没有启用/成功启用任何机器人,程序退出")
|
||||
@@ -606,7 +606,7 @@ async def oper_msg(message: Union[GroupMessage, FriendMessage, GuildMessage, Nak
|
||||
if session_id in gocq_bot.waiting and gocq_bot.waiting[session_id] == '':
|
||||
gocq_bot.waiting[session_id] = qq_msg
|
||||
return
|
||||
hit, command_result = llm_command_instance[chosen_provider].check_command(
|
||||
hit, command_result = await llm_command_instance[chosen_provider].check_command(
|
||||
qq_msg,
|
||||
session_id,
|
||||
role,
|
||||
@@ -752,7 +752,6 @@ class botClient(botpy.Client):
|
||||
# 收到频道消息
|
||||
async def on_at_message_create(self, message: Message):
|
||||
gu.log(str(message), gu.LEVEL_DEBUG, max_len=9999)
|
||||
|
||||
# 转换层
|
||||
nakuru_guild_message = qqchannel_bot.gocq_compatible_receive(message)
|
||||
gu.log(f"转换后: {str(nakuru_guild_message)}", gu.LEVEL_DEBUG, max_len=9999)
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
class CommandArg:
|
||||
pass
|
||||
@@ -0,0 +1,32 @@
|
||||
import sys
|
||||
from types import ModuleType
|
||||
import asyncio
|
||||
from pyppeteer import launch
|
||||
|
||||
|
||||
async def template_to_pic(template_path, template_name, templates, pages, wait, type, quality, device_scale_factor):
|
||||
browser = await launch()
|
||||
page = await browser.newPage()
|
||||
await page.setViewport(pages["viewport"])
|
||||
await page.goto(pages["base_url"])
|
||||
await asyncio.sleep(wait)
|
||||
await page.evaluate('''(templates) => {
|
||||
// 在页面中执行 JavaScript 代码,将数据注入到模板中
|
||||
// 这里的示例代码仅供参考,具体需要根据实际情况修改
|
||||
document.getElementById('css').innerText = templates.css;
|
||||
document.getElementById('data').innerText = JSON.stringify(templates.data);
|
||||
document.getElementById('detail').innerText = templates.detail;
|
||||
}''', templates)
|
||||
screenshot = await page.screenshot({
|
||||
'type': type,
|
||||
'quality': quality,
|
||||
'deviceScaleFactor': device_scale_factor
|
||||
})
|
||||
await browser.close()
|
||||
return screenshot
|
||||
|
||||
def require(module_str: str):
|
||||
module = ModuleType(module_str)
|
||||
sys.modules[module_str] = module
|
||||
if module_str == 'nonebot_plugin_htmlrender':
|
||||
module.template_to_pic = template_to_pic
|
||||
@@ -0,0 +1,11 @@
|
||||
class Driver:
|
||||
def __init__(self) -> None:
|
||||
self.config = {}
|
||||
|
||||
def on_startup(self, func):
|
||||
pass
|
||||
def on_bot_connect(self, func):
|
||||
pass
|
||||
|
||||
def get_driver():
|
||||
return Driver()
|
||||
@@ -0,0 +1,2 @@
|
||||
class Bot:
|
||||
pass
|
||||
@@ -0,0 +1,2 @@
|
||||
class Message:
|
||||
pass
|
||||
@@ -0,0 +1,2 @@
|
||||
class MessageEvent:
|
||||
pass
|
||||
@@ -0,0 +1,2 @@
|
||||
class MessageSegment:
|
||||
pass
|
||||
@@ -0,0 +1,137 @@
|
||||
import sys
|
||||
from types import ModuleType
|
||||
import asyncio
|
||||
from pyppeteer import launch
|
||||
|
||||
from model.platform.qqchan import QQChan
|
||||
|
||||
from .nonebot.driver import Driver, get_driver
|
||||
from .onebot.message import Message
|
||||
from .onebot.message_event import MessageEvent
|
||||
from .onebot.message_segment import MessageSegment
|
||||
from .nonebot.command_arg import CommandArg
|
||||
from .onebot.bot import Bot
|
||||
|
||||
from nakuru import (
|
||||
GuildMessage,
|
||||
GroupMessage,
|
||||
FriendMessage
|
||||
)
|
||||
|
||||
from typing import Union
|
||||
|
||||
NONEBOT = "nonebot"
|
||||
|
||||
class UnifiedBotCompatibleLayer():
|
||||
def __init__(self, platform_qq_sdk: QQChan) -> None:
|
||||
# 初始化兼容层
|
||||
self.plugins: dict[str, CommandOper] = {}
|
||||
self.platform_qq_sdk = platform_qq_sdk
|
||||
self._nonebot()
|
||||
self.load_plugins()
|
||||
|
||||
async def check_commands(self, message: str, message_obj: Union[GroupMessage, FriendMessage, GuildMessage]):
|
||||
for k in self.plugins:
|
||||
if message.startswith(k):
|
||||
if self.plugins[k].framework_name == NONEBOT:
|
||||
await self._nonebot_plugins_oper(message, message_obj, k)
|
||||
|
||||
async def _nonebot_plugins_oper(self, message: str, message_obj: Union[GroupMessage, FriendMessage, GuildMessage], plugin_name: str = None):
|
||||
# bad implementation
|
||||
# 高并发场景下,下面的代码是不安全的
|
||||
while self.plugins[plugin_name].message_obj is not None:
|
||||
await asyncio.sleep(1)
|
||||
self.plugins[plugin_name].message_obj = message_obj
|
||||
bot, event, arg = self._nonebot_adapter(message_obj)
|
||||
await self.plugins[plugin_name].exec(bot, event, arg) # wrapper
|
||||
|
||||
def load_plugins(self):
|
||||
import nonebot_plugin_gspanel.nonebot_plugin_gspanel
|
||||
|
||||
def _nonebot(self):
|
||||
# 模拟 nonebot 模块
|
||||
nonebot_module = ModuleType('nonebot')
|
||||
sys.modules['nonebot'] = nonebot_module
|
||||
|
||||
nonebot_log_module = ModuleType('nonebot.log')
|
||||
sys.modules['nonebot.log'] = nonebot_log_module
|
||||
|
||||
nonebot_adapter_module = ModuleType('nonebot.adapters')
|
||||
sys.modules['nonebot.adapters'] = nonebot_adapter_module
|
||||
|
||||
nonebot_params_module = ModuleType('nonebot.params')
|
||||
sys.modules['nonebot.params'] = nonebot_params_module
|
||||
|
||||
nonebot_drivers_module = ModuleType('nonebot.drivers')
|
||||
sys.modules['nonebot.drivers'] = nonebot_drivers_module
|
||||
|
||||
nonebot_plugin_module = ModuleType('nonebot.plugin')
|
||||
sys.modules['nonebot.plugin'] = nonebot_plugin_module
|
||||
|
||||
nonebot_adapter_onebot_v11_module = ModuleType('nonebot.adapters.onebot.v11')
|
||||
sys.modules['nonebot.adapters.onebot.v11'] = nonebot_adapter_onebot_v11_module
|
||||
|
||||
nonebot_adapter_onebot_v11_event_module = ModuleType('nonebot.adapters.onebot.v11.event')
|
||||
sys.modules['nonebot.adapters.onebot.v11.event'] = nonebot_adapter_onebot_v11_event_module
|
||||
|
||||
nonebot_adapter_onebot_v11_message_module = ModuleType('nonebot.adapters.onebot.v11.message')
|
||||
sys.modules['nonebot.adapters.onebot.v11.message'] = nonebot_adapter_onebot_v11_message_module
|
||||
|
||||
nonebot_log_module.logger = lambda: None
|
||||
nonebot_adapter_module.Message = Message
|
||||
nonebot_params_module.CommandArg = CommandArg
|
||||
on_command = wrap_on_command(self)
|
||||
nonebot_plugin_module.on_command = on_command
|
||||
nonebot_adapter_onebot_v11_module.Bot = Bot
|
||||
nonebot_adapter_onebot_v11_event_module.MessageEvent = MessageEvent
|
||||
nonebot_adapter_onebot_v11_message_module.MessageSegment = MessageSegment
|
||||
nonebot_module.get_driver = get_driver
|
||||
nonebot_module.require = require
|
||||
nonebot_drivers_module.Driver = Driver
|
||||
|
||||
def _nonebot_adapter(self, message_obj):
|
||||
bot = Bot()
|
||||
event = MessageEvent()
|
||||
arg = CommandArg()
|
||||
# tododssss
|
||||
return bot, event, arg
|
||||
|
||||
|
||||
class BaseBot():
|
||||
def __init__(self, framework_name) -> None:
|
||||
self.framework_name = framework_name
|
||||
|
||||
class CommandOper(BaseBot):
|
||||
'''
|
||||
CommandOper for NoneBot
|
||||
'''
|
||||
def __init__(self, name, aliases=None, priority=1, block=False, _ubcl: UnifiedBotCompatibleLayer = None) -> None:
|
||||
super().__init__("nonebot")
|
||||
self.name = name
|
||||
self.aliases = aliases
|
||||
self.priority = priority
|
||||
self.block = block
|
||||
self.exec = None
|
||||
self._ubcl = _ubcl
|
||||
self.message_obj: Union[GroupMessage, FriendMessage, GuildMessage] = None
|
||||
_ubcl.plugins[name] = self
|
||||
|
||||
def handle(self):
|
||||
def decorator(func):
|
||||
async def wrapper(bot: Bot, event: MessageEvent, arg: Message = CommandArg(), *args, **kwargs):
|
||||
# 你可以在这里添加自定义的处理逻辑
|
||||
print(f"Command {self.name} is executed.")
|
||||
await func(bot, event, arg, *args, **kwargs)
|
||||
self.exec = wrapper
|
||||
return wrapper
|
||||
return decorator
|
||||
|
||||
async def finish(self, msg, at_sender = True):
|
||||
if self.message_obj is not None:
|
||||
self._ubcl.platform_qq_sdk.send(self.message_obj, msg)
|
||||
self.message_obj = None
|
||||
|
||||
def wrap_on_command(_ubcl: UnifiedBotCompatibleLayer):
|
||||
def on_command(name, aliases=None, priority=1, block=False):
|
||||
return CommandOper(name, aliases, priority, block, _ubcl = _ubcl)
|
||||
return on_command
|
||||
@@ -26,21 +26,29 @@ from PIL import Image as PILImage
|
||||
from cores.qqbot.global_object import GlobalObject, AstrMessageEvent
|
||||
from pip._internal import main as pipmain
|
||||
|
||||
from .adapter.protocol_adapter import UnifiedBotCompatibleLayer
|
||||
import asyncio
|
||||
|
||||
PLATFORM_QQCHAN = 'qqchan'
|
||||
PLATFORM_GOCQ = 'gocq'
|
||||
|
||||
# 指令功能的基类,通用的(不区分语言模型)的指令就在这实现
|
||||
class Command:
|
||||
def __init__(self, provider: Provider, global_object: GlobalObject = None):
|
||||
def __init__(self, provider: Provider, global_object: GlobalObject = None, unified_bot_compatible_layer: UnifiedBotCompatibleLayer = None):
|
||||
self.provider = provider
|
||||
self.global_object = global_object
|
||||
self.unified_bot_compatible_layer = unified_bot_compatible_layer
|
||||
|
||||
def check_command(self,
|
||||
|
||||
async def check_command(self,
|
||||
message,
|
||||
session_id: str,
|
||||
role,
|
||||
platform,
|
||||
message_obj):
|
||||
# UBCL
|
||||
await self.unified_bot_compatible_layer.check_commands(message, message_obj)
|
||||
|
||||
# 插件
|
||||
cached_plugins = self.global_object.cached_plugins
|
||||
ame = AstrMessageEvent(
|
||||
@@ -70,10 +78,8 @@ class Command:
|
||||
|
||||
if self.command_start_with(message, "nick"):
|
||||
return True, self.set_nick(message, platform, role)
|
||||
|
||||
if self.command_start_with(message, "plugin"):
|
||||
return True, self.plugin_oper(message, role, cached_plugins, platform)
|
||||
|
||||
if self.command_start_with(message, "myid") or self.command_start_with(message, "!myid"):
|
||||
return True, self.get_my_id(message_obj)
|
||||
if self.command_start_with(message, "nconf") or self.command_start_with(message, "newconf"):
|
||||
|
||||
@@ -5,6 +5,7 @@ from cores.qqbot.personality import personalities
|
||||
from model.platform.qq import QQ
|
||||
from util import general_utils as gu
|
||||
from cores.qqbot.global_object import GlobalObject
|
||||
from .adapter.protocol_adapter import UnifiedBotCompatibleLayer
|
||||
|
||||
class CommandOpenAIOfficial(Command):
|
||||
def __init__(self, provider: ProviderOpenAIOfficial, global_object: GlobalObject):
|
||||
@@ -12,16 +13,17 @@ class CommandOpenAIOfficial(Command):
|
||||
self.cached_plugins = {}
|
||||
self.global_object = global_object
|
||||
self.personality_str = ""
|
||||
super().__init__(provider, global_object)
|
||||
self.unified_bot_compatible_layer = UnifiedBotCompatibleLayer(self.global_object.platform_qqchan)
|
||||
super().__init__(provider, global_object, self.unified_bot_compatible_layer)
|
||||
|
||||
def check_command(self,
|
||||
async def check_command(self,
|
||||
message: str,
|
||||
session_id: str,
|
||||
role: str,
|
||||
platform: str,
|
||||
message_obj):
|
||||
self.platform = platform
|
||||
hit, res = super().check_command(
|
||||
hit, res = await super().check_command(
|
||||
message,
|
||||
session_id,
|
||||
role,
|
||||
|
||||
@@ -12,14 +12,14 @@ class CommandRevChatGPT(Command):
|
||||
self.personality_str = ""
|
||||
super().__init__(provider, global_object)
|
||||
|
||||
def check_command(self,
|
||||
async def check_command(self,
|
||||
message: str,
|
||||
session_id: str,
|
||||
role: str,
|
||||
platform: str,
|
||||
message_obj):
|
||||
self.platform = platform
|
||||
hit, res = super().check_command(
|
||||
hit, res = await super().check_command(
|
||||
message,
|
||||
session_id,
|
||||
role,
|
||||
|
||||
@@ -188,4 +188,9 @@ class QQChan():
|
||||
_n = NakuruGuildMessage()
|
||||
_n.channel_id = channel_id
|
||||
self.send_qq_msg(_n, message_chain)
|
||||
|
||||
|
||||
def send(self, message: NakuruGuildMessage, res: list):
|
||||
'''
|
||||
同 send_qq_msg。回复频道消息
|
||||
'''
|
||||
self.send_qq_msg(message, res)
|
||||
Reference in New Issue
Block a user