Go SDK
下载源码
从 RustPBXGo 的 GitHub 仓库 下载:
git clone github.com/restsend/rustpbxgo
目录结构
rustpbxgo/
├── README.md
├── client.go # SDK 核心实现
├── cmd/ # 示例应用
│ ├── main.go # 程序入口
│ ├── llm.go # 大模型交互逻辑
│ ├── media.go # WebRTC 媒体处理
│ └── webhook.go # Webhook 处理
├── go.mod
└── go.sum
client.go 包含核心数据结构 (命令、事件 和回调函数) 的定义。
cmd/ 目录中包含一个应用示例,包含 SIP/WebRTC 呼叫, 配合 webhook 的呼入处理,以及大模型交互逻辑。
客户端 (Client)
Client 结构体字段主要包括:
endpoint: RustPBX 监听地址。id: 设置连接的会话 id,主要用于接听。OnXXX:处理事件的回调函数。
流程图
client 在调用 Connect 方法时,会创建两个协程 (下图中绿色部分)。
- 一个负责读 WebSocket 消息并解析(上)
- 另一个负责处理消息和发送命令(下)。 当收到事件时,会依据事件类型调用对应的回调函数。
创建客户端
使用 NewClient 函数创建客户端实例:
client := rustpbxgo.NewClient(endpoint, opts...)
参数:
| 参数 | 类型 | 必填 | 描述 |
|---|---|---|---|
endpoint | string | ✅ | RustPBX 服务器地址 |
opts | ...ClientOption | ❌ | 可选配置选项 |
选项:
| 选项 | 参数类型 | 描述 |
|---|---|---|
WithLogger(logger) | *logrus.Logger | 设置日志记录器 |
WithContext(ctx) | context.Context | 设置上下文,用于关闭创建的协程 |
WithID(id) | string | 设置会话 ID,用于接听电话 |
WithDumpEvents(enable) | bool | 启用事件转储 |
示例:
client := rustpbxgo.NewClient(
"ws://localhost:8080",
rustpbxgo.WithLogger(logger),
rustpbxgo.WithContext(ctx),
rustpbxgo.WithID("my-session-id"),
rustpbxgo.WithDumpEvents(true),
)
连接服务器
使用 Connect 方法连接 RustPBX:
err := client.Connect(callType)
参数:
| 参数 | 类型 | 可选值 | 描述 |
|---|---|---|---|
callType | string | "sip", "webrtc", "" | 通话类型 |
关闭客户端
使用 Shutdown 方法关闭客户端连接:
err := client.Shutdown()
发送命令
客户端提供了多种方法用于发送命令到服务器。
Invite - 发起呼叫
发起一个呼叫,返回 AnswerEvent 或者异常。参见: 发起呼叫。
answer, err := client.Invite(ctx, callOption)
参数:
| 参数 | 类型 | 描述 |
|---|---|---|
ctx | context.Context | 上下文,用于取消 |
callOption | CallOption | 通话配置,参见 CallOption |
返回值:
| 类型 | 描述 |
|---|---|
*AnswerEvent | 接听事件(如果成功) |
error | 错误信息 |
示例:
// sip 通话
callOption := rustpbxgo.CallOption{
Caller: "sip:alice@example.com",
Callee: "sip:bob@example.com",
}
answer, err := client.Invite(ctx, callOption)
if err != nil {
log.Fatalf("呼叫失败: %v", err)
}
Accept - 接听来电
接听来电。用于接听呼入电话,参见:接听/拒绝来电。
err := client.Accept(callOption)
参数:
| 参数 | 类型 | 描述 |
|---|---|---|
callOption | CallOption | 通话配置,参见 CallOption |
- Accept 的 CallOption 配置除不需设置被叫地址外,剩余选项与 Invite 相同。
示例:
server := gin.Default()
server.POST(prefix, func(c *gin.Context) {
var form IncomingCall
if err := c.ShouldBindJSON(&form); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
client := createClient(parent, option, form.DialogID)
go func() {
ctx, cancel := context.WithCancel(parent)
defer cancel()
err := client.Connect("sip")
if err != nil {
option.Logger.Errorf("Failed to connect to server: %v", err)
}
defer client.Shutdown()
client.Accept(option.CallOption)
<-ctx.Done()
}()
c.JSON(200, gin.H{"message": "OK"})
})
server.Run(addr)
Ringing - 发送振铃
发送振铃响应。用于 SIP 通话,参见:180 Ringing。
err := client.Ringing(ringtone, recorder)
参数:
| 参数 | 类型 | 必需 | 描述 |
|---|---|---|---|
ringtone | string | ❌ | 铃声 URL |
recorder | *RecorderOption | ❌ | 录音配置,参见 RecorderOption |
示例:
client.Ringing("http://example.com/ringtone.wav", recorder)
Reject - 拒绝来电
拒绝来电。用于拒绝呼入电话,参见:接听/拒绝来电。
err := client.Reject(reason)
参数:
| 参数 | 类型 | 描述 |
|---|---|---|
reason | string | 拒绝原因 |
示例:
client.Reject("Busy")
TTS - 文本转语音
文本转语音后播放,参见:TTS (Text-to-Speech)。
参数:
| 参数 | 类型 | 必需 | 描述 |
|---|---|---|---|
text | string | ✅ | 要合成的文本 |
speaker | string | ❌ | 音色 |
playID | string | ❌ | TTS Track 的标识符 |
endOfStream | bool | ✅ | 是否为当前 playId 的最后一条 TTS 命令 |
autoHangup | bool | ✅ | TTS 播放完成后是否自动挂断 |
option | *TTSOption | ❌ | TTS 选项,参见 TTSOption |
waitInputTimeout | *uint32 | ❌ | 等待用户输入的最大时间(秒) |
StreamTTS - 流式 TTS
文本转语音后播放(用于 LLM 流式输出)。
err := client.StreamTTS(text, speaker, playID, endOfStream, autoHangup, option, waitInputTimeout)
Play - 播放音频
播放音频文件:
err := client.Play(url, autoHangup, waitInputTimeout)
参数:
| 参数 | 类型 | 必需 | 描述 |
|---|---|---|---|
url | string | ✅ | 音频文件 URL |
autoHangup | bool | ✅ | 播放完成后是否自动挂断 |
waitInputTimeout | *uint32 | ❌ | 等待输入超时(秒) |
Interrupt - 打断播放
打断当前的 TTS 或音频播放:
err := client.Interrupt(graceful)
参数:
| 参数 | 类型 | 必填 | 描述 |
|---|---|---|---|
graceful | bool | ✅ | 是否优雅打断(等待当前 TTS 命令播放完毕后才退出) |
-
当 TTS 未播放完毕时,会返回 Interruption 事件。
-
当设置
graceful=true时,TTS Track 会等待当前 TTS 命令播放完毕时才退出, 否则会直接退出。 -
graceful仅当非流式 TTS 时才生效 (streaming=false)。
参见 打断
示例:
client.OnSpeaking = func(event rustpbxgo.SpeakingEvent) {
// 检测到用户说话时立即打断 TTS
client.Interrupt(false)
}
Hangup - 挂断通话
当通话已经建立时,使用 Hangup 结束通话:
参数:
| 参数 | 类型 | 必需 | 描述 |
|---|---|---|---|
reason | string | ❌ | 挂断原因 |
Refer - 转接通话
将通话转接到另一个目标,用于转人工逻辑。参见: 转接通话
err := client.Refer(caller, callee, options)
参数:
| 参数 | 类型 | 必需 | 描述 |
|---|---|---|---|
caller | string | ✅ | 转移的主叫 SIP 地址 |
callee | string | ✅ | 转接目标的 SIP URI |
options | *ReferOption | ❌ | 转接选项,参见 ReferOption |
Mute - 静音
静音所有或者指定的 Track:
err := client.Mute(trackID)
参数:
| 参数 | 类型 | 描述 |
|---|---|---|
trackID | *string | Track ID(如果为 nil,则静音所有 Track) |
示例:
// 静音所有 Track
client.Mute(nil)
// 静音特定 Track
trackID := "track-123"
client.Mute(&trackID)
Unmute - 取消静音
取消静音所有或指定的 Track:
err := client.Unmute(trackID)
参数:
| 参数 | 类型 | 描述 |
|---|---|---|
trackID | *string | Track ID(如果为 nil,则静音所有 Track) |
事件回调
Client 定义了多个字段 (On开头的字段),用于设置事件的回调函数。
OnAnswer - 接听回调
触发时机: 通话被接听且 SDP 协商完成时触发。参见:AnswerEvent。
用途: 呼叫成功后的初始化操作。AnswerEvent中包含 SDP answer。
client.OnAnswer = func(event rustpbxgo.AnswerEvent) {
log.Printf("通话已接听: %s", event.TrackID)
// 开始发送欢迎语
client.TTS("您好,欢迎致电", "", "welcome", true, false, nil, nil)
}
OnReject - 拒绝回调
触发时机: 呼叫被拒绝时触发。参见 RejectEvent。
用途: 处理拒绝逻辑,记录拒绝原因(event.Reason)或进行后续处理。
client.OnReject = func(event rustpbxgo.RejectEvent) {
log.Printf("通话被拒绝: %s", event.Reason)
// 记录拒绝原因,清理资源
}
OnRinging - 振铃回调
触发时机: 通话振铃时触发(SIP 通话)。参见 RingingEvent。
用途: 监控呼叫进度,判断是否有早期媒体(EarlyMedia)可用。
client.OnRinging = func(event rustpbxgo.RingingEvent) {
log.Printf("正在振铃,早期媒体: %v", event.EarlyMedia)
}
OnHangup - 挂断回调
触发时机: 通话结束时触发。参见 HangupEvent。
用途: 清理资源、保存通话记录。可从 HangupEvent 中获取挂断原因(event.Reason)、通话时长(event.hangUpTime - event.startTime)、主被叫信息(event.From,event.To)等。
client.OnHangup = func(event rustpbxgo.HangupEvent) {
log.Printf("通话已挂断: %s, 发起方: %s", event.Reason, event.Initiator)
// 保存通话记录,清理资源
}
OnSpeaking - 说话回调
触发时机: VAD 检测到用户开始说话时触发。参见 SpeakingEvent。
用途: 检测用户输入,常用于打断 TTS 播放。可从事件中获取语音开始时间(event.StartTime)。
client.OnSpeaking = func(event rustpbxgo.SpeakingEvent) {
log.Printf("检测到用户说话,打断播放")
// 立即打断当前 TTS
client.Interrupt(false)
}
OnSilence - 静音回调
触发时机: 检测到用户停止说话后触发。参见 SilenceEvent。
用途: 判断用户是否说完,可结合静音持续时间(event.Duration)来决定是否开始处理。
client.OnSilence = func(event rustpbxgo.SilenceEvent) {
log.Printf("检测到静音,持续 %d ms", event.Duration)
// 用户可能说完了,准备处理
}
OnAsrFinal - ASR 最终结果回调
触发时机: 语音识别获得稳定结果时触发。参见 AsrFinalEvent。
用途: 获取用户的最终语音输入(event.Text),用于业务逻辑处理或发送给 LLM。可通过序列号(event.Index)区分不同的语音片段。
client.OnAsrFinal = func(event rustpbxgo.AsrFinalEvent) {
log.Printf("用户说: %s", event.Text)
// 将用户输入发送给 LLM 处理
response := callLLM(event.Text)
client.TTS(response, "", "reply", true, false, nil, nil)
}
OnAsrDelta - ASR 增量结果回调
触发时机: 语音识别过程中的中间结果,内容可能会变动。参见 AsrDeltaEvent。
用途: 实时显示识别进度,提升用户体验。不应直接用于业务逻辑处理。
client.OnAsrDelta = func(event rustpbxgo.AsrDeltaEvent) {
log.Printf("识别中: %s", event.Text)
// 仅用于显示,不做业务处理
}
OnTrackStart - Track 开始回调
触发时机: Track 开始时触发(RTP、TTS、文件播放等)。参见 TrackStartEvent。
用途: 监控音频播放开始。对于 TTS Track,可通过 event.PlayID 获取 TTS 命令的 playId;对于 Play Track,可获取播放的 URL。
client.OnTrackStart = func(event rustpbxgo.TrackStartEvent) {
log.Printf("Track 开始: %s, PlayID: %s", event.TrackID, event.PlayID)
}
OnTrackEnd - Track 结束回调
触发时机: Track 结束时触发(RTP 结束、TTS 完成、文件播放完成等)。参见 TrackEndEvent。
用途: 监控音频播放结束,可用于控制播放流程或清理资源。可从事件中获取持续时间(event.Duration)和 PlayID(event.PlayID)。
client.OnTrackEnd = func(event rustpbxgo.TrackEndEvent) {
log.Printf("Track 结束: %s, 持续时间: %d ms, PlayID: %s",
event.TrackID, event.Duration, event.PlayID)
// TTS 播放完成,可以发送下一条
}
OnInterruption - 打断回调
触发时机: 收到 Interrupt 命令且有未播放完毕的 TTS 时触发。参见 InterruptionEvent。
用途: 获取打断信息,如已播放时间(event.PlayedMs)和已播放文字位置(event.Subtitle,如果提供商支持字幕)。
client.OnInterruption = func(event rustpbxgo.InterruptionEvent) {
if event.Subtitle != nil {
log.Printf("播放被打断,已播放: %s", *event.Subtitle)
}
// 记录打断位置,用于后续处理
}
OnDTMF - DTMF 回调
触发时机: 检测到按键时触发。参见 DTMFEvent。
用途: 处理用户按键输入,如IVR菜单选择。可从 event.Digit 获取按键值(0-9, *, #, A-D)。
client.OnDTMF = func(event rustpbxgo.DTMFEvent) {
log.Printf("用户按键: %s", event.Digit)
// 处理按键逻辑,如菜单导航
if event.Digit == "1" {
client.TTS("您选择了选项1", "", "menu", true, false, nil, nil)
}
}
OnError - 错误回调
触发时机: 发生错误时触发。参见 ErrorEvent。
用途: 处理各种错误情况。从 event.Sender 可知错误来源(asr、tts、media 等),从 event.Error 获取错误信息。
client.OnError = func(event rustpbxgo.ErrorEvent) {
log.Printf("错误 [%s]: %s", event.Sender, event.Error)
// 记录错误日志,进行降级处理
}
OnMetrics - 指标回调
触发时机: 收集性能指标时触发。参见 MetricsEvent。
用途: 监控性能指标。可从事件中获取指标名称(event.Key)和持续时间(event.Duration)。
client.OnMetrics = func(event rustpbxgo.MetricsEvent) {
log.Printf("指标 [%s]: %d ms", event.Key, event.Duration)
// 记录性能数据用于分析
}
OnClose - 连接关闭
触发时机: WebSocket 连接关闭时触发。
用途: 处理连接断开逻辑,清理资源或尝试重连。
client.OnClose = func(reason string) {
log.Printf("连接已关闭: %s", reason)
// 清理资源或重连
}
OnEvent - 通用事件处理器
触发时机: 收到任何事件时都会触发。
用途: 记录所有事件或处理未定义的特殊事件。接收原始事件类型(event)和 JSON 数据(payload)。
client.OnEvent = func(event string, payload string) {
log.Printf("收到事件 [%s]: %s", event, payload)
// 通用事件日志或特殊处理
}
选项
CallOption
通话配置选项:
type CallOption struct {
Denoise bool
Offer string
Callee string
Caller string
Recorder *RecorderOption
VAD *VADOption
ASR *ASROption
TTS *TTSOption
HandshakeTimeout string
EnableIPv6 bool
Sip *SipOption
Extra map[string]string
}
字段说明:
| 字段 | 类型 | 描述 |
|---|---|---|
Denoise | bool | 是否启用降噪 |
Offer | string | 用于 WebRTC/SIP 协商的 SDP offer 字符串 |
Callee | string | 被叫的 SIP URI 或电话号码 |
Caller | string | 主叫的 SIP URI 或电话号码 |
Recorder | *RecorderOption | 通话录音配置,参见 RecorderOption |
VAD | *VADOption | 语音活动检测配置,参见 VADOption |
ASR | *ASROption | 语音识别 (ASR) 配置,参见 ASROption |
TTS | *TTSOption | 文字转语音 配置,参见 TTSOption |
HandshakeTimeout | string | 连接握手超时 |
EnableIPv6 | bool | 启用 IPv6 支持 |
Sip | *SipOption | SIP 注册账号、密码和域配置,参见 SipOption |
Extra | map[string]string | 补充参数 |
RecorderOption
录音配置选项:
type RecorderOption struct {
RecorderFile string
Samplerate int
Ptime int
}
字段说明:
| 字段 | 类型 | 默认值 | 描述 |
|---|---|---|---|
RecorderFile | string | - | 录音文件路径 |
Samplerate | int | 16000 | 采样率(Hz) |
Ptime | int | 200 | 数据包时间(毫秒) |
ASROption
语音识别配置选项:
type ASROption struct {
Provider string
Model string
Language string
AppID string
SecretID string
SecretKey string
ModelType string
BufferSize int
SampleRate uint32
Endpoint string
Extra map[string]string
StartWhenAnswer bool
}
字段说明:
| 字段 | 类型 | 描述 |
|---|---|---|
Provider | string | ASR 提供商:tencent, aliyun, Deepgram 等 |
Model | string | 模型名称 |
Language | string | 语言(例如:zh-CN, en-US),具体参见对应提供商文档 |
AppID | string | 腾讯云的 appId |
SecretID | string | 腾讯云的 secretId |
SecretKey | string | 腾讯云的 secretKey,或者其他提供商的 API Key |
ModelType | string | ASR 模型类型(例如:16k_zh, 8k_en),具体参见提供商的文档 |
BufferSize | int | 音频缓冲区大小,单位:字节 |
SampleRate | uint32 | 采样率 |
Endpoint | string | 自定义服务端点 URL |
Extra | map[string]string | 提供商特定参数 |
StartWhenAnswer | bool | 呼叫接通后再请求 ASR 服务 |
TTSOption
语音合成配置选项:
type TTSOption struct {
Samplerate int32
Provider string
Speed float32
AppID string
SecretID string
SecretKey string
Volume int32
Speaker string
Codec string
Subtitle bool
Emotion string
Endpoint string
Extra map[string]string
WaitInputTimeout uint32
}
字段说明:
| 字段 | 类型 | 描述 |
|---|---|---|
Samplerate | int32 | 采样率,单位:Hz |
Provider | string | TTS 提供商:tencent, aliyun, deepgram, voiceapi |
Speed | float32 | 语速 |
AppID | string | 腾讯云的 appId |
SecretID | string | 腾讯云的 secretId |
SecretKey | string | 腾讯云的 secretKey,或者其他提供商的 API Key |
Volume | int32 | 音量(1-10) |
Speaker | string | 音色,参见提供商文档 |
Codec | string | 编码格式 |
Subtitle | bool | 是否启用字幕 |
Emotion | string | 情绪:neutral, happy, sad, angry 等 |
Endpoint | string | 自定义 TTS 服务端点 URL |
Extra | map[string]string | 提供商特定参数 |
WaitInputTimeout | uint32 | 等待用户输入的最大时间(毫秒) |
VADOption
语音活动检测配置选项:
type VADOption struct {
Type string
Samplerate uint32
SpeechPadding uint64
SilencePadding uint64
Ratio float32
VoiceThreshold float32
MaxBufferDurationSecs uint64
Endpoint string
SecretKey string
SecretID string
SilenceTimeout uint
}
字段说明:
| 字段 | 类型 | 默认值 | 描述 |
|---|---|---|---|
Type | string | webrtc | VAD 算法类型:silero, ten, webrtc |
Samplerate | uint32 | 16000 | 采样率 |
SpeechPadding | uint64 | 250 | 语音开始 speechPadding 毫秒后开始检测 |
SilencePadding | uint64 | 100 | Silence 事件触发间隔,单位毫秒 |
Ratio | float32 | 0.5 | 语音检测比率阈值 |
VoiceThreshold | float32 | 0.5 | 语音能量阈值 |
MaxBufferDurationSecs | uint64 | 50 | 最大缓冲区持续时间,单位秒 |
Endpoint | string | - | 自定义 VAD 服务端点 |
SecretKey | string | - | VAD 服务身份验证密钥 |
SecretID | string | - | VAD 服务身份验证 ID |
SilenceTimeout | uint | 5000 | 静音检测超时,单位毫秒 |
SipOption
SIP 配置选项:
type SipOption struct {
Username string
Password string
Realm string
Headers map[string]string
}
字段说明:
| 字段 | 类型 | 描述 |
|---|---|---|
Username | string | SIP 用户名,用于身份验证 |
Password | string | SIP 密码,用于身份验证 |
Realm | string | SIP 域/领域,用于身份验证 |
Headers | map[string]string | 额外的 SIP 协议头(键值对) |
ReferOption
转接配置选项:
type ReferOption struct {
Denoise bool
Timeout uint32
MusicOnHold string
AutoHangup bool
Sip *SipOption
ASR *ASROption
}
字段说明:
| 字段 | 类型 | 描述 |
|---|---|---|
Denoise | bool | 是否启用降噪 |
Timeout | uint32 | 超时时间(秒) |
MusicOnHold | string | 等待音乐 URL |
AutoHangup | bool | 转接完成后自动挂断 |
Sip | *SipOption | SIP 配置 |
ASR | *ASROption | ASR 配置 |
事件类型
Client 支持的所有事件类型定义:
Event
基础事件结构,包含事件类型名称。
type Event struct {
Event string `json:"event"`
}
字段说明:
| 字段 | 类型 | 描述 |
|---|---|---|
Event | string | 事件类型名称 |
IncomingEvent
来电事件,当有新的呼入电话时触发。
type IncomingEvent struct {
TrackID string `json:"trackId"`
Timestamp uint64 `json:"timestamp"`
Caller string `json:"caller"`
Callee string `json:"callee"`
Sdp string `json:"sdp"`
}
字段说明:
| 字段 | 类型 | 描述 |
|---|---|---|
TrackID | string | 通话轨道 ID |
Timestamp | uint64 | 事件时间戳(毫秒) |
Caller | string | 主叫号码 |
Callee | string | 被叫号码 |
Sdp | string | SDP offer 字符串 |
AnswerEvent
接听事件,当通话被接听且 SDP 协商完成时触发。
type AnswerEvent struct {
TrackID string `json:"trackId"`
Timestamp uint64 `json:"timestamp"`
Sdp string `json:"sdp"`
}
字段说明:
| 字段 | 类型 | 描述 |
|---|---|---|
TrackID | string | 通话轨道 ID |
Timestamp | uint64 | 事件时间戳(毫秒) |
Sdp | string | SDP answer 字符串 |
RejectEvent
拒绝事件,当呼叫被拒绝时触发。
type RejectEvent struct {
TrackID string `json:"trackId"`
Timestamp uint64 `json:"timestamp"`
Reason string `json:"reason"`
}
字段说明:
| 字段 | 类型 | 描述 |
|---|---|---|
TrackID | string | 通话轨道 ID |
Timestamp | uint64 | 事件时间戳(毫秒) |
Reason | string | 拒绝原因 |
RingingEvent
振铃事件,当通话振铃时触发(SIP 通话)。
type RingingEvent struct {
TrackID string `json:"trackId"`
Timestamp uint64 `json:"timestamp"`
EarlyMedia bool `json:"earlyMedia"`
}
字段说明:
| 字段 | 类型 | 描述 |
|---|---|---|
TrackID | string | 通话轨道 ID |
Timestamp | uint64 | 事件时间戳(毫秒) |
EarlyMedia | bool | 是否有早期媒体 |
HangupEvent
挂断事件,当通话结束时触发。
type HangupEventAttendee struct {
Username string `json:"username"`
Realm string `json:"realm"`
Source string `json:"source"`
}
type HangupEvent struct {
Timestamp uint64 `json:"timestamp"`
Reason string `json:"reason"`
Initiator string `json:"initiator"`
StartTime string `json:"startTime,omitempty"`
HangupTime string `json:"hangupTime,omitempty"`
AnswerTime *string `json:"answerTime,omitempty"`
RingingTime *string `json:"ringingTime,omitempty"`
From *HangupEventAttendee `json:"from,omitempty"`
To *HangupEventAttendee `json:"to,omitempty"`
Extra map[string]any `json:"extra,omitempty"`
}
HangupEvent 字段说明:
| 字段 | 类型 | 描述 |
|---|---|---|
Timestamp | uint64 | 事件时间戳(毫秒) |
Reason | string | 挂断原因 |
Initiator | string | 挂断发起方 |
StartTime | string | 通话开始时间 |
HangupTime | string | 挂断时间 |
AnswerTime | *string | 接听时间 |
RingingTime | *string | 振铃时间 |
From | *HangupEventAttendee | 主叫信息 |
To | *HangupEventAttendee | 被叫信息 |
Extra | map[string]any | 额外信息 |
HangupEventAttendee 字段说明:
| 字段 | 类型 | 描述 |
|---|---|---|
Username | string | 用户名 |
Realm | string | 域 |
Source | string | 来源 |
SpeakingEvent
说话事件,当 VAD 检测到用户开始说话时触发。
type SpeakingEvent struct {
TrackID string `json:"trackId"`
Timestamp uint64 `json:"timestamp"`
StartTime uint64 `json:"startTime"`
}
字段说明:
| 字段 | 类型 | 描述 |
|---|---|---|
TrackID | string | 通话轨道 ID |
Timestamp | uint64 | 事件时间戳(毫秒) |
StartTime | uint64 | 语音开始时间(毫秒) |
SilenceEvent
静音事件,当检测到用户停止说话后触发。
type SilenceEvent struct {
TrackID string `json:"trackId"`
Timestamp uint64 `json:"timestamp"`
StartTime uint64 `json:"startTime"`
Duration uint64 `json:"duration"`
}
字段说明:
| 字段 | 类型 | 描述 |
|---|---|---|
TrackID | string | 通话轨道 ID |
Timestamp | uint64 | 事件时间戳(毫秒) |
StartTime | uint64 | 静音开始时间(毫秒) |
Duration | uint64 | 静音持续时间(毫秒) |
EouEvent
语音结束事件,当检测到语音结束时触发。
type EouEvent struct {
TrackID string `json:"trackId"`
Timestamp uint64 `json:"timestamp"`
Complete bool `json:"complete"`
}
字段说明:
| 字段 | 类型 | 描述 |
|---|---|---|
TrackID | string | 通话轨道 ID |
Timestamp | uint64 | 事件时间戳(毫秒) |
Complete | bool | 是否完整结束 |
AsrFinalEvent
ASR 最终结果事件,当语音识别获得稳定结果时触发。
type AsrFinalEvent struct {
TrackID string `json:"trackId"`
Timestamp uint64 `json:"timestamp"`
Index uint32 `json:"index"`
StartTime *uint64 `json:"startTime,omitempty"`
EndTime *uint64 `json:"endTime,omitempty"`
Text string `json:"text"`
}
字段说明:
| 字段 | 类型 | 描述 |
|---|---|---|
TrackID | string | 通话轨道 ID |
Timestamp | uint64 | 事件时间戳(毫秒) |
Index | uint32 | 语音片段索引 |
StartTime | *uint64 | 语音开始时间(毫秒) |
EndTime | *uint64 | 语音结束时间(毫秒) |
Text | string | 识别的文本内容 |
AsrDeltaEvent
ASR 增量结果事件,语音识别过程中的中间结果,内容可能会变动。
type AsrDeltaEvent struct {
TrackID string `json:"trackId"`
Index uint32 `json:"index"`
Timestamp uint64 `json:"timestamp"`
StartTime *uint64 `json:"startTime,omitempty"`
EndTime *uint64 `json:"endTime,omitempty"`
Text string `json:"text"`
}
字段说明:
| 字段 | 类型 | 描述 |
|---|---|---|
TrackID | string | 通话轨道 ID |
Index | uint32 | 语音片段索引 |
Timestamp | uint64 | 事件时间戳(毫秒) |
StartTime | *uint64 | 语音开始时间(毫秒) |
EndTime | *uint64 | 语音结束时间(毫秒) |
Text | string | 识别的文本内容(可能变化) |
TrackStartEvent
Track 开始事件,当 Track 开始时触发(RTP、TTS、文件播放等)。
type TrackStartEvent struct {
TrackID string `json:"trackId"`
Timestamp uint64 `json:"timestamp"`
PlayId *string `json:"playId,omitempty"`
}
字段说明:
| 字段 | 类型 | 描述 |
|---|---|---|
TrackID | string | 轨道 ID |
Timestamp | uint64 | 事件时间戳(毫秒) |
PlayId | *string | 播放 ID(TTS/Play 命令) |
TrackEndEvent
Track 结束事件,当 Track 结束时触发(RTP 结束、TTS 完成、文件播放完成等)。
type TrackEndEvent struct {
TrackID string `json:"trackId"`
Timestamp uint64 `json:"timestamp"`
Duration uint64 `json:"duration"`
PlayId *string `json:"playId,omitempty"`
}
字段说明:
| 字段 | 类型 | 描述 |
|---|---|---|
TrackID | string | 轨道 ID |
Timestamp | uint64 | 事件时间戳(毫秒) |
Duration | uint64 | 播放持续时间(毫秒) |
PlayId | *string | 播放 ID(TTS/Play 命令) |
InterruptionEvent
打断事件,当收到 Interrupt 命令且有未播放完毕的 TTS 时触发。
type InterruptionEvent struct {
TrackID string `json:"trackId"`
Timestamp uint64 `json:"timestamp"`
Subtitle *string `json:"subtitle,omitempty"`
Position *uint32 `json:"position,omitempty"`
TotalDuration uint32 `json:"totalDuration"`
Current uint32 `json:"current"`
}
字段说明:
| 字段 | 类型 | 描述 |
|---|---|---|
TrackID | string | 轨道 ID |
Timestamp | uint64 | 事件时间戳(毫秒) |
Subtitle | *string | 已播放的字幕文本 |
Position | *uint32 | 播放位置(字符数) |
TotalDuration | uint32 | 总时长(毫秒) |
Current | uint32 | 当前播放时长(毫秒) |
DTMFEvent
DTMF 事件,当检测到按键时触发。
type DTMFEvent struct {
TrackID string `json:"trackId"`
Timestamp uint64 `json:"timestamp"`
Digit string `json:"digit"`
}
字段说明:
| 字段 | 类型 | 描述 |
|---|---|---|
TrackID | string | 通话轨道 ID |
Timestamp | uint64 | 事件时间戳(毫秒) |
Digit | string | 按键值(0-9, *, #, A-D) |
AnswerMachineDetectionEvent
答录机检测事件,当检测到答录机时触发。
type AnswerMachineDetectionEvent struct {
Timestamp uint64 `json:"timestamp"`
StartTime uint64 `json:"startTime"`
EndTime uint64 `json:"endTime"`
Text string `json:"text"`
}
字段说明:
| 字段 | 类型 | 描述 |
|---|---|---|
Timestamp | uint64 | 事件时间戳(毫秒) |
StartTime | uint64 | 检测开始时间(毫秒) |
EndTime | uint64 | 检测结束时间(毫秒) |
Text | string | 检测到的文本 |
LLMFinalEvent
LLM 最终结果事件,当大语言模型生成最终结果时触发。
type LLMFinalEvent struct {
Timestamp uint64 `json:"timestamp"`
Text string `json:"text"`
}
字段说明:
| 字段 | 类型 | 描述 |
|---|---|---|
Timestamp | uint64 | 事件时间戳(毫秒) |
Text | string | LLM 生成的最终文本 |
LLMDeltaEvent
LLM 增量结果事件,当大语言模型生成增量结果时触发。
type LLMDeltaEvent struct {
Timestamp uint64 `json:"timestamp"`
Word string `json:"word"`
}
字段说明:
| 字段 | 类型 | 描述 |
|---|---|---|
Timestamp | uint64 | 事件时间戳(毫秒) |
Word | string | LLM 生成的增量词汇 |
MetricsEvent
指标事件,当收集性能指标时触发。
type MetricsEvent struct {
Timestamp uint64 `json:"timestamp"`
Key string `json:"key"`
Duration uint32 `json:"duration"`
Data map[string]any `json:"data"`
}
字段说明:
| 字段 | 类型 | 描述 |
|---|---|---|
Timestamp | uint64 | 事件时间戳(毫秒) |
Key | string | 指标名称 |
Duration | uint32 | 持续时间(毫秒) |
Data | map[string]any | 指标数据 |
ErrorEvent
错误事件,当发生错误时触发。
type ErrorEvent struct {
TrackID string `json:"trackId"`
Timestamp uint64 `json:"timestamp"`
Sender string `json:"sender"`
Error string `json:"error"`
Code *uint32 `json:"code,omitempty"`
}
字段说明:
| 字段 | 类型 | 描述 |
|---|---|---|
TrackID | string | 通话轨道 ID |
Timestamp | uint64 | 事件时间戳(毫秒) |
Sender | string | 错误来源(asr、tts、media 等) |
Error | string | 错误信息 |
Code | *uint32 | 错误代码 |
AddHistoryEvent
添加历史记录事件,当添加对话历史记录时触发。
type AddHistoryEvent struct {
Sender string `json:"sender"`
Timestamp uint64 `json:"timestamp"`
Speaker string `json:"speaker"`
Text string `json:"text"`
}
字段说明:
| 字段 | 类型 | 描述 |
|---|---|---|
Sender | string | 发送者 |
Timestamp | uint64 | 事件时间戳(毫秒) |
Speaker | string | 说话人标识 |
Text | string | 对话文本 |
OtherEvent
其他事件,用于处理未定义的事件类型。
type OtherEvent struct {
TrackID string `json:"trackId"`
Timestamp uint64 `json:"timestamp"`
Sender string `json:"sender"`
Extra map[string]string `json:"extra,omitempty"`
}
字段说明:
| 字段 | 类型 | 描述 |
|---|---|---|
TrackID | string | 通话轨道 ID |
Timestamp | uint64 | 事件时间戳(毫秒) |
Sender | string | 发送者 |
Extra | map[string]string | 额外信息 |
完整示例
SIP 通话示例
package main
import (
"context"
"log"
"os"
"os/signal"
"syscall"
"github.com/restsend/rustpbxgo"
"github.com/sirupsen/logrus"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
logger := logrus.New()
logger.SetLevel(logrus.InfoLevel)
// 创建客户端
client := rustpbxgo.NewClient(
"ws://localhost:8080",
rustpbxgo.WithLogger(logger),
rustpbxgo.WithContext(ctx),
)
// 设置事件处理器
client.OnAnswer = func(event rustpbxgo.AnswerEvent) {
logger.Info("通话已接听")
// 发送欢迎语
client.TTS("你好,欢迎致电", "", "greeting", true, false, nil, nil)
}
client.OnAsrFinal = func(event rustpbxgo.AsrFinalEvent) {
logger.Infof("用户说: %s", event.Text)
// 根据用户输入响应
client.TTS("我收到了你的消息", "", "response", true, false, nil, nil)
}
client.OnHangup = func(event rustpbxgo.HangupEvent) {
logger.Infof("通话结束: %s", event.Reason)
cancel()
}
// 连接服务器
if err := client.Connect("sip"); err != nil {
log.Fatalf("连接失败: %v", err)
}
defer client.Shutdown()
// 配置通话
callOption := rustpbxgo.CallOption{
Caller: "sip:1000@example.com",
Callee: "sip:2000@example.com",
Denoise: true,
Sip: &rustpbxgo.SipOption{
Username: "user",
Password: "pass",
Realm: "example.com",
},
ASR: &rustpbxgo.ASROption{
Provider: "tencent",
Language: "zh-CN",
},
TTS: &rustpbxgo.TTSOption{
Provider: "tencent",
Speaker: "xiaoyan",
},
VAD: &rustpbxgo.VADOption{
Type: "webrtc",
SilenceTimeout: 5000,
},
}
// 发起呼叫
_, err := client.Invite(ctx, callOption)
if err != nil {
log.Fatalf("呼叫失败: %v", err)
}
// 等待信号
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
select {
case <-ctx.Done():
logger.Info("通话已结束")
case <-sigChan:
logger.Info("收到中断信号")
client.Hangup("user_interrupt")
}
}
接听来电示例
package main
import (
"context"
"encoding/json"
"log"
"net/http"
"github.com/restsend/rustpbxgo"
"github.com/sirupsen/logrus"
)
type WebhookRequest struct {
DialogID string `json:"dialogId"`
Caller string `json:"caller"`
Callee string `json:"callee"`
}
func main() {
logger := logrus.New()
// 设置 Webhook 处理器
http.HandleFunc("/webhook", func(w http.ResponseWriter, r *http.Request) {
var req WebhookRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
logger.Infof("收到来电: %s -> %s", req.Caller, req.Callee)
// 处理来电
go handleIncomingCall(req.DialogID, req.Caller, req.Callee, logger)
w.WriteHeader(http.StatusOK)
})
log.Fatal(http.ListenAndServe(":8090", nil))
}
func handleIncomingCall(dialogID, caller, callee string, logger *logrus.Logger) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// 使用 dialogID 创建客户端
client := rustpbxgo.NewClient(
"ws://localhost:8080",
rustpbxgo.WithLogger(logger),
rustpbxgo.WithContext(ctx),
rustpbxgo.WithID(dialogID),
)
client.OnHangup = func(event rustpbxgo.HangupEvent) {
logger.Info("通话结束")
cancel()
}
// 连接服务器
if err := client.Connect("sip"); err != nil {
logger.Errorf("连接失败: %v", err)
return
}
defer client.Shutdown()
// 发送振铃
recorder := &rustpbxgo.RecorderOption{
RecorderFile: "/recordings/" + dialogID + ".wav",
Samplerate: 16000,
}
client.Ringing("", recorder)
// 接听来电
callOption := rustpbxgo.CallOption{
Caller: caller,
Callee: callee,
ASR: &rustpbxgo.ASROption{
Provider: "tencent",
Language: "zh-CN",
},
TTS: &rustpbxgo.TTSOption{
Provider: "tencent",
Speaker: "xiaoyan",
},
}
if err := client.Accept(callOption); err != nil {
logger.Errorf("接听失败: %v", err)
return
}
// 发送欢迎语
client.TTS("您好,我是智能助手", "", "greeting", true, false, nil, nil)
// 等待通话结束
<-ctx.Done()
}
流式 TTS 示例(LLM 集成)
package main
import (
"bufio"
"context"
"encoding/json"
"log"
"net/http"
"github.com/restsend/rustpbxgo"
"github.com/sirupsen/logrus"
)
func streamLLMResponse(client *rustpbxgo.Client, userInput string) {
// 模拟调用 LLM API
req, _ := http.NewRequest("POST", "https://api.openai.com/v1/chat/completions", nil)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Printf("LLM 请求失败: %v", err)
return
}
defer resp.Body.Close()
playID := "llm-stream"
scanner := bufio.NewScanner(resp.Body)
for scanner.Scan() {
line := scanner.Text()
// 解析 SSE 数据
var data map[string]interface{}
if err := json.Unmarshal([]byte(line), &data); err != nil {
continue
}
// 获取增量文本
if content, ok := data["content"].(string); ok && content != "" {
// 发送流式 TTS
isEnd := data["finish_reason"] != nil
client.StreamTTS(content, "", playID, isEnd, false, nil, nil)
}
}
}
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
logger := logrus.New()
client := rustpbxgo.NewClient(
"ws://localhost:8080",
rustpbxgo.WithLogger(logger),
rustpbxgo.WithContext(ctx),
)
client.OnAsrFinal = func(event rustpbxgo.AsrFinalEvent) {
logger.Infof("用户输入: %s", event.Text)
// 中断当前播放
client.Interrupt()
// 流式响应
go streamLLMResponse(client, event.Text)
}
// ... 其他代码
}
最佳实践
错误处理
始终检查错误并适当处理:
if err := client.Connect("sip"); err != nil {
log.Fatalf("连接失败: %v", err)
}
if err := client.TTS("Hello", "", "1", true, false, nil, nil); err != nil {
log.Printf("TTS 失败: %v", err)
// 重试或使用备用方案
}
资源清理
确保正确清理资源:
defer client.Shutdown()
上下文管理
使用上下文控制生命周期:
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
client := rustpbxgo.NewClient(
endpoint,
rustpbxgo.WithContext(ctx),
)
日志记录
使用适当的日志级别:
logger := logrus.New()
logger.SetLevel(logrus.InfoLevel) // 生产环境
// logger.SetLevel(logrus.DebugLevel) // 开发环境
事件处理
避免在事件处理器中执行长时间操作,使用 goroutine:
client.OnAsrFinal = func(event rustpbxgo.AsrFinalEvent) {
go func() {
// 长时间操作
response := processWithLLM(event.Text)
client.TTS(response, "", "reply", true, false, nil, nil)
}()
}
故障排除
连接问题
如果无法连接到服务器:
- 检查端点 URL 是否正确
- 确认服务器正在运行
- 检查防火墙设置
- 启用调试日志查看详细信息
logger.SetLevel(logrus.DebugLevel)
client := rustpbxgo.NewClient(
endpoint,
rustpbxgo.WithLogger(logger),
rustpbxgo.WithDumpEvents(true),
)
ASR 不工作
如果 ASR 无法识别语音:
- 确认 ASR 配置正确
- 检查 API 密钥是否有效
- 验证采样率设置
- 检查 VAD 配置
TTS 无声音
如果 TTS 没有播放声音:
- 检查 TTS 配置是否正确
- 验证说话人参数
- 确认
endOfStream设置正确 - 检查网络连接
更多资源
参考
完整示例代码请参考:/Users/yangli/Desktop/rustpbxgo/cmd