This commit is contained in:
UnCLAS-Prommer
2025-03-13 16:01:16 +08:00
26 changed files with 764 additions and 275 deletions

View File

@@ -3,8 +3,9 @@ import time
import os
from loguru import logger
from nonebot import get_driver, on_message, require
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message, MessageSegment, MessageEvent
from nonebot import get_driver, on_message, on_notice, require
from nonebot.rule import to_me
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message, MessageSegment, MessageEvent, NoticeEvent
from nonebot.typing import T_State
from ..moods.moods import MoodManager # 导入情绪管理器
@@ -39,6 +40,8 @@ logger.debug(f"正在唤醒{global_config.BOT_NICKNAME}......")
chat_bot = ChatBot()
# 注册消息处理器
msg_in = on_message(priority=5)
# 注册和bot相关的通知处理器
notice_matcher = on_notice(priority=1)
# 创建定时任务
scheduler = require("nonebot_plugin_apscheduler").scheduler
@@ -95,6 +98,12 @@ async def _(bot: Bot, event: MessageEvent, state: T_State):
await chat_bot.handle_message(event, bot)
@notice_matcher.handle()
async def _(bot: Bot, event: NoticeEvent, state: T_State):
logger.debug(f"收到通知:{event}")
await chat_bot.handle_notice(event, bot)
# 添加build_memory定时任务
@scheduler.scheduled_job("interval", seconds=global_config.build_memory_interval, id="build_memory")
async def build_memory_task():

View File

@@ -7,6 +7,8 @@ from nonebot.adapters.onebot.v11 import (
GroupMessageEvent,
MessageEvent,
PrivateMessageEvent,
NoticeEvent,
PokeNotifyEvent,
)
from ..memory_system.memory import hippocampus
@@ -25,6 +27,7 @@ from .relationship_manager import relationship_manager
from .storage import MessageStorage
from .utils import calculate_typing_time, is_mentioned_bot_in_message
from .utils_image import image_path_to_base64
from .utils_user import get_user_nickname, get_user_cardname, get_groupname
from .willing_manager import willing_manager # 导入意愿管理器
from .message_base import UserInfo, GroupInfo, Seg
@@ -46,6 +49,69 @@ class ChatBot:
if not self._started:
self._started = True
async def handle_notice(self, event: NoticeEvent, bot: Bot) -> None:
"""处理收到的通知"""
# 戳一戳通知
if isinstance(event, PokeNotifyEvent):
# 用户屏蔽,不区分私聊/群聊
if event.user_id in global_config.ban_user_id:
return
reply_poke_probability = 1 # 回复戳一戳的概率
if random() < reply_poke_probability:
user_info = UserInfo(
user_id=event.user_id,
user_nickname=get_user_nickname(event.user_id) or None,
user_cardname=get_user_cardname(event.user_id) or None,
platform="qq",
)
group_info = GroupInfo(group_id=event.group_id, group_name=None, platform="qq")
message_cq = MessageRecvCQ(
message_id=None,
user_info=user_info,
raw_message=str("[戳了戳]你"),
group_info=group_info,
reply_message=None,
platform="qq",
)
message_json = message_cq.to_dict()
# 进入maimbot
message = MessageRecv(message_json)
groupinfo = message.message_info.group_info
userinfo = message.message_info.user_info
messageinfo = message.message_info
chat = await chat_manager.get_or_create_stream(
platform=messageinfo.platform, user_info=userinfo, group_info=groupinfo
)
message.update_chat_stream(chat)
await message.process()
bot_user_info = UserInfo(
user_id=global_config.BOT_QQ,
user_nickname=global_config.BOT_NICKNAME,
platform=messageinfo.platform,
)
response, raw_content = await self.gpt.generate_response(message)
if response:
for msg in response:
message_segment = Seg(type="text", data=msg)
bot_message = MessageSending(
message_id=None,
chat_stream=chat,
bot_user_info=bot_user_info,
sender_info=userinfo,
message_segment=message_segment,
reply=None,
is_head=False,
is_emoji=False,
)
message_manager.add_message(bot_message)
async def handle_message(self, event: MessageEvent, bot: Bot) -> None:
"""处理收到的消息"""
@@ -54,7 +120,10 @@ class ChatBot:
# 用户屏蔽,不区分私聊/群聊
if event.user_id in global_config.ban_user_id:
return
if event.reply and hasattr(event.reply, 'sender') and hasattr(event.reply.sender, 'user_id') and event.reply.sender.user_id in global_config.ban_user_id:
logger.debug(f"跳过处理回复来自被ban用户 {event.reply.sender.user_id} 的消息")
return
# 处理私聊消息
if isinstance(event, PrivateMessageEvent):
if not global_config.enable_friend_chat: # 私聊过滤
@@ -126,7 +195,7 @@ class ChatBot:
for word in global_config.ban_words:
if word in message.processed_plain_text:
logger.info(
f"[{chat.group_info.group_name if chat.group_info.group_id else '私聊'}]{userinfo.user_nickname}:{message.processed_plain_text}"
f"[{chat.group_info.group_name if chat.group_info else '私聊'}]{userinfo.user_nickname}:{message.processed_plain_text}"
)
logger.info(f"[过滤词识别]消息中含有{word}filtered")
return
@@ -135,7 +204,7 @@ class ChatBot:
for pattern in global_config.ban_msgs_regex:
if re.search(pattern, message.raw_message):
logger.info(
f"[{chat.group_info.group_name if chat.group_info.group_id else '私聊'}]{userinfo.user_nickname}:{message.raw_message}"
f"[{chat.group_info.group_name if chat.group_info else '私聊'}]{userinfo.user_nickname}:{message.raw_message}"
)
logger.info(f"[正则表达式过滤]消息匹配到{pattern}filtered")
return
@@ -143,7 +212,7 @@ class ChatBot:
current_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(messageinfo.time))
# topic=await topic_identifier.identify_topic_llm(message.processed_plain_text)
topic = ""
interested_rate = await hippocampus.memory_activate_value(message.processed_plain_text) / 100
logger.debug(f"{message.processed_plain_text}的激活度:{interested_rate}")
@@ -159,11 +228,12 @@ class ChatBot:
config=global_config,
is_emoji=message.is_emoji,
interested_rate=interested_rate,
sender_id=str(message.message_info.user_info.user_id),
)
current_willing = willing_manager.get_willing(chat_stream=chat)
logger.info(
f"[{current_time}][{chat.group_info.group_name if chat.group_info.group_id else '私聊'}]{chat.user_info.user_nickname}:"
f"[{current_time}][{chat.group_info.group_name if chat.group_info else '私聊'}]{chat.user_info.user_nickname}:"
f"{message.processed_plain_text}[回复意愿:{current_willing:.2f}][概率:{reply_probability * 100:.1f}%]"
)
@@ -189,6 +259,9 @@ class ChatBot:
willing_manager.change_reply_willing_sent(chat)
response, raw_content = await self.gpt.generate_response(message)
else:
# 决定不回复时,也更新回复意愿
willing_manager.change_reply_willing_not_sent(chat)
# print(f"response: {response}")
if response:

View File

@@ -86,9 +86,12 @@ class CQCode:
else:
self.translated_segments = Seg(type="text", data="[图片]")
elif self.type == "at":
user_nickname = get_user_nickname(self.params.get("qq", ""))
self.translated_segments = Seg(
type="text", data=f"[@{user_nickname or '某人'}]"
if self.params.get("qq") == "all":
self.translated_segments = Seg(type="text", data="@[全体成员]")
else:
user_nickname = get_user_nickname(self.params.get("qq", ""))
self.translated_segments = Seg(
type="text", data=f"[@{user_nickname or '某人'}]"
)
elif self.type == "reply":
reply_segments = self.translate_reply()

View File

@@ -25,7 +25,7 @@ image_manager = ImageManager()
class EmojiManager:
_instance = None
EMOJI_DIR = "data/emoji" # 表情包存储目录
EMOJI_DIR = os.path.join("data", "emoji") # 表情包存储目录
def __new__(cls):
if cls._instance is None:
@@ -211,7 +211,7 @@ class EmojiManager:
async def scan_new_emojis(self):
"""扫描新的表情包"""
try:
emoji_dir = "data/emoji"
emoji_dir = self.EMOJI_DIR
os.makedirs(emoji_dir, exist_ok=True)
# 获取所有支持的图片文件
@@ -232,7 +232,7 @@ class EmojiManager:
image_hash = hashlib.md5(image_bytes).hexdigest()
image_format = Image.open(io.BytesIO(image_bytes)).format.lower()
# 检查是否已经注册过
existing_emoji = db["emoji"].find_one({"filename": filename})
existing_emoji = db["emoji"].find_one({"hash": image_hash})
description = None
if existing_emoji:

View File

@@ -44,18 +44,23 @@ class ImageManager:
"""确保images集合存在并创建索引"""
if "images" not in db.list_collection_names():
db.create_collection("images")
# 创建索引
db.images.create_index([("hash", 1)], unique=True)
db.images.create_index([("url", 1)])
db.images.create_index([("path", 1)])
# 删除旧索引
db.images.drop_indexes()
# 创建新的复合索引
db.images.create_index([("hash", 1), ("type", 1)], unique=True)
db.images.create_index([("url", 1)])
db.images.create_index([("path", 1)])
def _ensure_description_collection(self):
"""确保image_descriptions集合存在并创建索引"""
if "image_descriptions" not in db.list_collection_names():
db.create_collection("image_descriptions")
# 创建索引
db.image_descriptions.create_index([("hash", 1)], unique=True)
db.image_descriptions.create_index([("type", 1)])
# 删除旧索引
db.image_descriptions.drop_indexes()
# 创建新的复合索引
db.image_descriptions.create_index([("hash", 1), ("type", 1)], unique=True)
def _get_description_from_db(self, image_hash: str, description_type: str) -> Optional[str]:
"""从数据库获取图片描述
@@ -78,151 +83,21 @@ class ImageManager:
description: 描述文本
description_type: 描述类型 ('emoji''image')
"""
db.image_descriptions.update_one(
{"hash": image_hash, "type": description_type},
{"$set": {"description": description, "timestamp": int(time.time())}},
upsert=True,
)
async def save_image(
self, image_data: Union[str, bytes], url: str = None, description: str = None, is_base64: bool = False
) -> Optional[str]:
"""保存图像
Args:
image_data: 图像数据(base64字符串或字节)
url: 图像URL
description: 图像描述
is_base64: image_data是否为base64格式
Returns:
str: 保存后的文件路径,失败返回None
"""
try:
# 转换为字节格式
if is_base64:
if isinstance(image_data, str):
image_bytes = base64.b64decode(image_data)
else:
return None
else:
if isinstance(image_data, bytes):
image_bytes = image_data
else:
return None
# 计算哈希值
image_hash = hashlib.md5(image_bytes).hexdigest()
image_format = Image.open(io.BytesIO(image_bytes)).format.lower()
# 查重
existing = db.images.find_one({"hash": image_hash})
if existing:
return existing["path"]
# 生成文件名和路径
timestamp = int(time.time())
filename = f"{timestamp}_{image_hash[:8]}.{image_format}"
file_path = os.path.join(self.IMAGE_DIR, filename)
# 保存文件
with open(file_path, "wb") as f:
f.write(image_bytes)
# 保存到数据库
image_doc = {
"hash": image_hash,
"path": file_path,
"url": url,
"description": description,
"timestamp": timestamp,
}
db.images.insert_one(image_doc)
return file_path
db.image_descriptions.update_one(
{"hash": image_hash, "type": description_type},
{
"$set": {
"description": description,
"timestamp": int(time.time()),
"hash": image_hash, # 确保hash字段存在
"type": description_type, # 确保type字段存在
}
},
upsert=True,
)
except Exception as e:
logger.error(f"保存图像失败: {str(e)}")
return None
async def get_image_by_url(self, url: str) -> Optional[str]:
"""根据URL获取图像路径(带查重)
Args:
url: 图像URL
Returns:
str: 本地文件路径,不存在返回None
"""
try:
# 先查找是否已存在
existing = db.images.find_one({"url": url})
if existing:
return existing["path"]
# 下载图像
async with aiohttp.ClientSession() as session:
async with session.get(url) as resp:
if resp.status == 200:
image_bytes = await resp.read()
return await self.save_image(image_bytes, url=url)
return None
except Exception as e:
logger.error(f"获取图像失败: {str(e)}")
return None
async def get_base64_by_url(self, url: str) -> Optional[str]:
"""根据URL获取base64(带查重)
Args:
url: 图像URL
Returns:
str: base64字符串,失败返回None
"""
try:
image_path = await self.get_image_by_url(url)
if not image_path:
return None
with open(image_path, "rb") as f:
image_bytes = f.read()
return base64.b64encode(image_bytes).decode("utf-8")
except Exception as e:
logger.error(f"获取base64失败: {str(e)}")
return None
def check_url_exists(self, url: str) -> bool:
"""检查URL是否已存在
Args:
url: 图像URL
Returns:
bool: 是否存在
"""
return db.images.find_one({"url": url}) is not None
def check_hash_exists(self, image_data: Union[str, bytes], is_base64: bool = False) -> bool:
"""检查图像是否已存在
Args:
image_data: 图像数据(base64或字节)
is_base64: 是否为base64格式
Returns:
bool: 是否存在
"""
try:
if is_base64:
if isinstance(image_data, str):
image_bytes = base64.b64decode(image_data)
else:
return False
else:
if isinstance(image_data, bytes):
image_bytes = image_data
else:
return False
image_hash = hashlib.md5(image_bytes).hexdigest()
return db.images.find_one({"hash": image_hash}) is not None
except Exception as e:
logger.error(f"检查哈希失败: {str(e)}")
return False
logger.error(f"保存描述到数据库失败: {str(e)}")
async def get_emoji_description(self, image_base64: str) -> str:
"""获取表情包描述,带查重和保存功能"""
@@ -242,6 +117,11 @@ class ImageManager:
prompt = "这是一个表情包,使用中文简洁的描述一下表情包的内容和表情包所表达的情感"
description, _ = await self._llm.generate_response_for_image(prompt, image_base64, image_format)
cached_description = self._get_description_from_db(image_hash, "emoji")
if cached_description:
logger.warning(f"虽然生成了描述,但是找到缓存表情包描述: {cached_description}")
return f"[表情包:{cached_description}]"
# 根据配置决定是否保存图片
if global_config.EMOJI_SAVE:
# 生成文件名和路径
@@ -280,7 +160,6 @@ class ImageManager:
async def get_image_description(self, image_base64: str) -> str:
"""获取普通图片描述,带查重和保存功能"""
try:
print("处理图片中")
# 计算图片哈希
image_bytes = base64.b64decode(image_base64)
image_hash = hashlib.md5(image_bytes).hexdigest()
@@ -289,7 +168,7 @@ class ImageManager:
# 查询缓存的描述
cached_description = self._get_description_from_db(image_hash, "image")
if cached_description:
print("图片描述缓存中")
logger.info(f"图片描述缓存中 {cached_description}")
return f"[图片:{cached_description}]"
# 调用AI获取描述
@@ -298,7 +177,12 @@ class ImageManager:
)
description, _ = await self._llm.generate_response_for_image(prompt, image_base64, image_format)
print(f"描述是{description}")
cached_description = self._get_description_from_db(image_hash, "image")
if cached_description:
logger.warning(f"虽然生成了描述,但是找到缓存图片描述 {cached_description}")
return f"[图片:{cached_description}]"
logger.info(f"描述是{description}")
if description is None:
logger.warning("AI未能生成图片描述")

View File

@@ -5,14 +5,16 @@ from .relationship_manager import relationship_manager
def get_user_nickname(user_id: int) -> str:
if int(user_id) == int(global_config.BOT_QQ):
return global_config.BOT_NICKNAME
# print(user_id)
# print(user_id)
return relationship_manager.get_name(user_id)
def get_user_cardname(user_id: int) -> str:
if int(user_id) == int(global_config.BOT_QQ):
return global_config.BOT_NICKNAME
# print(user_id)
return ''
# print(user_id)
return ""
def get_groupname(group_id: int) -> str:
return f"{group_id}"
return f"{group_id}"

View File

@@ -1,109 +1,259 @@
import asyncio
import random
import time
from typing import Dict
from loguru import logger
from .config import global_config
from .chat_stream import ChatStream
from loguru import logger
class WillingManager:
def __init__(self):
self.chat_reply_willing: Dict[str, float] = {} # 存储每个聊天流的回复意愿
self.chat_high_willing_mode: Dict[str, bool] = {} # 存储每个聊天流是否处于高回复意愿期
self.chat_msg_count: Dict[str, int] = {} # 存储每个聊天流接收到的消息数量
self.chat_last_mode_change: Dict[str, float] = {} # 存储每个聊天流上次模式切换的时间
self.chat_high_willing_duration: Dict[str, int] = {} # 高意愿期持续时间(秒)
self.chat_low_willing_duration: Dict[str, int] = {} # 低意愿期持续时间(秒)
self.chat_last_reply_time: Dict[str, float] = {} # 存储每个聊天流上次回复的时间
self.chat_last_sender_id: Dict[str, str] = {} # 存储每个聊天流上次回复的用户ID
self.chat_conversation_context: Dict[str, bool] = {} # 标记是否处于对话上下文中
self._decay_task = None
self._mode_switch_task = None
self._started = False
async def _decay_reply_willing(self):
"""定期衰减回复意愿"""
while True:
await asyncio.sleep(5)
for chat_id in self.chat_reply_willing:
self.chat_reply_willing[chat_id] = max(0, self.chat_reply_willing[chat_id] * 0.6)
is_high_mode = self.chat_high_willing_mode.get(chat_id, False)
if is_high_mode:
# 高回复意愿期内轻微衰减
self.chat_reply_willing[chat_id] = max(0.5, self.chat_reply_willing[chat_id] * 0.95)
else:
# 低回复意愿期内正常衰减
self.chat_reply_willing[chat_id] = max(0, self.chat_reply_willing[chat_id] * 0.8)
async def _mode_switch_check(self):
"""定期检查是否需要切换回复意愿模式"""
while True:
current_time = time.time()
await asyncio.sleep(10) # 每10秒检查一次
for chat_id in self.chat_high_willing_mode:
last_change_time = self.chat_last_mode_change.get(chat_id, 0)
is_high_mode = self.chat_high_willing_mode.get(chat_id, False)
# 获取当前模式的持续时间
duration = 0
if is_high_mode:
duration = self.chat_high_willing_duration.get(chat_id, 180) # 默认3分钟
else:
duration = self.chat_low_willing_duration.get(chat_id, random.randint(300, 1200)) # 默认5-20分钟
# 检查是否需要切换模式
if current_time - last_change_time > duration:
self._switch_willing_mode(chat_id)
elif not is_high_mode and random.random() < 0.1:
# 低回复意愿期有10%概率随机切换到高回复期
self._switch_willing_mode(chat_id)
# 检查对话上下文状态是否需要重置
last_reply_time = self.chat_last_reply_time.get(chat_id, 0)
if current_time - last_reply_time > 300: # 5分钟无交互重置对话上下文
self.chat_conversation_context[chat_id] = False
def _switch_willing_mode(self, chat_id: str):
"""切换聊天流的回复意愿模式"""
is_high_mode = self.chat_high_willing_mode.get(chat_id, False)
if is_high_mode:
# 从高回复期切换到低回复期
self.chat_high_willing_mode[chat_id] = False
self.chat_reply_willing[chat_id] = 0.1 # 设置为最低回复意愿
self.chat_low_willing_duration[chat_id] = random.randint(600, 1200) # 10-20分钟
logger.debug(f"聊天流 {chat_id} 切换到低回复意愿期,持续 {self.chat_low_willing_duration[chat_id]}")
else:
# 从低回复期切换到高回复期
self.chat_high_willing_mode[chat_id] = True
self.chat_reply_willing[chat_id] = 1.0 # 设置为较高回复意愿
self.chat_high_willing_duration[chat_id] = random.randint(180, 240) # 3-4分钟
logger.debug(f"聊天流 {chat_id} 切换到高回复意愿期,持续 {self.chat_high_willing_duration[chat_id]}")
self.chat_last_mode_change[chat_id] = time.time()
self.chat_msg_count[chat_id] = 0 # 重置消息计数
def get_willing(self, chat_stream: ChatStream) -> float:
"""获取指定聊天流的回复意愿"""
stream = chat_stream
if stream:
return self.chat_reply_willing.get(stream.stream_id, 0)
return 0
def set_willing(self, chat_id: str, willing: float):
"""设置指定聊天流的回复意愿"""
self.chat_reply_willing[chat_id] = willing
async def change_reply_willing_received(
self,
chat_stream: ChatStream,
topic: str = None,
is_mentioned_bot: bool = False,
config=None,
is_emoji: bool = False,
interested_rate: float = 0,
) -> float:
def _ensure_chat_initialized(self, chat_id: str):
"""确保聊天流的所有数据已初始化"""
if chat_id not in self.chat_reply_willing:
self.chat_reply_willing[chat_id] = 0.1
if chat_id not in self.chat_high_willing_mode:
self.chat_high_willing_mode[chat_id] = False
self.chat_last_mode_change[chat_id] = time.time()
self.chat_low_willing_duration[chat_id] = random.randint(300, 1200) # 5-20分钟
if chat_id not in self.chat_msg_count:
self.chat_msg_count[chat_id] = 0
if chat_id not in self.chat_conversation_context:
self.chat_conversation_context[chat_id] = False
async def change_reply_willing_received(self,
chat_stream: ChatStream,
topic: str = None,
is_mentioned_bot: bool = False,
config = None,
is_emoji: bool = False,
interested_rate: float = 0,
sender_id: str = None) -> float:
"""改变指定聊天流的回复意愿并返回回复概率"""
# 获取或创建聊天流
stream = chat_stream
chat_id = stream.stream_id
current_time = time.time()
self._ensure_chat_initialized(chat_id)
# 增加消息计数
self.chat_msg_count[chat_id] = self.chat_msg_count.get(chat_id, 0) + 1
current_willing = self.chat_reply_willing.get(chat_id, 0)
if is_mentioned_bot and current_willing < 1.0:
current_willing += 0.9
is_high_mode = self.chat_high_willing_mode.get(chat_id, False)
msg_count = self.chat_msg_count.get(chat_id, 0)
in_conversation_context = self.chat_conversation_context.get(chat_id, False)
# 检查是否是对话上下文中的追问
last_reply_time = self.chat_last_reply_time.get(chat_id, 0)
last_sender = self.chat_last_sender_id.get(chat_id, "")
is_follow_up_question = False
# 如果是同一个人在短时间内2分钟内发送消息且消息数量较少<=5条视为追问
if sender_id and sender_id == last_sender and current_time - last_reply_time < 120 and msg_count <= 5:
is_follow_up_question = True
in_conversation_context = True
self.chat_conversation_context[chat_id] = True
logger.debug(f"检测到追问 (同一用户), 提高回复意愿")
current_willing += 0.3
# 特殊情况处理
if is_mentioned_bot:
current_willing += 0.5
in_conversation_context = True
self.chat_conversation_context[chat_id] = True
logger.debug(f"被提及, 当前意愿: {current_willing}")
elif is_mentioned_bot:
current_willing += 0.05
logger.debug(f"被重复提及, 当前意愿: {current_willing}")
if is_emoji:
current_willing *= 0.1
logger.debug(f"表情包, 当前意愿: {current_willing}")
logger.debug(f"放大系数_interested_rate: {global_config.response_interested_rate_amplifier}")
interested_rate *= global_config.response_interested_rate_amplifier # 放大回复兴趣度
if interested_rate > 0.4:
# print(f"兴趣度: {interested_rate}, 当前意愿: {current_willing}")
current_willing += interested_rate - 0.4
current_willing *= global_config.response_willing_amplifier # 放大回复意愿
# print(f"放大系数_willing: {global_config.response_willing_amplifier}, 当前意愿: {current_willing}")
reply_probability = max((current_willing - 0.45) * 2, 0)
# 根据话题兴趣度适当调整
if interested_rate > 0.5:
current_willing += (interested_rate - 0.5) * 0.5
# 根据当前模式计算回复概率
base_probability = 0.0
if in_conversation_context:
# 在对话上下文中,降低基础回复概率
base_probability = 0.5 if is_high_mode else 0.25
logger.debug(f"处于对话上下文中,基础回复概率: {base_probability}")
elif is_high_mode:
# 高回复周期4-8句话有50%的概率会回复一次
base_probability = 0.50 if 4 <= msg_count <= 8 else 0.2
else:
# 低回复周期需要最少15句才有30%的概率会回一句
base_probability = 0.30 if msg_count >= 15 else 0.03 * min(msg_count, 10)
# 考虑回复意愿的影响
reply_probability = base_probability * current_willing
# 检查群组权限(如果是群聊)
if chat_stream.group_info:
if chat_stream.group_info and config:
if chat_stream.group_info.group_id in config.talk_frequency_down_groups:
reply_probability = reply_probability / global_config.down_frequency_rate
reply_probability = min(reply_probability, 1)
# 限制最大回复概率
reply_probability = min(reply_probability, 0.75) # 设置最大回复概率为75%
if reply_probability < 0:
reply_probability = 0
# 记录当前发送者ID以便后续追踪
if sender_id:
self.chat_last_sender_id[chat_id] = sender_id
self.chat_reply_willing[chat_id] = min(current_willing, 3.0)
return reply_probability
def change_reply_willing_sent(self, chat_stream: ChatStream):
"""开始思考后降低聊天流的回复意愿"""
stream = chat_stream
if stream:
current_willing = self.chat_reply_willing.get(stream.stream_id, 0)
self.chat_reply_willing[stream.stream_id] = max(0, current_willing - 2)
def change_reply_willing_after_sent(self, chat_stream: ChatStream):
"""发送消息后提高聊天流的回复意愿"""
chat_id = stream.stream_id
self._ensure_chat_initialized(chat_id)
is_high_mode = self.chat_high_willing_mode.get(chat_id, False)
current_willing = self.chat_reply_willing.get(chat_id, 0)
# 回复后减少回复意愿
self.chat_reply_willing[chat_id] = max(0, current_willing - 0.3)
# 标记为对话上下文中
self.chat_conversation_context[chat_id] = True
# 记录最后回复时间
self.chat_last_reply_time[chat_id] = time.time()
# 重置消息计数
self.chat_msg_count[chat_id] = 0
def change_reply_willing_not_sent(self, chat_stream: ChatStream):
"""决定不回复后提高聊天流的回复意愿"""
stream = chat_stream
if stream:
current_willing = self.chat_reply_willing.get(stream.stream_id, 0)
if current_willing < 1:
self.chat_reply_willing[stream.stream_id] = min(1, current_willing + 0.2)
chat_id = stream.stream_id
self._ensure_chat_initialized(chat_id)
is_high_mode = self.chat_high_willing_mode.get(chat_id, False)
current_willing = self.chat_reply_willing.get(chat_id, 0)
in_conversation_context = self.chat_conversation_context.get(chat_id, False)
# 根据当前模式调整不回复后的意愿增加
if is_high_mode:
willing_increase = 0.1
elif in_conversation_context:
# 在对话上下文中但决定不回复,小幅增加回复意愿
willing_increase = 0.15
else:
willing_increase = random.uniform(0.05, 0.1)
self.chat_reply_willing[chat_id] = min(2.0, current_willing + willing_increase)
def change_reply_willing_after_sent(self, chat_stream: ChatStream):
"""发送消息后提高聊天流的回复意愿"""
# 由于已经在sent中处理这个方法保留但不再需要额外调整
pass
async def ensure_started(self):
"""确保衰减任务已启动"""
"""确保所有任务已启动"""
if not self._started:
if self._decay_task is None:
self._decay_task = asyncio.create_task(self._decay_reply_willing())
if self._mode_switch_task is None:
self._mode_switch_task = asyncio.create_task(self._mode_switch_check())
self._started = True
# 创建全局实例
willing_manager = WillingManager()
willing_manager = WillingManager()