+// receive stores images and transcribed voice notes before their batches and
+// wakes Run. link gives the device and the own IDs. It returns false when the
+// state file cannot hold the message, so that the library does not acknowledge
+// it, and Run stops the daemon. Failed media input gets a text marker. A
+// message that the batches do not take is handled with nothing to do.
+func (i *WhatsAppInbox) receive(ctx context.Context, link WhatsAppLink, event *events.Message) bool {
+ lookupCtx, cancel := context.WithTimeout(ctx, whatsappContactLookupTimeout)
+ defer cancel()
+
+ chat, message, found := whatsappMessage(lookupCtx, link.device, link.own, event)
+ if !found {
+ return true
+ }
+
+ if event.Message.GetImageMessage() != nil {
+ ctx, cancel := context.WithTimeout(ctx, whatsappImageDownloadTimeout)
+ defer cancel()
+
+ message.Text = i.imageText(ctx, link, event, message.Text)
+ } else if event.Message.GetAudioMessage().GetPTT() {
+ message.Text = whatsappRedact(link.own, whatsappVoice(ctx, link.client, i.voices, i.voice, event.Message.GetAudioMessage()))
+ }
+
+ _, err := i.state.Add(chat, message, time.Now())
+ if err != nil {
+ select {
+ case i.failures <- err:
+ default:
+ }
+
+ return false
+ }
+
+ select {
+ case i.wake <- struct{}{}:
+ default:
+ }
+
+ return true
+}
+
+// imageText stores the image of event before returning its marker and caption.
+// Failures keep the caption with a text marker. The daemon log identifies the
+// chat by a short hash, not its phone number.
+func (i *WhatsAppInbox) imageText(ctx context.Context, link WhatsAppLink, event *events.Message, caption string) string {
+ name, err := whatsappImage(ctx, link.client, i.images, event.Message.GetImageMessage())
+ if err != nil {
+ chat := event.Info.Chat.ToNonAD()
+ redact := append(slices.Clone(link.own), chat.User)
+ i.logger.Error("image receive failed", "chat", whatsappChatHash(chat), "err", whatsappRedact(redact, err.Error()))
+
+ text := "(image not downloaded)"
+ if caption != "" {
+ text += "\n" + caption
+ }
+
+ return text
+ }
+
+ return imageText([]string{name}, caption)
+}
+