Files
ts-mobile-go/docs/sdk文档-go.md
T
2026-07-20 19:01:03 +08:00

889 lines
42 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 packetIDbig-endian
2 2 clientIDlittle-endian
4 1 codec4=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 友好 APIkotlin_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 对象 |