slack_actions

ActionHandler


source

ActionHandler

 ActionHandler ()

*Handles Slack interactive actions, including acknowledgment, response, and storing results in Snowflake.

Implements a singleton pattern to ensure only one handler exists.*

Responding to Actions:

Storing Action in Snowflake

When the user interacts with the bot, the action is stored in Snowflake. The action is stored in a table called user_interaction. The table has the following columns:

We also need a way to get information about a view in Snowflake:

ActionIdManager


source

ActionIdManager

 ActionIdManager ()

Manages action IDs for Slack interactive elements to ensure uniqueness and provide routing capabilities for action handlers.

test_eq(ActionIdManager.generate_action_id("button", 0), "tk_interaction_btn_0")

test_eq(ActionIdManager.parse_action_id("tk_interaction_btn_0"),{'type': 'btn', 'idx': '0', 'action_type': 'button', 'index': 0})

Let’s now update the ActionHandler class to use the new ActionIDManager class.

Let’s create a method to allow the user to setup the action handler in their slackbot app, and this handler will need to use an instance of the ActionIDManager class to generate a unique action ID for each action. The action ID will be used to identify the action in the Snowflake table


source

ActionHandler.setup_slack_action_handler

 ActionHandler.setup_slack_action_handler (app)

*Set up a single Slack action handler with the Bolt app.

Args: app: Slack Bolt app snowflake_connector: Connector for Snowflake operations

Returns: Initialized ActionHandler instance*

We also need to update the ActionHandler class to use the new ActionIDManager class. When processing actions from the app, it will need to call parse_action_id to get the information from the id

body_example = {'type': 'block_actions', 'user': {'id': 'U08SWJ7MGDC', 'username': 'cooperrichason', 'name': 'cooperrichason', 'team_id': 'T08SWJ7MGAJ'}, 'api_app_id': 'A08SWJJGW1L', 'token': 'wjBURx2qV2cSMgnUlvDvI7D9', 'container': {'type': 'message', 'message_ts': '1747869345.068909', 'channel_id': 'C08TFTZHY4R', 'is_ephemeral': False}, 'trigger_id': '8931953858965.8914619730358.3acc5cd59b50a162f407f5d3a049a08b', 'team': {'id': 'T08SWJ7MGAJ', 'domain': 'datatistics'}, 'enterprise': None, 'is_enterprise_install': False, 'channel': {'id': 'C08TFTZHY4R', 'name': 'tk_slack'}, 'message': {'user': 'U08TFUY0HND', 'type': 'message', 'ts': '1747869345.068909', 'bot_id': 'B08TFUXV0M7', 'app_id': 'A08SWJJGW1L', 'text': 'Example Data Alert', 'team': 'T08SWJ7MGAJ', 'metadata': {'event_type': 'example_view_notification', 'event_payload': {'view_name': 'example_view', 'view_group': 'examples', 'response_type': 'ephemeral', 'response_message': 'Thanks {user}! You selected "{text}" ({value})', 'replace_original': False, 'custom_row_index': 2}}, 'blocks': [{'type': 'section', 'block_id': 'DpeTS', 'text': {'type': 'mrkdwn', 'text': '*Feature Z Implementation*', 'verbatim': False}}, {'type': 'section', 'block_id': 'BOqKS', 'text': {'type': 'mrkdwn', 'text': 'Planning for Feature Z implementation', 'verbatim': False}}, {'type': 'section', 'block_id': '3rZsp', 'fields': [{'type': 'mrkdwn', 'text': '*Name*\nFeature Z Implementation', 'verbatim': False}, {'type': 'mrkdwn', 'text': '*Text*\nPlanning for Feature Z implementation', 'verbatim': False}, {'type': 'mrkdwn', 'text': '*Status*\nIn Progress', 'verbatim': False}, {'type': 'mrkdwn', 'text': '*Priority*\nHigh', 'verbatim': False}, {'type': 'mrkdwn', 'text': '*Due Date*\nJun 15, 2025', 'verbatim': False}, {'type': 'mrkdwn', 'text': '*Option Name*\nStart Work, Assign to Me, Request More Info', 'verbatim': False}, {'type': 'mrkdwn', 'text': '*Option Value*\nstart, assign_self, request_info', 'verbatim': False}]}, {'type': 'actions', 'block_id': 'WKbX2', 'elements': [{'type': 'button', 'action_id': 'tk_interaction_btn_0', 'text': {'type': 'plain_text', 'text': 'Start Work', 'emoji': True}, 'style': 'primary', 'value': 'start'}, {'type': 'button', 'action_id': 'tk_interaction_btn_1', 'text': {'type': 'plain_text', 'text': 'Assign to Me', 'emoji': True}, 'value': 'assign_self'}, {'type': 'button', 'action_id': 'tk_interaction_btn_2', 'text': {'type': 'plain_text', 'text': 'Request More Info', 'emoji': True}, 'value': 'request_info'}]}, {'type': 'divider', 'block_id': 'MnqyR'}]}, 'state': {'values': {}}, 'response_url': 'https://hooks.slack.com/actions/T08SWJ7MGAJ/8931953776821/BeYdqU9hrCKACdosBdn9GVBA', 'actions': [{'action_id': 'tk_interaction_btn_0', 'block_id': 'WKbX2', 'text': {'type': 'plain_text', 'text': 'Start Work', 'emoji': True}, 'value': 'start', 'style': 'primary', 'type': 'button', 'action_ts': '1747870391.878433'}]}
body_example
{'type': 'block_actions',
 'user': {'id': 'U08SWJ7MGDC',
  'username': 'cooperrichason',
  'name': 'cooperrichason',
  'team_id': 'T08SWJ7MGAJ'},
 'api_app_id': 'A08SWJJGW1L',
 'token': 'wjBURx2qV2cSMgnUlvDvI7D9',
 'container': {'type': 'message',
  'message_ts': '1747869345.068909',
  'channel_id': 'C08TFTZHY4R',
  'is_ephemeral': False},
 'trigger_id': '8931953858965.8914619730358.3acc5cd59b50a162f407f5d3a049a08b',
 'team': {'id': 'T08SWJ7MGAJ', 'domain': 'datatistics'},
 'enterprise': None,
 'is_enterprise_install': False,
 'channel': {'id': 'C08TFTZHY4R', 'name': 'tk_slack'},
 'message': {'user': 'U08TFUY0HND',
  'type': 'message',
  'ts': '1747869345.068909',
  'bot_id': 'B08TFUXV0M7',
  'app_id': 'A08SWJJGW1L',
  'text': 'Example Data Alert',
  'team': 'T08SWJ7MGAJ',
  'metadata': {'event_type': 'example_view_notification',
   'event_payload': {'view_name': 'example_view',
    'view_group': 'examples',
    'response_type': 'ephemeral',
    'response_message': 'Thanks {user}! You selected "{text}" ({value})',
    'replace_original': False,
    'custom_row_index': 2}},
  'blocks': [{'type': 'section',
    'block_id': 'DpeTS',
    'text': {'type': 'mrkdwn',
     'text': '*Feature Z Implementation*',
     'verbatim': False}},
   {'type': 'section',
    'block_id': 'BOqKS',
    'text': {'type': 'mrkdwn',
     'text': 'Planning for Feature Z implementation',
     'verbatim': False}},
   {'type': 'section',
    'block_id': '3rZsp',
    'fields': [{'type': 'mrkdwn',
      'text': '*Name*\nFeature Z Implementation',
      'verbatim': False},
     {'type': 'mrkdwn',
      'text': '*Text*\nPlanning for Feature Z implementation',
      'verbatim': False},
     {'type': 'mrkdwn', 'text': '*Status*\nIn Progress', 'verbatim': False},
     {'type': 'mrkdwn', 'text': '*Priority*\nHigh', 'verbatim': False},
     {'type': 'mrkdwn', 'text': '*Due Date*\nJun 15, 2025', 'verbatim': False},
     {'type': 'mrkdwn',
      'text': '*Option Name*\nStart Work, Assign to Me, Request More Info',
      'verbatim': False},
     {'type': 'mrkdwn',
      'text': '*Option Value*\nstart, assign_self, request_info',
      'verbatim': False}]},
   {'type': 'actions',
    'block_id': 'WKbX2',
    'elements': [{'type': 'button',
      'action_id': 'tk_interaction_btn_0',
      'text': {'type': 'plain_text', 'text': 'Start Work', 'emoji': True},
      'style': 'primary',
      'value': 'start'},
     {'type': 'button',
      'action_id': 'tk_interaction_btn_1',
      'text': {'type': 'plain_text', 'text': 'Assign to Me', 'emoji': True},
      'value': 'assign_self'},
     {'type': 'button',
      'action_id': 'tk_interaction_btn_2',
      'text': {'type': 'plain_text',
       'text': 'Request More Info',
       'emoji': True},
      'value': 'request_info'}]},
   {'type': 'divider', 'block_id': 'MnqyR'}]},
 'state': {'values': {}},
 'response_url': 'https://hooks.slack.com/actions/T08SWJ7MGAJ/8931953776821/BeYdqU9hrCKACdosBdn9GVBA',
 'actions': [{'action_id': 'tk_interaction_btn_0',
   'block_id': 'WKbX2',
   'text': {'type': 'plain_text', 'text': 'Start Work', 'emoji': True},
   'value': 'start',
   'style': 'primary',
   'type': 'button',
   'action_ts': '1747870391.878433'}]}

source

ActionHandler.process_slack_action

 ActionHandler.process_slack_action (body:Dict[str,Any], respond:Callable)

*Process a Slack action event.

Args: body: Slack event body respond: Slack respond function

Returns: Processed action data*