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.
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{
continue poll
}
- return err
+ return fmt.Errorf("handle Telegram update: %w", err)
}
imageOutage.End()
offset = update.UpdateID + 1
err = telegramSaveOffset(b.offsetPath, offset)
if err != nil {
- return err
+ return fmt.Errorf("save Telegram offset: %w", err)
}
}
}
// 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,
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))
}
}
+// 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{
}
}
-// 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.
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) {