summaryrefslogblamecommitdiff
path: root/lib/matrix.ex
blob: 0ad08361c0f2d096e30549b9b6d9f8f4f48af7b9 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
                        





                                                 
              

                   
                                                                                   









                                                       
                                          
                                    
                                    

                           




                                                                                               









                                                           
                                                                        





















                                                                                      
                                                                                                




                                                                                   
                                                       


























                                                                                                    
                                                                                     
                                                                                                                                               


















                                                                                     
                                  


                                                   
                                           







                                             
                                       







                                                      
                       
                                                                        
       
























                                                                          
defmodule Nola.Matrix do
  require Logger
  alias Polyjuice.Client

  @behaviour MatrixAppService.Adapter.Room
  @behaviour MatrixAppService.Adapter.Transaction
  @behaviour MatrixAppService.Adapter.User
  @env Mix.env

  def dets(part) do
    (Nola.data_path() <> "/matrix-#{to_string(part)}.dets") |> String.to_charlist()
  end

  def setup() do
    {:ok, _} = :dets.open_file(dets(:rooms), [])
    {:ok, _} = :dets.open_file(dets(:room_aliases), [])
    {:ok, _} = :dets.open_file(dets(:users), [])
    :ok
  end

  def myself?("@_dev:random.sh"), do: true
  def myself?("@_bot:random.sh"), do: true
  def myself?("@_dev."<>_), do: true
  def myself?("@_bot."<>_), do: true
  def myself?(_), do: false

  def mxc_to_http(mxc = "mxc://"<>_) do
    uri = URI.parse(mxc)
    %URI{uri | scheme: "https", path: "/_matrix/media/r0/download/#{uri.authority}#{uri.path}"}
    |> URI.to_string()
  end

  def get_or_create_matrix_user(id) do
    if mxid = lookup_user(id) do
      mxid
    else
      opts = [
        type: "m.login.application_service",
        inhibit_login: true,
        device_id: "APP_SERVICE",
        initial_device_display_name: "Application Service",
        username: if(@env == :dev, do: "_dev.#{id}", else: "_bot.#{id}")
      ]
      Logger.debug("Registering user for #{id}")
      {:ok, %{"user_id" => mxid}} = Polyjuice.Client.LowLevel.register(client(), opts)
      :dets.insert(dets(:users), {id, mxid})
    end
  end

  def lookup_user(id) do
    case :dets.lookup(dets(:users), id) do
      [{_, matrix_id}] -> matrix_id
      _ -> nil
    end
  end

  def user_name("@"<>name) do
    [username, _] = String.split(name, ":", parts: 2)
    username
  end

  def application_childs() do
    import Supervisor.Spec
    [
      supervisor(Nola.Matrix.Room.Supervisor, [], [name: Nola.Irc.PuppetConnection.Supervisor]),
    ]
  end

  def after_start() do
    rooms = :dets.foldl(fn({id, _, _, _}, acc) -> [id | acc] end, [], dets(:rooms))
    for room <- rooms, do: Nola.Matrix.Room.start(room)
  end

  def lookup_room(room) do
    case :dets.lookup(dets(:rooms), room) do
      [{_, network, channel, opts}] -> {:ok, Map.merge(opts, %{network: network, channel: channel})}
      _ -> {:error, :no_such_room}
    end
  end

  def lookup_room_alias(room_alias) do
    case :dets.lookup(dets(:room_aliases), room_alias) do
      [{_, room_id}] -> {:ok, room_id}
      _ -> {:error, :no_such_room_alias}
    end
  end

  def lookup_or_create_room(room_alias) do
    case lookup_room_alias(room_alias) do
      {:ok, room_id} -> {:ok, room_id}
      {:error, :no_such_room_alias} -> create_room(room_alias)
    end
  end

  def create_room(room_alias) do
    Logger.debug("Matrix: creating room #{inspect room_alias}")
    localpart = localpart(room_alias)
    with {:ok, network, channel} <- extract_network_channel_from_localpart(localpart),
         %Nola.Irc.Connection{} <- Nola.Irc.Connection.get_network(network, channel),
           room = [visibility: :public, room_alias_name: localpart, name: if(network == "random", do: channel, else: "#{network}/#{channel}")],
         {:ok, %{"room_id" => room_id}} <- Client.Room.create_room(client(), room) do
      Logger.info("Matrix: created room #{room_alias} #{room_id}")
      :dets.insert(dets(:rooms), {room_id, network, channel, %{}})
      :dets.insert(dets(:room_aliases), {room_alias, room_id})
      {:ok, room_id}
    else
      nil -> {:error, :no_such_network_channel}
      error -> error
    end
  end

  def localpart(room_alias) do
    [<<"#", localpart :: binary>>, _] = String.split(room_alias, ":", parts: 2)
    localpart
  end

  def extract_network_channel_from_localpart(localpart) do
    s = localpart
    |> String.replace("dev.", "")
    |> String.split("/", parts: 2)

    case s do
      [network, channel] -> {:ok, network, channel}
      [channel] -> {:ok, "random", channel}
      _ -> {:error, :invalid_localpart}
    end
  end

  @impl MatrixAppService.Adapter.Room
  def query_alias(room_alias) do
    case lookup_or_create_room(room_alias) do
      {:ok, room_id} ->
        Nola.Matrix.Room.start(room_id)
        :ok
      error -> error
    end
  end

  @impl MatrixAppService.Adapter.Transaction
  def new_event(event = %MatrixAppService.Event{}) do
    Logger.debug("New matrix event: #{inspect event}")
    if event.room_id do
      Nola.Matrix.Room.start_and_send_matrix_event(event.room_id, event)
    end
    :noop
  end

  @impl MatrixAppService.Adapter.User
  def query_user(user_id) do
    Logger.warn("Matrix lookup user: #{inspect user_id}")
    :error
  end

  def client(opts \\ []) do
    base_url = Application.get_env(:matrix_app_service, :base_url)
    access_token = Application.get_env(:matrix_app_service, :access_token)
    default_opts = [
      access_token: access_token,
      device_id: "APP_SERVICE",
      application_service: true,
      user_id: nil
    ]
    opts = Keyword.merge(default_opts, opts)

    Polyjuice.Client.LowLevel.create(base_url, opts)
  end


end