// correction.go — the correction gesture as it appears in the chat (V-637). // Two taps at most: "не то" opens the seven intents, and one of them writes the // label. The web's version of the same gesture is cmd/mavweb/chat.go. package telegramsink import ( "fmt" "strconv" "strings" ) // CorrectionTargets — the intents a correction may name, in the order the // buttons are drawn. It mirrors the seven the web offers, and it is a closed // list for the same reason: V-632 fits prototypes from the label table, and a // label nothing can score is worse than no label. var CorrectionTargets = []string{"fact", "note", "reminder", "query", "act", "chat", "system"} // correctionKeyboard — the one gesture beside the reply. Nothing when the turn // did not persist: a button that cannot name a row would report a failure the // owner cannot act on. func (p *Poller) correctionKeyboard(traceID int64) *inlineKeyboard { if traceID <= 0 || p.correct == nil { return nil } return &inlineKeyboard{Rows: [][]inlineButton{{ {Text: "не то", Data: fmt.Sprintf("%s%d", prefixAsk, traceID)}, }}} } // targetKeyboard — the seven intents, plus the cheap half kept reachable. He // opened the row without knowing he had to name something, and closing it with // no way out would price the negative he was willing to give. func targetKeyboard(traceID int64) *inlineKeyboard { var rows [][]inlineButton row := []inlineButton{} for _, t := range CorrectionTargets { row = append(row, inlineButton{Text: t, Data: fmt.Sprintf("%s%d:%s", prefixTarget, traceID, t)}) if len(row) == 4 { rows, row = append(rows, row), nil } } if len(row) > 0 { rows = append(rows, row) } return &inlineKeyboard{Rows: append(rows, []inlineButton{ {Text: "просто неверно", Data: fmt.Sprintf("%s%d:", prefixTarget, traceID)}, })} } // Callback data is capped at 64 bytes by telegram, so it carries the trace id // and the target and nothing else. const ( prefixAsk = "w:" prefixTarget = "t:" ) type callbackKind int const ( callbackUnknown callbackKind = iota callbackAskTarget callbackTarget ) // parseCallback reads button data. An unparseable id, or a target that is not // one of the seven, is callbackUnknown — the data came off the wire, and a // label the fitting code cannot score is worse than no label. func parseCallback(data string) (traceID int64, target string, kind callbackKind) { switch { case strings.HasPrefix(data, prefixAsk): id, err := strconv.ParseInt(strings.TrimPrefix(data, prefixAsk), 10, 64) if err != nil || id <= 0 { return 0, "", callbackUnknown } return id, "", callbackAskTarget case strings.HasPrefix(data, prefixTarget): rest := strings.TrimPrefix(data, prefixTarget) idPart, target, ok := strings.Cut(rest, ":") if !ok { return 0, "", callbackUnknown } id, err := strconv.ParseInt(idPart, 10, 64) if err != nil || id <= 0 { return 0, "", callbackUnknown } if target != "" && !isCorrectionTarget(target) { return 0, "", callbackUnknown } return id, target, callbackTarget } return 0, "", callbackUnknown } func isCorrectionTarget(s string) bool { for _, t := range CorrectionTargets { if t == s { return true } } return false }