matrix: skip room invite when the user is already invited or joined
This commit is contained in:
parent
3795ffc1c6
commit
d090df3c36
1 changed files with 45 additions and 2 deletions
|
|
@ -1353,9 +1353,41 @@ async fn invite_to_room(
|
|||
invite_user_id(client, admin_token, room_id, &user_id).await
|
||||
}
|
||||
|
||||
/// Fetch a user's current membership in a room via the admin token, or
|
||||
/// `None` if there is no membership event (never invited) or the lookup
|
||||
/// fails. Returns the raw membership string (`invite`, `join`, `leave`, …).
|
||||
async fn room_membership(
|
||||
client: &reqwest::Client,
|
||||
admin_token: &str,
|
||||
encoded_room_id: &str,
|
||||
user_id: &str,
|
||||
) -> Option<String> {
|
||||
// `:` must be percent-encoded in both the room-id and user-id path
|
||||
// segments; `@` and `!` are permitted path characters per RFC 3986.
|
||||
let encoded_user = user_id.replace(':', "%3A");
|
||||
let url = format!(
|
||||
"{MATRIX_HTTP}/_matrix/client/v3/rooms/{encoded_room_id}/state/m.room.member/{encoded_user}"
|
||||
);
|
||||
let resp = client
|
||||
.get(&url)
|
||||
.bearer_auth(admin_token)
|
||||
.send()
|
||||
.await
|
||||
.ok()?;
|
||||
if !resp.status().is_success() {
|
||||
// 404 = no membership event yet; anything else we treat as "unknown"
|
||||
// and let the caller fall through to the invite attempt.
|
||||
return None;
|
||||
}
|
||||
let body = resp.json::<serde_json::Value>().await.ok()?;
|
||||
body["membership"].as_str().map(ToOwned::to_owned)
|
||||
}
|
||||
|
||||
/// Invite a fully-qualified Matrix user id (`@user:server`) to `room_id`
|
||||
/// using the admin token. Idempotent: a 403 `M_FORBIDDEN` / `M_BAD_STATE`
|
||||
/// (already a member or pending invite) is treated as success.
|
||||
/// using the admin token. Idempotent: a user who is already a member or
|
||||
/// already has a pending invite is left untouched (no fresh invite is sent,
|
||||
/// so they are not re-notified), and a 403 `M_FORBIDDEN` / `M_BAD_STATE`
|
||||
/// from a racing invite is still treated as success.
|
||||
async fn invite_user_id(
|
||||
client: &reqwest::Client,
|
||||
admin_token: &str,
|
||||
|
|
@ -1365,6 +1397,17 @@ async fn invite_user_id(
|
|||
// `:` must be percent-encoded in the room-id path segment; `!` is
|
||||
// permitted in URL path characters per RFC 3986.
|
||||
let encoded_room_id = room_id.replace(':', "%3A");
|
||||
|
||||
// Skip the invite entirely when the user is already invited or joined.
|
||||
// Re-POSTing an invite to a pending member re-sends the invite event,
|
||||
// which re-notifies the agent on every provisioning sweep.
|
||||
if let Some(membership) = room_membership(client, admin_token, &encoded_room_id, user_id).await
|
||||
&& matches!(membership.as_str(), "invite" | "join")
|
||||
{
|
||||
tracing::debug!(%user_id, %room_id, %membership, "matrix: invite skipped (already a member/invited)");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let url = format!("{MATRIX_HTTP}/_matrix/client/v3/rooms/{encoded_room_id}/invite");
|
||||
let resp = client
|
||||
.post(&url)
|
||||
|
|
|
|||
Loading…
Reference in a new issue