matrix-mcp: sync through a filter that drops presence, typing and unbounded state
This commit is contained in:
parent
b8b55ba23e
commit
e436d356f3
1 changed files with 57 additions and 11 deletions
|
|
@ -749,6 +749,27 @@ async fn restore_account(cfg: &AccountCfg, is_primary: bool) -> Result<Option<Cl
|
||||||
Ok(Some(client))
|
Ok(Some(client))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The server-side sync filter every account uses. The default (no filter)
|
||||||
|
/// ships presence, typing notices, full member state and unbounded
|
||||||
|
/// timelines in every delta, about 50 MB/h on a busy public homeserver.
|
||||||
|
/// This keeps what the daemon uses: room messages (short timelines; unread
|
||||||
|
/// scanning pages back with `/messages`), read receipts (the agent's own
|
||||||
|
/// receipt marks what it has read), invites and account data. Members are
|
||||||
|
/// lazy-loaded: `Room::members` fetches the full list on demand.
|
||||||
|
fn sync_filter() -> matrix_sdk::ruma::api::client::sync::sync_events::v3::Filter {
|
||||||
|
use matrix_sdk::ruma::api::client::{filter::FilterDefinition, sync::sync_events::v3::Filter};
|
||||||
|
let definition: FilterDefinition = serde_json::from_value(serde_json::json!({
|
||||||
|
"presence": {"not_types": ["*"]},
|
||||||
|
"room": {
|
||||||
|
"ephemeral": {"not_types": ["m.typing"]},
|
||||||
|
"state": {"lazy_load_members": true},
|
||||||
|
"timeline": {"limit": 20},
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
.expect("the sync filter is a valid filter definition");
|
||||||
|
Filter::FilterDefinition(definition)
|
||||||
|
}
|
||||||
|
|
||||||
/// `client`'s sync loop, sweeping invites and unread rooms into todos tagged
|
/// `client`'s sync loop, sweeping invites and unread rooms into todos tagged
|
||||||
/// `tag` after each sync. Builds the loop only; nothing runs until it is
|
/// `tag` after each sync. Builds the loop only; nothing runs until it is
|
||||||
/// polled. Never wipes the todo set: that is `main`'s, once, at startup.
|
/// polled. Never wipes the todo set: that is `main`'s, once, at startup.
|
||||||
|
|
@ -762,17 +783,20 @@ fn account_sync_loop(client: &Client, tag: Option<String>) -> SyncLoop {
|
||||||
let unread_notified = Arc::new(tokio::sync::Mutex::new(std::collections::HashSet::new()));
|
let unread_notified = Arc::new(tokio::sync::Mutex::new(std::collections::HashSet::new()));
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
sync_client
|
sync_client
|
||||||
.sync_with_callback(SyncSettings::default(), move |_response| {
|
.sync_with_callback(
|
||||||
let client = cb_client.clone();
|
SyncSettings::default().filter(sync_filter()),
|
||||||
let invite_notified = invite_notified.clone();
|
move |_response| {
|
||||||
let unread_notified = unread_notified.clone();
|
let client = cb_client.clone();
|
||||||
let tag = tag.clone();
|
let invite_notified = invite_notified.clone();
|
||||||
async move {
|
let unread_notified = unread_notified.clone();
|
||||||
timeline::sweep_invites(&client, &invite_notified, tag.as_deref()).await;
|
let tag = tag.clone();
|
||||||
timeline::sweep_unread(&client, &unread_notified, tag.as_deref()).await;
|
async move {
|
||||||
matrix_sdk::LoopCtrl::Continue
|
timeline::sweep_invites(&client, &invite_notified, tag.as_deref()).await;
|
||||||
}
|
timeline::sweep_unread(&client, &unread_notified, tag.as_deref()).await;
|
||||||
})
|
matrix_sdk::LoopCtrl::Continue
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.context("matrix-sdk sync loop exited")?;
|
.context("matrix-sdk sync loop exited")?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -781,6 +805,28 @@ fn account_sync_loop(client: &Client, tag: Option<String>) -> SyncLoop {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
/// The filter keeps the sync cheap: no presence, no typing, lazy members,
|
||||||
|
/// short timelines. Receipts must stay: the unread check reads the
|
||||||
|
/// agent's own.
|
||||||
|
#[test]
|
||||||
|
fn the_sync_filter_drops_the_bulk_and_keeps_receipts() {
|
||||||
|
use matrix_sdk::ruma::api::client::sync::sync_events::v3::Filter;
|
||||||
|
|
||||||
|
let Filter::FilterDefinition(def) = super::sync_filter() else {
|
||||||
|
panic!("an inline definition, not a filter id");
|
||||||
|
};
|
||||||
|
let v = serde_json::to_value(&def).expect("serialize");
|
||||||
|
assert_eq!(v["presence"]["not_types"], serde_json::json!(["*"]));
|
||||||
|
let ephemeral = &v["room"]["ephemeral"]["not_types"];
|
||||||
|
assert_eq!(*ephemeral, serde_json::json!(["m.typing"]));
|
||||||
|
assert!(
|
||||||
|
!ephemeral.to_string().contains("receipt"),
|
||||||
|
"receipts are kept"
|
||||||
|
);
|
||||||
|
assert_eq!(v["room"]["state"]["lazy_load_members"], true);
|
||||||
|
assert_eq!(v["room"]["timeline"]["limit"], 20);
|
||||||
|
}
|
||||||
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use tokio::time::Instant;
|
use tokio::time::Instant;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue