defmodule Slug do @moduledoc """ The slug specification. Slugs are like [Plug](https://github.com/elixir-plug/plug)s, but for Slack. What do slugs do? They... * Receive events from Slack. * Perform any actions they desire as a reaction to the event. For example, sending a message, or adding a reaction. * Optionally annotate the event they received with metadata that may be helpful to other slugs. * Return the event, passing it on to the next slug in the pipeline. In addition to the documentation below, check out the bundled slugs and associated tests under the `Slug.Common` namespace for examples. ## The Slug Pipeline Many slugs are strung together inside of a `Slug.Bot` module to build a slug pipeline. A bot's pipeline of slugs defines how a bot reacts to events coming from the Slack [RTM API](https://api.slack.com/rtm). ## Kinds of Slugs There are two kind of slugs: function slugs and module slugs. ### Function slugs A function slug is any function that receives a `Slug.Event` and returns a `Slug.Event` or `:halt`. Its type signature must be: (Slug.Event.t) :: Slug.Event.t ### Module slugs A module slug is an extension of the function slug. The API expected by a module slug is defined as a behaviour by the `Slug` module (this module), specifically the export of `c:call/2`. ## Examples ### Function Slug Here's an example of a function slug that logs out the event it receives. Later slugs may like to know that an inspection occurred, so this slug also makes a note in the metadata that it inspected the event. ``` def simple_reply(%Slug.Event{data: %{user: user_id, channel: channel_id}} = event) do IO.inspect("Event from \#{user_id} in channel \#{channel_id}: \#{inspect(event)}") event |> Event.add_metadata(:got_inspected, true) end ``` ### Module Slug Here's an example of a module slug that checks if the bot was mentioned in a Slack message and annotates the `Slug.Event`'s metadata accordingly. ``` defmodule Slug.Common.CheckMentioned do @behaviour Slug def call(event, _bot) do %Slug.Event{ bot_id: bot_id, data: %{text: message}, metadata: %{bot_name: bot_name} } = event is_mentioned = Regex.match?(~r/.*(<@\#{bot_id}>|@\#{bot_name}).*/, message) event |> Slug.Event.add_metadata(:mentioned, is_mentioned) end end ``` ## Don't forget the catch-all If your slug function, or module, uses pattern matching in its clause you probably want to provide a catch-all clause that passes on any event that is not recognized. ``` def simple_slug(%Slug.Event{data: %{user: "UTEST"}} = event) do IO.puts("The test user produced an event") event end #Ignore and forward any events that don't match the clause above def simple_slug(event), do: event ``` If you actually want to halt the progress of the pipeline, return `:halt` from your slug. """ @doc """ Takes a `Slug.Event` and the `Slug.Bot` module that received the event. Must return a `Slug.Event` or `:halt`. Returning `:halt` will abort the pipeline execution, and subsequent slugs in the pipeline will not be run. """ @callback call(event :: Slug.Event.t(), bot :: Slug.Bot.t()) :: Slug.Event.t() | :halt end