Put avatar_url and displayname on m.room.member events
This commit is contained in:
parent
064a398a37
commit
739c496ac6
9 changed files with 193 additions and 70 deletions
|
@ -25,6 +25,8 @@ defmodule Architex.RoomServer do
|
|||
Alias
|
||||
}
|
||||
|
||||
alias Architex.Types.UserId
|
||||
|
||||
alias Architex.StateResolution.Authorization
|
||||
alias ArchitexWeb.Client.Request.{CreateRoom, Kick, Ban}
|
||||
|
||||
|
@ -112,9 +114,10 @@ defmodule Architex.RoomServer do
|
|||
@doc """
|
||||
Invite the a user to this room.
|
||||
"""
|
||||
@spec invite(pid(), Account.t(), String.t()) :: :ok | {:error, atom()}
|
||||
def invite(pid, account, user_id) do
|
||||
GenServer.call(pid, {:invite, account, user_id})
|
||||
@spec invite(pid(), Account.t(), String.t(), String.t() | nil, String.t() | nil) ::
|
||||
:ok | {:error, atom()}
|
||||
def invite(pid, account, user_id, avatar_url, displayname) do
|
||||
GenServer.call(pid, {:invite, account, user_id, avatar_url, displayname})
|
||||
end
|
||||
|
||||
@doc """
|
||||
|
@ -136,25 +139,28 @@ defmodule Architex.RoomServer do
|
|||
@doc """
|
||||
Kick a user from this room.
|
||||
"""
|
||||
@spec kick(pid(), Account.t(), Kick.t()) :: :ok | {:error, atom()}
|
||||
def kick(pid, account, request) do
|
||||
GenServer.call(pid, {:kick, account, request})
|
||||
@spec kick(pid(), Account.t(), Kick.t(), String.t() | nil, String.t() | nil) ::
|
||||
:ok | {:error, atom()}
|
||||
def kick(pid, account, request, avatar_url, displayname) do
|
||||
GenServer.call(pid, {:kick, account, request, avatar_url, displayname})
|
||||
end
|
||||
|
||||
@doc """
|
||||
Ban a user from this room.
|
||||
"""
|
||||
@spec ban(pid(), Account.t(), Ban.t()) :: :ok | {:error, atom()}
|
||||
def ban(pid, account, request) do
|
||||
GenServer.call(pid, {:ban, account, request})
|
||||
@spec ban(pid(), Account.t(), Ban.t(), String.t() | nil, String.t() | nil) ::
|
||||
:ok | {:error, atom()}
|
||||
def ban(pid, account, request, avatar_url, displayname) do
|
||||
GenServer.call(pid, {:ban, account, request, avatar_url, displayname})
|
||||
end
|
||||
|
||||
@doc """
|
||||
Unban a user from this room.
|
||||
"""
|
||||
@spec unban(pid(), Account.t(), String.t()) :: :ok | {:error, atom()}
|
||||
def unban(pid, account, user_id) do
|
||||
GenServer.call(pid, {:unban, account, user_id})
|
||||
@spec unban(pid(), Account.t(), String.t(), String.t() | nil, String.t() | nil) ::
|
||||
:ok | {:error, atom()}
|
||||
def unban(pid, account, user_id, avatar_url, displayname) do
|
||||
GenServer.call(pid, {:unban, account, user_id, avatar_url, displayname})
|
||||
end
|
||||
|
||||
@doc """
|
||||
|
@ -288,8 +294,12 @@ defmodule Architex.RoomServer do
|
|||
{:reply, {state_events, auth_chain}, state}
|
||||
end
|
||||
|
||||
def handle_call({:invite, account, user_id}, _from, %{room: room, state_set: state_set} = state) do
|
||||
invite_event = Event.Invite.new(room, account, user_id)
|
||||
def handle_call(
|
||||
{:invite, account, user_id, avatar_url, displayname},
|
||||
_from,
|
||||
%{room: room, state_set: state_set} = state
|
||||
) do
|
||||
invite_event = Event.Invite.new(room, account, user_id, avatar_url, displayname)
|
||||
|
||||
case Repo.transaction(process_event(room, state_set, invite_event)) do
|
||||
{:ok, {state_set, room, _}} -> {:reply, :ok, %{state | state_set: state_set, room: room}}
|
||||
|
@ -323,11 +333,12 @@ defmodule Architex.RoomServer do
|
|||
end
|
||||
|
||||
def handle_call(
|
||||
{:kick, account, %Kick{user_id: user_id, reason: reason}},
|
||||
{:kick, account, %Kick{user_id: user_id, reason: reason}, avatar_url, displayname},
|
||||
_from,
|
||||
%{room: room, state_set: state_set} = state
|
||||
) do
|
||||
kick_event = Event.Kick.new(room, account, user_id, reason)
|
||||
kick_event =
|
||||
Event.Kick.new(room, account, to_string(user_id), avatar_url, displayname, reason)
|
||||
|
||||
case Repo.transaction(process_event(room, state_set, kick_event)) do
|
||||
{:ok, {state_set, room, _}} -> {:reply, :ok, %{state | state_set: state_set, room: room}}
|
||||
|
@ -336,11 +347,11 @@ defmodule Architex.RoomServer do
|
|||
end
|
||||
|
||||
def handle_call(
|
||||
{:ban, account, %Kick{user_id: user_id, reason: reason}},
|
||||
{:ban, account, %Ban{user_id: user_id, reason: reason}, avatar_url, displayname},
|
||||
_from,
|
||||
%{room: room, state_set: state_set} = state
|
||||
) do
|
||||
ban_event = Event.Ban.new(room, account, user_id, reason)
|
||||
ban_event = Event.Ban.new(room, account, to_string(user_id), avatar_url, displayname, reason)
|
||||
|
||||
case Repo.transaction(process_event(room, state_set, ban_event)) do
|
||||
{:ok, {state_set, room, _}} -> {:reply, :ok, %{state | state_set: state_set, room: room}}
|
||||
|
@ -348,8 +359,12 @@ defmodule Architex.RoomServer do
|
|||
end
|
||||
end
|
||||
|
||||
def handle_call({:unban, account, user_id}, _from, %{room: room, state_set: state_set} = state) do
|
||||
unban_event = Event.Unban.new(room, account, user_id)
|
||||
def handle_call(
|
||||
{:unban, account, user_id, avatar_url, displayname},
|
||||
_from,
|
||||
%{room: room, state_set: state_set} = state
|
||||
) do
|
||||
unban_event = Event.Unban.new(room, account, user_id, avatar_url, displayname)
|
||||
|
||||
case Repo.transaction(process_event(room, state_set, unban_event)) do
|
||||
{:ok, {state_set, room, _}} -> {:reply, :ok, %{state | state_set: state_set, room: room}}
|
||||
|
@ -578,12 +593,16 @@ defmodule Architex.RoomServer do
|
|||
end
|
||||
|
||||
# Get the events for room creation for inviting other users.
|
||||
@spec room_creation_invite_events(Account.t(), [String.t()] | nil, Room.t(), boolean() | nil) ::
|
||||
@spec room_creation_invite_events(Account.t(), [UserId.t()] | nil, Room.t(), boolean() | nil) ::
|
||||
[%Event{}]
|
||||
defp room_creation_invite_events(_, nil, _, _), do: []
|
||||
|
||||
defp room_creation_invite_events(account, invite_user_ids, room, is_direct) do
|
||||
Enum.map(invite_user_ids, &Event.Invite.new(room, account, &1, is_direct))
|
||||
Enum.map(invite_user_ids, fn user_id ->
|
||||
{avatar_url, displayname} = UserId.try_get_user_information(user_id)
|
||||
|
||||
Event.Invite.new(room, account, to_string(user_id), avatar_url, displayname, is_direct)
|
||||
end)
|
||||
end
|
||||
|
||||
defp room_creation_initial_state_events(_, nil, _), do: []
|
||||
|
|
|
@ -314,4 +314,15 @@ defmodule Architex.Event do
|
|||
{:ok, :crypto.hash(:sha256, json)}
|
||||
end
|
||||
end
|
||||
|
||||
@spec default_membership_content(String.t() | nil, String.t() | nil) :: %{
|
||||
optional(String.t()) => String.t()
|
||||
}
|
||||
def default_membership_content(avatar_url, displayname) do
|
||||
content = %{}
|
||||
content = if avatar_url, do: Map.put(content, "avatar_url", avatar_url), else: content
|
||||
content = if displayname, do: Map.put(content, "displayname", displayname), else: content
|
||||
|
||||
content
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,16 +2,21 @@ defmodule Architex.Event.Join do
|
|||
alias Architex.{Event, Account, Room}
|
||||
|
||||
@spec new(Room.t(), Account.t()) :: %Event{}
|
||||
def new(room, %Account{localpart: localpart} = sender) do
|
||||
def new(
|
||||
room,
|
||||
%Account{localpart: localpart, avatar_url: avatar_url, displayname: displayname} = sender
|
||||
) do
|
||||
mxid = Architex.get_mxid(localpart)
|
||||
|
||||
content =
|
||||
Event.default_membership_content(avatar_url, displayname)
|
||||
|> Map.put("membership", "join")
|
||||
|
||||
%Event{
|
||||
Event.new(room, sender)
|
||||
| type: "m.room.member",
|
||||
state_key: mxid,
|
||||
content: %{
|
||||
"membership" => "join"
|
||||
}
|
||||
content: content
|
||||
}
|
||||
end
|
||||
end
|
||||
|
@ -50,6 +55,7 @@ end
|
|||
|
||||
defmodule Architex.Event.PowerLevels do
|
||||
alias Architex.{Event, Account, Room}
|
||||
alias Architex.Types.UserId
|
||||
alias ArchitexWeb.Client.Request.CreateRoom
|
||||
alias ArchitexWeb.Client.Request.CreateRoom.PowerLevelContentOverride
|
||||
|
||||
|
@ -67,7 +73,7 @@ defmodule Architex.Event.PowerLevels do
|
|||
Room.t(),
|
||||
Account.t(),
|
||||
CreateRoom.PowerLevelContentOverride.t(),
|
||||
[String.t()] | nil,
|
||||
[UserId.t()] | nil,
|
||||
String.t() | nil
|
||||
) :: %Event{}
|
||||
def create_room_new(room, sender, nil, invite_ids, preset) do
|
||||
|
@ -101,7 +107,7 @@ defmodule Architex.Event.PowerLevels do
|
|||
# This overrides the content override, but the spec is not clear on this.
|
||||
users =
|
||||
if preset == "trusted_private_chat" and invite_ids do
|
||||
invite_users_pls = Enum.into(invite_ids, %{}, &{&1, creator_pl})
|
||||
invite_users_pls = Enum.into(invite_ids, %{}, &{to_string(&1), creator_pl})
|
||||
Map.merge(users, invite_users_pls)
|
||||
else
|
||||
users
|
||||
|
@ -216,9 +222,19 @@ end
|
|||
defmodule Architex.Event.Invite do
|
||||
alias Architex.{Event, Account, Room}
|
||||
|
||||
@spec new(Room.t(), Account.t(), String.t(), boolean() | nil) :: %Event{}
|
||||
def new(room, sender, user_id, is_direct \\ nil) do
|
||||
content = %{"membership" => "invite"}
|
||||
@spec new(
|
||||
Room.t(),
|
||||
Account.t(),
|
||||
String.t(),
|
||||
String.t() | nil,
|
||||
String.t() | nil,
|
||||
boolean() | nil
|
||||
) :: %Event{}
|
||||
def new(room, sender, user_id, avatar_url, displayname, is_direct \\ nil) do
|
||||
content =
|
||||
Event.default_membership_content(avatar_url, displayname)
|
||||
|> Map.put("membership", "invite")
|
||||
|
||||
content = if is_direct != nil, do: Map.put(content, "is_direct", is_direct), else: content
|
||||
|
||||
%Event{
|
||||
|
@ -234,14 +250,16 @@ defmodule Architex.Event.Leave do
|
|||
alias Architex.{Event, Account, Room}
|
||||
|
||||
@spec new(Room.t(), Account.t()) :: %Event{}
|
||||
def new(room, sender) do
|
||||
def new(room, %Account{avatar_url: avatar_url, displayname: displayname} = sender) do
|
||||
content =
|
||||
Event.default_membership_content(avatar_url, displayname)
|
||||
|> Map.put("membership", "leave")
|
||||
|
||||
%Event{
|
||||
Event.new(room, sender)
|
||||
| type: "m.room.member",
|
||||
state_key: Account.get_mxid(sender),
|
||||
content: %{
|
||||
"membership" => "leave"
|
||||
}
|
||||
content: content
|
||||
}
|
||||
end
|
||||
end
|
||||
|
@ -249,9 +267,19 @@ end
|
|||
defmodule Architex.Event.Kick do
|
||||
alias Architex.{Event, Account, Room}
|
||||
|
||||
@spec new(Room.t(), Account.t(), String.t(), String.t() | nil) :: %Event{}
|
||||
def new(room, sender, user_id, reason \\ nil) do
|
||||
content = %{"membership" => "leave"}
|
||||
@spec new(
|
||||
Room.t(),
|
||||
Account.t(),
|
||||
String.t(),
|
||||
String.t() | nil,
|
||||
String.t() | nil,
|
||||
String.t() | nil
|
||||
) :: %Event{}
|
||||
def new(room, sender, user_id, avatar_url, displayname, reason \\ nil) do
|
||||
content =
|
||||
Event.default_membership_content(avatar_url, displayname)
|
||||
|> Map.put("membership", "leave")
|
||||
|
||||
content = if reason, do: Map.put(content, "reason", reason), else: content
|
||||
|
||||
%Event{
|
||||
|
@ -266,9 +294,19 @@ end
|
|||
defmodule Architex.Event.Ban do
|
||||
alias Architex.{Event, Account, Room}
|
||||
|
||||
@spec new(Room.t(), Account.t(), String.t(), String.t() | nil) :: %Event{}
|
||||
def new(room, sender, user_id, reason \\ nil) do
|
||||
content = %{"membership" => "ban"}
|
||||
@spec new(
|
||||
Room.t(),
|
||||
Account.t(),
|
||||
String.t(),
|
||||
String.t() | nil,
|
||||
String.t() | nil,
|
||||
String.t() | nil
|
||||
) :: %Event{}
|
||||
def new(room, sender, user_id, avatar_url, displayname, reason \\ nil) do
|
||||
content =
|
||||
Event.default_membership_content(avatar_url, displayname)
|
||||
|> Map.put("membership", "ban")
|
||||
|
||||
content = if reason, do: Map.put(content, "reason", reason), else: content
|
||||
|
||||
%Event{
|
||||
|
@ -283,15 +321,17 @@ end
|
|||
defmodule Architex.Event.Unban do
|
||||
alias Architex.{Event, Account, Room}
|
||||
|
||||
@spec new(Room.t(), Account.t(), String.t()) :: %Event{}
|
||||
def new(room, sender, user_id) do
|
||||
@spec new(Room.t(), Account.t(), String.t(), String.t() | nil, String.t() | nil) :: %Event{}
|
||||
def new(room, sender, avatar_url, displayname, user_id) do
|
||||
content =
|
||||
Event.default_membership_content(avatar_url, displayname)
|
||||
|> Map.put("membership", "leave")
|
||||
|
||||
%Event{
|
||||
Event.new(room, sender)
|
||||
| type: "m.room.member",
|
||||
state_key: user_id,
|
||||
content: %{
|
||||
"membership" => "leave"
|
||||
}
|
||||
content: content
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
defmodule Architex.Types.UserId do
|
||||
use Ecto.Type
|
||||
|
||||
import Ecto.Query
|
||||
|
||||
alias Architex.{Repo, Account}
|
||||
alias Architex.Types.UserId
|
||||
alias ArchitexWeb.Federation.HTTPClient
|
||||
|
||||
@type t :: %__MODULE__{
|
||||
localpart: String.t(),
|
||||
|
@ -53,4 +57,37 @@ defmodule Architex.Types.UserId do
|
|||
|
||||
def dump(%UserId{} = user_id), do: {:ok, to_string(user_id)}
|
||||
def dump(_), do: :error
|
||||
|
||||
# TODO: Seems out of place here.
|
||||
@spec try_get_user_information(UserId.t()) :: {String.t() | nil, String.t() | nil}
|
||||
def try_get_user_information(%UserId{localpart: localpart, domain: domain} = user_id) do
|
||||
if domain == Architex.server_name() do
|
||||
# Get information about a user on this homeserver.
|
||||
query =
|
||||
Account
|
||||
|> where([a], a.localpart == ^localpart)
|
||||
|> select([a], {a.avatar_url, a.displayname})
|
||||
|
||||
case Repo.one(query) do
|
||||
nil -> {nil, nil}
|
||||
info -> info
|
||||
end
|
||||
else
|
||||
# Get information about a user on another homeserver.
|
||||
client = HTTPClient.client(domain)
|
||||
|
||||
case HTTPClient.query_profile(client, to_string(user_id)) do
|
||||
{:ok, response} ->
|
||||
avatar_url = Map.get(response, "avatar_url")
|
||||
avatar_url = if is_binary(avatar_url), do: avatar_url
|
||||
displayname = Map.get(response, "displayname")
|
||||
displayname = if is_binary(displayname), do: displayname
|
||||
|
||||
{avatar_url, displayname}
|
||||
|
||||
{:error, _, _} ->
|
||||
{nil, nil}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
defmodule ArchitexWeb.Client.Request.Ban do
|
||||
use ArchitexWeb.APIRequest
|
||||
|
||||
alias Architex.Types.UserId
|
||||
|
||||
@type t :: %__MODULE__{
|
||||
user_id: String.t(),
|
||||
user_id: UserId.t(),
|
||||
reason: String.t() | nil
|
||||
}
|
||||
|
||||
@primary_key false
|
||||
embedded_schema do
|
||||
field :user_id, :string
|
||||
field :user_id, UserId
|
||||
field :reason, :string
|
||||
end
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
defmodule ArchitexWeb.Client.Request.CreateRoom do
|
||||
use ArchitexWeb.APIRequest
|
||||
|
||||
alias Architex.Types.UserId
|
||||
|
||||
defmodule PowerLevelContentOverride do
|
||||
use Ecto.Schema
|
||||
|
||||
|
@ -97,7 +99,7 @@ defmodule ArchitexWeb.Client.Request.CreateRoom do
|
|||
room_alias_name: String.t() | nil,
|
||||
name: String.t() | nil,
|
||||
topic: String.t() | nil,
|
||||
invite: list(String.t()) | nil,
|
||||
invite: [UserId.t()] | nil,
|
||||
room_version: String.t() | nil,
|
||||
preset: String.t() | nil,
|
||||
is_direct: boolean() | nil,
|
||||
|
@ -113,7 +115,7 @@ defmodule ArchitexWeb.Client.Request.CreateRoom do
|
|||
field :room_alias_name, :string
|
||||
field :name, :string
|
||||
field :topic, :string
|
||||
field :invite, {:array, :string}
|
||||
field :invite, {:array, UserId}
|
||||
field :room_version, :string
|
||||
field :preset, :string
|
||||
field :is_direct, :boolean
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
defmodule ArchitexWeb.Client.Request.Kick do
|
||||
use ArchitexWeb.APIRequest
|
||||
|
||||
alias Architex.Types.UserId
|
||||
|
||||
@type t :: %__MODULE__{
|
||||
user_id: String.t(),
|
||||
user_id: UserId.t(),
|
||||
reason: String.t() | nil
|
||||
}
|
||||
|
||||
@primary_key false
|
||||
embedded_schema do
|
||||
field :user_id, :string
|
||||
field :user_id, UserId
|
||||
field :reason, :string
|
||||
end
|
||||
|
||||
|
|
|
@ -63,9 +63,11 @@ defmodule ArchitexWeb.Client.RoomController do
|
|||
"room_id" => room_id,
|
||||
"user_id" => user_id
|
||||
}) do
|
||||
with {:ok, _} <- UserId.cast(user_id),
|
||||
with {:ok, user_id_struct} <- UserId.cast(user_id),
|
||||
{:ok, pid} <- RoomServer.get_room_server(room_id) do
|
||||
case RoomServer.invite(pid, account, user_id) do
|
||||
{avatar_url, displayname} = UserId.try_get_user_information(user_id_struct)
|
||||
|
||||
case RoomServer.invite(pid, account, user_id, avatar_url, displayname) do
|
||||
:ok ->
|
||||
conn
|
||||
|> send_resp(200, [])
|
||||
|
@ -135,9 +137,11 @@ defmodule ArchitexWeb.Client.RoomController do
|
|||
Action for POST /_matrix/client/r0/rooms/{roomId}/kick.
|
||||
"""
|
||||
def kick(%Conn{assigns: %{account: account}} = conn, %{"room_id" => room_id} = params) do
|
||||
with {:ok, request} <- Kick.parse(params),
|
||||
with {:ok, %Kick{user_id: user_id} = request} <- Kick.parse(params),
|
||||
{:ok, pid} <- RoomServer.get_room_server(room_id) do
|
||||
case RoomServer.kick(pid, account, request) do
|
||||
{avatar_url, displayname} = UserId.try_get_user_information(user_id)
|
||||
|
||||
case RoomServer.kick(pid, account, request, avatar_url, displayname) do
|
||||
:ok ->
|
||||
conn
|
||||
|> send_resp(200, [])
|
||||
|
@ -158,9 +162,11 @@ defmodule ArchitexWeb.Client.RoomController do
|
|||
Action for POST /_matrix/client/r0/rooms/{roomId}/ban.
|
||||
"""
|
||||
def ban(%Conn{assigns: %{account: account}} = conn, %{"room_id" => room_id} = params) do
|
||||
with {:ok, request} <- Ban.parse(params),
|
||||
with {:ok, %Ban{user_id: user_id} = request} <- Ban.parse(params),
|
||||
{:ok, pid} <- RoomServer.get_room_server(room_id) do
|
||||
case RoomServer.ban(pid, account, request) do
|
||||
{avatar_url, displayname} = UserId.try_get_user_information(user_id)
|
||||
|
||||
case RoomServer.ban(pid, account, request, avatar_url, displayname) do
|
||||
:ok ->
|
||||
conn
|
||||
|> send_resp(200, [])
|
||||
|
@ -184,9 +190,11 @@ defmodule ArchitexWeb.Client.RoomController do
|
|||
"room_id" => room_id,
|
||||
"user_id" => user_id
|
||||
}) do
|
||||
case RoomServer.get_room_server(room_id) do
|
||||
{:ok, pid} ->
|
||||
case RoomServer.unban(pid, account, user_id) do
|
||||
with {:ok, user_id_struct} <- UserId.cast(user_id),
|
||||
{:ok, pid} <- RoomServer.get_room_server(room_id) do
|
||||
{avatar_url, displayname} = UserId.try_get_user_information(user_id_struct)
|
||||
|
||||
case RoomServer.unban(pid, account, user_id, avatar_url, displayname) do
|
||||
:ok ->
|
||||
conn
|
||||
|> send_resp(200, [])
|
||||
|
@ -195,9 +203,9 @@ defmodule ArchitexWeb.Client.RoomController do
|
|||
{:error, _} ->
|
||||
put_error(conn, :unknown)
|
||||
end
|
||||
|
||||
{:error, :not_found} ->
|
||||
put_error(conn, :not_found, "The given room was not found.")
|
||||
else
|
||||
:error -> put_error(conn, :invalid_param, "Given user ID is invalid.")
|
||||
{:error, :not_found} -> put_error(conn, :not_found, "The given room was not found.")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -33,6 +33,8 @@ defmodule ArchitexWeb.Federation.HTTPClient do
|
|||
"""
|
||||
@spec client(String.t()) :: Tesla.Client.t()
|
||||
def client(server_name) do
|
||||
# TODO: When implementing resolving homeservers, probably create
|
||||
# a homeserver struct instead of using domain names directly.
|
||||
Tesla.client(
|
||||
[
|
||||
{Tesla.Middleware.Opts, [server_name: server_name]},
|
||||
|
|
Loading…
Reference in a new issue