defmodule LSG.IRC.SmsPlugin do
@moduledoc """
## sms
* **!sms `<nick>` `<message>`** 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 = IRC.Account.find_meta_account("sms-validation-code", String.downcase(key))
if account do
net = IRC.Account.get_meta(account, "sms-validation-target")
IRC.Account.put_meta(account, "sms-number", from)
IRC.Account.delete_meta(account, "sms-validation-code")
IRC.Account.delete_meta(account, "sms-validation-number")
IRC.Account.delete_meta(account, "sms-validation-target")
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 = IRC.Account.find_meta_account("sms-number", from)
if account do
reply_fun = fn(text) ->
send_sms(from, text)
end
trigger_text = if Enum.any?(IRC.Connection.triggers(), fn({trigger, _}) -> String.starts_with?(message, trigger) end) do
message
else
"!"<>message
end
message = %IRC.Message{
transport: :sms,
network: "sms",
channel: nil,
text: message,
account: account,
sender: %ExIRC.SenderInfo{nick: account.name},
replyfun: reply_fun,
trigger: IRC.Connection.extract_trigger(trigger_text)
}
IO.puts("converted sms to message: #{inspect message}")
IRC.Connection.publish(message, ["messages:sms"])
message
end
end
def my_number() do
Keyword.get(Application.get_env(:lsg, :sms, []), :number, "+33000000000")
end
def start_link() do
GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
def path() do
account = Keyword.get(Application.get_env(:lsg, :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(IRC.PubSub, "trigger:sms", [plugin: __MODULE__])
:ok = register_ovh_callback()
{:ok, %{}}
:ignore
end
def handle_info({:irc, :trigger, "sms", m = %IRC.Message{trigger: %IRC.Trigger{type: :bang, args: [nick | text]}}}, state) do
with \
{:tree, false} <- {:tree, m.sender.nick == "Tree"},
{_, %IRC.Account{} = account} <- {:account, IRC.Account.find_always_by_nick(m.network, m.channel, nick)},
{_, number} when not is_nil(number) <- {:number, IRC.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" =>LSGWeb.Router.Helpers.sms_url(LSGWeb.Endpoint, :ovh_callback),
"smsResponse" => %{
"cgiUrl" => LSGWeb.Router.Helpers.sms_url(LSGWeb.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(:lsg, :sms)
end
defp env(key) do
Keyword.get(env(), key)
end
end