提供了根据日程生成日常生活分享的功能

pull/348/head
Ark-Hakobune 2025-03-13 21:31:09 +08:00
parent 46af2ef94d
commit ba31402015
6 changed files with 278 additions and 2 deletions

View File

@ -30,6 +30,7 @@ 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
from ..utils.logger_config import setup_logger, LogModule
from .daily_share_scheduler import DailyShareScheduler
# 配置日志
logger = setup_logger(LogModule.CHAT)
@ -45,6 +46,7 @@ class ChatBot:
self.mood_manager.start_mood_update() # 启动情绪更新
self.emoji_chance = 0.2 # 发送表情包的基础概率
self.daily_share_scheduler = DailyShareScheduler()
# self.message_streams = MessageStreamContainer()
async def _ensure_started(self):

View File

@ -105,6 +105,11 @@ class BotConfig:
default_factory=lambda: ["表情包", "图片", "回复", "聊天记录"]
) # 添加新的配置项默认值
daily_share_willing: float = 0.3 # 基础分享意愿
daily_share_interval: int = 1800 # 日常分享检查间隔(秒)
daily_share_time_start: int = 8 # 日常分享开始时间(小时)
daily_share_time_end: int = 22 # 日常分享结束时间(小时)
@staticmethod
def get_config_dir() -> str:
"""获取配置文件目录"""
@ -342,6 +347,17 @@ class BotConfig:
config.enable_debug_output = others_config.get("enable_debug_output", config.enable_debug_output)
config.enable_friend_chat = others_config.get("enable_friend_chat", config.enable_friend_chat)
def shares(parent: dict):
shares_config = parent["shares"]
config.daily_share_willing = shares_config.get("daily_share_willing", config.daily_share_willing)
config.daily_share_interval = shares_config.get("daily_share_interval", config.daily_share_interval)
config.daily_share_time_start = shares_config.get("daily_share_time_start", config.daily_share_time_start)
config.daily_share_time_end = shares_config.get("daily_share_time_end", config.daily_share_time_end)
def shares_allow_groups(parent: dict):
shares_allow_groups_config = parent["shares_allow_groups"]
config.shares_allow_groups = set(shares_allow_groups_config.get("shares_allow_groups", []))
# 版本表达式:>=1.0.0,<2.0.0
# 允许字段func: method, support: str, notice: str, necessary: bool
# 如果使用 notice 字段,在该组配置加载时,会展示该字段对用户的警示
@ -361,6 +377,8 @@ class BotConfig:
"chinese_typo": {"func": chinese_typo, "support": ">=0.0.3", "necessary": False},
"groups": {"func": groups, "support": ">=0.0.0"},
"others": {"func": others, "support": ">=0.0.0"},
"shares": {"func": shares, "support": ">=0.0.0"},
"shares_allow_groups": {"func": shares_allow_groups, "support": ">=0.0.0"},
}
# 原地修改,将 字符串版本表达式 转换成 版本对象

View File

@ -0,0 +1,143 @@
import asyncio
import random
import datetime
from typing import Optional, Dict
from loguru import logger
from nonebot import get_driver
from .config import global_config
from .willing_manager import willing_manager
from .llm_generator import DailyMessageGenerate
from .message import Message, MessageSending, MessageSet
from .message_sender import message_manager
from .message_base import UserInfo, GroupInfo, Seg
from .chat_stream import chat_manager
from .message_cq import (
MessageRecvCQ,
)
class DailyShareScheduler:
def __init__(self):
self.daily_generator = DailyMessageGenerate()
self._tasks: Dict[str, asyncio.Task] = {} # 存储每个群的任务
self._started = False
async def _group_share_task(self, group_id: str):
"""单个群组的分享任务"""
# 为每个群组设置随机的初始等待时间,避免同时执行
initial_wait = random.uniform(0, 30)
daily_share_interval = global_config.daily_share_interval + initial_wait
await asyncio.sleep(daily_share_interval)
while True:
try:
current_time = datetime.datetime.now()
# 检查是否在允许的时间范围内
if global_config.daily_share_time_start <= current_time.hour < global_config.daily_share_time_end:
try:
# 创建用户信息
bot_user_info = UserInfo(
user_id=global_config.BOT_QQ,
user_nickname=global_config.BOT_NICKNAME,
platform="qq"
)
# 创建群组信息
group_info = GroupInfo(
group_id=group_id,
group_name=None,
platform="qq"
)
# 创建聊天流
chat_stream = await chat_manager.get_or_create_stream(
platform="qq",
user_info=bot_user_info,
group_info=group_info
)
# 检查分享意愿
share_willing = await willing_manager.check_daily_share_wiling(chat_stream)
# 生成随机数并与意愿比较
if random.random() < share_willing:
try:
# 创建消息对象
message = Message(
message_id=f"daily_{int(datetime.datetime.now().timestamp())}",
time=int(datetime.datetime.now().timestamp()),
chat_stream=chat_stream,
user_info=bot_user_info,
message_segment=None,
reply=None,
detailed_plain_text="",
processed_plain_text=""
)
# 生成日常分享内容
content = await self.daily_generator.gen_response(message)
if content:
# 创建消息段
message_segment = Seg(type="text", data=content)
message_set = MessageSet(
chat_stream=chat_stream,
message_id=message.message_info.message_id
)
# 创建发送消息对象
bot_message = MessageSending(
message_id=message.message_info.message_id,
chat_stream=chat_stream,
bot_user_info=bot_user_info,
sender_info=bot_user_info,
message_segment=message_segment,
reply=None,
is_head=True,
is_emoji=False
)
await bot_message.process()
message_set.add_message(bot_message)
message_manager.add_message(message_set)
logger.info(f"群[{group_id}]分享意愿[{share_willing:.2f}],已生成日常分享并加入发送队列")
# 重置该群的分享意愿
await willing_manager.reset_daily_share_wiling(chat_stream)
except Exception as e:
logger.error(f"生成或发送消息时出错: {str(e)}")
else:
logger.debug(f"群[{group_id}]分享意愿[{share_willing:.2f}]不足,跳过分享")
except Exception as e:
logger.error(f"处理群[{group_id}]时出错: {str(e)}")
# 等待2小时加上随机偏移避免同时执行
wait_time = 30 + random.uniform(-5, 5) # 测试时使用30秒加上随机偏移
await asyncio.sleep(wait_time)
except Exception as e:
logger.error(f"群[{group_id}]的日常分享任务出错: {str(e)}")
await asyncio.sleep(60) # 出错后等待1分钟再试
async def start(self):
"""启动定时任务"""
if not self._started:
self._started = True
# 为每个群创建独立的任务
for group_id in global_config.shares_allow_groups:
logger.info(f"启动群[{group_id}]的日常分享任务")
# 使用 create_task 创建独立的任务
task = asyncio.create_task(self._group_share_task(group_id))
self._tasks[group_id] = task
logger.info("日常分享定时任务已启动")
# 创建全局实例
daily_share_scheduler = DailyShareScheduler()
# 在驱动器启动时启动定时任务
driver = get_driver()
@driver.on_startup
async def _():
await daily_share_scheduler.start()

View File

@ -243,3 +243,20 @@ class InitiativeMessageGenerate:
content, reasoning = self.model_r1.generate_response_async(prompt)
logger.debug(f"[DEBUG] {content} {reasoning}")
return content
class DailyMessageGenerate:
def __init__(self):
self.model_r1 = LLM_request(model=global_config.llm_reasoning, temperature=0.7)
self.model_v3 = LLM_request(model=global_config.llm_normal, temperature=0.7)
self.model_r1_distill = LLM_request(
model=global_config.llm_reasoning_minor, temperature=0.7
)
async def gen_response(self, message: Message):
prompt, prompt_template = (
prompt_builder._build_daily_prompt(message.message_info.group_info.group_id)
)
content, reasoning =await self.model_r1.generate_response_async(prompt)
# logger.debug(f"[DEBUG] {content} {reasoning}")
return content

View File

@ -319,5 +319,32 @@ class PromptBuilder:
# 返回所有找到的内容,用换行分隔
return '\n'.join(str(result['content']) for result in results)
def _build_daily_prompt(self, group_id, probability_1=0.8, probability_2=0.1):
current_date = time.strftime("%Y-%m-%d", time.localtime())
current_time = time.strftime("%H:%M:%S", time.localtime())
bot_schedule_now_time, bot_schedule_now_activity = bot_schedule.get_current_task()
prompt_date = f'''今天是{current_date},现在是{current_time},你今天的日程是:\n{bot_schedule.today_schedule}\n你现在正在{bot_schedule_now_activity}\n'''
# print(f"\033[1;34m[调试]\033[0m 已从数据库获取群 {group_id} 的消息记录:{chat_talking_prompt}")
# 激活prompt构建
personality = global_config.PROMPT_PERSONALITY
prompt_personality = ''
personality_choice = random.random()
if personality_choice < probability_1: # 第一种人格
prompt_personality = f'''你的网名叫{global_config.BOT_NICKNAME}{personality[0]}'''
elif personality_choice < probability_1 + probability_2: # 第二种人格
prompt_personality = f'''你的网名叫{global_config.BOT_NICKNAME}{personality[1]}'''
else: # 第三种人格
prompt_personality = f'''你的网名叫{global_config.BOT_NICKNAME}{personality[2]}'''
prompt_for_select = f"现在根据你今天的日程生成一条日常分享讲述你的上一个日程是什么在日程中发生了什么有意思的事情。注意只需要输出日常分享不要输出其他任何内容不要在也不要提到之后的日程是什么字数不要超过50字。"
prompt_initiative_select = f"{prompt_date}\n{prompt_personality}\n{prompt_for_select}"
prompt_regular = f"{prompt_date}\n{prompt_personality}"
return prompt_initiative_select, prompt_regular
prompt_builder = PromptBuilder()

View File

@ -4,7 +4,7 @@ import time
from typing import Dict
from loguru import logger
from ...common.database import db
from .config import global_config
from .chat_stream import ChatStream
@ -20,10 +20,12 @@ class WillingManager:
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.daily_share_wiling: Dict[str, float] = {} # 存储每个聊天流的分享意愿
self._decay_task = None
self._mode_switch_task = None
self._started = False
async def _decay_reply_willing(self):
"""定期衰减回复意愿"""
while True:
@ -255,5 +257,72 @@ class WillingManager:
self._mode_switch_task = asyncio.create_task(self._mode_switch_check())
self._started = True
async def check_daily_share_wiling(self, chat_stream: ChatStream) -> float:
"""检查群聊消息活跃度,决定是否提高分享日常意愿
目前仅支持群聊主动发起聊天私聊不支持
Args:
chat_stream: 聊天流对象, 分享意愿不依赖聊天内容只与群聊活跃度相关
Returns:
float: 分享意愿值
"""
if not chat_stream or not chat_stream.group_info:
return 0.0
try:
# 获取24小时前的时间戳
one_day_ago = int(time.time()) - 86400 # 24小时 = 86400秒
# 从数据库查询24小时内的总消息数
daily_messages = list(db.messages.find({
"chat_id": chat_stream.stream_id,
"time": {"$gte": one_day_ago}
}))
# 如果24小时内消息数不超过100不激活分享意愿
# 仅统计bot运行期间的消息
# 暂时不启用,配置文档指定群聊分享功能是否开启
# if len(daily_messages) <= 100:
# logger.debug(f"群{chat_stream.group_info.group_id}在24小时内消息数{len(daily_messages)}条,小于阈值,鉴定为不活跃群,不激活分享意愿")
# self.daily_share_wiling[chat_stream.stream_id] = 0
# return 0.0
# 获取60分钟前的时间戳
thirty_minutes_ago = int(time.time()) - 36
# 从数据库查询最近60分钟的消息
recent_messages = list(db.messages.find({
"chat_id": chat_stream.stream_id,
"time": {"$gte": thirty_minutes_ago}
}))
# 如果没有最近消息,大幅提高分享意愿
if not recent_messages:
share_willing = self.daily_share_wiling.get(chat_stream.stream_id, global_config.daily_share_willing)
new_willing = min(0.6, max(0.3, share_willing + 0.2))
self.daily_share_wiling[chat_stream.stream_id] = new_willing
logger.info(f"{chat_stream.group_info.group_id}最近60分钟无消息提高分享意愿至{new_willing}")
return new_willing
# 如果有消息但消息数量很少比如少于3条适度提高意愿
if len(recent_messages) < 3:
share_willing = self.daily_share_wiling.get(chat_stream.stream_id, global_config.daily_share_willing)
new_willing = min(0.4, max(0.2, share_willing + 0.1))
self.daily_share_wiling[chat_stream.stream_id] = new_willing
logger.info(f"{chat_stream.group_info.group_id}最近60分钟消息较少适度提高分享意愿至{new_willing}")
return new_willing
# 消息活跃度正常,保持当前意愿
logger.debug(
f"{chat_stream.group_info.group_id}消息活跃度正常,保持分享意愿{self.daily_share_wiling.get(chat_stream.stream_id, global_config.daily_share_willing)}")
return self.daily_share_wiling.get(chat_stream.stream_id, global_config.daily_share_willing)
except Exception as e:
logger.error(f"检查群聊活跃度时出错: {str(e)}")
return global_config.daily_share_willing # 出错时返回基础分享意愿
async def reset_daily_share_wiling(self, chat_stream: ChatStream) -> float:
"""重置分享意愿"""
self.daily_share_wiling[chat_stream.stream_id] = 0
# 创建全局实例
willing_manager = WillingManager()