Slack

TestsHex.pmHex.pmHex.pm

This is for creating Slack applications or bots in Elixir.

Why?

The existing libraries I was looking at use the deprecated RTM API, and no longer work with new apps or bots.

What?

To listen for subscribed events, it uses Socket Mode to connect to Slack. It has some pros and cons, so please read up on it (and pay attention to the info blocks).

It's a relatively thin wrapper, which keeps it flexible and easy to maintain.

How

Installation

Add slack_elixir to your list of dependencies in mix.exs:

def deps do
  [
    {:slack_elixir, "~> 1.2.1"}
  ]
end

Setup

The easiest way to get started is to create a new Slack app and use the following App Manifest to configure all the permissions that are needed:

display_information:
  name: YOURBOTNAME
features:
  bot_user:
    display_name: YOURBOTNAME
    always_online: true
oauth_config:
  scopes:
    bot:
      - channels:history
      - channels:read
      - groups:read
      - im:read
      - mpim:read
      - chat:write
settings:
  event_subscriptions:
    bot_events:
      - message.channels
      - member_joined_channel
      - channel_left
  interactivity:
    is_enabled: true
  org_deploy_enabled: false
  socket_mode_enabled: true
  token_rotation_enabled: false

Manual setup

You will need to:

See below for some minimum required scopes and event subscriptions. You will need to add more scopes and subscriptions depending on what you want to do.

Required Bot Token scopes:

Required Bot Event Subscriptions

Usage

Write the Bot module:

defmodule MyApp.Slackbot do
  use Slack.Bot

  require Logger

  @impl true
  # A silly example of old-school style bot commands.
  def handle_event("message", %{"text" => "!" <> cmd, "channel" => channel, "user" => user}, _bot) do
    case cmd do
      "roll" ->
        send_message(channel, "<@#{user}> rolled a #{Enum.random(1..6)}")

      "echo " <> text ->
        send_message(channel, text)

      _ ->
        send_message(channel, "Unknown command: #{cmd}")
    end
  end

  def handle_event("message", %{"channel" => channel, "text" => text, "user" => user}, _bot) do
    if String.match?(text, ~r/hello/i) do
      send_message(channel, "Hello! <@#{user}>")
    end
  end

  def handle_event(type, payload, _bot) do
    Logger.debug("Unhandled #{type} event: #{inspect(payload)}")
    :ok
  end
end

Then you start the Slack Supervisor in your application's supervision tree.

For example:

  def start(_type, _args) do
    children = [
      # ...
      {Slack.Supervisor, Application.fetch_env!(:my_app, MyApp.SlackBot)}
    ]

    opts = [strategy: :one_for_one, name: MyApp.Supervisor]
    Supervisor.start_link(children, opts)
  end
# config/runtime.exs

config :my_app, MyApp.SlackBot,
  app_token: "MY_SLACK_APP_TOKEN",
  bot_token: "MY_SLACK_BOT_TOKEN",
  bot: MyApp.SlackBot,
  # Add this if you want to customize the channel types to join.
  # By default we join all channel types: public_channel, private_channel, im, mpim.
  channels: [
    types: ["public_channel", "im", "private_channel"]
  ]

Journey to v1.0 (Things that may or may not be added)

PRs welcome!