1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
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: 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
|