matrix-mcp: name a downloaded attachment by its file name, not its caption
This commit is contained in:
parent
64dfe0d205
commit
eb4ce56100
1 changed files with 57 additions and 11 deletions
|
|
@ -1106,6 +1106,28 @@ pub async fn read_room(
|
||||||
DaemonResponse::ok(&collapse_edits(events))
|
DaemonResponse::ok(&collapse_edits(events))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The media source, file name and caption of an `m.file` / `m.image` /
|
||||||
|
/// `m.audio` / `m.video` message; `None` for any other type. The event's
|
||||||
|
/// `body` is the caption when the sender gave one, so the name comes from
|
||||||
|
/// `filename()` (which falls back to `body` for senders that set no
|
||||||
|
/// `filename`).
|
||||||
|
fn media_parts(
|
||||||
|
msgtype: &MessageType,
|
||||||
|
) -> Option<(
|
||||||
|
matrix_sdk::ruma::events::room::MediaSource,
|
||||||
|
String,
|
||||||
|
Option<String>,
|
||||||
|
)> {
|
||||||
|
let (source, name, caption) = match msgtype {
|
||||||
|
MessageType::File(f) => (&f.source, f.filename(), f.caption()),
|
||||||
|
MessageType::Image(i) => (&i.source, i.filename(), i.caption()),
|
||||||
|
MessageType::Audio(a) => (&a.source, a.filename(), a.caption()),
|
||||||
|
MessageType::Video(v) => (&v.source, v.filename(), v.caption()),
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
Some((source.clone(), name.to_owned(), caption.map(str::to_owned)))
|
||||||
|
}
|
||||||
|
|
||||||
/// Download the media attachment carried by `event_id` in `room` and
|
/// Download the media attachment carried by `event_id` in `room` and
|
||||||
/// write it to a local file, returning the path. Resolves the event,
|
/// write it to a local file, returning the path. Resolves the event,
|
||||||
/// extracts the `MediaSource` from its `m.file` / `m.image` / `m.audio`
|
/// extracts the `MediaSource` from its `m.file` / `m.image` / `m.audio`
|
||||||
|
|
@ -1145,16 +1167,10 @@ pub async fn download_file(
|
||||||
let Some(orig) = msg.as_original() else {
|
let Some(orig) = msg.as_original() else {
|
||||||
return DaemonResponse::error(format!("event {eid} is redacted"));
|
return DaemonResponse::error(format!("event {eid} is redacted"));
|
||||||
};
|
};
|
||||||
let (source, filename) = match &orig.content.msgtype {
|
let Some((source, filename, caption)) = media_parts(&orig.content.msgtype) else {
|
||||||
MessageType::File(f) => (f.source.clone(), f.body.clone()),
|
return DaemonResponse::error(format!(
|
||||||
MessageType::Image(i) => (i.source.clone(), i.body.clone()),
|
"event {eid} carries no file/image/audio/video attachment"
|
||||||
MessageType::Audio(a) => (a.source.clone(), a.body.clone()),
|
));
|
||||||
MessageType::Video(v) => (v.source.clone(), v.body.clone()),
|
|
||||||
_ => {
|
|
||||||
return DaemonResponse::error(format!(
|
|
||||||
"event {eid} carries no file/image/audio/video attachment"
|
|
||||||
));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
let req = MediaRequestParameters {
|
let req = MediaRequestParameters {
|
||||||
source,
|
source,
|
||||||
|
|
@ -1180,6 +1196,7 @@ pub async fn download_file(
|
||||||
DaemonResponse::ok(&serde_json::json!({
|
DaemonResponse::ok(&serde_json::json!({
|
||||||
"path": dest.display().to_string(),
|
"path": dest.display().to_string(),
|
||||||
"filename": filename,
|
"filename": filename,
|
||||||
|
"caption": caption,
|
||||||
"bytes": bytes.len(),
|
"bytes": bytes.len(),
|
||||||
"room_id": room.room_id().to_string(),
|
"room_id": room.room_id().to_string(),
|
||||||
}))
|
}))
|
||||||
|
|
@ -1298,7 +1315,7 @@ mod utd_body_tests {
|
||||||
mod unread_tests {
|
mod unread_tests {
|
||||||
use super::{
|
use super::{
|
||||||
ParsedEvent, TimelineEvent, attachment_marker, collapse_edits, extract_body_sync,
|
ParsedEvent, TimelineEvent, attachment_marker, collapse_edits, extract_body_sync,
|
||||||
extract_in_reply_to, is_intentional, replacement_of, unread_verdict,
|
extract_in_reply_to, is_intentional, media_parts, replacement_of, unread_verdict,
|
||||||
};
|
};
|
||||||
use matrix_sdk::ruma::events::AnySyncTimelineEvent;
|
use matrix_sdk::ruma::events::AnySyncTimelineEvent;
|
||||||
use matrix_sdk::ruma::events::room::message::MessageType;
|
use matrix_sdk::ruma::events::room::message::MessageType;
|
||||||
|
|
@ -1659,6 +1676,35 @@ mod unread_tests {
|
||||||
.expect("known msgtype")
|
.expect("known msgtype")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn a_captioned_attachment_is_named_by_its_file_not_its_caption() {
|
||||||
|
for kind in ["m.file", "m.image", "m.audio", "m.video"] {
|
||||||
|
let captioned = msgtype(&json!({
|
||||||
|
"msgtype": kind,
|
||||||
|
"body": "here's the screenshot of the failing step",
|
||||||
|
"filename": "a.png",
|
||||||
|
"url": "mxc://example.org/a",
|
||||||
|
}));
|
||||||
|
let (_, name, caption) = media_parts(&captioned).expect(kind);
|
||||||
|
assert_eq!(name, "a.png", "{kind}");
|
||||||
|
assert_eq!(
|
||||||
|
caption.as_deref(),
|
||||||
|
Some("here's the screenshot of the failing step"),
|
||||||
|
"{kind}"
|
||||||
|
);
|
||||||
|
|
||||||
|
// A sender that sets no `filename` puts the name in `body`.
|
||||||
|
let plain = msgtype(&json!({
|
||||||
|
"msgtype": kind,
|
||||||
|
"body": "b.png",
|
||||||
|
"url": "mxc://example.org/a",
|
||||||
|
}));
|
||||||
|
let (_, name, caption) = media_parts(&plain).expect(kind);
|
||||||
|
assert_eq!((name.as_str(), caption), ("b.png", None), "{kind}");
|
||||||
|
}
|
||||||
|
assert!(media_parts(&msgtype(&json!({"msgtype": "m.text", "body": "x"}))).is_none());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn attachments_render_as_typed_markers_and_text_as_itself() {
|
fn attachments_render_as_typed_markers_and_text_as_itself() {
|
||||||
let marker = |kind: &str, extra: serde_json::Value| {
|
let marker = |kind: &str, extra: serde_json::Value| {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue