whatsappSendRecords = 1000
)
-// openWhatsAppState reads the batches and the send records of the state file at
-// path. A missing file holds none. A batch closes after
-// whatsappBatchIdleTimeout with no message, or after whatsappBatchTimeout at
-// the latest.
-func openWhatsAppState(path string) (*WhatsAppState, error) {
- s := &WhatsAppState{
- path: path,
- }
-
- data, err := os.ReadFile(path)
- if errors.Is(err, os.ErrNotExist) {
- return s, nil
- }
-
- if err != nil {
- return nil, fmt.Errorf("read WhatsApp state: %w", err)
- }
-
- err = json.Unmarshal(data, &s.file)
- if err != nil {
- return nil, fmt.Errorf("%s: %w", path, err)
- }
-
- return s, nil
-}
-
// WhatsAppState holds the state file: the send records, and the batches of the
// WhatsApp messages that are not in the log yet, open and closed, in the order
// of their first message. The messages of one chat form one batch while the
file WhatsAppStateFile
}
+// WhatsAppStateFile is the content of the state file: the number of the last
+// batch, the batches that are not in the log yet, and the send records, oldest
+// first.
+type WhatsAppStateFile struct {
+ Next int `json:"next"`
+ Batches []WhatsAppBatch `json:"batches"`
+ Sends []WhatsAppSend `json:"sends"`
+}
+
+// WhatsAppSend is the record of one send: the ID that the agent gave, the chat,
+// the SHA-256 of the text, an optional stored image name, the WhatsApp message
+// ID, the state, which is sending, sent, or failed, the answer of the tool, and
+// the time of its start.
+type WhatsAppSend struct {
+ ID string `json:"id"`
+ Chat string `json:"chat"`
+ Digest string `json:"digest"`
+ Image string `json:"image,omitempty"`
+ MessageID string `json:"message_id"`
+ State string `json:"state"`
+ Answer string `json:"answer,omitempty"`
+ Time time.Time `json:"time"`
+}
+
+// openWhatsAppState reads the batches and the send records of the state file at
+// path. A missing file holds none. A batch closes after
+// whatsappBatchIdleTimeout with no message, or after whatsappBatchTimeout at
+// the latest.
+func openWhatsAppState(path string) (*WhatsAppState, error) {
+ s := &WhatsAppState{
+ path: path,
+ }
+
+ data, err := os.ReadFile(path)
+ if errors.Is(err, os.ErrNotExist) {
+ return s, nil
+ }
+
+ if err != nil {
+ return nil, fmt.Errorf("read WhatsApp state: %w", err)
+ }
+
+ err = json.Unmarshal(data, &s.file)
+ if err != nil {
+ return nil, fmt.Errorf("%s: %w", path, err)
+ }
+
+ return s, nil
+}
+
// Add puts message of chat into the open batch of chat, or into a new batch,
// with arrival as its time of arrival. A message that the batch holds already,
// which the servers sent again, changes nothing. It returns true when a batch
Chat: chat,
First: arrival,
})
+
index = len(s.file.Batches) - 1
}
return closed, s.save()
}
+// open returns the index of the open batch of chat, or -1. The caller holds the
+// mutex.
+func (s *WhatsAppState) open(chat string) int {
+ return slices.IndexFunc(s.file.Batches, func(batch WhatsAppBatch) bool {
+ return !batch.Closed && batch.Chat.ID == chat
+ })
+}
+
+// save writes the state file whole. The caller holds the mutex.
+func (s *WhatsAppState) save() error {
+ data, err := json.Marshal(s.file)
+ if err != nil {
+ return fmt.Errorf("encode WhatsApp state: %w", err)
+ }
+
+ err = os.MkdirAll(filepath.Dir(s.path), 0o700)
+ if err == nil {
+ err = writeFileAtomic(s.path, append(data, '\n'))
+ }
+
+ if err != nil {
+ return fmt.Errorf("save WhatsApp state: %w", err)
+ }
+
+ return nil
+}
+
// CloseDue closes each open batch whose time is up at now and tells if one
// closed.
func (s *WhatsAppState) CloseDue(now time.Time) (bool, error) {
return true, s.save()
}
+// deadline returns when batch closes in real time.
+func (s *WhatsAppState) deadline(batch WhatsAppBatch) time.Time {
+ quiet := batch.Last.Add(whatsappBatchIdleTimeout)
+ window := batch.First.Add(whatsappBatchTimeout)
+ if window.Before(quiet) {
+ return window
+ }
+
+ return quiet
+}
+
// CloseAll closes every open batch.
func (s *WhatsAppState) CloseAll() error {
s.mutex.Lock()
return s.save()
}
-
-// open returns the index of the open batch of chat, or -1. The caller holds the
-// mutex.
-func (s *WhatsAppState) open(chat string) int {
- return slices.IndexFunc(s.file.Batches, func(batch WhatsAppBatch) bool {
- return !batch.Closed && batch.Chat.ID == chat
- })
-}
-
-// deadline returns when batch closes in real time.
-func (s *WhatsAppState) deadline(batch WhatsAppBatch) time.Time {
- quiet := batch.Last.Add(whatsappBatchIdleTimeout)
- window := batch.First.Add(whatsappBatchTimeout)
- if window.Before(quiet) {
- return window
- }
-
- return quiet
-}
-
-// save writes the state file whole. The caller holds the mutex.
-func (s *WhatsAppState) save() error {
- data, err := json.Marshal(s.file)
- if err != nil {
- return fmt.Errorf("encode WhatsApp state: %w", err)
- }
-
- err = os.MkdirAll(filepath.Dir(s.path), 0o700)
- if err == nil {
- err = writeFileAtomic(s.path, append(data, '\n'))
- }
-
- if err != nil {
- return fmt.Errorf("save WhatsApp state: %w", err)
- }
-
- return nil
-}
-
-// WhatsAppStateFile is the content of the state file: the number of the last
-// batch, the batches that are not in the log yet, and the send records, oldest
-// first.
-type WhatsAppStateFile struct {
- Next int `json:"next"`
- Batches []WhatsAppBatch `json:"batches"`
- Sends []WhatsAppSend `json:"sends"`
-}
-
-// WhatsAppSend is the record of one send: the ID that the agent gave, the chat,
-// the SHA-256 of the text, the WhatsApp message ID, the state, which is
-// sending, sent, or failed, the answer of the tool, and the time of its start.
-type WhatsAppSend struct {
- ID string `json:"id"`
- Chat string `json:"chat"`
- Digest string `json:"digest"`
- MessageID string `json:"message_id"`
- State string `json:"state"`
- Answer string `json:"answer,omitempty"`
- Time time.Time `json:"time"`
-}