Skip to content

feat(broadcasts): the 15th module — WhatsApp campaigns over the flow API - #7

Merged
OlegKrasikov merged 1 commit into
mainfrom
w22-broadcasts
Sep 8, 2026
Merged

OlegKrasikov merged 1 commit into
mainfrom
w22-broadcasts

Conversation

@OlegKrasikov

@OlegKrasikov OlegKrasikov commented Sep 7, 2026

Copy link
Copy Markdown
Collaborator

What

The broadcasts module: WhatsApp campaigns shaped like an email-marketing tool, over Chatfuel's flow API.

  • Campaigns — every campaign-shaped flow on the bot, with a status derived from the entry-point element and the clock (draft / scheduled / sending / sent), filters, search, an inline panel, row actions, ⌘K palette and keyboard bindings.
  • Composer — five steps over one server draft: name + kind (now / later / repeating), message (approved template from the catalog, blanks filled, {{Attribute}} inserted from the bot's own list), audience (two-level AND/OR builder with a live recipient count), schedule (bot wall clock, once / weekdays / every N days / on dates, weekdays corrected by zone), review (send behind a confirm, or arm). Duplicate replays a campaign onto a fresh draft.
  • Templates — the read-only catalog with Meta status and a hand-off to WhatsApp Manager. No public API authors a template.
  • A bot with no WhatsApp number sees a connect state on every surface, never the server error.
  • The skill: five references, two playbooks, 36 operations — everything the live pass found (no stats, no paused state, an unknown attribute name creates the attribute, a template block cannot be deleted, unregistered errors are bare InternalServerError).

Review pass

Four reviewers over the branch before this squash; fixed here:

  • kind switch dropped the audience; a Repeating draft could never be set to Once; a refused first pick showed nothing
  • writes on one blank could race (blur vs insert); two schedule commits could overlap
  • the "Broadcasts" flow group could be created twice; a refused delete still removed the row; refetchFlow could revert a write that landed while it was out; writeBlock merged into a stale flow
  • Duplicate from the row menu opened the original's review (whose primary is the send); "Use in a campaign" never set the template
  • n / palette could create a draft on a bot with no number; [ ] walked hidden rows
  • copy-code verdicts landed unattached; a third-level segment was dropped on re-save; the audience summary lost its operators
  • docs: the not-Approved verdict is the module's own, stats is not in the bundled schema, the knob rows name the right file

Gates

validate, codegen:check, check, lint, test (4040 shell tests), build green. The four write EPIPE proxy tests are the known macOS-only set. Live pass on the Panel bot: use-in-a-campaign, duplicate, repeating→once, kind switch with a filtered audience — all verified, net-zero.

🤖 Generated with Claude Code

A campaign is a flow: an entry point (one-time or scheduled) connected to a
template block. The module reads every such flow on the bot, lists it with a
status derived from the element and the clock (draft / scheduled / sending /
sent — the API has no paused state), and edits it through a five-step
composer: name and kind, the message (an approved template picked from the
catalog and filled, attribute references inserted from the bot's own list),
the audience (a two-level AND/OR builder over the contact attributes, with a
live recipient count), the schedule (first send on the bot's wall clock,
once / weekdays / every N days / on dates, weekdays corrected for the bot's
zone), and the review, where a one-time campaign is sent behind a confirm and
a scheduled one is armed. Duplicate replays a campaign onto a fresh draft;
there is no clone API. The Templates tab is the read-only catalog with a
hand-off to WhatsApp Manager, because no public API authors a template.

A bot with no WhatsApp number sees a connect state on every surface instead
of the server error the catalog query answers.

What the live pass found and the code is built around: there are no send
statistics (the resolver panics), disabling a scheduled entry point resets it
to Draft, the one-time pair is born enabled, an unknown attribute name in a
parameter silently creates the attribute, a template block cannot be deleted,
and every unregistered error arrives as a bare InternalServerError. All of it
is written into the skill.

The generated client for this module is joined with `inlineFragmentTypes:
'combine'`, which brings the Flow/Block/BlockElement interface product down
from 13.5 MB to 250 KB.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E21NfSAQzT78rt8eK26B6K
@OlegKrasikov
OlegKrasikov merged commit 6003b8f into main Sep 8, 2026
7 checks passed
OlegKrasikov added a commit that referenced this pull request Sep 8, 2026
The broadcasts module landed on main (#7) and nothing published carries it.
Minor bump: a new default module changes what --yes scaffolds.

Changed: the manifest version and the 0.4.0 section of the changelog, which the
release workflow reads for the GitHub Release body.


Claude-Session: https://claude.ai/code/session_01E21NfSAQzT78rt8eK26B6K

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant