Current section
Files
Jump to
Current section
Files
lib/websocket/client.ex
defmodule SimplerSlack.Websocket.Client do
@moduledoc """
Websocket client that connects to the provided URL.
Used to connect to the slack RTM API and dispatch messages to
the provided module.
only supports `user_typing` and `message` events.
"""
@behaviour :websocket_client
@valid_message_types [
"message",
"user_typing"
]
require Logger
@doc """
starts the web socket client
"""
@spec start_link(String.t, module, map) :: {:ok, pid}
def start_link(url, module, state \\%{}) do
new_state = Map.merge(state, %{url: url, module: module})
:crypto.start
:ssl.start
:websocket_client.start_link(to_charlist(url), __MODULE__, new_state)
end
@doc false
def init(state), do: {:once, state}
@doc false
def onconnect(_req, %{url: url} = state) do
Logger.debug "Connected to #{url}"
ok state
end
@doc false
def ondisconnect(reason, %{url: url} = state) do
Logger.debug "Disconnected from #{url}"
Logger.debug "Reason Being: #{reason}"
reconnect state
end
@doc false
def websocket_handle({:ping, _}, _conn, state) do
Logger.debug "pinged"
ok state
end
@doc """
Handles incoming slack messages.
Validates and dispatches the messages to the registered module
"""
def websocket_handle({:text, msg}, _conn, %{module: module} = state) do
parse(msg)
|> is_valid?(state)
|> dispatch(module, state)
ok state
end
@doc false
def websocket_info(:start, _conn, state) do
ok state
end
@doc false
def websocket_terminate({:close, code, payload}, _conn, state) do
Logger.debug "Websocket closed"
Logger.debug "State: #{state}"
Logger.debug "Code: #{code}"
Logger.debug "Payload: #{payload}"
end
defp parse(json) do
{:ok, message} = Poison.Parser.parse(json, keys: :atoms)
message
end
defp is_valid?(%{type: "message", text: text} = message, %{self: %{id: id}}) do
case String.contains?(text, "<@#{id}>") do
false -> {:error, "message does not contain bot id"}
true -> message
end
end
defp is_valid?(%{type: "user_typing"} = message, _state), do: message
defp is_valid?(message, _state) do
{:error, "unsupported message: #{message.type}"}
end
defp dispatch(%{type: type} = json, module, state) do
Logger.debug "Received message of type #{type}"
Logger.debug "Dispatching message to #{module}"
# dynamically making atoms, i know.
# but this will only ever be :slack_message or :slack_user_typing
function_name = String.to_atom("slack_#{type}")
args = [Map.merge(json, state)]
apply(
module,
function_name,
args
)
end
defp dispatch({:error, reason}, _module, _state) do
Logger.debug "Error while dispatching: #{reason}"
end
defp ok(state) do
{:ok, state}
end
defp reconnect(state) do
{:reconnect, state}
end
end