Skip to content

Commit

Permalink
Add IRC -> Matrix message redaction
Browse files Browse the repository at this point in the history
  • Loading branch information
progval committed Jun 25, 2023
1 parent 96d187f commit 794a083
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 1 deletion.
76 changes: 76 additions & 0 deletions lib/irc/handler.ex
Original file line number Diff line number Diff line change
Expand Up @@ -906,6 +906,27 @@ defmodule M51.IrcConn.Handler do
{"TAGMSG", _} ->
send_needmoreparams.()

{"REDACT", [channel, targetmsgid, reason | _]} ->
send_redact(
sup_pid,
channel,
Map.get(command.tags, "label"),
targetmsgid,
reason
)

{"REDACT", [channel, targetmsgid | _]} ->
send_redact(
sup_pid,
channel,
Map.get(command.tags, "label"),
targetmsgid,
nil
)

{"REDACT", _} ->
send_needmoreparams.()

{"CHATHISTORY", ["TARGETS", _ts1, _ts2, _limit | _]} ->
# This is mainly used for PMs, and we don't support those yet; so there
# is little point in storing state to actually implement it
Expand Down Expand Up @@ -1377,6 +1398,61 @@ defmodule M51.IrcConn.Handler do
end
end

defp send_redact(sup_pid, channel, label, targetmsgid, reason) do
writer = M51.IrcConn.Supervisor.writer(sup_pid)
matrix_client = M51.IrcConn.Supervisor.matrix_client(sup_pid)
matrix_state = M51.IrcConn.Supervisor.matrix_state(sup_pid)
send = fn cmd -> M51.IrcConn.Writer.write_command(writer, cmd) end

# If the client provided a label, use it as txnId on Matrix's side.
# This way we can parse it when receiving the echo from Matrix's event
# stream instead of storing state.
# Otherwise, generate a random transaction id.

nicklist =
case M51.MatrixClient.State.room_from_irc_channel(matrix_state, channel) do
{_room_id, room} -> room.members |> Map.keys()
nil -> []
end

reason =
case reason do
nil ->
nil

reason ->
{reason, _formatted_reason} = M51.Format.irc2matrix(reason, nicklist)
reason
end

result =
M51.MatrixClient.Client.send_redact(
matrix_client,
channel,
label,
targetmsgid,
reason
)

case result do
{:ok, _event_id} ->
nil

{:error, error} ->
send.(%M51.Irc.Command{
source: "server.",
command: "FAIL",
params: [
"REDACT",
"UNKNOWN_ERROR",
channel,
targetmsgid,
"Error while redacting message: " <> Kernel.inspect(error)
]
})
end
end

defp close_connection(sup_pid) do
writer = M51.IrcConn.Supervisor.writer(sup_pid)
M51.IrcConn.Writer.close(writer)
Expand Down
46 changes: 45 additions & 1 deletion lib/matrix_client/client.ex
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,40 @@ defmodule M51.MatrixClient.Client do
end
end

@impl true
def handle_call({:send_redact, channel, label, event_id, reason}, _from, state) do
%M51.MatrixClient.Client{
state: :connected,
irc_pid: irc_pid,
raw_client: raw_client
} = state

matrix_state = M51.IrcConn.Supervisor.matrix_state(irc_pid)

transaction_id = label_to_transaction_id(label)

reply =
case M51.MatrixClient.State.room_from_irc_channel(matrix_state, channel) do
nil ->
{:reply, {:error, {:room_not_found, channel}}, state}

{room_id, _room} ->
path =
"/_matrix/client/r0/rooms/#{urlquote(room_id)}/redact/#{urlquote(event_id)}/#{urlquote(label)}"

body =
case reason do
reason when is_binary(reason) -> Jason.encode!(%{"reason" => reason})
_ -> Jason.encode!({})
end

case M51.Matrix.RawClient.put(raw_client, path, body) do
{:ok, %{"event_id" => event_id}} -> {:ok, event_id}
{:error, error} -> {:error, error}
end
end
end

@impl true
def handle_call({:get_event_context, channel, event_id, limit}, _from, state) do
%M51.MatrixClient.Client{
Expand Down Expand Up @@ -543,13 +577,23 @@ defmodule M51.MatrixClient.Client do
@doc """
Sends the given event object.
If 'label' is not nil, it will be passed as a 'label' message tagt when
If 'label' is not nil, it will be passed as a 'label' message tag when
the event is seen in the event stream.
"""
def send_event(pid, channel, label, event_type, event) do
GenServer.call(pid, {:send_event, channel, event_type, label, event}, @timeout)
end

@doc """
Asks the server to redact the event with the given id
If 'label' is not nil, it will be passed as a 'label' message tag when
the event is seen in the event stream.
"""
def send_redact(pid, channel, label, event_id, reason) do
GenServer.call(pid, {:send_redact, channel, label, event_id, reason}, @timeout)
end

@doc """
Returns events that happened just before or after the specified event_id.
Expand Down
16 changes: 16 additions & 0 deletions test/irc/handler_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -1078,4 +1078,20 @@ defmodule M51.IrcConn.HandlerTest do

assert_line("BATCH :-#{batch_id}\r\n")
end

test "redact a message for no reason", %{handler: handler} do
do_connection_registration(handler)

send(handler, cmd("REDACT #existing_room:example.org $event1"))

assert_message({:send_redact, "#existing_room:example.org", nil, "$event1", nil})
end

test "redact a message for a reason", %{handler: handler} do
do_connection_registration(handler)

send(handler, cmd("REDACT #existing_room:example.org $event1 :spam"))

assert_message({:send_redact, "#existing_room:example.org", nil, "$event1", "spam"})
end
end

0 comments on commit 794a083

Please sign in to comment.