Packages

A Slack Web & Real Time Messaging API client.

Current section

Files

Jump to
slack_kit lib slack.ex
Raw

lib/slack.ex

defmodule Slack do
@moduledoc """
Defines the behaviour for a Slack [Real Time Messaging] (RTM) bot.
`use Slack` injects four overridable callbacks — `handle_connect/2`,
`handle_event/3`, `handle_close/3`, `handle_info/3` — plus a default
`child_spec/1` so the module can be supervised, and imports the helper
functions in `Slack.Lookups` and `Slack.Sends`. `Slack.Bot.start_link/4`
takes the resulting module, opens the WebSocket, and dispatches incoming
events to it.
## Quick start
Define your bot:
defmodule MyBot do
use Slack
def handle_connect(slack, state) do
IO.puts("Connected as @\#{slack.me.name} to \#{slack.team.name}")
{:ok, state}
end
# Greet anyone who says "hi" in any channel the bot can see.
def handle_event(%{type: "message", text: "hi"} = msg, slack, state) do
send_message("Hello to you too!", msg.channel, slack)
{:ok, state}
end
# Catch-all — Slack sends many event types; ignoring unknown ones keeps
# the bot from crashing on each new flavour.
def handle_event(_event, _slack, state), do: {:ok, state}
end
Start it under your supervision tree:
children = [
{MyBot, []}
]
Supervisor.start_link(children, strategy: :one_for_one)
Or, for ad-hoc use:
{:ok, _pid} = Slack.Bot.start_link(MyBot, [], "xoxb-…")
See [Token Generation Instructions](token_generation_instructions.html) for
obtaining a token.
## Callbacks
All four callbacks are optional; the default implementations ignore their
input and return the unchanged state.
| Callback | When it fires |
| --------------------------------- | -------------------------------------------------------------------------- |
| `handle_connect(slack, state)` | The WebSocket has connected and the bot identity is known. |
| `handle_event(event, slack, state)` | An RTM event arrived from Slack. `event` is an atom-keyed map. |
| `handle_close(reason, slack, state)` | The WebSocket closed — return `:close` to stop, or `{:reconnect, state}`. |
| `handle_info(msg, slack, state)` | An arbitrary Erlang message arrived in the bot's mailbox. |
Each callback returns `{:ok, state}` (with the exception of `handle_close/3`
which may also return `:close`). The returned state is threaded into the
next callback invocation, so you can carry arbitrary bot state across events.
## The `slack` argument
Every callback receives a `Slack.State` struct that tracks the live view of
the workspace. SlackKit folds each incoming RTM event into it before invoking
your callback, so `slack.channels`, `slack.users`, and friends always reflect
the latest known state. See `Slack.State` for the field layout.
## Sending messages
Inside a callback, `send_message/3` (imported from `Slack.Sends`) writes a
text message back through the WebSocket:
send_message("Hello!", "C123ABC", slack)
For richer messages — attachments, blocks, threads, ephemeral posts — use
the corresponding Web API function under `Slack.Web.Chat`.
## Driving the bot from outside
Anything sent to the bot's process arrives in `handle_info/3`, which is the
natural entry point for "send this message from elsewhere in my app":
def handle_info({:say, text, channel}, slack, state) do
send_message(text, channel, slack)
{:ok, state}
end
# …then, from a controller, a job, IEx, etc.
send(bot_pid, {:say, "External hello", "#general"})
[Real Time Messaging]: https://api.slack.com/rtm
"""
defmacro __using__(_) do
quote do
import Slack
import Slack.Lookups
import Slack.Sends
def handle_connect(_slack, state), do: {:ok, state}
def handle_event(_message, _slack, state), do: {:ok, state}
def handle_close(_reason, _slack, state), do: :close
def handle_info(_message, _slack, state), do: {:ok, state}
def child_spec(_opts) do
%{
id: __MODULE__,
start: {__MODULE__, :start_link, []},
type: :worker,
restart: :permanent,
shutdown: 500
}
end
defoverridable handle_connect: 2,
handle_event: 3,
handle_close: 3,
handle_info: 3,
child_spec: 1
end
end
end