]> git.giacomolli.uk - onechat/blobdiff - telegram.go
Bind external events to permanent agent IDs
[onechat] / telegram.go
index 4f19c6242ba6f631c8aa8c07d97f06407588d04f..78a1c7af9079c22982a02ec19bd2d377c0f71fde 100644 (file)
@@ -192,35 +192,6 @@ func (c TelegramConfig) validate() error {
        return nil
 }
 
-// Notify queues event for the chat. It never blocks, so a slow network does not
-// stop the agent. The WhatsApp batches and the runs of the schedules do not go
-// to the chat: the reply of the agent tells the user what needs the user.
-func (b *TelegramBot) Notify(event Event) {
-       switch event.Kind {
-       case "busy", "idle", "reply", "error", "notice", "subagent":
-       case "work":
-               if event.Source == whatsappSource || event.Source == scheduleSource {
-                       return
-               }
-       case "user":
-               if event.Source == telegramSource {
-                       return
-               }
-       default:
-               return
-       }
-
-       b.mutex.Lock()
-
-       b.queue = append(b.queue, event)
-       b.mutex.Unlock()
-
-       select {
-       case b.ready <- struct{}{}:
-       default:
-       }
-}
-
 // Run polls for the messages of the owner and sends the queued events to the
 // chat until ctx ends. It returns an error only when a message or the offset or
 // image cannot be written, because then the bot would lose or repeat messages.
@@ -293,7 +264,7 @@ func (b *TelegramBot) send(ctx context.Context) {
 func (b *TelegramBot) poll(ctx context.Context, l *TurnLoop, whatsapp *WhatsApp) error {
        offset, err := telegramLoadOffset(b.offsetPath)
        if err != nil {
-               return err
+               return fmt.Errorf("load Telegram offset: %w", err)
        }
 
        outage := &TelegramOutage{
@@ -342,7 +313,7 @@ poll:
                                        continue poll
                                }
 
-                               return err
+                               return fmt.Errorf("handle Telegram update: %w", err)
                        }
 
                        imageOutage.End()
@@ -352,7 +323,7 @@ poll:
                        offset = update.UpdateID + 1
                        err = telegramSaveOffset(b.offsetPath, offset)
                        if err != nil {
-                               return err
+                               return fmt.Errorf("save Telegram offset: %w", err)
                        }
                }
        }
@@ -378,7 +349,8 @@ func (b *TelegramBot) next() (Event, bool) {
 // typing shows the typing action once. A failure is not logged, because the
 // next message or poll logs the state of the network.
 func (b *TelegramBot) typing(ctx context.Context) {
-       _ = b.api.SendTyping(ctx, b.owner)
+       // The next message or poll reports a network failure.
+       b.api.SendTyping(ctx, b.owner) // nolint: errcheck
 }
 
 // post sends event to the chat. A reply goes as rich messages of its Markdown,
@@ -504,7 +476,7 @@ func (b *TelegramBot) handle(ctx context.Context, l *TurnLoop, whatsapp *WhatsAp
        default:
                _, err := l.Submit(telegramSource, message.Text)
                if err != nil {
-                       return err
+                       return fmt.Errorf("submit Telegram text: %w", err)
                }
 
                err = l.Flush(context.WithoutCancel(ctx))
@@ -595,6 +567,14 @@ func (b *TelegramBot) deliverRich(ctx context.Context, markdown string) {
        }
 }
 
+// notice queues a short answer of the bot for the chat.
+func (b *TelegramBot) notice(text string) {
+       b.Notify(Event{
+               Kind: "notice",
+               Text: text,
+       })
+}
+
 // sendOutage returns a new outage of the messages to the chat.
 func (b *TelegramBot) sendOutage() *TelegramOutage {
        return &TelegramOutage{
@@ -604,22 +584,33 @@ func (b *TelegramBot) sendOutage() *TelegramOutage {
        }
 }
 
-// notice queues a short answer of the bot for the chat.
-func (b *TelegramBot) notice(text string) {
-       b.Notify(Event{
-               Kind: "notice",
-               Text: text,
-       })
-}
-
-// Fail logs the start of an outage with err and delay, unless an outage runs.
-func (o *TelegramOutage) Fail(err error, delay time.Duration) {
-       if !o.start.IsZero() {
+// Notify queues event for the chat. It never blocks, so a slow network does not
+// stop the agent. External events, WhatsApp batches, and schedule runs do not
+// go to the chat: the reply of the agent tells the user what needs the user.
+func (b *TelegramBot) Notify(event Event) {
+       switch event.Kind {
+       case "busy", "idle", "reply", "error", "notice", "subagent":
+       case "work":
+               if event.Source == whatsappSource || event.Source == scheduleSource || event.Source == notifySource {
+                       return
+               }
+       case "user":
+               if event.Source == telegramSource {
+                       return
+               }
+       default:
                return
        }
 
-       o.start = time.Now()
-       o.logger.Warn(o.loss, "err", err, "retry", delay)
+       b.mutex.Lock()
+
+       b.queue = append(b.queue, event)
+       b.mutex.Unlock()
+
+       select {
+       case b.ready <- struct{}{}:
+       default:
+       }
 }
 
 // End logs the end of the outage that runs, with its length.
@@ -633,6 +624,16 @@ func (o *TelegramOutage) End() {
        o.start = time.Time{}
 }
 
+// Fail logs the start of an outage with err and delay, unless an outage runs.
+func (o *TelegramOutage) Fail(err error, delay time.Duration) {
+       if !o.start.IsZero() {
+               return
+       }
+
+       o.start = time.Now()
+       o.logger.Warn(o.loss, "err", err, "retry", delay)
+}
+
 // telegramLoadOffset reads the update offset at path. A missing file gives 0,
 // which asks for every update that Telegram keeps.
 func telegramLoadOffset(path string) (int64, error) {