Skip to content

Commit 37cfc06

Browse files
committed
Merge branch 'main' into pantalaimon
2 parents 50db541 + bcbcf5d commit 37cfc06

File tree

10 files changed

+549
-137
lines changed

10 files changed

+549
-137
lines changed

lib/irc/command.ex

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
##
2-
# Copyright (C) 2021-2022 Valentin Lorentz
2+
# Copyright (C) 2021-2023 Valentin Lorentz
33
#
44
# This program is free software: you can redistribute it and/or modify
55
# it under the terms of the GNU Affero General Public License version 3,
@@ -251,6 +251,8 @@ defmodule M51.Irc.Command do
251251
252252
"""
253253
def downgrade(command, capabilities) do
254+
original_tags = command.tags
255+
254256
# downgrade echo-message
255257
command =
256258
if Enum.member?(capabilities, :echo_message) do
@@ -319,6 +321,47 @@ defmodule M51.Irc.Command do
319321
nil
320322
end
321323

324+
%{command: "REDACT"} ->
325+
if Enum.member?(capabilities, :message_redaction) do
326+
command
327+
else
328+
sender = Map.get(original_tags, "account")
329+
330+
display_name =
331+
case Map.get(original_tags, "+draft/display-name", nil) do
332+
dn when is_binary(dn) -> " (#{dn})"
333+
_ -> ""
334+
end
335+
336+
tags = Map.drop(command.tags, ["+draft/display-name", "account"])
337+
338+
command =
339+
case command do
340+
%{params: [channel, msgid, reason]} ->
341+
%M51.Irc.Command{
342+
tags: Map.put(tags, "+draft/reply", msgid),
343+
source: "server.",
344+
command: "NOTICE",
345+
params: [channel, "#{sender}#{display_name} deleted an event: #{reason}"]
346+
}
347+
348+
%{params: [channel, msgid]} ->
349+
%M51.Irc.Command{
350+
tags: Map.put(tags, "+draft/reply", msgid),
351+
source: "server.",
352+
command: "NOTICE",
353+
params: [channel, "#{sender}#{display_name} deleted an event"]
354+
}
355+
356+
_ ->
357+
# shouldn't happen
358+
nil
359+
end
360+
361+
# run downgrade() recursively in order to drop the new tags if necessary
362+
downgrade(command, capabilities)
363+
end
364+
322365
%{command: "TAGMSG"} ->
323366
if Enum.member?(capabilities, :message_tags) do
324367
command

lib/irc/handler.ex

Lines changed: 96 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,24 @@ defmodule M51.IrcConn.Handler do
7272
# https://ircv3.net/specs/extensions/multiline
7373
"draft/multiline" => {:multiline, "max-bytes=#{@multiline_max_bytes}"},
7474

75+
# https://github.com/progval/ircv3-specifications/blob/redaction/extensions/message-redaction.md
76+
"draft/message-redaction" => {:message_redaction, nil},
77+
78+
# https://github.com/ircv3/ircv3-specifications/pull/527
79+
"draft/no-implicit-names" => {:no_implicit_names, nil},
80+
7581
# https://ircv3.net/specs/extensions/sasl-3.1
7682
"sasl" => {:sasl, "PLAIN"},
7783

84+
# https://github.com/ircv3/ircv3-specifications/pull/520
85+
"draft/sasl-ir" => {:sasl_ir, nil},
86+
7887
# https://ircv3.net/specs/extensions/server-time
7988
"server-time" => {:server_time, nil},
8089

90+
# https://ircv3.net/specs/extensions/standard-replies
91+
"standard-replies" => {:standard_replies, nil},
92+
8193
# https://ircv3.net/specs/extensions/userhost-in-names
8294
# not really useful; but kiwiirc/irc-framework interprets "foo:example.org"
8395
# as {nick: '', user: '', hostname: 'foo:example.org'} without this,
@@ -398,10 +410,16 @@ defmodule M51.IrcConn.Handler do
398410

399411
nil
400412

401-
{"AUTHENTICATE", ["PLAIN" | _]} ->
413+
{"AUTHENTICATE", ["PLAIN"]} ->
402414
send.(%M51.Irc.Command{command: "AUTHENTICATE", params: ["+"]})
403415
nil
404416

417+
{"AUTHENTICATE", ["PLAIN" | params]} ->
418+
# SASL-IR: https://github.com/ircv3/ircv3-specifications/pull/520
419+
# Call this function recursively without the mechanism, to be handled
420+
# by the next case below
421+
handle_connreg(sup_pid, %{command | params: params}, nick)
422+
405423
{"AUTHENTICATE", [param | _]} ->
406424
# this catches both invalid mechs and actual PLAIN message.
407425
# FIXME: add some state to tell the two apart.
@@ -599,7 +617,7 @@ defmodule M51.IrcConn.Handler do
599617
"CLIENTTAGDENY=*,-draft/react,-draft/reply",
600618
"CHANLIMIT=",
601619
"CHANTYPES=#!",
602-
"CHATHISTORY=1000",
620+
"CHATHISTORY=100",
603621
"MAXTARGETS=1",
604622
# https://github.com/ircv3/ircv3-specifications/pull/510
605623
"MSGREFTYPES=msgid",
@@ -903,6 +921,27 @@ defmodule M51.IrcConn.Handler do
903921
{"TAGMSG", _} ->
904922
send_needmoreparams.()
905923

924+
{"REDACT", [channel, targetmsgid, reason | _]} ->
925+
send_redact(
926+
sup_pid,
927+
channel,
928+
Map.get(command.tags, "label"),
929+
targetmsgid,
930+
reason
931+
)
932+
933+
{"REDACT", [channel, targetmsgid | _]} ->
934+
send_redact(
935+
sup_pid,
936+
channel,
937+
Map.get(command.tags, "label"),
938+
targetmsgid,
939+
nil
940+
)
941+
942+
{"REDACT", _} ->
943+
send_needmoreparams.()
944+
906945
{"CHATHISTORY", ["TARGETS", _ts1, _ts2, _limit | _]} ->
907946
# This is mainly used for PMs, and we don't support those yet; so there
908947
# is little point in storing state to actually implement it
@@ -1374,6 +1413,61 @@ defmodule M51.IrcConn.Handler do
13741413
end
13751414
end
13761415

1416+
defp send_redact(sup_pid, channel, label, targetmsgid, reason) do
1417+
writer = M51.IrcConn.Supervisor.writer(sup_pid)
1418+
matrix_client = M51.IrcConn.Supervisor.matrix_client(sup_pid)
1419+
matrix_state = M51.IrcConn.Supervisor.matrix_state(sup_pid)
1420+
send = fn cmd -> M51.IrcConn.Writer.write_command(writer, cmd) end
1421+
1422+
# If the client provided a label, use it as txnId on Matrix's side.
1423+
# This way we can parse it when receiving the echo from Matrix's event
1424+
# stream instead of storing state.
1425+
# Otherwise, generate a random transaction id.
1426+
1427+
nicklist =
1428+
case M51.MatrixClient.State.room_from_irc_channel(matrix_state, channel) do
1429+
{_room_id, room} -> room.members |> Map.keys()
1430+
nil -> []
1431+
end
1432+
1433+
reason =
1434+
case reason do
1435+
nil ->
1436+
nil
1437+
1438+
reason ->
1439+
{reason, _formatted_reason} = M51.Format.irc2matrix(reason, nicklist)
1440+
reason
1441+
end
1442+
1443+
result =
1444+
M51.MatrixClient.Client.send_redact(
1445+
matrix_client,
1446+
channel,
1447+
label,
1448+
targetmsgid,
1449+
reason
1450+
)
1451+
1452+
case result do
1453+
{:ok, _event_id} ->
1454+
nil
1455+
1456+
{:error, error} ->
1457+
send.(%M51.Irc.Command{
1458+
source: "server.",
1459+
command: "FAIL",
1460+
params: [
1461+
"REDACT",
1462+
"UNKNOWN_ERROR",
1463+
channel,
1464+
targetmsgid,
1465+
"Error while redacting message: " <> Kernel.inspect(error)
1466+
]
1467+
})
1468+
end
1469+
end
1470+
13771471
defp close_connection(sup_pid) do
13781472
writer = M51.IrcConn.Supervisor.writer(sup_pid)
13791473
M51.IrcConn.Writer.close(writer)

lib/matrix/raw_client.ex

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ defmodule M51.Matrix.RawClient do
2424

2525
def get(client, path, headers \\ [], options \\ []) do
2626
headers = [Authorization: "Bearer " <> client.access_token] ++ headers
27-
options = options |> Keyword.put_new(:timeout, 20000)
27+
options = options |> Keyword.put_new(:timeout, 60000)
2828

2929
url = client.base_url <> path
3030

@@ -47,7 +47,7 @@ defmodule M51.Matrix.RawClient do
4747

4848
def post(client, path, body, headers \\ [], options \\ []) do
4949
headers = [Authorization: "Bearer " <> client.access_token] ++ headers
50-
options = options |> Keyword.put_new(:timeout, 20000)
50+
options = options |> Keyword.put_new(:timeout, 60000)
5151

5252
url = client.base_url <> path
5353

@@ -71,7 +71,7 @@ defmodule M51.Matrix.RawClient do
7171

7272
def put(client, path, body, headers \\ [], options \\ []) do
7373
headers = [Authorization: "Bearer " <> client.access_token] ++ headers
74-
options = options |> Keyword.put_new(:timeout, 20000)
74+
options = options |> Keyword.put_new(:timeout, 60000)
7575

7676
url = client.base_url <> path
7777

lib/matrix_client/client.ex

Lines changed: 55 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ defmodule M51.MatrixClient.Client do
3838

3939
# timeout used for all requests sent to a homeserver.
4040
# It should be slightly larger than M51.Matrix.RawClient's timeout,
41-
@timeout 25000
41+
@timeout 65000
4242

4343
def start_link(opts) do
4444
{sup_pid, _extra_args} = opts
@@ -102,7 +102,10 @@ defmodule M51.MatrixClient.Client do
102102
body =
103103
Jason.encode!(%{
104104
"type" => "m.login.password",
105-
"user" => local_name,
105+
"identifier" => %{
106+
"type" => "m.id.user",
107+
"user" => local_name
108+
},
106109
"password" => password
107110
})
108111

@@ -147,9 +150,7 @@ defmodule M51.MatrixClient.Client do
147150

148151
%HTTPoison.Response{status_code: status_code} ->
149152
message =
150-
"Could not reach the Matrix homeserver for #{hostname}, #{url} returned HTTP #{
151-
status_code
152-
}. Make sure this is a Matrix homeserver and https://#{hostname}/.well-known/matrix/client is properly configured."
153+
"Could not reach the Matrix homeserver for #{hostname}, #{url} returned HTTP #{status_code}. Make sure this is a Matrix homeserver and https://#{hostname}/.well-known/matrix/client is properly configured."
153154

154155
{:reply, {:error, :unknown, message}, state}
155156
end
@@ -298,6 +299,42 @@ defmodule M51.MatrixClient.Client do
298299
end
299300
end
300301

302+
@impl true
303+
def handle_call({:send_redact, channel, label, event_id, reason}, _from, state) do
304+
%M51.MatrixClient.Client{
305+
state: :connected,
306+
irc_pid: irc_pid,
307+
raw_client: raw_client
308+
} = state
309+
310+
matrix_state = M51.IrcConn.Supervisor.matrix_state(irc_pid)
311+
312+
transaction_id = label_to_transaction_id(label)
313+
314+
reply =
315+
case M51.MatrixClient.State.room_from_irc_channel(matrix_state, channel) do
316+
nil ->
317+
{:reply, {:error, {:room_not_found, channel}}, state}
318+
319+
{room_id, _room} ->
320+
path =
321+
"/_matrix/client/r0/rooms/#{urlquote(room_id)}/redact/#{urlquote(event_id)}/#{transaction_id}"
322+
323+
body =
324+
case reason do
325+
reason when is_binary(reason) -> Jason.encode!(%{"reason" => reason})
326+
_ -> Jason.encode!({})
327+
end
328+
329+
case M51.Matrix.RawClient.put(raw_client, path, body) do
330+
{:ok, %{"event_id" => event_id}} -> {:ok, event_id}
331+
{:error, nil, error} -> {:error, error}
332+
end
333+
end
334+
335+
{:reply, reply, state}
336+
end
337+
301338
@impl true
302339
def handle_call({:get_event_context, channel, event_id, limit}, _from, state) do
303340
%M51.MatrixClient.Client{
@@ -320,7 +357,7 @@ defmodule M51.MatrixClient.Client do
320357

321358
case M51.Matrix.RawClient.get(raw_client, path) do
322359
{:ok, events} -> {:ok, events}
323-
{:error, error} -> {:error, error}
360+
{:error, nil, error} -> {:error, error}
324361
end
325362
end
326363

@@ -349,7 +386,7 @@ defmodule M51.MatrixClient.Client do
349386

350387
case M51.Matrix.RawClient.get(raw_client, path) do
351388
{:ok, events} -> {:ok, events}
352-
{:error, error} -> {:error, error}
389+
{:error, nil, error} -> {:error, error}
353390
end
354391
end
355392

@@ -487,7 +524,6 @@ defmodule M51.MatrixClient.Client do
487524
)
488525

489526
base_url
490-
491527
end
492528
end
493529

@@ -546,13 +582,23 @@ defmodule M51.MatrixClient.Client do
546582
@doc """
547583
Sends the given event object.
548584
549-
If 'label' is not nil, it will be passed as a 'label' message tagt when
585+
If 'label' is not nil, it will be passed as a 'label' message tag when
550586
the event is seen in the event stream.
551587
"""
552588
def send_event(pid, channel, label, event_type, event) do
553589
GenServer.call(pid, {:send_event, channel, event_type, label, event}, @timeout)
554590
end
555591

592+
@doc """
593+
Asks the server to redact the event with the given id
594+
595+
If 'label' is not nil, it will be passed as a 'label' message tag when
596+
the event is seen in the event stream.
597+
"""
598+
def send_redact(pid, channel, label, event_id, reason) do
599+
GenServer.call(pid, {:send_redact, channel, label, event_id, reason}, @timeout)
600+
end
601+
556602
@doc """
557603
Returns events that happened just before or after the specified event_id.
558604

0 commit comments

Comments
 (0)