]> git.giacomolli.uk - onechat/blob - whatsapp_groups.go
Give the root agent the schedule tools
[onechat] / whatsapp_groups.go
1 package main
2
3 import (
4 "context"
5 "log"
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 *log.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 *log.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 name, found := g.names[id]
53 fresh := time.Since(g.triedAt) < whatsappGroupsRefresh
54 g.mutex.Unlock()
55
56 if !found && !fresh {
57 g.read(ctx, link)
58
59 g.mutex.Lock()
60 name, found = g.names[id]
61 g.mutex.Unlock()
62 }
63
64 if !found || name == "" {
65 return whatsappUnknownGroup
66 }
67
68 return name
69 }
70
71 // read reads the names of the groups of the account from WhatsApp with the
72 // client of link, and tells if it could.
73 func (g *WhatsAppGroups) read(ctx context.Context, link WhatsAppLink) bool {
74 if link.client == nil {
75 return false
76 }
77
78 ctx, cancel := context.WithTimeout(ctx, whatsappLookupTime)
79 defer cancel()
80
81 groups, err := link.client.GetJoinedGroups(ctx)
82 if err != nil {
83 g.logger.Printf("read the groups: %s", whatsappRedact(link.own, err.Error()))
84 }
85
86 g.mutex.Lock()
87 defer g.mutex.Unlock()
88
89 g.triedAt = time.Now()
90 if err != nil {
91 return false
92 }
93
94 g.readAt = g.triedAt
95 for _, group := range groups {
96 g.names[group.JID.String()] = group.Name
97 }
98
99 return true
100 }
101
102 // set makes name the name of the group id.
103 func (g *WhatsAppGroups) set(id, name string) {
104 g.mutex.Lock()
105 defer g.mutex.Unlock()
106
107 g.names[id] = name
108 }
109
110 // stale tells if the last read that succeeded is older than whatsappGroupsAge.
111 func (g *WhatsAppGroups) stale() bool {
112 g.mutex.Lock()
113 defer g.mutex.Unlock()
114
115 return time.Since(g.readAt) >= whatsappGroupsAge
116 }
117
118 // all returns a copy of the names of the groups.
119 func (g *WhatsAppGroups) all() map[string]string {
120 g.mutex.Lock()
121 defer g.mutex.Unlock()
122
123 return maps.Clone(g.names)
124 }