/
githubmirror
/
client
Обзор
Документация
Войти
/
githubmirror
/
client
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
go/chat/teamchannelsource.go
367 строк
11 KB
zoom-ua
gofumpt (#28710)
17 дек 2025, 17:16
Не верифицирован
17 дек 2025, 17:16
cda510b
Код
Авторство
О чём код?
package chat import ( "context" "fmt" "sort" "sync" "time" "github.com/keybase/client/go/chat/globals" "github.com/keybase/client/go/chat/types" "github.com/keybase/client/go/chat/utils" "github.com/keybase/client/go/libkb" "github.com/keybase/client/go/protocol/chat1" "github.com/keybase/client/go/protocol/gregor1" "github.com/keybase/client/go/protocol/keybase1" ) type recentJoinsCacheItem struct { numJoins int mtime gregor1.Time } type recentJoinsMemCache struct { sync.RWMutex cache map[chat1.ConvIDStr]recentJoinsCacheItem } func newRecentJoinsMemCache() *recentJoinsMemCache { return &recentJoinsMemCache{ cache: make(map[chat1.ConvIDStr]recentJoinsCacheItem), } } func (i *recentJoinsMemCache) Get(convID chat1.ConversationID) int { i.RLock() defer i.RUnlock() if item, ok := i.cache[convID.ConvIDStr()]; ok { if time.Since(item.mtime.Time()) > time.Hour { delete(i.cache, convID.ConvIDStr()) return -1 } return item.numJoins } return -1 } func (i *recentJoinsMemCache) Put(convID chat1.ConversationID, numJoins int) { i.Lock() defer i.Unlock() i.cache[convID.ConvIDStr()] = recentJoinsCacheItem{ numJoins: numJoins, mtime: gregor1.ToTime(time.Now()), } } func (i *recentJoinsMemCache) clearCache() { i.Lock() defer i.Unlock() i.cache = make(map[chat1.ConvIDStr]recentJoinsCacheItem) } func (i *recentJoinsMemCache) OnLogout(mctx libkb.MetaContext) error { i.clearCache() return nil } func (i *recentJoinsMemCache) OnDbNuke(mctx libkb.MetaContext) error { i.clearCache() return nil } type lastActiveAtCacheItem struct { lastActiveAt gregor1.Time mtime gregor1.Time } type lastActiveAtMemCache struct { sync.RWMutex // key: teamID||uid cache map[string]lastActiveAtCacheItem } func newLastActiveAtMemCache() *lastActiveAtMemCache { return &lastActiveAtMemCache{ cache: make(map[string]lastActiveAtCacheItem), } } func (i *lastActiveAtMemCache) key(teamID keybase1.TeamID, uid gregor1.UID) string { return fmt.Sprintf("%s:%s", teamID, uid) } func (i *lastActiveAtMemCache) Get(teamID keybase1.TeamID, uid gregor1.UID) (gregor1.Time, bool) { i.RLock() defer i.RUnlock() if item, ok := i.cache[i.key(teamID, uid)]; ok { if time.Since(item.mtime.Time()) > time.Hour { delete(i.cache, i.key(teamID, uid)) return 0, false } return item.lastActiveAt, true } return 0, false } func (i *lastActiveAtMemCache) Put(teamID keybase1.TeamID, uid gregor1.UID, lastActiveAt gregor1.Time) { i.Lock() defer i.Unlock() i.cache[i.key(teamID, uid)] = lastActiveAtCacheItem{ lastActiveAt: lastActiveAt, mtime: gregor1.ToTime(time.Now()), } } func (i *lastActiveAtMemCache) clearCache() { i.Lock() defer i.Unlock() i.cache = make(map[string]lastActiveAtCacheItem) } func (i *lastActiveAtMemCache) OnLogout(mctx libkb.MetaContext) error { i.clearCache() return nil } func (i *lastActiveAtMemCache) OnDbNuke(mctx libkb.MetaContext) error { i.clearCache() return nil } type TeamChannelSource struct { sync.Mutex globals.Contextified utils.DebugLabeler recentJoinsCache *recentJoinsMemCache lastActiveAtCache *lastActiveAtMemCache } var _ types.TeamChannelSource = (*TeamChannelSource)(nil) func NewTeamChannelSource(g *globals.Context) *TeamChannelSource { return &TeamChannelSource{ Contextified: globals.NewContextified(g), DebugLabeler: utils.NewDebugLabeler(g.ExternalG(), "TeamChannelSource", false), recentJoinsCache: newRecentJoinsMemCache(), lastActiveAtCache: newLastActiveAtMemCache(), } } func (c *TeamChannelSource) OnLogout(mctx libkb.MetaContext) error { epick := libkb.FirstErrorPicker{} epick.Push(c.recentJoinsCache.OnLogout(mctx)) epick.Push(c.lastActiveAtCache.OnLogout(mctx)) return epick.Error() } func (c *TeamChannelSource) OnDbNuke(mctx libkb.MetaContext) error { epick := libkb.FirstErrorPicker{} epick.Push(c.recentJoinsCache.OnDbNuke(mctx)) epick.Push(c.lastActiveAtCache.OnDbNuke(mctx)) return epick.Error() } func (c *TeamChannelSource) getTLFConversations(ctx context.Context, uid gregor1.UID, tlfID chat1.TLFID, topicType chat1.TopicType, ) ([]types.RemoteConversation, error) { inbox, err := c.G().InboxSource.ReadUnverified(ctx, uid, types.InboxSourceDataSourceAll, &chat1.GetInboxQuery{ TlfID: &tlfID, TopicType: &topicType, SummarizeMaxMsgs: false, MemberStatus: chat1.AllConversationMemberStatuses(), Existences: []chat1.ConversationExistence{chat1.ConversationExistence_ACTIVE}, SkipBgLoads: true, }) return inbox.ConvsUnverified, err } func (c *TeamChannelSource) GetLastActiveForTLF(ctx context.Context, uid gregor1.UID, tlfID chat1.TLFID, topicType chat1.TopicType, ) (res gregor1.Time, err error) { defer c.Trace(ctx, &err, "GetLastActiveForTLF: tlfID: %v, topicType: %v", tlfID, topicType)() rcs, err := c.getTLFConversations(ctx, uid, tlfID, topicType) if err != nil { return 0, err } sort.Sort(utils.RemoteConvByMtime(rcs)) if len(rcs) > 0 { return utils.GetConvMtime(rcs[0]), nil } return 0, nil } func (c *TeamChannelSource) GetLastActiveForTeams(ctx context.Context, uid gregor1.UID, topicType chat1.TopicType) ( res chat1.LastActiveTimeAll, err error, ) { ctx = globals.CtxModifyUnboxMode(ctx, types.UnboxModeQuick) defer c.Trace(ctx, &err, "GetLastActiveForTeams: topicType: %v", topicType)() inbox, err := c.G().InboxSource.ReadUnverified(ctx, uid, types.InboxSourceDataSourceAll, &chat1.GetInboxQuery{ TopicType: &topicType, SummarizeMaxMsgs: false, MemberStatus: chat1.AllConversationMemberStatuses(), Existences: []chat1.ConversationExistence{chat1.ConversationExistence_ACTIVE}, MembersTypes: []chat1.ConversationMembersType{chat1.ConversationMembersType_TEAM}, SkipBgLoads: true, }) byTLFID := make(map[chat1.TLFIDStr][]types.RemoteConversation) channels := make(map[chat1.ConvIDStr]gregor1.Time, len(inbox.ConvsUnverified)) for _, conv := range inbox.ConvsUnverified { rc := conv tlfID := rc.Conv.Metadata.IdTriple.Tlfid.TLFIDStr() byTLFID[tlfID] = append(byTLFID[tlfID], rc) channels[rc.ConvIDStr] = utils.GetConvMtime(rc) } teams := make(map[chat1.TLFIDStr]gregor1.Time, len(byTLFID)) for tlfID, rcs := range byTLFID { sort.Sort(utils.RemoteConvByMtime(rcs)) teams[tlfID] = channels[rcs[0].ConvIDStr] } res.Teams = teams res.Channels = channels return res, nil } func (c *TeamChannelSource) GetChannelsFull(ctx context.Context, uid gregor1.UID, tlfID chat1.TLFID, topicType chat1.TopicType, ) (res []chat1.ConversationLocal, err error) { ctx = globals.CtxModifyUnboxMode(ctx, types.UnboxModeQuick) defer c.Trace(ctx, &err, "GetChannelsFull: tlfID: %v, topicType: %v", tlfID, topicType)() rcs, err := c.getTLFConversations(ctx, uid, tlfID, topicType) if err != nil { return nil, err } for _, rc := range rcs { c.G().ParticipantsSource.GetWithNotifyNonblock(ctx, uid, rc.GetConvID(), types.InboxSourceDataSourceAll) } convs, _, err := c.G().InboxSource.Localize(ctx, uid, rcs, types.ConversationLocalizerBlocking) if err != nil { c.Debug(ctx, "GetChannelsFull: failed to localize conversations: %s", err.Error()) return nil, err } sort.Sort(utils.ConvLocalByTopicName(convs)) c.Debug(ctx, "GetChannelsFull: found %d convs", len(convs)) return convs, nil } func (c *TeamChannelSource) GetChannelsTopicName(ctx context.Context, uid gregor1.UID, tlfID chat1.TLFID, topicType chat1.TopicType, ) (res []chat1.ChannelNameMention, err error) { ctx = globals.CtxModifyUnboxMode(ctx, types.UnboxModeQuick) defer c.Trace(ctx, &err, "GetChannelsTopicName: tlfID: %v, topicType: %v", tlfID, topicType)() addValidMetadataMsg := func(convID chat1.ConversationID, msg chat1.MessageUnboxed) { if !msg.IsValid() { c.Debug(ctx, "GetChannelsTopicName: metadata message invalid: convID, %s", convID) return } body := msg.Valid().MessageBody typ, err := body.MessageType() if err != nil { c.Debug(ctx, "GetChannelsTopicName: error getting message type: convID, %s", convID, err) return } if typ != chat1.MessageType_METADATA { c.Debug(ctx, "GetChannelsTopicName: message not a real metadata message: convID, %s msgID: %d", convID, msg.GetMessageID()) return } res = append(res, chat1.ChannelNameMention{ ConvID: convID, TopicName: body.Metadata().ConversationTitle, }) } convs, err := c.getTLFConversations(ctx, uid, tlfID, topicType) if err != nil { return nil, err } for _, rc := range convs { conv := rc.Conv msg, err := conv.GetMaxMessage(chat1.MessageType_METADATA) if err != nil { continue } unboxeds, err := c.G().ConvSource.GetMessages(ctx, conv.GetConvID(), uid, []chat1.MessageID{msg.GetMessageID()}, nil, nil, false) if err != nil { c.Debug(ctx, "GetChannelsTopicName: failed to unbox metadata message for: convID: %s err: %s", conv.GetConvID(), err) continue } if len(unboxeds) != 1 { c.Debug(ctx, "GetChannelsTopicName: empty result: convID: %s", conv.GetConvID()) continue } addValidMetadataMsg(conv.GetConvID(), unboxeds[0]) } return res, nil } func (c *TeamChannelSource) GetChannelTopicName(ctx context.Context, uid gregor1.UID, tlfID chat1.TLFID, topicType chat1.TopicType, convID chat1.ConversationID, ) (res string, err error) { ctx = globals.CtxModifyUnboxMode(ctx, types.UnboxModeQuick) defer c.Trace(ctx, &err, "GetChannelTopicName: tlfID: %v, topicType: %v, convID: %v", tlfID, topicType, convID)() convs, err := c.GetChannelsTopicName(ctx, uid, tlfID, topicType) if err != nil { return "", err } if len(convs) == 0 { return "", fmt.Errorf("no convs found") } for _, conv := range convs { if conv.ConvID.Eq(convID) { return conv.TopicName, nil } } return "", fmt.Errorf("no convs found with convID") } func (c *TeamChannelSource) GetRecentJoins(ctx context.Context, convID chat1.ConversationID, remoteClient chat1.RemoteInterface) (res int, err error) { defer c.Trace(ctx, &err, "GetRecentJoins")() numJoins := c.recentJoinsCache.Get(convID) if numJoins < 0 { res, err := remoteClient.GetRecentJoins(ctx, convID) if err != nil { return 0, err } numJoins = res.NumJoins c.recentJoinsCache.Put(convID, numJoins) } return numJoins, nil } func (c *TeamChannelSource) GetLastActiveAt(ctx context.Context, teamID keybase1.TeamID, uid gregor1.UID, remoteClient chat1.RemoteInterface, ) (res gregor1.Time, err error) { defer c.Trace(ctx, &err, "GetLastActiveAt")() lastActiveAt, found := c.lastActiveAtCache.Get(teamID, uid) if !found { res, err := remoteClient.GetLastActiveAt(ctx, chat1.GetLastActiveAtArg{ TeamID: teamID, Uid: uid, }) if err != nil { return 0, err } lastActiveAt = res.LastActiveAt c.lastActiveAtCache.Put(teamID, uid, lastActiveAt) } return lastActiveAt, nil }