package main import ( "slices" "strings" "time" ) const ( // whatsappBatchEmptyReply is the last line of a batch that starts a // turn. whatsappBatchEmptyReply = "If nothing in this batch needs the user, end your turn with an empty reply." // whatsappBatchDayLayout is the layout of the day in the first line of // a batch. whatsappBatchDayLayout = "Monday 2006-01-02" ) // WhatsAppBatch is one batch of the messages of one chat, with the arrival // times of its first and its last message. type WhatsAppBatch struct { Number int `json:"number"` Chat WhatsAppChat `json:"chat"` Closed bool `json:"closed"` First time.Time `json:"first"` Last time.Time `json:"last"` Messages []WhatsAppMessage `json:"messages"` } // Turn tells if the batch starts a turn: a private batch with a message of // another person, or a group batch with a message of another person that // mentions the user or replies to a message of the user. func (b WhatsAppBatch) Turn() bool { return slices.ContainsFunc(b.Messages, func(message WhatsAppMessage) bool { return !message.Mine && (!b.Chat.Group || message.Mention || message.Reply) }) } // Text returns the text of the batch for the log, with the times in location. // The first line starts with [WhatsApp] and names the chat, its ID, and the day // of the first message. Each message has a line with its time and its sender, // and the messages of the user have the sender You. A batch that starts a turn // ends with the line about the empty reply. Image markers start on their own // lines so that the shared image reader can find them. func (b WhatsAppBatch) Text(location *time.Location) string { kind := "private chat" if b.Chat.Group { kind = "group" } lines := []string{"[WhatsApp] " + b.Chat.Name + ", " + kind + " " + b.Chat.ID + ", " + b.Messages[0].Time.In(location).Format(whatsappBatchDayLayout)} for _, message := range b.Messages { sender := message.Sender switch { case message.Mine: sender = "You" case message.Mention: sender += " (mentions you)" case message.Reply: sender += " (replies to you)" } prefix := message.Time.In(location).Format("15:04") + " " + sender + ":" if len(imageFiles(message.Text)) > 0 { lines = append(lines, prefix, message.Text) } else { lines = append(lines, prefix+" "+message.Text) } } if b.Turn() { lines = append(lines, whatsappBatchEmptyReply) } return strings.Join(lines, "\n") } // WhatsAppChat is a chat: its ID, its name, and whether it is a group. type WhatsAppChat struct { ID string `json:"id"` Name string `json:"name"` Group bool `json:"group,omitempty"` } // WhatsAppMessage is one message of a batch: its ID, the name of its sender, // its WhatsApp time, and its text. Mine marks a message of the user. Mention // and Reply mark a message that mentions the user or replies to a message of // the user. type WhatsAppMessage struct { ID string `json:"id"` Sender string `json:"sender"` Time time.Time `json:"time"` Text string `json:"text"` Mine bool `json:"mine,omitempty"` Mention bool `json:"mention,omitempty"` Reply bool `json:"reply,omitempty"` }