defmodule Slack.State do @moduledoc """ Slack state. """ @behaviour Access def fetch(client, key) defdelegate fetch(client, key), to: Map def get(client, key, default) defdelegate get(client, key, default), to: Map def get_and_update(client, key, function) defdelegate get_and_update(client, key, function), to: Map def pop(client, key) defdelegate pop(client, key), to: Map defstruct [ :process, :client, :token, :me, :team, bots: %{}, channels: %{}, groups: %{}, users: %{}, ims: %{} ] @type t :: %__MODULE__{ process: pid() | nil, client: module() | nil, token: String.t() | nil, me: map() | nil, team: map() | nil, bots: map(), channels: map(), groups: map(), users: map(), ims: map() } defp safe_map_getter(key) do Access.key(key, %{}) end defp safe_list_getter(key) do Access.key(key, []) end @doc """ Pattern matches against messages and returns updated Slack state. """ @spec update(map(), t()) :: t() def update(%{type: "channel_created", channel: channel}, slack) do put_in(slack, [:channels, channel.id], channel) end def update(%{type: "channel_joined", channel: channel}, slack) do slack |> put_in([:channels, channel.id], channel) |> put_in([:channels, channel.id, :is_member], true) end def update(%{type: "group_joined", channel: channel}, slack) do put_in(slack, [:groups, channel.id], channel) end def update(%{type: "channel_left", channel: channel_id}, slack) do put_in(slack, [:channels, channel_id, :is_member], false) end def update(%{type: "group_left", channel: channel}, slack) do update_in(slack, [:groups], &Map.delete(&1, channel)) end Enum.map(["channel", "group"], fn type -> plural_atom = String.to_atom(type <> "s") def update(%{type: unquote(type <> "_rename"), channel: channel}, slack) do put_in(slack, [unquote(plural_atom), safe_map_getter(channel.id), :name], channel.name) end def update(%{type: unquote(type <> "_archive"), channel: channel}, slack) do put_in(slack, [unquote(plural_atom), safe_map_getter(channel), :is_archived], true) end def update(%{type: unquote(type <> "_unarchive"), channel: channel}, slack) do put_in(slack, [unquote(plural_atom), safe_map_getter(channel), :is_archived], false) end def update( %{ type: "message", subtype: unquote(type <> "_topic"), channel: channel, user: user, topic: topic }, slack ) do put_in(slack, [unquote(plural_atom), safe_map_getter(channel), :topic], %{ creator: user, last_set: System.system_time(:second), value: topic }) end def update( %{type: "message", subtype: unquote(type <> "_join"), channel: channel, user: user}, slack ) do update_in( slack, [unquote(plural_atom), safe_map_getter(channel), safe_list_getter(:members)], &Enum.uniq([user | &1]) ) end def update( %{type: "message", subtype: unquote(type <> "_leave"), channel: channel, user: user}, slack ) do update_in( slack, [unquote(plural_atom), safe_map_getter(channel), safe_list_getter(:members)], &(&1 -- [user]) ) end end) def update(%{type: "team_rename", name: name}, slack) do put_in(slack, [:team, :name], name) end def update(%{type: "presence_change", user: user, presence: presence}, slack) do put_in(slack, [:users, user, :presence], presence) end def update(%{type: "presence_change", users: users, presence: presence}, slack) do Enum.reduce(users, slack, fn user, acc -> put_in(acc, [:users, user, :presence], presence) end) end def update(%{type: "team_join", user: user}, slack) do put_in(slack, [:users, user.id], user) end def update(%{type: "user_change", user: user}, slack) do update_in(slack, [:users, Access.key(user.id, %{})], &Map.merge(&1, user)) end Enum.map(["bot_added", "bot_changed"], fn type -> def update(%{type: unquote(type), bot: bot}, slack) do put_in(slack, [:bots, bot.id], bot) end end) def update(%{type: "im_created", channel: channel}, slack) do put_in(slack, [:ims, channel.id], channel) end def update(%{type: _type}, slack) do slack end end