Go SDK 訊息
本頁介紹 Go SDK 中訊息的傳送與接收、撤回、刪除、表情回應、置頂訊息、已讀與“正在輸入”。會話列表與未讀數見會話,檔案上傳的細節見檔案與圖片,事件與 Store 的運作見事件與錯誤處理。
傳送目標統一用 Target 描述,用 im.ToUser / im.ToGroup 構造,恰選其一:
im.ToUser("bob") // 发给用户 bob
im.ToGroup(groupID) // 发到一个群下文的 c 指初始化、登入與連線後得到的客戶端,ctx 為 context.Context。
| 要做什麼 | 看哪裡 |
|---|---|
| 發文字、自定義訊息 | 傳送文字、傳送自定義訊息 |
| @ 某人、引用回覆 | @ 提及、引用回覆 |
| 發圖片、檔案 | 檔案與圖片 |
| 收訊息、自動回覆 | 接收訊息 |
| 標記已讀、傳送“正在輸入” | 已讀與正在輸入 |
| 撤回、刪除、表情回應、置頂 | 訊息管理 |
| 解析各型別訊息的 body | 訊息型別 |
傳送訊息
| 方法 | 用途 | 關鍵引數 |
|---|---|---|
SendText | 傳送文字訊息 | text 正文;ext 擴充套件欄位 |
Send | 傳送任意型別的訊息 | msgType 型別;body 完整訊息體 JSON;onlineOnly 只推線上 |
SendMedia | 傳送媒體訊息 | msgType 為 image / voice / video / file;info 為上傳返回的 *FileInfo |
SendReply | 引用回覆 | replySeq 被引用訊息的 seq |
SendWithMentions | 傳送帶 @ 提醒的訊息 | usernames 被 @ 的人;all @ 全體;兩者恰選其一 |
全部返回 (*im.Message, error)。型別與訊息體結構見下文訊息型別。
傳送文字
m, err := c.SendText(ctx, im.ToUser("bob"), "你好", nil)
if err != nil {
log.Fatal(err)
}
fmt.Println(m.ConversationID, m.Seq)ext是你自定義的擴充套件欄位(map[string]any),原樣儲存、原樣返回,適合放業務 ID 之類的資料;不需要時傳nil。- 沒有會話也能發:給一個使用者發出第一條訊息時,服務端自動建立單聊會話。
傳送自定義訊息
Send 的 body 是完整的訊息體 JSON([]byte),按 msgType 解釋。SendText 就等價於以 "text" 型別和 {"text": ...} 訊息體呼叫它:
body, _ := json.Marshal(map[string]any{"card": "order", "order_id": "8812"})
m, err := c.Send(ctx, im.ToGroup(groupID), "custom", body, nil, false)msgType為空串時按text處理;body為nil時傳送空物件。clientMsgID由 SDK 自動生成(隨機十六進位制串),傳送方法不接受自定義值,重複呼叫由服務端按它去重。- 最後一個引數
onlineOnly為true時訊息不落庫、只推給線上裝置(僅custom可用)。
onlineOnly 與長連線
長連線線上時訊息優先經 WS 傳送,此時不攜帶 online_only 標記;只有回退到 REST 傳送時它才會生效。需要確保“只推線上”的實時信令,請確認傳送時長連線可用,或自行改用其他通道。
@ 提及
群訊息可以 @ 成員或全體成員。正文中的“@張三”由你自己拼進訊息體,SDK 只提交 mentions(usernames 與 all 恰選其一):
// @ 指定成员
m, err := c.SendWithMentions(ctx, im.ToGroup(groupID), "text",
[]byte(`{"text":"@李四 请确认"}`),
[]string{"lisi"}, false, nil)
// @ 全体成员
c.SendWithMentions(ctx, im.ToGroup(groupID), "text",
[]byte(`{"text":"@所有人 明天放假"}`),
nil, true, nil)被 @ 的人在會話狀態的 MentionSeq 中看到“有人 @ 我”,見會話。
引用回覆
// 回复该群第 120 条消息
m, err := c.SendReply(ctx, im.ToGroup(groupID), 120, "text", []byte(`{"text":"同意"}`), nil)發出後訊息的 ReplyTo 為 {seq, sender};接收方按 seq 在會話中定位被引用的訊息,不在本地時用 MessageLookup 取回,見下文。
傳送路徑與本地兜底
- 長連線線上時優先走 WS(
message.send),延遲低;連線剛好斷開(unavailable、timeout)時自動回退 REST,業務錯誤則直接返回。 - 傳送成功返回服務端的訊息(帶
Seq、MessageID、CreatedAt),並寫入 Store。 - 服務端對傳送者本人不回顯
message.received,新會話也不一定向傳送者推會話事件;SDK 在傳送成功後於本地兜底建立/推進會話狀態,保證你自己發出的訊息出現在會話列表裡、你自己的已讀回執有處落地。因此不要在收到message.received時才把訊息加進介面——傳送方法的返回值就是這條訊息。
接收訊息
別人發來的訊息透過 message.received 事件推送。事件先應用到 Store、再回撥你的處理函式:
c.OnEvent(func(e im.Event) {
if e.Name != im.EventMessageReceived {
return
}
var m im.Message
if err := json.Unmarshal(e.Data, &m); err != nil {
return
}
if m.Type == "text" {
fmt.Println(m.Sender, m.TextBody())
}
})- 通常你不需要自己維護訊息列表:回撥裡直接讀 Store(
c.Store().Messages(conversationID),seq 升序),事件到達後重建介面即可。 - 亂序到達時 SDK 先收下、再非同步補齊缺口;
cleared_seq變大時自動清除本地不超過它的訊息。規則詳見事件與錯誤處理的 Store 小節。 - 撤回、編輯、表情回應、置頂變化透過
message.changed實時更新到 Store 中的訊息上。
訊息管理
| 方法 | 用途 | 關鍵引數 |
|---|---|---|
Recall | 撤回訊息 | conversationID、seq;時限內、有許可權才成功 |
DeleteMessages | 刪除訊息(本人視角) | seqs:要刪除的序號列表 |
AddReaction / RemoveReaction | 新增 / 取消表情回應 | seq、key:表情標識 |
PinMessage / UnpinMessage | 置頂 / 取消置頂訊息 | conversationID、seq |
MessageLookup | 按 seq 批次取訊息 | seqs:一次最多 100 個 |
Read | 標記會話已讀 | seq:讀到哪條,0 表示推進到最新 |
Typing | 傳送“正在輸入” | conversationID |
已讀與正在輸入
// 用户正在看这个会话:已读位置推进到最新一条
_ = c.Read(ctx, convID, 0)
// 只标记到某一条(如用户只看到了第 120 条)
_ = c.Read(ctx, convID, 120)
// 输入框每次变化时报告;长连接未连上时静默忽略
c.Typing(ctx, convID)Read同樣 WS 優先、REST 回退,成功後同步推進 Store 中該會話的ReadSeq與UnreadCount。- 單聊中對方的已讀位置在
ConversationState.PeerReadSeq,由message.peer_read事件實時更新,見會話的“已讀回執”。 - 收到的“正在輸入”經
message.typing事件進入 Store,c.Store().Typing(convID)返回 10 秒內最近的一次,超時自動失效。
表情回應
convID := m.ConversationID
seq := m.Seq
_ = c.AddReaction(ctx, convID, seq, "thumbs_up")
_ = c.RemoveReaction(ctx, convID, seq, "thumbs_up")- 每種表情的聚合(人數、前 3 個回應者、本人是否回應過)在訊息的
Reactions中,別人加減回應時經message.changed實時更新:
for _, r := range m.Reactions {
fmt.Println(r.Key, r.Count, r.Users, r.Reacted)
}- 回應某種表情的全部人用
ReactionUsers翻頁獲取,見會話的“回應名單”。 - 表情的
key由客戶端定義(如thumbs_up、heart),各端保持一致。
撤回、刪除、置頂與查詢
_ = c.Recall(ctx, convID, 120) // 撤回
_ = c.DeleteMessages(ctx, convID, []int64{101, 102}) // 只删本人视角
_ = c.PinMessage(ctx, convID, 120) // 置顶,UnpinMessage 取消
msgs, _ := c.MessageLookup(ctx, convID, []int64{3, 57}) // 按 seq 取回- 撤回後雙方的訊息都變為已撤回:
Recalled為{by, role, at},正文與擴充套件欄位被清空,經message.changed更新。 DeleteMessages隻影響本人,其他裝置由message.deleted事件同步刪除。- 引用、置頂、通知裡提到的訊息不在本地時,用
MessageLookup從服務端取回,結果同時寫入 Store。
訊息型別
Message.Body(json.RawMessage)按 Type 解釋:
| 型別 | body 結構 | 讀取方式 |
|---|---|---|
text | {"text": "..."} | m.TextBody(),非文字訊息返回空串 |
image | 上傳返回的 FileInfo JSON:url、thumbnail_url、width、height 等 | json.Unmarshal(m.Body, &info) 到 im.FileInfo |
voice | FileInfo,另有 duration_ms | 同上 |
video | FileInfo | 同上 |
file | FileInfo | 同上 |
custom / tip | 自定義 JSON 物件 | m.BodyMap(),解析失敗返回 nil |
圖片、語音、影片、檔案訊息的 url 不能直接請求,要先換取下載直鏈或用 Download,見檔案與圖片。
資料結構
Message
| 欄位 | 型別 | 說明 |
|---|---|---|
ConversationID | string | 會話 ID |
ConversationType | string | single 或 group |
Seq | int64 | 在會話中的序號 |
MessageID | string | 全應用唯一的訊息 ID |
ClientMsgID | string | 傳送方生成的去重 ID |
Sender | string | 傳送者使用者名稱 |
SenderType | string | user 或 system |
Recipient | string | 單聊的接收者 |
Type | string | 訊息型別,見上表 |
Body | json.RawMessage | 訊息體,按型別解釋 |
Ext | map[string]any | 傳送時的擴充套件欄位 |
Mentions | *Mentions | @ 的人:Usernames、All |
ReplyTo | *ReplyTo | 引用的訊息:Seq、Sender |
Reactions | []Reaction | 表情回應聚合:Key、Count、Users、Reacted |
Pinned | *Pinned | 置頂資訊:By、At |
Edited | *Edited | 編輯資訊:At、Count |
Recalled | *Recalled | 撤回資訊:By、Role、At |
Erased | bool | 內容是否已被擦除 |
Via | string | 寫入方式:client、openapi、console、system |
CreatedAt | string | 服務端的傳送時間(RFC3339) |
MessageVersion | int64 | 變更版本號,判斷新舊用 |
輔助方法:TextBody() 提取文字正文;BodyMap() 把 body 解析為通用物件。錯誤物件 *im.APIError 與 im.IsCode 的用法見事件與錯誤處理。
