matrix-mcp: join invites from the own homeserver automatically
This commit is contained in:
parent
a47edaec5b
commit
2728a02369
1 changed files with 64 additions and 2 deletions
|
|
@ -17,7 +17,10 @@
|
|||
use std::collections::HashSet;
|
||||
use std::hash::BuildHasher;
|
||||
|
||||
use matrix_sdk::{Client, ruma::OwnedRoomId};
|
||||
use matrix_sdk::{
|
||||
Client,
|
||||
ruma::{OwnedRoomId, ServerName, UserId},
|
||||
};
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use crate::{handlers, wake};
|
||||
|
|
@ -127,13 +130,41 @@ pub async fn sweep_invites<S: BuildHasher>(
|
|||
// from the unread sweep's `<room>` key) so the two never collide; the
|
||||
// harness coalesces the wake iff the summary is new or changed, so
|
||||
// re-upserting an unchanged invite every tick is a cheap no-op.
|
||||
let own_server = client.user_id().map(|u| u.server_name().to_owned());
|
||||
for room in ¤t {
|
||||
let room_id = room.room_id().to_owned();
|
||||
let inviter = room
|
||||
.invite_details()
|
||||
.await
|
||||
.ok()
|
||||
.and_then(|d| d.inviter)
|
||||
.map(|m| m.user_id().to_owned());
|
||||
// An invite from the swarm's own homeserver is trusted: join it
|
||||
// without waking the agent. A failed join falls through to the todo
|
||||
// below, so the invite is never lost.
|
||||
if is_local(own_server.as_deref(), inviter.as_deref()) {
|
||||
match room.join().await {
|
||||
Ok(()) => {
|
||||
tracing::info!(room = %room_id, inviter = ?inviter, "matrix: auto-joined an invite from the swarm homeserver");
|
||||
continue;
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::warn!(error = %e, room = %room_id, "matrix: auto-join failed; leaving the invite as a todo");
|
||||
}
|
||||
}
|
||||
}
|
||||
let label = room.name().unwrap_or_else(|| room_id.to_string());
|
||||
let source = match &inviter {
|
||||
Some(u) if !is_local(own_server.as_deref(), Some(u)) => {
|
||||
format!("an external matrix user ({u})")
|
||||
}
|
||||
Some(u) => u.to_string(),
|
||||
None => "an unknown inviter".to_owned(),
|
||||
};
|
||||
let summary = wake::tag_account(
|
||||
account_tag,
|
||||
format!(
|
||||
"[matrix] invited to {label} ({room_id}) — \
|
||||
"[matrix] invited to {label} ({room_id}) by {source} — \
|
||||
use list_invites to see pending invites, resolve_invite to accept or reject"
|
||||
),
|
||||
);
|
||||
|
|
@ -148,6 +179,12 @@ pub async fn sweep_invites<S: BuildHasher>(
|
|||
}
|
||||
}
|
||||
|
||||
/// Whether `inviter` is a user of the agent's own homeserver. No known
|
||||
/// inviter or no known own server counts as external.
|
||||
fn is_local(own: Option<&ServerName>, inviter: Option<&UserId>) -> bool {
|
||||
matches!((own, inviter), (Some(own), Some(u)) if u.server_name() == own)
|
||||
}
|
||||
|
||||
/// Todo dedup key for a pending invite. Namespaced with an `invite:`
|
||||
/// prefix so an invited room and an unread room (which the unread sweep
|
||||
/// keys by bare room id) never share a todo row. `pub(crate)` so the
|
||||
|
|
@ -155,3 +192,28 @@ pub async fn sweep_invites<S: BuildHasher>(
|
|||
pub(crate) fn invite_key(room_id: &matrix_sdk::ruma::RoomId) -> String {
|
||||
format!("invite:{room_id}")
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod invite_tests {
|
||||
use matrix_sdk::ruma::{server_name, user_id};
|
||||
|
||||
use super::is_local;
|
||||
|
||||
#[test]
|
||||
fn only_an_inviter_on_the_own_homeserver_is_local() {
|
||||
let own = server_name!("pr1ma.darkest.space");
|
||||
assert!(is_local(
|
||||
Some(own),
|
||||
Some(user_id!("@mara:pr1ma.darkest.space"))
|
||||
));
|
||||
assert!(!is_local(Some(own), Some(user_id!("@mara:matrix.org"))));
|
||||
// A look-alike server name is not the own homeserver.
|
||||
assert!(!is_local(
|
||||
Some(own),
|
||||
Some(user_id!("@x:pr1ma.darkest.space.evil.example"))
|
||||
));
|
||||
// Anything unknown is external.
|
||||
assert!(!is_local(Some(own), None));
|
||||
assert!(!is_local(None, Some(user_id!("@mara:pr1ma.darkest.space"))));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue