Packages

Handle Slack slash command and interactive message HTTP requests in Elixir

Current section

Files

Jump to
slack_interactive lib phoenix action_controller.ex
Raw

lib/phoenix/action_controller.ex

defmodule Slack.Phoenix.ActionController do
@moduledoc """
This is a helper module to use with a Phoenix controller to
handle HTTP requests coming from Slack for Slash Commands or
actions from Interactive Messages.
You must provide a Slack token for the `__using__/1` macro that
this module will use to verify incoming Slack requests. See the
[Slack API docs] to obtain the token.
## Example
```
defmodule App.SlackController do
use Slack.Phoenix.ActionController, token: "SLACK_TOKEN"
import Plug.Conn
def handle_action(action, conn, slack) do
conn
|> put_status(200)
|> text("Working on this action")
end
def handle_command(command, conn, slack) do
conn
|> put_status(200)
|> text("Working on this command")
end
end
defmodule App.Router do
use Phoenix.Router
post "/slack/*path", App.SlackController, :dispatch
end
```
## Handling Requests
Define a route in your Phoenix router with the `:dispatch` action
to validate incoming requests and dispatch to the overridable
functions `handle_action` or `handle_command`.
If an incoming request contains a matching validation token, either
`handle_action` or `handle_command` will be called. Override these
functions in your controller to manipulate the validated Slack data
and respond to the request. The following arugments are passed to
these functions:
* `action` or `command`: Either the action `map` or command `string`
from the request.
* `conn`: The incoming request as a `Plug.Conn`.
* `slack`: The incoming request parameters converted to a `Slack`
struct.
A `Plug.Conn` is expected as the return value for these functions.
## Slack Argument
The third argument passed to the handle functions contains the
below fields. The default value of these fields is `nil` if the
incoming request did not contain a matching field name.
* action - map with :name and :value for action request (action
request only)
* command - slash command string (command request only)
* text - the text string following the slash command (command
request only)
* team - map with :domain and :id for Slack team
* channel - map with :name and :id for Slack channel
* user - map with :name and :id for Slack user
* token - validation token used to confirm request came from Slack
* reponse_url - string containing URL for delayed response to request
Action request only fields (nil for command requests)
* callback_id - string of callback_id from message attachment
* action_ts - string timestamp when action occurred
* message_ts - string timestamp when message containing action
was posted
* attachment_id - string id for specific attachment within message
* original_message - original message JSON object
See Slack docs for [responding to button actions] and [slash commands]
[Slack API docs]: https://api.slack.com/
[responding to button actions]: https://api.slack.com/docs/message-buttons
[slash commands]: https://api.slack.com/slash-commands
"""
defmacro __using__([token: token]) do
quote do
use Phoenix.Controller
plug Slack.Plug.FetchSlackData
plug Slack.Plug.VerifyToken, token: unquote(token)
@spec dispatch(Plug.Conn.t, map) :: Plug.Conn.t
def dispatch(%{private: %{slack_data: slack}} = conn, _),
do: dispatch_slack(conn, slack)
defp dispatch_slack(conn, nil),
do: put_status(conn, 400)
defp dispatch_slack(conn, %{action: action} = slack) when action != nil,
do: apply(__MODULE__, :handle_action, [action, conn, slack])
defp dispatch_slack(conn, %{command: command} = slack) when command != nil,
do: apply(__MODULE__, :handle_command, [command, conn, slack])
@spec handle_action(map, Plug.Conn.t, Slack.t) :: Plug.Conn.t
def handle_action(_action, conn, _slack), do: conn
@spec handle_command(binary, Plug.Conn.t, Slack.t) :: Plug.Conn.t
def handle_command(_command, conn, _slack), do: conn
defoverridable [handle_action: 3, handle_command: 3]
end
end
end