Files
AskaEth 28afa11aeb feat(channelbridge): 出站语音结论落 voice 列并挂出站 Media(M58)
- voice.Ref 增 SourceTTS 与 FallbackReason,Validate/IsZero/validSource 同步
- 出站成功:INSERT -> 补写 voice 列 -> notify(首帧即带 voice)
- 出站降级:source=tts + status=failed + fallbackReason,正文原样
- 首条气泡挂出站 Media(新开条目,不复制 target.Media)
- gateway historyVoice 投影 fallbackReason
- 新增 outbound_voice.go 与整链 e2e 测试(含变异打红验证时序)
2026-10-27 22:08:38 +08:00

37 lines
1.6 KiB
Go

package channelbridge
import (
"context"
"fmt"
"git.yeij.top/AskaEth/Cyrene_v2/internal/channel/adapter"
)
// 本文件承载出站回复路径(design/16 §4.6 §5):循环产生的文本
// 通过接收该消息的同一账号适配器
// 送回来源会话。
// sendReply 把循环产生的文本送回渠道的来源会话,
// 经由接收该消息的账号适配器(多账号安全,
// design/16 §4.6 §5)。它照搬入站消息的渠道路由字段,
// 使适配器解析出消息来自的同一对端,并经
// 共享的 IM 气泡派发器出口(剥离角色扮演/旁白 + 按 <p> 切分 + 0.5-2s 间隔)。
func (b *Bridge) sendReply(ctx context.Context, in adapter.UnifiedMessage, a adapter.PlatformAdapter, text string) error {
return b.sendReplyVoice(ctx, in, a, text, nil)
}
// sendReplyVoice 是 sendReply 的**带音频**变体:voice 非 nil 时随第一个气泡一起送出
// (出站语音,design/25 §7.1)。正文照发文本 —— 音频是这条回复的另一种呈现,不是
// 文本的替代品,因此音频发不出去时对方仍读得到字。
func (b *Bridge) sendReplyVoice(ctx context.Context, in adapter.UnifiedMessage, a adapter.PlatformAdapter, text string, voice *adapter.Media) error {
if a == nil {
a = b.adapter(in.Platform)
}
if a == nil {
return fmt.Errorf("channelbridge: no adapter for platform %q", in.Platform)
}
return b.sendIMBubblesVoice(ctx, in, func(ctx context.Context, msg adapter.UnifiedMessage) error {
return a.SendMessage(ctx, adapter.UnifiedResponse{Messages: []adapter.UnifiedMessage{msg}})
}, text, voice)
}