Attach images to posts and return the Telegram message id
The adapter response now carries telegram_message_id and a t.me/c link. compose visual resolves a post's image: own visual_url, else the entry's rendered portrait, else hold an entry with only a visual prompt, else the HelixForge main image. send-post.sh resolves before touching credentials, requires an image on public posts and runs the adapter with ATTACH_VISUAL. 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
This commit is contained in:
parent
b61261d042
commit
4861dd994b
7 changed files with 342 additions and 12 deletions
74
internal/compose/visual_test.go
Normal file
74
internal/compose/visual_test.go
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
package compose
|
||||
|
||||
import "testing"
|
||||
|
||||
const hallRaw = "https://forge.example/coulomb/hall-of-helix/raw/branch/main"
|
||||
const fallback = "https://forge.example/coulomb/pr-hall-of-helix/raw/branch/main/assets/helix-forge.png"
|
||||
|
||||
func TestResolveVisualPrefersRenderedPortrait(t *testing.T) {
|
||||
entry := "# Seat\n\n## Visual prompt\n\n> Constellation dialect.\n\n\n\n## Handoff\n"
|
||||
v, err := ResolveVisual(entry, hallRaw, fallback)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if v.Kind != VisualPortrait || v.Path != "visuals/bernd-how-it-started.png" ||
|
||||
v.URL != hallRaw+"/visuals/bernd-how-it-started.png" {
|
||||
t.Fatalf("got %+v", v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveVisualPromptWithoutPortraitNeedsRender(t *testing.T) {
|
||||
entry := "# Seat\n\n## Visual prompt\n\n> Constellation dialect. Square.\n> Brass scales.\n\n" +
|
||||
"<!--  -->\n\n" +
|
||||
"_Draft: portrait not yet rendered, expected as `claude-x-scales.jpg`._\n\n## Handoff\n"
|
||||
v, err := ResolveVisual(entry, hallRaw, fallback)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if v.Kind != VisualNeedsRender || v.URL != "" {
|
||||
t.Fatalf("a commented-out portrait or the fallback was used: %+v", v)
|
||||
}
|
||||
if v.Prompt != "Constellation dialect. Square. Brass scales." {
|
||||
t.Fatalf("prompt = %q", v.Prompt)
|
||||
}
|
||||
if v.Target != "visuals/claude-x-scales.jpg" {
|
||||
t.Fatalf("target = %q", v.Target)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveVisualFallsBackOnlyWithoutPrompt(t *testing.T) {
|
||||
for _, entry := range []string{"", "# Seat\n\n## Handoff\n\nNothing visual.\n"} {
|
||||
v, err := ResolveVisual(entry, hallRaw, fallback)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if v.Kind != VisualFallback || v.URL != fallback {
|
||||
t.Fatalf("entry %q: got %+v", entry, v)
|
||||
}
|
||||
}
|
||||
if _, err := ResolveVisual("", hallRaw, ""); err == nil {
|
||||
t.Fatal("resolved with no fallback configured")
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveVisualIgnoresExternalImages(t *testing.T) {
|
||||
entry := "# Seat\n\n\n"
|
||||
v, err := ResolveVisual(entry, hallRaw, fallback)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if v.Kind != VisualFallback {
|
||||
t.Fatalf("got %+v", v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveVisualTargetFromBareFileName(t *testing.T) {
|
||||
entry := "# Seat\n\n## Visual prompt\n\n> Scales.\n\n_Draft: expected under `visuals/` as `claude-y-scales.jpg`._\n"
|
||||
v, err := ResolveVisual(entry, hallRaw, fallback)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if v.Target != "visuals/claude-y-scales.jpg" {
|
||||
t.Fatalf("target = %q", v.Target)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue