2021-07-03 10:30:57 +00:00
|
|
|
defmodule MatrixServer.Event do
|
2021-07-10 21:16:00 +00:00
|
|
|
use Ecto.Schema
|
2021-07-03 10:30:57 +00:00
|
|
|
|
2021-07-10 21:16:00 +00:00
|
|
|
import Ecto.Changeset
|
2021-07-03 10:30:57 +00:00
|
|
|
|
2021-07-17 15:38:20 +00:00
|
|
|
alias MatrixServer.{Room, Event, Account}
|
|
|
|
alias MatrixServerWeb.API.CreateRoom
|
2021-07-03 10:30:57 +00:00
|
|
|
|
2021-07-17 15:38:20 +00:00
|
|
|
@primary_key {:event_id, :string, []}
|
2021-07-10 21:16:00 +00:00
|
|
|
schema "events" do
|
|
|
|
field :type, :string
|
2021-07-17 15:38:20 +00:00
|
|
|
field :origin_server_ts, :integer
|
2021-07-10 21:16:00 +00:00
|
|
|
field :state_key, :string
|
|
|
|
field :sender, :string
|
2021-07-17 15:38:20 +00:00
|
|
|
field :content, :map
|
2021-07-10 21:16:00 +00:00
|
|
|
field :prev_events, {:array, :string}
|
|
|
|
field :auth_events, {:array, :string}
|
2021-07-17 15:38:20 +00:00
|
|
|
belongs_to :room, Room, type: :string
|
2021-07-03 10:30:57 +00:00
|
|
|
end
|
|
|
|
|
2021-07-10 21:16:00 +00:00
|
|
|
def changeset(event, params \\ %{}) do
|
|
|
|
# TODO: prev/auth events?
|
2021-07-05 14:12:44 +00:00
|
|
|
event
|
2021-07-10 21:16:00 +00:00
|
|
|
|> cast(params, [:type, :timestamp, :state_key, :sender, :content])
|
|
|
|
|> validate_required([:type, :timestamp, :sender])
|
2021-07-03 10:30:57 +00:00
|
|
|
end
|
2021-07-17 15:38:20 +00:00
|
|
|
|
|
|
|
def new(room_id, sender) do
|
|
|
|
%Event{
|
|
|
|
room_id: room_id,
|
|
|
|
sender: sender,
|
|
|
|
event_id: generate_event_id(),
|
|
|
|
origin_server_ts: DateTime.utc_now() |> DateTime.to_unix(),
|
|
|
|
prev_events: [],
|
|
|
|
auth_events: []
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
def create_room(room_id, creator, room_version) do
|
|
|
|
%Event{
|
|
|
|
new(room_id, creator)
|
|
|
|
| type: "m.room.create",
|
|
|
|
state_key: "",
|
|
|
|
content: %{
|
2021-07-17 16:54:49 +00:00
|
|
|
"creator" => creator,
|
|
|
|
"room_version" => room_version || MatrixServer.default_room_version()
|
2021-07-17 15:38:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
def join(room_id, sender) do
|
|
|
|
%Event{
|
|
|
|
new(room_id, sender)
|
|
|
|
| type: "m.room.member",
|
|
|
|
state_key: sender,
|
|
|
|
content: %{
|
2021-07-23 19:00:01 +00:00
|
|
|
"membership" => "join"
|
2021-07-17 15:38:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
2021-07-17 16:54:49 +00:00
|
|
|
def power_levels(room_id, sender) do
|
|
|
|
%Event{
|
|
|
|
new(room_id, sender)
|
|
|
|
| type: "m.room.power_levels",
|
|
|
|
state_key: "",
|
|
|
|
content: %{
|
|
|
|
"ban" => 50,
|
|
|
|
"events" => %{},
|
|
|
|
"events_default" => 0,
|
|
|
|
"invite" => 50,
|
|
|
|
"kick" => 50,
|
|
|
|
"redact" => 50,
|
|
|
|
"state_default" => 50,
|
|
|
|
"users" => %{
|
|
|
|
sender => 50
|
|
|
|
},
|
|
|
|
"users_default" => 0,
|
|
|
|
"notifications" => %{
|
|
|
|
"room" => 50
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
def room_name(room_id, sender, name) do
|
|
|
|
%Event{
|
|
|
|
new(room_id, sender)
|
|
|
|
| type: "m.room.name",
|
|
|
|
state_key: "",
|
|
|
|
content: %{
|
|
|
|
"name" => name
|
|
|
|
}
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
def room_topic(room_id, sender, topic) do
|
|
|
|
%Event{
|
|
|
|
new(room_id, sender)
|
|
|
|
| type: "m.room.topic",
|
|
|
|
state_key: "",
|
|
|
|
content: %{
|
|
|
|
"topic" => topic
|
|
|
|
}
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
def room_creation_create_room(repo, %{
|
|
|
|
input: %CreateRoom{room_version: room_version},
|
|
|
|
account: %Account{localpart: localpart},
|
|
|
|
room: %Room{id: room_id}
|
2021-07-17 15:38:20 +00:00
|
|
|
}) do
|
2021-07-17 16:54:49 +00:00
|
|
|
# TODO: state resolution
|
2021-07-23 19:00:01 +00:00
|
|
|
create_room(room_id, MatrixServer.get_mxid(localpart), room_version)
|
|
|
|
# resolve([events_to_state_set([create_room_event])])
|
|
|
|
# MatrixServer.StateResolution.resolve(create_room_event)
|
|
|
|
# repo.insert(create_room_event)
|
2021-07-17 15:38:20 +00:00
|
|
|
end
|
|
|
|
|
2021-07-17 16:54:49 +00:00
|
|
|
def room_creation_join_creator(repo, %{
|
|
|
|
room: %Room{id: room_id},
|
2021-07-21 13:50:28 +00:00
|
|
|
create_room_event: %Event{sender: creator, event_id: create_room_id}
|
2021-07-17 16:54:49 +00:00
|
|
|
}) do
|
|
|
|
# TODO: state resolution
|
|
|
|
join(room_id, creator)
|
2021-07-21 13:50:28 +00:00
|
|
|
|> Map.put(:prev_events, [create_room_id])
|
|
|
|
|> Map.put(:auth_events, [create_room_id])
|
2021-07-17 16:54:49 +00:00
|
|
|
|> repo.insert()
|
2021-07-17 15:38:20 +00:00
|
|
|
end
|
|
|
|
|
2021-07-17 16:54:49 +00:00
|
|
|
def room_creation_power_levels(
|
|
|
|
repo,
|
|
|
|
%{
|
|
|
|
room: %Room{id: room_id},
|
2021-07-21 13:50:28 +00:00
|
|
|
create_room_event: %Event{sender: creator, event_id: create_room_id},
|
|
|
|
join_creator_event: %Event{event_id: join_creator_id}
|
2021-07-17 16:54:49 +00:00
|
|
|
}
|
|
|
|
) do
|
|
|
|
# TODO: state resolution
|
|
|
|
power_levels(room_id, creator)
|
2021-07-21 13:50:28 +00:00
|
|
|
|> Map.put(:prev_events, [join_creator_id])
|
|
|
|
|> Map.put(:auth_events, [create_room_id, join_creator_id])
|
2021-07-17 16:54:49 +00:00
|
|
|
|> repo.insert()
|
2021-07-17 15:38:20 +00:00
|
|
|
end
|
|
|
|
|
2021-07-21 13:50:28 +00:00
|
|
|
def room_creation_name(_repo, %{input: %CreateRoom{name: nil}}), do: {:ok, nil}
|
|
|
|
|
|
|
|
def room_creation_name(_repo, %{input: %CreateRoom{name: name}}) when byte_size(name) > 255,
|
|
|
|
do: {:error, :name}
|
2021-07-17 16:54:49 +00:00
|
|
|
|
|
|
|
def room_creation_name(
|
|
|
|
repo,
|
|
|
|
%{
|
|
|
|
input: %CreateRoom{name: name},
|
|
|
|
room: %Room{id: room_id},
|
2021-07-21 13:50:28 +00:00
|
|
|
create_room_event: %Event{sender: creator, event_id: create_room_id},
|
|
|
|
join_creator_event: %Event{event_id: join_creator_id},
|
|
|
|
power_levels_event: %Event{event_id: power_levels_id}
|
2021-07-17 16:54:49 +00:00
|
|
|
}
|
|
|
|
) do
|
|
|
|
# TODO: state resolution
|
|
|
|
room_name(room_id, creator, name)
|
2021-07-21 13:50:28 +00:00
|
|
|
|> Map.put(:prev_events, [power_levels_id])
|
|
|
|
|> Map.put(:auth_events, [create_room_id, join_creator_id, power_levels_id])
|
2021-07-17 16:54:49 +00:00
|
|
|
|> repo.insert()
|
2021-07-17 15:38:20 +00:00
|
|
|
end
|
|
|
|
|
2021-07-21 13:50:28 +00:00
|
|
|
def room_creation_topic(_repo, %{input: %CreateRoom{topic: nil}}), do: {:ok, nil}
|
2021-07-17 16:54:49 +00:00
|
|
|
|
|
|
|
def room_creation_topic(
|
|
|
|
repo,
|
|
|
|
%{
|
|
|
|
input: %CreateRoom{topic: topic},
|
|
|
|
room: %Room{id: room_id},
|
2021-07-21 13:50:28 +00:00
|
|
|
create_room_event: %Event{sender: creator, event_id: create_room_id},
|
|
|
|
join_creator_event: %Event{event_id: join_creator_id},
|
|
|
|
power_levels_event: %Event{event_id: power_levels_id},
|
|
|
|
name_event: name_event
|
2021-07-17 16:54:49 +00:00
|
|
|
}
|
|
|
|
) do
|
|
|
|
# TODO: state resolution
|
2021-07-21 13:50:28 +00:00
|
|
|
prev_event = if name_event, do: name_event.event_id, else: power_levels_id
|
|
|
|
|
2021-07-17 16:54:49 +00:00
|
|
|
room_topic(room_id, creator, topic)
|
2021-07-21 13:50:28 +00:00
|
|
|
|> Map.put(:prev_events, [prev_event])
|
|
|
|
|> Map.put(:auth_events, [create_room_id, join_creator_id, power_levels_id])
|
2021-07-17 16:54:49 +00:00
|
|
|
|> repo.insert()
|
2021-07-17 15:38:20 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def generate_event_id do
|
|
|
|
"$" <> MatrixServer.random_string(17) <> ":" <> MatrixServer.server_name()
|
|
|
|
end
|
2021-07-21 13:50:28 +00:00
|
|
|
|
|
|
|
def events_to_state_set(events) do
|
|
|
|
Enum.into(events, %{}, fn %Event{type: type, state_key: state_key} = event ->
|
|
|
|
{{type, state_key}, event}
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
|
|
|
def resolve(state_sets) do
|
|
|
|
{unconflicted_state_map, conflicted_set} = calculate_conflict(state_sets)
|
|
|
|
# full_conflicted_set = MapSet.union(conflicted_set, auth_difference(state_sets))
|
|
|
|
|
|
|
|
# conflicted_control_events =
|
|
|
|
# Enum.filter(full_conflicted_set, &is_control_event/1) |> MapSet.new()
|
|
|
|
|
|
|
|
# conflicted_control_events_with_auth =
|
|
|
|
# MapSet.union(
|
|
|
|
# conflicted_control_events,
|
|
|
|
# MapSet.intersection(
|
|
|
|
# full_conflicted_set,
|
|
|
|
# full_auth_chain(MapSet.to_list(conflicted_control_events))
|
|
|
|
# )
|
|
|
|
# )
|
|
|
|
|
|
|
|
# sorted_control_events = Enum.sort(conflicted_control_events_with_auth, &rev_top_pow_order/2)
|
|
|
|
# partial_resolved_state = iterative_auth_checks(sorted_control_events, unconflicted_state_map)
|
|
|
|
|
|
|
|
# other_conflicted_events =
|
|
|
|
# MapSet.difference(full_conflicted_set, conflicted_control_events_with_auth)
|
|
|
|
|
|
|
|
# resolved_power_levels = partial_resolved_state[{:power_levels, ""}]
|
|
|
|
|
|
|
|
# sorted_other_events =
|
|
|
|
# Enum.sort(other_conflicted_events, mainline_order(resolved_power_levels))
|
|
|
|
|
|
|
|
# nearly_final_state = iterative_auth_checks(sorted_other_events, partial_resolved_state)
|
|
|
|
|
|
|
|
# Map.merge(nearly_final_state, unconflicted_state_map)
|
|
|
|
end
|
|
|
|
|
|
|
|
def calculate_conflict(state_sets) do
|
|
|
|
{unconflicted, conflicted} =
|
|
|
|
state_sets
|
|
|
|
|> Enum.flat_map(&Map.keys/1)
|
|
|
|
|> MapSet.new()
|
|
|
|
|> Enum.map(fn state_pair ->
|
|
|
|
events =
|
|
|
|
Enum.map(state_sets, &Map.get(&1, state_pair))
|
|
|
|
|> MapSet.new()
|
|
|
|
|
|
|
|
{state_pair, events}
|
|
|
|
end)
|
|
|
|
|> Enum.split_with(fn {_k, events} ->
|
|
|
|
MapSet.size(events) == 1
|
|
|
|
end)
|
|
|
|
|
2021-07-23 19:00:01 +00:00
|
|
|
unconflicted_state_map =
|
|
|
|
Enum.into(unconflicted, %{}, fn {state_pair, events} ->
|
|
|
|
event = MapSet.to_list(events) |> hd()
|
2021-07-21 13:50:28 +00:00
|
|
|
|
2021-07-23 19:00:01 +00:00
|
|
|
{state_pair, event}
|
|
|
|
end)
|
2021-07-21 13:50:28 +00:00
|
|
|
|
2021-07-23 19:00:01 +00:00
|
|
|
conflicted_state_set =
|
|
|
|
Enum.reduce(conflicted, MapSet.new(), fn {_, events}, acc ->
|
|
|
|
MapSet.union(acc, events)
|
|
|
|
end)
|
|
|
|
|> MapSet.delete(nil)
|
2021-07-21 13:50:28 +00:00
|
|
|
|
|
|
|
{unconflicted_state_map, conflicted_state_set}
|
|
|
|
end
|
2021-07-03 10:30:57 +00:00
|
|
|
end
|