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

799 lines
40 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.
# Bridge 层 API 文档
> 本文档是 `go/teamspeak/bridge.go` + `go/teamspeak/kotlin_api.go` 的完整 API 参考。
> 两个文件共同构成 gomobile 导出的 `teamspeak` 包,供 Kotlin 侧通过 `TSBridge` 调用。
>
> **gomobile 导出约定**
> - 返回 `string`:空字符串 `""` = 成功,非空 = 错误信息
> - 返回 JSON `string`:查询结果以 JSON 编码,`"[]"` / `"{}"` 表示空或错误
> - 不支持 `[]string`、`[]*T`、`error` 等 Go 类型,复杂数据一律通过 JSON 传递
> - 回调通过 `EventCallback` 接口定义,所有 JNI 回调在同一线程顺序执行
---
## 一、客户端构造
### NewClient
| 名字 | 触发形式 | 用法 | 作用 | 详细描述 |
|------|----------|------|------|----------|
| `NewClient` | 本地调用 | `NewClient() *TSClient` | 创建客户端实例 | 创建一个空的 TSClient 实例。不建立网络连接,不生成 Identity。需要随后调用 `Connect``ConnectWithIdentity` 发起连接。 |
---
## 二、Identity 管理
### GenerateIdentity
| 名字 | 触发形式 | 用法 | 作用 | 详细描述 |
|------|----------|------|------|----------|
| `GenerateIdentity` | 本地调用 | `GenerateIdentity(securityLevel int) string` | 生成加密身份 | 生成 TeamSpeak 加密身份(ECDSA P-256 密钥对),返回序列化字符串 `"base64PrivateKey:offset"``securityLevel` 推荐值 82048-bit RSA challenge),值越大生成越慢。Kotlin 侧应将返回值持久化到 SharedPreferences / DataStore,后续通过 `ConnectWithIdentity` 复用。返回空字符串表示生成失败。 |
### ConnectWithIdentity
| 名字 | 触发形式 | 用法 | 作用 | 详细描述 |
|------|----------|------|------|----------|
| `ConnectWithIdentity` | 客户端请求 | `(c *TSClient) ConnectWithIdentity(identityStr, host, nickname, password, defaultChannel, defaultChannelPassword string, cb EventCallback) string` | 用已有身份连接 | 使用 `GenerateIdentity` 生成并持久化的 identity 字符串连接服务器。与 `Connect` 行为相同(注册事件 → 发起 UDP 连接 → 等待握手完成,30 秒超时),但复用已有身份而非每次生成新的。`password``defaultChannel``defaultChannelPassword` 为空时忽略。返回空字符串表示连接成功。 |
---
## 三、连接管理
### Connect
| 名字 | 触发形式 | 用法 | 作用 | 详细描述 |
|------|----------|------|------|----------|
| `Connect` | 客户端请求 | `(c *TSClient) Connect(host, nickname, password, defaultChannel, defaultChannelPassword string, cb EventCallback) string` | 连接服务器 | 每次生成新 Identity,注册全部事件回调,发起 UDP 连接并等待握手完成(30 秒超时)。`host` 支持域名/IP/TSDNS,可带端口号(默认 9987)。`password` 为服务器密码(可选)。`defaultChannel` 为连接后自动加入的频道名(可选)。`defaultChannelPassword` 为默认频道密码(可选)。返回空字符串表示连接成功。 |
### Disconnect
| 名字 | 触发形式 | 用法 | 作用 | 详细描述 |
|------|----------|------|------|----------|
| `Disconnect` | 客户端请求 | `(c *TSClient) Disconnect()` | 断开连接 | 停止事件消费协程,发送 disconnect 命令通知服务器,清理客户端状态。断开后触发 `OnDisconnected` 回调。可安全重复调用。 |
### IsConnected
| 名字 | 触发形式 | 用法 | 作用 | 详细描述 |
|------|----------|------|------|----------|
| `IsConnected` | 本地调用 | `(c *TSClient) IsConnected() bool` | 查询连接状态 | 返回当前是否已连接且客户端实例有效。线程安全。 |
### GetClientID
| 名字 | 触发形式 | 用法 | 作用 | 详细描述 |
|------|----------|------|------|----------|
| `GetClientID` | 本地调用 | `(c *TSClient) GetClientID() int` | 获取自身客户端 ID | 返回服务器分配的当前客户端 ID(clid)。未连接时返回 0。ID 由 SDK 本地缓存,不产生网络请求。 |
### GetChannelID
| 名字 | 触发形式 | 用法 | 作用 | 详细描述 |
|------|----------|------|------|----------|
| `GetChannelID` | 客户端请求 | `(c *TSClient) GetChannelID() string` | 获取自身所在频道 ID | 通过 `clientinfo clid=self` 查询当前客户端所在频道 ID。返回频道 ID 字符串,未连接或查询失败返回 `"0"`。 |
---
## 四、频道查询
### GetChannelsJSON
| 名字 | 触发形式 | 用法 | 作用 | 详细描述 |
|------|----------|------|------|----------|
| `GetChannelsJSON` | 客户端请求 | `(c *TSClient) GetChannelsJSON() string` | 获取基础频道列表 | 调用 `channellist` 协议命令,返回 JSON 数组。每个元素包含 `id``name``parentId``description` 四个字段。ID 和 ParentID 均为字符串格式的 uint64。未连接返回 `"[]"`。 |
**返回示例**
```json
[{"id":"1","name":"Lobby","parentId":"0","description":""}]
```
### GetChannelsDetailedJSON
| 名字 | 触发形式 | 用法 | 作用 | 详细描述 |
|------|----------|------|------|----------|
| `GetChannelsDetailedJSON` | 客户端请求 | `(c *TSClient) GetChannelsDetailedJSON() string` | 获取详细频道列表 | 调用 `channellist -topic -flags -voice -limits -icon` 协议命令,返回 JSON 数组。比 `GetChannelsJSON` 多出 14 个字段:`topic``order``codec``codecQuality``neededTalkPower``maxClients``maxFamilyClients``isMaxClientsUnlimited``isMaxFamilyClientsUnlimited``isPermanent``isSemiPermanent``isDefault``isPassword``isOrdered``iconId``neededModifyPower`。用于频道列表页 UI 渲染(密码图标🔒、永久标记📌、人数限制👥等)。未连接返回 `"[]"`。 |
**返回示例**
```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
}]
```
### ListChannelsSortedJSON
| 名字 | 触发形式 | 用法 | 作用 | 详细描述 |
|------|----------|------|------|----------|
| `ListChannelsSortedJSON` | 客户端请求 | `(c *TSClient) ListChannelsSortedJSON() string` | 获取去重+排序的详细频道列表 | 底层同样调用 `channellist -topic -flags -voice -limits -icon`,但额外处理:①按 ID 去重(保留最后出现的条目);②按 `channel_order` 排序,先顶层频道再子频道(树形扁平化:顶层频道按 order 排序,每个频道的子频道紧跟其后并按 order 排序)。返回 JSON 数组,字段与 `GetChannelsDetailedJSON` 完全一致。适合频道列表页直接展示,无需客户端侧再做排序和去重。未连接返回 `"[]"`。 |
### GetChannelDetailInfoJSON
| 名字 | 触发形式 | 用法 | 作用 | 详细描述 |
|------|----------|------|------|----------|
| `GetChannelDetailInfoJSON` | 客户端请求 | `(c *TSClient) GetChannelDetailInfoJSON(channelIDStr string) string` | 获取单频道详情 | 调用 `channelinfo cid=X` 协议命令,返回 JSON 对象。与 `GetChannelsDetailedJSON` 的区别:本方法返回单个频道的完整信息(含 `description` 完整描述和 `bannerGfxUrl`),适合频道详情面板展示。`channelIDStr` 为频道 ID 字符串。未连接或无效 ID 返回 `"{}"`。 |
**返回示例**
```json
{
"id": "1", "parentId": "0", "name": "Lobby", "topic": "欢迎",
"description": "这是频道的完整描述...", "codec": 4, "codecQuality": 7,
"maxClients": -1, "maxFamilyClients": -1, "neededTalkPower": 0,
"iconId": "0", "isPermanent": true, "isSemiPermanent": false,
"isDefault": true, "isPassword": false, "order": "0", "bannerGfxUrl": ""
}
```
### FindChannelsJSON
| 名字 | 触发形式 | 用法 | 作用 | 详细描述 |
|------|----------|------|------|----------|
| `FindChannelsJSON` | 客户端请求 | `(c *TSClient) FindChannelsJSON(pattern string) string` | 按名称搜索频道 | 调用 `channelfind pattern=X` 协议命令,返回 JSON 数组。每个元素包含 `id``name``pattern` 支持通配符。用于频道搜索/快速跳转功能。未连接或无匹配返回 `"[]"`。 |
**返回示例**
```json
[{"id":"5","name":"Gaming Room"},{"id":"12","name":"Gaming Lounge"}]
```
---
## 五、客户端查询
### GetClientsJSON
| 名字 | 触发形式 | 用法 | 作用 | 详细描述 |
|------|----------|------|------|----------|
| `GetClientsJSON` | 客户端请求 | `(c *TSClient) GetClientsJSON() string` | 获取在线客户端列表 | 调用 `clientlist -uid -away -voice -groups` 协议命令,返回 JSON 数组。每个元素包含 `id`clid)、`nickname``uid``channelId``serverGroups`(字符串数组)、`isSelf`(是否是当前用户)。自动比对 `ClientID()` 标记 `isSelf` 字段。未连接返回 `"[]"`。 |
**返回示例**
```json
[
{"id":1,"nickname":"Admin","uid":"abc123","channelId":"1","serverGroups":["6","9"],"isSelf":true},
{"id":2,"nickname":"User","uid":"def456","channelId":"2","serverGroups":[],"isSelf":false}
]
```
### GetClientDetailInfoJSON
| 名字 | 触发形式 | 用法 | 作用 | 详细描述 |
|------|----------|------|------|----------|
| `GetClientDetailInfoJSON` | 客户端请求 | `(c *TSClient) GetClientDetailInfoJSON(clid int) string` | 获取单客户端详情 | 调用 `clientinfo clid=X` 协议命令,返回 JSON 对象。比 `GetClientsJSON` 的列表项多出 `away``awayMessage``inputMuted``outputMuted``platform``version``ip``created``lastConnected``totalConnections``description``iconId` 等字段。用于点击客户端后的详情弹窗。`clid` 为客户端 ID。未连接返回 `"{}"`。 |
**返回示例**
```json
{
"id": "1", "nickname": "Admin", "uid": "abc123", "channelId": "1",
"type": 0, "serverGroups": ["6","9"],
"away": false, "awayMessage": "", "inputMuted": false, "outputMuted": false,
"platform": "Windows", "version": "3.x.x", "ip": "192.168.1.1",
"created": "1700000000", "lastConnected": "1710000000", "totalConnections": 42,
"description": "", "iconId": "0"
}
```
### FindClientByNameJSON
| 名字 | 触发形式 | 用法 | 作用 | 详细描述 |
|------|----------|------|------|----------|
| `FindClientByNameJSON` | 客户端请求 | `(c *TSClient) FindClientByNameJSON(nickname string) string` | 按昵称搜索数据库客户端 | 调用 `clientdbfind pattern=X -uid` 协议命令,返回 JSON 对象 `{"uid":"xxx","dbid":"123"}`。搜索服务器数据库中注册过的客户端(不要求在线)。未连接或无匹配返回 `"{}"`。 |
### FindClientByDBIDJSON
| 名字 | 触发形式 | 用法 | 作用 | 详细描述 |
|------|----------|------|------|----------|
| `FindClientByDBIDJSON` | 客户端请求 | `(c *TSClient) FindClientByDBIDJSON(dbidStr string) string` | 按 DBID 查找客户端 UID | 调用 `clientdbfind -uid cldbid=X` 协议命令,返回 JSON 对象 `{"uid":"xxx"}`。通过数据库 ID 反查唯一标识。`dbidStr` 为数据库 ID 字符串。未连接或无匹配返回 `"{}"`。 |
### ListDBClientsJSON
| 名字 | 触发形式 | 用法 | 作用 | 详细描述 |
|------|----------|------|------|----------|
| `ListDBClientsJSON` | 客户端请求 | `(c *TSClient) ListDBClientsJSON(start, duration int) string` | 获取数据库客户端列表 | 调用 `clientdblist start=X duration=X` 协议命令,返回 JSON 数组。每个元素包含 `dbid``uid``nickname``created``lastConnected``totalConnections``description``start` 为起始位置,`duration` 为返回数量(0=全部)。用于管理功能中的用户数据库浏览。未连接返回 `"[]"`。 |
---
## 六、服务器信息
### GetServerInfoJSON
| 名字 | 触发形式 | 用法 | 作用 | 详细描述 |
|------|----------|------|------|----------|
| `GetServerInfoJSON` | 客户端请求 | `(c *TSClient) GetServerInfoJSON() string` | 获取服务器信息 | 调用 `serverinfo` 协议命令,返回 JSON 对象。包含 `name`(服务器名)、`welcomeMessage``maxClients``clientsOnline``channelsOnline``uptime`(运行秒数)、`version``platform``created`(创建时间戳)、`iconId``defaultServerGroup``defaultChannelGroup`。用于服务器详情展示。未连接返回 `"{}"`。 |
**返回示例**
```json
{
"name": "My TeamSpeak Server", "welcomeMessage": "Welcome!",
"maxClients": 100, "clientsOnline": 5, "channelsOnline": 3,
"uptime": "86400", "version": "3.13.7", "platform": "Linux",
"created": "1700000000", "iconId": "0",
"defaultServerGroup": 8, "defaultChannelGroup": 1
}
```
---
## 七、文本消息与 Poke
### SendTextMessage
| 名字 | 触发形式 | 用法 | 作用 | 详细描述 |
|------|----------|------|------|----------|
| `SendTextMessage` | 客户端请求 | `(c *TSClient) SendTextMessage(targetMode int, targetIDStr string, message string) string` | 发送文字消息 | 发送文本消息到指定目标。`targetMode`: 1=私聊(target 为客户端 ID)、2=频道消息(target 为频道 ID)、3=服务器消息(target 忽略)。`targetIDStr` 为目标 ID 字符串。`message` 为消息内容。返回空字符串表示发送成功。 |
### SendChannelMessage
| 名字 | 触发形式 | 用法 | 作用 | 详细描述 |
|------|----------|------|------|----------|
| `SendChannelMessage` | 客户端请求 | `(c *TSClient) SendChannelMessage(channelIDStr, message string) string` | 发送频道消息 | `SendTextMessage(targetMode=2, ...)` 的快捷方式。`channelIDStr` 为频道 ID 字符串。返回空字符串表示发送成功。 |
### Poke
| 名字 | 触发形式 | 用法 | 作用 | 详细描述 |
|------|----------|------|------|----------|
| `Poke` | 客户端请求 | `(c *TSClient) Poke(clidStr string, message string) string` | 发送 Poke | 向指定客户端发送 Poke(戳一戳)消息。`clidStr` 为目标客户端 ID 字符串。`message` 为 Poke 文本。对方会收到弹窗通知。返回空字符串表示发送成功。 |
---
## 八、频道切换与客户端移动
### MoveToChannel
| 名字 | 触发形式 | 用法 | 作用 | 详细描述 |
|------|----------|------|------|----------|
| `MoveToChannel` | 客户端请求 | `(c *TSClient) MoveToChannel(channelIDStr, password string) string` | 移动自己到目标频道 | 将当前客户端移动到指定频道。`channelIDStr` 为目标频道 ID 字符串。`password` 为频道密码(无密码传空字符串)。调用 `ClientMove(selfID, channelID, password)`。返回空字符串表示命令已发送,实际移动需等待 `OnClientMoved` 事件确认。 |
### MoveClient
| 名字 | 触发形式 | 用法 | 作用 | 详细描述 |
|------|----------|------|------|----------|
| `MoveClient` | 客户端请求 | `(c *TSClient) MoveClient(clientID int, channelIDStr, password string) string` | 移动指定客户端到目标频道 | 将任意客户端移动到指定频道(需要权限)。`clientID` 为要移动的客户端 IDclid)。`channelIDStr` 为目标频道 ID 字符串。`password` 为频道密码(可选)。返回空字符串表示命令已发送。 |
### MoveChannel
| 名字 | 触发形式 | 用法 | 作用 | 详细描述 |
|------|----------|------|------|----------|
| `MoveChannel` | 客户端请求 | `(c *TSClient) MoveChannel(channelIDStr, parentIDStr, orderStr string) string` | 移动频道位置 | 移动频道到新的父频道下或调整排序。`channelIDStr` 为要移动的频道 ID。`parentIDStr` 为新的父频道 ID`"0"` = 顶层)。`orderStr` 为排序位置(`"0"` = 最顶部)。返回空字符串表示成功。 |
---
## 九、频道管理
### CreateChannelJSON
| 名字 | 触发形式 | 用法 | 作用 | 详细描述 |
|------|----------|------|------|----------|
| `CreateChannelJSON` | 客户端请求 | `(c *TSClient) CreateChannelJSON(name, propertiesJSON string) string` | 创建频道 | 调用 `channelcreate channel_name=X [params...]``name` 为频道名。`propertiesJSON` 为 JSON 对象,包含可选属性如 `{"channel_topic":"主题","channel_flag_permanent":"1","cpid":"父频道ID","channel_password":"密码","channel_maxclients":"10","channel_codec":"4"}`。返回新频道 ID 字符串,空字符串表示失败。 |
### EditChannelJSON
| 名字 | 触发形式 | 用法 | 作用 | 详细描述 |
|------|----------|------|------|----------|
| `EditChannelJSON` | 客户端请求 | `(c *TSClient) EditChannelJSON(channelIDStr, propertiesJSON string) string` | 编辑频道属性 | 调用 `channeledit cid=X [params...]``channelIDStr` 为频道 ID 字符串。`propertiesJSON` 为 JSON 对象,包含要修改的属性。返回空字符串表示成功。 |
### DeleteChannel
| 名字 | 触发形式 | 用法 | 作用 | 详细描述 |
|------|----------|------|------|----------|
| `DeleteChannel` | 客户端请求 | `(c *TSClient) DeleteChannel(channelIDStr string, force bool) string` | 删除频道 | 调用 `channeldelete cid=X force=X``channelIDStr` 为频道 ID 字符串。`force=true` 强制删除(含子频道),`false` 仅在频道为空时删除。返回空字符串表示成功。 |
---
## 十、客户端管理
### UpdateSelfJSON
| 名字 | 触发形式 | 用法 | 作用 | 详细描述 |
|------|----------|------|------|----------|
| `UpdateSelfJSON` | 客户端请求 | `(c *TSClient) UpdateSelfJSON(propertiesJSON string) string` | 更新自身属性 | 调用 `clientupdate [params...]``propertiesJSON` 为 JSON 对象。常用参数:`client_nickname`(新昵称)、`client_away``"1"`/`"0"` 离开状态)、`client_away_message`(离开消息)、`client_input_muted``"1"`/`"0"` 输入静音)、`client_output_muted``"1"`/`"0"` 输出静音)、`client_phonetic_nickname`(语音昵称)。返回空字符串表示成功。 |
**用法示例**
```
UpdateSelfJSON('{"client_nickname":"NewName","client_away":"1","client_away_message":"AFK"}')
```
### KickClient
| 名字 | 触发形式 | 用法 | 作用 | 详细描述 |
|------|----------|------|------|----------|
| `KickClient` | 客户端请求 | `(c *TSClient) KickClient(clid int, reasonID int, reasonMsg string) string` | 踢出客户端 | 调用 `clientkick clid=X reasonid=X reasonmsg=X``clid` 为客户端 ID。`reasonID`: 4=从频道踢出,5=从服务器踢出。`reasonMsg` 为踢出原因文本。返回空字符串表示成功。 |
---
## 十一、语音
### SendVoice
| 名字 | 触发形式 | 用法 | 作用 | 详细描述 |
|------|----------|------|------|----------|
| `SendVoice` | 客户端请求 | `(c *TSClient) SendVoice(data []byte, codec int64) string` | 发送语音帧 | 发送原始 Opus 编码的语音帧到当前频道。`data` 为 Opus 编码的音频数据(20ms 帧)。`codec`: 4=Opus Voice(语音通话,默认),5=Opus Music(高保真音频)。通过 UDP 传输。返回空字符串表示发送成功。 |
---
## 十二、Ban 管理
### ListBansJSON
| 名字 | 触发形式 | 用法 | 作用 | 详细描述 |
|------|----------|------|------|----------|
| `ListBansJSON` | 客户端请求 | `(c *TSClient) ListBansJSON() string` | 获取封禁列表 | 调用 `banlist` 协议命令,返回 JSON 数组。每个元素包含 `banId``ip``name``uid``created`(时间戳)、`invokerName`(操作者)、`invokerUid``reason``enforcement`(是否立即执行)。未连接返回 `"[]"`。 |
### AddBan
| 名字 | 触发形式 | 用法 | 作用 | 详细描述 |
|------|----------|------|------|----------|
| `AddBan` | 客户端请求 | `(c *TSClient) AddBan(ip, name, uid string, timeSeconds int, reason string) string` | 添加封禁 | 调用 `banadd ip=X name=X uid=X time=X banreason=X``ip`/`name`/`uid` 至少指定一个,可组合使用。`timeSeconds` 为封禁时长(秒),0=永久。`reason` 为封禁原因(可选)。返回空字符串表示成功。 |
### DeleteBan
| 名字 | 触发形式 | 用法 | 作用 | 详细描述 |
|------|----------|------|------|----------|
| `DeleteBan` | 客户端请求 | `(c *TSClient) DeleteBan(banIDStr string) string` | 删除封禁 | 调用 `bandel banid=X``banIDStr` 为封禁 ID 字符串(从 `ListBansJSON` 获取)。返回空字符串表示成功。 |
### DeleteAllBans
| 名字 | 触发形式 | 用法 | 作用 | 详细描述 |
|------|----------|------|------|----------|
| `DeleteAllBans` | 客户端请求 | `(c *TSClient) DeleteAllBans() string` | 清除所有封禁 | 调用 `bandelall` 协议命令,删除服务器上的全部封禁记录。返回空字符串表示成功。 |
---
## 十三、Token 管理
### ListTokensJSON
| 名字 | 触发形式 | 用法 | 作用 | 详细描述 |
|------|----------|------|------|----------|
| `ListTokensJSON` | 客户端请求 | `(c *TSClient) ListTokensJSON() string` | 获取权限密钥列表 | 调用 `tokenlist` 协议命令,返回 JSON 数组。每个元素包含 `token`(密钥字符串)、`tokenType`0=服务器组, 1=频道组)、`tokenId1`(组 ID)、`tokenId2`(频道 ID,仅 tokenType=1)、`created`(创建时间戳)、`description`。未连接返回 `"[]"`。 |
### UseToken
| 名字 | 触发形式 | 用法 | 作用 | 详细描述 |
|------|----------|------|------|----------|
| `UseToken` | 客户端请求 | `(c *TSClient) UseToken(token string) string` | 激活权限密钥 | 调用 `tokenuse token=X``token` 为权限密钥字符串。激活后自动获得对应的服务器组或频道组权限。返回空字符串表示成功。 |
---
## 十四、投诉管理
### ListComplaintsJSON
| 名字 | 触发形式 | 用法 | 作用 | 详细描述 |
|------|----------|------|------|----------|
| `ListComplaintsJSON` | 客户端请求 | `(c *TSClient) ListComplaintsJSON(targetDBIDStr string) string` | 获取投诉列表 | 调用 `complainlist [tcldbid=X]` 协议命令,返回 JSON 数组。每个元素包含 `fromDbid`(投诉者 DBID)、`toDbid`(被投诉者 DBID)、`message`(投诉内容)、`timestamp`(投诉时间戳)。`targetDBIDStr``"0"` 或空字符串时返回全部投诉。未连接返回 `"[]"`。 |
### AddComplaint
| 名字 | 触发形式 | 用法 | 作用 | 详细描述 |
|------|----------|------|------|----------|
| `AddComplaint` | 客户端请求 | `(c *TSClient) AddComplaint(targetDBIDStr, message string) string` | 提交投诉 | 调用 `complainadd tcldbid=X message=X``targetDBIDStr` 为被投诉者的数据库 ID 字符串。`message` 为投诉内容。返回空字符串表示成功。 |
### DeleteComplaint
| 名字 | 触发形式 | 用法 | 作用 | 详细描述 |
|------|----------|------|------|----------|
| `DeleteComplaint` | 客户端请求 | `(c *TSClient) DeleteComplaint(targetDBIDStr, fromDBIDStr string) string` | 删除投诉 | 调用 `complaindel tcldbid=X fcldbid=X``targetDBIDStr` 为被投诉者 DBID`fromDBIDStr` 为投诉者 DBID。返回空字符串表示成功。 |
---
## 十五、文件传输
### ListFilesJSON
| 名字 | 触发形式 | 用法 | 作用 | 详细描述 |
|------|----------|------|------|----------|
| `ListFilesJSON` | 客户端请求 | `(c *TSClient) ListFilesJSON(channelIDStr, path string) string` | 获取频道文件列表 | 调用 `ftgetfilelist cid=X path=X` 协议命令,返回 JSON 数组。每个元素包含 `name`(文件/目录名)、`size`(字节数,目录为 0)、`dateTime`(修改时间戳)、`isFile`true=文件, false=目录)。`channelIDStr` 为频道 ID 字符串。`path` 为虚拟路径,根目录为 `"/"`。未连接返回 `"[]"`。 |
### FileTransferInitUploadJSON
| 名字 | 触发形式 | 用法 | 作用 | 详细描述 |
|------|----------|------|------|----------|
| `FileTransferInitUploadJSON` | 客户端请求 | `(c *TSClient) FileTransferInitUploadJSON(channelIDStr, path string, size int64, overwrite bool) string` | 初始化文件上传 | 调用 `ftinitupload cid=X path=X size=X overwrite=X`,等待服务器返回传输参数。返回 JSON 对象:`port`TCP 端口)、`key`(传输密钥)、`clientFileTransferID``serverFileTransferID``seekPosition`(断点续传位置)。`channelIDStr` 为频道 ID。`path` 为目标路径。`size` 为文件字节数。`overwrite` 为是否覆盖。TCP 连接 host 为当前服务器地址。未连接返回 `"{}"`。 |
### FileTransferInitDownloadJSON
| 名字 | 触发形式 | 用法 | 作用 | 详细描述 |
|------|----------|------|------|----------|
| `FileTransferInitDownloadJSON` | 客户端请求 | `(c *TSClient) FileTransferInitDownloadJSON(channelIDStr, path string) string` | 初始化文件下载 | 调用 `ftinitdownload cid=X path=X`,等待服务器返回传输参数。返回 JSON 对象:`port`TCP 端口)、`key`(传输密钥)、`size`(文件字节数)、`clientFileTransferID``serverFileTransferID``channelIDStr` 为频道 ID。`path` 为源路径。未连接返回 `"{}"`。 |
### DeleteFile
| 名字 | 触发形式 | 用法 | 作用 | 详细描述 |
|------|----------|------|------|----------|
| `DeleteFile` | 客户端请求 | `(c *TSClient) DeleteFile(channelIDStr, pathsJSON string) string` | 删除频道文件 | 调用 `ftdeletefile cid=X path=X [path2=X ...]``channelIDStr` 为频道 ID 字符串。`pathsJSON` 为要删除的文件路径 JSON 数组,如 `["/file1.txt","/dir/file2.txt"]`。返回空字符串表示成功。 |
---
## 十六、批量查询
### GetInitialSyncJSON
| 名字 | 触发形式 | 用法 | 作用 | 详细描述 |
|------|----------|------|------|----------|
| `GetInitialSyncJSON` | 客户端请求 | `(c *TSClient) GetInitialSyncJSON() string` | 首次同步批量查询 | 一次性返回连接后首次同步所需的全部数据,将 4 次 JNI 调用合并为 1 次。内部依次调用 `GetChannelsDetailedJSON``GetClientsJSON``GetClientID``GetChannelID``GetServerInfoJSON`,合并为单个 JSON 对象返回。任一子查询失败时对应字段为 null/空,不影响其他字段。未连接返回 `"{}"`。 |
**返回格式**
```json
{
"channels": [
{"id":"1","name":"Lobby","parentId":"0","topic":"","order":"0","codec":4,...}
],
"clients": [
{"id":1,"nickname":"Admin","uid":"abc","channelId":"1","serverGroups":[],"isSelf":true}
],
"selfId": 1,
"selfChannelId": "1",
"server": {
"name":"My Server","welcomeMessage":"","maxClients":100,"clientsOnline":5,...
}
}
```
---
## 十七、事件回调接口
### EventCallback
所有事件通过 `EventCallback` 接口回调。Kotlin 侧实现此接口并传入 `Connect``ConnectWithIdentity`。所有回调在同一个 goroutine 中顺序执行(通过内部事件队列保证),无需担心并发问题。
| 回调方法 | 触发时机 | 参数 | 描述 |
|----------|----------|------|------|
| `OnConnected()` | 连接握手完成 | 无 | 服务器握手成功,可以开始发送业务命令。此时应执行首次同步(调用 `GetInitialSyncJSON`)。 |
| `OnDisconnected(message string)` | 连接断开 | `message`: 错误原因 | 网络中断或服务器主动断开。`message` 为空表示正常断开。此时应清理 UI 状态并提示用户。 |
| `OnTextMessage(msg *TextMsg)` | 收到文本消息 | `msg.TargetMode`: 1=私聊/2=频道/3=服务器;`msg.TargetID`: 目标 ID`msg.InvokerName`: 发送者昵称;`msg.InvokerUID`: 发送者 UID`msg.Message`: 消息内容 | 收到其他客户端发送的文本消息。按 TargetMode + Target 归档到正确的会话。 |
| `OnClientEnter(client *Client)` | 用户进入视野 | `client.ID`: 客户端 ID`client.Nickname`: 昵称;`client.UID`: UID`client.ChannelID`: 所在频道 ID | 新客户端进入服务器。应添加到本地成员表。`client.ChannelID` 来自 `notifycliententerview`,可能不准确,需补偿同步。 |
| `OnClientLeave(id int, reasonMsg string)` | 用户离开 | `id`: 客户端 ID`reasonMsg`: 离开原因 | 客户端离开服务器。应从本地成员表移除。 |
| `OnClientMoved(id int, targetChannelID string)` | 用户移动频道 | `id`: 客户端 ID`targetChannelID`: 目标频道 ID | 客户端在频道间移动。当 `id == GetClientID()` 时,表示自己被移动(含密码验证结果)。应更新成员所在频道。 |
| `OnKicked(reason string)` | 被踢出 | `reason`: 踢出原因 | 自己被从频道或服务器踢出。应停止语音采集,清理会话状态,显示被踢原因并允许重连。 |
| `OnVoiceData(clientID int, data []byte, codec int)` | 收到语音 | `clientID`: 发送者 ID`data`: Opus 编码帧;`codec`: 4=Opus Voice/5=Opus Music | 收到同频道其他客户端的语音数据。`data` 可直接送入 Opus 解码器。回调中不要做耗时操作,应推入 channel 由独立协程解码播放。 |
| `OnPoked(event *PokeEvent)` | 被 Poke | `event.InvokerID`: 发送者 ID`event.InvokerName`: 发送者昵称;`event.InvokerUID`: 发送者 UID`event.Message`: Poke 消息 | 收到其他用户的 Poke 消息。应显示 Toast 或弹窗通知。 |
### Kotlin 侧事件处理流程
以下三个事件在 Android 端有完整的 UI 处理链路:
#### 被管理员移动到指定频道(OnClientMoved
```
Go SDK notifyclientmoved
→ EventCallback.OnClientMoved(id, targetChannelID)
→ TSBridge 转发
→ ChannelViewModel.handleClientMoved()
→ 判断 id == selfId → 标记为"被外部移动"
→ 更新 _currentChannelId
→ refreshClientList() 刷新当前频道成员
→ UI 自动更新频道列表和成员列表
```
**日志标识**: `Client moved: {id} -> channel {channelID}``Self moved by external action to channel {channelID}`
#### 收到频道内文本消息(OnTextMessage, targetMode=2
```
Go SDK notifytextmessage
→ EventCallback.OnTextMessage(msg)
→ TSBridge 转发
→ ServerViewModel.handleTextMessage()
→ 解析 senderId(通过 clientlist 匹配 invokerUID
→ 转发给 ChatViewModel
→ ChatViewModel.handleTextMessage()
→ 判断 targetMode=2(频道消息)
→ 归档到对应会话(频道 ID 为 key)
→ 如果匹配当前打开的聊天,更新实时消息列表
```
**日志标识**: `onTextMessage: targetMode=2, targetID={channelID}, invokerName={name}, message={content}`
#### 被 Poke 带文本消息(OnPoked
```
Go SDK notifyclientpoke
→ EventCallback.OnPoked(event)
→ TSBridge 转发
→ ServerViewModel.handlePoked()
→ 更新 _pokeNotification 状态
→ triggerVibration() 震动反馈(需 VIBRATE 权限)
→ 5 秒后自动隐藏通知
→ UI 显示 Poke 弹窗(发送者昵称 + 消息内容)
```
**日志标识**: `Poked by {name}: {message}`
**注意**: Poke 震动需要 `android.permission.VIBRATE` 权限(AndroidManifest.xml 中声明即可,无需运行时请求)。
---
## 十八、导出数据类型
以下类型通过 gomobile 导出到 Kotlin 侧,可直接在回调参数中使用。
### Channel
| 字段 | 类型 | 描述 |
|------|------|------|
| `ID` | `string` | 频道 ID |
| `Name` | `string` | 频道名称 |
| `ParentID` | `string` | 父频道 ID |
| `Description` | `string` | 频道描述 |
### Client
| 字段 | 类型 | 描述 |
|------|------|------|
| `ID` | `int` | 客户端 IDclid |
| `Nickname` | `string` | 昵称 |
| `UID` | `string` | 唯一标识 |
| `ChannelID` | `string` | 所在频道 ID |
| `IsSelf` | `bool` | 是否是当前用户(仅 `GetClientsJSON` 中自动标记) |
### TextMsg
| 字段 | 类型 | 描述 |
|------|------|------|
| `InvokerName` | `string` | 发送者昵称 |
| `InvokerUID` | `string` | 发送者唯一标识 |
| `Message` | `string` | 消息内容 |
| `TargetMode` | `int` | 1=私聊, 2=频道, 3=服务器 |
| `TargetID` | `string` | 目标 ID |
### PokeEvent
| 字段 | 类型 | 描述 |
|------|------|------|
| `InvokerID` | `int` | 发送者客户端 ID |
| `InvokerName` | `string` | 发送者昵称 |
| `InvokerUID` | `string` | 发送者唯一标识 |
| `Message` | `string` | Poke 消息内容 |
---
## 十九、JSON 数据结构参考
以下 JSON 结构由查询方法返回,供 Kotlin 侧反序列化使用。
### channelJSONGetChannelsJSON / FindChannelsJSON
| 字段 | 类型 | 描述 |
|------|------|------|
| `id` | `string` | 频道 ID |
| `name` | `string` | 频道名称 |
| `parentId` | `string` | 父频道 IDGetChannelsJSON 有,FindChannelsJSON 无) |
| `description` | `string` | 频道描述(GetChannelsJSON 有,FindChannelsJSON 无) |
### channelDetailedJSONGetChannelsDetailedJSON
| 字段 | 类型 | 描述 |
|------|------|------|
| `id` | `string` | 频道 ID |
| `name` | `string` | 频道名称 |
| `parentId` | `string` | 父频道 ID |
| `topic` | `string` | 频道主题 |
| `order` | `string` | 排序顺序 |
| `codec` | `int` | 编解码器(4=Opus Voice, 5=Opus Music |
| `codecQuality` | `int` | 编解码质量(0-10 |
| `neededTalkPower` | `int` | 发言所需权限等级 |
| `maxClients` | `int` | 最大客户端数(-1=无限) |
| `maxFamilyClients` | `int` | 最大族客户端数(-1=无限) |
| `isMaxClientsUnlimited` | `bool` | 是否无限人数 |
| `isMaxFamilyClientsUnlimited` | `bool` | 是否无限族人数 |
| `isPermanent` | `bool` | 永久频道 |
| `isSemiPermanent` | `bool` | 半永久频道 |
| `isDefault` | `bool` | 默认频道 |
| `isPassword` | `bool` | 有密码 |
| `isOrdered` | `bool` | 手动排序 |
| `iconId` | `string` | 频道图标 ID |
| `neededModifyPower` | `int` | 修改频道所需权限 |
### channelDetailedInfoJSONGetChannelDetailInfoJSON
| 字段 | 类型 | 描述 |
|------|------|------|
| `id` | `string` | 频道 ID |
| `parentId` | `string` | 父频道 ID |
| `name` | `string` | 频道名称 |
| `topic` | `string` | 频道主题 |
| `description` | `string` | 完整描述(可能很长) |
| `codec` | `int` | 编解码器 |
| `codecQuality` | `int` | 编解码质量 |
| `maxClients` | `int` | 最大人数 |
| `maxFamilyClients` | `int` | 最大族人数 |
| `neededTalkPower` | `int` | 发言权限 |
| `iconId` | `string` | 图标 ID |
| `isPermanent` | `bool` | 永久 |
| `isSemiPermanent` | `bool` | 半永久 |
| `isDefault` | `bool` | 默认 |
| `isPassword` | `bool` | 有密码 |
| `order` | `string` | 排序 |
| `bannerGfxUrl` | `string` | Banner 图片 URL |
### clientJSONGetClientsJSON
| 字段 | 类型 | 描述 |
|------|------|------|
| `id` | `int` | 客户端 ID |
| `nickname` | `string` | 昵称 |
| `uid` | `string` | 唯一标识 |
| `channelId` | `string` | 所在频道 ID |
| `serverGroups` | `[]string` | 所在服务器组 ID 列表 |
| `isSelf` | `bool` | 是否是当前用户 |
### clientDetailedInfoJSONGetClientDetailInfoJSON
| 字段 | 类型 | 描述 |
|------|------|------|
| `id` | `string` | 客户端 ID |
| `nickname` | `string` | 昵称 |
| `uid` | `string` | 唯一标识 |
| `channelId` | `string` | 所在频道 ID |
| `type` | `int` | 客户端类型 |
| `serverGroups` | `[]string` | 服务器组 |
| `away` | `bool` | 是否离开 |
| `awayMessage` | `string` | 离开消息 |
| `inputMuted` | `bool` | 输入静音 |
| `outputMuted` | `bool` | 输出静音 |
| `platform` | `string` | 平台 |
| `version` | `string` | 版本 |
| `ip` | `string` | IP 地址 |
| `created` | `string` | 首次连接时间戳 |
| `lastConnected` | `string` | 最近连接时间戳 |
| `totalConnections` | `int` | 总连接次数 |
| `description` | `string` | 用户描述 |
| `iconId` | `string` | 图标 ID |
### serverInfoJSONGetServerInfoJSON
| 字段 | 类型 | 描述 |
|------|------|------|
| `name` | `string` | 服务器名称 |
| `welcomeMessage` | `string` | 欢迎消息 |
| `maxClients` | `int` | 最大客户端数 |
| `clientsOnline` | `int` | 在线客户端数 |
| `channelsOnline` | `int` | 在线频道数 |
| `uptime` | `string` | 运行时长(秒) |
| `version` | `string` | 服务器版本 |
| `platform` | `string` | 服务器平台 |
| `created` | `string` | 创建时间戳 |
| `iconId` | `string` | 服务器图标 ID |
| `defaultServerGroup` | `int` | 默认服务器组 ID |
| `defaultChannelGroup` | `int` | 默认频道组 ID |
### dbClientJSONListDBClientsJSON
| 字段 | 类型 | 描述 |
|------|------|------|
| `dbid` | `string` | 数据库 ID |
| `uid` | `string` | 唯一标识 |
| `nickname` | `string` | 昵称 |
| `created` | `string` | 首次连接时间戳 |
| `lastConnected` | `string` | 最近连接时间戳 |
| `totalConnections` | `int` | 总连接次数 |
| `description` | `string` | 描述 |
### banEntryJSONListBansJSON
| 字段 | 类型 | 描述 |
|------|------|------|
| `banId` | `string` | 封禁 ID |
| `ip` | `string` | IP |
| `name` | `string` | 名称 |
| `uid` | `string` | 唯一标识 |
| `created` | `string` | 封禁时间戳 |
| `invokerName` | `string` | 操作者昵称 |
| `invokerUid` | `string` | 操作者 UID |
| `reason` | `string` | 封禁原因 |
| `enforcement` | `bool` | 是否立即执行 |
### fileEntryJSONListFilesJSON
| 字段 | 类型 | 描述 |
|------|------|------|
| `name` | `string` | 文件/目录名 |
| `size` | `string` | 字节数(目录为 "0" |
| `dateTime` | `string` | 修改时间戳 |
| `isFile` | `bool` | true=文件, false=目录 |
### tokenEntryJSONListTokensJSON
| 字段 | 类型 | 描述 |
|------|------|------|
| `token` | `string` | 权限密钥字符串 |
| `tokenType` | `int` | 0=服务器组, 1=频道组 |
| `tokenId1` | `string` | 组 ID |
| `tokenId2` | `string` | 频道 ID(仅 tokenType=1 |
| `created` | `string` | 创建时间戳 |
| `description` | `string` | 描述 |
### complaintEntryJSONListComplaintsJSON
| 字段 | 类型 | 描述 |
|------|------|------|
| `fromDbid` | `string` | 投诉者 DBID |
| `toDbid` | `string` | 被投诉者 DBID |
| `message` | `string` | 投诉内容 |
| `timestamp` | `string` | 投诉时间戳 |
### fileTransferInitJSONFileTransferInitUploadJSON / FileTransferInitDownloadJSON
| 字段 | 类型 | 描述 |
|------|------|------|
| `port` | `int` | TCP 传输端口 |
| `key` | `string` | 传输密钥 |
| `size` | `int` | 文件字节数(仅下载) |
| `clientFileTransferID` | `int` | 客户端传输 ID |
| `serverFileTransferID` | `int` | 服务器传输 ID |
| `seekPosition` | `int` | 断点续传位置(仅上传) |
### initialSyncJSONGetInitialSyncJSON
| 字段 | 类型 | 描述 |
|------|------|------|
| `channels` | `[]channelDetailedJSON` | 详细频道列表 |
| `clients` | `[]clientJSON` | 在线客户端列表 |
| `selfId` | `int` | 自身客户端 ID |
| `selfChannelId` | `string` | 自身所在频道 ID |
| `server` | `serverInfoJSON` | 服务器信息 |
---
## 二十、方法来源标记
标记每个方法的实现文件,便于定位代码。
| 方法 | 来源文件 | 说明 |
|------|----------|------|
| `NewClient` | bridge.go | |
| `GenerateIdentity` | kotlin_api.go | |
| `Connect` | bridge.go | 每次生成新 Identity |
| `ConnectWithIdentity` | kotlin_api.go | 复用已有 Identity |
| `Disconnect` | bridge.go | |
| `IsConnected` | bridge.go | |
| `GetClientID` | bridge.go | |
| `GetChannelID` | bridge.go | |
| `GetChannelsJSON` | bridge.go | |
| `GetChannelsDetailedJSON` | bridge.go | |
| `ListChannelsSortedJSON` | kotlin_api.go | 去重+排序 |
| `GetClientsJSON` | bridge.go | |
| `GetServerInfoJSON` | bridge.go | |
| `GetChannelDetailInfoJSON` | bridge.go | |
| `GetClientDetailInfoJSON` | bridge.go | |
| `ListDBClientsJSON` | bridge.go | |
| `FindChannelsJSON` | kotlin_api.go | |
| `FindClientByNameJSON` | kotlin_api.go | |
| `FindClientByDBIDJSON` | kotlin_api.go | |
| `SendTextMessage` | bridge.go | |
| `SendChannelMessage` | bridge.go | 快捷方式 |
| `Poke` | bridge.go | |
| `MoveToChannel` | bridge.go | 移动自己 |
| `MoveClient` | kotlin_api.go | 移动指定客户端 |
| `MoveChannel` | kotlin_api.go | 移动频道位置 |
| `CreateChannelJSON` | bridge.go | |
| `EditChannelJSON` | bridge.go | |
| `DeleteChannel` | bridge.go | |
| `UpdateSelfJSON` | bridge.go | |
| `KickClient` | bridge.go | |
| `SendVoice` | bridge.go | |
| `ListBansJSON` | bridge.go | |
| `AddBan` | bridge.go | |
| `DeleteBan` | bridge.go | |
| `DeleteAllBans` | kotlin_api.go | |
| `ListTokensJSON` | bridge.go | |
| `UseToken` | bridge.go | |
| `ListComplaintsJSON` | bridge.go | |
| `AddComplaint` | bridge.go | |
| `DeleteComplaint` | kotlin_api.go | |
| `ListFilesJSON` | bridge.go | |
| `FileTransferInitUploadJSON` | kotlin_api.go | |
| `FileTransferInitDownloadJSON` | kotlin_api.go | |
| `DeleteFile` | kotlin_api.go | |
| `GetInitialSyncJSON` | kotlin_api.go | |