]> git.giacomolli.uk - onechat/blob - whatsapp_batch.go
Pass caller contexts through WhatsApp callbacks
[onechat] / whatsapp_batch.go
1 package main
2
3 import (
4 "slices"
5 "strings"
6 "time"
7 )
8
9 const (
10 // whatsappBatchEmptyReply is the last line of a batch that starts a
11 // turn.
12 whatsappBatchEmptyReply = "If nothing in this batch needs the user, end your turn with an empty reply."
13
14 // whatsappBatchDayLayout is the layout of the day in the first line of
15 // a batch.
16 whatsappBatchDayLayout = "Monday 2006-01-02"
17 )
18
19 // WhatsAppBatch is one batch of the messages of one chat, with the arrival
20 // times of its first and its last message.
21 type WhatsAppBatch struct {
22 Number int `json:"number"`
23 Chat WhatsAppChat `json:"chat"`
24 Closed bool `json:"closed"`
25 First time.Time `json:"first"`
26 Last time.Time `json:"last"`
27 Messages []WhatsAppMessage `json:"messages"`
28 }
29
30 // Turn tells if the batch starts a turn: a private batch with a message of
31 // another person, or a group batch with a message of another person that
32 // mentions the user or replies to a message of the user.
33 func (b WhatsAppBatch) Turn() bool {
34 return slices.ContainsFunc(b.Messages, func(message WhatsAppMessage) bool {
35 return !message.Mine && (!b.Chat.Group || message.Mention || message.Reply)
36 })
37 }
38
39 // Text returns the text of the batch for the log, with the times in location.
40 // The first line starts with [WhatsApp] and names the chat, its ID, and the day
41 // of the first message. Each message has a line with its time and its sender,
42 // and the messages of the user have the sender You. A batch that starts a turn
43 // ends with the line about the empty reply. Image markers start on their own
44 // lines so that the shared image reader can find them.
45 func (b WhatsAppBatch) Text(location *time.Location) string {
46 kind := "private chat"
47 if b.Chat.Group {
48 kind = "group"
49 }
50
51 lines := []string{"[WhatsApp] " + b.Chat.Name + ", " + kind + " " + b.Chat.ID + ", " + b.Messages[0].Time.In(location).Format(whatsappBatchDayLayout)}
52 for _, message := range b.Messages {
53 sender := message.Sender
54 switch {
55 case message.Mine:
56 sender = "You"
57 case message.Mention:
58 sender += " (mentions you)"
59 case message.Reply:
60 sender += " (replies to you)"
61 }
62
63 prefix := message.Time.In(location).Format("15:04") + " " + sender + ":"
64 if len(imageFiles(message.Text)) > 0 {
65 lines = append(lines, prefix, message.Text)
66 } else {
67 lines = append(lines, prefix+" "+message.Text)
68 }
69 }
70
71 if b.Turn() {
72 lines = append(lines, whatsappBatchEmptyReply)
73 }
74
75 return strings.Join(lines, "\n")
76 }
77
78 // WhatsAppChat is a chat: its ID, its name, and whether it is a group.
79 type WhatsAppChat struct {
80 ID string `json:"id"`
81 Name string `json:"name"`
82 Group bool `json:"group,omitempty"`
83 }
84
85 // WhatsAppMessage is one message of a batch: its ID, the name of its sender,
86 // its WhatsApp time, and its text. Mine marks a message of the user. Mention
87 // and Reply mark a message that mentions the user or replies to a message of
88 // the user.
89 type WhatsAppMessage struct {
90 ID string `json:"id"`
91 Sender string `json:"sender"`
92 Time time.Time `json:"time"`
93 Text string `json:"text"`
94 Mine bool `json:"mine,omitempty"`
95 Mention bool `json:"mention,omitempty"`
96 Reply bool `json:"reply,omitempty"`
97 }