slackline/app

A router for Slack apps. Register handlers for slash commands, events, block actions, shortcuts, view submissions and options, then pass it HTTP requests or Socket Mode frames.

let app =
  app.new(signing_secret)
  |> app.command("/feedback", fn(command) {
    use <- app.ack_then
    views.open(view: feedback_modal())
    |> open.trigger_id(command.trigger_id)
    |> httpc.send(client)
  })
  |> app.view_submission("feedback", fn(submission) {
    let text =
      view.input_text(submission.view, block_id: "thoughts", action_id: "thoughts")
    case text {
      Some("") | None -> app.errors([#("thoughts", "say something!")])
      Some(_) -> app.ack()
    }
  })
  |> app.action("preview", fn(_action, _payload) {
    app.reply(interaction.ephemeral("Previewing"))
  })
  |> app.on_event(fn(event, _delivery) {
    case event {
      event.AppMention(..) -> todo
      _ -> Nil
    }
  })

// in your HTTP server, with the raw body:
let handled = app.handle(app, request)
// send handled.response, then call handled.after()

Slack expects an acknowledgement within three seconds. Handlers return an Ack at once. Put slow work, such as API calls, in a continuation with app.ack_then or app.then. handle and handle_socket return it as after, and the server runs it once the acknowledgement is sent. Event handlers always run in after, so a server that skips after never runs them.

Everything here is synchronous and does no IO of its own, so it works on both targets and with any HTTP server or WebSocket client.

Types

How an app acknowledges a request: an optional body, and work to run after the acknowledgement is sent.

pub opaque type Ack
pub opaque type App

Where an event came from. retry is set when Slack sends the event again. Use event_id to skip events you already handled.

pub type Delivery {
  Delivery(
    team_id: option.Option(String),
    enterprise_id: option.Option(String),
    api_app_id: option.Option(String),
    event_id: option.Option(String),
    event_time: option.Option(Int),
    authorizations: List(event.EnvelopeAuthorization),
    is_ext_shared_channel: option.Option(Bool),
    retry: option.Option(Retry),
  )
}

Constructors

The response to send back to Slack, and the work to run once it is sent. after does nothing when no handler asked for follow-up work.

pub type Handled {
  Handled(
    response: response.Response(String),
    after: fn() -> Nil,
  )
}

Constructors

A request from Slack, decoded.

pub type Incoming {
  Events(event.Envelope)
  Interactive(interaction.Interaction)
  Command(command.SlashCommand)
  SslCheck
}

Constructors

Why a request could not be handled.

pub type RequestError {
  NotVerified(verify.VerifyError)
  Malformed(String)
}

Constructors

  • NotVerified(verify.VerifyError)
  • Malformed(String)

    The body was not something Slack sends.

Slack sends an event again when your app did not acknowledge it in time. num counts the retries and reason says why, such as http_timeout.

pub type Retry {
  Retry(num: Int, reason: String)
}

Constructors

  • Retry(num: Int, reason: String)

What to do after a Socket Mode frame.

pub type SocketOutcome {
  Send(frame: String, after: fn() -> Nil)
  Reconnect(socket_mode.DisconnectReason)
  Ignore
}

Constructors

  • Send(frame: String, after: fn() -> Nil)

    Send this text frame back to acknowledge the message, then run after.

  • Slack is closing this connection: open a new one with apps.connections.open.

  • Ignore

    Nothing to send, as after a hello.

Values

pub fn ack() -> Ack

Acknowledge with an empty body. Right for events and for most interactions.

pub fn ack_json(body: json.Json) -> Ack

Acknowledge with any json body.

pub fn ack_then(after: fn() -> a) -> Ack

Acknowledge with an empty body, then run after once the acknowledgement is sent. Made for use:

app.command("/deploy", fn(command) {
  use <- app.ack_then
  deploy(command.text)
})
pub fn action(
  app: App,
  action_id: String,
  handler: fn(
    interaction.BlockAction,
    interaction.BlockActionsPayload,
  ) -> Ack,
) -> App

Handles block_actions for elements with this action_id. The handler gets the action that matched and the whole payload.

pub fn block_suggestion(
  app: App,
  action_id: String,
  handler: fn(interaction.BlockSuggestionPayload) -> Ack,
) -> App

Handles block_suggestion requests for external selects with this action_id.

pub fn clear() -> Ack

Acknowledge a view_submission by closing every view in the stack.

pub fn command(
  app: App,
  name: String,
  handler: fn(command.SlashCommand) -> Ack,
) -> App

Handles a slash command, such as "/hello".

pub fn dispatch(
  app: App,
  incoming: Incoming,
  retry: option.Option(Retry),
) -> Ack

Runs the handlers for something that already arrived.

pub fn errors(errors: List(#(String, String))) -> Ack

Acknowledge a view_submission by showing errors on its input blocks, given as #(block_id, message) pairs.

pub fn global_shortcut(
  app: App,
  callback_id: String,
  handler: fn(interaction.GlobalShortcutPayload) -> Ack,
) -> App

Handles global shortcuts with this callback_id.

pub fn handle(
  app: App,
  request: request.Request(String),
) -> Handled

Verifies, decodes and dispatches an HTTP request, and builds the response to send back to Slack. Send response, then run after.

pub fn handle_socket(app: App, frame: String) -> SocketOutcome

Decodes and dispatches one Socket Mode text frame.

pub fn message_shortcut(
  app: App,
  callback_id: String,
  handler: fn(interaction.MessageShortcutPayload) -> Ack,
) -> App

Handles message shortcuts with this callback_id.

pub fn new(signing_secret: String) -> App

An app that verifies every HTTP request with your signing secret.

pub fn on_event(
  app: App,
  handler: fn(event.Event, Delivery) -> a,
) -> App

Handles every Events API event. You can add several handlers. They run in the order you added them, after the event is acknowledged.

pub fn on_interaction(
  app: App,
  handler: fn(interaction.Interaction) -> Ack,
) -> App

Handles any interaction that no other handler matched, such as a legacy dialog or interactive message. A second call replaces the first handler.

pub fn option_groups(groups: List(block.OptionGroup)) -> Ack

Acknowledge a block_suggestion with groups of options to show.

pub fn options(options: List(block.SelectOption)) -> Ack

Acknowledge a block_suggestion with the options to show.

pub fn parse_request(
  request: request.Request(String),
) -> Result(Incoming, RequestError)

Decodes an HTTP request body from Slack. It does not check the signature. handle does.

pub fn push(view: view.View) -> Ack

Acknowledge a view_submission by pushing a view onto the stack.

pub fn reply(message: interaction.ResponseMessage) -> Ack

Acknowledge a slash command, shortcut or block action with a message, as if posted to its response_url.

pub fn then(ack: Ack, after: fn() -> a) -> Ack

Runs after once this acknowledgement is sent, after any work it already had.

pub fn unverified() -> App

An app that does not verify HTTP requests. Use it for Socket Mode, where the connection itself is authenticated, and in tests.

pub fn update(view: view.View) -> Ack

Acknowledge a view_submission by replacing the submitted view.

pub fn view_closed(
  app: App,
  callback_id: String,
  handler: fn(interaction.ViewClosedPayload) -> Ack,
) -> App

Handles view_closed for views with this callback_id (sent when the view set notify_on_close).

pub fn view_response(
  response: interaction.ViewSubmissionResponse,
) -> Ack

Acknowledge a view_submission with a response_action: show errors, update or push a view, or clear the stack.

pub fn view_submission(
  app: App,
  callback_id: String,
  handler: fn(interaction.ViewSubmissionPayload) -> Ack,
) -> App

Handles view_submission for views with this callback_id. Answer with ack, errors, update, push or clear.

✨ Search Document