feat:添加了眨眼动作和微动作,注视动作
This commit is contained in:
@@ -15,13 +15,14 @@ install(extra_lines=3)
|
|||||||
logger = get_logger("sender")
|
logger = get_logger("sender")
|
||||||
|
|
||||||
|
|
||||||
async def send_message(message: MessageSending) -> bool:
|
async def send_message(message: MessageSending, show_log=True) -> bool:
|
||||||
"""合并后的消息发送函数,包含WS发送和日志记录"""
|
"""合并后的消息发送函数,包含WS发送和日志记录"""
|
||||||
message_preview = truncate_message(message.processed_plain_text, max_length=40)
|
message_preview = truncate_message(message.processed_plain_text, max_length=120)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# 直接调用API发送消息
|
# 直接调用API发送消息
|
||||||
await get_global_api().send_message(message)
|
await get_global_api().send_message(message)
|
||||||
|
if show_log:
|
||||||
logger.info(f"已将消息 '{message_preview}' 发往平台'{message.message_info.platform}'")
|
logger.info(f"已将消息 '{message_preview}' 发往平台'{message.message_info.platform}'")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -37,7 +38,7 @@ class HeartFCSender:
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.storage = MessageStorage()
|
self.storage = MessageStorage()
|
||||||
|
|
||||||
async def send_message(self, message: MessageSending, typing=False, set_reply=False, storage_message=True):
|
async def send_message(self, message: MessageSending, typing=False, set_reply=False, storage_message=True, show_log=True):
|
||||||
"""
|
"""
|
||||||
处理、发送并存储一条消息。
|
处理、发送并存储一条消息。
|
||||||
|
|
||||||
@@ -73,7 +74,7 @@ class HeartFCSender:
|
|||||||
)
|
)
|
||||||
await asyncio.sleep(typing_time)
|
await asyncio.sleep(typing_time)
|
||||||
|
|
||||||
sent_msg = await send_message(message)
|
sent_msg = await send_message(message, show_log=show_log)
|
||||||
if not sent_msg:
|
if not sent_msg:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ from src.chat.message_receive.message import MessageSending, MessageRecv
|
|||||||
from src.config.config import global_config
|
from src.config.config import global_config
|
||||||
from src.common.message.api import get_global_api
|
from src.common.message.api import get_global_api
|
||||||
from src.chat.message_receive.storage import MessageStorage
|
from src.chat.message_receive.storage import MessageStorage
|
||||||
|
from .s4u_watching_manager import watching_manager
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
|
||||||
@@ -336,6 +337,11 @@ class S4UChat:
|
|||||||
async def _generate_and_send(self, message: MessageRecv):
|
async def _generate_and_send(self, message: MessageRecv):
|
||||||
"""为单个消息生成文本和音频回复。整个过程可以被中断。"""
|
"""为单个消息生成文本和音频回复。整个过程可以被中断。"""
|
||||||
self._is_replying = True
|
self._is_replying = True
|
||||||
|
|
||||||
|
# 视线管理:开始生成回复时切换视线状态
|
||||||
|
chat_watching = watching_manager.get_watching_by_chat_id(self.stream_id)
|
||||||
|
await chat_watching.on_reply_start()
|
||||||
|
|
||||||
sender_container = MessageSenderContainer(self.chat_stream, message)
|
sender_container = MessageSenderContainer(self.chat_stream, message)
|
||||||
sender_container.start()
|
sender_container.start()
|
||||||
|
|
||||||
@@ -368,6 +374,11 @@ class S4UChat:
|
|||||||
logger.error(f"[{self.stream_name}] 回复生成过程中出现错误: {e}", exc_info=True)
|
logger.error(f"[{self.stream_name}] 回复生成过程中出现错误: {e}", exc_info=True)
|
||||||
finally:
|
finally:
|
||||||
self._is_replying = False
|
self._is_replying = False
|
||||||
|
|
||||||
|
# 视线管理:回复结束时切换视线状态
|
||||||
|
chat_watching = watching_manager.get_watching_by_chat_id(self.stream_id)
|
||||||
|
await chat_watching.on_reply_finished()
|
||||||
|
|
||||||
# 确保发送器被妥善关闭(即使已关闭,再次调用也是安全的)
|
# 确保发送器被妥善关闭(即使已关闭,再次调用也是安全的)
|
||||||
sender_container.resume()
|
sender_container.resume()
|
||||||
if not sender_container._task.done():
|
if not sender_container._task.done():
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
import time
|
import time
|
||||||
|
import random
|
||||||
|
|
||||||
from src.chat.message_receive.message import MessageRecv
|
from src.chat.message_receive.message import MessageRecv
|
||||||
from src.llm_models.utils_model import LLMRequest
|
from src.llm_models.utils_model import LLMRequest
|
||||||
@@ -36,6 +37,9 @@ from src.plugin_system.apis import send_api
|
|||||||
# 重置为中性表情
|
# 重置为中性表情
|
||||||
await facial_expression.reset_expression()
|
await facial_expression.reset_expression()
|
||||||
|
|
||||||
|
# 执行眨眼动作
|
||||||
|
await facial_expression.perform_blink()
|
||||||
|
|
||||||
3. 自动表情系统:
|
3. 自动表情系统:
|
||||||
- 当情绪值更新时,系统会自动根据mood_values选择合适的面部表情
|
- 当情绪值更新时,系统会自动根据mood_values选择合适的面部表情
|
||||||
- 只有当新表情与当前表情不同时才会发送,避免重复发送
|
- 只有当新表情与当前表情不同时才会发送,避免重复发送
|
||||||
@@ -46,7 +50,14 @@ from src.plugin_system.apis import send_api
|
|||||||
- 每次mood更新后立即发送表情更新
|
- 每次mood更新后立即发送表情更新
|
||||||
- 发送消息类型为"amadus_expression_update",格式为{"action": "表情名", "data": 1.0}
|
- 发送消息类型为"amadus_expression_update",格式为{"action": "表情名", "data": 1.0}
|
||||||
|
|
||||||
5. 表情选择逻辑:
|
5. 眨眼系统:
|
||||||
|
- 每4-6秒随机执行一次眨眼动作
|
||||||
|
- 眨眼包含两个阶段:先闭眼(eye_close=1.0),保持0.1-0.15秒,然后睁眼(eye_close=0.0)
|
||||||
|
- 眨眼使用override_values参数临时覆盖eye_close值,不修改原始表情状态
|
||||||
|
- 眨眼时会发送完整的表情状态,包含当前表情的所有动作
|
||||||
|
- 当eye部位已经是eye_happy_weak时,跳过眨眼动作
|
||||||
|
|
||||||
|
6. 表情选择逻辑:
|
||||||
- 系统会找出最强的情绪(joy, anger, sorrow, fear)
|
- 系统会找出最强的情绪(joy, anger, sorrow, fear)
|
||||||
- 根据情绪强度选择相应的表情组合
|
- 根据情绪强度选择相应的表情组合
|
||||||
- 默认情况下返回neutral表情
|
- 默认情况下返回neutral表情
|
||||||
@@ -130,78 +141,87 @@ class FacialExpression:
|
|||||||
def __init__(self, chat_id: str):
|
def __init__(self, chat_id: str):
|
||||||
self.chat_id: str = chat_id
|
self.chat_id: str = chat_id
|
||||||
|
|
||||||
# 预定义面部表情动作
|
# 预定义面部表情动作(根据用户定义的表情动作)
|
||||||
self.expressions = {
|
self.expressions = {
|
||||||
# 眼睛表情
|
# 眼睛表情
|
||||||
"eye_smile": {"action": "eye_smile", "data": 1.0},
|
"eye_happy_weak": {"action": "eye_happy_weak", "data": 1.0},
|
||||||
"eye_cry": {"action": "eye_cry", "data": 1.0},
|
|
||||||
"eye_close": {"action": "eye_close", "data": 1.0},
|
"eye_close": {"action": "eye_close", "data": 1.0},
|
||||||
"eye_normal": {"action": "eye_normal", "data": 1.0},
|
"eye_shift_left": {"action": "eye_shift_left", "data": 1.0},
|
||||||
|
"eye_shift_right": {"action": "eye_shift_right", "data": 1.0},
|
||||||
|
# "eye_smile": {"action": "eye_smile", "data": 1.0}, # 未定义,占位
|
||||||
|
# "eye_cry": {"action": "eye_cry", "data": 1.0}, # 未定义,占位
|
||||||
|
# "eye_normal": {"action": "eye_normal", "data": 1.0}, # 未定义,占位
|
||||||
|
|
||||||
# 眉毛表情
|
# 眉毛表情
|
||||||
"eyebrow_smile": {"action": "eyebrow_smile", "data": 1.0},
|
"eyebrow_happy_weak": {"action": "eyebrow_happy_weak", "data": 1.0},
|
||||||
"eyebrow_angry": {"action": "eyebrow_angry", "data": 1.0},
|
"eyebrow_happy_strong": {"action": "eyebrow_happy_strong", "data": 1.0},
|
||||||
"eyebrow_sad": {"action": "eyebrow_sad", "data": 1.0},
|
"eyebrow_angry_weak": {"action": "eyebrow_angry_weak", "data": 1.0},
|
||||||
"eyebrow_normal": {"action": "eyebrow_normal", "data": 1.0},
|
"eyebrow_angry_strong": {"action": "eyebrow_angry_strong", "data": 1.0},
|
||||||
|
"eyebrow_sad_weak": {"action": "eyebrow_sad_weak", "data": 1.0},
|
||||||
|
"eyebrow_sad_strong": {"action": "eyebrow_sad_strong", "data": 1.0},
|
||||||
|
# "eyebrow_smile": {"action": "eyebrow_smile", "data": 1.0}, # 未定义,占位
|
||||||
|
# "eyebrow_angry": {"action": "eyebrow_angry", "data": 1.0}, # 未定义,占位
|
||||||
|
# "eyebrow_sad": {"action": "eyebrow_sad", "data": 1.0}, # 未定义,占位
|
||||||
|
# "eyebrow_normal": {"action": "eyebrow_normal", "data": 1.0}, # 未定义,占位
|
||||||
|
|
||||||
# 嘴巴表情
|
# 嘴巴表情(注意:用户定义的是mouth,可能是mouth的拼写错误)
|
||||||
"mouth_sad": {"action": "mouth_sad", "data": 1.0},
|
"mouth_default": {"action": "mouth_default", "data": 1.0},
|
||||||
"mouth_angry": {"action": "mouth_angry", "data": 1.0},
|
"mouth_happy_strong": {"action": "mouth_happy_strong", "data": 1.0}, # 保持用户原始拼写
|
||||||
"mouth_laugh": {"action": "mouth_laugh", "data": 1.0},
|
"mouth_angry_weak": {"action": "mouth_angry_weak", "data": 1.0},
|
||||||
"mouth_pout": {"action": "mouth_pout", "data": 1.0},
|
# "mouth_sad": {"action": "mouth_sad", "data": 1.0}, # 未定义,占位
|
||||||
"mouth_normal": {"action": "mouth_normal", "data": 1.0},
|
# "mouth_angry": {"action": "mouth_angry", "data": 1.0}, # 未定义,占位
|
||||||
|
# "mouth_laugh": {"action": "mouth_laugh", "data": 1.0}, # 未定义,占位
|
||||||
|
# "mouth_pout": {"action": "mouth_pout", "data": 1.0}, # 未定义,占位
|
||||||
|
# "mouth_normal": {"action": "mouth_normal", "data": 1.0}, # 未定义,占位
|
||||||
|
|
||||||
# 脸部表情
|
# 脸部表情
|
||||||
"face_blush": {"action": "face_blush", "data": 1.0},
|
# "face_blush": {"action": "face_blush", "data": 1.0}, # 未定义,占位
|
||||||
"face_normal": {"action": "face_normal", "data": 1.0},
|
# "face_normal": {"action": "face_normal", "data": 1.0}, # 未定义,占位
|
||||||
}
|
}
|
||||||
|
|
||||||
# 表情组合模板
|
# 表情组合模板(根据新的表情动作调整)
|
||||||
self.expression_combinations = {
|
self.expression_combinations = {
|
||||||
"happy": {
|
"happy": {
|
||||||
"eye": "eye_smile",
|
"eye": "eye_happy_weak",
|
||||||
"eyebrow": "eyebrow_smile",
|
"eyebrow": "eyebrow_happy_weak",
|
||||||
"mouth": "mouth_laugh",
|
"mouth": "mouth_default",
|
||||||
"face": "face_normal"
|
|
||||||
},
|
},
|
||||||
"very_happy": {
|
"very_happy": {
|
||||||
"eye": "eye_smile",
|
"eye": "eye_happy_weak",
|
||||||
"eyebrow": "eyebrow_smile",
|
"eyebrow": "eyebrow_happy_strong",
|
||||||
"mouth": "mouth_laugh",
|
"mouth": "mouth_happy_strong",
|
||||||
"face": "face_blush"
|
|
||||||
},
|
},
|
||||||
"sad": {
|
"sad": {
|
||||||
"eye": "eye_cry",
|
"eyebrow": "eyebrow_sad_strong",
|
||||||
"eyebrow": "eyebrow_sad",
|
"mouth": "mouth_default",
|
||||||
"mouth": "mouth_sad",
|
|
||||||
"face": "face_normal"
|
|
||||||
},
|
},
|
||||||
"angry": {
|
"angry": {
|
||||||
"eye": "eye_normal",
|
"eyebrow": "eyebrow_angry_strong",
|
||||||
"eyebrow": "eyebrow_angry",
|
"mouth": "mouth_angry_weak",
|
||||||
"mouth": "mouth_angry",
|
|
||||||
"face": "face_normal"
|
|
||||||
},
|
},
|
||||||
"fear": {
|
"fear": {
|
||||||
"eye": "eye_close",
|
"eyebrow": "eyebrow_sad_weak",
|
||||||
"eyebrow": "eyebrow_normal",
|
"mouth": "mouth_default",
|
||||||
"mouth": "mouth_normal",
|
|
||||||
"face": "face_normal"
|
|
||||||
},
|
},
|
||||||
"shy": {
|
"shy": {
|
||||||
"eye": "eye_normal",
|
"eyebrow": "eyebrow_happy_weak",
|
||||||
"eyebrow": "eyebrow_normal",
|
"mouth": "mouth_default",
|
||||||
"mouth": "mouth_pout",
|
|
||||||
"face": "face_blush"
|
|
||||||
},
|
},
|
||||||
"neutral": {
|
"neutral": {
|
||||||
"eye": "eye_normal",
|
"eyebrow": "eyebrow_happy_weak",
|
||||||
"eyebrow": "eyebrow_normal",
|
"mouth": "mouth_default",
|
||||||
"mouth": "mouth_normal",
|
|
||||||
"face": "face_normal"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# 未定义的表情部位(保留备用):
|
||||||
|
# 眼睛:eye_smile, eye_cry, eye_close, eye_normal
|
||||||
|
# 眉毛:eyebrow_smile, eyebrow_angry, eyebrow_sad, eyebrow_normal
|
||||||
|
# 嘴巴:mouth_sad, mouth_angry, mouth_laugh, mouth_pout, mouth_normal
|
||||||
|
# 脸部:face_blush, face_normal
|
||||||
|
|
||||||
|
# 初始化当前表情状态
|
||||||
|
self.last_expression = "neutral"
|
||||||
|
|
||||||
def select_expression_by_mood(self, mood_values: dict[str, int]) -> str:
|
def select_expression_by_mood(self, mood_values: dict[str, int]) -> str:
|
||||||
"""根据情绪值选择合适的表情组合"""
|
"""根据情绪值选择合适的表情组合"""
|
||||||
joy = mood_values.get("joy", 5)
|
joy = mood_values.get("joy", 5)
|
||||||
@@ -240,25 +260,86 @@ class FacialExpression:
|
|||||||
else:
|
else:
|
||||||
return "neutral"
|
return "neutral"
|
||||||
|
|
||||||
async def send_expression(self, expression_name: str):
|
async def _send_expression_actions(self, expression_name: str, log_prefix: str = "发送面部表情", override_values: dict = None):
|
||||||
"""发送表情组合"""
|
"""统一的表情动作发送函数 - 发送完整的表情状态
|
||||||
|
|
||||||
|
Args:
|
||||||
|
expression_name: 表情名称
|
||||||
|
log_prefix: 日志前缀
|
||||||
|
override_values: 需要覆盖的动作值,格式为 {"action_name": value}
|
||||||
|
"""
|
||||||
if expression_name not in self.expression_combinations:
|
if expression_name not in self.expression_combinations:
|
||||||
logger.warning(f"[{self.chat_id}] 未知表情: {expression_name}")
|
logger.warning(f"[{self.chat_id}] 未知表情: {expression_name}")
|
||||||
return
|
return
|
||||||
|
|
||||||
combination = self.expression_combinations[expression_name]
|
combination = self.expression_combinations[expression_name]
|
||||||
|
|
||||||
# 依次发送各部位表情
|
# 按部位分组所有已定义的表情动作
|
||||||
for part, expression_key in combination.items():
|
expressions_by_part = {
|
||||||
if expression_key in self.expressions:
|
"eye": {},
|
||||||
expression_data = self.expressions[expression_key]
|
"eyebrow": {},
|
||||||
|
"mouth": {}
|
||||||
|
}
|
||||||
|
|
||||||
|
# 将所有已定义的表情按部位分组
|
||||||
|
for expression_key, expression_data in self.expressions.items():
|
||||||
|
if expression_key.startswith("eye_"):
|
||||||
|
expressions_by_part["eye"][expression_key] = expression_data
|
||||||
|
elif expression_key.startswith("eyebrow_"):
|
||||||
|
expressions_by_part["eyebrow"][expression_key] = expression_data
|
||||||
|
elif expression_key.startswith("mouth_"):
|
||||||
|
expressions_by_part["mouth"][expression_key] = expression_data
|
||||||
|
|
||||||
|
# 构建完整的表情状态
|
||||||
|
complete_expression_state = {}
|
||||||
|
|
||||||
|
# 为每个部位构建完整的表情动作状态
|
||||||
|
for part in expressions_by_part.keys():
|
||||||
|
if expressions_by_part[part]: # 如果该部位有已定义的表情
|
||||||
|
part_actions = {}
|
||||||
|
active_expression = combination.get(part) # 当前激活的表情
|
||||||
|
|
||||||
|
# 添加该部位所有已定义的表情动作
|
||||||
|
for expression_key, expression_data in expressions_by_part[part].items():
|
||||||
|
# 复制表情数据并设置激活状态
|
||||||
|
action_data = expression_data.copy()
|
||||||
|
|
||||||
|
# 检查是否有覆盖值
|
||||||
|
if override_values and expression_key in override_values:
|
||||||
|
action_data["data"] = override_values[expression_key]
|
||||||
|
else:
|
||||||
|
action_data["data"] = 1.0 if expression_key == active_expression else 0.0
|
||||||
|
|
||||||
|
part_actions[expression_key] = action_data
|
||||||
|
|
||||||
|
complete_expression_state[part] = part_actions
|
||||||
|
logger.debug(f"[{self.chat_id}] 部位 {part}: 激活 {active_expression}, 总共 {len(part_actions)} 个动作")
|
||||||
|
|
||||||
|
# 发送完整的表情状态
|
||||||
|
if complete_expression_state:
|
||||||
|
package_data = {
|
||||||
|
"expression_name": expression_name,
|
||||||
|
"actions": complete_expression_state
|
||||||
|
}
|
||||||
|
|
||||||
await send_api.custom_to_stream(
|
await send_api.custom_to_stream(
|
||||||
message_type="facial_expression",
|
message_type="face_emotion",
|
||||||
content=expression_data,
|
content=package_data,
|
||||||
stream_id=self.chat_id
|
stream_id=self.chat_id,
|
||||||
|
storage_message=False,
|
||||||
|
show_log=False,
|
||||||
)
|
)
|
||||||
logger.info(f"[{self.chat_id}] 发送面部表情 {part}: {expression_data}")
|
|
||||||
await asyncio.sleep(0.1) # 短暂延迟避免同时发送过多消息
|
# 统计信息
|
||||||
|
total_actions = sum(len(part_actions) for part_actions in complete_expression_state.values())
|
||||||
|
active_actions = [f"{part}:{combination.get(part, 'none')}" for part in complete_expression_state.keys()]
|
||||||
|
logger.info(f"[{self.chat_id}] {log_prefix}: {expression_name} - 发送{total_actions}个动作,激活: {', '.join(active_actions)}")
|
||||||
|
else:
|
||||||
|
logger.warning(f"[{self.chat_id}] 表情 {expression_name} 没有有效的动作可发送")
|
||||||
|
|
||||||
|
async def send_expression(self, expression_name: str):
|
||||||
|
"""发送表情组合"""
|
||||||
|
await self._send_expression_actions(expression_name, "发送面部表情")
|
||||||
|
|
||||||
# 通知ChatMood需要更新amadus
|
# 通知ChatMood需要更新amadus
|
||||||
# 这里需要从mood_manager获取ChatMood实例并标记
|
# 这里需要从mood_manager获取ChatMood实例并标记
|
||||||
@@ -277,6 +358,78 @@ class FacialExpression:
|
|||||||
"""重置为中性表情"""
|
"""重置为中性表情"""
|
||||||
await self.send_expression("neutral")
|
await self.send_expression("neutral")
|
||||||
|
|
||||||
|
async def perform_blink(self):
|
||||||
|
"""执行眨眼动作"""
|
||||||
|
# 检查当前表情组合中eye部位是否为eye_happy_weak
|
||||||
|
current_combination = self.expression_combinations.get(self.last_expression, {})
|
||||||
|
current_eye_expression = current_combination.get("eye")
|
||||||
|
|
||||||
|
if current_eye_expression == "eye_happy_weak":
|
||||||
|
logger.debug(f"[{self.chat_id}] 当前eye表情为{current_eye_expression},跳过眨眼动作")
|
||||||
|
return
|
||||||
|
|
||||||
|
logger.debug(f"[{self.chat_id}] 执行眨眼动作")
|
||||||
|
|
||||||
|
# 第一阶段:闭眼
|
||||||
|
await self._send_expression_actions(
|
||||||
|
self.last_expression,
|
||||||
|
"眨眼-闭眼",
|
||||||
|
override_values={"eye_close": 1.0}
|
||||||
|
)
|
||||||
|
|
||||||
|
# 等待0.1-0.15秒
|
||||||
|
blink_duration = random.uniform(0.7, 0.12)
|
||||||
|
await asyncio.sleep(blink_duration)
|
||||||
|
|
||||||
|
# 第二阶段:睁眼
|
||||||
|
await self._send_expression_actions(
|
||||||
|
self.last_expression,
|
||||||
|
"眨眼-睁眼",
|
||||||
|
override_values={"eye_close": 0.0}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def perform_shift(self):
|
||||||
|
"""执行眨眼动作"""
|
||||||
|
# 检查当前表情组合中eye部位是否为eye_happy_weak
|
||||||
|
current_combination = self.expression_combinations.get(self.last_expression, {})
|
||||||
|
current_eye_expression = current_combination.get("eye")
|
||||||
|
|
||||||
|
direction = random.choice(["left", "right"])
|
||||||
|
strength = random.randint(6, 9) / 10
|
||||||
|
time_duration = random.randint(5, 15) / 10
|
||||||
|
|
||||||
|
if current_eye_expression == "eye_happy_weak" or current_eye_expression == "eye_close":
|
||||||
|
logger.debug(f"[{self.chat_id}] 当前eye表情为{current_eye_expression},跳过漂移动作")
|
||||||
|
return
|
||||||
|
|
||||||
|
logger.debug(f"[{self.chat_id}] 执行漂移动作,方向:{direction},强度:{strength},时间:{time_duration}")
|
||||||
|
|
||||||
|
if direction == "left":
|
||||||
|
override_values = {"eye_shift_left": strength}
|
||||||
|
back_values = {"eye_shift_left": 0.0}
|
||||||
|
else:
|
||||||
|
override_values = {"eye_shift_right": strength}
|
||||||
|
back_values = {"eye_shift_right": 0.0}
|
||||||
|
|
||||||
|
# 第一阶段:闭眼
|
||||||
|
await self._send_expression_actions(
|
||||||
|
self.last_expression,
|
||||||
|
"漂移",
|
||||||
|
override_values=override_values
|
||||||
|
)
|
||||||
|
|
||||||
|
# 等待0.1-0.15秒
|
||||||
|
await asyncio.sleep(time_duration)
|
||||||
|
|
||||||
|
# 第二阶段:睁眼
|
||||||
|
await self._send_expression_actions(
|
||||||
|
self.last_expression,
|
||||||
|
"回归",
|
||||||
|
override_values=back_values
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ChatMood:
|
class ChatMood:
|
||||||
def __init__(self, chat_id: str):
|
def __init__(self, chat_id: str):
|
||||||
@@ -504,51 +657,150 @@ class ChatMood:
|
|||||||
async def send_expression_update_if_needed(self):
|
async def send_expression_update_if_needed(self):
|
||||||
"""如果表情有变化,发送更新到amadus"""
|
"""如果表情有变化,发送更新到amadus"""
|
||||||
if self.expression_needs_update:
|
if self.expression_needs_update:
|
||||||
# 发送当前表情状态到amadus,使用简洁的action/data格式
|
# 使用统一的表情发送函数
|
||||||
expression_data = {
|
await self.facial_expression._send_expression_actions(
|
||||||
"action": self.last_expression,
|
self.last_expression,
|
||||||
"data": 1.0
|
"发送表情更新到amadus"
|
||||||
}
|
|
||||||
|
|
||||||
await send_api.custom_to_stream(
|
|
||||||
message_type="amadus_expression_update",
|
|
||||||
content=expression_data,
|
|
||||||
stream_id=self.chat_id
|
|
||||||
)
|
)
|
||||||
|
|
||||||
logger.info(f"[{self.chat_id}] 发送表情更新到amadus: {expression_data}")
|
|
||||||
self.expression_needs_update = False # 重置标记
|
self.expression_needs_update = False # 重置标记
|
||||||
|
|
||||||
|
async def perform_blink(self):
|
||||||
|
"""执行眨眼动作"""
|
||||||
|
await self.facial_expression.perform_blink()
|
||||||
|
|
||||||
|
async def perform_shift(self):
|
||||||
|
"""执行漂移动作"""
|
||||||
|
await self.facial_expression.perform_shift()
|
||||||
|
|
||||||
|
|
||||||
class MoodRegressionTask(AsyncTask):
|
class MoodRegressionTask(AsyncTask):
|
||||||
def __init__(self, mood_manager: "MoodManager"):
|
def __init__(self, mood_manager: "MoodManager"):
|
||||||
super().__init__(task_name="MoodRegressionTask", run_interval=30)
|
super().__init__(task_name="MoodRegressionTask", run_interval=30)
|
||||||
self.mood_manager = mood_manager
|
self.mood_manager = mood_manager
|
||||||
|
self.run_count = 0
|
||||||
|
|
||||||
async def run(self):
|
async def run(self):
|
||||||
logger.debug("Running mood regression task...")
|
self.run_count += 1
|
||||||
|
logger.info(f"[回归任务] 第{self.run_count}次检查,当前管理{len(self.mood_manager.mood_list)}个聊天的情绪状态")
|
||||||
|
|
||||||
now = time.time()
|
now = time.time()
|
||||||
|
regression_executed = 0
|
||||||
|
|
||||||
for mood in self.mood_manager.mood_list:
|
for mood in self.mood_manager.mood_list:
|
||||||
|
chat_info = f"chat {mood.chat_id}"
|
||||||
|
|
||||||
if mood.last_change_time == 0:
|
if mood.last_change_time == 0:
|
||||||
|
logger.debug(f"[回归任务] {chat_info} 尚未有情绪变化,跳过回归")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if now - mood.last_change_time > 180:
|
time_since_last_change = now - mood.last_change_time
|
||||||
|
|
||||||
|
if time_since_last_change > 120: # 2分钟
|
||||||
if mood.regression_count >= 3:
|
if mood.regression_count >= 3:
|
||||||
|
logger.debug(f"[回归任务] {chat_info} 已达到最大回归次数(3次),停止回归")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
logger.info(f"chat {mood.chat_id} 开始情绪回归, 这是第 {mood.regression_count + 1} 次")
|
logger.info(f"[回归任务] {chat_info} 开始情绪回归 (距上次变化{int(time_since_last_change)}秒,第{mood.regression_count + 1}次回归)")
|
||||||
await mood.regress_mood()
|
await mood.regress_mood()
|
||||||
|
regression_executed += 1
|
||||||
|
else:
|
||||||
|
remaining_time = 120 - time_since_last_change
|
||||||
|
logger.debug(f"[回归任务] {chat_info} 距离回归还需等待{int(remaining_time)}秒")
|
||||||
|
|
||||||
|
if regression_executed > 0:
|
||||||
|
logger.info(f"[回归任务] 本次执行了{regression_executed}个聊天的情绪回归")
|
||||||
|
else:
|
||||||
|
logger.debug(f"[回归任务] 本次没有符合回归条件的聊天")
|
||||||
|
|
||||||
|
|
||||||
class ExpressionUpdateTask(AsyncTask):
|
class ExpressionUpdateTask(AsyncTask):
|
||||||
def __init__(self, mood_manager: "MoodManager"):
|
def __init__(self, mood_manager: "MoodManager"):
|
||||||
super().__init__(task_name="ExpressionUpdateTask", run_interval=1)
|
super().__init__(task_name="ExpressionUpdateTask", run_interval=0.3)
|
||||||
self.mood_manager = mood_manager
|
self.mood_manager = mood_manager
|
||||||
|
self.run_count = 0
|
||||||
|
self.last_log_time = 0
|
||||||
|
|
||||||
async def run(self):
|
async def run(self):
|
||||||
logger.debug("Running expression update task...")
|
self.run_count += 1
|
||||||
|
now = time.time()
|
||||||
|
|
||||||
|
# 每60秒输出一次状态信息(避免日志太频繁)
|
||||||
|
if now - self.last_log_time > 60:
|
||||||
|
logger.info(f"[表情任务] 已运行{self.run_count}次,当前管理{len(self.mood_manager.mood_list)}个聊天的表情状态")
|
||||||
|
self.last_log_time = now
|
||||||
|
|
||||||
|
updates_sent = 0
|
||||||
for mood in self.mood_manager.mood_list:
|
for mood in self.mood_manager.mood_list:
|
||||||
|
if mood.expression_needs_update:
|
||||||
|
logger.debug(f"[表情任务] chat {mood.chat_id} 检测到表情变化,发送更新")
|
||||||
await mood.send_expression_update_if_needed()
|
await mood.send_expression_update_if_needed()
|
||||||
|
updates_sent += 1
|
||||||
|
|
||||||
|
if updates_sent > 0:
|
||||||
|
logger.info(f"[表情任务] 发送了{updates_sent}个表情更新")
|
||||||
|
|
||||||
|
|
||||||
|
class BlinkTask(AsyncTask):
|
||||||
|
def __init__(self, mood_manager: "MoodManager"):
|
||||||
|
# 初始随机间隔4-6秒
|
||||||
|
super().__init__(task_name="BlinkTask", run_interval=4)
|
||||||
|
self.mood_manager = mood_manager
|
||||||
|
self.run_count = 0
|
||||||
|
self.last_log_time = 0
|
||||||
|
|
||||||
|
async def run(self):
|
||||||
|
self.run_count += 1
|
||||||
|
now = time.time()
|
||||||
|
|
||||||
|
# 每60秒输出一次状态信息(避免日志太频繁)
|
||||||
|
if now - self.last_log_time > 20:
|
||||||
|
logger.debug(f"[眨眼任务] 已运行{self.run_count}次,当前管理{len(self.mood_manager.mood_list)}个聊天的眨眼状态")
|
||||||
|
self.last_log_time = now
|
||||||
|
|
||||||
|
interval_add = random.randint(0, 2)
|
||||||
|
await asyncio.sleep(interval_add)
|
||||||
|
|
||||||
|
blinks_executed = 0
|
||||||
|
for mood in self.mood_manager.mood_list:
|
||||||
|
try:
|
||||||
|
await mood.perform_blink()
|
||||||
|
blinks_executed += 1
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"[眨眼任务] 处理chat {mood.chat_id}时出错: {e}")
|
||||||
|
|
||||||
|
if blinks_executed > 0:
|
||||||
|
logger.debug(f"[眨眼任务] 本次执行了{blinks_executed}个聊天的眨眼动作")
|
||||||
|
|
||||||
|
class ShiftTask(AsyncTask):
|
||||||
|
def __init__(self, mood_manager: "MoodManager"):
|
||||||
|
# 初始随机间隔4-6秒
|
||||||
|
super().__init__(task_name="ShiftTask", run_interval=8)
|
||||||
|
self.mood_manager = mood_manager
|
||||||
|
self.run_count = 0
|
||||||
|
self.last_log_time = 0
|
||||||
|
|
||||||
|
async def run(self):
|
||||||
|
self.run_count += 1
|
||||||
|
now = time.time()
|
||||||
|
|
||||||
|
# 每60秒输出一次状态信息(避免日志太频繁)
|
||||||
|
if now - self.last_log_time > 20:
|
||||||
|
logger.debug(f"[漂移任务] 已运行{self.run_count}次,当前管理{len(self.mood_manager.mood_list)}个聊天的漂移状态")
|
||||||
|
self.last_log_time = now
|
||||||
|
|
||||||
|
interval_add = random.randint(0, 3)
|
||||||
|
await asyncio.sleep(interval_add)
|
||||||
|
|
||||||
|
blinks_executed = 0
|
||||||
|
for mood in self.mood_manager.mood_list:
|
||||||
|
try:
|
||||||
|
await mood.perform_shift()
|
||||||
|
blinks_executed += 1
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"[漂移任务] 处理chat {mood.chat_id}时出错: {e}")
|
||||||
|
|
||||||
|
if blinks_executed > 0:
|
||||||
|
logger.debug(f"[漂移任务] 本次执行了{blinks_executed}个聊天的漂移动作")
|
||||||
|
|
||||||
|
|
||||||
class MoodManager:
|
class MoodManager:
|
||||||
@@ -572,8 +824,16 @@ class MoodManager:
|
|||||||
expression_task = ExpressionUpdateTask(self)
|
expression_task = ExpressionUpdateTask(self)
|
||||||
await async_task_manager.add_task(expression_task)
|
await async_task_manager.add_task(expression_task)
|
||||||
|
|
||||||
|
# 启动眨眼任务
|
||||||
|
blink_task = BlinkTask(self)
|
||||||
|
await async_task_manager.add_task(blink_task)
|
||||||
|
|
||||||
|
# 启动漂移任务
|
||||||
|
shift_task = ShiftTask(self)
|
||||||
|
await async_task_manager.add_task(shift_task)
|
||||||
|
|
||||||
self.task_started = True
|
self.task_started = True
|
||||||
logger.info("情绪管理任务已启动(包含情绪回归和表情更新)")
|
logger.info("情绪管理任务已启动(包含情绪回归、表情更新和眨眼动作)")
|
||||||
|
|
||||||
def get_mood_by_chat_id(self, chat_id: str) -> ChatMood:
|
def get_mood_by_chat_id(self, chat_id: str) -> ChatMood:
|
||||||
for mood in self.mood_list:
|
for mood in self.mood_list:
|
||||||
@@ -617,4 +877,5 @@ class MoodManager:
|
|||||||
init_prompt()
|
init_prompt()
|
||||||
|
|
||||||
mood_manager = MoodManager()
|
mood_manager = MoodManager()
|
||||||
|
|
||||||
"""全局情绪管理器"""
|
"""全局情绪管理器"""
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ from src.common.logger import get_logger
|
|||||||
from src.config.config import global_config
|
from src.config.config import global_config
|
||||||
from src.mais4u.mais4u_chat.body_emotion_action_manager import action_manager
|
from src.mais4u.mais4u_chat.body_emotion_action_manager import action_manager
|
||||||
from src.mais4u.mais4u_chat.s4u_mood_manager import mood_manager
|
from src.mais4u.mais4u_chat.s4u_mood_manager import mood_manager
|
||||||
|
from src.mais4u.mais4u_chat.s4u_watching_manager import watching_manager
|
||||||
|
|
||||||
from .s4u_chat import get_s4u_chat_manager
|
from .s4u_chat import get_s4u_chat_manager
|
||||||
|
|
||||||
@@ -102,11 +103,17 @@ class S4UMessageProcessor:
|
|||||||
|
|
||||||
interested_rate, _ = await _calculate_interest(message)
|
interested_rate, _ = await _calculate_interest(message)
|
||||||
|
|
||||||
|
await mood_manager.start()
|
||||||
|
|
||||||
chat_mood = mood_manager.get_mood_by_chat_id(chat.stream_id)
|
chat_mood = mood_manager.get_mood_by_chat_id(chat.stream_id)
|
||||||
asyncio.create_task(chat_mood.update_mood_by_message(message))
|
asyncio.create_task(chat_mood.update_mood_by_message(message))
|
||||||
chat_action = action_manager.get_action_state_by_chat_id(chat.stream_id)
|
chat_action = action_manager.get_action_state_by_chat_id(chat.stream_id)
|
||||||
asyncio.create_task(chat_action.update_action_by_message(message))
|
asyncio.create_task(chat_action.update_action_by_message(message))
|
||||||
# asyncio.create_task(chat_action.update_facial_expression_by_message(message, interested_rate))
|
# asyncio.create_task(chat_action.update_facial_expression_by_message(message, interested_rate))
|
||||||
|
|
||||||
|
# 视线管理:收到消息时切换视线状态
|
||||||
|
chat_watching = watching_manager.get_watching_by_chat_id(chat.stream_id)
|
||||||
|
asyncio.create_task(chat_watching.on_message_received())
|
||||||
|
|
||||||
# 7. 日志记录
|
# 7. 日志记录
|
||||||
logger.info(f"[S4U]{userinfo.user_nickname}:{message.processed_plain_text}")
|
logger.info(f"[S4U]{userinfo.user_nickname}:{message.processed_plain_text}")
|
||||||
|
|||||||
@@ -107,7 +107,6 @@ class S4UStreamGenerator:
|
|||||||
model_name: str,
|
model_name: str,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
) -> AsyncGenerator[str, None]:
|
) -> AsyncGenerator[str, None]:
|
||||||
print(prompt)
|
|
||||||
|
|
||||||
buffer = ""
|
buffer = ""
|
||||||
delimiters = ",。!?,.!?\n\r" # For final trimming
|
delimiters = ",。!?,.!?\n\r" # For final trimming
|
||||||
|
|||||||
210
src/mais4u/mais4u_chat/s4u_watching_manager.py
Normal file
210
src/mais4u/mais4u_chat/s4u_watching_manager.py
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
import asyncio
|
||||||
|
import time
|
||||||
|
from enum import Enum
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from src.common.logger import get_logger
|
||||||
|
from src.plugin_system.apis import send_api
|
||||||
|
|
||||||
|
"""
|
||||||
|
视线管理系统使用说明:
|
||||||
|
|
||||||
|
1. 视线状态:
|
||||||
|
- wandering: 随意看
|
||||||
|
- danmu: 看弹幕
|
||||||
|
- lens: 看镜头
|
||||||
|
|
||||||
|
2. 状态切换逻辑:
|
||||||
|
- 收到消息时 → 切换为看弹幕,立即发送更新
|
||||||
|
- 开始生成回复时 → 切换为看镜头或随意,立即发送更新
|
||||||
|
- 生成完毕后 → 看弹幕1秒,然后回到看镜头直到有新消息,状态变化时立即发送更新
|
||||||
|
|
||||||
|
3. 使用方法:
|
||||||
|
# 获取视线管理器
|
||||||
|
watching = watching_manager.get_watching_by_chat_id(chat_id)
|
||||||
|
|
||||||
|
# 收到消息时调用
|
||||||
|
await watching.on_message_received()
|
||||||
|
|
||||||
|
# 开始生成回复时调用
|
||||||
|
await watching.on_reply_start()
|
||||||
|
|
||||||
|
# 生成回复完毕时调用
|
||||||
|
await watching.on_reply_finished()
|
||||||
|
|
||||||
|
4. 自动更新系统:
|
||||||
|
- 状态变化时立即发送type为"watching",data为状态值的websocket消息
|
||||||
|
- 使用定时器自动处理状态转换(如看弹幕时间结束后自动切换到看镜头)
|
||||||
|
- 无需定期检查,所有状态变化都是事件驱动的
|
||||||
|
"""
|
||||||
|
|
||||||
|
logger = get_logger("watching")
|
||||||
|
|
||||||
|
|
||||||
|
class WatchingState(Enum):
|
||||||
|
"""视线状态枚举"""
|
||||||
|
WANDERING = "wandering" # 随意看
|
||||||
|
DANMU = "danmu" # 看弹幕
|
||||||
|
LENS = "lens" # 看镜头
|
||||||
|
|
||||||
|
|
||||||
|
class ChatWatching:
|
||||||
|
def __init__(self, chat_id: str):
|
||||||
|
self.chat_id: str = chat_id
|
||||||
|
self.current_state: WatchingState = WatchingState.LENS # 默认看镜头
|
||||||
|
self.last_sent_state: Optional[WatchingState] = None # 上次发送的状态
|
||||||
|
self.state_needs_update: bool = True # 是否需要更新状态
|
||||||
|
|
||||||
|
# 状态切换相关
|
||||||
|
self.is_replying: bool = False # 是否正在生成回复
|
||||||
|
self.reply_finished_time: Optional[float] = None # 回复完成时间
|
||||||
|
self.danmu_viewing_duration: float = 1.0 # 看弹幕持续时间(秒)
|
||||||
|
|
||||||
|
logger.info(f"[{self.chat_id}] 视线管理器初始化,默认状态: {self.current_state.value}")
|
||||||
|
|
||||||
|
async def _change_state(self, new_state: WatchingState, reason: str = ""):
|
||||||
|
"""内部状态切换方法"""
|
||||||
|
if self.current_state != new_state:
|
||||||
|
old_state = self.current_state
|
||||||
|
self.current_state = new_state
|
||||||
|
self.state_needs_update = True
|
||||||
|
logger.info(f"[{self.chat_id}] 视线状态切换: {old_state.value} → {new_state.value} ({reason})")
|
||||||
|
|
||||||
|
# 立即发送视线状态更新
|
||||||
|
await self._send_watching_update()
|
||||||
|
else:
|
||||||
|
logger.debug(f"[{self.chat_id}] 状态无变化,保持: {new_state.value} ({reason})")
|
||||||
|
|
||||||
|
async def on_message_received(self):
|
||||||
|
"""收到消息时调用"""
|
||||||
|
if not self.is_replying: # 只有在非回复状态下才切换到看弹幕
|
||||||
|
await self._change_state(WatchingState.DANMU, "收到消息")
|
||||||
|
else:
|
||||||
|
logger.debug(f"[{self.chat_id}] 正在生成回复中,暂不切换到弹幕状态")
|
||||||
|
|
||||||
|
async def on_reply_start(self, look_at_lens: bool = True):
|
||||||
|
"""开始生成回复时调用"""
|
||||||
|
self.is_replying = True
|
||||||
|
self.reply_finished_time = None
|
||||||
|
|
||||||
|
if look_at_lens:
|
||||||
|
await self._change_state(WatchingState.LENS, "开始生成回复-看镜头")
|
||||||
|
else:
|
||||||
|
await self._change_state(WatchingState.WANDERING, "开始生成回复-随意看")
|
||||||
|
|
||||||
|
async def on_reply_finished(self):
|
||||||
|
"""生成回复完毕时调用"""
|
||||||
|
self.is_replying = False
|
||||||
|
self.reply_finished_time = time.time()
|
||||||
|
|
||||||
|
# 先看弹幕1秒
|
||||||
|
await self._change_state(WatchingState.DANMU, "回复完毕-看弹幕")
|
||||||
|
logger.info(f"[{self.chat_id}] 回复完毕,将看弹幕{self.danmu_viewing_duration}秒后转为看镜头")
|
||||||
|
|
||||||
|
# 设置定时器,1秒后自动切换到看镜头
|
||||||
|
asyncio.create_task(self._auto_switch_to_lens())
|
||||||
|
|
||||||
|
async def _auto_switch_to_lens(self):
|
||||||
|
"""自动切换到看镜头(延迟执行)"""
|
||||||
|
await asyncio.sleep(self.danmu_viewing_duration)
|
||||||
|
|
||||||
|
# 检查是否仍需要切换(可能状态已经被其他事件改变)
|
||||||
|
if (self.reply_finished_time is not None and
|
||||||
|
self.current_state == WatchingState.DANMU and
|
||||||
|
not self.is_replying):
|
||||||
|
|
||||||
|
await self._change_state(WatchingState.LENS, "看弹幕时间结束")
|
||||||
|
self.reply_finished_time = None # 重置完成时间
|
||||||
|
|
||||||
|
async def _send_watching_update(self):
|
||||||
|
"""立即发送视线状态更新"""
|
||||||
|
await send_api.custom_to_stream(
|
||||||
|
message_type="watching",
|
||||||
|
content=self.current_state.value,
|
||||||
|
stream_id=self.chat_id
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info(f"[{self.chat_id}] 发送视线状态更新: {self.current_state.value}")
|
||||||
|
self.last_sent_state = self.current_state
|
||||||
|
self.state_needs_update = False
|
||||||
|
|
||||||
|
def get_current_state(self) -> WatchingState:
|
||||||
|
"""获取当前视线状态"""
|
||||||
|
return self.current_state
|
||||||
|
|
||||||
|
def get_state_info(self) -> dict:
|
||||||
|
"""获取状态信息(用于调试)"""
|
||||||
|
return {
|
||||||
|
"current_state": self.current_state.value,
|
||||||
|
"is_replying": self.is_replying,
|
||||||
|
"reply_finished_time": self.reply_finished_time,
|
||||||
|
"state_needs_update": self.state_needs_update
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class WatchingManager:
|
||||||
|
def __init__(self):
|
||||||
|
self.watching_list: list[ChatWatching] = []
|
||||||
|
"""当前视线状态列表"""
|
||||||
|
self.task_started: bool = False
|
||||||
|
|
||||||
|
async def start(self):
|
||||||
|
"""启动视线管理系统"""
|
||||||
|
if self.task_started:
|
||||||
|
return
|
||||||
|
|
||||||
|
logger.info("启动视线管理系统...")
|
||||||
|
|
||||||
|
self.task_started = True
|
||||||
|
logger.info("视线管理系统已启动(状态变化时立即发送)")
|
||||||
|
|
||||||
|
def get_watching_by_chat_id(self, chat_id: str) -> ChatWatching:
|
||||||
|
"""获取或创建聊天对应的视线管理器"""
|
||||||
|
for watching in self.watching_list:
|
||||||
|
if watching.chat_id == chat_id:
|
||||||
|
return watching
|
||||||
|
|
||||||
|
new_watching = ChatWatching(chat_id)
|
||||||
|
self.watching_list.append(new_watching)
|
||||||
|
logger.info(f"为chat {chat_id}创建新的视线管理器")
|
||||||
|
|
||||||
|
# 发送初始状态
|
||||||
|
asyncio.create_task(new_watching._send_watching_update())
|
||||||
|
|
||||||
|
return new_watching
|
||||||
|
|
||||||
|
def reset_watching_by_chat_id(self, chat_id: str):
|
||||||
|
"""重置聊天的视线状态"""
|
||||||
|
for watching in self.watching_list:
|
||||||
|
if watching.chat_id == chat_id:
|
||||||
|
watching.current_state = WatchingState.LENS
|
||||||
|
watching.last_sent_state = None
|
||||||
|
watching.state_needs_update = True
|
||||||
|
watching.is_replying = False
|
||||||
|
watching.reply_finished_time = None
|
||||||
|
logger.info(f"[{chat_id}] 视线状态已重置为默认状态")
|
||||||
|
|
||||||
|
# 发送重置后的状态
|
||||||
|
asyncio.create_task(watching._send_watching_update())
|
||||||
|
return
|
||||||
|
|
||||||
|
# 如果没有找到现有的watching,创建新的
|
||||||
|
new_watching = ChatWatching(chat_id)
|
||||||
|
self.watching_list.append(new_watching)
|
||||||
|
logger.info(f"为chat {chat_id}创建并重置视线管理器")
|
||||||
|
|
||||||
|
# 发送初始状态
|
||||||
|
asyncio.create_task(new_watching._send_watching_update())
|
||||||
|
|
||||||
|
def get_all_watching_info(self) -> dict:
|
||||||
|
"""获取所有聊天的视线状态信息(用于调试)"""
|
||||||
|
return {
|
||||||
|
watching.chat_id: watching.get_state_info()
|
||||||
|
for watching in self.watching_list
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# 全局视线管理器实例
|
||||||
|
watching_manager = WatchingManager()
|
||||||
|
"""全局视线管理器"""
|
||||||
@@ -51,6 +51,7 @@ async def _send_to_target(
|
|||||||
typing: bool = False,
|
typing: bool = False,
|
||||||
reply_to: str = "",
|
reply_to: str = "",
|
||||||
storage_message: bool = True,
|
storage_message: bool = True,
|
||||||
|
show_log: bool = True,
|
||||||
) -> bool:
|
) -> bool:
|
||||||
"""向指定目标发送消息的内部实现
|
"""向指定目标发送消息的内部实现
|
||||||
|
|
||||||
@@ -66,6 +67,7 @@ async def _send_to_target(
|
|||||||
bool: 是否发送成功
|
bool: 是否发送成功
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
|
if show_log:
|
||||||
logger.debug(f"[SendAPI] 发送{message_type}消息到 {stream_id}")
|
logger.debug(f"[SendAPI] 发送{message_type}消息到 {stream_id}")
|
||||||
|
|
||||||
# 查找目标聊天流
|
# 查找目标聊天流
|
||||||
@@ -112,7 +114,7 @@ async def _send_to_target(
|
|||||||
|
|
||||||
# 发送消息
|
# 发送消息
|
||||||
sent_msg = await heart_fc_sender.send_message(
|
sent_msg = await heart_fc_sender.send_message(
|
||||||
bot_message, typing=typing, set_reply=(anchor_message is not None), storage_message=storage_message
|
bot_message, typing=typing, set_reply=(anchor_message is not None), storage_message=storage_message, show_log=show_log
|
||||||
)
|
)
|
||||||
|
|
||||||
if sent_msg:
|
if sent_msg:
|
||||||
@@ -345,6 +347,7 @@ async def custom_to_stream(
|
|||||||
typing: bool = False,
|
typing: bool = False,
|
||||||
reply_to: str = "",
|
reply_to: str = "",
|
||||||
storage_message: bool = True,
|
storage_message: bool = True,
|
||||||
|
show_log: bool = True,
|
||||||
) -> bool:
|
) -> bool:
|
||||||
"""向指定流发送自定义类型消息
|
"""向指定流发送自定义类型消息
|
||||||
|
|
||||||
@@ -356,11 +359,11 @@ async def custom_to_stream(
|
|||||||
typing: 是否显示正在输入
|
typing: 是否显示正在输入
|
||||||
reply_to: 回复消息,格式为"发送者:消息内容"
|
reply_to: 回复消息,格式为"发送者:消息内容"
|
||||||
storage_message: 是否存储消息到数据库
|
storage_message: 是否存储消息到数据库
|
||||||
|
show_log: 是否显示日志
|
||||||
Returns:
|
Returns:
|
||||||
bool: 是否发送成功
|
bool: 是否发送成功
|
||||||
"""
|
"""
|
||||||
return await _send_to_target(message_type, content, stream_id, display_message, typing, reply_to, storage_message)
|
return await _send_to_target(message_type, content, stream_id, display_message, typing, reply_to, storage_message, show_log)
|
||||||
|
|
||||||
|
|
||||||
async def text_to_group(
|
async def text_to_group(
|
||||||
|
|||||||
Reference in New Issue
Block a user