json input format + required room_id on all room-scoped tools
This commit is contained in:
parent
ef461797ad
commit
cc3451eef3
5 changed files with 260 additions and 241 deletions
125
src/socket.rs
125
src/socket.rs
|
|
@ -12,7 +12,8 @@ use matrix_sdk::{
|
|||
},
|
||||
},
|
||||
};
|
||||
use serde_json::json;
|
||||
use crate::claude::{short_eid, wire_event_from};
|
||||
use crate::types::{FetchEventResult, MemberInfo, RoomInfo, WireEvent};
|
||||
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
|
||||
use tokio::net::{UnixListener, UnixStream};
|
||||
|
||||
|
|
@ -166,10 +167,10 @@ async fn list_rooms(client: &Client) -> DaemonResponse {
|
|||
.display_name()
|
||||
.await
|
||||
.map_or_else(|_| room.room_id().to_string(), |n| n.to_string());
|
||||
rooms.push(json!({
|
||||
"room_id": room.room_id().as_str(),
|
||||
"name": name,
|
||||
}));
|
||||
rooms.push(RoomInfo {
|
||||
room_id: room.room_id().as_str().to_owned(),
|
||||
name,
|
||||
});
|
||||
}
|
||||
DaemonResponse::ok(rooms)
|
||||
}
|
||||
|
|
@ -186,13 +187,11 @@ async fn list_room_members(client: &Client, room_id: &str) -> DaemonResponse {
|
|||
Ok(m) => m,
|
||||
Err(e) => return DaemonResponse::err(format!("failed to list members: {e}")),
|
||||
};
|
||||
let list: Vec<_> = members
|
||||
let list: Vec<MemberInfo> = members
|
||||
.iter()
|
||||
.map(|m| {
|
||||
json!({
|
||||
"user_id": m.user_id().as_str(),
|
||||
"display_name": m.display_name().unwrap_or_default(),
|
||||
})
|
||||
.map(|m| MemberInfo {
|
||||
user_id: m.user_id().as_str().to_owned(),
|
||||
display_name: m.display_name().unwrap_or_default().to_owned(),
|
||||
})
|
||||
.collect();
|
||||
DaemonResponse::ok(list)
|
||||
|
|
@ -282,48 +281,17 @@ async fn get_room_history(
|
|||
};
|
||||
}
|
||||
|
||||
let items: Vec<_> = tl.iter().map(timeline_item_to_json).collect();
|
||||
let read_markers = timeline::compute_read_markers(&room, &tl, &own_user).await;
|
||||
let items: Vec<WireEvent> = tl
|
||||
.iter()
|
||||
.map(|i| wire_event_from(i, &read_markers))
|
||||
.collect();
|
||||
DaemonResponse::ok(items)
|
||||
} else {
|
||||
DaemonResponse::err("event cache not available".to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
fn timeline_item_to_json(item: &crate::types::TimelineItem) -> serde_json::Value {
|
||||
match item {
|
||||
crate::types::TimelineItem::Message {
|
||||
event_id,
|
||||
sender,
|
||||
body,
|
||||
is_self,
|
||||
ts,
|
||||
in_reply_to,
|
||||
} => json!({
|
||||
"kind": "message",
|
||||
"event_id": event_id.as_str(),
|
||||
"sender": sender.as_str(),
|
||||
"body": body,
|
||||
"is_self": is_self,
|
||||
"ts": ts,
|
||||
"in_reply_to": in_reply_to.as_ref().map(|e| e.as_str()),
|
||||
}),
|
||||
crate::types::TimelineItem::Reaction {
|
||||
sender,
|
||||
target_event_id,
|
||||
key,
|
||||
is_self,
|
||||
ts,
|
||||
} => json!({
|
||||
"kind": "reaction",
|
||||
"sender": sender.as_str(),
|
||||
"target_event_id": target_event_id.as_str(),
|
||||
"key": key,
|
||||
"is_self": is_self,
|
||||
"ts": ts,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// Fetch a specific event by ID via the homeserver `/context` endpoint.
|
||||
/// Returns the event plus `context_before` events before it. Includes one
|
||||
/// extra event as `earlier_handle` so the shard can page further backward
|
||||
|
|
@ -381,7 +349,7 @@ async fn fetch_event(
|
|||
Err(e) => return DaemonResponse::err(format!("event_with_context failed: {e}")),
|
||||
};
|
||||
|
||||
let render = |raw: &matrix_sdk::deserialized_responses::TimelineEvent| -> Option<serde_json::Value> {
|
||||
let render = |raw: &matrix_sdk::deserialized_responses::TimelineEvent| -> Option<WireEvent> {
|
||||
let deserialized = raw.raw().deserialize().ok()?;
|
||||
let AnySyncTimelineEvent::MessageLike(msg) = deserialized else {
|
||||
return None;
|
||||
|
|
@ -401,47 +369,52 @@ async fn fetch_event(
|
|||
}
|
||||
_ => None,
|
||||
};
|
||||
Some(json!({
|
||||
"kind": "message",
|
||||
"event_id": orig.event_id.as_str(),
|
||||
"sender": orig.sender.as_str(),
|
||||
"body": text.body,
|
||||
"is_self": orig.sender == own_user,
|
||||
"ts": ts,
|
||||
"in_reply_to": in_reply_to,
|
||||
}))
|
||||
Some(WireEvent::Message {
|
||||
event_id: orig.event_id.as_str().to_owned(),
|
||||
event_id_short: short_eid(orig.event_id.as_str()),
|
||||
sender: orig.sender.as_str().to_owned(),
|
||||
is_self: orig.sender == own_user,
|
||||
ts,
|
||||
ts_human: format!("{} UTC", crate::timeline::format_ts(ts)),
|
||||
body: text.body.clone(),
|
||||
in_reply_to,
|
||||
read_by: Vec::new(),
|
||||
})
|
||||
}
|
||||
matrix_sdk::ruma::events::AnySyncMessageLikeEvent::Reaction(
|
||||
matrix_sdk::ruma::events::SyncMessageLikeEvent::Original(orig),
|
||||
) => {
|
||||
let ms: u64 = orig.origin_server_ts.0.into();
|
||||
let ts = (ms / 1000) as i64;
|
||||
Some(json!({
|
||||
"kind": "reaction",
|
||||
"sender": orig.sender.as_str(),
|
||||
"target_event_id": orig.content.relates_to.event_id.as_str(),
|
||||
"key": orig.content.relates_to.key,
|
||||
"is_self": orig.sender == own_user,
|
||||
"ts": ts,
|
||||
}))
|
||||
Some(WireEvent::Reaction {
|
||||
sender: orig.sender.as_str().to_owned(),
|
||||
is_self: orig.sender == own_user,
|
||||
ts,
|
||||
ts_human: format!("{} UTC", crate::timeline::format_ts(ts)),
|
||||
target_event_id: orig.content.relates_to.event_id.as_str().to_owned(),
|
||||
target_event_id_short: short_eid(orig.content.relates_to.event_id.as_str()),
|
||||
key: orig.content.relates_to.key.clone(),
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
};
|
||||
|
||||
// events_before is newest-first per matrix /context spec - reverse for chronological
|
||||
let mut before: Vec<_> = response.events_before.iter().filter_map(render).collect();
|
||||
let mut before: Vec<WireEvent> = response.events_before.iter().filter_map(render).collect();
|
||||
before.reverse();
|
||||
|
||||
// The "earlier handle" is the oldest event we got, if we asked for more than 0
|
||||
// It's used by the shard to page further back via another fetch_event call
|
||||
// The "earlier handle" is the oldest event we got, used by the shard
|
||||
// to page further back via another fetch_event call.
|
||||
let earlier_handle = if context_before > 0 && before.len() > context_before as usize {
|
||||
before.first().and_then(|e| e.get("event_id").and_then(|v| v.as_str()).map(String::from))
|
||||
before.first().map(|e| match e {
|
||||
WireEvent::Message { event_id, .. } => event_id.clone(),
|
||||
WireEvent::Reaction { target_event_id, .. } => target_event_id.clone(),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let context_events: Vec<_> = if earlier_handle.is_some() {
|
||||
// First event is the handle - skip it from the main events list
|
||||
let context_events: Vec<WireEvent> = if earlier_handle.is_some() {
|
||||
before.into_iter().skip(1).collect()
|
||||
} else {
|
||||
before
|
||||
|
|
@ -449,9 +422,9 @@ async fn fetch_event(
|
|||
|
||||
let target = response.event.as_ref().and_then(render);
|
||||
|
||||
DaemonResponse::ok(json!({
|
||||
"event": target,
|
||||
"context_before": context_events,
|
||||
"earlier_handle": earlier_handle,
|
||||
}))
|
||||
DaemonResponse::ok(FetchEventResult {
|
||||
event: target,
|
||||
context_before: context_events,
|
||||
earlier_handle,
|
||||
})
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue