Skip to main content

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.

typeWhat it does
memberchangeRichMenuFromWebhookId — 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.
setRichMenuMembersetRichMenuMemberByLineOaId — binds the member rich menu to every member of the OA, processing in chunks with a fresh CLS context per message.
setRichMenuMemberByLineUserIdBinds a rich menu to one specific user.
setRichMenuByTriggerRuleBinds a rich menu based on the outcome of a trigger rule, dispatched by the switch_rich_menu action.
cronSetRichMenuCustomFinds 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.
deletedeleteRichMenu — 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_token is resolved through LineOAResolver (the lineoa.Service that verifies and refreshes tokens), and a separate LINE client is created per OA.
  • Bulk user binding is split into chunks via chunkStrings so 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.Permanent and goes straight to the DLQ, while an unrecognised type returns nil and is silently acknowledged without retry.

Key Files & Functions

  • internal/richmenu/consumer.goConsumer.HandleLineChangeRichMenu() dispatches on type; Register()
  • internal/richmenu/richmenu.goService, NewService(), the typeOfRichMenuDefault/Member/Guest/Custom enum, chunkStrings(), delRedisAllCacheRelate()
  • internal/richmenu/service_methods.gochangeRichMenuFromWebhookId(), setRichMenuMemberByLineOaId(), setRichMenuMemberByLineUserId(), setRichMenuByTriggerRule(), deleteRichMenu()
  • internal/richmenu/service_cron.gocronSetRichMenuCustom(), cronDeleteRichMenuCustom(), findRichMenuMemberLineUserIds(), cascadeStatusToAliasRichMenus()
  • internal/line/richmenu.go — the LINE Rich Menu API client
  • cmd/worker/integration.go — wiring for lineOaForRichMenu and lineUserForRichMenu
  • Queue: line_change_richmenu (profile main)

Connections to Other Services

  • Receives jobs from: the line_webhook handler (when a user types "member"), the cms-api-go rich menu domain, and the action executor via the switch_rich_menu action
  • Tables: rich_menu, rich_menu_action, rich_menu_archive, line_user (its rich_menu_id and user_type columns), and line_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