Skip to main content

New Friend Source Tracking

Overview

Friend Track answers the question "where did this new friend come from?" An organisation can hand out links or QR codes that embed a different ref value per channel — a Facebook ad, in-store signage, an event booth. When a user opens the LIFF page through one of those links and then adds the OA as a friend, the follow event is automatically attributed back to the campaign that brought them in.

The worker owns two responsibilities: (1) bulk-importing ref values from CSV through the friend_track_ref_import queue, and (2) recording follow / unfollow events together with their attribution while LINE webhooks are being processed.

Business Flow

Ref import (queue friend_track_ref_import)

  1. Receive a payload containing campaignId, lineOaId, organizationId and a list of refIds.
  2. Insert the refs into friend_track_ref in batches of 1,000. Refs that already exist are counted as skipped rather than treated as errors.
  3. Total up inserted and skipped, then write the import status back to friend_track_campaign so the CMS can display progress.

Attribution on follow

This path runs from the LINE webhook handler whenever a follow event arrives.

  1. runFriendTrackAttribution starts the attribution process.
  2. The service looks up the user's most recent liff_visit event and eager-loads the friend_track_campaign attached to it.
  3. If a match is found, a follow event is written to friend_track_event along with the campaign, display name, picture URL and the originating ref.
  4. If the campaign defines attribute_config or ref_attribute_key, the ref value is also written as a custom attribute on the user — for example source = facebook-ads-jan — which makes the acquisition channel immediately available for segmentation.
  5. The INSERT into friend_track_event fires a Postgres trigger that emits pg_notify('friend_track_event_inserted'). The pg-listener forwards it to the friend_track_event_trigger queue, where the trigger engine evaluates rules with source_type = 'friend_track_event'.

Unfollow

When an unfollow event arrives, an event of type unfollow is recorded in the same table so that per-campaign churn rates can be calculated.

Key Files & Functions

FileResponsibility
internal/friendtrack/refimport.goRefImportService.ProcessImport imports refs in batches of 1,000 using the RefImportPayload structure
internal/friendtrack/service.goService.GetLastAttribution, Service.RecordFollowEvent, Service.RecordUnfollowEvent, plus the event-type and campaign-status enums
internal/friendtrack/consumer.goConsumer.HandleImport — consumer for the friend_track_ref_import queue
internal/lineoa/webhook.gorunFriendTrackAttribution, handleFollowEvent, handleUnfollowEvent, selectLineUserAttribute
cmd/worker/integration.gofriendTrackForLineOa — an adapter that narrows the data types for the lineoa domain

Queues involved: the worker consumes friend_track_ref_import on the main profile, while the effect of each INSERT surfaces on friend_track_event_trigger under the trigger-worker profile.

Connections to Other Services

  • cms-api-go — publishes the import job when an administrator uploads a campaign's ref list.
  • client-api-go — records the liff_visit event when a user opens LIFF through a ref link; this is the raw material attribution depends on.
  • Database tablesfriend_track_ref (ref list), friend_track_campaign (campaign definition and import status), friend_track_event (liff_visit / follow / unfollow) and line_user (custom attribute writes).
  • Postgres trigger and pg-listener — allow a follow event to drive downstream workflows.
  • No direct LINE API calls are made; user profile data already arrives through the webhook flow.