400653810e
The correction gesture now reaches all three surfaces, and CLAUDE.md said only /chat had it. Doc 23 carries the decisions: long-poll rather than a webhook, the backlog dropped on start, one accepted sender, and the two-tap keyboard.
71 lines
3.0 KiB
Markdown
71 lines
3.0 KiB
Markdown
# Inbound telegram
|
|
|
|
Last verified: 06-08-2026 @ c61b0b3
|
|
|
|
V-637, under V-628. Reads with `22-correcting-a-turn.md`.
|
|
|
|
## What was missing
|
|
|
|
Telegram was a reach and nothing else. `telegramsink` pushed an away message and the chat
|
|
had no way to answer, so the correction gesture reached the web and voice only.
|
|
|
|
That skews the labels. V-546 fits routing heads on them, and a sample drawn from wherever
|
|
the owner happens to be sitting is the wrong sample.
|
|
|
|
## Long-poll, not a webhook
|
|
|
|
The box takes no inbound connections and reaches api.telegram.org through a relay, so the
|
|
connection has to open outward. `getUpdates` with a 25 second hold, one goroutine in the
|
|
daemon's WaitGroup.
|
|
|
|
A failed poll waits 15 seconds and retries without escalating. The relay going down is the
|
|
normal cause and it comes back on its own.
|
|
|
|
## The backlog is dropped on start
|
|
|
|
Telegram keeps undelivered updates for 24 hours. A daemon that was down overnight would
|
|
otherwise wake and answer every queued message in order.
|
|
|
|
That is worse than missing them. A question asked eight hours ago has been answered
|
|
already. A reminder set from it lands at the wrong time. So the first call moves the offset
|
|
past whatever is queued and acts on none of it.
|
|
|
|
## One chat
|
|
|
|
`ChatID` is the only accepted sender, and it is the same chat the push half already sends
|
|
to. A message from anywhere else is dropped with no reply, because a reply confirms the bot
|
|
exists and whose it is.
|
|
|
|
Chat ids are not guessable. They are also not secret, since they travel in every forwarded
|
|
message. So this is the whole authorisation and it is an allowlist of one.
|
|
|
|
## The gesture
|
|
|
|
Two taps at most. The reply carries one button, `не то`. Tapping it writes nothing and opens
|
|
the seven intents plus `просто неверно`. The untargeted negative stays reachable, because he
|
|
may have opened the row without meaning to name anything.
|
|
|
|
Callback data carries the trace id and the target, under telegram's 64 byte cap. It comes
|
|
off the wire. So an id that will not parse is dropped, and so is a target that is not one of
|
|
the seven. A label nothing can score is worse than no label.
|
|
|
|
A failed write says so on the button and leaves the keyboard up. A successful one takes the
|
|
keyboard off, because a live keyboard on an answered turn invites correcting it twice.
|
|
|
|
## The seam
|
|
|
|
`NewPoller` takes two functions and no daemon type. `cmd/mavend/telegramintake.go` fills
|
|
them from `ipc.CoreAPI`: `Chat` returns the reply and the trace id it collected off the
|
|
context, and `CorrectTurn` writes the label. So a chat turn takes the path
|
|
`POST /api/chat` already takes, and nothing in `internal/delivery` knows what a handler is.
|
|
|
|
## What is not done
|
|
|
|
The turn source is still `tap:text`, which telegram shares with the web. Provenance cannot
|
|
tell a chat turn from a typed one, so a label's `source` column cannot either.
|
|
That matters the first time someone asks whether corrections given in the chat differ from
|
|
corrections given at the desk.
|
|
|
|
Voice messages are ignored. The poller reads `message.text` and nothing else, so a voice
|
|
note in the chat does not reach `mavsttd`.
|