smtp2shoutrrr/go.mod
butterrobot 06de333118
All checks were successful
CI / goreleaser-lint (pull_request) Successful in 3s
CI / format (pull_request) Successful in 33s
CI / test (pull_request) Successful in 2m21s
CI / lint (pull_request) Successful in 3m6s
CI / build (pull_request) Successful in 3m13s
refactor: convert HTML with html-to-markdown instead of by hand (FMG-9)
The hand-written renderer is replaced by
github.com/JohannesKaufmann/html-to-markdown/v2 plus the mail-specific policy
it has no opinion about. 986 lines of html.go become 372; the conversion
itself — CommonMark escaping, delimiter runs, fencing a code block past the
backticks inside it — is now a maintained library's problem rather than ours.

The original justification for writing it by hand was that the library would
drag in goquery and its dependencies. That was true of v1 and wrong for v2,
which dropped it: the measured cost is two modules, html-to-markdown/v2 and
JohannesKaufmann/dom, on top of the golang.org/x/net this already used.

What the library does not know is mail, because it is written for documents.
The parsed message is prepared before conversion:

- Hidden preheaders, written for the inbox list, are removed.
- Images without alt text go, which takes the tracking pixels, spacers and
  sliced-up banners with them. An inline cid: attachment leaves its alt text
  behind as ordinary words.
- Destinations a reader cannot open are dropped and the link text kept; tabs
  and line breaks are stripped from the rest, since a line break inside an
  href is invisible in the document and a fabricated line in the message.
- Table rows become lines and their cells stay apart, which the converter has
  no rule for: "Total4Failed0" otherwise.
- A link left holding nothing but a pixel falls back to its own destination
  rather than rendering as an invisible "[](url)".
- Quote and list nesting is flattened past six levels, and the output is
  capped at 64 KiB with a marker.

That last one is not something any of the candidates solved. html-to-markdown
amplifies exactly as the hand-written renderer did before it was capped, from
the same cause — a line prefix re-emitted per line and per level. Measured on
one message at the server's own 1 MB limit, nested 250 deep: 131 MB of output
over 2m16s, against 64 KiB in 1.5s and 85 MiB of peak heap with the flattening
in place.

Format = "text" is dropped, leaving raw and markdown. Markdown reads as plain
text wherever nothing renders it, so a second conversion would only have been
a worse copy of this one, and the plain-text libraries surveyed were the weak
half of the field. Nothing has shipped with "text", so no released
configuration names it; an unknown Format is still refused at startup.

The test suite carries over almost unchanged, because it asserts output rather
than internals — which is what made the swap safe to judge. Every mail-policy
and injection case still holds, and the pathological-input test is sized from
the constants now so the suite stays quick.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 09:09:46 +00:00

30 lines
963 B
Modula-2

module git.nakama.town/fmartingr/smtp2shoutrrr
go 1.27
toolchain go1.27.1
require (
github.com/JohannesKaufmann/html-to-markdown/v2 v2.5.2
github.com/containrrr/shoutrrr v0.8.0
github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6
github.com/emersion/go-smtp v0.25.0
github.com/pelletier/go-toml/v2 v2.4.3
github.com/stretchr/testify v1.12.1
golang.org/x/crypto/x509roots/fallback v0.0.0-20260902180247-86efde54dc70
golang.org/x/net v0.58.0
)
require (
github.com/JohannesKaufmann/dom v0.3.1 // indirect
github.com/fatih/color v1.19.0 // indirect
github.com/go-logr/logr v1.4.4 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/mattn/go-colorable v0.1.15 // indirect
github.com/mattn/go-isatty v0.0.24 // indirect
go.yaml.in/yaml/v3 v3.0.5 // indirect
golang.org/x/sys v0.47.0 // indirect
golang.org/x/text v0.41.0 // indirect
golang.org/x/tools v0.49.0 // indirect
)