# 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` 推荐值 8(2048-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` 为要移动的客户端 ID(clid)。`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` | 客户端 ID(clid) | | `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 侧反序列化使用。 ### channelJSON(GetChannelsJSON / FindChannelsJSON) | 字段 | 类型 | 描述 | |------|------|------| | `id` | `string` | 频道 ID | | `name` | `string` | 频道名称 | | `parentId` | `string` | 父频道 ID(GetChannelsJSON 有,FindChannelsJSON 无) | | `description` | `string` | 频道描述(GetChannelsJSON 有,FindChannelsJSON 无) | ### channelDetailedJSON(GetChannelsDetailedJSON) | 字段 | 类型 | 描述 | |------|------|------| | `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` | 修改频道所需权限 | ### channelDetailedInfoJSON(GetChannelDetailInfoJSON) | 字段 | 类型 | 描述 | |------|------|------| | `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 | ### clientJSON(GetClientsJSON) | 字段 | 类型 | 描述 | |------|------|------| | `id` | `int` | 客户端 ID | | `nickname` | `string` | 昵称 | | `uid` | `string` | 唯一标识 | | `channelId` | `string` | 所在频道 ID | | `serverGroups` | `[]string` | 所在服务器组 ID 列表 | | `isSelf` | `bool` | 是否是当前用户 | ### clientDetailedInfoJSON(GetClientDetailInfoJSON) | 字段 | 类型 | 描述 | |------|------|------| | `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 | ### serverInfoJSON(GetServerInfoJSON) | 字段 | 类型 | 描述 | |------|------|------| | `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 | ### dbClientJSON(ListDBClientsJSON) | 字段 | 类型 | 描述 | |------|------|------| | `dbid` | `string` | 数据库 ID | | `uid` | `string` | 唯一标识 | | `nickname` | `string` | 昵称 | | `created` | `string` | 首次连接时间戳 | | `lastConnected` | `string` | 最近连接时间戳 | | `totalConnections` | `int` | 总连接次数 | | `description` | `string` | 描述 | ### banEntryJSON(ListBansJSON) | 字段 | 类型 | 描述 | |------|------|------| | `banId` | `string` | 封禁 ID | | `ip` | `string` | IP | | `name` | `string` | 名称 | | `uid` | `string` | 唯一标识 | | `created` | `string` | 封禁时间戳 | | `invokerName` | `string` | 操作者昵称 | | `invokerUid` | `string` | 操作者 UID | | `reason` | `string` | 封禁原因 | | `enforcement` | `bool` | 是否立即执行 | ### fileEntryJSON(ListFilesJSON) | 字段 | 类型 | 描述 | |------|------|------| | `name` | `string` | 文件/目录名 | | `size` | `string` | 字节数(目录为 "0") | | `dateTime` | `string` | 修改时间戳 | | `isFile` | `bool` | true=文件, false=目录 | ### tokenEntryJSON(ListTokensJSON) | 字段 | 类型 | 描述 | |------|------|------| | `token` | `string` | 权限密钥字符串 | | `tokenType` | `int` | 0=服务器组, 1=频道组 | | `tokenId1` | `string` | 组 ID | | `tokenId2` | `string` | 频道 ID(仅 tokenType=1) | | `created` | `string` | 创建时间戳 | | `description` | `string` | 描述 | ### complaintEntryJSON(ListComplaintsJSON) | 字段 | 类型 | 描述 | |------|------|------| | `fromDbid` | `string` | 投诉者 DBID | | `toDbid` | `string` | 被投诉者 DBID | | `message` | `string` | 投诉内容 | | `timestamp` | `string` | 投诉时间戳 | ### fileTransferInitJSON(FileTransferInitUploadJSON / FileTransferInitDownloadJSON) | 字段 | 类型 | 描述 | |------|------|------| | `port` | `int` | TCP 传输端口 | | `key` | `string` | 传输密钥 | | `size` | `int` | 文件字节数(仅下载) | | `clientFileTransferID` | `int` | 客户端传输 ID | | `serverFileTransferID` | `int` | 服务器传输 ID | | `seekPosition` | `int` | 断点续传位置(仅上传) | ### initialSyncJSON(GetInitialSyncJSON) | 字段 | 类型 | 描述 | |------|------|------| | `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 | |