mirror of https://github.com/Mai-with-u/MaiBot.git
提供了根据日程生成日常生活分享的功能
parent
46af2ef94d
commit
ba31402015
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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"},
|
||||
}
|
||||
|
||||
# 原地修改,将 字符串版本表达式 转换成 版本对象
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
@ -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
|
||||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
Loading…
Reference in New Issue