defmodule LSG.TelegramRoom do
require Logger
@behaviour Telegram.ChatBot
alias Telegram.Api
def dets() do
(LSG.data_path() <> "/telegram-rooms.dets") |> String.to_charlist()
end
def setup() do
{:ok, _} = :dets.open_file(dets(), [])
:ok
end
def after_start() do
rooms = :dets.foldl(fn({id, _, _}, acc) -> [id | acc] end, [], dets())
for id <- rooms, do: Telegram.Bot.ChatBot.Chat.Session.Supervisor.start_child(LSG.Telegram, id)
end
@impl Telegram.ChatBot
def init(id) do
token = Keyword.get(Application.get_env(:lsg, :telegram, []), :key)
{:ok, chat} = Api.request(token, "getChat", chat_id: id)
Logger.debug("Starting ChatBot for room #{id} \"#{chat["title"]}\"")
[net, chan] = String.split(chat["title"], "/", parts: 2)
case IRC.Connection.get_network(net, chan) do
%IRC.Connection{} ->
{:ok, _} = Registry.register(IRC.PubSub, "#{net}/#{chan}:messages", plugin: __MODULE__)
{:ok, _} = Registry.register(IRC.PubSub, "#{net}/#{chan}:triggers", plugin: __MODULE__)
{:ok, _} = Registry.register(IRC.PubSub, "#{net}/#{chan}:outputs", plugin: __MODULE__)
err ->
Logger.warn("Did not found telegram match for #{id} \"#{chat["title"]}\"")
end
:dets.insert(dets(), {id, net, chan})
{:ok, %{id: id, net: net, chan: chan}}
end
def handle_update(%{"message" => %{"from" => %{"id" => user_id}, "text" => text}}, _token, state) do
account = IRC.Account.find_meta_account("telegram-id", user_id)
connection = IRC.Connection.get_network(state.net)
IRC.send_message_as(account, state.net, state.chan, text, true)
{:ok, state}
end
def handle_update(data = %{"message" => %{"from" => %{"id" => user_id}, "location" => %{"latitude" => lat, "longitude" => lon}}}, _token, state) do
account = IRC.Account.find_meta_account("telegram-id", user_id)
connection = IRC.Connection.get_network(state.net)
IRC.send_message_as(account, state.net, state.chan, "@ #{lat}, #{lon}", true)
{:ok, state}
end
for type <- ~w(photo voice video document animation) do
def handle_update(data = %{"message" => %{unquote(type) => _}}, token, state) do
upload(unquote(type), data, token, state)
end
end
def handle_update(update, token, state) do
{:ok, state}
end
def handle_info({:irc, _, _, message}, state) do
handle_info({:irc, nil, message}, state)
end
def handle_info({:irc, _, message = %IRC.Message{sender: %{nick: nick}, text: text}}, state) do
if Map.get(message.meta, :from) == self() do
else
body = if Map.get(message.meta, :self), do: text, else: "<#{nick}> #{text}"
LSG.Telegram.send_message(state.id, body)
end
{:ok, state}
end
def handle_info(info, state) do
Logger.info("UNhandled #{inspect info}")
{:ok, state}
end
defp upload(_type, %{"message" => m = %{"chat" => %{"id" => chat_id}, "from" => %{"id" => user_id}}}, token, state) do
account = IRC.Account.find_meta_account("telegram-id", user_id)
if account do
{content, type} = cond do
m["photo"] -> {m["photo"], "photo"}
m["voice"] -> {m["voice"], "voice message"}
m["video"] -> {m["video"], "video"}
m["document"] -> {m["document"], "file"}
m["animation"] -> {m["animation"], "gif"}
end
file = if is_list(content) && Enum.count(content) > 1 do
Enum.sort_by(content, fn(p) -> p["file_size"] end, &>=/2)
|> List.first()
else
content
end
file_id = file["file_id"]
file_unique_id = file["file_unique_id"]
text = if(m["caption"], do: m["caption"] <> " ", else: "")
spawn(fn() ->
with \
{:ok, file} <- Telegram.Api.request(token, "getFile", file_id: file_id),
path = "https://api.telegram.org/file/bot#{token}/#{file["file_path"]}",
{:ok, %HTTPoison.Response{status_code: 200, body: body}} <- HTTPoison.get(path),
<<smol_body::binary-size(20), _::binary>> = body,
{:ok, magic} <- GenMagic.Pool.perform(LSG.GenMagic, {:bytes, smol_body}),
bucket = Application.get_env(:lsg, :s3, []) |> Keyword.get(:bucket),
ext = Path.extname(file["file_path"]),
s3path = "#{account.id}/#{file_unique_id}#{ext}",
s3req = ExAws.S3.put_object(bucket, s3path, body, acl: :public_read, content_type: magic.mime_type),
{:ok, _} <- ExAws.request(s3req)
do
path = LSGWeb.Router.Helpers.url(LSGWeb.Endpoint) <> "/files/#{s3path}"
txt = "#{type}: #{text}#{path}"
connection = IRC.Connection.get_network(state.net)
IRC.send_message_as(account, state.net, state.chan, txt, true)
else
error ->
Telegram.Api.request(token, "sendMessage", chat_id: chat_id, text: "File upload failed, sorry.")
Logger.error("Failed upload from Telegram: #{inspect error}")
end
end)
{:ok, state}
end
end
end