From b97128d773a70a053c057e779df4988b75fff025 Mon Sep 17 00:00:00 2001 From: koonwen Date: Mon, 7 Oct 2024 17:24:35 +0800 Subject: [PATCH 1/6] add interaction api --- lib/slack.atd | 42 +++++++++++++++++++ lib/utils.ml | 10 ++++- lib_test/dune | 1 + .../global_shortcut.json | 16 +++++++ .../message_shortcut.json | 25 +++++++++++ lib_test/slack_payloads.expected | 25 +++++++++++ lib_test/test.ml | 24 +++++++++++ 7 files changed, 142 insertions(+), 1 deletion(-) create mode 100644 lib_test/mock-slack-interactions/global_shortcut.json create mode 100644 lib_test/mock-slack-interactions/message_shortcut.json diff --git a/lib/slack.atd b/lib/slack.atd index b5c7432..4a501b6 100644 --- a/lib/slack.atd +++ b/lib/slack.atd @@ -598,3 +598,45 @@ type slack_api_error = [ type 'ok slack_response = ('ok, slack_api_error) http_response + +type interaction = [ +| Shortcut of message_actions +| Message_actions of message_actions +(* | Block_actions of *) +(* | View_submission of *) +(* | View_closed of *) +] + +type message_actions = { + callback_id: string; + trigger_id: string; + token: string; + team: interaction_team; + ?response_url: string nullable; + ?message: interaction_message nullable; + ?user: interaction_user nullable; + ?channel : interaction_channel nullable; +} + +type interaction_user = { + id: string; + ?name : string nullable; + ?username: string nullable; + ?team_id: string nullable; +} + +type interaction_team = { + id: string; + ?domain: string nullable; +} + +type interaction_channel = { + id: string; + ?name: string nullable; +} + +type interaction_message = { + user : string; + ts : string; + text : string; +} diff --git a/lib/utils.ml b/lib/utils.ml index 9663232..080c91d 100644 --- a/lib/utils.ml +++ b/lib/utils.ml @@ -6,7 +6,7 @@ open Slack_t (***************** General Slack Utilities for Handling Event Hooks *****************) (** [ validate_signature signing_key headers body ] validate the signature - from a Slack event API hook. + from a Slack event API hook. *) let validate_signature ?(version = "v0") ?signing_key ~headers body = match signing_key with @@ -47,6 +47,14 @@ let process_slack_event (ctx : Context.t) headers body ~event_handler = event_handler notification.event ) +let process_slack_interaction (ctx : Context.t) headers body ~interaction_handler = + match interaction_of_string body with + | exception Yojson.Json_error e -> Lwt.return_error (sprintf "Invalid interaction: %s" e) + | Shortcut i | Message_actions i -> + match validate_signature ?signing_key:ctx.secrets.slack_signing_secret ~headers body with + | Error e -> Lwt.return_error (sprintf "signature not validated: %s" e) + | Ok () -> interaction_handler i + (***************** Utilities over Slack API returns *****************) (** conversation types of a [Slack channel] *) diff --git a/lib_test/dune b/lib_test/dune index ce587bb..01013f6 100644 --- a/lib_test/dune +++ b/lib_test/dune @@ -8,6 +8,7 @@ (deps (source_tree slack-api-cache) (source_tree mock-slack-events) + (source_tree mock-slack-interactions) secrets.json) (action (with-stdout-to diff --git a/lib_test/mock-slack-interactions/global_shortcut.json b/lib_test/mock-slack-interactions/global_shortcut.json new file mode 100644 index 0000000..97d1a66 --- /dev/null +++ b/lib_test/mock-slack-interactions/global_shortcut.json @@ -0,0 +1,16 @@ +{ + "type": "shortcut", + "token": "XXXXXXXXXXXXX", + "action_ts": "1581106241.371594", + "team": { + "id": "TXXXXXXXX", + "domain": "shortcuts-test" + }, + "user": { + "id": "UXXXXXXXXX", + "username": "aman", + "team_id": "TXXXXXXXX" + }, + "callback_id": "shortcut_create_task", + "trigger_id": "944799105734.773906753841.38b5894552bdd4a780554ee59d1f3638" +} diff --git a/lib_test/mock-slack-interactions/message_shortcut.json b/lib_test/mock-slack-interactions/message_shortcut.json new file mode 100644 index 0000000..c3f045a --- /dev/null +++ b/lib_test/mock-slack-interactions/message_shortcut.json @@ -0,0 +1,25 @@ +{ + "token": "Nj2rfC2hU8mAfgaJLemZgO7H", + "callback_id": "chirp_message", + "type": "message_action", + "trigger_id": "13345224609.8534564800.6f8ab1f53e13d0cd15f96106292d5536", + "response_url": "https://hooks.slack.com/app-actions/T0MJR11A4/21974584944/yk1S9ndf35Q1flupVG5JbpM6", + "team": { + "id": "T0MJRM1A7", + "domain": "pandamonium" + }, + "channel": { + "id": "D0LFFBKLZ", + "name": "cats" + }, + "user": { + "id": "U0D15K92L", + "name": "dr_maomao" + }, + "message": { + "type": "message", + "user": "U0MJRG1AL", + "ts": "1516229207.000133", + "text": "World's smallest big cat! " + } +} diff --git a/lib_test/slack_payloads.expected b/lib_test/slack_payloads.expected index 454fe67..046e1aa 100644 --- a/lib_test/slack_payloads.expected +++ b/lib_test/slack_payloads.expected @@ -724,3 +724,28 @@ listing at cursor #dXNlcjpVMDQ2WE4wTTJSNQ==... "event_id": "Ev04C7PRDB53", "event_time": 1669265351 } +===== file mock-slack-interactions/global_shortcut.json ===== +{ + "type": "shortcut", + "callback_id": "shortcut_create_task", + "trigger_id": "944799105734.773906753841.38b5894552bdd4a780554ee59d1f3638", + "token": "XXXXXXXXXXXXX", + "team": { "id": "TXXXXXXXX", "domain": "shortcuts-test" }, + "user": { "id": "UXXXXXXXXX", "username": "aman", "team_id": "TXXXXXXXX" } +} +===== file mock-slack-interactions/message_shortcut.json ===== +{ + "type": "message_action", + "callback_id": "chirp_message", + "trigger_id": "13345224609.8534564800.6f8ab1f53e13d0cd15f96106292d5536", + "token": "Nj2rfC2hU8mAfgaJLemZgO7H", + "team": { "id": "T0MJRM1A7", "domain": "pandamonium" }, + "response_url": "https://hooks.slack.com/app-actions/T0MJR11A4/21974584944/yk1S9ndf35Q1flupVG5JbpM6", + "message": { + "user": "U0MJRG1AL", + "ts": "1516229207.000133", + "text": "World's smallest big cat! " + }, + "user": { "id": "U0D15K92L", "name": "dr_maomao" }, + "channel": { "id": "D0LFFBKLZ", "name": "cats" } +} diff --git a/lib_test/test.ml b/lib_test/test.ml index e0c9bab..a58291c 100644 --- a/lib_test/test.ml +++ b/lib_test/test.ml @@ -241,8 +241,31 @@ let process_events path = printf "failed to process slack event %s due to:\n%s\n" path (Printexc.to_string e); Lwt.return_unit +let mock_slack_interaction_dir = "mock-slack-interactions" + +let get_mock_slack_interactions () = + List.map (Filename.concat mock_slack_interaction_dir) (get_sorted_files_from mock_slack_interaction_dir) + +let process_interactions path = + Printf.printf "===== file %s =====\n" path; + try + let interaction = Slack_j.interaction_of_string (get_local_file path) in + let json = + interaction |> Slack_j.string_of_interaction |> Yojson.Basic.from_string |> Yojson.Basic.pretty_to_string + in + printf "%s\n" json; + Lwt.return_unit + with + | Yojson.Json_error e -> + printf "failed to parse slack interaction json %s due to: %s\n" path e; + Lwt.return_unit + | e -> + printf "failed to process slack interaction %s due to:\n%s\n" path (Printexc.to_string e); + Lwt.return_unit + let () = let slack_events = get_mock_slack_events () in + let slack_interactions = get_mock_slack_interactions () in Lwt_main.run (let%lwt () = Lwt_list.iter_s process_send_msg simple_text_msg_cases in let%lwt () = Lwt_list.iter_s process_send_msg_as_user text_msg_as_user_cases in @@ -257,5 +280,6 @@ let () = let%lwt () = Lwt_list.iter_s process_list_usergroup_users list_usergroup_users_usergroup_id_list in let%lwt () = Lwt_list.iter_s process_list_users list_users_cursors_list in let%lwt () = Lwt_list.iter_s process_events slack_events in + let%lwt () = Lwt_list.iter_s process_interactions slack_interactions in Lwt.return_unit ) From 0739aae58544d1d90833ccb8aa8ddf6d6fcf10aa Mon Sep 17 00:00:00 2001 From: koonwen Date: Mon, 7 Oct 2024 18:12:32 +0800 Subject: [PATCH 2/6] let users handle interaction type in the handler --- lib/utils.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/utils.ml b/lib/utils.ml index 080c91d..6dc2dfd 100644 --- a/lib/utils.ml +++ b/lib/utils.ml @@ -50,10 +50,10 @@ let process_slack_event (ctx : Context.t) headers body ~event_handler = let process_slack_interaction (ctx : Context.t) headers body ~interaction_handler = match interaction_of_string body with | exception Yojson.Json_error e -> Lwt.return_error (sprintf "Invalid interaction: %s" e) - | Shortcut i | Message_actions i -> + | interaction -> match validate_signature ?signing_key:ctx.secrets.slack_signing_secret ~headers body with | Error e -> Lwt.return_error (sprintf "signature not validated: %s" e) - | Ok () -> interaction_handler i + | Ok () -> interaction_handler interaction (***************** Utilities over Slack API returns *****************) From 6ee3841777cc27668a8c7c65579b455b60d51ade Mon Sep 17 00:00:00 2001 From: Koonwen Lee Date: Tue, 8 Oct 2024 14:19:37 +0800 Subject: [PATCH 3/6] parse body from x-www-form --- lib/utils.ml | 3 ++- lib_test/mock-slack-interactions/message_shortcut.www | 1 + lib_test/test.ml | 7 ++++++- 3 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 lib_test/mock-slack-interactions/message_shortcut.www diff --git a/lib/utils.ml b/lib/utils.ml index 6dc2dfd..bfdcffe 100644 --- a/lib/utils.ml +++ b/lib/utils.ml @@ -48,7 +48,8 @@ let process_slack_event (ctx : Context.t) headers body ~event_handler = ) let process_slack_interaction (ctx : Context.t) headers body ~interaction_handler = - match interaction_of_string body with + let payload = Uri.query_of_encoded body |> List.assoc "payload" |> List.hd in + match interaction_of_string payload with | exception Yojson.Json_error e -> Lwt.return_error (sprintf "Invalid interaction: %s" e) | interaction -> match validate_signature ?signing_key:ctx.secrets.slack_signing_secret ~headers body with diff --git a/lib_test/mock-slack-interactions/message_shortcut.www b/lib_test/mock-slack-interactions/message_shortcut.www new file mode 100644 index 0000000..2e7ce9e --- /dev/null +++ b/lib_test/mock-slack-interactions/message_shortcut.www @@ -0,0 +1 @@ +payload=%7B%22type%22%3A%22message_action%22%2C%22token%22%3A%22XXXXX%22%2C%22action_ts%22%3A%221728360716.833015%22%2C%22team%22%3A%7B%22id%22%3A%22T07MUBTEWGP%22%2C%22domain%22%3A%22testworkspace-jrm5644%22%7D%2C%22user%22%3A%7B%22id%22%3A%22U07NBD2PZ36%22%2C%22username%22%3A%22koonwen.lee%22%2C%22team_id%22%3A%22T07MUBTEWGP%22%2C%22name%22%3A%22koonwen.lee%22%7D%2C%22channel%22%3A%7B%22id%22%3A%22C07N62BSZ9B%22%2C%22name%22%3A%22social%22%7D%2C%22is_enterprise_install%22%3Afalse%2C%22enterprise%22%3Anull%2C%22callback_id%22%3A%22bc_create%22%2C%22trigger_id%22%3A%227846996185716.7742401506567.765fbc775e9236446b5284868c907fc7%22%2C%22response_url%22%3A%22https%3A%5C%2F%5C%2Fhooks.slack.com%5C%2Fapp%5C%2FT07MUBTEWGP%5C%2F7857176821457%5C%2FGjp1XOnzOCKndQZ47NQTT47i%22%2C%22message_ts%22%3A%221728287349.707269%22%2C%22message%22%3A%7B%22user%22%3A%22U07NBD2PZ36%22%2C%22type%22%3A%22message%22%2C%22ts%22%3A%221728287349.707269%22%2C%22client_msg_id%22%3A%22ad1b8ef7-55b2-4915-856e-eb970e7afa42%22%2C%22text%22%3A%22hello+i+need+some+help+this+is+a+long+message+that+will+be+shortened+by+slack+police%22%2C%22team%22%3A%22T07MUBTEWGP%22%2C%22blocks%22%3A%5B%7B%22type%22%3A%22rich_text%22%2C%22block_id%22%3A%22mQ1G5%22%2C%22elements%22%3A%5B%7B%22type%22%3A%22rich_text_section%22%2C%22elements%22%3A%5B%7B%22type%22%3A%22text%22%2C%22text%22%3A%22hello+i+need+some+help+this+is+a+long+message+that+will+be+shortened+by+slack+police%22%7D%5D%7D%5D%7D%5D%7D%7D \ No newline at end of file diff --git a/lib_test/test.ml b/lib_test/test.ml index a58291c..e09e41c 100644 --- a/lib_test/test.ml +++ b/lib_test/test.ml @@ -249,7 +249,12 @@ let get_mock_slack_interactions () = let process_interactions path = Printf.printf "===== file %s =====\n" path; try - let interaction = Slack_j.interaction_of_string (get_local_file path) in + let text = get_local_file path in + let interaction = + if Filename.check_suffix path "www" then + Uri.query_of_encoded text |> List.assoc "payload" |> List.hd |> Slack_j.interaction_of_string + else Slack_j.interaction_of_string (get_local_file path) + in let json = interaction |> Slack_j.string_of_interaction |> Yojson.Basic.from_string |> Yojson.Basic.pretty_to_string in From 0dbd1fb6398fece01fcaa0723a12450250592d91 Mon Sep 17 00:00:00 2001 From: Koonwen Lee Date: Mon, 14 Oct 2024 09:51:40 +0000 Subject: [PATCH 4/6] add other interaction types --- lib/slack.atd | 7 +- lib/utils.ml | 4 + .../block_action1.json | 49 +++++ .../block_action2.json | 98 +++++++++ .../mock-slack-interactions/view_closed.json | 13 ++ .../view_submission.json | 34 ++++ lib_test/slack_payloads.expected | 191 ++++++++++++++++++ 7 files changed, 393 insertions(+), 3 deletions(-) create mode 100644 lib_test/mock-slack-interactions/block_action1.json create mode 100644 lib_test/mock-slack-interactions/block_action2.json create mode 100644 lib_test/mock-slack-interactions/view_closed.json create mode 100644 lib_test/mock-slack-interactions/view_submission.json diff --git a/lib/slack.atd b/lib/slack.atd index 4a501b6..693915c 100644 --- a/lib/slack.atd +++ b/lib/slack.atd @@ -599,12 +599,13 @@ type slack_api_error = [ type 'ok slack_response = ('ok, slack_api_error) http_response + type interaction = [ | Shortcut of message_actions | Message_actions of message_actions -(* | Block_actions of *) -(* | View_submission of *) -(* | View_closed of *) +| Block_actions of json +| View_submission of json +| View_closed of json ] type message_actions = { diff --git a/lib/utils.ml b/lib/utils.ml index bfdcffe..d809284 100644 --- a/lib/utils.ml +++ b/lib/utils.ml @@ -47,6 +47,10 @@ let process_slack_event (ctx : Context.t) headers body ~event_handler = event_handler notification.event ) +(** [ process_slack_interaction ] handles slack interactions which are + similar to slack notifications except that they are specifically + for handling features such as block actions, shortcuts and modals +*) let process_slack_interaction (ctx : Context.t) headers body ~interaction_handler = let payload = Uri.query_of_encoded body |> List.assoc "payload" |> List.hd in match interaction_of_string payload with diff --git a/lib_test/mock-slack-interactions/block_action1.json b/lib_test/mock-slack-interactions/block_action1.json new file mode 100644 index 0000000..184c3b7 --- /dev/null +++ b/lib_test/mock-slack-interactions/block_action1.json @@ -0,0 +1,49 @@ +{ + "type": "block_actions", + "team": { + "id": "T9TK3CUKW", + "domain": "example" + }, + "user": { + "id": "UA8RXUSPL", + "username": "jtorrance", + "team_id": "T9TK3CUKW" + }, + "api_app_id": "AABA1ABCD", + "token": "9s8d9as89d8as9d8as989", + "container": { + "type": "message_attachment", + "message_ts": "1548261231.000200", + "attachment_id": 1, + "channel_id": "CBR2V3XEX", + "is_ephemeral": false, + "is_app_unfurl": false + }, + "trigger_id": "12321423423.333649436676.d8c1bb837935619ccad0f624c448ffb3", + "channel": { + "id": "CBR2V3XEX", + "name": "review-updates" + }, + "message": { + "bot_id": "BAH5CA16Z", + "type": "message", + "text": "This content can't be displayed.", + "user": "UAJ2RU415", + "ts": "1548261231.000200" + }, + "response_url": "https://hooks.slack.com/actions/AABA1ABCD/1232321423432/D09sSasdasdAS9091209", + "actions": [ + { + "action_id": "WaXA", + "block_id": "=qXel", + "text": { + "type": "plain_text", + "text": "View", + "emoji": true + }, + "value": "click_me_123", + "type": "button", + "action_ts": "1548426417.840180" + } + ] +} diff --git a/lib_test/mock-slack-interactions/block_action2.json b/lib_test/mock-slack-interactions/block_action2.json new file mode 100644 index 0000000..6e90094 --- /dev/null +++ b/lib_test/mock-slack-interactions/block_action2.json @@ -0,0 +1,98 @@ +{ + "type":"block_actions", + "team":{ + "id": "T9TK3CUKW", + "domain": "example" + }, + "user":{ + "id": "UA8RXUSPL", + "username": "jtorrance", + "name": "jtorrance", + "team_id": "T9TK3CUKW" + }, + "api_app_id":"AABA1ABCD", + "token":"9s8d9as89d8as9d8as989", + "container":{ + "type":"view", + "view_id":"V0PKB1ZFV" + }, + "trigger_id":"24571818370.22717085937.b9c7ca14b87be6b44ff5864edba8306f", + "view":{ + "id":"V0PKB1ZFV", + "team_id":"T9TK3CUKW", + "type":"home", + "blocks":[ + { + "type":"section", + "block_id":"8ZG", + "text":{ + "type":"mrkdwn", + "text":"A stack of blocks for the sample Block Kit Home tab.", + "verbatim":false + } + }, + { + "type":"actions", + "block_id":"7fhg", + "elements":[ + { + "type":"button", + "action_id":"XRX", + "text":{ + "type":"plain_text", + "text":"Action A", + "emoji":true + } + }, + { + "type":"button", + "action_id":"GFBew", + "text":{ + "type":"plain_text", + "text":"Action B", + "emoji":true + } + } + ] + }, + { + "type":"section", + "block_id":"6evU", + "text":{ + "type":"mrkdwn", + "text":"And now it's slightly more complex.", + "verbatim":false + } + } + ], + "private_metadata":"", + "callback_id":"", + "state":{ + "values":{} + }, + "hash":"1571318366.2468e46f", + "clear_on_close":false, + "notify_on_close":false, + "close":null, + "submit":null, + "previous_view_id":null, + "root_view_id":"V0PKB1ZFV", + "app_id":"AABA1ABCD", + "external_id":"", + "app_installed_team_id":"T9TK3CUKW", + "bot_id":"B0B00B00" + }, + "actions":[ + { + "type":"button", + "block_id":"7fhg", + "action_id":"XRX", + "text":{ + "type":"plain_text", + "text":"Action A", + "emoji":true + }, + "action_ts":"1571318425.267782" + } + ] +} diff --git a/lib_test/mock-slack-interactions/view_closed.json b/lib_test/mock-slack-interactions/view_closed.json new file mode 100644 index 0000000..381068a --- /dev/null +++ b/lib_test/mock-slack-interactions/view_closed.json @@ -0,0 +1,13 @@ +{ + "type": "view_closed", + "team": { + "id": "TXXXXXX", + "domain": "coverbands" + }, + "user": { + "id": "UXXXXXX", + "name": "dreamweaver" + }, + "api_app_id": "AXXXXXX", + "is_cleared": false +} diff --git a/lib_test/mock-slack-interactions/view_submission.json b/lib_test/mock-slack-interactions/view_submission.json new file mode 100644 index 0000000..a133a08 --- /dev/null +++ b/lib_test/mock-slack-interactions/view_submission.json @@ -0,0 +1,34 @@ +{ + "type": "view_submission", + "view": { + "id": "VNHU13V36", + "type": "modal", + "private_metadata": "shhh-its-secret", + "callback_id": "modal-with-inputs", + "state": { + "values": { + "multiline": { + "mlvalue": { + "type": "plain_text_input", + "value": "This is my example inputted value" + } + }, + "target_channel": { + "target_select": { + "type": "conversations_select", + "selected_conversation": "C123B12DE" + } + } + } + }, + "hash": "156663117.cd33ad1f", + "response_urls": [ + { + "block_id": "target_channel", + "action_id": "target_select", + "channel_id": "C123B12DE", + "response_url": "https:\/\/hooks.slack.com\/app\/ABC12312\/1234567890\/A100B100C100d100" + } + ] + } +} diff --git a/lib_test/slack_payloads.expected b/lib_test/slack_payloads.expected index 046e1aa..bd78a62 100644 --- a/lib_test/slack_payloads.expected +++ b/lib_test/slack_payloads.expected @@ -724,6 +724,133 @@ listing at cursor #dXNlcjpVMDQ2WE4wTTJSNQ==... "event_id": "Ev04C7PRDB53", "event_time": 1669265351 } +===== file mock-slack-interactions/block_action1.json ===== +{ + "type": "block_actions", + "team": { "id": "T9TK3CUKW", "domain": "example" }, + "user": { + "id": "UA8RXUSPL", + "username": "jtorrance", + "team_id": "T9TK3CUKW" + }, + "api_app_id": "AABA1ABCD", + "token": "9s8d9as89d8as9d8as989", + "container": { + "type": "message_attachment", + "message_ts": "1548261231.000200", + "attachment_id": 1, + "channel_id": "CBR2V3XEX", + "is_ephemeral": false, + "is_app_unfurl": false + }, + "trigger_id": "12321423423.333649436676.d8c1bb837935619ccad0f624c448ffb3", + "channel": { "id": "CBR2V3XEX", "name": "review-updates" }, + "message": { + "bot_id": "BAH5CA16Z", + "type": "message", + "text": "This content can't be displayed.", + "user": "UAJ2RU415", + "ts": "1548261231.000200" + }, + "response_url": "https://hooks.slack.com/actions/AABA1ABCD/1232321423432/D09sSasdasdAS9091209", + "actions": [ + { + "action_id": "WaXA", + "block_id": "=qXel", + "text": { "type": "plain_text", "text": "View", "emoji": true }, + "value": "click_me_123", + "type": "button", + "action_ts": "1548426417.840180" + } + ] +} +===== file mock-slack-interactions/block_action2.json ===== +{ + "type": "block_actions", + "team": { "id": "T9TK3CUKW", "domain": "example" }, + "user": { + "id": "UA8RXUSPL", + "username": "jtorrance", + "name": "jtorrance", + "team_id": "T9TK3CUKW" + }, + "api_app_id": "AABA1ABCD", + "token": "9s8d9as89d8as9d8as989", + "container": { "type": "view", "view_id": "V0PKB1ZFV" }, + "trigger_id": "24571818370.22717085937.b9c7ca14b87be6b44ff5864edba8306f", + "view": { + "id": "V0PKB1ZFV", + "team_id": "T9TK3CUKW", + "type": "home", + "blocks": [ + { + "type": "section", + "block_id": "8ZG", + "text": { + "type": "mrkdwn", + "text": "A stack of blocks for the sample Block Kit Home tab.", + "verbatim": false + } + }, + { + "type": "actions", + "block_id": "7fhg", + "elements": [ + { + "type": "button", + "action_id": "XRX", + "text": { + "type": "plain_text", + "text": "Action A", + "emoji": true + } + }, + { + "type": "button", + "action_id": "GFBew", + "text": { + "type": "plain_text", + "text": "Action B", + "emoji": true + } + } + ] + }, + { + "type": "section", + "block_id": "6evU", + "text": { + "type": "mrkdwn", + "text": "And now it's slightly more complex.", + "verbatim": false + } + } + ], + "private_metadata": "", + "callback_id": "", + "state": { "values": {} }, + "hash": "1571318366.2468e46f", + "clear_on_close": false, + "notify_on_close": false, + "close": null, + "submit": null, + "previous_view_id": null, + "root_view_id": "V0PKB1ZFV", + "app_id": "AABA1ABCD", + "external_id": "", + "app_installed_team_id": "T9TK3CUKW", + "bot_id": "B0B00B00" + }, + "actions": [ + { + "type": "button", + "block_id": "7fhg", + "action_id": "XRX", + "text": { "type": "plain_text", "text": "Action A", "emoji": true }, + "action_ts": "1571318425.267782" + } + ] +} ===== file mock-slack-interactions/global_shortcut.json ===== { "type": "shortcut", @@ -749,3 +876,67 @@ listing at cursor #dXNlcjpVMDQ2WE4wTTJSNQ==... "user": { "id": "U0D15K92L", "name": "dr_maomao" }, "channel": { "id": "D0LFFBKLZ", "name": "cats" } } +===== file mock-slack-interactions/message_shortcut.www ===== +{ + "type": "message_action", + "callback_id": "bc_create", + "trigger_id": "7846996185716.7742401506567.765fbc775e9236446b5284868c907fc7", + "token": "XXXXX", + "team": { "id": "T07MUBTEWGP", "domain": "testworkspace-jrm5644" }, + "response_url": "https://hooks.slack.com/app/T07MUBTEWGP/7857176821457/Gjp1XOnzOCKndQZ47NQTT47i", + "message": { + "user": "U07NBD2PZ36", + "ts": "1728287349.707269", + "text": "hello i need some help this is a long message that will be shortened by slack police" + }, + "user": { + "id": "U07NBD2PZ36", + "name": "koonwen.lee", + "username": "koonwen.lee", + "team_id": "T07MUBTEWGP" + }, + "channel": { "id": "C07N62BSZ9B", "name": "social" } +} +===== file mock-slack-interactions/view_closed.json ===== +{ + "type": "view_closed", + "team": { "id": "TXXXXXX", "domain": "coverbands" }, + "user": { "id": "UXXXXXX", "name": "dreamweaver" }, + "api_app_id": "AXXXXXX", + "is_cleared": false +} +===== file mock-slack-interactions/view_submission.json ===== +{ + "type": "view_submission", + "view": { + "id": "VNHU13V36", + "type": "modal", + "private_metadata": "shhh-its-secret", + "callback_id": "modal-with-inputs", + "state": { + "values": { + "multiline": { + "mlvalue": { + "type": "plain_text_input", + "value": "This is my example inputted value" + } + }, + "target_channel": { + "target_select": { + "type": "conversations_select", + "selected_conversation": "C123B12DE" + } + } + } + }, + "hash": "156663117.cd33ad1f", + "response_urls": [ + { + "block_id": "target_channel", + "action_id": "target_select", + "channel_id": "C123B12DE", + "response_url": "https://hooks.slack.com/app/ABC12312/1234567890/A100B100C100d100" + } + ] + } +} From 6b74e38d31a364cffd7eb5d5df6c64a930575cc7 Mon Sep 17 00:00:00 2001 From: Lee Koon Wen Date: Tue, 15 Oct 2024 12:41:19 +0800 Subject: [PATCH 5/6] Apply suggestions from code review Co-authored-by: Louis --- lib/utils.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/utils.ml b/lib/utils.ml index d809284..50df323 100644 --- a/lib/utils.ml +++ b/lib/utils.ml @@ -47,7 +47,7 @@ let process_slack_event (ctx : Context.t) headers body ~event_handler = event_handler notification.event ) -(** [ process_slack_interaction ] handles slack interactions which are +(** [process_slack_interaction] handles slack interactions which are similar to slack notifications except that they are specifically for handling features such as block actions, shortcuts and modals *) From a012e93da88c49436c765ab382b30b19bc288c3b Mon Sep 17 00:00:00 2001 From: Koonwen Lee Date: Tue, 15 Oct 2024 04:47:29 +0000 Subject: [PATCH 6/6] remove List.hd --- lib/utils.ml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/utils.ml b/lib/utils.ml index 50df323..fcf9d36 100644 --- a/lib/utils.ml +++ b/lib/utils.ml @@ -52,7 +52,9 @@ let process_slack_event (ctx : Context.t) headers body ~event_handler = for handling features such as block actions, shortcuts and modals *) let process_slack_interaction (ctx : Context.t) headers body ~interaction_handler = - let payload = Uri.query_of_encoded body |> List.assoc "payload" |> List.hd in + match Uri.query_of_encoded body |> List.assoc "payload" with + | [] -> Lwt.return_error "Empty payload" + | payload :: _ -> match interaction_of_string payload with | exception Yojson.Json_error e -> Lwt.return_error (sprintf "Invalid interaction: %s" e) | interaction ->