Rich Menu Management
Overview
A rich menu is the image-based menu shown at the bottom of a LINE chat screen. The platform
supports several menu types (default, member, guest, custom) and can bind a different
menu to each individual user.
This job is the consumer that receives change, link, and delete commands for rich menus and
carries them out against the real LINE Rich Menu API. Everything flows through a single queue,
line_change_richmenu, which accepts six different commands distinguished by the type field
in the payload.
Business Flow
The handler reads the type field and branches accordingly.
type | What it does |
|---|---|
member | changeRichMenuFromWebhookId — triggered when a user types "member" in chat. Resolves the OA from webhookId, looks up the line_user, flips user_type to MEMBER, and binds the member rich menu to that user via the LINE API. |
setRichMenuMember | setRichMenuMemberByLineOaId — binds the member rich menu to every member of the OA, processing in chunks with a fresh CLS context per message. |
setRichMenuMemberByLineUserId | Binds a rich menu to one specific user. |
setRichMenuByTriggerRule | Binds a rich menu based on the outcome of a trigger rule, dispatched by the switch_rich_menu action. |
cronSetRichMenuCustom | Finds custom rich menus with status=active, complete_link_user=false, and start_date <= now < end_date, then progressively binds them to the target member set. Once complete, marks complete_link_user=true. |
delete | deleteRichMenu — unlinks all users, deletes the rich menu on LINE, and cascades the status change to alias rich menus. |
Principles shared across all branches:
- The
channel_access_tokenis resolved throughLineOAResolver(thelineoa.Servicethat verifies and refreshes tokens), and a separate LINE client is created per OA. - Bulk user binding is split into chunks via
chunkStringsso the API is never hit all at once. - Related Redis cache entries are cleared after every change through
delRedisAllCacheRelate. - A payload that fails to decode returns
mq.Permanentand goes straight to the DLQ, while an unrecognisedtypereturns nil and is silently acknowledged without retry.
Key Files & Functions
internal/richmenu/consumer.go—Consumer.HandleLineChangeRichMenu()dispatches ontype;Register()internal/richmenu/richmenu.go—Service,NewService(), thetypeOfRichMenuDefault/Member/Guest/Customenum,chunkStrings(),delRedisAllCacheRelate()internal/richmenu/service_methods.go—changeRichMenuFromWebhookId(),setRichMenuMemberByLineOaId(),setRichMenuMemberByLineUserId(),setRichMenuByTriggerRule(),deleteRichMenu()internal/richmenu/service_cron.go—cronSetRichMenuCustom(),cronDeleteRichMenuCustom(),findRichMenuMemberLineUserIds(),cascadeStatusToAliasRichMenus()internal/line/richmenu.go— the LINE Rich Menu API clientcmd/worker/integration.go— wiring forlineOaForRichMenuandlineUserForRichMenu- Queue:
line_change_richmenu(profilemain)
Connections to Other Services
- Receives jobs from: the
line_webhookhandler (when a user types "member"), the cms-api-go rich menu domain, and the action executor via theswitch_rich_menuaction - Tables:
rich_menu,rich_menu_action,rich_menu_archive,line_user(itsrich_menu_idanduser_typecolumns), andline_oa - Redis: rich menu domain cache, prefixed by
MODULE_NAME - LINE API: create/delete rich menu, linking via
POST /v2/bot/user/:userId/richmenu/:richMenuId, unlinking, and rich menu aliases - Menu click statistics are covered in Rich Menu Stat Calculation