Merge pull request #881 from RockChinQ/version/3.3.1.0

Version/3.3.1.0
This commit is contained in:
Junyan Qin 2024-09-08 15:13:39 +08:00 committed by GitHub
commit bcf1d92f73
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 204 additions and 16 deletions

59
pkg/config/impls/yaml.py Normal file
View File

@ -0,0 +1,59 @@
import os
import shutil
import yaml
from .. import model as file_model
class YAMLConfigFile(file_model.ConfigFile):
"""YAML配置文件"""
def __init__(
self, config_file_name: str, template_file_name: str = None, template_data: dict = None
) -> None:
self.config_file_name = config_file_name
self.template_file_name = template_file_name
self.template_data = template_data
def exists(self) -> bool:
return os.path.exists(self.config_file_name)
async def create(self):
if self.template_file_name is not None:
shutil.copyfile(self.template_file_name, self.config_file_name)
elif self.template_data is not None:
with open(self.config_file_name, "w", encoding="utf-8") as f:
yaml.dump(self.template_data, f, indent=4, allow_unicode=True)
else:
raise ValueError("template_file_name or template_data must be provided")
async def load(self, completion: bool=True) -> dict:
if not self.exists():
await self.create()
if self.template_file_name is not None:
with open(self.template_file_name, "r", encoding="utf-8") as f:
self.template_data = yaml.load(f, Loader=yaml.FullLoader)
with open(self.config_file_name, "r", encoding="utf-8") as f:
try:
cfg = yaml.load(f, Loader=yaml.FullLoader)
except yaml.YAMLError as e:
raise Exception(f"配置文件 {self.config_file_name} 语法错误: {e}")
if completion:
for key in self.template_data:
if key not in cfg:
cfg[key] = self.template_data[key]
return cfg
async def save(self, cfg: dict):
with open(self.config_file_name, "w", encoding="utf-8") as f:
yaml.dump(cfg, f, indent=4, allow_unicode=True)
def save_sync(self, cfg: dict):
with open(self.config_file_name, "w", encoding="utf-8") as f:
yaml.dump(cfg, f, indent=4, allow_unicode=True)

View File

@ -1,7 +1,7 @@
from __future__ import annotations
from . import model as file_model
from .impls import pymodule, json as json_file
from .impls import pymodule, json as json_file, yaml as yaml_file
managers: ConfigManager = []
@ -31,7 +31,16 @@ class ConfigManager:
async def load_python_module_config(config_name: str, template_name: str, completion: bool=True) -> ConfigManager:
"""加载Python模块配置文件"""
"""加载Python模块配置文件
Args:
config_name (str): 配置文件名
template_name (str): 模板文件名
completion (bool): 是否自动补全内存中的配置文件
Returns:
ConfigManager: 配置文件管理器
"""
cfg_inst = pymodule.PythonModuleConfigFile(
config_name,
template_name
@ -44,7 +53,14 @@ async def load_python_module_config(config_name: str, template_name: str, comple
async def load_json_config(config_name: str, template_name: str=None, template_data: dict=None, completion: bool=True) -> ConfigManager:
"""加载JSON配置文件"""
"""加载JSON配置文件
Args:
config_name (str): 配置文件名
template_name (str): 模板文件名
template_data (dict): 模板数据
completion (bool): 是否自动补全内存中的配置文件
"""
cfg_inst = json_file.JSONConfigFile(
config_name,
template_name,
@ -55,3 +71,27 @@ async def load_json_config(config_name: str, template_name: str=None, template_d
await cfg_mgr.load_config(completion=completion)
return cfg_mgr
async def load_yaml_config(config_name: str, template_name: str=None, template_data: dict=None, completion: bool=True) -> ConfigManager:
"""加载YAML配置文件
Args:
config_name (str): 配置文件名
template_name (str): 模板文件名
template_data (dict): 模板数据
completion (bool): 是否自动补全内存中的配置文件
Returns:
ConfigManager: 配置文件管理器
"""
cfg_inst = yaml_file.YAMLConfigFile(
config_name,
template_name,
template_data
)
cfg_mgr = ConfigManager(cfg_inst)
await cfg_mgr.load_config(completion=completion)
return cfg_mgr

View File

@ -3,16 +3,15 @@ from __future__ import annotations
import logging
import typing
import datetime
import asyncio
import re
import traceback
import json
import threading
import mirai
import botpy
import botpy.message as botpy_message
import botpy.types.message as botpy_message_type
import pydantic
import pydantic.networks
from .. import adapter as adapter_model
from ...pipeline.longtext.strategies import forward
@ -23,10 +22,12 @@ from ...config import manager as cfg_mgr
class OfficialGroupMessage(mirai.GroupMessage):
pass
class OfficialFriendMessage(mirai.FriendMessage):
pass
event_handler_mapping = {
mirai.GroupMessage: ["on_at_message_create", "on_group_at_message_create"],
mirai.FriendMessage: ["on_direct_message_create"],
mirai.FriendMessage: ["on_direct_message_create", "on_c2c_message_create"],
}
@ -193,7 +194,7 @@ class OfficialMessageConverter(adapter_model.MessageConverter):
@staticmethod
def extract_message_chain_from_obj(
message: typing.Union[botpy_message.Message, botpy_message.DirectMessage],
message: typing.Union[botpy_message.Message, botpy_message.DirectMessage, botpy_message.GroupMessage, botpy_message.C2CMessage],
message_id: str = None,
bot_account_id: int = 0,
) -> mirai.MessageChain:
@ -206,7 +207,7 @@ class OfficialMessageConverter(adapter_model.MessageConverter):
)
)
if type(message) is not botpy_message.DirectMessage:
if type(message) not in [botpy_message.DirectMessage, botpy_message.C2CMessage]:
yiri_msg_list.append(mirai.At(target=bot_account_id))
if hasattr(message, "mentions"):
@ -255,7 +256,7 @@ class OfficialEventConverter(adapter_model.EventConverter):
def target2yiri(
self,
event: typing.Union[botpy_message.Message, botpy_message.DirectMessage]
event: typing.Union[botpy_message.Message, botpy_message.DirectMessage, botpy_message.GroupMessage, botpy_message.C2CMessage],
) -> mirai.Event:
import mirai.models.entities as mirai_entities
@ -295,7 +296,7 @@ class OfficialEventConverter(adapter_model.EventConverter):
).timestamp()
),
)
elif type(event) == botpy_message.DirectMessage: # 私聊,转私聊事件
elif type(event) == botpy_message.DirectMessage: # 频道私聊,转私聊事件
return mirai.FriendMessage(
sender=mirai_entities.Friend(
id=event.guild_id,
@ -311,7 +312,7 @@ class OfficialEventConverter(adapter_model.EventConverter):
).timestamp()
),
)
elif type(event) == botpy_message.GroupMessage:
elif type(event) == botpy_message.GroupMessage: # 群聊,转群聊事件
replacing_member_id = self.member_openid_mapping.save_openid(event.author.member_openid)
@ -339,6 +340,25 @@ class OfficialEventConverter(adapter_model.EventConverter):
).timestamp()
),
)
elif type(event) == botpy_message.C2CMessage: # 私聊,转私聊事件
user_id_alter = self.member_openid_mapping.save_openid(event.author.user_openid) # 实测这里的user_openid与group的member_openid是一样的
return OfficialFriendMessage(
sender=mirai_entities.Friend(
id=user_id_alter,
nickname=user_id_alter,
remark=user_id_alter,
),
message_chain=OfficialMessageConverter.extract_message_chain_from_obj(
event, event.id
),
time=int(
datetime.datetime.strptime(
event.timestamp, "%Y-%m-%dT%H:%M:%S%z"
).timestamp()
),
)
@adapter_model.adapter_class("qq-botpy")
@ -368,6 +388,7 @@ class OfficialAdapter(adapter_model.MessageSourceAdapter):
group_openid_mapping: OpenIDMapping[str, int] = None
group_msg_seq = None
c2c_msg_seq = None
def __init__(self, cfg: dict, ap: app.Application):
"""初始化适配器"""
@ -375,6 +396,7 @@ class OfficialAdapter(adapter_model.MessageSourceAdapter):
self.ap = ap
self.group_msg_seq = 1
self.c2c_msg_seq = 1
switchs = {}
@ -454,18 +476,58 @@ class OfficialAdapter(adapter_model.MessageSourceAdapter):
]
await self.bot.api.post_dms(**args)
elif type(message_source) == OfficialGroupMessage:
if "image" in args or "file_image" in args:
if "file_image" in args: # 暂不支持发送文件图片
continue
args["group_openid"] = self.group_openid_mapping.getkey(
message_source.sender.group.id
)
if "image" in args:
uploadMedia = await self.bot.api.post_group_file(
group_openid=args["group_openid"],
file_type=1,
url=str(args['image'])
)
del args['image']
args['media'] = uploadMedia
args['msg_type'] = 7
args["msg_id"] = cached_message_ids[
str(message_source.message_chain.message_id)
]
args["msg_seq"] = self.group_msg_seq
self.group_msg_seq += 1
await self.bot.api.post_group_message(**args)
elif type(message_source) == OfficialFriendMessage:
if "file_image" in args:
continue
args["openid"] = self.member_openid_mapping.getkey(
message_source.sender.id
)
if "image" in args:
uploadMedia = await self.bot.api.post_c2c_file(
openid=args["openid"],
file_type=1,
url=str(args['image'])
)
del args['image']
args['media'] = uploadMedia
args['msg_type'] = 7
args["msg_id"] = cached_message_ids[
str(message_source.message_chain.message_id)
]
args["msg_seq"] = self.c2c_msg_seq
self.c2c_msg_seq += 1
await self.bot.api.post_c2c_message(**args)
async def is_muted(self, group_id: int) -> bool:
return False

View File

@ -140,7 +140,7 @@ class PluginManager:
for plugin in self.plugins:
if plugin.enabled:
if event.__class__ in plugin.event_handlers:
self.ap.logger.debug(f'插件 {plugin.plugin_name} 触发事件 {event.__class__.__name__}')
self.ap.logger.debug(f'插件 {plugin.plugin_name} 处理事件 {event.__class__.__name__}')
is_prevented_default_before_call = ctx.is_prevented_default()
@ -150,7 +150,7 @@ class PluginManager:
ctx
)
except Exception as e:
self.ap.logger.error(f'插件 {plugin.plugin_name} 触发事件 {event.__class__.__name__} 时发生错误: {e}')
self.ap.logger.error(f'插件 {plugin.plugin_name} 处理事件 {event.__class__.__name__} 时发生错误: {e}')
self.ap.logger.debug(f"Traceback: {traceback.format_exc()}")
emitted_plugins.append(plugin)

View File

@ -1,3 +1,6 @@
> [!WARNING]
> 此 Wiki 已弃用,所有文档已迁移到 [项目主页](https://qchatgpt.rockchin.top)
## 功能点列举
<details>

View File

@ -1,3 +1,6 @@
> [!WARNING]
> 此 Wiki 已弃用,所有文档已迁移到 [项目主页](https://qchatgpt.rockchin.top)
使用过程中的一些疑问,这里不是解决异常的地方,遇到异常请见`常见错误`页
### ❓ 如何更新代码到最新版本?

View File

@ -1 +1,4 @@
> [!WARNING]
> 此 Wiki 已弃用,所有文档已迁移到 [项目主页](https://qchatgpt.rockchin.top)
搜索[主仓库issue](https://github.com/RockChinQ/QChatGPT/issues)和[安装器issue](https://github.com/RockChinQ/qcg-installer/issues)

View File

@ -1,3 +1,6 @@
> [!WARNING]
> 此 Wiki 已弃用,所有文档已迁移到 [项目主页](https://qchatgpt.rockchin.top)
以下是QChatGPT实现原理等技术信息贡献之前请仔细阅读
> 太久没更了,过时了,建议读源码,~~注释还挺全的~~

View File

@ -1,3 +1,6 @@
> [!WARNING]
> 此 Wiki 已弃用,所有文档已迁移到 [项目主页](https://qchatgpt.rockchin.top)
QChatGPT 插件使用Wiki
## 简介

View File

@ -1,3 +1,6 @@
> [!WARNING]
> 此 Wiki 已弃用,所有文档已迁移到 [项目主页](https://qchatgpt.rockchin.top)
> 说白了就是ChatGPT官方插件那种东西
内容函数是基于[GPT的Function Calling能力](https://platform.openai.com/docs/guides/gpt/function-calling)实现的这是一种嵌入对话中由GPT自动调用的函数。

View File

@ -1,3 +1,6 @@
> [!WARNING]
> 此 Wiki 已弃用,所有文档已迁移到 [项目主页](https://qchatgpt.rockchin.top)
QChatGPT 插件开发Wiki
> 请先阅读[插件使用页](https://github.com/RockChinQ/QChatGPT/wiki/5-%E6%8F%92%E4%BB%B6%E4%BD%BF%E7%94%A8)

View File

@ -1,3 +1,6 @@
> [!WARNING]
> 此 Wiki 已弃用,所有文档已迁移到 [项目主页](https://qchatgpt.rockchin.top)
## 多个对话接口有何区别?
出于对稳定性的高要求本项目主线接入的是GPT-3模型接口此接口由OpenAI官方开放稳定性强。

View File

@ -1,3 +1,6 @@
> [!WARNING]
> 此 Wiki 已弃用,所有文档已迁移到 [项目主页](https://qchatgpt.rockchin.top)
# 配置go-cqhttp用于登录QQ
> 若您是从旧版本升级到此版本以使用go-cqhttp的用户请您按照`config-template.py`的内容修改`config.py`,添加`msg_source_adapter`配置项并将其设为`nakuru`,同时添加`nakuru_config`字段按照说明配置。