fluid-telegram/cmd/adapter/main.go
tegwick 86cbb43965 FT-WP-0001-T14: upload post images instead of linking them
The adapter fetches visual_url from an allowlisted host (https, image types,
10 MB cap, optional read token) and uploads it as multipart sendPhoto, so the
forge can close without breaking post images.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 5457@bnt-lap001
Assistant-Session: 792bf84a-3783-4f5d-a811-cded61dd2012
2026-10-08 20:55:32 +02:00

69 lines
2.2 KiB
Go

// Command adapter runs the R-1 Telegram publishing adapter for one channel.
//
// It owns rendering and delivery only: it has no MTProto session and no
// provisioning code path, so a compromised adapter cannot create, rename,
// delete or re-permission anything (docs/adapter-contract.md). Configuration
// comes from `provision adapter-config`, per that same doc's Configuration
// section.
package main
import (
"fmt"
"log"
"net/http"
"os"
"strconv"
"strings"
"github.com/tegwick/fluid-telegram/internal/adapter"
)
func main() {
if err := run(); err != nil {
log.Fatal(err)
}
}
func run() error {
token := os.Getenv("TELEGRAM_BOT_TOKEN")
if token == "" {
return fmt.Errorf("TELEGRAM_BOT_TOKEN is not set; injected from OpenBao at start, see docs/adapter-contract.md")
}
chatIDStr := os.Getenv("TELEGRAM_CHANNEL_ID")
if chatIDStr == "" {
return fmt.Errorf("TELEGRAM_CHANNEL_ID is not set; run `provision adapter-config`")
}
chatID, err := strconv.ParseInt(chatIDStr, 10, 64)
if err != nil {
return fmt.Errorf("TELEGRAM_CHANNEL_ID %q is not an integer: %w", chatIDStr, err)
}
attachVisual := os.Getenv("ATTACH_VISUAL") == "true"
controlURL := os.Getenv("FLUID_CONTROL_URL")
hallBaseURL := os.Getenv("HALL_BASE_URL")
addr := os.Getenv("ADAPTER_LISTEN_ADDR")
if addr == "" {
addr = ":8080"
}
cfg := adapter.Config{
ChatID: chatID,
AttachVisual: attachVisual,
HallBaseURL: hallBaseURL,
}
// UPLOAD_VISUALS=true: fetch images from VISUAL_HOSTS (default the forge)
// and upload them, optionally with FORGE_READ_TOKEN, so Telegram never needs
// a public image URL (FT-WP-0001-T14).
if os.Getenv("UPLOAD_VISUALS") == "true" {
hosts := strings.Split(os.Getenv("VISUAL_HOSTS"), ",")
if strings.TrimSpace(os.Getenv("VISUAL_HOSTS")) == "" {
hosts = []string{"forgejo.coulomb.social"}
}
cfg.Visuals = adapter.NewVisualFetcher(hosts, os.Getenv("FORGE_READ_TOKEN"))
}
a := adapter.New(cfg, adapter.NewBotAPISender(token), adapter.NewMemStore(), adapter.NewHTTPTelemetry(controlURL))
log.Printf("adapter listening on %s (chat_id=%d, attach_visual=%v, upload_visuals=%v)", addr, chatID, attachVisual, cfg.Visuals != nil)
return http.ListenAndServe(addr, a.Handler())
}