refactor: 将提及检测移动到Adapter端,添加名称和提及兴趣
parent
d64670a930
commit
f1ef511b7c
|
|
@ -14,8 +14,9 @@ from src.config.official_configs import (
|
||||||
ChatConfig,
|
ChatConfig,
|
||||||
DebugConfig,
|
DebugConfig,
|
||||||
MaiBotServerConfig,
|
MaiBotServerConfig,
|
||||||
|
MentionInterestConfig,
|
||||||
NapcatServerConfig,
|
NapcatServerConfig,
|
||||||
NicknameConfig,
|
NamesConfig,
|
||||||
VoiceConfig,
|
VoiceConfig,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -106,10 +107,11 @@ def update_config():
|
||||||
class Config(ConfigBase):
|
class Config(ConfigBase):
|
||||||
"""总配置类"""
|
"""总配置类"""
|
||||||
|
|
||||||
nickname: NicknameConfig
|
|
||||||
napcat_server: NapcatServerConfig
|
napcat_server: NapcatServerConfig
|
||||||
maibot_server: MaiBotServerConfig
|
maibot_server: MaiBotServerConfig
|
||||||
chat: ChatConfig
|
chat: ChatConfig
|
||||||
|
names: NamesConfig
|
||||||
|
mention_interest: MentionInterestConfig
|
||||||
voice: VoiceConfig
|
voice: VoiceConfig
|
||||||
debug: DebugConfig
|
debug: DebugConfig
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,12 +14,6 @@ from src.config.config_base import ConfigBase
|
||||||
ADAPTER_PLATFORM = "qq"
|
ADAPTER_PLATFORM = "qq"
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class NicknameConfig(ConfigBase):
|
|
||||||
nickname: str
|
|
||||||
"""机器人昵称"""
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class NapcatServerConfig(ConfigBase):
|
class NapcatServerConfig(ConfigBase):
|
||||||
host: str = "localhost"
|
host: str = "localhost"
|
||||||
|
|
@ -61,9 +55,39 @@ class ChatConfig(ConfigBase):
|
||||||
ban_user_id: list[str] = field(default_factory=[])
|
ban_user_id: list[str] = field(default_factory=[])
|
||||||
"""被封禁的用户ID列表,封禁后将无法与其进行交互"""
|
"""被封禁的用户ID列表,封禁后将无法与其进行交互"""
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class NamesConfig(ConfigBase):
|
||||||
|
name: str
|
||||||
|
"""机器人全名"""
|
||||||
|
|
||||||
|
alias_names: list[str] = field(default_factory=[])
|
||||||
|
"""机器人别名列表,支持多个别名"""
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class MentionInterestConfig(ConfigBase):
|
||||||
|
at: float = 1.0
|
||||||
|
"""at的兴趣权重"""
|
||||||
|
|
||||||
|
reply: float = 1.0
|
||||||
|
"""回复的兴趣权重"""
|
||||||
|
|
||||||
enable_poke: bool = True
|
enable_poke: bool = True
|
||||||
"""是否启用戳一戳功能"""
|
"""是否启用戳一戳功能"""
|
||||||
|
|
||||||
|
poke: float = 1.0
|
||||||
|
"""戳一戳的兴趣权重(在启用戳一戳时有效)"""
|
||||||
|
|
||||||
|
enable_mention_in_text: bool = True
|
||||||
|
"""是否启用在正文中提及"""
|
||||||
|
|
||||||
|
name_in_text: float = 0.4
|
||||||
|
"""正文中提及全名的兴趣权重(在enable_mention_in_text为True时有效)"""
|
||||||
|
|
||||||
|
alias_in_text: float = 0.2
|
||||||
|
"""正文中提及别名的兴趣权重(在enable_mention_in_text为True时有效)"""
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class VoiceConfig(ConfigBase):
|
class VoiceConfig(ConfigBase):
|
||||||
|
|
|
||||||
|
|
@ -264,7 +264,7 @@ class RecvHandler:
|
||||||
case RealMessageType.text:
|
case RealMessageType.text:
|
||||||
ret_seg = await self.handle_text_message(sub_message)
|
ret_seg = await self.handle_text_message(sub_message)
|
||||||
if ret_seg:
|
if ret_seg:
|
||||||
seg_message.append(ret_seg)
|
seg_message.extend(ret_seg)
|
||||||
else:
|
else:
|
||||||
logger.warning("text处理失败")
|
logger.warning("text处理失败")
|
||||||
case RealMessageType.face:
|
case RealMessageType.face:
|
||||||
|
|
@ -274,12 +274,20 @@ class RecvHandler:
|
||||||
else:
|
else:
|
||||||
logger.warning("face处理失败或不支持")
|
logger.warning("face处理失败或不支持")
|
||||||
case RealMessageType.reply:
|
case RealMessageType.reply:
|
||||||
if not in_reply:
|
if in_reply:
|
||||||
ret_seg = await self.handle_reply_message(sub_message)
|
# 如果是回复消息,则不再处理回复消息
|
||||||
if ret_seg:
|
logger.debug("取消深层回复解析")
|
||||||
seg_message += ret_seg
|
seg_message.append(Seg(type="text", data="[回复消息]"))
|
||||||
else:
|
continue
|
||||||
logger.warning("reply处理失败")
|
|
||||||
|
ret_seg = await self.handle_reply_message(
|
||||||
|
sub_message,
|
||||||
|
raw_message.get("self_id"),
|
||||||
|
)
|
||||||
|
if ret_seg:
|
||||||
|
seg_message.extend(ret_seg)
|
||||||
|
else:
|
||||||
|
logger.warning("reply处理失败")
|
||||||
case RealMessageType.image:
|
case RealMessageType.image:
|
||||||
ret_seg = await self.handle_image_message(sub_message)
|
ret_seg = await self.handle_image_message(sub_message)
|
||||||
if ret_seg:
|
if ret_seg:
|
||||||
|
|
@ -297,7 +305,7 @@ class RecvHandler:
|
||||||
raw_message.get("group_id"),
|
raw_message.get("group_id"),
|
||||||
)
|
)
|
||||||
if ret_seg:
|
if ret_seg:
|
||||||
seg_message.append(ret_seg)
|
seg_message.extend(ret_seg)
|
||||||
else:
|
else:
|
||||||
logger.warning("at处理失败")
|
logger.warning("at处理失败")
|
||||||
case RealMessageType.rps:
|
case RealMessageType.rps:
|
||||||
|
|
@ -325,7 +333,7 @@ class RecvHandler:
|
||||||
logger.warning(f"未知消息类型: {sub_message_type}")
|
logger.warning(f"未知消息类型: {sub_message_type}")
|
||||||
return seg_message
|
return seg_message
|
||||||
|
|
||||||
async def handle_text_message(self, raw_message: dict) -> Seg:
|
async def handle_text_message(self, raw_message: dict) -> list[Seg]:
|
||||||
"""
|
"""
|
||||||
处理纯文本信息
|
处理纯文本信息
|
||||||
Parameters:
|
Parameters:
|
||||||
|
|
@ -335,7 +343,31 @@ class RecvHandler:
|
||||||
"""
|
"""
|
||||||
message_data: dict = raw_message.get("data")
|
message_data: dict = raw_message.get("data")
|
||||||
plain_text: str = message_data.get("text")
|
plain_text: str = message_data.get("text")
|
||||||
return Seg(type="text", data=plain_text)
|
|
||||||
|
seg_list = [
|
||||||
|
Seg(type="text", data=plain_text),
|
||||||
|
]
|
||||||
|
|
||||||
|
if global_config.mention_interest.enable_mention_in_text:
|
||||||
|
# 如果启用了正文中提及
|
||||||
|
if global_config.names.name in plain_text:
|
||||||
|
# 文本中包含机器人的全名,添加提示
|
||||||
|
seg_list.append(
|
||||||
|
Seg(
|
||||||
|
type="mention_bot",
|
||||||
|
data=global_config.mention_interest.name_in_text,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
elif any(alias_name in plain_text for alias_name in global_config.names.alias_names):
|
||||||
|
# 文本中包含机器人的别名,添加提示
|
||||||
|
seg_list.append(
|
||||||
|
Seg(
|
||||||
|
type="mention_bot",
|
||||||
|
data=global_config.mention_interest.alias_in_text,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return seg_list
|
||||||
|
|
||||||
async def handle_face_message(self, raw_message: dict) -> Seg | None:
|
async def handle_face_message(self, raw_message: dict) -> Seg | None:
|
||||||
"""
|
"""
|
||||||
|
|
@ -379,8 +411,7 @@ class RecvHandler:
|
||||||
logger.warning(f"不支持的图片子类型:{image_sub_type}")
|
logger.warning(f"不支持的图片子类型:{image_sub_type}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
async def handle_at_message(self, raw_message: dict, self_id: int, group_id: int) -> Seg | None:
|
async def handle_at_message(self, raw_message: dict, self_id: int, group_id: int) -> List[Seg] | None:
|
||||||
# sourcery skip: use-named-expression
|
|
||||||
"""
|
"""
|
||||||
处理at消息
|
处理at消息
|
||||||
Parameters:
|
Parameters:
|
||||||
|
|
@ -390,22 +421,31 @@ class RecvHandler:
|
||||||
Returns:
|
Returns:
|
||||||
seg_data: Seg: 处理后的消息段
|
seg_data: Seg: 处理后的消息段
|
||||||
"""
|
"""
|
||||||
message_data: dict = raw_message.get("data")
|
message_data = raw_message.get("data")
|
||||||
if message_data:
|
|
||||||
qq_id = message_data.get("qq")
|
if not message_data:
|
||||||
if str(self_id) == str(qq_id):
|
logger.warning("at消息数据为空")
|
||||||
logger.debug("机器人被at")
|
return None
|
||||||
self_info: dict = await get_self_info(self.server_connection)
|
|
||||||
if self_info:
|
qq_id = message_data.get("qq")
|
||||||
return Seg(type="text", data=f"@<{self_info.get('nickname')}:{self_info.get('user_id')}>")
|
if str(self_id) == str(qq_id):
|
||||||
else:
|
logger.debug("机器人被at")
|
||||||
return None
|
self_target = True
|
||||||
else:
|
target_user_info: dict = await get_self_info(self.server_connection)
|
||||||
member_info: dict = await get_member_info(self.server_connection, group_id=group_id, user_id=qq_id)
|
else:
|
||||||
if member_info:
|
self_target = False
|
||||||
return Seg(type="text", data=f"@<{member_info.get('nickname')}:{member_info.get('user_id')}>")
|
target_user_info: dict = await get_member_info(self.server_connection, group_id=group_id, user_id=qq_id)
|
||||||
else:
|
|
||||||
return None
|
seg_list = []
|
||||||
|
|
||||||
|
if target_user_info:
|
||||||
|
seg_list.append(Seg(type="text", data=f"@{target_user_info.get('nickname')}"))
|
||||||
|
|
||||||
|
if self_target:
|
||||||
|
# 如果是at机器人,则添加一条提示信息
|
||||||
|
seg_list.append(Seg(type="mention_bot", data=global_config.mention_interest.at))
|
||||||
|
|
||||||
|
return seg_list
|
||||||
|
|
||||||
async def get_forward_message(self, raw_message: dict) -> Dict[str, Any] | None:
|
async def get_forward_message(self, raw_message: dict) -> Dict[str, Any] | None:
|
||||||
forward_message_data: Dict = raw_message.get("data")
|
forward_message_data: Dict = raw_message.get("data")
|
||||||
|
|
@ -441,37 +481,57 @@ class RecvHandler:
|
||||||
return None
|
return None
|
||||||
return response_data.get("messages")
|
return response_data.get("messages")
|
||||||
|
|
||||||
async def handle_reply_message(self, raw_message: dict) -> List[Seg] | None:
|
async def handle_reply_message(self, raw_message: dict, self_id) -> List[Seg] | None:
|
||||||
# sourcery skip: move-assign-in-block, use-named-expression
|
|
||||||
"""
|
"""
|
||||||
处理回复消息
|
处理回复消息
|
||||||
|
|
||||||
"""
|
"""
|
||||||
raw_message_data: dict = raw_message.get("data")
|
|
||||||
message_id: int = None
|
if not (raw_message_data := raw_message.get("data")):
|
||||||
if raw_message_data:
|
|
||||||
message_id = raw_message_data.get("id")
|
|
||||||
else:
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
message_id = raw_message_data.get("id")
|
||||||
|
|
||||||
message_detail: dict = await get_message_detail(self.server_connection, message_id)
|
message_detail: dict = await get_message_detail(self.server_connection, message_id)
|
||||||
if not message_detail:
|
if not message_detail:
|
||||||
logger.warning("获取被引用的消息详情失败")
|
logger.warning("获取被引用的消息详情失败")
|
||||||
return None
|
return None
|
||||||
reply_message = await self.handle_real_message(message_detail, in_reply=True)
|
|
||||||
if reply_message is None:
|
# 解析被引消息
|
||||||
reply_message = "(获取发言内容失败)"
|
replied_message = await self.handle_real_message(message_detail, in_reply=True)
|
||||||
|
if replied_message is None:
|
||||||
|
replied_message = Seg(type="text", data="(获取消息内容失败)") # 如果获取被引用消息失败,则返回默认文本
|
||||||
|
|
||||||
|
# 尝试获取被引用消息的发送人
|
||||||
sender_info: dict = message_detail.get("sender")
|
sender_info: dict = message_detail.get("sender")
|
||||||
sender_nickname: str = sender_info.get("nickname")
|
if (
|
||||||
sender_id: str = sender_info.get("user_id")
|
sender_info
|
||||||
seg_message: List[Seg] = []
|
and (sender_nickname := sender_info.get("nickname"))
|
||||||
if not sender_nickname:
|
and (sender_id := sender_info.get("user_id"))
|
||||||
logger.warning("无法获取被引用的人的昵称,返回默认值")
|
):
|
||||||
seg_message.append(Seg(type="text", data="[回复 未知用户:"))
|
# 如果获取到昵称和id
|
||||||
|
self_target = str(self_id) == str(sender_id) # 判断是否是回复自己的消息
|
||||||
else:
|
else:
|
||||||
seg_message.append(Seg(type="text", data=f"[回复<{sender_nickname}:{sender_id}>:"))
|
# 如果没有获取到昵称和id
|
||||||
seg_message += reply_message
|
logger.warning("无法获取被引用的人的昵称或id,使用默认值")
|
||||||
seg_message.append(Seg(type="text", data="],说:"))
|
sender_nickname = None
|
||||||
return seg_message
|
|
||||||
|
seg_list = []
|
||||||
|
|
||||||
|
if sender_nickname:
|
||||||
|
seg_list.append(Seg(type="text", data=f"[回复<{sender_nickname}:{sender_id}>:"))
|
||||||
|
else:
|
||||||
|
seg_list.append(Seg(type="text", data="[回复 未知用户:"))
|
||||||
|
|
||||||
|
seg_list.extend(replied_message) # 添加被回复的消息体
|
||||||
|
|
||||||
|
seg_list.append(Seg(type="text", data="],说:"))
|
||||||
|
|
||||||
|
if self_target:
|
||||||
|
# 如果是回复自己的消息,则添加提示
|
||||||
|
seg_list.append(Seg(type="mention_bot", data=global_config.mention_interest.reply))
|
||||||
|
|
||||||
|
return seg_list
|
||||||
|
|
||||||
async def handle_notice(self, raw_message: dict) -> None:
|
async def handle_notice(self, raw_message: dict) -> None:
|
||||||
notice_type = raw_message.get("notice_type")
|
notice_type = raw_message.get("notice_type")
|
||||||
|
|
@ -500,7 +560,7 @@ class RecvHandler:
|
||||||
sub_type = raw_message.get("sub_type")
|
sub_type = raw_message.get("sub_type")
|
||||||
match sub_type:
|
match sub_type:
|
||||||
case NoticeType.Notify.poke:
|
case NoticeType.Notify.poke:
|
||||||
if global_config.chat.enable_poke:
|
if global_config.mention_interest.enable_poke:
|
||||||
handled_message: Seg = await self.handle_poke_notify(raw_message)
|
handled_message: Seg = await self.handle_poke_notify(raw_message)
|
||||||
else:
|
else:
|
||||||
logger.warning("戳一戳消息被禁用,取消戳一戳处理")
|
logger.warning("戳一戳消息被禁用,取消戳一戳处理")
|
||||||
|
|
@ -580,21 +640,15 @@ class RecvHandler:
|
||||||
target_id = raw_message.get("target_id")
|
target_id = raw_message.get("target_id")
|
||||||
target_name: str = None
|
target_name: str = None
|
||||||
raw_info: list = raw_message.get("raw_info")
|
raw_info: list = raw_message.get("raw_info")
|
||||||
# 计算Seg
|
if self_id != target_id:
|
||||||
if self_id == target_id:
|
|
||||||
target_name = self_info.get("nickname")
|
|
||||||
else:
|
|
||||||
return None
|
return None
|
||||||
try:
|
|
||||||
first_txt = raw_info[2].get("txt", "戳了戳")
|
# 如果是戳一戳自己的消息
|
||||||
second_txt = raw_info[4].get("txt", "")
|
self_target = True
|
||||||
except Exception as e:
|
target_name = self_info.get("nickname")
|
||||||
logger.warning(f"解析戳一戳消息失败: {str(e)},将使用默认文本")
|
|
||||||
first_txt = "戳了戳"
|
|
||||||
second_txt = ""
|
|
||||||
"""
|
"""
|
||||||
# 不启用戳其他人的处理
|
# 不启用戳其他人的处理
|
||||||
else:
|
self_target = False
|
||||||
# 由于Napcat不支持获取昵称,所以需要单独获取
|
# 由于Napcat不支持获取昵称,所以需要单独获取
|
||||||
group_id = raw_message.get("group_id")
|
group_id = raw_message.get("group_id")
|
||||||
fetched_member_info: dict = await get_member_info(
|
fetched_member_info: dict = await get_member_info(
|
||||||
|
|
@ -603,11 +657,31 @@ class RecvHandler:
|
||||||
if fetched_member_info:
|
if fetched_member_info:
|
||||||
target_name = fetched_member_info.get("nickname")
|
target_name = fetched_member_info.get("nickname")
|
||||||
"""
|
"""
|
||||||
seg_data: Seg = Seg(
|
|
||||||
type="text",
|
try:
|
||||||
data=f"{first_txt}{target_name}{second_txt}(这是QQ的一个功能,用于提及某人,但没那么明显)",
|
first_txt = raw_info[2].get("txt", "戳了戳")
|
||||||
)
|
second_txt = raw_info[4].get("txt", "")
|
||||||
return seg_data
|
except Exception as e:
|
||||||
|
logger.warning(f"解析戳一戳消息失败: {str(e)},将使用默认文本")
|
||||||
|
first_txt = "戳了戳"
|
||||||
|
second_txt = ""
|
||||||
|
|
||||||
|
seg_list = [
|
||||||
|
Seg(
|
||||||
|
type="text",
|
||||||
|
data=f"{first_txt}{target_name}{second_txt}(这是QQ的一个功能,用于提及某人,但没那么明显)",
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
if self_target:
|
||||||
|
seg_list.append(
|
||||||
|
Seg(
|
||||||
|
type="mention_bot",
|
||||||
|
data=global_config.mention_interest.poke,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return Seg(type="seglist", data=seg_list)
|
||||||
|
|
||||||
async def handle_forward_message(self, message_list: list) -> Seg | None:
|
async def handle_forward_message(self, message_list: list) -> Seg | None:
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,7 @@
|
||||||
[inner]
|
[inner]
|
||||||
version = "0.1.0" # 版本号
|
version = "0.2.0" # 版本号
|
||||||
# 请勿修改版本号,除非你知道自己在做什么
|
# 请勿修改版本号,除非你知道自己在做什么
|
||||||
|
|
||||||
[nickname] # 现在没用
|
|
||||||
nickname = ""
|
|
||||||
|
|
||||||
[napcat_server] # Napcat连接的ws服务设置
|
[napcat_server] # Napcat连接的ws服务设置
|
||||||
host = "localhost" # Napcat设定的主机地址
|
host = "localhost" # Napcat设定的主机地址
|
||||||
port = 8095 # Napcat设定的端口
|
port = 8095 # Napcat设定的端口
|
||||||
|
|
@ -15,16 +12,33 @@ host = "localhost" # 麦麦在.env文件中设置的主机地址,即HOST字段
|
||||||
port = 8000 # 麦麦在.env文件中设置的端口,即PORT字段
|
port = 8000 # 麦麦在.env文件中设置的端口,即PORT字段
|
||||||
|
|
||||||
[chat] # 黑白名单功能
|
[chat] # 黑白名单功能
|
||||||
group_list_type = "whitelist" # 群组名单类型,可选为:whitelist, blacklist
|
|
||||||
group_list = [] # 群组名单
|
|
||||||
# 当group_list_type为whitelist时,只有群组名单中的群组可以聊天
|
# 当group_list_type为whitelist时,只有群组名单中的群组可以聊天
|
||||||
# 当group_list_type为blacklist时,群组名单中的任何群组无法聊天
|
# 当group_list_type为blacklist时,群组名单中的任何群组无法聊天
|
||||||
private_list_type = "whitelist" # 私聊名单类型,可选为:whitelist, blacklist
|
# 群组名单类型,可选为:whitelist, blacklist
|
||||||
private_list = [] # 私聊名单
|
group_list_type = "whitelist"
|
||||||
|
group_list = [] # 群组名单
|
||||||
|
|
||||||
|
# 私聊名单类型,可选为:whitelist, blacklist
|
||||||
# 当private_list_type为whitelist时,只有私聊名单中的用户可以聊天
|
# 当private_list_type为whitelist时,只有私聊名单中的用户可以聊天
|
||||||
# 当private_list_type为blacklist时,私聊名单中的任何用户无法聊天
|
# 当private_list_type为blacklist时,私聊名单中的任何用户无法聊天
|
||||||
ban_user_id = [] # 全局禁止名单(全局禁止名单中的用户无法进行任何聊天)
|
private_list_type = "whitelist"
|
||||||
enable_poke = true # 是否启用戳一戳功能
|
private_list = [] # 私聊名单
|
||||||
|
|
||||||
|
# 全局禁止名单(全局禁止名单中的用户无法进行任何聊天)
|
||||||
|
ban_user_id = []
|
||||||
|
|
||||||
|
[names] # 名称设置,用于正文提及检测
|
||||||
|
name = "麦麦" # bot的全名(大名)
|
||||||
|
alias_names = ["牢麦"] # 别名(昵称)列表,支持多个别名
|
||||||
|
|
||||||
|
[mention_interest] # 提及兴趣设置(0.0-1.0之间的浮点数,越大越感兴趣)
|
||||||
|
at = 1.0 # at的兴趣权重
|
||||||
|
reply = 1.0 # 回复的兴趣权重
|
||||||
|
enable_poke = true # 是否启用戳一戳功能
|
||||||
|
poke = 1.0 # 戳一戳的兴趣权重
|
||||||
|
enable_mention_in_text = true # 是否启用“在正文中提及”
|
||||||
|
name_in_text = 0.4 # 正文中提及全名的兴趣权重
|
||||||
|
alias_in_text = 0.2 # 正文中提及别名的兴趣权重
|
||||||
|
|
||||||
[voice] # 发送语音设置
|
[voice] # 发送语音设置
|
||||||
use_tts = false # 是否使用tts语音(请确保你配置了tts并有对应的adapter)
|
use_tts = false # 是否使用tts语音(请确保你配置了tts并有对应的adapter)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue