# 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.0.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`
 - `mpim:read`
 - `im: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" => "!" <> command, "channel" => channel, "user" => user}) do
    case command do
      "roll" ->
        send_message(channel, "<@#{user}> rolled a #{Enum.random(1..6)}")

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

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

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

  def handle_event(type, payload) 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
    # Often, you'd fetch this from application env,
    # set in `config/runtime.exs`, instead of like this.
    config = [
      app_token: "MY_SLACK_APP_TOKEN",
      bot_token: "MY_SLACK_BOT_TOKEN",
      bot: MyApp.SlackBot

    children = [
      # ...
      {Slack.Supervisor, config}

    opts = [strategy: :one_for_one, name: MyApp.Supervisor]
    Supervisor.start_link(children, opts)

## 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).