github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/chat/transcripts.go (about) 1 package chat 2 3 import ( 4 "github.com/keybase/client/go/chat/types" 5 "github.com/keybase/client/go/kbun" 6 "github.com/keybase/client/go/libkb" 7 "github.com/keybase/client/go/protocol/chat1" 8 "github.com/keybase/client/go/protocol/gregor1" 9 ) 10 11 type ConvTranscript struct { 12 Messages []ConvTranscriptMsg `json:"messages"` 13 } 14 15 type ConvTranscriptMsg struct { 16 SenderUsername string `json:"senderUsername"` 17 Body chat1.MessageBody `json:"body"` 18 Ctime gregor1.Time `json:"ctime_ms"` 19 } 20 21 type PullTranscriptConfig struct { 22 // How many messages to pull with given filters (so list of usernames, or 23 // all users). 24 messageCount int 25 26 batchSize int // how many to pull in one batch 27 batchCount int // how many batches to try 28 } 29 30 func PullTranscriptConfigDefault() PullTranscriptConfig { 31 return PullTranscriptConfig{ 32 messageCount: 100, 33 batchSize: 100, 34 batchCount: 5, 35 } 36 } 37 38 func PullTranscript(mctx libkb.MetaContext, convSource types.ConversationSource, convID chat1.ConvIDStr, 39 usernames []kbun.NormalizedUsername, config PullTranscriptConfig) (res ConvTranscript, err error) { 40 41 convIDBytes, err := chat1.MakeConvID(convID.String()) 42 if err != nil { 43 return res, err 44 } 45 46 mctx.Debug("Pulling transcript for convID=%s, usernames=%v", convID, usernames) 47 usernameMap := make(map[string]struct{}, len(usernames)) 48 if len(usernames) != 0 { 49 for _, v := range usernames { 50 usernameMap[v.String()] = struct{}{} 51 } 52 } else { 53 mctx.Debug("usernames array is empty, pulling messages for ALL usernames") 54 } 55 56 uidBytes := gregor1.UID(mctx.CurrentUID().ToBytes()) 57 chatQuery := &chat1.GetThreadQuery{ 58 MarkAsRead: false, 59 MessageTypes: chat1.VisibleChatMessageTypes(), 60 } 61 62 var next []byte 63 64 outerLoop: 65 for i := 0; i < config.batchCount; i++ { 66 pagination := &chat1.Pagination{ 67 Num: config.batchSize, 68 Next: next, 69 } 70 mctx.Debug("Pulling from ConvSource: i=%d, Pagination=%#v", i, pagination) 71 threadView, err := convSource.Pull(mctx.Ctx(), convIDBytes, uidBytes, 72 chat1.GetThreadReason_GENERAL, nil, chatQuery, pagination) 73 if err != nil { 74 return ConvTranscript{}, err 75 } 76 mctx.Debug("Got %d messages to search through", len(threadView.Messages)) 77 for _, msg := range threadView.Messages { 78 if !msg.IsValid() { 79 continue 80 } 81 mv := msg.Valid() 82 // Filter by usernames 83 if len(usernames) != 0 { 84 if _, ok := usernameMap[mv.SenderUsername]; !ok { 85 // Skip this message 86 continue 87 } 88 } 89 tMsg := ConvTranscriptMsg{ 90 SenderUsername: mv.SenderUsername, 91 Body: mv.MessageBody, 92 Ctime: mv.ServerHeader.Ctime, 93 } 94 res.Messages = append(res.Messages, tMsg) 95 if len(res.Messages) >= config.messageCount { 96 mctx.Debug("Got all messages we wanted (%d) at i=%d", config.messageCount, i) 97 break outerLoop 98 } 99 } 100 101 if threadView.Pagination == nil { 102 mctx.Debug("i=%d got no Pagination struct", i) 103 break 104 } 105 if threadView.Pagination.Last { 106 mctx.Debug("i=%d was the last page", i) 107 break 108 } 109 next = threadView.Pagination.Next 110 } 111 112 return res, nil 113 }