defmodule Nola.Telegram do require Logger @behaviour Telegram.ChatBot def my_path() do "https://t.me/beauttebot" end def send_message(id, text, md2 \\ false) do md = if md2, do: "MarkdownV2", else: "Markdown" token = Keyword.get(Application.get_env(:nola, :telegram, []), :key) Telegram.Bot.ChatBot.Chat.Session.Supervisor.start_child(Nola.Telegram, id) Telegram.Api.request(token, "sendMessage", chat_id: id, text: text, parse_mode: "Markdown") end @impl Telegram.ChatBot def init(chat_id) when chat_id < 0 do {:ok, state} = Nola.TelegramRoom.init(chat_id) {:ok, %{room_state: state}} end def init(chat_id) do Logger.info("Telegram session starting: #{chat_id}") account = Nola.Account.find_meta_account("telegram-id", chat_id) account_id = if account, do: account.id {:ok, %{account: account_id}} end @impl Telegram.ChatBot def handle_update(update, token, %{room_state: room_state}) do {:ok, room_state} = Nola.TelegramRoom.handle_update(update, token, room_state) {:ok, %{room_state: room_state}} end def handle_update(%{"message" => m = %{"chat" => %{"type" => "private"}, "text" => text = "/start"<>_}}, _token, state) do text = "*Welcome to beautte!*\n\nQuery the bot on IRC and say \"enable-telegram\" to continue." send_message(m["chat"]["id"], text) {:ok, %{account: nil}} end def handle_update(%{"message" => m = %{"chat" => %{"type" => "private"}, "text" => text = "/enable"<>_}}, _token, state) do key = case String.split(text, " ") do ["/enable", key | _] -> key _ -> "nil" end #Handled message "1247435154:AAGnSSCnySn0RuVxy_SUcDEoOX_rbF6vdq0" %{"message" => # %{"chat" => %{"first_name" => "J", "id" => 2075406, "type" => "private", "username" => "ahref"}, # "date" => 1591027272, "entities" => # [%{"length" => 7, "offset" => 0, "type" => "bot_command"}], # "from" => %{"first_name" => "J", "id" => 2075406, "is_bot" => false, "language_code" => "en", "username" => "ahref"}, # "message_id" => 11, "text" => "/enable salope"}, "update_id" => 764148578} account = Nola.Account.find_meta_account("telegram-validation-code", String.downcase(key)) text = if account do net = Nola.Account.get_meta(account, "telegram-validation-target") Nola.Account.put_meta(account, "telegram-id", m["chat"]["id"]) Nola.Account.put_meta(account, "telegram-username", m["chat"]["username"]) Nola.Account.put_meta(account, "telegram-username", m["chat"]["username"]) Nola.Account.delete_meta(account, "telegram-validation-code") Nola.Account.delete_meta(account, "telegram-validation-target") IRC.Connection.broadcast_message(net, account, "Telegram #{m["chat"]["username"]} account added!") "Yay! Linked to account **#{account.name}**." else "Token invalid" end send_message(m["chat"]["id"], text) {:ok, %{account: account.id}} end #[debug] Unhandled update: %{"message" => # %{"chat" => %{"first_name" => "J", "id" => 2075406, "type" => "private", "username" => "ahref"}, # "date" => 1591096015, # "from" => %{"first_name" => "J", "id" => 2075406, "is_bot" => false, "language_code" => "en", "username" => "ahref"}, # "message_id" => 29, # "photo" => [ # %{"file_id" => "AgACAgQAAxkBAAMdXtYyz4RQqLcpOlR6xKK3w3NayHAAAnCzMRuL4bFSgl_cTXMl4m5G_T0kXQADAQADAgADbQADZVMBAAEaBA", # "file_size" => 9544, "file_unique_id" => "AQADRv09JF0AA2VTAQAB", "height" => 95, "width" => 320}, # %{"file_id" => "AgACAgQAAxkBAAMdXtYyz4RQqLcpOlR6xKK3w3NayHAAAnCzMRuL4bFSgl_cTXMl4m5G_T0kXQADAQADAgADeAADZFMBAAEaBA", # "file_size" => 21420, "file_unique_id" => "AQADRv09JF0AA2RTAQAB", "height" => 148, "width" => 501}]}, # "update_id" => 218161546} for type <- ~w(photo voice video document animation) do def handle_update(data = %{"message" => %{unquote(type) => _}}, token, state) do start_upload(unquote(type), data, token, state) end end #[debug] Unhandled update: %{"callback_query" => # %{ # "chat_instance" => "-7948978714441865930", "data" => "evolu.net/#dmz", # "from" => %{"first_name" => "J", "id" => 2075406, "is_bot" => false, "language_code" => "en", "username" => "ahref"}, # "id" => "8913804780149600", # "message" => %{"chat" => %{"first_name" => "J", "id" => 2075406, "type" => "private", "username" => "ahref"}, # "date" => 1591098553, "from" => %{"first_name" => "devbeautte", "id" => 1293058838, "is_bot" => true, "username" => "devbeauttebot"}, # "message_id" => 62, # "reply_markup" => %{"inline_keyboard" => [[%{"callback_data" => "random/#", "text" => "random/#"}, # %{"callback_data" => "evolu.net/#dmz", "text" => "evolu.net/#dmz"}]]}, # "text" => "Where should I send the file?"} # } # , "update_id" => 218161568} #def handle_update(t, %{"callback_query" => cb = %{"data" => "resend", "id" => id, "message" => m = %{"message_id" => m_id, "chat" => %{"id" => chat_id}, "reply_to_message" => op}}}) do #end def handle_update(%{"callback_query" => cb = %{"data" => "start-upload:"<>target, "id" => id, "message" => m = %{"message_id" => m_id, "chat" => %{"id" => chat_id}, "reply_to_message" => op}}}, t, state) do account = Nola.Account.find_meta_account("telegram-id", chat_id) if account do target = case String.split(target, "/") do ["everywhere"] -> Nola.Membership.of_account(account) [net, chan] -> [{net, chan}] end Telegram.Api.request(t, "editMessageText", chat_id: chat_id, message_id: m_id, text: "Processing...", reply_markup: %{}) {content, type} = cond do op["photo"] -> {op["photo"], ""} op["voice"] -> {op["voice"], " a voice message"} op["video"] -> {op["video"], ""} op["document"] -> {op["document"], ""} op["animation"] -> {op["animation"], ""} 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(op["caption"], do: ": "<> op["caption"] <> "", else: "") resend = %{"inline_keyboard" => [ [%{"text" => "re-share", "callback_data" => "resend"}] ]} spawn(fn() -> with \ {:ok, file} <- Telegram.Api.request(t, "getFile", file_id: file_id), path = "https://api.telegram.org/file/bot#{t}/#{file["file_path"]}", {:ok, %HTTPoison.Response{status_code: 200, body: body}} <- HTTPoison.get(path), <> = body, {:ok, magic} <- GenMagic.Pool.perform(Nola.GenMagic, {:bytes, smol_body}), bucket = Application.get_env(:nola, :s3, []) |> Keyword.get(:bucket), ext = Path.extname(file["file_path"]), s3path = "#{account.id}/#{file_unique_id}#{ext}", Telegram.Api.request(t, "editMessageText", chat_id: chat_id, message_id: m_id, text: "*Uploading...*", reply_markup: %{}, parse_mode: "MarkdownV2"), s3req = ExAws.S3.put_object(bucket, s3path, body, acl: :public_read, content_type: magic.mime_type), {:ok, _} <- ExAws.request(s3req) do path = NolaWeb.Router.Helpers.url(NolaWeb.Endpoint) <> "/files/#{s3path}" sent = for {net, chan} <- target do txt = "sent#{type}#{text} #{path}" IRC.send_message_as(account, net, chan, txt) "#{net}/#{chan}" end if caption = op["caption"], do: as_irc_message(chat_id, caption, account) text = "Sent on " <> Enum.join(sent, ", ") <> " !" Telegram.Api.request(t, "editMessageText", chat_id: chat_id, message_id: m_id, text: "_Sent!_", reply_markup: %{}, parse_mode: "MarkdownV2") else error -> Telegram.Api.request(t, "editMessageText", chat_id: chat_id, message_id: m_id, text: "Something failed.", reply_markup: %{}, parse_mode: "MarkdownV2") Logger.error("Failed upload from Telegram: #{inspect error}") end end) end {:ok, state} end def handle_update(%{"message" => m = %{"chat" => %{"id" => id, "type" => "private"}, "text" => text}}, _, state) do account = Nola.Account.find_meta_account("telegram-id", id) if account do as_irc_message(id, text, account) end {:ok, state} end def handle_update(m, _, state) do Logger.debug("Unhandled update: #{inspect m}") {:ok, state} end @impl Telegram.ChatBot def handle_info(info, %{room_state: room_state}) do {:ok, room_state} = Nola.TelegramRoom.handle_info(info, room_state) {:ok, %{room_state: room_state}} end def handle_info(_info, state) do {:ok, state} end defp as_irc_message(id, text, account) do reply_fun = fn(text) -> send_message(id, text) end trigger_text = cond do String.starts_with?(text, "/") -> "/"<>text = text "!"<>text Enum.any?(IRC.Connection.triggers(), fn({trigger, _}) -> String.starts_with?(text, trigger) end) -> text true -> "!"<>text end message = %IRC.Message{ id: FlakeId.get(), transport: :telegram, network: "telegram", channel: nil, text: text, account: account, sender: %ExIRC.SenderInfo{nick: account.name}, replyfun: reply_fun, trigger: IRC.Connection.extract_trigger(trigger_text), at: nil } IRC.Connection.publish(message, ["messages:private", "messages:telegram", "telegram/#{account.id}:messages"]) message end defp start_upload(_type, %{"message" => m = %{"chat" => %{"id" => id, "type" => "private"}}}, token, state) do account = Nola.Account.find_meta_account("telegram-id", id) if account do text = if(m["text"], do: m["text"], else: nil) targets = Nola.Membership.of_account(account) |> Enum.map(fn({net, chan}) -> "#{net}/#{chan}" end) |> Enum.map(fn(i) -> %{"text" => i, "callback_data" => "start-upload:#{i}"} end) kb = if Enum.count(targets) > 1 do [%{"text" => "everywhere", "callback_data" => "start-upload:everywhere"}] ++ targets else targets end |> Enum.chunk_every(2) keyboard = %{"inline_keyboard" => kb} Telegram.Api.request(token, "sendMessage", chat_id: id, text: "Where should I send this file?", reply_markup: keyboard, reply_to_message_id: m["message_id"], parse_mode: "MarkdownV2") end {:ok, state} end end