添加私聊功能,通过在群白名单中添加群号"0"开启

pull/182/head
Pliosauroidea 2025-03-11 05:55:15 +08:00
parent 0f5f1c920d
commit 77311a1125
5 changed files with 295 additions and 186 deletions

View File

@ -5,7 +5,7 @@ import time
from loguru import logger from loguru import logger
from nonebot import get_driver, on_command, on_message, require from nonebot import get_driver, on_command, on_message, require
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message, MessageSegment from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message, MessageSegment,MessageEvent
from nonebot.rule import to_me from nonebot.rule import to_me
from nonebot.typing import T_State from nonebot.typing import T_State
@ -103,7 +103,7 @@ async def _(bot: Bot):
print("\033[1;38;5;208m-----------开始偷表情包!-----------\033[0m") print("\033[1;38;5;208m-----------开始偷表情包!-----------\033[0m")
@group_msg.handle() @group_msg.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State): async def _(bot: Bot, event: MessageEvent, state: T_State):
await chat_bot.handle_message(event, bot) await chat_bot.handle_message(event, bot)
# 添加build_memory定时任务 # 添加build_memory定时任务

View File

@ -2,7 +2,7 @@ import time
from random import random from random import random
from loguru import logger from loguru import logger
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, PrivateMessageEvent,MessageEvent
from ..memory_system.memory import hippocampus from ..memory_system.memory import hippocampus
from ..moods.moods import MoodManager # 导入情绪管理器 from ..moods.moods import MoodManager # 导入情绪管理器
@ -31,37 +31,60 @@ class ChatBot:
self._started = False self._started = False
self.mood_manager = MoodManager.get_instance() # 获取情绪管理器单例 self.mood_manager = MoodManager.get_instance() # 获取情绪管理器单例
self.mood_manager.start_mood_update() # 启动情绪更新 self.mood_manager.start_mood_update() # 启动情绪更新
self.emoji_chance = 0.2 # 发送表情包的基础概率 self.emoji_chance = 0.2 # 发送表情包的基础概率
# self.message_streams = MessageStreamContainer() # self.message_streams = MessageStreamContainer()
async def _ensure_started(self): async def _ensure_started(self):
"""确保所有任务已启动""" """确保所有任务已启动"""
if not self._started: if not self._started:
self._started = True self._started = True
async def handle_message(self, event: GroupMessageEvent, bot: Bot) -> None: async def handle_message(self, event: MessageEvent, bot: Bot) -> None:
"""处理收到的消息""" """处理收到的消息"""
# 私聊消息接口
if isinstance(event, PrivateMessageEvent):
event.group_id = 0
if event.group_id not in global_config.talk_allowed_groups: if event.group_id not in global_config.talk_allowed_groups:
return return
self.bot = bot # 更新 bot 实例 self.bot = bot # 更新 bot 实例
if event.user_id in global_config.ban_user_id: if event.user_id in global_config.ban_user_id:
return return
group_info = await bot.get_group_info(group_id=event.group_id) if isinstance(event, GroupMessageEvent):
sender_info = await bot.get_group_member_info(group_id=event.group_id, user_id=event.user_id, no_cache=True) group_info = await bot.get_group_info(
group_id=event.group_id, no_cache=True
await relationship_manager.update_relationship(user_id = event.user_id, data = sender_info) )
await relationship_manager.update_relationship_value(user_id = event.user_id, relationship_value = 0.5) sender_info = await bot.get_group_member_info(
group_id=event.group_id, user_id=event.user_id, no_cache=True
)
else:
group_info = {"group_name": "私聊"}
sender_info = {
"user_id": event.user_id,
"nickname": (
await bot.get_stranger_info(user_id=event.user_id, no_cache=True)
)["nickname"],
"card": None,
}
await relationship_manager.update_relationship(
user_id=event.user_id, data=sender_info
)
await relationship_manager.update_relationship_value(
user_id=event.user_id, relationship_value=0.5
)
message = Message( message = Message(
group_id=event.group_id, group_id=event.group_id,
user_id=event.user_id, user_id=event.user_id,
message_id=event.message_id, message_id=event.message_id,
user_cardname=sender_info['card'], user_cardname=sender_info["card"],
raw_message=str(event.original_message), raw_message=str(event.original_message),
plain_text=event.get_plaintext(), plain_text=event.get_plaintext(),
reply_message=event.reply, reply_message=event.reply,
) )
@ -70,53 +93,57 @@ class ChatBot:
# 过滤词 # 过滤词
for word in global_config.ban_words: for word in global_config.ban_words:
if word in message.detailed_plain_text: if word in message.detailed_plain_text:
logger.info(f"\033[1;32m[{message.group_name}]{message.user_nickname}:\033[0m {message.processed_plain_text}") logger.info(
f"\033[1;32m[{group_info['group_name']}]{message.user_nickname}:\033[0m {message.processed_plain_text}"
)
logger.info(f"\033[1;32m[过滤词识别]\033[0m 消息中含有{word}filtered") logger.info(f"\033[1;32m[过滤词识别]\033[0m 消息中含有{word}filtered")
return return
current_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(message.time)) current_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(message.time))
# topic=await topic_identifier.identify_topic_llm(message.processed_plain_text) # topic=await topic_identifier.identify_topic_llm(message.processed_plain_text)
topic = '' topic = ""
interested_rate = 0 interested_rate = 0
interested_rate = await hippocampus.memory_activate_value(message.processed_plain_text)/100 interested_rate = (
print(f"\033[1;32m[记忆激活]\033[0m 对{message.processed_plain_text}的激活度:---------------------------------------{interested_rate}\n") await hippocampus.memory_activate_value(message.processed_plain_text) / 100
)
print(
f"\033[1;32m[记忆激活]\033[0m 对{message.processed_plain_text}的激活度:---------------------------------------{interested_rate}\n"
)
# logger.info(f"\033[1;32m[主题识别]\033[0m 使用{global_config.topic_extract}主题: {topic}") # logger.info(f"\033[1;32m[主题识别]\033[0m 使用{global_config.topic_extract}主题: {topic}")
await self.storage.store_message(message, topic[0] if topic else None) await self.storage.store_message(message, topic[0] if topic else None)
is_mentioned = is_mentioned_bot_in_txt(message.processed_plain_text) is_mentioned = is_mentioned_bot_in_txt(message.processed_plain_text)
reply_probability = willing_manager.change_reply_willing_received( reply_probability = willing_manager.change_reply_willing_received(
event.group_id, event.group_id,
topic[0] if topic else None, topic[0] if topic else None,
is_mentioned, is_mentioned,
global_config, global_config,
event.user_id, event.user_id,
message.is_emoji, message.is_emoji,
interested_rate interested_rate,
) )
current_willing = willing_manager.get_willing(event.group_id) current_willing = willing_manager.get_willing(event.group_id)
print(
print(f"\033[1;32m[{current_time}][{message.group_name}]{message.user_nickname}:\033[0m {message.processed_plain_text}\033[1;36m[回复意愿:{current_willing:.2f}][概率:{reply_probability * 100:.1f}%]\033[0m") f"\033[1;32m[{current_time}][{message.group_name}]{message.user_nickname}:\033[0m {message.processed_plain_text}\033[1;36m[回复意愿:{current_willing:.2f}][概率:{reply_probability * 100:.1f}%]\033[0m"
)
response = "" response = ""
if random() < reply_probability: if random() < reply_probability:
tinking_time_point = round(time.time(), 2) tinking_time_point = round(time.time(), 2)
think_id = 'mt' + str(tinking_time_point) think_id = "mt" + str(tinking_time_point)
thinking_message = Message_Thinking(message=message,message_id=think_id) thinking_message = Message_Thinking(message=message, message_id=think_id)
message_manager.add_message(thinking_message) message_manager.add_message(thinking_message)
willing_manager.change_reply_willing_sent(thinking_message.group_id) willing_manager.change_reply_willing_sent(thinking_message.group_id)
response,raw_content = await self.gpt.generate_response(message) response, raw_content = await self.gpt.generate_response(message)
if response: if response:
container = message_manager.get_container(event.group_id) container = message_manager.get_container(event.group_id)
thinking_message = None thinking_message = None
@ -127,27 +154,29 @@ class ChatBot:
container.messages.remove(msg) container.messages.remove(msg)
# print(f"\033[1;32m[思考消息删除]\033[0m 已找到思考消息对象,开始删除") # print(f"\033[1;32m[思考消息删除]\033[0m 已找到思考消息对象,开始删除")
break break
# 如果找不到思考消息,直接返回 # 如果找不到思考消息,直接返回
if not thinking_message: if not thinking_message:
print(f"\033[1;33m[警告]\033[0m 未找到对应的思考消息,可能已超时被移除") print(f"\033[1;33m[警告]\033[0m 未找到对应的思考消息,可能已超时被移除")
return return
#记录开始思考的时间,避免从思考到回复的时间太久 # 记录开始思考的时间,避免从思考到回复的时间太久
thinking_start_time = thinking_message.thinking_start_time thinking_start_time = thinking_message.thinking_start_time
message_set = MessageSet(event.group_id, global_config.BOT_QQ, think_id) # 发送消息的id和产生发送消息的message_thinking是一致的 message_set = MessageSet(
#计算打字时间1是为了模拟打字2是避免多条回复乱序 event.group_id, global_config.BOT_QQ, think_id
) # 发送消息的id和产生发送消息的message_thinking是一致的
# 计算打字时间1是为了模拟打字2是避免多条回复乱序
accu_typing_time = 0 accu_typing_time = 0
# print(f"\033[1;32m[开始回复]\033[0m 开始将回复1载入发送容器") # print(f"\033[1;32m[开始回复]\033[0m 开始将回复1载入发送容器")
mark_head = False mark_head = False
for msg in response: for msg in response:
# print(f"\033[1;32m[回复内容]\033[0m {msg}") # print(f"\033[1;32m[回复内容]\033[0m {msg}")
#通过时间改变时间戳 # 通过时间改变时间戳
typing_time = calculate_typing_time(msg) typing_time = calculate_typing_time(msg)
accu_typing_time += typing_time accu_typing_time += typing_time
timepoint = tinking_time_point + accu_typing_time timepoint = tinking_time_point + accu_typing_time
bot_message = Message_Sending( bot_message = Message_Sending(
group_id=event.group_id, group_id=event.group_id,
user_id=global_config.BOT_QQ, user_id=global_config.BOT_QQ,
@ -157,36 +186,38 @@ class ChatBot:
processed_plain_text=msg, processed_plain_text=msg,
user_nickname=global_config.BOT_NICKNAME, user_nickname=global_config.BOT_NICKNAME,
group_name=message.group_name, group_name=message.group_name,
time=timepoint, #记录了回复生成的时间 time=timepoint, # 记录了回复生成的时间
thinking_start_time=thinking_start_time, #记录了思考开始的时间 thinking_start_time=thinking_start_time, # 记录了思考开始的时间
reply_message_id=message.message_id reply_message_id=message.message_id,
reply_user_id=message.user_id,
) )
await bot_message.initialize() await bot_message.initialize()
if not mark_head: if not mark_head:
bot_message.is_head = True bot_message.is_head = True
mark_head = True mark_head = True
message_set.add_message(bot_message) message_set.add_message(bot_message)
#message_set 可以直接加入 message_manager # message_set 可以直接加入 message_manager
# print(f"\033[1;32m[回复]\033[0m 将回复载入发送容器") # print(f"\033[1;32m[回复]\033[0m 将回复载入发送容器")
message_manager.add_message(message_set) message_manager.add_message(message_set)
bot_response_time = tinking_time_point bot_response_time = tinking_time_point
if random() < global_config.emoji_chance: if random() < global_config.emoji_chance:
emoji_raw = await emoji_manager.get_emoji_for_text(response) emoji_raw = await emoji_manager.get_emoji_for_text(response)
# 检查是否 <没有找到> emoji # 检查是否 <没有找到> emoji
if emoji_raw != None: if emoji_raw != None:
emoji_path,discription = emoji_raw emoji_path, discription = emoji_raw
emoji_cq = CQCode.create_emoji_cq(emoji_path) emoji_cq = CQCode.create_emoji_cq(emoji_path)
if random() < 0.5: if random() < 0.5:
bot_response_time = tinking_time_point - 1 bot_response_time = tinking_time_point - 1
else: else:
bot_response_time = bot_response_time + 1 bot_response_time = bot_response_time + 1
bot_message = Message_Sending( bot_message = Message_Sending(
group_id=event.group_id, group_id=event.group_id,
user_id=global_config.BOT_QQ, user_id=global_config.BOT_QQ,
@ -207,20 +238,25 @@ class ChatBot:
message_manager.add_message(bot_message) message_manager.add_message(bot_message)
emotion = await self.gpt._get_emotion_tags(raw_content) emotion = await self.gpt._get_emotion_tags(raw_content)
print(f"'{response}' 获取到的情感标签为:{emotion}") print(f"'{response}' 获取到的情感标签为:{emotion}")
valuedict={ valuedict = {
'happy': 0.5, "happy": 0.5,
'angry': -1, "angry": -1,
'sad': -0.5, "sad": -0.5,
'surprised': 0.2, "surprised": 0.2,
'disgusted': -1.5, "disgusted": -1.5,
'fearful': -0.7, "fearful": -0.7,
'neutral': 0.1 "neutral": 0.1,
} }
await relationship_manager.update_relationship_value(message.user_id, relationship_value=valuedict[emotion[0]]) await relationship_manager.update_relationship_value(
message.user_id, relationship_value=valuedict[emotion[0]]
)
# 使用情绪管理器更新情绪 # 使用情绪管理器更新情绪
self.mood_manager.update_mood_from_emotion(emotion[0], global_config.mood_intensity_factor) self.mood_manager.update_mood_from_emotion(
emotion[0], global_config.mood_intensity_factor
)
# willing_manager.change_reply_willing_after_sent(event.group_id) # willing_manager.change_reply_willing_after_sent(event.group_id)
# 创建全局ChatBot实例 # 创建全局ChatBot实例
chat_bot = ChatBot() chat_bot = ChatBot()

View File

@ -8,19 +8,19 @@ from .cq_code import CQCode, cq_code_tool
from .utils_cq import parse_cq_code from .utils_cq import parse_cq_code
from .utils_user import get_groupname, get_user_cardname, get_user_nickname from .utils_user import get_groupname, get_user_cardname, get_user_nickname
Message = ForwardRef('Message') # 添加这行 Message = ForwardRef("Message") # 添加这行
# 禁用SSL警告 # 禁用SSL警告
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
#这个类是消息数据类,用于存储和管理消息数据。 # 这个类是消息数据类,用于存储和管理消息数据。
#它定义了消息的属性包括群组ID、用户ID、消息ID、原始消息内容、纯文本内容和时间戳。 # 它定义了消息的属性包括群组ID、用户ID、消息ID、原始消息内容、纯文本内容和时间戳。
#它还定义了两个辅助属性keywords用于提取消息的关键词is_plain_text用于判断消息是否为纯文本。 # 它还定义了两个辅助属性keywords用于提取消息的关键词is_plain_text用于判断消息是否为纯文本。
@dataclass @dataclass
class Message: class Message:
"""消息数据类""" """消息数据类"""
message_id: int = None message_id: int = None
time: float = None time: float = None
@ -59,11 +59,12 @@ class Message:
# 消息解析 # 消息解析
if self.raw_message: if self.raw_message:
if not isinstance(self,Message_Sending): if not isinstance(self, Message_Sending):
self.message_segments = await self.parse_message_segments(self.raw_message) self.message_segments = await self.parse_message_segments(
self.processed_plain_text = ' '.join( self.raw_message
seg.translated_plain_text )
for seg in self.message_segments self.processed_plain_text = " ".join(
seg.translated_plain_text for seg in self.message_segments
) )
# 构建详细文本 # 构建详细文本
@ -75,13 +76,17 @@ class Message:
if self.user_cardname if self.user_cardname
else f"{self.user_nickname or f'用户{self.user_id}'}" else f"{self.user_nickname or f'用户{self.user_id}'}"
) )
if isinstance(self,Message_Sending) and self.is_emoji: if isinstance(self, Message_Sending) and self.is_emoji:
self.detailed_plain_text = f"[{time_str}] {name}: {self.detailed_plain_text}\n" self.detailed_plain_text = (
f"[{time_str}] {name}: {self.detailed_plain_text}\n"
)
else: else:
self.detailed_plain_text = f"[{time_str}] {name}: {self.processed_plain_text}\n" self.detailed_plain_text = (
f"[{time_str}] {name}: {self.processed_plain_text}\n"
)
self._initialized = True self._initialized = True
async def parse_message_segments(self, message: str) -> List[CQCode]: async def parse_message_segments(self, message: str) -> List[CQCode]:
""" """
将消息解析为片段列表包括纯文本和CQ码 将消息解析为片段列表包括纯文本和CQ码
@ -92,12 +97,12 @@ class Message:
# print(f"\033[1;34m[调试信息]\033[0m 正在处理消息: {message}") # print(f"\033[1;34m[调试信息]\033[0m 正在处理消息: {message}")
cq_code_dict_list = [] cq_code_dict_list = []
trans_list = [] trans_list = []
start = 0 start = 0
while True: while True:
# 查找下一个CQ码的开始位置 # 查找下一个CQ码的开始位置
cq_start = message.find('[CQ:', start) cq_start = message.find("[CQ:", start)
#如果没有cq码直接返回文本内容 # 如果没有cq码直接返回文本内容
if cq_start == -1: if cq_start == -1:
# 如果没有找到更多CQ码添加剩余文本 # 如果没有找到更多CQ码添加剩余文本
if start < len(message): if start < len(message):
@ -111,85 +116,93 @@ class Message:
if text: # 只添加非空文本 if text: # 只添加非空文本
cq_code_dict_list.append(parse_cq_code(text)) cq_code_dict_list.append(parse_cq_code(text))
# 查找CQ码的结束位置 # 查找CQ码的结束位置
cq_end = message.find(']', cq_start) cq_end = message.find("]", cq_start)
if cq_end == -1: if cq_end == -1:
# CQ码未闭合作为普通文本处理 # CQ码未闭合作为普通文本处理
text = message[cq_start:].strip() text = message[cq_start:].strip()
if text: if text:
cq_code_dict_list.append(parse_cq_code(text)) cq_code_dict_list.append(parse_cq_code(text))
break break
cq_code = message[cq_start:cq_end + 1] cq_code = message[cq_start : cq_end + 1]
#将cq_code解析成字典 # 将cq_code解析成字典
cq_code_dict_list.append(parse_cq_code(cq_code)) cq_code_dict_list.append(parse_cq_code(cq_code))
# 更新start位置到当前CQ码之后 # 更新start位置到当前CQ码之后
start = cq_end + 1 start = cq_end + 1
# print(f"\033[1;34m[调试信息]\033[0m 提取的消息对象:列表: {cq_code_dict_list}") # print(f"\033[1;34m[调试信息]\033[0m 提取的消息对象:列表: {cq_code_dict_list}")
#判定是否是表情包消息,以及是否含有表情包 # 判定是否是表情包消息,以及是否含有表情包
if len(cq_code_dict_list) == 1 and cq_code_dict_list[0]['type'] == 'image': if len(cq_code_dict_list) == 1 and cq_code_dict_list[0]["type"] == "image":
self.is_emoji = True self.is_emoji = True
self.has_emoji_emoji = True self.has_emoji_emoji = True
else: else:
for segment in cq_code_dict_list: for segment in cq_code_dict_list:
if segment['type'] == 'image' and segment['data'].get('sub_type') == '1': if (
segment["type"] == "image"
and segment["data"].get("sub_type") == "1"
):
self.has_emoji_emoji = True self.has_emoji_emoji = True
break break
# 翻译作为字典的CQ码
#翻译作为字典的CQ码
for _code_item in cq_code_dict_list: for _code_item in cq_code_dict_list:
message_obj = await cq_code_tool.cq_from_dict_to_class(_code_item,reply = self.reply_message) message_obj = await cq_code_tool.cq_from_dict_to_class(
trans_list.append(message_obj) _code_item, reply=self.reply_message
)
trans_list.append(message_obj)
return trans_list return trans_list
class Message_Thinking: class Message_Thinking:
"""消息思考类""" """消息思考类"""
def __init__(self, message: Message,message_id: str):
def __init__(self, message: Message, message_id: str):
# 复制原始消息的基本属性 # 复制原始消息的基本属性
self.group_id = message.group_id self.group_id = message.group_id
self.user_id = message.user_id self.user_id = message.user_id
self.user_nickname = message.user_nickname self.user_nickname = message.user_nickname
self.user_cardname = message.user_cardname self.user_cardname = message.user_cardname
self.group_name = message.group_name self.group_name = message.group_name
self.message_id = message_id self.message_id = message_id
# 思考状态相关属性 # 思考状态相关属性
self.thinking_start_time = int(time.time()) self.thinking_start_time = int(time.time())
self.thinking_time = 0 self.thinking_time = 0
self.interupt=False self.interupt = False
def update_thinking_time(self): def update_thinking_time(self):
self.thinking_time = round(time.time(), 2) - self.thinking_start_time self.thinking_time = round(time.time(), 2) - self.thinking_start_time
@dataclass @dataclass
class Message_Sending(Message): class Message_Sending(Message):
"""发送中的消息类""" """发送中的消息类"""
thinking_start_time: float = None # 思考开始时间 thinking_start_time: float = None # 思考开始时间
thinking_time: float = None # 思考时间 thinking_time: float = None # 思考时间
reply_message_id: int = None # 存储 回复的 源消息ID reply_message_id: int = None # 存储 回复的 源消息ID
reply_user_id: int = None # 私聊中需要回复的用户ID
is_head: bool = False # 是否是头部消息 is_head: bool = False # 是否是头部消息
def update_thinking_time(self): def update_thinking_time(self):
self.thinking_time = round(time.time(), 2) - self.thinking_start_time self.thinking_time = round(time.time(), 2) - self.thinking_start_time
return self.thinking_time return self.thinking_time
class MessageSet: class MessageSet:
"""消息集合类,可以存储多个发送消息""" """消息集合类,可以存储多个发送消息"""
def __init__(self, group_id: int, user_id: int, message_id: str): def __init__(self, group_id: int, user_id: int, message_id: str):
self.group_id = group_id self.group_id = group_id
self.user_id = user_id self.user_id = user_id
self.message_id = message_id self.message_id = message_id
self.messages: List[Message_Sending] = [] # 修改类型标注 self.messages: List[Message_Sending] = [] # 修改类型标注
self.time = round(time.time(), 2) self.time = round(time.time(), 2)
def add_message(self, message: Message_Sending) -> None: def add_message(self, message: Message_Sending) -> None:
"""添加消息到集合只接受Message_Sending类型""" """添加消息到集合只接受Message_Sending类型"""
if not isinstance(message, Message_Sending): if not isinstance(message, Message_Sending):
@ -197,18 +210,18 @@ class MessageSet:
self.messages.append(message) self.messages.append(message)
# 按时间排序 # 按时间排序
self.messages.sort(key=lambda x: x.time) self.messages.sort(key=lambda x: x.time)
def get_message_by_index(self, index: int) -> Optional[Message_Sending]: def get_message_by_index(self, index: int) -> Optional[Message_Sending]:
"""通过索引获取消息""" """通过索引获取消息"""
if 0 <= index < len(self.messages): if 0 <= index < len(self.messages):
return self.messages[index] return self.messages[index]
return None return None
def get_message_by_time(self, target_time: float) -> Optional[Message_Sending]: def get_message_by_time(self, target_time: float) -> Optional[Message_Sending]:
"""获取最接近指定时间的消息""" """获取最接近指定时间的消息"""
if not self.messages: if not self.messages:
return None return None
# 使用二分查找找到最接近的消息 # 使用二分查找找到最接近的消息
left, right = 0, len(self.messages) - 1 left, right = 0, len(self.messages) - 1
while left < right: while left < right:
@ -217,26 +230,22 @@ class MessageSet:
left = mid + 1 left = mid + 1
else: else:
right = mid right = mid
return self.messages[left] return self.messages[left]
def clear_messages(self) -> None: def clear_messages(self) -> None:
"""清空所有消息""" """清空所有消息"""
self.messages.clear() self.messages.clear()
def remove_message(self, message: Message_Sending) -> bool: def remove_message(self, message: Message_Sending) -> bool:
"""移除指定消息""" """移除指定消息"""
if message in self.messages: if message in self.messages:
self.messages.remove(message) self.messages.remove(message)
return True return True
return False return False
def __str__(self) -> str: def __str__(self) -> str:
return f"MessageSet(id={self.message_id}, count={len(self.messages)})" return f"MessageSet(id={self.message_id}, count={len(self.messages)})"
def __len__(self) -> int: def __len__(self) -> int:
return len(self.messages) return len(self.messages)

View File

@ -10,98 +10,118 @@ from .storage import MessageStorage
from .utils import calculate_typing_time from .utils import calculate_typing_time
from .config import global_config from .config import global_config
from loguru import logger
class Message_Sender: class Message_Sender:
"""发送器""" """发送器"""
def __init__(self): def __init__(self):
self.message_interval = (0.5, 1) # 消息间隔时间范围(秒) self.message_interval = (0.5, 1) # 消息间隔时间范围(秒)
self.last_send_time = 0 self.last_send_time = 0
self._current_bot = None self._current_bot = None
def set_bot(self, bot: Bot): def set_bot(self, bot: Bot):
"""设置当前bot实例""" """设置当前bot实例"""
self._current_bot = bot self._current_bot = bot
async def send_group_message( async def send_group_message(
self, self,
group_id: int, group_id: int,
send_text: str, send_text: str,
auto_escape: bool = False, auto_escape: bool = False,
reply_message_id: int = None, reply_message_id: int = None,
at_user_id: int = None at_user_id: int = None,
) -> None: ) -> None:
if not self._current_bot: if not self._current_bot:
raise RuntimeError("Bot未设置请先调用set_bot方法设置bot实例") raise RuntimeError("Bot未设置请先调用set_bot方法设置bot实例")
message = send_text message = send_text
# 如果需要回复 # 如果需要回复
if reply_message_id: if reply_message_id:
reply_cq = cq_code_tool.create_reply_cq(reply_message_id) reply_cq = cq_code_tool.create_reply_cq(reply_message_id)
message = reply_cq + message message = reply_cq + message
# 如果需要at # 如果需要at
# if at_user_id: # if at_user_id:
# at_cq = cq_code_tool.create_at_cq(at_user_id) # at_cq = cq_code_tool.create_at_cq(at_user_id)
# message = at_cq + " " + message # message = at_cq + " " + message
typing_time = calculate_typing_time(message) typing_time = calculate_typing_time(message)
if typing_time > 10: if typing_time > 10:
typing_time = 10 typing_time = 10
await asyncio.sleep(typing_time) await asyncio.sleep(typing_time)
# 发送消息 # 发送消息
try: try:
await self._current_bot.send_group_msg( await self._current_bot.send_group_msg(
group_id=group_id, group_id=group_id, message=message, auto_escape=auto_escape
message=message,
auto_escape=auto_escape
) )
print(f"\033[1;34m[调试]\033[0m 发送消息{message}成功") print(f"\033[1;34m[调试]\033[0m 发送消息{message}成功")
except Exception as e: except Exception as e:
print(f"发生错误 {e}") print(f"发生错误 {e}")
print(f"\033[1;34m[调试]\033[0m 发送消息{message}失败") print(f"\033[1;34m[调试]\033[0m 发送消息{message}失败")
async def send_private_message(
self, user_id: int, send_text: str, auto_escape: bool = False
) -> None:
if not self._current_bot:
raise RuntimeError("Bot未设置请先调用set_bot方法设置bot实例")
message = send_text
# 发送消息
try:
await self._current_bot.send_private_msg(
user_id=user_id, message=message, auto_escape=auto_escape
)
logger.info(f"发送私聊消息{message}成功")
except Exception as e:
logger.error(f"发送私聊消息{message}失败,错误信息:{e}")
class MessageContainer: class MessageContainer:
"""单个群的发送/思考消息容器""" """单个群的发送/思考消息容器"""
def __init__(self, group_id: int, max_size: int = 100): def __init__(self, group_id: int, max_size: int = 100):
self.group_id = group_id self.group_id = group_id
self.max_size = max_size self.max_size = max_size
self.messages = [] self.messages = []
self.last_send_time = 0 self.last_send_time = 0
self.thinking_timeout = 20 # 思考超时时间(秒) self.thinking_timeout = 20 # 思考超时时间(秒)
def get_timeout_messages(self) -> List[Message_Sending]: def get_timeout_messages(self) -> List[Message_Sending]:
"""获取所有超时的Message_Sending对象思考时间超过30秒按thinking_start_time排序""" """获取所有超时的Message_Sending对象思考时间超过30秒按thinking_start_time排序"""
current_time = time.time() current_time = time.time()
timeout_messages = [] timeout_messages = []
for msg in self.messages: for msg in self.messages:
if isinstance(msg, Message_Sending): if isinstance(msg, Message_Sending):
if current_time - msg.thinking_start_time > self.thinking_timeout: if current_time - msg.thinking_start_time > self.thinking_timeout:
timeout_messages.append(msg) timeout_messages.append(msg)
# 按thinking_start_time排序时间早的在前面 # 按thinking_start_time排序时间早的在前面
timeout_messages.sort(key=lambda x: x.thinking_start_time) timeout_messages.sort(key=lambda x: x.thinking_start_time)
return timeout_messages return timeout_messages
def get_earliest_message(self) -> Optional[Union[Message_Thinking, Message_Sending]]: def get_earliest_message(
self,
) -> Optional[Union[Message_Thinking, Message_Sending]]:
"""获取thinking_start_time最早的消息对象""" """获取thinking_start_time最早的消息对象"""
if not self.messages: if not self.messages:
return None return None
earliest_time = float('inf') earliest_time = float("inf")
earliest_message = None earliest_message = None
for msg in self.messages: for msg in self.messages:
msg_time = msg.thinking_start_time msg_time = msg.thinking_start_time
if msg_time < earliest_time: if msg_time < earliest_time:
earliest_time = msg_time earliest_time = msg_time
earliest_message = msg earliest_message = msg
return earliest_message return earliest_message
def add_message(self, message: Union[Message_Thinking, Message_Sending]) -> None: def add_message(self, message: Union[Message_Thinking, Message_Sending]) -> None:
"""添加消息到队列""" """添加消息到队列"""
# print(f"\033[1;32m[添加消息]\033[0m 添加消息到对应群") # print(f"\033[1;32m[添加消息]\033[0m 添加消息到对应群")
@ -110,7 +130,7 @@ class MessageContainer:
self.messages.append(single_message) self.messages.append(single_message)
else: else:
self.messages.append(message) self.messages.append(message)
def remove_message(self, message: Union[Message_Thinking, Message_Sending]) -> bool: def remove_message(self, message: Union[Message_Thinking, Message_Sending]) -> bool:
"""移除消息如果消息存在则返回True否则返回False""" """移除消息如果消息存在则返回True否则返回False"""
try: try:
@ -121,95 +141,137 @@ class MessageContainer:
except Exception as e: except Exception as e:
print(f"\033[1;31m[错误]\033[0m 移除消息时发生错误: {e}") print(f"\033[1;31m[错误]\033[0m 移除消息时发生错误: {e}")
return False return False
def has_messages(self) -> bool: def has_messages(self) -> bool:
"""检查是否有待发送的消息""" """检查是否有待发送的消息"""
return bool(self.messages) return bool(self.messages)
def get_all_messages(self) -> List[Union[Message, Message_Thinking]]: def get_all_messages(self) -> List[Union[Message, Message_Thinking]]:
"""获取所有消息""" """获取所有消息"""
return list(self.messages) return list(self.messages)
class MessageManager: class MessageManager:
"""管理所有群的消息容器""" """管理所有群的消息容器"""
def __init__(self): def __init__(self):
self.containers: Dict[int, MessageContainer] = {} self.containers: Dict[int, MessageContainer] = {}
self.storage = MessageStorage() self.storage = MessageStorage()
self._running = True self._running = True
def get_container(self, group_id: int) -> MessageContainer: def get_container(self, group_id: int) -> MessageContainer:
"""获取或创建群的消息容器""" """获取或创建群的消息容器"""
if group_id not in self.containers: if group_id not in self.containers:
self.containers[group_id] = MessageContainer(group_id) self.containers[group_id] = MessageContainer(group_id)
return self.containers[group_id] return self.containers[group_id]
def add_message(self, message: Union[Message_Thinking, Message_Sending, MessageSet]) -> None: def add_message(
self, message: Union[Message_Thinking, Message_Sending, MessageSet]
) -> None:
container = self.get_container(message.group_id) container = self.get_container(message.group_id)
container.add_message(message) container.add_message(message)
async def process_group_messages(self, group_id: int): async def process_group_messages(self, group_id: int):
"""处理群消息""" """处理群消息"""
# if int(time.time() / 3) == time.time() / 3: # if int(time.time() / 3) == time.time() / 3:
# print(f"\033[1;34m[调试]\033[0m 开始处理群{group_id}的消息") # print(f"\033[1;34m[调试]\033[0m 开始处理群{group_id}的消息")
container = self.get_container(group_id) container = self.get_container(group_id)
if container.has_messages(): if container.has_messages():
#最早的对象,可能是思考消息,也可能是发送消息 # 最早的对象,可能是思考消息,也可能是发送消息
message_earliest = container.get_earliest_message() #一个message_thinking or message_sending message_earliest = (
container.get_earliest_message()
#如果是思考消息 ) # 一个message_thinking or message_sending
# 如果是思考消息
if isinstance(message_earliest, Message_Thinking): if isinstance(message_earliest, Message_Thinking):
#优先等待这条消息 # 优先等待这条消息
message_earliest.update_thinking_time() message_earliest.update_thinking_time()
thinking_time = message_earliest.thinking_time thinking_time = message_earliest.thinking_time
print(f"\033[1;34m[调试]\033[0m 消息正在思考中,已思考{int(thinking_time)}\033[K\r", end='', flush=True) print(
f"\033[1;34m[调试]\033[0m 消息正在思考中,已思考{int(thinking_time)}\033[K\r",
end="",
flush=True,
)
# 检查是否超时 # 检查是否超时
if thinking_time > global_config.thinking_timeout: if thinking_time > global_config.thinking_timeout:
print(f"\033[1;33m[警告]\033[0m 消息思考超时({thinking_time}秒),移除该消息") print(
f"\033[1;33m[警告]\033[0m 消息思考超时({thinking_time}秒),移除该消息"
)
container.remove_message(message_earliest) container.remove_message(message_earliest)
else:# 如果不是message_thinking就只能是message_sending else: # 如果不是message_thinking就只能是message_sending
print(f"\033[1;34m[调试]\033[0m 消息'{message_earliest.processed_plain_text}'正在发送中") print(
#直接发,等什么呢 f"\033[1;34m[调试]\033[0m 消息'{message_earliest.processed_plain_text}'正在发送中"
if message_earliest.is_head and message_earliest.update_thinking_time() >30: )
await message_sender.send_group_message(group_id, message_earliest.processed_plain_text, auto_escape=False, reply_message_id=message_earliest.reply_message_id) # 直接发,等什么呢
if group_id == 0:
await message_sender.send_private_message(
user_id=message_earliest.reply_user_id,
send_text=message_earliest.processed_plain_text,
auto_escape=False,
)
else: else:
await message_sender.send_group_message(group_id, message_earliest.processed_plain_text, auto_escape=False) if (
#移除消息 message_earliest.is_head
and message_earliest.update_thinking_time() > 30
):
await message_sender.send_group_message(
group_id,
message_earliest.processed_plain_text,
auto_escape=False,
reply_message_id=message_earliest.reply_message_id,
)
else:
await message_sender.send_group_message(
group_id,
message_earliest.processed_plain_text,
auto_escape=False,
)
# 移除消息
if message_earliest.is_emoji: if message_earliest.is_emoji:
message_earliest.processed_plain_text = "[表情包]" message_earliest.processed_plain_text = "[表情包]"
await self.storage.store_message(message_earliest, None) await self.storage.store_message(message_earliest, None)
container.remove_message(message_earliest) container.remove_message(message_earliest)
#获取并处理超时消息 # 获取并处理超时消息
message_timeout = container.get_timeout_messages() #也许是一堆message_sending message_timeout = (
container.get_timeout_messages()
) # 也许是一堆message_sending
if message_timeout: if message_timeout:
print(f"\033[1;34m[调试]\033[0m 发现{len(message_timeout)}条超时消息") print(f"\033[1;34m[调试]\033[0m 发现{len(message_timeout)}条超时消息")
for msg in message_timeout: for msg in message_timeout:
if msg == message_earliest: if msg == message_earliest:
continue # 跳过已经处理过的消息 continue # 跳过已经处理过的消息
try: try:
#发送 # 发送
if msg.is_head and msg.update_thinking_time() >30: if msg.is_head and msg.update_thinking_time() > 30:
await message_sender.send_group_message(group_id, msg.processed_plain_text, auto_escape=False, reply_message_id=msg.reply_message_id) await message_sender.send_group_message(
group_id,
msg.processed_plain_text,
auto_escape=False,
reply_message_id=msg.reply_message_id,
)
else: else:
await message_sender.send_group_message(group_id, msg.processed_plain_text, auto_escape=False) await message_sender.send_group_message(
group_id, msg.processed_plain_text, auto_escape=False
)
#如果是表情包,则替换为"[表情包]"
# 如果是表情包,则替换为"[表情包]"
if msg.is_emoji: if msg.is_emoji:
msg.processed_plain_text = "[表情包]" msg.processed_plain_text = "[表情包]"
await self.storage.store_message(msg, None) await self.storage.store_message(msg, None)
# 安全地移除消息 # 安全地移除消息
if not container.remove_message(msg): if not container.remove_message(msg):
print("\033[1;33m[警告]\033[0m 尝试删除不存在的消息") print("\033[1;33m[警告]\033[0m 尝试删除不存在的消息")
except Exception as e: except Exception as e:
print(f"\033[1;31m[错误]\033[0m 处理超时消息时发生错误: {e}") print(f"\033[1;31m[错误]\033[0m 处理超时消息时发生错误: {e}")
continue continue
async def start_processor(self): async def start_processor(self):
"""启动消息处理器""" """启动消息处理器"""
while self._running: while self._running:
@ -217,9 +279,10 @@ class MessageManager:
tasks = [] tasks = []
for group_id in self.containers.keys(): for group_id in self.containers.keys():
tasks.append(self.process_group_messages(group_id)) tasks.append(self.process_group_messages(group_id))
await asyncio.gather(*tasks) await asyncio.gather(*tasks)
# 创建全局消息管理器实例 # 创建全局消息管理器实例
message_manager = MessageManager() message_manager = MessageManager()
# 创建全局发送器实例 # 创建全局发送器实例

View File

@ -82,6 +82,7 @@ enable_kuuki_read = true # 是否启用读空气功能
talk_allowed = [ talk_allowed = [
123, 123,
123, 123,
#0, 如果填0则开启私聊功能
] #可以回复消息的群 ] #可以回复消息的群
talk_frequency_down = [] #降低回复频率的群 talk_frequency_down = [] #降低回复频率的群
ban_user_id = [] #禁止回复消息的QQ号 ban_user_id = [] #禁止回复消息的QQ号