Go SDK 會話
會話是兩個使用者之間(單聊)或一個群中(群聊)的訊息流。本頁介紹 Go SDK 中會話的拉取與本地讀取、未讀數、已讀回執,以及置頂、草稿、標記未讀、刪除和清空。會話裡的訊息見訊息,事件訂閱與 Store 的運作見事件與錯誤處理。
| 要做什麼 | 看哪裡 |
|---|---|
| 顯示會話列表 | 用 Store 讀會話列表、拉取最近會話 |
| 未讀數、標記未讀、全部已讀 | 未讀數 |
| 顯示“已讀”、上報群已讀回執 | 已讀回執 |
| 置頂、草稿 | 置頂與草稿 |
| 刪除會話、清空聊天記錄 | 刪除與清空 |
| 從聯絡人進入單聊 | 單聊會話的定位 |
下文的 c 指初始化、登入與連線後得到的客戶端:
import im "github.com/DeepRespond-AI/deeprespond-im-go-sdk"
c, _ := im.New(im.Config{
AppKey: "1575529652#demo", // 组织#应用
APIURL: "https://im.example.com", // IM 服务端 REST 地址
})
_, _ = c.Login(ctx, im.LoginPassword, "alice", "pass-123")會話從哪裡來
你不需要“建立”會話。給一個使用者發出第一條訊息、在群裡發出第一條訊息時,服務端自動建立會話;對方先發來訊息也一樣。之後所有會話操作都針對服務端分配的會話 ID 進行。會話狀態由服務端維護,SDK 透過登入後同步和長連線事件把它保持在記憶體 Store 中,你不需要自己合併。
會話的標識
一個會話在 SDK 中是 ConversationState,單聊和群聊用 ConversationType 區分:
- 單聊(
single):ConversationID是服務端生成的 ID,對端使用者名稱在Peer中。PeerReadSeq是對方已讀到的位置。 - 群聊(
group):ConversationID就是群 ID,同時出現在GroupID欄位中。本人是否還在群裡看Membership(member、left、removed、dismissed)。
群會話 ID 即群 ID,所以從群聊頁進入會話時直接用 im.ToGroup(groupID) 發訊息即可;單聊用 im.ToUser(peer),見訊息。
方法一覽
會話方法都在 Client 上,全部走 REST,第一個引數是 context.Context,失敗時返回 *im.APIError:
| 方法 | 用途 | 關鍵引數 |
|---|---|---|
RecentConversations | 拉取最近會話,遊標翻頁 | cursor:首頁傳 "",之後傳上頁的 NextCursor;limit:每頁條數,≤0 時按 100 |
Conversation | 獲取單個會話狀態 | id:會話 ID;結果同時寫入本地 Store |
SingleConversation | 取與某使用者的單聊會話 | peer:對方使用者名稱;兩人還沒有會話時返回 not_found |
DeleteConversation | 刪除會話 | id;服務端隱藏會話,本地快取清空 |
ClearConversation | 清空會話歷史 | id;服務端推進 ClearedSeq |
SetConversationPinned | 置頂 / 取消置頂 | id;pinned |
MarkConversationUnread | 標記 / 取消“未讀” | id;marked |
SetConversationDraft | 儲存會話草稿 | id;draft 草稿文字,空串清除 |
ReadAllConversations | 全部會話標記已讀 | 無 |
UnreadCount | 全部會話的未讀總數 | 無 |
GroupReceipts | 上報群聊已讀回執 | conversationID;seqs:已讀到的訊息序號 |
ReactionUsers | 訊息某種回應的名單 | conversationID、seq、key:表情標識 |
標記某個會話已讀的方法 Read 在訊息頁介紹。
拉取最近會話
RecentConversations 返回 Page[ConversationState]:Items 為這一頁的會話,NextCursor 非空表示還有下一頁:
cursor := ""
for {
page, err := c.RecentConversations(ctx, cursor, 100)
if err != nil {
log.Fatal(err)
}
for _, st := range page.Items {
fmt.Printf("%s %s 未读 %d\n", st.ConversationID, st.ConversationType, st.UnreadCount)
}
if page.NextCursor == "" {
break
}
cursor = page.NextCursor
}用 Store 讀會話列表
服務端拉取之外,更常用的做法是直接讀記憶體 Store:長連線開啟後,Store 隨事件自動維護(新訊息、已讀、置頂、其他裝置上的刪除都會更新),直接重建介面即可:
store := c.Store()
for _, st := range store.Conversations() { // 按最近消息时间倒序
title := st.GroupID
if st.ConversationType == "single" {
title = st.Peer
}
// 未读数为 0 但被标记为未读时,显示一个小圆点
dot := st.UnreadCount == 0 && st.MarkedUnread
// 有 @ 本人还没读到的消息时,可以显示“[有人@我]”
mentioned := st.MentionSeq > 0
fmt.Println(title, st.UnreadCount, dot, mentioned)
}- Store 是程序內的記憶體快取:程序重啟後為空,先呼叫
SyncAll(或等 SDK 的同步流程)填充,見事件與錯誤處理的 Store 小節。 store.Conversation(id)讀取單個會話,沒有時返回nil;REST 的Conversation、SingleConversation等取到的狀態也可以用store.PutConversation寫進去。store.Typing(conversationID)返回 10 秒內的“正在輸入”,見訊息。- 介面重新整理的時機由事件驅動(
message.received、message.conversation_changed等),在OnEvent回撥中重新讀取 Store 即可。
未讀數
- 單個會話的未讀數在
ConversationState.UnreadCount。Store 收到別人發來的訊息時自動累加;本人發的訊息和exclude_from_unread的訊息不計入。 - 全部會話的未讀總數:
total, err := c.UnreadCount(ctx)- 使用者想稍後再處理某個會話時,把它標記為未讀,不影響已讀位置:
_ = c.MarkConversationUnread(ctx, convID, true)
_ = c.MarkConversationUnread(ctx, convID, false)- 一鍵全部已讀:
_ = c.ReadAllConversations(ctx)使用者正在看某個會話時,應呼叫 Read 標記已讀,見訊息的“已讀與正在輸入”。
已讀回執
- 單聊:不需要單獨查詢。
ConversationState.PeerReadSeq是對方已讀到的序號,本人發出的、Seq不大於它的訊息都可以顯示“已讀”。對方標記已讀時服務端推送message.peer_read事件,Store 中的會話狀態隨之更新。 - 群聊:群成員讀到要求回執的訊息後,用
GroupReceipts上報已讀的序號(單聊沒有這個介面)。傳送方透過message.read_by事件得知誰讀到了哪些訊息,事件載荷見事件與錯誤處理。
// 用户把群会话看到第 120 条时
_ = c.GroupReceipts(ctx, groupConvID, []int64{120})置頂與草稿
// 置顶、取消置顶
_ = c.SetConversationPinned(ctx, convID, true)
_ = c.SetConversationPinned(ctx, convID, false)
// 离开会话时保存草稿,发出后清除
_ = c.SetConversationDraft(ctx, convID, "明天上午十点")
_ = c.SetConversationDraft(ctx, convID, "")- 置頂後
ConversationState.PinnedAt為置頂時間,會話列表可以把置頂的排前面。 - 草稿是一段任意文字,由服務端透傳儲存,在本人登入的裝置間共享。Go SDK 的
ConversationState不包含草稿欄位,需要恢復草稿時請在應用側自行記錄草稿與會話的對應關係。
刪除與清空
// 从会话列表中删除:服务端隐藏会话,Store 中的本地缓存一并清空
_ = c.DeleteConversation(ctx, convID)
// 只清空聊天记录,会话仍在列表中:服务端推进 ClearedSeq,
// Store 中不大于它的消息随后被自动清除
_ = c.ClearConversation(ctx, convID)- 兩者都隻影響本人,對方和群裡的其他成員不受影響。
- 刪除後的會話在對方再發來訊息時會隨事件重新同步回來。
- 清空後本人看不到此前的訊息;
StartSeq(可見範圍的起點)隨之變大。
回應名單
某條訊息上回應了某個表情的全部使用者,用 ReactionUsers 翻頁獲取(新增、取消回應的方法 AddReaction / RemoveReaction 見訊息):
users, err := c.ReactionUsers(ctx, convID, 120, "thumbs_up")
if err != nil {
log.Fatal(err)
}
for _, u := range users.Items { // 用户名列表
fmt.Println(u)
}
if users.NextCursor != "" {
// 下一页
}單聊會話的定位
從聯絡人頁等入口進入聊天時,用對方使用者名稱取單聊會話:
st, err := c.SingleConversation(ctx, "bob")
if im.IsCode(err, im.CodeNotFound) {
// 两人还没有会话:直接发第一条消息即可,服务端会自动建立
}會話已存在時返回它的狀態;Conversation(ctx, id) 則按會話 ID 精確獲取,取到後同時寫入 Store。
資料結構
ConversationState
本人在一個會話中的狀態,REST 與 Store 讀到的是同一結構。
| 欄位 | 型別 | 說明 |
|---|---|---|
ConversationID | string | 會話 ID;群會話的 ID 就是群 ID |
ConversationType | string | single 或 group |
Peer | string | 單聊的對方使用者名稱,群聊為空 |
GroupID | string | 群聊的群 ID,單聊為空 |
Membership | *string | 群聊中本人的身份:member、left、removed、dismissed;單聊為 nil |
StartSeq | int64 | 本人能看到的第一條訊息的序號(入群、清空都會讓它變大) |
MaxSeq | int64 | 本人能看到的最後一條訊息的序號 |
ReadSeq | int64 | 本人的已讀位置 |
UnreadCount | int64 | 未讀訊息數 |
MarkedUnread | bool | 是否被標記為未讀 |
PeerReadSeq | int64 | 單聊中對方已讀到的序號 |
MentionSeq | int64 | @ 本人或全體、本人還沒讀到的訊息的序號,0 表示沒有 |
ClearedSeq | int64 | 清空聊天記錄的位置,0 表示沒有清空過 |
HiddenSeq | int64 | 刪除會話時的位置,0 表示沒有刪除過 |
Hidden | bool | 會話是否處於刪除(隱藏)狀態 |
PinnedAt | string | 置頂的時間,未置頂為空 |
JoinedAt / LeftAt | string | 群聊中本次入群 / 離開的時間 |
LastMessageAt | string | 最後一條訊息的時間 |
MessageVersion | int64 | 會話中訊息的變更版本號 |
Version | int64 | 狀態版本號,同步與去重用 |
Page[T]
泛型分頁返回,RecentConversations 與 ReactionUsers 都返回它。
| 欄位 | 型別 | 說明 |
|---|---|---|
Items | []T | 這一頁的條目 |
NextCursor | string | 下一頁的遊標,為空表示沒有下一頁 |
Total | int64 | 總數(介面給出時才有) |
