跳到主要内容

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 消息并解析(上)
  • 另一个负责处理消息和发送命令(下)。 当收到事件时,会依据事件类型调用对应的回调函数。
c.conn.ReadMessage()
c.conn.ReadMessage()
RustPBX
RustPBX
事件/WebSocket
事件/WebSocket
事件/c.eventChan
事件/c.eventChan
c.processEvent()
c.processEvent()
OnAsrFinal
OnAsrFinal
client
client
c.conn.WriteJson()
c.conn.WriteJson()
命令/WebSocket
命令/WebSocket
OnSpeaking
OnSpeaking
OnHangup
OnHangup
Text is not SVG - cannot display

创建客户端

使用 NewClient 函数创建客户端实例:

client := rustpbxgo.NewClient(endpoint, opts...)

参数:

参数类型必填描述
endpointstringRustPBX 服务器地址
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)

参数:

参数类型可选值描述
callTypestring"sip", "webrtc", ""通话类型

关闭客户端

使用 Shutdown 方法关闭客户端连接:

err := client.Shutdown()

发送命令

客户端提供了多种方法用于发送命令到服务器。

Invite - 发起呼叫

发起一个呼叫,返回 AnswerEvent 或者异常。参见: 发起呼叫

answer, err := client.Invite(ctx, callOption)

参数:

参数类型描述
ctxcontext.Context上下文,用于取消
callOptionCallOption通话配置,参见 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)

参数:

参数类型描述
callOptionCallOption通话配置,参见 CallOption
信息
  • Accept 的 CallOption 配置除不需设置被叫地址外,剩余选项与 Invite 相同。

示例:

cmd/webhook.go
	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)

参数:

参数类型必需描述
ringtonestring铃声 URL
recorder*RecorderOption录音配置,参见 RecorderOption

示例:

client.Ringing("http://example.com/ringtone.wav", recorder)

Reject - 拒绝来电

拒绝来电。用于拒绝呼入电话,参见:接听/拒绝来电

err := client.Reject(reason)

参数:

参数类型描述
reasonstring拒绝原因

示例:

client.Reject("Busy")

TTS - 文本转语音

文本转语音后播放,参见:TTS (Text-to-Speech)

参数:

参数类型必需描述
textstring要合成的文本
speakerstring音色
playIDstringTTS Track 的标识符
endOfStreambool是否为当前 playId 的最后一条 TTS 命令
autoHangupboolTTS 播放完成后是否自动挂断
option*TTSOptionTTS 选项,参见 TTSOption
waitInputTimeout*uint32等待用户输入的最大时间(秒)
提示
  • 设置 endOfStream = true, 表示当前 playId 的所有 TTS 命令已经发送完毕。TTS Track 将在所有命令结果播放完毕之后退出,并且发送 Track End 事件。
  • 如果设置了 playId,在该 TTS Track 发送的 Track End 事件会包含该 playId
    • 如果当前 playId 和之前的 TTS 命令 playId 相同,则会复用之前的 TTS Track,否则会终止之前的 TTS Track 并创建一个新的 TTS Track。

详见 TTS(Text-to-Speech)

StreamTTS - 流式 TTS

文本转语音后播放(用于 LLM 流式输出)。

err := client.StreamTTS(text, speaker, playID, endOfStream, autoHangup, option, waitInputTimeout)
提示

和 TTS 的区别是对应的 TTS 命令 streaming = true,其余相同。

参见 流式 TTS

Play - 播放音频

播放音频文件:

err := client.Play(url, autoHangup, waitInputTimeout)

参数:

参数类型必需描述
urlstring音频文件 URL
autoHangupbool播放完成后是否自动挂断
waitInputTimeout*uint32等待输入超时(秒)

Interrupt - 打断播放

打断当前的 TTS 或音频播放:

err := client.Interrupt(graceful)

参数:

参数类型必填描述
gracefulbool是否优雅打断(等待当前 TTS 命令播放完毕后才退出)
信息
  • 当 TTS 未播放完毕时,会返回 Interruption 事件

  • 当设置 graceful=true 时,TTS Track 会等待当前 TTS 命令播放完毕时才退出, 否则会直接退出。

  • graceful 仅当非流式 TTS 时才生效 (streaming=false)。

参见 打断

示例:

client.OnSpeaking = func(event rustpbxgo.SpeakingEvent) {
// 检测到用户说话时立即打断 TTS
client.Interrupt(false)
}

Hangup - 挂断通话

当通话已经建立时,使用 Hangup 结束通话:

参数:

参数类型必需描述
reasonstring挂断原因

Refer - 转接通话

将通话转接到另一个目标,用于转人工逻辑。参见: 转接通话

err := client.Refer(caller, callee, options)

参数:

参数类型必需描述
callerstring转移的主叫 SIP 地址
calleestring转接目标的 SIP URI
options*ReferOption转接选项,参见 ReferOption

Mute - 静音

静音所有或者指定的 Track:

err := client.Mute(trackID)

参数:

参数类型描述
trackID*stringTrack ID(如果为 nil,则静音所有 Track)

示例:

// 静音所有 Track
client.Mute(nil)

// 静音特定 Track
trackID := "track-123"
client.Mute(&trackID)

Unmute - 取消静音

取消静音所有或指定的 Track:

err := client.Unmute(trackID)

参数:

参数类型描述
trackID*stringTrack 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
}

字段说明:

字段类型描述
Denoisebool是否启用降噪
Offerstring用于 WebRTC/SIP 协商的 SDP offer 字符串
Calleestring被叫的 SIP URI 或电话号码
Callerstring主叫的 SIP URI 或电话号码
Recorder*RecorderOption通话录音配置,参见 RecorderOption
VAD*VADOption语音活动检测配置,参见 VADOption
ASR*ASROption语音识别 (ASR) 配置,参见 ASROption
TTS*TTSOption文字转语音 配置,参见 TTSOption
HandshakeTimeoutstring连接握手超时
EnableIPv6bool启用 IPv6 支持
Sip*SipOptionSIP 注册账号、密码和域配置,参见 SipOption
Extramap[string]string补充参数

RecorderOption

录音配置选项:

type RecorderOption struct {
RecorderFile string
Samplerate int
Ptime int
}

字段说明:

字段类型默认值描述
RecorderFilestring-录音文件路径
Samplerateint16000采样率(Hz)
Ptimeint200数据包时间(毫秒)

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
}

字段说明:

字段类型描述
ProviderstringASR 提供商:tencent, aliyun, Deepgram
Modelstring模型名称
Languagestring语言(例如:zh-CN, en-US),具体参见对应提供商文档
AppIDstring腾讯云的 appId
SecretIDstring腾讯云的 secretId
SecretKeystring腾讯云的 secretKey,或者其他提供商的 API Key
ModelTypestringASR 模型类型(例如:16k_zh, 8k_en),具体参见提供商的文档
BufferSizeint音频缓冲区大小,单位:字节
SampleRateuint32采样率
Endpointstring自定义服务端点 URL
Extramap[string]string提供商特定参数
StartWhenAnswerbool呼叫接通后再请求 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
}

字段说明:

字段类型描述
Samplerateint32采样率,单位:Hz
ProviderstringTTS 提供商:tencent, aliyun, deepgram, voiceapi
Speedfloat32语速
AppIDstring腾讯云的 appId
SecretIDstring腾讯云的 secretId
SecretKeystring腾讯云的 secretKey,或者其他提供商的 API Key
Volumeint32音量(1-10)
Speakerstring音色,参见提供商文档
Codecstring编码格式
Subtitlebool是否启用字幕
Emotionstring情绪:neutral, happy, sad, angry
Endpointstring自定义 TTS 服务端点 URL
Extramap[string]string提供商特定参数
WaitInputTimeoutuint32等待用户输入的最大时间(毫秒)

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
}

字段说明:

字段类型默认值描述
TypestringwebrtcVAD 算法类型:silero, ten, webrtc
Samplerateuint3216000采样率
SpeechPaddinguint64250语音开始 speechPadding 毫秒后开始检测
SilencePaddinguint64100Silence 事件触发间隔,单位毫秒
Ratiofloat320.5语音检测比率阈值
VoiceThresholdfloat320.5语音能量阈值
MaxBufferDurationSecsuint6450最大缓冲区持续时间,单位秒
Endpointstring-自定义 VAD 服务端点
SecretKeystring-VAD 服务身份验证密钥
SecretIDstring-VAD 服务身份验证 ID
SilenceTimeoutuint5000静音检测超时,单位毫秒

SipOption

SIP 配置选项:

type SipOption struct {
Username string
Password string
Realm string
Headers map[string]string
}

字段说明:

字段类型描述
UsernamestringSIP 用户名,用于身份验证
PasswordstringSIP 密码,用于身份验证
RealmstringSIP 域/领域,用于身份验证
Headersmap[string]string额外的 SIP 协议头(键值对)

ReferOption

转接配置选项:

type ReferOption struct {
Denoise bool
Timeout uint32
MusicOnHold string
AutoHangup bool
Sip *SipOption
ASR *ASROption
}

字段说明:

字段类型描述
Denoisebool是否启用降噪
Timeoutuint32超时时间(秒)
MusicOnHoldstring等待音乐 URL
AutoHangupbool转接完成后自动挂断
Sip*SipOptionSIP 配置
ASR*ASROptionASR 配置

事件类型

Client 支持的所有事件类型定义:

Event

基础事件结构,包含事件类型名称。

type Event struct {
Event string `json:"event"`
}

字段说明:

字段类型描述
Eventstring事件类型名称

IncomingEvent

来电事件,当有新的呼入电话时触发。

type IncomingEvent struct {
TrackID string `json:"trackId"`
Timestamp uint64 `json:"timestamp"`
Caller string `json:"caller"`
Callee string `json:"callee"`
Sdp string `json:"sdp"`
}

字段说明:

字段类型描述
TrackIDstring通话轨道 ID
Timestampuint64事件时间戳(毫秒)
Callerstring主叫号码
Calleestring被叫号码
SdpstringSDP offer 字符串

AnswerEvent

接听事件,当通话被接听且 SDP 协商完成时触发。

type AnswerEvent struct {
TrackID string `json:"trackId"`
Timestamp uint64 `json:"timestamp"`
Sdp string `json:"sdp"`
}

字段说明:

字段类型描述
TrackIDstring通话轨道 ID
Timestampuint64事件时间戳(毫秒)
SdpstringSDP answer 字符串

RejectEvent

拒绝事件,当呼叫被拒绝时触发。

type RejectEvent struct {
TrackID string `json:"trackId"`
Timestamp uint64 `json:"timestamp"`
Reason string `json:"reason"`
}

字段说明:

字段类型描述
TrackIDstring通话轨道 ID
Timestampuint64事件时间戳(毫秒)
Reasonstring拒绝原因

RingingEvent

振铃事件,当通话振铃时触发(SIP 通话)。

type RingingEvent struct {
TrackID string `json:"trackId"`
Timestamp uint64 `json:"timestamp"`
EarlyMedia bool `json:"earlyMedia"`
}

字段说明:

字段类型描述
TrackIDstring通话轨道 ID
Timestampuint64事件时间戳(毫秒)
EarlyMediabool是否有早期媒体

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 字段说明:

字段类型描述
Timestampuint64事件时间戳(毫秒)
Reasonstring挂断原因
Initiatorstring挂断发起方
StartTimestring通话开始时间
HangupTimestring挂断时间
AnswerTime*string接听时间
RingingTime*string振铃时间
From*HangupEventAttendee主叫信息
To*HangupEventAttendee被叫信息
Extramap[string]any额外信息

HangupEventAttendee 字段说明:

字段类型描述
Usernamestring用户名
Realmstring
Sourcestring来源

SpeakingEvent

说话事件,当 VAD 检测到用户开始说话时触发。

type SpeakingEvent struct {
TrackID string `json:"trackId"`
Timestamp uint64 `json:"timestamp"`
StartTime uint64 `json:"startTime"`
}

字段说明:

字段类型描述
TrackIDstring通话轨道 ID
Timestampuint64事件时间戳(毫秒)
StartTimeuint64语音开始时间(毫秒)

SilenceEvent

静音事件,当检测到用户停止说话后触发。

type SilenceEvent struct {
TrackID string `json:"trackId"`
Timestamp uint64 `json:"timestamp"`
StartTime uint64 `json:"startTime"`
Duration uint64 `json:"duration"`
}

字段说明:

字段类型描述
TrackIDstring通话轨道 ID
Timestampuint64事件时间戳(毫秒)
StartTimeuint64静音开始时间(毫秒)
Durationuint64静音持续时间(毫秒)

EouEvent

语音结束事件,当检测到语音结束时触发。

type EouEvent struct {
TrackID string `json:"trackId"`
Timestamp uint64 `json:"timestamp"`
Complete bool `json:"complete"`
}

字段说明:

字段类型描述
TrackIDstring通话轨道 ID
Timestampuint64事件时间戳(毫秒)
Completebool是否完整结束

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"`
}

字段说明:

字段类型描述
TrackIDstring通话轨道 ID
Timestampuint64事件时间戳(毫秒)
Indexuint32语音片段索引
StartTime*uint64语音开始时间(毫秒)
EndTime*uint64语音结束时间(毫秒)
Textstring识别的文本内容

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"`
}

字段说明:

字段类型描述
TrackIDstring通话轨道 ID
Indexuint32语音片段索引
Timestampuint64事件时间戳(毫秒)
StartTime*uint64语音开始时间(毫秒)
EndTime*uint64语音结束时间(毫秒)
Textstring识别的文本内容(可能变化)

TrackStartEvent

Track 开始事件,当 Track 开始时触发(RTP、TTS、文件播放等)。

type TrackStartEvent struct {
TrackID string `json:"trackId"`
Timestamp uint64 `json:"timestamp"`
PlayId *string `json:"playId,omitempty"`
}

字段说明:

字段类型描述
TrackIDstring轨道 ID
Timestampuint64事件时间戳(毫秒)
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"`
}

字段说明:

字段类型描述
TrackIDstring轨道 ID
Timestampuint64事件时间戳(毫秒)
Durationuint64播放持续时间(毫秒)
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"`
}

字段说明:

字段类型描述
TrackIDstring轨道 ID
Timestampuint64事件时间戳(毫秒)
Subtitle*string已播放的字幕文本
Position*uint32播放位置(字符数)
TotalDurationuint32总时长(毫秒)
Currentuint32当前播放时长(毫秒)

DTMFEvent

DTMF 事件,当检测到按键时触发。

type DTMFEvent struct {
TrackID string `json:"trackId"`
Timestamp uint64 `json:"timestamp"`
Digit string `json:"digit"`
}

字段说明:

字段类型描述
TrackIDstring通话轨道 ID
Timestampuint64事件时间戳(毫秒)
Digitstring按键值(0-9, *, #, A-D)

AnswerMachineDetectionEvent

答录机检测事件,当检测到答录机时触发。

type AnswerMachineDetectionEvent struct {
Timestamp uint64 `json:"timestamp"`
StartTime uint64 `json:"startTime"`
EndTime uint64 `json:"endTime"`
Text string `json:"text"`
}

字段说明:

字段类型描述
Timestampuint64事件时间戳(毫秒)
StartTimeuint64检测开始时间(毫秒)
EndTimeuint64检测结束时间(毫秒)
Textstring检测到的文本

LLMFinalEvent

LLM 最终结果事件,当大语言模型生成最终结果时触发。

type LLMFinalEvent struct {
Timestamp uint64 `json:"timestamp"`
Text string `json:"text"`
}

字段说明:

字段类型描述
Timestampuint64事件时间戳(毫秒)
TextstringLLM 生成的最终文本

LLMDeltaEvent

LLM 增量结果事件,当大语言模型生成增量结果时触发。

type LLMDeltaEvent struct {
Timestamp uint64 `json:"timestamp"`
Word string `json:"word"`
}

字段说明:

字段类型描述
Timestampuint64事件时间戳(毫秒)
WordstringLLM 生成的增量词汇

MetricsEvent

指标事件,当收集性能指标时触发。

type MetricsEvent struct {
Timestamp uint64 `json:"timestamp"`
Key string `json:"key"`
Duration uint32 `json:"duration"`
Data map[string]any `json:"data"`
}

字段说明:

字段类型描述
Timestampuint64事件时间戳(毫秒)
Keystring指标名称
Durationuint32持续时间(毫秒)
Datamap[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"`
}

字段说明:

字段类型描述
TrackIDstring通话轨道 ID
Timestampuint64事件时间戳(毫秒)
Senderstring错误来源(asr、tts、media 等)
Errorstring错误信息
Code*uint32错误代码

AddHistoryEvent

添加历史记录事件,当添加对话历史记录时触发。

type AddHistoryEvent struct {
Sender string `json:"sender"`
Timestamp uint64 `json:"timestamp"`
Speaker string `json:"speaker"`
Text string `json:"text"`
}

字段说明:

字段类型描述
Senderstring发送者
Timestampuint64事件时间戳(毫秒)
Speakerstring说话人标识
Textstring对话文本

OtherEvent

其他事件,用于处理未定义的事件类型。

type OtherEvent struct {
TrackID string `json:"trackId"`
Timestamp uint64 `json:"timestamp"`
Sender string `json:"sender"`
Extra map[string]string `json:"extra,omitempty"`
}

字段说明:

字段类型描述
TrackIDstring通话轨道 ID
Timestampuint64事件时间戳(毫秒)
Senderstring发送者
Extramap[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)
}()
}

故障排除

连接问题

如果无法连接到服务器:

  1. 检查端点 URL 是否正确
  2. 确认服务器正在运行
  3. 检查防火墙设置
  4. 启用调试日志查看详细信息
logger.SetLevel(logrus.DebugLevel)
client := rustpbxgo.NewClient(
endpoint,
rustpbxgo.WithLogger(logger),
rustpbxgo.WithDumpEvents(true),
)

ASR 不工作

如果 ASR 无法识别语音:

  1. 确认 ASR 配置正确
  2. 检查 API 密钥是否有效
  3. 验证采样率设置
  4. 检查 VAD 配置

TTS 无声音

如果 TTS 没有播放声音:

  1. 检查 TTS 配置是否正确
  2. 验证说话人参数
  3. 确认 endOfStream 设置正确
  4. 检查网络连接

更多资源

参考

完整示例代码请参考:/Users/yangli/Desktop/rustpbxgo/cmd