]> git.giacomolli.uk - onechat/blob - whatsapp_groups.go
Back up the daemon and restore its data
[onechat] / whatsapp_groups.go
1 package main
2
3 import (
4         "context"
5         "log/slog"
6         "maps"
7         "sync"
8         "time"
9 )
10
11 const (
12         // whatsappGroupsRefresh is the shortest time between two reads of the
13         // groups from WhatsApp for a group with no name.
14         whatsappGroupsRefresh = time.Minute
15
16         // whatsappUnknownGroup is the name of a group whose name is not known.
17         whatsappUnknownGroup = "A group with no known name"
18
19         // whatsappGroupsAge is how long whatsapp_find uses the groups that it
20         // read from WhatsApp.
21         whatsappGroupsAge = time.Hour
22 )
23
24 // newWhatsAppGroups returns the names of the groups with no names yet. logger
25 // gets the failures of a read.
26 func newWhatsAppGroups(logger *slog.Logger) *WhatsAppGroups {
27         return &WhatsAppGroups{
28                 logger: logger,
29                 names:  make(map[string]string),
30         }
31 }
32
33 // WhatsAppGroups holds the names of the groups of the account, by the ID of the
34 // group. They come from the events of the groups and from reads of the groups
35 // from WhatsApp, and they stay in memory only. triedAt is the time of the last
36 // read, and readAt the time of the last read that succeeded. The mutex
37 // protects all fields after logger.
38 type WhatsAppGroups struct {
39         logger *slog.Logger
40
41         mutex   sync.Mutex
42         names   map[string]string
43         triedAt time.Time
44         readAt  time.Time
45 }
46
47 // name returns the name of the group id, or whatsappUnknownGroup. A group that
48 // is not in the names reads the groups from WhatsApp with the client of link
49 // again, at most once in whatsappGroupsRefresh.
50 func (g *WhatsAppGroups) name(ctx context.Context, link WhatsAppLink, id string) string {
51         g.mutex.Lock()
52
53         name, found := g.names[id]
54         fresh := time.Since(g.triedAt) < whatsappGroupsRefresh
55         g.mutex.Unlock()
56
57         if !found && !fresh {
58                 g.read(ctx, link)
59
60                 g.mutex.Lock()
61
62                 name, found = g.names[id]
63                 g.mutex.Unlock()
64         }
65
66         if !found || name == "" {
67                 return whatsappUnknownGroup
68         }
69
70         return name
71 }
72
73 // read reads the names of the groups of the account from WhatsApp with the
74 // client of link, and tells if it could.
75 func (g *WhatsAppGroups) read(ctx context.Context, link WhatsAppLink) bool {
76         if link.client == nil {
77                 return false
78         }
79
80         ctx, cancel := context.WithTimeout(ctx, whatsappLookupTime)
81         defer cancel()
82
83         groups, err := link.client.GetJoinedGroups(ctx)
84         if err != nil {
85                 g.logger.Warn("group list failed", "err", whatsappRedact(link.own, err.Error()))
86         }
87
88         g.mutex.Lock()
89         defer g.mutex.Unlock()
90
91         g.triedAt = time.Now()
92         if err != nil {
93                 return false
94         }
95
96         g.readAt = g.triedAt
97         for _, group := range groups {
98                 g.names[group.JID.String()] = group.Name
99         }
100
101         return true
102 }
103
104 // set makes name the name of the group id.
105 func (g *WhatsAppGroups) set(id, name string) {
106         g.mutex.Lock()
107         defer g.mutex.Unlock()
108
109         g.names[id] = name
110 }
111
112 // stale tells if the last read that succeeded is older than whatsappGroupsAge.
113 func (g *WhatsAppGroups) stale() bool {
114         g.mutex.Lock()
115         defer g.mutex.Unlock()
116
117         return time.Since(g.readAt) >= whatsappGroupsAge
118 }
119
120 // all returns a copy of the names of the groups.
121 func (g *WhatsAppGroups) all() map[string]string {
122         g.mutex.Lock()
123         defer g.mutex.Unlock()
124
125         return maps.Clone(g.names)
126 }