defmodule Nola.Plugins.Sms do @moduledoc """ ## sms * **!sms `` ``** envoie un SMS. """ def short_irc_doc, do: false def irc_doc, do: @moduledoc require Logger def incoming(from, "enable " <> key) do key = String.trim(key) account = Nola.Account.find_meta_account("sms-validation-code", String.downcase(key)) if account do net = Nola.Account.get_meta(account, "sms-validation-target") Nola.Account.put_meta(account, "sms-number", from) Nola.Account.delete_meta(account, "sms-validation-code") Nola.Account.delete_meta(account, "sms-validation-number") Nola.Account.delete_meta(account, "sms-validation-target") Nola.Irc.Connection.broadcast_message(net, account, "SMS Number #{from} added!") send_sms(from, "Yay! Number linked to account #{account.name}") end end def incoming(from, message) do account = Nola.Account.find_meta_account("sms-number", from) if account do reply_fun = fn text -> send_sms(from, text) end trigger_text = if Enum.any?(Nola.Irc.Connection.triggers(), fn {trigger, _} -> String.starts_with?(message, trigger) end) do message else "!" <> message end message = %Nola.Message{ id: FlakeId.get(), transport: :sms, network: "sms", channel: nil, text: message, account: account, sender: %ExIRC.SenderInfo{nick: account.name}, replyfun: reply_fun, trigger: Nola.Irc.Connection.extract_trigger(trigger_text) } Logger.debug("converted sms to message: #{inspect(message)}") Nola.Irc.Connection.publish(message, ["messages:sms"]) message end end def my_number() do Keyword.get(Application.get_env(:nola, :sms, []), :number, "+33000000000") end def start_link() do GenServer.start_link(__MODULE__, [], name: __MODULE__) end def path() do account = Keyword.get(Application.get_env(:nola, :sms), :account) "https://eu.api.ovh.com/1.0/sms/#{account}" end def path(rest) do Path.join(path(), rest) end def send_sms(number, text) do url = path("/virtualNumbers/#{my_number()}/jobs") body = %{ "message" => text, "receivers" => [number], # "senderForResponse" => true, # "noStopClause" => true, "charset" => "UTF-8", "coding" => "8bit" } |> Poison.encode!() headers = [{"content-type", "application/json"}] ++ sign("POST", url, body) options = [] case HTTPoison.post(url, body, headers, options) do {:ok, %HTTPoison.Response{status_code: 200}} -> :ok {:ok, %HTTPoison.Response{status_code: code} = resp} -> Logger.error("SMS Error: #{inspect(resp)}") {:error, code} {:error, error} -> {:error, error} end end def init([]) do {:ok, _} = Registry.register(Nola.PubSub, "trigger:sms", plugin: __MODULE__) :ok = register_ovh_callback() {:ok, %{}} :ignore end def handle_info( {:irc, :trigger, "sms", m = %Nola.Message{trigger: %Nola.Trigger{type: :bang, args: [nick | text]}}}, state ) do with {:tree, false} <- {:tree, m.sender.nick == "Tree"}, {_, %Nola.Account{} = account} <- {:account, Nola.Account.find_always_by_nick(m.network, m.channel, nick)}, {_, number} when not is_nil(number) <- {:number, Nola.Account.get_meta(account, "sms-number")} do text = Enum.join(text, " ") sender = if m.channel do "#{m.channel} <#{m.sender.nick}> " else "<#{m.sender.nick}> " end case send_sms(number, sender <> text) do :ok -> m.replyfun.("sent!") {:error, error} -> m.replyfun.("not sent, error: #{inspect(error)}") end else {:tree, _} -> m.replyfun.("Tree: va en enfer") {:account, _} -> m.replyfun.("#{nick} not known") {:number, _} -> m.replyfun.("#{nick} have not enabled sms") end {:noreply, state} end def handle_info(msg, state) do {:noreply, state} end defp register_ovh_callback() do url = path() body = %{ "callBack" => NolaWeb.Router.Helpers.sms_url(NolaWeb.Endpoint, :ovh_callback), "smsResponse" => %{ "cgiUrl" => NolaWeb.Router.Helpers.sms_url(NolaWeb.Endpoint, :ovh_callback), "responseType" => "cgi" } } |> Poison.encode!() headers = [{"content-type", "application/json"}] ++ sign("PUT", url, body) options = [] case HTTPoison.put(url, body, headers, options) do {:ok, %HTTPoison.Response{status_code: 200}} -> :ok error -> error end end defp sign(method, url, body) do ts = DateTime.utc_now() |> DateTime.to_unix() as = env(:app_secret) ck = env(:consumer_key) sign = Enum.join([as, ck, String.upcase(method), url, body, ts], "+") sign_hex = :crypto.hash(:sha, sign) |> Base.encode16(case: :lower) headers = [ {"X-OVH-Application", env(:app_key)}, {"X-OVH-Timestamp", ts}, {"X-OVH-Signature", "$1$" <> sign_hex}, {"X-Ovh-Consumer", ck} ] end def parse_number(num) do {:error, :todo} end defp env() do Application.get_env(:nola, :sms) end defp env(key) do Keyword.get(env(), key) end end