summaryrefslogblamecommitdiff
path: root/lib/nola_telegram/telegram.ex
blob: 1c6a9a9a46f75ca2292999bef50a288c41f9d1f5 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
                          
                
                             




                             

                                                   
                                                                        
                                                                               



                                                                                               
                                       
                                                  

                               




                                                                   

     
                        
                                                                
                                                                                  


                                    

                                                                                                                            
                                       
                          

     
                                                                                                                             




                                         
                                                                                    

                                                                                                       
                                                                    






                                                                                                                              
                                                                               


                                                                                                        
                                                   



                                       
                                 

     
                                           










                                                                                                                             



                                                                                    

     













                                                                                                                                                            

                                                                                                                                                                                           
 
                                                                                                                                                                                                                































                                                                                                                                
                                                                                    
                                                                               

                                                           
                                                                                                                                                              


                                                                                                              
                                                                                   
                                             

                                                        



                                                                                   
                                                                                                                                                      

                  
                                                                                                                                                                  



                                                                         
                

     
                                                                                                                     



                                                              
                

     
                                   
                                                  
                

     

                                                     
                                                                       






                                    











                                                                                                         
                          






                                                    

                                                              
     
                                                                                                                 


           
                                                                                                                




                                                              
                                                                                      
                                        
                                                                                            




                                           
                                                                                                                                                                                             
       
                

     
   
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 = IRC.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 = IRC.Account.find_meta_account("telegram-validation-code", String.downcase(key))
    text = if account do
      net = IRC.Account.get_meta(account, "telegram-validation-target")
      IRC.Account.put_meta(account, "telegram-id", m["chat"]["id"])
      IRC.Account.put_meta(account, "telegram-username", m["chat"]["username"])
      IRC.Account.put_meta(account, "telegram-username", m["chat"]["username"])
      IRC.Account.delete_meta(account, "telegram-validation-code")
      IRC.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 = IRC.Account.find_meta_account("telegram-id", chat_id)
    if account do
      target = case String.split(target, "/") do
        ["everywhere"] -> IRC.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),
          <<smol_body::binary-size(20), _::binary>> = 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 = IRC.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 = IRC.Account.find_meta_account("telegram-id", id)
    if account do
      text = if(m["text"], do: m["text"], else: nil)
      targets = IRC.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