fix(#2468,#2473): matrix read_room exposes in_reply_to + gates UTD sentinel on encryption
This commit is contained in:
parent
3f1643c594
commit
741852a8b4
1 changed files with 30 additions and 4 deletions
|
|
@ -63,6 +63,11 @@ pub struct TimelineEvent {
|
|||
/// (text / notice / emote); empty for events without a textual
|
||||
/// body (state changes, reactions, etc.).
|
||||
pub body: String,
|
||||
/// Event id of the message this replies to (`m.relates_to` →
|
||||
/// `m.in_reply_to`), including the reply fallback a thread message
|
||||
/// carries. `None` for top-level (non-reply) messages.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub in_reply_to_event_id: Option<String>,
|
||||
}
|
||||
|
||||
/// Resolve a room reference (id `!abc:server` OR alias `#name:server`)
|
||||
|
|
@ -143,6 +148,23 @@ fn extract_body_sync(event: &matrix_sdk::ruma::events::AnySyncTimelineEvent) ->
|
|||
}
|
||||
}
|
||||
|
||||
/// Extract the event id this message replies to, from its `m.relates_to`.
|
||||
/// Handles both a direct `m.in_reply_to` reply and the reply fallback a
|
||||
/// thread message carries. Returns `None` for non-reply messages and for
|
||||
/// non-`m.room.message` events.
|
||||
fn extract_in_reply_to(event: &matrix_sdk::ruma::events::AnySyncTimelineEvent) -> Option<String> {
|
||||
use matrix_sdk::ruma::events::room::message::Relation;
|
||||
use matrix_sdk::ruma::events::{AnySyncMessageLikeEvent, AnySyncTimelineEvent};
|
||||
let AnySyncTimelineEvent::MessageLike(AnySyncMessageLikeEvent::RoomMessage(ev)) = event else {
|
||||
return None;
|
||||
};
|
||||
match ev.as_original()?.content.relates_to.as_ref()? {
|
||||
Relation::Reply { in_reply_to } => Some(in_reply_to.event_id.to_string()),
|
||||
Relation::Thread(thread) => thread.in_reply_to.as_ref().map(|r| r.event_id.to_string()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Human-facing label for a room: its canonical alias when set,
|
||||
/// otherwise the raw room id. Used wherever a room is named in
|
||||
/// agent-facing text (wake bodies, loose-ends, the unread guard).
|
||||
|
|
@ -615,13 +637,16 @@ pub async fn read_room(client: &Client, room_ref: &str, limit: Option<usize>) ->
|
|||
let mut opts = MessagesOptions::backward();
|
||||
opts.limit = matrix_sdk::ruma::UInt::from(limit_val);
|
||||
// room.messages() transparently decrypts events in encrypted rooms.
|
||||
// UTD (unable to decrypt) events surface via `ev.kind.is_utd()` and
|
||||
// get a sentinel body so claude knows decryption failed rather than
|
||||
// seeing the raw encrypted blob.
|
||||
// UTD (unable to decrypt) events surface via `ev.kind.is_utd()`, but
|
||||
// that flag can transiently fire in a NON-encrypted room when the SDK
|
||||
// hasn't finished reclassifying a freshly-synced `m.room.encrypted`
|
||||
// cache row — so gate the sentinel on the room actually being
|
||||
// encrypted to avoid a false `[unable to decrypt]` on plaintext.
|
||||
let msgs = match room.messages(opts).await {
|
||||
Ok(m) => m,
|
||||
Err(e) => return DaemonResponse::error(format!("messages: {e}")),
|
||||
};
|
||||
let is_encrypted = room.encryption_state().is_encrypted();
|
||||
let events: Vec<TimelineEvent> = msgs
|
||||
.chunk
|
||||
.iter()
|
||||
|
|
@ -631,7 +656,7 @@ pub async fn read_room(client: &Client, room_ref: &str, limit: Option<usize>) ->
|
|||
let sender = parsed.sender().to_string();
|
||||
let origin_server_ts: i64 = parsed.origin_server_ts().0.into();
|
||||
let event_type = parsed.event_type().to_string();
|
||||
let body = if ev.kind.is_utd() {
|
||||
let body = if is_encrypted && ev.kind.is_utd() {
|
||||
"[unable to decrypt]".to_owned()
|
||||
} else {
|
||||
extract_body_sync(&parsed)
|
||||
|
|
@ -642,6 +667,7 @@ pub async fn read_room(client: &Client, room_ref: &str, limit: Option<usize>) ->
|
|||
origin_server_ts,
|
||||
event_type,
|
||||
body,
|
||||
in_reply_to_event_id: extract_in_reply_to(&parsed),
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
|
|
|||
Loading…
Reference in a new issue