Initial codex telegram bot source

This commit is contained in:
Codex
2026-05-21 08:40:16 +00:00
commit ad61f7eeed
275 changed files with 101972 additions and 0 deletions

183
internal/telegram/render.go Normal file
View File

@@ -0,0 +1,183 @@
package telegram
import (
"encoding/base64"
"fmt"
"html"
"strconv"
"strings"
)
const TelegramMessageLimit = 4096
const TelegramHTMLMessageLimit = 3900
func EscapeHTML(text string) string {
return html.EscapeString(text)
}
func SummaryDetailsHTML(summary, details string) string {
summary = strings.TrimSpace(summary)
details = strings.TrimSpace(details)
if details == "" {
return EscapeHTML(summary)
}
if summary == "" {
return ExpandableQuoteHTML(details)
}
return EscapeHTML(summary) + "\n" + ExpandableQuoteHTML(details)
}
func ExpandableQuoteHTML(text string) string {
text = strings.TrimSpace(text)
if text == "" {
return ""
}
return "<blockquote expandable>" + EscapeHTML(text) + "</blockquote>"
}
func SummaryDetailsHTMLLimited(summary, details string, limit int) string {
if limit <= 0 {
limit = TelegramHTMLMessageLimit
}
summary = strings.TrimSpace(summary)
details = strings.TrimSpace(details)
out := SummaryDetailsHTML(summary, details)
if len([]rune(out)) <= limit || details == "" {
return out
}
suffix := "\n...[truncated]"
runes := []rune(details)
for len(runes) > 0 {
candidateLen := len(runes) - max(1, (len([]rune(out))-limit)/2)
if candidateLen < 0 {
candidateLen = 0
}
if candidateLen > len(runes) {
candidateLen = len(runes)
}
candidate := strings.TrimSpace(string(runes[:candidateLen])) + suffix
out = SummaryDetailsHTML(summary, candidate)
if len([]rune(out)) <= limit || candidateLen == 0 {
return out
}
runes = runes[:candidateLen]
}
return SummaryDetailsHTML(summary, suffix)
}
func ChunkText(text string, max int) []string {
if max <= 0 {
max = TelegramMessageLimit
}
runes := []rune(text)
if len(runes) == 0 {
return nil
}
var chunks []string
for len(runes) > max {
cut := max
for i := max; i > max/2; i-- {
if runes[i-1] == '\n' {
cut = i
break
}
}
chunks = append(chunks, string(runes[:cut]))
runes = runes[cut:]
}
if len(runes) > 0 {
chunks = append(chunks, string(runes))
}
return chunks
}
func ApprovalCallbackData(id int64, decision string) string {
return fmt.Sprintf("approval:%d:%s", id, decision)
}
func ParseApprovalCallbackData(data string) (int64, string, bool) {
parts := strings.Split(data, ":")
if len(parts) != 3 || parts[0] != "approval" {
return 0, "", false
}
id, err := strconv.ParseInt(parts[1], 10, 64)
if err != nil || id <= 0 {
return 0, "", false
}
switch parts[2] {
case "accept", "acceptForSession", "decline", "cancel", "details":
return id, parts[2], true
default:
return 0, "", false
}
}
func WorkspaceCallbackData(id int64) string {
return fmt.Sprintf("workspace:%d", id)
}
func ParseWorkspaceCallbackData(data string) (int64, bool) {
if !strings.HasPrefix(data, "workspace:") {
return 0, false
}
id, err := strconv.ParseInt(strings.TrimPrefix(data, "workspace:"), 10, 64)
return id, err == nil && id > 0
}
func ResumeThreadCallbackData(id int64) string {
return fmt.Sprintf("resume:thread:%d", id)
}
func ParseResumeThreadCallbackData(data string) (int64, bool) {
if !strings.HasPrefix(data, "resume:thread:") {
return 0, false
}
id, err := strconv.ParseInt(strings.TrimPrefix(data, "resume:thread:"), 10, 64)
return id, err == nil && id > 0
}
func ResumePageCallbackData(page int) string {
return fmt.Sprintf("resume:page:%d", page)
}
func ParseResumePageCallbackData(data string) (int, bool) {
if !strings.HasPrefix(data, "resume:page:") {
return 0, false
}
page, err := strconv.Atoi(strings.TrimPrefix(data, "resume:page:"))
return page, err == nil && page >= 0
}
func ModelCallbackData(modelID string) (string, bool) {
encoded := base64.RawURLEncoding.EncodeToString([]byte(modelID))
data := "model:" + encoded
return data, len([]rune(data)) <= 64
}
func ParseModelCallbackData(data string) (string, bool) {
if !strings.HasPrefix(data, "model:") {
return "", false
}
decoded, err := base64.RawURLEncoding.DecodeString(strings.TrimPrefix(data, "model:"))
if err != nil || len(decoded) == 0 {
return "", false
}
return string(decoded), true
}
func EffortCallbackData(effort string) string {
encoded := base64.RawURLEncoding.EncodeToString([]byte(effort))
return "effort:" + encoded
}
func ParseEffortCallbackData(data string) (string, bool) {
if !strings.HasPrefix(data, "effort:") {
return "", false
}
decoded, err := base64.RawURLEncoding.DecodeString(strings.TrimPrefix(data, "effort:"))
if err != nil || len(decoded) == 0 {
return "", false
}
return string(decoded), true
}