# Slack


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

 - connects to Slack using a websocket connection to listen for your event subscriptions.
 - uses the [Web API]( to send messages, etc.
 - uses dynamically supervised gen servers to handle each channel's message rate-limiting with a message queue
   per channel.

## Installation

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

def deps do
    {:slack_elixir, "~> 1.2.0"}

## Setup

You will need to:

  - Create a Slack app for your workspace
  - Add permissions (scopes)
  - Connect it to your workspace
  - Get an OAuth Bot Token (will have the scopes you defined)
  - Enable Socket Mode
  - Get an app-level token with `connections:write` scope
  - Add Event Subscriptions

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:
 - `channels:history`
 - `channels:read`
 - `groups:read`
 - `im:read`
 - `mpim:read`

#### Required Bot Event Subscriptions
 - `message.channels`
 - `member_joined_channel`
 - `channel_left`

## 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}")

  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}>")

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

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)

# 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!

- [x] **Socket Mode** for events
- [x] Web API POST requests
- [x] Web API GET requests
- [x] Message Server per channel (rate-limited to 1 message per second per channel).