@kookapp/voice-bot-sdk
v0.1.0
Published
Node bot adapter for KOOK WebRTC voice channels
Keywords
Readme
@kookapp/voice-bot-sdk
voice-bot-sdk 是 KOOK 语音机器人的 Node.js 适配层。它基于 @kookapp/voice-rtc-core 连接语音频道,并通过可注入的 WebRTC 实现提供 PCM 输入和输出。
本包不内置 wrtc 等原生依赖。不同 Node.js 版本、操作系统和部署环境需要选择不同的 WebRTC 实现,因此由机器人项目自行安装和注入。
功能
- 使用 QuickJoin 和 InitSpeaking 加入 KOOK 语音频道。
- 默认以只收听模式建立会话,之后可升级为真实音频 Producer。
- 通过
NodeAudioSource把 PCM 数据发送到频道。 - 通过
NodeAudioSink接收远端 PCM 数据。 - 自动安装并在关闭时恢复 mediasoup 所需的 WebRTC 全局对象。
- ESM、CommonJS 和 TypeScript 类型声明。
环境要求
- Node.js 20 或更高版本。
- 一个兼容
wrtcAPI 的 WebRTC 实现。 - WebRTC 实现至少提供
RTCPeerConnection和MediaStream。 - 使用 PCM 输入输出时,还要提供
nonstandard.RTCAudioSource和nonstandard.RTCAudioSink。
安装
pnpm add @kookapp/voice-bot-sdk再安装项目选择的 WebRTC 实现。例如:
pnpm add wrtcwrtc 这里只是接口示例。正式部署前必须确认该实现支持目标 Node.js 版本、操作系统和 CPU 架构。
最小接收示例
import wrtc from 'wrtc'
import NodeBotClient from '@kookapp/voice-bot-sdk'
const channelId = 'channel-123'
const botUid = 'bot-456'
const bot = new NodeBotClient({
webrtc: wrtc,
uid: botUid,
joinId: `bot-${Date.now()}-${channelId}`,
autoAudioSink: true,
})
bot.on('audio-data', ({ peerId, consumerId, data }) => {
console.log('收到 PCM', {
peerId,
consumerId,
sampleRate: data.sampleRate,
channelCount: data.channelCount,
samples: data.samples.length,
})
})
bot.on('peer-join', (peer) => console.log('成员加入', peer))
bot.on('peer-leave', (peer) => console.log('成员离开', peer))
bot.on('error', (error) => console.error('RTC 错误', error))
await bot.connect({
channelId,
tokenResponse,
listenOnly: true,
muted: true,
deafened: false,
})connect() 默认 listenOnly: true。SDK 会通过静音音轨完成 InitSpeaking,使机器人可以稳定接收远端 Consumer。
Token 使用
connect() 可以直接接收 KOOK Token 响应:
await bot.connect({
channelId: 'channel-123',
tokenResponse: {
gateway_url: 'wss://gateway.example.com/path?token=...',
iceServers: [
{
urls: 'turn:turn.example.com',
username: 'user',
credential: 'pass',
},
],
iceTransportPolicy: 'all',
rtc_config: {},
},
})也可以在业务层提前归一化:
import { normalizeVoiceTokenConfig } from '@kookapp/voice-bot-sdk'
const tokenConfig = normalizeVoiceTokenConfig(tokenResponse)
await bot.connect({
channelId: 'channel-123',
tokenConfig,
})不要记录完整 gateway_url,其中通常包含临时鉴权信息。
发送 PCM 音频
const source = bot.createAudioSource({
sampleRate: 48000,
bitsPerSample: 16,
channelCount: 1,
frameDurationMs: 10,
})
await bot.publishAudioSource(source, {
stopTracks: false,
})
// 48 kHz、单声道、10 ms 对应 480 个采样点
const frame = new Int16Array(480)
source.push(frame, {
sampleRate: 48000,
bitsPerSample: 16,
channelCount: 1,
numberOfFrames: 480,
})push() 不负责调度播放节奏。调用方必须按真实时间持续推送 PCM 帧,例如每 10 ms 推送一帧,否则远端会出现卡顿、加速或断续。
PCM 数据要求:
Int16Array或可转换为Int16Array的数据。- 有符号 16 位 PCM。
- 多声道数据按帧交错排列。
numberOfFrames是每声道帧数,不是数组总长度。
停止发布:
await bot.unpublishTrack()
source.close()接收 PCM 音频
当 autoAudioSink 为 true 时,SDK 会为每个远端音轨自动创建 NodeAudioSink,并派发 audio-data:
bot.on('audio-data', ({ peerId, data }) => {
pcmRecorder.write(peerId, data.samples, {
sampleRate: data.sampleRate,
channelCount: data.channelCount,
})
})需要自行管理 Sink 时:
const bot = new NodeBotClient({
webrtc: wrtc,
uid: botUid,
joinId,
autoAudioSink: false,
})
bot.on('audio-track', ({ peerId, track }) => {
const sink = bot.createAudioSink(track)
sink.on('data', (frame) => {
console.log(peerId, frame.samples)
})
})手动创建的 Sink 也必须由调用方调用 close()。
复用 NodeWebRtcRuntime
多个 Bot 使用同一个 WebRTC 实现时,可以显式创建 Runtime:
import { NodeBotClient, NodeWebRtcRuntime } from '@kookapp/voice-bot-sdk'
const runtime = new NodeWebRtcRuntime(wrtc, {
handlerName: 'Chrome111',
})
const botA = new NodeBotClient({ runtime, uid: 'bot-a', joinId: 'join-a' })
const botB = new NodeBotClient({ runtime, uid: 'bot-b', joinId: 'join-b' })Runtime 会对全局 WebRTC 对象做引用计数。最后一个客户端关闭后,会恢复进程原有的全局对象。
connect 参数
| 参数 | 默认值 | 说明 |
| --- | --- | --- |
| channelId / roomId | 构造时的 joinId | KOOK 频道或 RTC 房间 ID |
| tokenResponse | 无 | 原始 Token 接口响应 |
| tokenConfig | 无 | 已归一化的 Token 配置 |
| gatewayUrl / gateway_url | Token 中的地址 | Protoo WebSocket 地址 |
| iceServers | Token 中的配置 | TURN/STUN 配置 |
| iceTransportPolicy | Token 中的策略 | ICE 传输策略 |
| forceRelay | Token 中的配置 | 是否强制中继 |
| listenOnly | true | 是否使用静音占位音轨加入 |
| muted | false | 初始麦克风暂停状态 |
| deafened | false | 初始耳机静音状态 |
常用事件
| 事件 | 说明 |
| --- | --- |
| audio-track | 收到远端音轨;包含自动创建的 sink 或 null |
| audio-data | 自动 Sink 输出的一帧 PCM |
| consumer / consumer-close | Core Consumer 生命周期 |
| peer-join / peer-leave | 成员变化 |
| connection-state-change | RTC 连接状态变化 |
| reconnect / disconnect | 服务端重连或断开通知 |
| error | 异步错误 |
关闭
try {
await bot.leave()
} finally {
bot.close()
}close() 会关闭自动创建的 Sink、RTC Core 和注入的全局 WebRTC 环境。进程退出前还应关闭业务侧创建的音频源、文件句柄和定时器。
不包含的能力
- 不负责申请 KOOK Bot Token 或频道 Token。
- 不内置音频解码器、FFmpeg、音乐播放队列或混音器。
- 不提供浏览器设备 API 和 AI 降噪。
- 不保证任意
wrtc分支都兼容,必须进行目标环境端到端验证。
测试、构建和打包
pnpm --filter @kookapp/voice-bot-sdk test
pnpm --filter @kookapp/voice-bot-sdk typecheck
pnpm --filter @kookapp/voice-bot-sdk build
pnpm --filter @kookapp/voice-bot-sdk dev
pnpm --dir packages/voice-bot-sdk pack --pack-destination packsdev 会持续构建 ESM、CommonJS 和类型声明。Node 消费项目可以配合 node --watch 或进程管理器在产物变化后重启。
