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