Skip to main content

Multicast / Targeted Campaign Delivery

Overview

Multicast sends a campaign to a specific set of recipients (an audience), or to every OA member recorded in the database. Unlike broadcast, it requires knowing each recipient's lineUserId — but in exchange it supports merge tags for personalising the message and per-user tracking links.

This is the heaviest path in the system: recipient counts range from hundreds of thousands to several million. It therefore includes concurrency control, resume support, a dry-run mode, and a choice between two LINE API strategies.

Business Flow

  1. Receive a payload containing campaignId, audienceId, organizationId, and lineOaId, and start the claim heartbeat that refreshes every 5 minutes.

  2. Resolve the OA and validate the campaign using the same rules as broadcast, then load the quick reply once up front. If that load fails, delivery continues without the quick reply rather than failing the whole campaign.

  3. Resolve the recipient list, which falls into three cases:

    • audienceId is set and the audience has a CSV file at info.lineUserIdsPathFilename — read the CSV from S3 in pages of 10,000 IDs until exhausted.
    • audienceId is set but there is no CSV, meaning an automated audience — query the line_user table filtered by audience_ids @> to_jsonb($1::int) and line_oa_id.
    • No audienceId — call FindAllMemberUID to fetch every member of the lineOaId and organizationId pair.
  4. Determine whether the content uses merge tags, either from campaign.has_merge_tags or by scanning the content, then choose a delivery path.

    Path A — the LINE Multicast API, used when there are no merge tags and MULTICAST_USE_BATCH_API is enabled.

    • Build the shared redirect mappings once and transform the message once.
    • Send in batches of 500 recipients, using retry keys of the form campaign:<id>:multicast:<batchNo>.
    • This is the fastest path and consumes the fewest API calls.

    Path B — per-user push messages, the default, and mandatory when merge tags are present.

    • Split recipients into batches and, within each batch, bulk-fetch user data such as display_name, firstname, email, and custom_attribute — but only when merge tags are actually in use.
    • Distribute the work across concurrent workers according to MULTICAST_CONCURRENCY (default 200).
    • For each user: resolve merge tags, mint a tracking link bound to their lineUserId, transform the message, attach the quick reply, and call push message.
    • Users whose merge tag data cannot be found are skipped, counted in skippedCount, with the first 100 recorded in the log, governed by campaign.skip_merge_tag_missing.
  5. Resume support — successfully delivered recipients are recorded in a per-campaign Redis set. If the message is redelivered mid-send, already-delivered users are skipped automatically. A Redis outage does not block delivery.

  6. Dry-run — with MULTICAST_DRY_RUN enabled, every step runs normally except the actual LINE API call.

  7. Completion — update the campaign table with line_message_object, template_tracking, rich_message_content, end_tracking_date, total_recipient, and set the status to sent. On failure, multicastFailed restores the original content, sets the status to failed, and records a reason.

Key Files & Functions

  • internal/linemessageapi/multicast.go
    • MulticastService.HandleLineMulticastRichMessage(ctx, payload) — the entire flow
    • The MulticastToggles struct holding DryRun, UseBatchAPI, and Concurrency
    • sentSetKey(), isAlreadySent(), markSent() — the Redis-backed resume mechanism
    • multicastFailed()
  • internal/linemessageapi/consumer.goConsumer.HandleLineMulticastRichMessage and withClaimHeartbeat
  • internal/linemessageapi/mergetag.goResolveMergeTags(), extractMergeTagKeys(), hasMergeTags()
  • internal/linemessageapi/extend.gocreateRedirectMappings() in per-user mode
  • internal/csvaudience/csvaudience.go — reads audience CSV files from S3 through Count and Page
  • cmd/worker/integration.go — the audienceForLineMessageApi and lineUserRepoForLineMessageApi adapters
  • Queue: line_multicast_rich_message on the main profile

Connections to Other Services

  • Job sources — the process_campaign scanner, or cms-api-go when the user sends immediately
  • Tablescampaign, rich_message, audience (reading info.lineUserIdsPathFilename), line_user (recipient list and merge tag data), line_oa, and the quick reply tables
  • S3 — stores the audience CSV files
  • Redis — holds the sent-set used for resume
  • LINE APIPOST /v2/bot/message/multicast for Path A and POST /v2/bot/message/push for Path B
  • Behavioural environment variablesMULTICAST_DRY_RUN, MULTICAST_USE_BATCH_API, MULTICAST_CONCURRENCY
  • A future chunked-delivery design, adding the line_campaign_delivery_batch queue and a campaign_delivery_batch table, is documented in docs/campaign-delivery-tracking-redesign.md in the worker repository