Initial implementation of the eec-campaigns tool
Drives listmonk's real Campaign API from git-authored Markdown+frontmatter, so broadcast/segment emails get listmonk's mature unsubscribe/bulk-send/ attachment handling instead of reimplementing it. sync only ever creates or updates a draft (idempotent, diff-based, refuses to touch a non-draft campaign); a pushed send/<slug> tag or manual workflow run is the only way to actually trigger a send. Includes list-name resolution, segment_query materialization into managed lists, content-hash-deduped attachment uploads, and an automatic post-sync preview email.
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
package campaign
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"reground.org/eec-campaigns/internal/listmonk"
|
||||
)
|
||||
|
||||
// Send transitions a campaign from draft/paused to running — the one real
|
||||
// send trigger in this whole tool (see cmd/send, triggered only by a pushed
|
||||
// send/<slug> git tag or a manual workflow_dispatch, never by a plain
|
||||
// sync). The campaign is looked up fresh by name each call, so this always
|
||||
// acts on listmonk's current live state, not whatever sync last saw.
|
||||
func Send(lm *listmonk.Client, slug string) (*listmonk.Campaign, error) {
|
||||
camp, err := lm.FindCampaignByName(slug)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if camp == nil {
|
||||
return nil, fmt.Errorf("no campaign named %q in listmonk — run sync first", slug)
|
||||
}
|
||||
if camp.Status != "draft" && camp.Status != "paused" {
|
||||
return nil, fmt.Errorf("campaign %q is %s in listmonk — only draft or paused campaigns can be sent", slug, camp.Status)
|
||||
}
|
||||
if err := lm.SetCampaignStatus(camp.ID, "running"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return camp, nil
|
||||
}
|
||||
|
||||
// Test sends a preview of a campaign's current content to the given
|
||||
// addresses without touching its status — the same mechanism sync's
|
||||
// automatic preview uses, exposed standalone for on-demand re-previews.
|
||||
func Test(lm *listmonk.Client, slug string, emails []string) error {
|
||||
camp, err := lm.FindCampaignByName(slug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if camp == nil {
|
||||
return fmt.Errorf("no campaign named %q in listmonk — run sync first", slug)
|
||||
}
|
||||
return lm.TestCampaign(camp.ID, emails)
|
||||
}
|
||||
Reference in New Issue
Block a user