summaryrefslogtreecommitdiff
path: root/lib/matrix/matrix.ex
diff options
context:
space:
mode:
Diffstat (limited to 'lib/matrix/matrix.ex')
-rw-r--r--lib/matrix/matrix.ex169
1 files changed, 169 insertions, 0 deletions
diff --git a/lib/matrix/matrix.ex b/lib/matrix/matrix.ex
new file mode 100644
index 0000000..9334816
--- /dev/null
+++ b/lib/matrix/matrix.ex
@@ -0,0 +1,169 @@
+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: 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),
+ %IRC.Connection{} <- 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