mirror of https://github.com/Mai-with-u/MaiBot.git
添加私聊功能,通过在群白名单中添加群号"0"开启
parent
0f5f1c920d
commit
77311a1125
|
|
@ -5,7 +5,7 @@ import time
|
|||
|
||||
from loguru import logger
|
||||
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.typing import T_State
|
||||
|
||||
|
|
@ -103,7 +103,7 @@ async def _(bot: Bot):
|
|||
print("\033[1;38;5;208m-----------开始偷表情包!-----------\033[0m")
|
||||
|
||||
@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)
|
||||
|
||||
# 添加build_memory定时任务
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import time
|
|||
from random import random
|
||||
|
||||
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 ..moods.moods import MoodManager # 导入情绪管理器
|
||||
|
|
@ -31,37 +31,60 @@ class ChatBot:
|
|||
self._started = False
|
||||
self.mood_manager = MoodManager.get_instance() # 获取情绪管理器单例
|
||||
self.mood_manager.start_mood_update() # 启动情绪更新
|
||||
|
||||
|
||||
self.emoji_chance = 0.2 # 发送表情包的基础概率
|
||||
# self.message_streams = MessageStreamContainer()
|
||||
|
||||
|
||||
async def _ensure_started(self):
|
||||
"""确保所有任务已启动"""
|
||||
if not self._started:
|
||||
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:
|
||||
return
|
||||
self.bot = bot # 更新 bot 实例
|
||||
|
||||
|
||||
if event.user_id in global_config.ban_user_id:
|
||||
return
|
||||
|
||||
group_info = await bot.get_group_info(group_id=event.group_id)
|
||||
sender_info = await bot.get_group_member_info(group_id=event.group_id, user_id=event.user_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)
|
||||
|
||||
if isinstance(event, GroupMessageEvent):
|
||||
group_info = await bot.get_group_info(
|
||||
group_id=event.group_id, no_cache=True
|
||||
)
|
||||
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(
|
||||
group_id=event.group_id,
|
||||
user_id=event.user_id,
|
||||
message_id=event.message_id,
|
||||
user_cardname=sender_info['card'],
|
||||
raw_message=str(event.original_message),
|
||||
user_cardname=sender_info["card"],
|
||||
raw_message=str(event.original_message),
|
||||
plain_text=event.get_plaintext(),
|
||||
reply_message=event.reply,
|
||||
)
|
||||
|
|
@ -70,53 +93,57 @@ class ChatBot:
|
|||
# 过滤词
|
||||
for word in global_config.ban_words:
|
||||
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")
|
||||
return
|
||||
|
||||
|
||||
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 = ''
|
||||
topic = ""
|
||||
interested_rate = 0
|
||||
interested_rate = await hippocampus.memory_activate_value(message.processed_plain_text)/100
|
||||
print(f"\033[1;32m[记忆激活]\033[0m 对{message.processed_plain_text}的激活度:---------------------------------------{interested_rate}\n")
|
||||
interested_rate = (
|
||||
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}")
|
||||
|
||||
|
||||
await self.storage.store_message(message, topic[0] if topic else None)
|
||||
|
||||
is_mentioned = is_mentioned_bot_in_txt(message.processed_plain_text)
|
||||
reply_probability = willing_manager.change_reply_willing_received(
|
||||
event.group_id,
|
||||
event.group_id,
|
||||
topic[0] if topic else None,
|
||||
is_mentioned,
|
||||
global_config,
|
||||
event.user_id,
|
||||
message.is_emoji,
|
||||
interested_rate
|
||||
interested_rate,
|
||||
)
|
||||
current_willing = willing_manager.get_willing(event.group_id)
|
||||
|
||||
|
||||
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")
|
||||
|
||||
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"
|
||||
)
|
||||
|
||||
response = ""
|
||||
|
||||
|
||||
if random() < reply_probability:
|
||||
|
||||
|
||||
|
||||
tinking_time_point = round(time.time(), 2)
|
||||
think_id = 'mt' + str(tinking_time_point)
|
||||
thinking_message = Message_Thinking(message=message,message_id=think_id)
|
||||
|
||||
think_id = "mt" + str(tinking_time_point)
|
||||
thinking_message = Message_Thinking(message=message, message_id=think_id)
|
||||
|
||||
message_manager.add_message(thinking_message)
|
||||
|
||||
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:
|
||||
container = message_manager.get_container(event.group_id)
|
||||
thinking_message = None
|
||||
|
|
@ -127,27 +154,29 @@ class ChatBot:
|
|||
container.messages.remove(msg)
|
||||
# print(f"\033[1;32m[思考消息删除]\033[0m 已找到思考消息对象,开始删除")
|
||||
break
|
||||
|
||||
|
||||
# 如果找不到思考消息,直接返回
|
||||
if not thinking_message:
|
||||
print(f"\033[1;33m[警告]\033[0m 未找到对应的思考消息,可能已超时被移除")
|
||||
return
|
||||
|
||||
#记录开始思考的时间,避免从思考到回复的时间太久
|
||||
|
||||
# 记录开始思考的时间,避免从思考到回复的时间太久
|
||||
thinking_start_time = thinking_message.thinking_start_time
|
||||
message_set = MessageSet(event.group_id, global_config.BOT_QQ, think_id) # 发送消息的id和产生发送消息的message_thinking是一致的
|
||||
#计算打字时间,1是为了模拟打字,2是避免多条回复乱序
|
||||
message_set = MessageSet(
|
||||
event.group_id, global_config.BOT_QQ, think_id
|
||||
) # 发送消息的id和产生发送消息的message_thinking是一致的
|
||||
# 计算打字时间,1是为了模拟打字,2是避免多条回复乱序
|
||||
accu_typing_time = 0
|
||||
|
||||
|
||||
# print(f"\033[1;32m[开始回复]\033[0m 开始将回复1载入发送容器")
|
||||
mark_head = False
|
||||
for msg in response:
|
||||
# print(f"\033[1;32m[回复内容]\033[0m {msg}")
|
||||
#通过时间改变时间戳
|
||||
# 通过时间改变时间戳
|
||||
typing_time = calculate_typing_time(msg)
|
||||
accu_typing_time += typing_time
|
||||
timepoint = tinking_time_point + accu_typing_time
|
||||
|
||||
|
||||
bot_message = Message_Sending(
|
||||
group_id=event.group_id,
|
||||
user_id=global_config.BOT_QQ,
|
||||
|
|
@ -157,36 +186,38 @@ class ChatBot:
|
|||
processed_plain_text=msg,
|
||||
user_nickname=global_config.BOT_NICKNAME,
|
||||
group_name=message.group_name,
|
||||
time=timepoint, #记录了回复生成的时间
|
||||
thinking_start_time=thinking_start_time, #记录了思考开始的时间
|
||||
reply_message_id=message.message_id
|
||||
time=timepoint, # 记录了回复生成的时间
|
||||
thinking_start_time=thinking_start_time, # 记录了思考开始的时间
|
||||
reply_message_id=message.message_id,
|
||||
reply_user_id=message.user_id,
|
||||
|
||||
)
|
||||
await bot_message.initialize()
|
||||
if not mark_head:
|
||||
bot_message.is_head = True
|
||||
mark_head = True
|
||||
message_set.add_message(bot_message)
|
||||
|
||||
#message_set 可以直接加入 message_manager
|
||||
|
||||
# message_set 可以直接加入 message_manager
|
||||
# print(f"\033[1;32m[回复]\033[0m 将回复载入发送容器")
|
||||
message_manager.add_message(message_set)
|
||||
|
||||
|
||||
bot_response_time = tinking_time_point
|
||||
|
||||
if random() < global_config.emoji_chance:
|
||||
emoji_raw = await emoji_manager.get_emoji_for_text(response)
|
||||
|
||||
|
||||
# 检查是否 <没有找到> emoji
|
||||
if emoji_raw != None:
|
||||
emoji_path,discription = emoji_raw
|
||||
emoji_path, discription = emoji_raw
|
||||
|
||||
emoji_cq = CQCode.create_emoji_cq(emoji_path)
|
||||
|
||||
|
||||
if random() < 0.5:
|
||||
bot_response_time = tinking_time_point - 1
|
||||
else:
|
||||
bot_response_time = bot_response_time + 1
|
||||
|
||||
|
||||
bot_message = Message_Sending(
|
||||
group_id=event.group_id,
|
||||
user_id=global_config.BOT_QQ,
|
||||
|
|
@ -207,20 +238,25 @@ class ChatBot:
|
|||
message_manager.add_message(bot_message)
|
||||
emotion = await self.gpt._get_emotion_tags(raw_content)
|
||||
print(f"为 '{response}' 获取到的情感标签为:{emotion}")
|
||||
valuedict={
|
||||
'happy': 0.5,
|
||||
'angry': -1,
|
||||
'sad': -0.5,
|
||||
'surprised': 0.2,
|
||||
'disgusted': -1.5,
|
||||
'fearful': -0.7,
|
||||
'neutral': 0.1
|
||||
valuedict = {
|
||||
"happy": 0.5,
|
||||
"angry": -1,
|
||||
"sad": -0.5,
|
||||
"surprised": 0.2,
|
||||
"disgusted": -1.5,
|
||||
"fearful": -0.7,
|
||||
"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)
|
||||
|
||||
|
||||
# 创建全局ChatBot实例
|
||||
chat_bot = ChatBot()
|
||||
chat_bot = ChatBot()
|
||||
|
|
|
|||
|
|
@ -8,19 +8,19 @@ from .cq_code import CQCode, cq_code_tool
|
|||
from .utils_cq import parse_cq_code
|
||||
from .utils_user import get_groupname, get_user_cardname, get_user_nickname
|
||||
|
||||
Message = ForwardRef('Message') # 添加这行
|
||||
Message = ForwardRef("Message") # 添加这行
|
||||
# 禁用SSL警告
|
||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||
|
||||
#这个类是消息数据类,用于存储和管理消息数据。
|
||||
#它定义了消息的属性,包括群组ID、用户ID、消息ID、原始消息内容、纯文本内容和时间戳。
|
||||
#它还定义了两个辅助属性:keywords用于提取消息的关键词,is_plain_text用于判断消息是否为纯文本。
|
||||
|
||||
# 这个类是消息数据类,用于存储和管理消息数据。
|
||||
# 它定义了消息的属性,包括群组ID、用户ID、消息ID、原始消息内容、纯文本内容和时间戳。
|
||||
# 它还定义了两个辅助属性:keywords用于提取消息的关键词,is_plain_text用于判断消息是否为纯文本。
|
||||
|
||||
|
||||
@dataclass
|
||||
class Message:
|
||||
"""消息数据类"""
|
||||
|
||||
message_id: int = None
|
||||
time: float = None
|
||||
|
||||
|
|
@ -59,11 +59,12 @@ class Message:
|
|||
|
||||
# 消息解析
|
||||
if self.raw_message:
|
||||
if not isinstance(self,Message_Sending):
|
||||
self.message_segments = await self.parse_message_segments(self.raw_message)
|
||||
self.processed_plain_text = ' '.join(
|
||||
seg.translated_plain_text
|
||||
for seg in self.message_segments
|
||||
if not isinstance(self, Message_Sending):
|
||||
self.message_segments = await self.parse_message_segments(
|
||||
self.raw_message
|
||||
)
|
||||
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
|
||||
else f"{self.user_nickname or f'用户{self.user_id}'}"
|
||||
)
|
||||
if isinstance(self,Message_Sending) and self.is_emoji:
|
||||
self.detailed_plain_text = f"[{time_str}] {name}: {self.detailed_plain_text}\n"
|
||||
if isinstance(self, Message_Sending) and self.is_emoji:
|
||||
self.detailed_plain_text = (
|
||||
f"[{time_str}] {name}: {self.detailed_plain_text}\n"
|
||||
)
|
||||
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
|
||||
|
||||
|
||||
async def parse_message_segments(self, message: str) -> List[CQCode]:
|
||||
"""
|
||||
将消息解析为片段列表,包括纯文本和CQ码
|
||||
|
|
@ -92,12 +97,12 @@ class Message:
|
|||
# print(f"\033[1;34m[调试信息]\033[0m 正在处理消息: {message}")
|
||||
cq_code_dict_list = []
|
||||
trans_list = []
|
||||
|
||||
|
||||
start = 0
|
||||
while True:
|
||||
# 查找下一个CQ码的开始位置
|
||||
cq_start = message.find('[CQ:', start)
|
||||
#如果没有cq码,直接返回文本内容
|
||||
cq_start = message.find("[CQ:", start)
|
||||
# 如果没有cq码,直接返回文本内容
|
||||
if cq_start == -1:
|
||||
# 如果没有找到更多CQ码,添加剩余文本
|
||||
if start < len(message):
|
||||
|
|
@ -111,85 +116,93 @@ class Message:
|
|||
if text: # 只添加非空文本
|
||||
cq_code_dict_list.append(parse_cq_code(text))
|
||||
# 查找CQ码的结束位置
|
||||
cq_end = message.find(']', cq_start)
|
||||
cq_end = message.find("]", cq_start)
|
||||
if cq_end == -1:
|
||||
# CQ码未闭合,作为普通文本处理
|
||||
text = message[cq_start:].strip()
|
||||
if text:
|
||||
cq_code_dict_list.append(parse_cq_code(text))
|
||||
break
|
||||
cq_code = message[cq_start:cq_end + 1]
|
||||
|
||||
#将cq_code解析成字典
|
||||
cq_code = message[cq_start : cq_end + 1]
|
||||
|
||||
# 将cq_code解析成字典
|
||||
cq_code_dict_list.append(parse_cq_code(cq_code))
|
||||
# 更新start位置到当前CQ码之后
|
||||
start = cq_end + 1
|
||||
|
||||
|
||||
# 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.has_emoji_emoji = True
|
||||
else:
|
||||
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
|
||||
break
|
||||
|
||||
|
||||
#翻译作为字典的CQ码
|
||||
|
||||
# 翻译作为字典的CQ码
|
||||
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)
|
||||
trans_list.append(message_obj)
|
||||
message_obj = await cq_code_tool.cq_from_dict_to_class(
|
||||
_code_item, reply=self.reply_message
|
||||
)
|
||||
trans_list.append(message_obj)
|
||||
return trans_list
|
||||
|
||||
|
||||
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.user_id = message.user_id
|
||||
self.user_nickname = message.user_nickname
|
||||
self.user_cardname = message.user_cardname
|
||||
self.group_name = message.group_name
|
||||
|
||||
|
||||
self.message_id = message_id
|
||||
|
||||
|
||||
# 思考状态相关属性
|
||||
self.thinking_start_time = int(time.time())
|
||||
self.thinking_time = 0
|
||||
self.interupt=False
|
||||
|
||||
self.interupt = False
|
||||
|
||||
def update_thinking_time(self):
|
||||
self.thinking_time = round(time.time(), 2) - self.thinking_start_time
|
||||
|
||||
|
||||
|
||||
@dataclass
|
||||
class Message_Sending(Message):
|
||||
"""发送中的消息类"""
|
||||
|
||||
thinking_start_time: float = None # 思考开始时间
|
||||
thinking_time: float = None # 思考时间
|
||||
|
||||
|
||||
reply_message_id: int = None # 存储 回复的 源消息ID
|
||||
|
||||
reply_user_id: int = None # 私聊中需要回复的用户ID
|
||||
|
||||
is_head: bool = False # 是否是头部消息
|
||||
|
||||
|
||||
def update_thinking_time(self):
|
||||
self.thinking_time = round(time.time(), 2) - self.thinking_start_time
|
||||
return self.thinking_time
|
||||
|
||||
|
||||
|
||||
class MessageSet:
|
||||
"""消息集合类,可以存储多个发送消息"""
|
||||
|
||||
def __init__(self, group_id: int, user_id: int, message_id: str):
|
||||
self.group_id = group_id
|
||||
self.user_id = user_id
|
||||
self.message_id = message_id
|
||||
self.messages: List[Message_Sending] = [] # 修改类型标注
|
||||
self.time = round(time.time(), 2)
|
||||
|
||||
|
||||
def add_message(self, message: Message_Sending) -> None:
|
||||
"""添加消息到集合,只接受Message_Sending类型"""
|
||||
if not isinstance(message, Message_Sending):
|
||||
|
|
@ -197,18 +210,18 @@ class MessageSet:
|
|||
self.messages.append(message)
|
||||
# 按时间排序
|
||||
self.messages.sort(key=lambda x: x.time)
|
||||
|
||||
|
||||
def get_message_by_index(self, index: int) -> Optional[Message_Sending]:
|
||||
"""通过索引获取消息"""
|
||||
if 0 <= index < len(self.messages):
|
||||
return self.messages[index]
|
||||
return None
|
||||
|
||||
|
||||
def get_message_by_time(self, target_time: float) -> Optional[Message_Sending]:
|
||||
"""获取最接近指定时间的消息"""
|
||||
if not self.messages:
|
||||
return None
|
||||
|
||||
|
||||
# 使用二分查找找到最接近的消息
|
||||
left, right = 0, len(self.messages) - 1
|
||||
while left < right:
|
||||
|
|
@ -217,26 +230,22 @@ class MessageSet:
|
|||
left = mid + 1
|
||||
else:
|
||||
right = mid
|
||||
|
||||
|
||||
return self.messages[left]
|
||||
|
||||
|
||||
|
||||
def clear_messages(self) -> None:
|
||||
"""清空所有消息"""
|
||||
self.messages.clear()
|
||||
|
||||
|
||||
def remove_message(self, message: Message_Sending) -> bool:
|
||||
"""移除指定消息"""
|
||||
if message in self.messages:
|
||||
self.messages.remove(message)
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"MessageSet(id={self.message_id}, count={len(self.messages)})"
|
||||
|
||||
|
||||
def __len__(self) -> int:
|
||||
return len(self.messages)
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -10,98 +10,118 @@ from .storage import MessageStorage
|
|||
from .utils import calculate_typing_time
|
||||
from .config import global_config
|
||||
|
||||
from loguru import logger
|
||||
|
||||
|
||||
class Message_Sender:
|
||||
"""发送器"""
|
||||
|
||||
def __init__(self):
|
||||
self.message_interval = (0.5, 1) # 消息间隔时间范围(秒)
|
||||
self.last_send_time = 0
|
||||
self._current_bot = None
|
||||
|
||||
|
||||
def set_bot(self, bot: Bot):
|
||||
"""设置当前bot实例"""
|
||||
self._current_bot = bot
|
||||
|
||||
|
||||
async def send_group_message(
|
||||
self,
|
||||
group_id: int,
|
||||
send_text: str,
|
||||
self,
|
||||
group_id: int,
|
||||
send_text: str,
|
||||
auto_escape: bool = False,
|
||||
reply_message_id: int = None,
|
||||
at_user_id: int = None
|
||||
at_user_id: int = None,
|
||||
) -> None:
|
||||
|
||||
if not self._current_bot:
|
||||
raise RuntimeError("Bot未设置,请先调用set_bot方法设置bot实例")
|
||||
|
||||
|
||||
message = send_text
|
||||
|
||||
|
||||
# 如果需要回复
|
||||
if reply_message_id:
|
||||
reply_cq = cq_code_tool.create_reply_cq(reply_message_id)
|
||||
message = reply_cq + message
|
||||
|
||||
|
||||
# 如果需要at
|
||||
# if at_user_id:
|
||||
# at_cq = cq_code_tool.create_at_cq(at_user_id)
|
||||
# message = at_cq + " " + message
|
||||
|
||||
|
||||
|
||||
typing_time = calculate_typing_time(message)
|
||||
if typing_time > 10:
|
||||
typing_time = 10
|
||||
await asyncio.sleep(typing_time)
|
||||
|
||||
|
||||
# 发送消息
|
||||
try:
|
||||
await self._current_bot.send_group_msg(
|
||||
group_id=group_id,
|
||||
message=message,
|
||||
auto_escape=auto_escape
|
||||
group_id=group_id, message=message, auto_escape=auto_escape
|
||||
)
|
||||
print(f"\033[1;34m[调试]\033[0m 发送消息{message}成功")
|
||||
except Exception as e:
|
||||
print(f"发生错误 {e}")
|
||||
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:
|
||||
"""单个群的发送/思考消息容器"""
|
||||
|
||||
def __init__(self, group_id: int, max_size: int = 100):
|
||||
self.group_id = group_id
|
||||
self.max_size = max_size
|
||||
self.messages = []
|
||||
self.last_send_time = 0
|
||||
self.thinking_timeout = 20 # 思考超时时间(秒)
|
||||
|
||||
|
||||
def get_timeout_messages(self) -> List[Message_Sending]:
|
||||
"""获取所有超时的Message_Sending对象(思考时间超过30秒),按thinking_start_time排序"""
|
||||
current_time = time.time()
|
||||
timeout_messages = []
|
||||
|
||||
|
||||
for msg in self.messages:
|
||||
if isinstance(msg, Message_Sending):
|
||||
if current_time - msg.thinking_start_time > self.thinking_timeout:
|
||||
timeout_messages.append(msg)
|
||||
|
||||
|
||||
# 按thinking_start_time排序,时间早的在前面
|
||||
timeout_messages.sort(key=lambda x: x.thinking_start_time)
|
||||
|
||||
|
||||
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最早的消息对象"""
|
||||
if not self.messages:
|
||||
return None
|
||||
earliest_time = float('inf')
|
||||
earliest_time = float("inf")
|
||||
earliest_message = None
|
||||
for msg in self.messages:
|
||||
for msg in self.messages:
|
||||
msg_time = msg.thinking_start_time
|
||||
if msg_time < earliest_time:
|
||||
earliest_time = msg_time
|
||||
earliest_message = msg
|
||||
earliest_message = msg
|
||||
return earliest_message
|
||||
|
||||
|
||||
def add_message(self, message: Union[Message_Thinking, Message_Sending]) -> None:
|
||||
"""添加消息到队列"""
|
||||
# print(f"\033[1;32m[添加消息]\033[0m 添加消息到对应群")
|
||||
|
|
@ -110,7 +130,7 @@ class MessageContainer:
|
|||
self.messages.append(single_message)
|
||||
else:
|
||||
self.messages.append(message)
|
||||
|
||||
|
||||
def remove_message(self, message: Union[Message_Thinking, Message_Sending]) -> bool:
|
||||
"""移除消息,如果消息存在则返回True,否则返回False"""
|
||||
try:
|
||||
|
|
@ -121,95 +141,137 @@ class MessageContainer:
|
|||
except Exception as e:
|
||||
print(f"\033[1;31m[错误]\033[0m 移除消息时发生错误: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def has_messages(self) -> bool:
|
||||
"""检查是否有待发送的消息"""
|
||||
return bool(self.messages)
|
||||
|
||||
|
||||
def get_all_messages(self) -> List[Union[Message, Message_Thinking]]:
|
||||
"""获取所有消息"""
|
||||
return list(self.messages)
|
||||
|
||||
|
||||
|
||||
class MessageManager:
|
||||
"""管理所有群的消息容器"""
|
||||
|
||||
def __init__(self):
|
||||
self.containers: Dict[int, MessageContainer] = {}
|
||||
self.storage = MessageStorage()
|
||||
self._running = True
|
||||
|
||||
|
||||
def get_container(self, group_id: int) -> MessageContainer:
|
||||
"""获取或创建群的消息容器"""
|
||||
if group_id not in self.containers:
|
||||
self.containers[group_id] = MessageContainer(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.add_message(message)
|
||||
|
||||
|
||||
async def process_group_messages(self, group_id: int):
|
||||
"""处理群消息"""
|
||||
# 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)
|
||||
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):
|
||||
#优先等待这条消息
|
||||
# 优先等待这条消息
|
||||
message_earliest.update_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:
|
||||
print(f"\033[1;33m[警告]\033[0m 消息思考超时({thinking_time}秒),移除该消息")
|
||||
print(
|
||||
f"\033[1;33m[警告]\033[0m 消息思考超时({thinking_time}秒),移除该消息"
|
||||
)
|
||||
container.remove_message(message_earliest)
|
||||
else:# 如果不是message_thinking就只能是message_sending
|
||||
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)
|
||||
else: # 如果不是message_thinking就只能是message_sending
|
||||
print(
|
||||
f"\033[1;34m[调试]\033[0m 消息'{message_earliest.processed_plain_text}'正在发送中"
|
||||
)
|
||||
# 直接发,等什么呢
|
||||
|
||||
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:
|
||||
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:
|
||||
message_earliest.processed_plain_text = "[表情包]"
|
||||
await self.storage.store_message(message_earliest, None)
|
||||
|
||||
|
||||
container.remove_message(message_earliest)
|
||||
|
||||
#获取并处理超时消息
|
||||
message_timeout = container.get_timeout_messages() #也许是一堆message_sending
|
||||
|
||||
# 获取并处理超时消息
|
||||
message_timeout = (
|
||||
container.get_timeout_messages()
|
||||
) # 也许是一堆message_sending
|
||||
if message_timeout:
|
||||
print(f"\033[1;34m[调试]\033[0m 发现{len(message_timeout)}条超时消息")
|
||||
for msg in message_timeout:
|
||||
if msg == message_earliest:
|
||||
continue # 跳过已经处理过的消息
|
||||
|
||||
|
||||
try:
|
||||
#发送
|
||||
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)
|
||||
# 发送
|
||||
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,
|
||||
)
|
||||
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:
|
||||
msg.processed_plain_text = "[表情包]"
|
||||
await self.storage.store_message(msg, None)
|
||||
|
||||
|
||||
# 安全地移除消息
|
||||
if not container.remove_message(msg):
|
||||
print("\033[1;33m[警告]\033[0m 尝试删除不存在的消息")
|
||||
except Exception as e:
|
||||
print(f"\033[1;31m[错误]\033[0m 处理超时消息时发生错误: {e}")
|
||||
continue
|
||||
|
||||
|
||||
async def start_processor(self):
|
||||
"""启动消息处理器"""
|
||||
while self._running:
|
||||
|
|
@ -217,9 +279,10 @@ class MessageManager:
|
|||
tasks = []
|
||||
for group_id in self.containers.keys():
|
||||
tasks.append(self.process_group_messages(group_id))
|
||||
|
||||
|
||||
await asyncio.gather(*tasks)
|
||||
|
||||
|
||||
# 创建全局消息管理器实例
|
||||
message_manager = MessageManager()
|
||||
# 创建全局发送器实例
|
||||
|
|
|
|||
|
|
@ -82,6 +82,7 @@ enable_kuuki_read = true # 是否启用读空气功能
|
|||
talk_allowed = [
|
||||
123,
|
||||
123,
|
||||
#0, 如果填0则开启私聊功能
|
||||
] #可以回复消息的群
|
||||
talk_frequency_down = [] #降低回复频率的群
|
||||
ban_user_id = [] #禁止回复消息的QQ号
|
||||
|
|
|
|||
Loading…
Reference in New Issue