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)
- Receive a payload containing
campaignId,lineOaId,organizationIdand a list ofrefIds. - Insert the refs into
friend_track_refin batches of 1,000. Refs that already exist are counted asskippedrather than treated as errors. - Total up
insertedandskipped, then write the import status back tofriend_track_campaignso the CMS can display progress.
Attribution on follow
This path runs from the LINE webhook handler whenever a follow event arrives.
runFriendTrackAttributionstarts the attribution process.- The service looks up the user's most recent
liff_visitevent and eager-loads thefriend_track_campaignattached to it. - If a match is found, a
followevent is written tofriend_track_eventalong with the campaign, display name, picture URL and the originatingref. - If the campaign defines
attribute_configorref_attribute_key, therefvalue is also written as a custom attribute on the user — for examplesource = facebook-ads-jan— which makes the acquisition channel immediately available for segmentation. - The INSERT into
friend_track_eventfires a Postgres trigger that emitspg_notify('friend_track_event_inserted'). The pg-listener forwards it to thefriend_track_event_triggerqueue, where the trigger engine evaluates rules withsource_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
| File | Responsibility |
|---|---|
internal/friendtrack/refimport.go | RefImportService.ProcessImport imports refs in batches of 1,000 using the RefImportPayload structure |
internal/friendtrack/service.go | Service.GetLastAttribution, Service.RecordFollowEvent, Service.RecordUnfollowEvent, plus the event-type and campaign-status enums |
internal/friendtrack/consumer.go | Consumer.HandleImport — consumer for the friend_track_ref_import queue |
internal/lineoa/webhook.go | runFriendTrackAttribution, handleFollowEvent, handleUnfollowEvent, selectLineUserAttribute |
cmd/worker/integration.go | friendTrackForLineOa — 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_visitevent when a user opens LIFF through a ref link; this is the raw material attribution depends on. - Database tables —
friend_track_ref(ref list),friend_track_campaign(campaign definition and import status),friend_track_event(liff_visit / follow / unfollow) andline_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.