# teamspeak-go SDK 文档 基于 `github.com/honeybbq/teamspeak-go` 源码整理。 触发形式说明: - **客户端请求** — 客户端主动发送命令到服务端,等待响应 - **服务端推送** — 服务端主动下发通知,客户端被动接收 - **本地调用** — 纯客户端本地操作,不涉及网络通信 > **关于"通过指令构建的能力"**: 部分 API 标注为"通过指令构建的能力",表示其底层封装了 TS3 协议命令(如 `serverinfo`、`channelinfo`、`banlist` 等),通过 SDK 的 `ExecCommand` / `ExecCommandWithResponse` 基础设施发送并解析响应。这些命令与 SDK 原生内置的命令(如握手、事件通知)不同,是通过协议命令扩展出的额外能力,可参考对应的协议命令语法进行调试或扩展。 --- ## 1. 连接管理 ### 构造与连接 | 方法 | 触发形式 | 用法 | 作用 | 描述 | |------|----------|------|------|------| | `NewClient` | 本地调用 | `NewClient(identity, addr, nickname, ...options) *Client` | 创建客户端 | 创建 TeamSpeak 客户端实例。`identity` 为加密身份,`addr` 为服务器地址,`nickname` 为昵称,`options` 可选配置 | | `Connect` | 客户端请求 | `Connect() error` | 连接服务器 | 发起 UDP 会话和握手连接 | | `Disconnect` | 客户端请求 | `Disconnect() error` | 断开连接 | 优雅断开连接,发送 shutdown reason | | `WaitConnected` | 本地调用 | `WaitConnected(ctx context.Context) error` | 等待连接就绪 | 阻塞等待握手完成,支持 context 取消。发送命令前必须先调用 | ### 连接选项(ClientOption) | 方法 | 触发形式 | 用法 | 作用 | 描述 | |------|----------|------|------|------| | `WithLogger` | 本地调用 | `WithLogger(logger *slog.Logger)` | 设置日志 | 注入自定义 slog.Logger | | `WithResolver` | 本地调用 | `WithResolver(r AddrResolver)` | 设置解析器 | 自定义 DNS/TSDNS 解析 | | `WithServerPassword` | 本地调用 | `WithServerPassword(password string)` | 服务器密码 | 连接时使用的服务器密码 | | `WithDefaultChannel` | 本地调用 | `WithDefaultChannel(channel string)` | 默认频道 | 连接后自动加入的频道名 | | `WithDefaultChannelPassword` | 本地调用 | `WithDefaultChannelPassword(password string)` | 默认频道密码 | 默认频道的密码 | | `WithCommandMiddleware` | 本地调用 | `WithCommandMiddleware(mw ...CommandMiddleware)` | 命令中间件 | 拦截/修改发送的命令 | | `WithEventMiddleware` | 本地调用 | `WithEventMiddleware(mw ...EventMiddleware)` | 事件中间件 | 拦截/修改接收的事件 | --- ## 2. 事件注册 ### 事件处理器 | 方法 | 触发形式 | 用法 | 作用 | 描述 | |------|----------|------|------|--------------------------------------------------------| | `OnConnected` | 服务端推送 | `OnConnected(fn func())` | 连接成功 | 客户端完成握手后触发 | | `OnDisconnected` | 服务端推送 | `OnDisconnected(fn func(error))` | 断开连接 | 连接断开时触发,携带错误原因 | | `OnTextMessage` | 服务端推送 | `OnTextMessage(fn func(TextMessage))` | 收到消息 | 收到文本消息(私聊/频道/服务器),对应 `notifytextmessage` | | `OnClientEnter` | 服务端推送 | `OnClientEnter(fn func(ClientInfo))` | 用户进入 | 客户端进入视野(进入服务器),对应 `notifycliententerview` | | `OnClientLeave` | 服务端推送 | `OnClientLeave(fn func(ClientLeftViewEvent))` | 用户离开 | 客户端离开视野(离开服务器),对应 `notifyclientleftview` | | `OnClientMoved` | 服务端推送 | `OnClientMoved(fn func(ClientMovedEvent))` | 用户移动 | 客户端在频道间移动,对应 `notifyclientmoved` | | `OnPoked` | 服务端推送 | `OnPoked(fn func(PokeEvent))` | 被戳一戳 | 收到其他用户的 Poke,对应 `notifyclientpoke` | | `OnKicked` | 服务端推送 | `OnKicked(fn func(string))` | 被踢出 | 自己被踢出频道或服务器,从 `notifyclientleftview` 中 reasonid=4/5 触发 | | `OnVoiceData` | 服务端推送 | `OnVoiceData(fn func(VoiceDataEvent))` | 收到语音 | 收到同频道其他客户端发送的 Opus 语音帧,通过 UDP 二进制包传输 | ### VoiceDataEvent 结构 收到语音数据的事件载荷,由 `OnVoiceData` 回调接收。 | 字段 | 类型 | 描述 | |------|------|------| | `ClientID` | `uint16` | 发送者客户端 ID | | `Data` | `[]byte` | Opus 编码的语音帧原始数据 | | `Codec` | `byte` | 编解码器类型:4 = Opus Voice(语音),5 = Opus Music(音乐) | **语音包 UDP 二进制格式**(SDK 内部解析后填充 `VoiceDataEvent`): ``` Offset Size Field 0 2 packetID(big-endian) 2 2 clientID(little-endian) 4 1 codec(4=Opus Voice, 5=Opus Music) 5 N Opus 编码数据 ``` **语音接收流程**: ``` Server UDP 语音包 → PacketHandler 解密 → handlePacket() 路由 PacketTypeVoice(0) / PacketTypeVoiceWhisper(1) → 解析 clientID、codec、opusData → notifyEvent(VoiceDataEvent{...}) → startEventLoop 串行分发 → OnVoiceData 注册的所有 handler 依次调用 ``` --- ## 3. 聊天命令 | 方法 | 触发形式 | 用法 | 作用 | 描述 | |------|----------|------|------|------| | `SendTextMessage` | 客户端请求 | `SendTextMessage(targetMode int, targetID uint64, msg string) error` | 发送文本消息 | `targetMode`: 1=私聊, 2=频道, 3=服务器。对应协议 `sendtextmessage` | | `Poke` | 客户端请求 | `Poke(clid uint16, msg string) error` | 发送 Poke | 向指定用户发送戳一戳消息。对应协议 `clientpoke` | ### TextMessage 结构 | 字段 | 类型 | 描述 | |------|------|------| | `TargetMode` | `int` | 1=私聊, 2=频道, 3=服务器 | | `Target` | `uint64` | 目标 ID(频道 ID 或客户端 ID) | | `InvokerID` | `uint16` | 发送者客户端 ID | | `InvokerName` | `string` | 发送者昵称 | | `InvokerUID` | `string` | 发送者唯一标识 | | `InvokerGroups` | `[]string` | 发送者所在组 | | `Message` | `string` | 消息内容 | ### PokeEvent 结构 | 字段 | 类型 | 描述 | |------|------|------| | `InvokerID` | `uint16` | 发送者客户端 ID | | `InvokerName` | `string` | 发送者昵称 | | `InvokerUID` | `string` | 发送者唯一标识 | | `Message` | `string` | Poke 消息内容 | --- ## 4. 客户端命令 ### 基础客户端操作 | 方法 | 触发形式 | 用法 | 作用 | 描述 | |------|----------|------|------|------| | `ClientID` | 本地调用 | `ClientID() uint16` | 获取自身 ID | 返回服务器分配的客户端 ID(本地缓存) | | `ListClients` | 客户端请求 | `ListClients() ([]ClientInfo, error)` | 在线用户列表 | 返回当前服务器所有在线客户端。对应协议 `clientlist` | | `ClientMove` | 客户端请求 | `ClientMove(clid uint16, channelID uint64, password string) error` | 移动用户 | 将客户端移至指定频道。对应协议 `clientmove` | ### 客户端信息查询 > **通过指令构建的能力** — 底层封装 TS3 协议命令 `clientinfo`、`clientdblist`、`clientdbfind`。 | 方法 | 触发形式 | 协议命令 | 用法 | 作用 | |------|----------|----------|------|------| | `GetClientInfo` | 客户端请求 | `clientinfo clid=X` | `GetClientInfo(clid uint16) (map[string]string, error)` | 查询在线用户详情(原始 map) | | `GetClientDetailInfo` | 客户端请求 | `clientinfo clid=X` | `GetClientDetailInfo(clid uint16) (*ClientDetailInfo, error)` | 查询在线用户详情(结构化) | | `ListDBClients` | 客户端请求 | `clientdblist start=X duration=X` | `ListDBClients(start, duration int) ([]DBClient, error)` | 数据库客户端列表 | | `FindClientByName` | 客户端请求 | `clientdbfind pattern=X -uid` | `FindClientByName(nickname string) (uid string, dbid uint64, err error)` | 按昵称搜索数据库用户 | | `FindClientByDBID` | 客户端请求 | `clientdbfind -uid cldbid=X` | `FindClientByDBID(dbid uint64) (string, error)` | 按 DBID 查找 UID | ### 客户端状态与操作 > **通过指令构建的能力** — 底层封装 TS3 协议命令 `clientupdate`、`clientkick`。 | 方法 | 触发形式 | 协议命令 | 用法 | 作用 | |------|----------|----------|------|------| | `UpdateSelf` | 客户端请求 | `clientupdate` | `UpdateSelf(properties map[string]string) error` | 更新自身属性(昵称、away、静音等) | | `KickClient` | 客户端请求 | `clientkick clid=X reasonid=X reasonmsg=X` | `KickClient(clid uint16, reasonID int, reasonMsg string) error` | 踢出用户(4=频道踢出, 5=服务器踢出) | ### ClientInfo 结构 | 字段 | 类型 | 描述 | |------|------|------| | `ID` | `uint16` | 客户端 ID | | `Nickname` | `string` | 昵称 | | `ChannelID` | `uint64` | 所在频道 ID | | `UID` | `string` | 唯一标识 | | `Type` | `int` | 客户端类型 | | `ServerGroups` | `[]string` | 所在服务器组 | ### ClientDetailInfo 结构 通过 `clientinfo clid=X` 获取的完整客户端信息。比 `ClientInfo` 多出 away 状态、平台、版本、IP 等字段。 | 字段 | 类型 | 描述 | |------|------|------| | `ID` | `uint16` | 客户端 ID (clid) | | `Nickname` | `string` | 昵称 | | `UID` | `string` | 唯一标识 | | `ChannelID` | `uint64` | 所在频道 ID | | `Type` | `int` | 客户端类型 | | `ServerGroups` | `[]string` | 所在服务器组 | | `Away` | `bool` | 是否离开 | | `AwayMessage` | `string` | 离开消息 | | `InputMuted` | `bool` | 输入静音 | | `OutputMuted` | `bool` | 输出静音 | | `Platform` | `string` | 客户端平台 | | `Version` | `string` | 客户端版本 | | `IP` | `string` | 客户端 IP(需权限) | | `Created` | `int64` | 首次连接时间(unix 时间戳) | | `LastConnected` | `int64` | 最近连接时间 | | `TotalConnections` | `int` | 总连接次数 | | `Description` | `string` | 用户描述 | | `IconID` | `int64` | 用户图标 ID | ### DBClient 结构 | 字段 | 类型 | 描述 | |------|------|------| | `DBID` | `uint64` | 数据库 ID (cldbid) | | `UID` | `string` | 唯一标识 | | `Nickname` | `string` | 昵称 | | `Created` | `int64` | 首次连接时间 | | `LastConnected` | `int64` | 最近连接时间 | | `TotalConnections` | `int` | 总连接次数 | | `Description` | `string` | 用户描述 | ### UpdateSelf 常用参数 | 参数 | 值 | 说明 | |------|------|------| | `client_nickname` | 字符串 | 新昵称 | | `client_away` | `"1"` / `"0"` | 是否离开 | | `client_away_message` | 字符串 | 离开消息 | | `client_input_muted` | `"1"` / `"0"` | 输入静音 | | `client_output_muted` | `"1"` / `"0"` | 输出静音 | | `client_phonetic_nickname` | 字符串 | 语音昵称 | ### Bridge 层 JSON 接口 | 方法 | 返回 | 描述 | |------|------|------| | `GetClientsJSON()` | `string` | 在线客户端列表(含 isSelf 标志) | | `GetClientDetailInfoJSON(clid int)` | `string` | 单个客户端详细信息 | | `ListDBClientsJSON(start, duration int)` | `string` | 数据库客户端列表 | | `UpdateSelfJSON(propertiesJSON string)` | `string` | 更新自身(错误信息或空串) | | `KickClient(clid int, reasonID int, reasonMsg string)` | `string` | 踢出用户(错误信息或空串) | ### ClientLeftViewEvent 结构 | 字段 | 类型 | 描述 | |------|------|------| | `ClientID` | `uint16` | 离开的客户端 ID | | `ReasonID` | `int` | 原因:0=正常离开, 4=频道踢, 5=服务器踢 | | `ReasonMessage` | `string` | 原因描述 | | `IsSelf` | `bool` | 是否是自己 | ### ClientMovedEvent 结构 | 字段 | 类型 | 描述 | |------|------|------| | `ClientID` | `uint16` | 被移动的客户端 ID | | `TargetChannelID` | `uint64` | 目标频道 ID | | `ReasonID` | `int` | 原因 | | `InvokerID` | `uint16` | 操作者 ID | | `InvokerName` | `string` | 操作者昵称 | | `InvokerUID` | `string` | 操作者唯一标识 | --- ## 5. 频道命令 ### 基础频道列表 | 方法 | 触发形式 | 用法 | 作用 | 描述 | |------|----------|------|------|------| | `ListChannels` | 客户端请求 | `ListChannels() ([]ChannelInfo, error)` | 频道列表(基础) | 返回服务器所有频道的基础信息(ID、父频道、名称)。对应协议 `channellist` | ### 详细频道列表 | 方法 | 触发形式 | 用法 | 作用 | 描述 | |------|----------|------|------|------| | `ListChannelsDetailed` | 客户端请求 | `ListChannelsDetailed() ([]ChannelInfoDetailed, error)` | 频道列表(详细) | 返回服务器所有频道的完整属性。对应协议 `channellist -topic -flags -voice -limits -icon` | `channellist` 命令支持的 flag 参数: | Flag | 返回字段 | 说明 | |------|---------|------| | `-topic` | `channel_topic` | 频道主题描述 | | `-flags` | `channel_flag_permanent`, `channel_flag_semi_permanent`, `channel_flag_default`, `channel_flag_password`, `channel_flag_maxclients_unlimited`, `channel_flag_maxfamilyclients_unlimited`, `channel_order` | 频道标志位(永久、半永久、默认、密码、人数限制、排序) | | `-voice` | `channel_codec`, `channel_codec_quality`, `channel_needed_talk_power` | 语音编解码相关 | | `-limits` | `channel_maxclients`, `channel_maxfamilyclients` | 频道人数限制 | | `-icon` | `channel_icon_id` | 频道自定义图标 ID | ### 频道查询 > **通过指令构建的能力** — 底层封装 TS3 协议命令 `channelinfo`、`channelfind`。 | 方法 | 触发形式 | 协议命令 | 用法 | 作用 | |------|----------|----------|------|------| | `GetChannelInfo` | 客户端请求 | `channelinfo cid=X` | `GetChannelInfo(channelID uint64) (*ChannelDetailInfo, error)` | 单频道完整详情(含 description) | | `FindChannels` | 客户端请求 | `channelfind pattern=X` | `FindChannels(pattern string) ([]ChannelInfo, error)` | 按名称搜索频道 | ### 频道管理 > **通过指令构建的能力** — 底层封装 TS3 协议命令 `channelcreate`、`channeledit`、`channeldelete`、`channelmove`。 | 方法 | 触发形式 | 协议命令 | 用法 | 作用 | |------|----------|----------|------|------| | `CreateChannel` | 客户端请求 | `channelcreate channel_name=X ...` | `CreateChannel(name string, options map[string]string) (uint64, error)` | 创建频道,返回新频道 ID | | `EditChannel` | 客户端请求 | `channeledit cid=X ...` | `EditChannel(channelID uint64, properties map[string]string) error` | 编辑频道属性 | | `DeleteChannel` | 客户端请求 | `channeldelete cid=X force=X` | `DeleteChannel(channelID uint64, force bool) error` | 删除频道,force=true 强制删除 | | `MoveChannel` | 客户端请求 | `channelmove cid=X cpid=X order=X` | `MoveChannel(channelID, parentID, order uint64) error` | 移动频道到新父频道或调整排序 | #### CreateChannel 常用可选参数 | 参数 | 值 | 说明 | |------|------|------| | `channel_topic` | 字符串 | 频道主题 | | `channel_flag_permanent` | `"1"` / `"0"` | 永久频道 | | `channel_flag_semi_permanent` | `"1"` / `"0"` | 半永久频道 | | `channel_flag_default` | `"1"` / `"0"` | 默认频道 | | `channel_password` | 字符串 | 频道密码 | | `cpid` | 字符串(父频道ID) | 父频道 | | `channel_maxclients` | 字符串 | 最大人数 | | `channel_codec` | 字符串 | 编解码器(0-5) | ### ChannelInfo 结构(基础) | 字段 | 类型 | 描述 | |------|------|------| | `ID` | `uint64` | 频道 ID | | `ParentID` | `uint64` | 父频道 ID(0 = 顶层频道) | | `Name` | `string` | 频道名称(已 Unescape) | | `Description` | `string` | 频道描述(`ListChannels` 返回为空) | ### ChannelInfoDetailed 结构(详细列表项) 来自 `channellist -topic -flags -voice -limits -icon` 的批量列表项。 | 字段 | 类型 | 描述 | |------|------|------| | **基础** | | | | `ID` | `uint64` | 频道 ID | | `ParentID` | `uint64` | 父频道 ID(0 = 顶层频道) | | `Order` | `uint64` | 排序顺序(前一个频道 ID,0 = 最顶部) | | `Name` | `string` | 频道名称 | | `Topic` | `string` | 频道主题(来自 `-topic`) | | **语音(`-voice`)** | | | | `Codec` | `int` | 0=Speex Narrowband, 1=Speex Wideband, 2=Speex UltraWideband, 3=CELT Mono, **4=Opus Voice**, **5=Opus Music** | | `CodecQuality` | `int` | 编解码质量(0-10) | | `NeededTalkPower` | `int` | 发言所需权限等级 | | **限制(`-limits`)** | | | | `MaxClients` | `int` | 最大客户端数 | | `MaxFamilyClients` | `int` | 最大族客户端数 | | `IsMaxClientsUnlimited` | `bool` | 是否无限人数 | | `IsMaxFamilyClientsUnlimited` | `bool` | 是否无限族人数 | | **标志(`-flags`)** | | | | `IsPermanent` | `bool` | 永久频道 | | `IsSemiPermanent` | `bool` | 半永久频道 | | `IsDefault` | `bool` | 默认频道 | | `IsPassword` | `bool` | 是否设置密码 | | `IsOrdered` | `bool` | 是否手动排序 | | `NeededModifyPower` | `int` | 修改频道所需权限 | | **图标(`-icon`)** | | | | `IconID` | `int64` | 频道图标 ID | ### ChannelDetailInfo 结构(单频道详情) 来自 `channelinfo cid=X` 的完整频道信息,比 `ChannelInfoDetailed` 多出 `Description`(完整描述)和 `BannerGfxURL` 等字段。 | 字段 | 类型 | 描述 | |------|------|------| | `ID` | `uint64` | 频道 ID | | `ParentID` | `uint64` | 父频道 ID | | `Name` | `string` | 频道名称 | | `Topic` | `string` | 频道主题 | | `Description` | `string` | 完整描述 | | `Codec` | `int` | 编解码器 | | `CodecQuality` | `int` | 编解码质量 | | `MaxClients` | `int` | 最大人数 | | `MaxFamilyClients` | `int` | 最大族人数 | | `NeededTalkPower` | `int` | 发言权限 | | `IconID` | `int64` | 图标 ID | | `IsPermanent` | `bool` | 永久 | | `IsSemiPermanent` | `bool` | 半永久 | | `IsDefault` | `bool` | 默认 | | `IsPassword` | `bool` | 有密码 | | `Order` | `uint64` | 排序 | | `BannerGfxURL` | `string` | Banner 图片 URL | ### Bridge 层 JSON 接口 | 方法 | 返回 | 描述 | |------|------|------| | `GetChannelsJSON()` | `string` | 基础频道列表(向后兼容) | | `GetChannelsDetailedJSON()` | `string` | 详细频道列表(含 flags/voice/limits/icon) | | `GetChannelDetailInfoJSON(channelIDStr string)` | `string` | 单频道完整详情 | | `CreateChannelJSON(name, propertiesJSON string)` | `string` | 创建频道,返回新频道 ID(空串=失败) | | `EditChannelJSON(channelIDStr, propertiesJSON string)` | `string` | 编辑频道(错误信息或空串) | | `DeleteChannel(channelIDStr string, force bool)` | `string` | 删除频道(错误信息或空串) | `GetChannelsDetailedJSON()` 返回的 JSON 示例: ```json [ { "id": "1", "name": "Lobby", "parentId": "0", "topic": "欢迎来到大厅", "order": "0", "codec": 4, "codecQuality": 7, "neededTalkPower": 0, "maxClients": -1, "maxFamilyClients": -1, "isMaxClientsUnlimited": true, "isMaxFamilyClientsUnlimited": true, "isPermanent": true, "isSemiPermanent": false, "isDefault": true, "isPassword": false, "isOrdered": false, "iconId": "0", "neededModifyPower": 75 } ] ``` ### 典型用法 ```go // 获取详细频道列表 channels, err := client.ListChannelsDetailed() if err != nil { return err } for _, ch := range channels { // 显示频道名和状态图标 icons := "" if ch.IsPassword { icons += "🔒" } if ch.IsPermanent { icons += "📌" } if !ch.IsMaxClientsUnlimited { icons += fmt.Sprintf(" 👥%d", ch.MaxClients) } log.Printf("%s %s %s", icons, ch.Name, ch.Topic) // 根据编解码器选择解码策略 switch ch.Codec { case 4: // Opus Voice — 20ms 帧,适合语音 case 5: // Opus Music — 更高采样率,适合音乐 } } ``` --- ## 6. 语音命令 ### 发送语音 | 方法 | 触发形式 | 用法 | 作用 | 描述 | |------|----------|------|------|------| | `SendVoice` | 客户端请求 | `SendVoice(data []byte, codec byte) error` | 发送语音帧 | 发送原始 Opus 帧。codec: 4=Opus 语音, 5=Opus 音乐。通过 UDP 传输 | ### 接收语音 接收语音通过事件回调实现,无需主动调用。 | 方法 | 触发形式 | 用法 | 作用 | 描述 | |------|----------|------|------|------| | `OnVoiceData` | 服务端推送 | `OnVoiceData(fn func(VoiceDataEvent))` | 注册语音接收回调 | 当同频道其他客户端发送语音时,SDK 解密后触发回调 | **Codec 值说明**: | 值 | 类型 | 适用场景 | |----|------|---------| | `4` | Opus Voice | 语音通话(默认) | | `5` | Opus Music | 音乐/高保真音频 | **典型用法**: ```go client.OnVoiceData(func(evt teamspeak.VoiceDataEvent) { // evt.ClientID — 发送者客户端 ID // evt.Data — Opus 编码帧,可直接送入解码器 // evt.Codec — 4=Opus Voice, 5=Opus Music decoded := opusDecoder.Decode(evt.Data, pcmBuffer) audioTrack.Write(pcmBuffer[:decoded]) }) ``` **注意事项**: - `OnVoiceData` 回调在事件循环 goroutine 中串行执行,**不要在回调中做耗时操作**(如 Opus 解码),应将数据推入 channel 由独立协程处理 - SDK 不内置 Opus 解码器,需要在应用层(Kotlin/Go)集成 `opus.Decode()` - 语音帧以 20ms 为单位发送,采样率通常为 48kHz --- ## 7. 服务器查询 > **通过指令构建的能力** — 底层封装 TS3 协议命令 `serverinfo`。 ### 服务器信息 | 方法 | 触发形式 | 协议命令 | 用法 | 作用 | |------|----------|----------|------|------| | `GetServerInfo` | 客户端请求 | `serverinfo` | `GetServerInfo() (*ServerInfo, error)` | 获取服务器完整信息 | ### ServerInfo 结构 | 字段 | 类型 | 描述 | |------|------|------| | `Name` | `string` | 服务器名称 | | `WelcomeMessage` | `string` | 欢迎消息 | | `MaxClients` | `int` | 最大客户端数 | | `ClientsOnline` | `int` | 在线客户端数 | | `ChannelsOnline` | `int` | 在线频道数 | | `Uptime` | `int64` | 服务器运行时长(秒) | | `Version` | `string` | 服务器版本 | | `Platform` | `string` | 服务器平台 | | `Created` | `int64` | 创建时间(unix 时间戳) | | `IconID` | `int64` | 服务器图标 ID | | `DefaultServerGroup` | `int` | 默认服务器组 ID | | `DefaultChannelGroup` | `int` | 默认频道组 ID | ### Bridge 层 JSON 接口 | 方法 | 返回 | 描述 | |------|------|------| | `GetServerInfoJSON()` | `string` | 服务器信息(JSON 对象,空 `"{}"` 表示未连接或出错) | --- ## 8. Ban 管理 > **通过指令构建的能力** — 底层封装 TS3 协议命令 `banlist`、`banadd`、`bandel`、`bandelall`。 ### Ban 操作 | 方法 | 触发形式 | 协议命令 | 用法 | 作用 | |------|----------|----------|------|------| | `ListBans` | 客户端请求 | `banlist` | `ListBans() ([]BanEntry, error)` | 获取所有封禁记录 | | `AddBan` | 客户端请求 | `banadd ip=X name=X uid=X time=X banreason=X` | `AddBan(ip, name, uid string, timeSeconds int, reason string) error` | 添加封禁(ip/name/uid 至少指定一个,time=0 为永久) | | `DeleteBan` | 客户端请求 | `bandel banid=X` | `DeleteBan(banID int64) error` | 解除指定封禁 | | `DeleteAllBans` | 客户端请求 | `bandelall` | `DeleteAllBans() error` | 清除所有封禁 | ### BanEntry 结构 | 字段 | 类型 | 描述 | |------|------|------| | `BanID` | `int64` | 封禁 ID | | `IP` | `string` | IP(可能为空或部分掩码) | | `Name` | `string` | 名称模式 | | `UID` | `string` | 唯一标识 | | `Created` | `int64` | 封禁时间(unix 时间戳) | | `InvokerName` | `string` | 操作者昵称 | | `InvokerUID` | `string` | 操作者 UID | | `Reason` | `string` | 封禁原因 | | `Enforcement` | `bool` | 是否立即执行 | ### Bridge 层 JSON 接口 | 方法 | 返回 | 描述 | |------|------|------| | `ListBansJSON()` | `string` | Ban 列表 | | `AddBan(ip, name, uid string, timeSeconds int, reason string)` | `string` | 添加封禁(错误信息或空串) | | `DeleteBan(banIDStr string)` | `string` | 删除封禁(错误信息或空串) | --- ## 9. Token 管理 > **通过指令构建的能力** — 底层封装 TS3 协议命令 `tokenlist`、`tokenuse`。 Token(权限密钥)用于让用户自动获得服务器组/频道组权限。 ### Token 操作 | 方法 | 触发形式 | 协议命令 | 用法 | 作用 | |------|----------|----------|------|------| | `ListTokens` | 客户端请求 | `tokenlist` | `ListTokens() ([]TokenEntry, error)` | 获取所有权限密钥 | | `UseToken` | 客户端请求 | `tokenuse token=X` | `UseToken(token string) error` | 激活权限密钥 | ### TokenEntry 结构 | 字段 | 类型 | 描述 | |------|------|------| | `Token` | `string` | 权限密钥字符串 | | `TokenType` | `int` | 类型:0=服务器组, 1=频道组 | | `TokenID1` | `int64` | 组 ID | | `TokenID2` | `int64` | 频道 ID(仅 token_type=1 时有效) | | `Created` | `int64` | 创建时间(unix 时间戳) | | `Description` | `string` | 描述 | ### Bridge 层 JSON 接口 | 方法 | 返回 | 描述 | |------|------|------| | `ListTokensJSON()` | `string` | Token 列表 | | `UseToken(token string)` | `string` | 使用 Token(错误信息或空串) | --- ## 10. 投诉管理 > **通过指令构建的能力** — 底层封装 TS3 协议命令 `complainlist`、`complainadd`、`complaindel`。 ### 投诉操作 | 方法 | 触发形式 | 协议命令 | 用法 | 作用 | |------|----------|----------|------|------| | `ListComplaints` | 客户端请求 | `complainlist [tcldbid=X]` | `ListComplaints(targetDBID uint64) ([]ComplaintEntry, error)` | 查询投诉(targetDBID=0 查全部) | | `AddComplaint` | 客户端请求 | `complainadd tcldbid=X message=X` | `AddComplaint(targetDBID uint64, message string) error` | 提交投诉 | | `DeleteComplaint` | 客户端请求 | `complaindel tcldbid=X fcldbid=X` | `DeleteComplaint(targetDBID, fromDBID uint64) error` | 删除投诉 | ### ComplaintEntry 结构 | 字段 | 类型 | 描述 | |------|------|------| | `FromDBID` | `uint64` | 投诉者 DBID | | `ToDBID` | `uint64` | 被投诉者 DBID | | `Message` | `string` | 投诉内容 | | `Timestamp` | `int64` | 投诉时间(unix 时间戳) | ### Bridge 层 JSON 接口 | 方法 | 返回 | 描述 | |------|------|------| | `ListComplaintsJSON(targetDBIDStr string)` | `string` | 投诉列表(`"0"` 表示全部) | | `AddComplaint(targetDBIDStr, message string)` | `string` | 提交投诉(错误信息或空串) | --- ## 11. 文件传输 | 方法 | 触发形式 | 用法 | 作用 | 描述 | |------|----------|------|------|------| | `FileTransferInitUpload` | 客户端请求 | `FileTransferInitUpload(channelID uint64, path string, password string, size uint64, overwrite bool) (*FileUploadInfo, error)` | 初始化上传 | 请求上传文件到频道文件目录。对应协议 `ftinitupload` | | `FileTransferInitDownload` | 客户端请求 | `FileTransferInitDownload(channelID uint64, path string, password string) (*FileDownloadInfo, error)` | 初始化下载 | 请求下载频道文件。对应协议 `ftinitdownload` | | `FileTransferDeleteFile` | 客户端请求 | `FileTransferDeleteFile(channelID uint64, paths []string) error` | 删除文件 | 删除频道中的文件。对应协议 `ftdeletefile` | ### 文件列表查询 > **通过指令构建的能力** — 底层封装 TS3 协议命令 `ftgetfilelist`。 | 方法 | 触发形式 | 协议命令 | 用法 | 作用 | |------|----------|----------|------|------| | `ListFiles` | 客户端请求 | `ftgetfilelist cid=X path=X` | `ListFiles(channelID uint64, path string) ([]FileEntry, error)` | 列出频道目录下的文件和子目录(path 为虚拟路径,根目录为 `/`) | ### FileEntry 结构 | 字段 | 类型 | 描述 | |------|------|------| | `Name` | `string` | 文件/目录名 | | `Size` | `uint64` | 文件大小(字节,目录为 0) | | `DateTime` | `int64` | 修改时间(unix 时间戳) | | `IsFile` | `bool` | 是否为文件(false = 目录) | ### Bridge 层 JSON 接口 | 方法 | 返回 | 描述 | |------|------|------| | `ListFilesJSON(channelIDStr, path string)` | `string` | 频道文件列表 | ### 辅助函数 | 函数 | 触发形式 | 用法 | 作用 | 描述 | |------|----------|------|------|------| | `DialFileTransfer` | 客户端请求 | `DialFileTransfer(host string, port uint16, key string) (net.Conn, error)` | 建立文件传输连接 | 建立到文件传输端口的 TCP 连接 | | `UploadFileData` | 客户端请求 | `UploadFileData(host string, info *FileUploadInfo, data io.Reader) error` | 上传数据 | 通过已初始化的连接上传文件数据 | | `DownloadFileData` | 客户端请求 | `DownloadFileData(host string, info *FileDownloadInfo, dest io.Writer) error` | 下载数据 | 通过已初始化的连接下载文件数据 | --- ## 12. 中间件 | 类型 | 触发形式 | 定义 | 作用 | 描述 | |------|----------|------|------|------| | `CommandMiddleware` | 本地调用 | `func(next func(string) error) func(string) error` | 命令中间件 | 拦截/修改即将发送的命令字符串 | | `EventMiddleware` | 本地调用 | `func(next func(any)) func(any)` | 事件中间件 | 拦截/修改即将分发的事件 | --- ## 13. 解析器接口 | 类型 | 触发形式 | 定义 | 作用 | 描述 | |------|----------|------|------|------| | `AddrResolver` | 本地调用 | `Resolve(ctx context.Context, addr string) ([]discovery.ResolvedAddr, error)` | 地址解析器 | 自定义 DNS/TSDNS 服务器地址解析 | --- ## 14. 协议转义 | 函数 | 触发形式 | 用法 | 作用 | 描述 | |------|----------|------|------|------| | `commands.Escape` | 本地调用 | `commands.Escape(s string) string` | 转义 | 将字符串转义为 TS3 协议安全格式 | | `commands.Unescape` | 本地调用 | `commands.Unescape(s string) string` | 反转义 | 将 TS3 协议转义字符串还原 | | `commands.BuildCommand` | 本地调用 | `commands.BuildCommand(cmd string, params map[string]string) string` | 构建命令 | 从命令名和参数 map 构建协议命令字符串 | | `commands.BuildCommandOrdered` | 本地调用 | `commands.BuildCommandOrdered(cmd string, params [][2]string) string` | 构建命令(有序) | 同上,但保持参数顺序 | --- ## 15. 服务器通知(内部处理) 以下通知由 SDK 内部解析并转换为事件,开发者通过 `On*` 方法注册处理器即可,无需直接处理。 | 通知 ID | 触发形式 | 事件类型 | 描述 | |---------|----------|----------|-------------------------| | `notifycliententerview` | 服务端推送 | `ClientInfo` | 客户端进入服务器(事件数据结构频道id未生效) | | `notifyclientleftview` | 服务端推送 | `ClientLeftViewEvent` | 客户端离开视野(含踢出) | | `notifyclientmoved` | 服务端推送 | `ClientMovedEvent` | 客户端频道移动 | | `notifytextmessage` | 服务端推送 | `TextMessage` | 收到文本消息 | | `notifyclientpoke` | 服务端推送 | `PokeEvent` | 收到 Poke | | `notifyclientneededpermissions` | 服务端推送 | — | 仅 Debug 日志,无事件 | | `notifystartupload` | 服务端推送 | `FileUploadInfo` | 文件上传开始 | | `notifystartdownload` | 服务端推送 | `FileDownloadInfo` | 文件下载开始 | | `notifystatusfiletransfer` | 服务端推送 | `FileTransferStatusInfo` | 文件传输状态变更 | --- ## 16. 未实现的 TS3 协议命令 以下命令在 teamspeak-go 中尚未封装,如需支持可在 `api.go` 中按现有模式扩展(通过 `ExecCommand` / `ExecCommandWithResponse` 发送协议命令)。 ### 用户管理 | 命令 | 触发形式 | 用法 | 作用 | 描述 | |------|----------|------|------|------| | `clientdbedit` | 客户端请求 | `clientdbedit cldbid=X ...` | 编辑数据库用户 | 修改数据库用户属性(描述、昵称等) | | `clientsetservergroup` | 客户端请求 | `clientsetservergroup cldbid=X sgid=X` | 设置服务器组 | 将用户添加到指定服务器组 | | `clientgetdbidfromuid` | 客户端请求 | `clientgetdbidfromuid cluid=X` | UID→DBID | 从唯一标识获取数据库 ID | | `clientgetnamefromuid` | 客户端请求 | `clientgetnamefromuid cluid=X` | UID→昵称 | 从唯一标识获取昵称 | | `clientgetnamefromdbid` | 客户端请求 | `clientgetnamefromdbid cldbid=X` | DBID→昵称 | 从数据库 ID 获取昵称 | | `clientgetids` | 客户端请求 | `clientgetids cluid=X` | UID→clid | 从唯一标识获取在线客户端 ID | ### 服务器组管理 | 命令 | 触发形式 | 用法 | 作用 | 描述 | |------|----------|------|------|------| | `servergroupadd` | 客户端请求 | `servergroupadd name=X` | 创建服务器组 | 创建新的服务器组 | | `servergroupdel` | 客户端请求 | `servergroupdel sgid=X force=1` | 删除服务器组 | 删除指定服务器组 | | `servergroupaddclient` | 客户端请求 | `servergroupaddclient sgid=X cldbid=X` | 添加用户到组 | 将数据库用户添加到服务器组 | | `servergroupdelclient` | 客户端请求 | `servergroupdelclient sgid=X cldbid=X` | 从组移除用户 | 将用户从服务器组移除 | | `servergrouplist` | 客户端请求 | `servergrouplist` | 服务器组列表 | 获取所有服务器组 | ### 服务器管理 | 命令 | 触发形式 | 用法 | 作用 | 描述 | |------|----------|------|------|------| | `serveredit` | 客户端请求 | `serveredit virtualserver_name=X ...` | 编辑服务器 | 修改服务器属性 | | `serverprocessstop` | 客户端请求 | `serverprocessstop reasonmsg=X` | 关闭服务器 | 停止服务器进程 | ### 权限查询 | 命令 | 触发形式 | 用法 | 作用 | 描述 | |------|----------|------|------|------| | `permoverview` | 客户端请求 | `permoverview cid=X cldbid=X` | 权限概览 | 获取用户在指定频道的权限概览 | | `permget` | 客户端请求 | `permget permid=X` | 获取权限 | 获取指定权限的当前值 | | `permfind` | 客户端请求 | `permfind permid=X` | 查找权限 | 查找拥有指定权限的所有对象 | ### 其他 | 命令 | 触发形式 | 用法 | 作用 | 描述 | |------|----------|------|------|------| | `sendpluginmessage` | 客户端请求 | `sendpluginmessage target=X msg=X` | 插件消息 | 向指定目标发送插件消息 | | `tokenadd` | 客户端请求 | `tokenadd tokentype=X ...` | 创建 Token | 创建新的权限 Token | | `tokendelete` | 客户端请求 | `tokendelete token=X` | 删除 Token | 删除指定 Token | | `banclient` | 客户端请求 | `banclient clid=X time=X banreason=X` | 踢+Ban | 踢出并封禁指定在线客户端 | --- ## 17. Bridge 层 Kotlin 友好 API(kotlin_api.go) `go/teamspeak/kotlin_api.go` 是对 `bridge.go` 的补充封装,暴露 SDK 中已有但 bridge.go 未导出的能力,并提供 Identity 管理接口。 所有方法遵循 bridge.go 的 gomobile 导出约定: - 返回 `string`:空字符串=成功,非空=错误信息 - 返回 JSON `string`:查询结果以 JSON 编码 ### 17.1 Identity 管理 | 方法 | 触发形式 | 用法 | 作用 | 描述 | |------|----------|------|------|------| | `GenerateIdentity` | 本地调用 | `GenerateIdentity(securityLevel int) string` | 生成身份 | 生成加密身份并序列化为字符串。`securityLevel` 推荐值 8。Kotlin 侧应持久化返回值,后续通过 `ConnectWithIdentity` 复用 | | `ConnectWithIdentity` | 客户端请求 | `ConnectWithIdentity(identityStr, host, nickname, password, defaultChannel, defaultChannelPassword string, cb EventCallback) string` | 用已有身份连接 | 使用已持久化的 identity 字符串连接。行为与 `Connect` 相同 | **Identity 序列化格式**: `"base64EncodedPrivateKey:offset"`(由 SDK 的 `Identity.String()` 生成) **典型用法**: ```kotlin // 首次使用:生成并持久化 val identity = GenerateIdentity(8) preferences.edit().putString("ts_identity", identity).apply() // 后续使用:从持久化读取 val identity = preferences.getString("ts_identity", "") ?: "" val error = client.connectWithIdentity(identity, host, nickname, password, "", "", callback) ``` ### 17.2 查询能力(补充) 以下方法补充 bridge.go 中未暴露的 SDK 查询能力。 | 方法 | 返回 | 描述 | |------|------|------| | `FindChannelsJSON(pattern string) string` | JSON 数组 | 按名称搜索频道。返回 `[{"id":"1","name":"匹配的频道"}]` | | `FindClientByNameJSON(nickname string) string` | JSON 对象 | 按昵称搜索数据库客户端。返回 `{"uid":"xxx","dbid":"123"}` | | `FindClientByDBIDJSON(dbidStr string) string` | JSON 对象 | 按 DBID 查找客户端 UID。返回 `{"uid":"xxx"}` | ### 17.3 操作能力(补充) 以下方法补充 bridge.go 中未暴露的 SDK 操作能力。 | 方法 | 返回 | 描述 | |------|------|------| | `MoveClient(clientID int, channelIDStr, password string) string` | 错误信息 | 将指定客户端移动到目标频道 | | `MoveChannel(channelIDStr, parentIDStr, orderStr string) string` | 错误信息 | 移动频道到新父频道或调整排序 | | `DeleteAllBans() string` | 错误信息 | 清除所有封禁记录 | | `DeleteComplaint(targetDBIDStr, fromDBIDStr string) string` | 错误信息 | 删除指定投诉 | ### 17.4 文件传输(初始化) 文件传输遵循三阶段流程:初始化 → TCP 连接 → 数据传输。以下方法封装初始化阶段。 | 方法 | 返回 | 描述 | |------|------|------| | `FileTransferInitUploadJSON(channelIDStr, path string, size int64, overwrite bool) string` | JSON 对象 | 初始化上传。返回 `{"port":0,"key":"...","clientFileTransferID":0,"serverFileTransferID":0,"seekPosition":0}` | | `FileTransferInitDownloadJSON(channelIDStr, path string) string` | JSON 对象 | 初始化下载。返回 `{"port":0,"key":"...","size":0,"clientFileTransferID":0,"serverFileTransferID":0}` | | `DeleteFile(channelIDStr, pathsJSON string) string` | 错误信息 | 删除频道文件。`pathsJSON` 为 JSON 数组如 `["/file1.txt","/file2.txt"]` | **注意**:TCP 连接的 host 为当前服务器地址,port 从初始化返回的 JSON 中获取。完整的文件传输流程(TCP 连接 + 数据传输)需要额外封装。 ### 17.5 批量查询(首次同步优化) | 方法 | 返回 | 描述 | |------|------|------| | `GetInitialSyncJSON() string` | JSON 对象 | 一次性返回首次同步所需的全部数据,减少 JNI 调用次数 | 返回格式: ```json { "channels": [ {"id":"1","name":"Lobby","parentId":"0","topic":"","order":"0","codec":4,"codecQuality":7,...} ], "clients": [ {"id":1,"nickname":"User","uid":"xxx","channelId":"1","serverGroups":[],"isSelf":true} ], "selfId": 1, "selfChannelId": "1", "server": { "name":"My Server","welcomeMessage":"","maxClients":100,"clientsOnline":5,"channelsOnline":3,... } } ``` 任一子查询失败时对应字段为 null/空数组,不影响其他字段。 ### 17.6 Bridge 层方法完整索引 #### bridge.go 已有方法 | 分类 | 方法 | 返回 | |------|------|------| | 连接 | `Connect(host, nickname, password, defaultChannel, defaultChannelPassword, cb)` | 错误信息 | | 连接 | `Disconnect()` | — | | 连接 | `IsConnected()` | bool | | 连接 | `GetClientID()` | int | | 连接 | `GetChannelID()` | string | | 查询 | `GetChannelsJSON()` | JSON 数组 | | 查询 | `GetChannelsDetailedJSON()` | JSON 数组 | | 查询 | `GetClientsJSON()` | JSON 数组 | | 查询 | `GetServerInfoJSON()` | JSON 对象 | | 查询 | `GetChannelDetailInfoJSON(channelIDStr)` | JSON 对象 | | 查询 | `GetClientDetailInfoJSON(clid)` | JSON 对象 | | 查询 | `ListDBClientsJSON(start, duration)` | JSON 数组 | | 查询 | `ListBansJSON()` | JSON 数组 | | 查询 | `ListTokensJSON()` | JSON 数组 | | 查询 | `ListFilesJSON(channelIDStr, path)` | JSON 数组 | | 查询 | `ListComplaintsJSON(targetDBIDStr)` | JSON 数组 | | 操作 | `SendChannelMessage(channelIDStr, message)` | 错误信息 | | 操作 | `SendTextMessage(targetMode, targetIDStr, message)` | 错误信息 | | 操作 | `MoveToChannel(channelIDStr, password)` | 错误信息 | | 操作 | `Poke(clidStr, message)` | 错误信息 | | 操作 | `SendVoice(data, codec)` | 错误信息 | | 操作 | `CreateChannelJSON(name, propertiesJSON)` | 新频道 ID | | 操作 | `EditChannelJSON(channelIDStr, propertiesJSON)` | 错误信息 | | 操作 | `DeleteChannel(channelIDStr, force)` | 错误信息 | | 操作 | `UpdateSelfJSON(propertiesJSON)` | 错误信息 | | 操作 | `KickClient(clid, reasonID, reasonMsg)` | 错误信息 | | 操作 | `AddBan(ip, name, uid, timeSeconds, reason)` | 错误信息 | | 操作 | `DeleteBan(banIDStr)` | 错误信息 | | 操作 | `UseToken(token)` | 错误信息 | | 操作 | `AddComplaint(targetDBIDStr, message)` | 错误信息 | #### kotlin_api.go 新增方法 | 分类 | 方法 | 返回 | |------|------|------| | Identity | `GenerateIdentity(securityLevel)` | 序列化的 identity 字符串 | | Identity | `ConnectWithIdentity(identityStr, host, nickname, password, defaultChannel, defaultChannelPassword, cb)` | 错误信息 | | 查询 | `FindChannelsJSON(pattern)` | JSON 数组 | | 查询 | `FindClientByNameJSON(nickname)` | JSON 对象 | | 查询 | `FindClientByDBIDJSON(dbidStr)` | JSON 对象 | | 操作 | `MoveClient(clientID, channelIDStr, password)` | 错误信息 | | 操作 | `MoveChannel(channelIDStr, parentIDStr, orderStr)` | 错误信息 | | 操作 | `DeleteAllBans()` | 错误信息 | | 操作 | `DeleteComplaint(targetDBIDStr, fromDBIDStr)` | 错误信息 | | 文件 | `FileTransferInitUploadJSON(channelIDStr, path, size, overwrite)` | JSON 对象 | | 文件 | `FileTransferInitDownloadJSON(channelIDStr, path)` | JSON 对象 | | 文件 | `DeleteFile(channelIDStr, pathsJSON)` | 错误信息 | | 批量 | `GetInitialSyncJSON()` | JSON 对象 |