mirror of
https://github.com/AstrBotDevs/AstrBot
synced 2026-07-15 17:30:13 +08:00
feat: Enhance Reply chain handling for Record components (#8527)
* Enhance Reply chain handling for Record components Added processing for Record components within Reply chains, including WAV conversion and STT functionality. * Refactor STT processing for Record components * Add STT record function for voice-to-text processing * Update stage.py * Update stage.py * Update stage.py
This commit is contained in:
@@ -4,7 +4,7 @@ import traceback
|
||||
from collections.abc import AsyncGenerator
|
||||
|
||||
from astrbot.core import logger
|
||||
from astrbot.core.message.components import Image, Plain, Record
|
||||
from astrbot.core.message.components import Image, Plain, Record, Reply
|
||||
from astrbot.core.platform.astr_message_event import AstrMessageEvent
|
||||
from astrbot.core.utils.media_utils import ensure_wav
|
||||
|
||||
@@ -80,6 +80,24 @@ class PreProcessStage(Stage):
|
||||
except Exception as e:
|
||||
logger.warning(f"Voice processing failed: {e}")
|
||||
|
||||
# Also process Record components inside Reply chains (wav conversion)
|
||||
for component in event.get_messages():
|
||||
if isinstance(component, Reply) and component.chain:
|
||||
for idx, reply_comp in enumerate(component.chain):
|
||||
if isinstance(reply_comp, Record):
|
||||
try:
|
||||
original_path = await reply_comp.convert_to_file_path()
|
||||
record_path = await ensure_wav(original_path)
|
||||
if record_path != original_path:
|
||||
event.track_temporary_local_file(record_path)
|
||||
reply_comp.file = record_path
|
||||
reply_comp.path = record_path
|
||||
component.chain[idx] = reply_comp
|
||||
except Exception as e:
|
||||
logger.warning(
|
||||
f"Voice processing in reply chain failed: {e}"
|
||||
)
|
||||
|
||||
# STT
|
||||
if self.stt_settings.get("enable", False):
|
||||
# TODO: 独立
|
||||
@@ -90,27 +108,53 @@ class PreProcessStage(Stage):
|
||||
f"会话 {event.unified_msg_origin} 未配置语音转文本模型。",
|
||||
)
|
||||
return
|
||||
|
||||
async def _stt_record(record_comp: Record, is_reply: bool = False):
|
||||
"""对单个 Record 组件执行语音转文本,成功返回 Plain,失败返回 None。"""
|
||||
prefix = "引用消息" if is_reply else ""
|
||||
try:
|
||||
path = await record_comp.convert_to_file_path()
|
||||
except Exception as e:
|
||||
logger.warning(f"获取{prefix}语音路径失败: {e}")
|
||||
return None
|
||||
|
||||
retry = 5
|
||||
for i in range(retry):
|
||||
try:
|
||||
result = await stt_provider.get_text(audio_url=path)
|
||||
if result:
|
||||
suffix = "(引用消息)" if is_reply else ""
|
||||
logger.info(f"语音转文本{suffix}结果: " + result)
|
||||
return Plain(result)
|
||||
break
|
||||
except FileNotFoundError:
|
||||
# napcat workaround: file may not be ready immediately
|
||||
logger.debug(f"文件尚未就绪 ({path}),重试 {i + 1}/{retry}")
|
||||
await asyncio.sleep(0.5)
|
||||
continue
|
||||
except BaseException as e:
|
||||
logger.error(traceback.format_exc())
|
||||
suffix = "(引用消息)" if is_reply else ""
|
||||
logger.error(f"语音转文本{suffix}失败: {e}")
|
||||
break
|
||||
return None
|
||||
|
||||
message_chain = event.get_messages()
|
||||
for idx, component in enumerate(message_chain):
|
||||
if isinstance(component, Record):
|
||||
path = await component.convert_to_file_path()
|
||||
retry = 5
|
||||
for i in range(retry):
|
||||
try:
|
||||
result = await stt_provider.get_text(audio_url=path)
|
||||
if result:
|
||||
logger.info("语音转文本结果: " + result)
|
||||
message_chain[idx] = Plain(result)
|
||||
event.message_str += result
|
||||
event.message_obj.message_str += result
|
||||
break
|
||||
except FileNotFoundError as e:
|
||||
# napcat workaround
|
||||
logger.warning(e)
|
||||
logger.warning(f"重试中: {i + 1}/{retry}")
|
||||
await asyncio.sleep(0.5)
|
||||
continue
|
||||
except BaseException as e:
|
||||
logger.error(traceback.format_exc())
|
||||
logger.error(f"语音转文本失败: {e}")
|
||||
break
|
||||
plain_comp = await _stt_record(component)
|
||||
if plain_comp:
|
||||
message_chain[idx] = plain_comp
|
||||
event.message_str += plain_comp.text
|
||||
event.message_obj.message_str += plain_comp.text
|
||||
|
||||
# Also STT for Record components inside Reply chains
|
||||
for component in event.get_messages():
|
||||
if isinstance(component, Reply) and component.chain:
|
||||
for idx, reply_comp in enumerate(component.chain):
|
||||
if isinstance(reply_comp, Record):
|
||||
plain_comp = await _stt_record(reply_comp, is_reply=True)
|
||||
if plain_comp:
|
||||
component.chain[idx] = plain_comp
|
||||
event.message_str += plain_comp.text
|
||||
event.message_obj.message_str += plain_comp.text
|
||||
|
||||
Reference in New Issue
Block a user