mirror of
https://forgejo.ellis.link/continuwuation/continuwuity.git
synced 2026-11-13 20:26:52 +00:00
87 lines
3.0 KiB
Rust
87 lines
3.0 KiB
Rust
use std::collections::BTreeMap;
|
|
|
|
use ruma::{
|
|
MilliSecondsSinceUnixEpoch,
|
|
events::{AnyTimelineEvent, room::member::MembershipState},
|
|
serde::Raw,
|
|
};
|
|
use serde_json::value::{RawValue as RawJsonValue, to_raw_value};
|
|
|
|
use super::{Pdu, sticky};
|
|
use crate::Event;
|
|
|
|
type Unsigned = BTreeMap<String, Box<RawJsonValue>>;
|
|
|
|
impl Pdu {
|
|
/// Sets the `unsigned` field of the PDU using a mix of the provided
|
|
/// optional data, and information available in the PDU itself. The
|
|
/// `unsigned` field will always be inserted if it is not attached to the
|
|
/// PDU, however no operations will be performed if `unsigned` is present
|
|
/// but malformed and unparsable.
|
|
///
|
|
/// `age` is always inserted and is calculated based on the time of the
|
|
/// function call. `membership`, `prev_content`, and `redacted_by` are
|
|
/// inserted if not `None`. If `user_id` is `None`, `transaction_id` will be
|
|
/// removed. The sticky event TTL field is always inserted and calculated
|
|
/// (per `age`) if the PDU is sticky.
|
|
///
|
|
/// This function panics if any value cannot be serialised, which should not
|
|
/// happen provided `membership`, `prev_content`, and `redacted_because` are
|
|
/// well-formed.
|
|
pub fn set_unsigned(
|
|
&mut self,
|
|
user_id: Option<&ruma::UserId>,
|
|
membership: Option<MembershipState>,
|
|
prev_content: Option<Box<RawJsonValue>>,
|
|
redacted_because: Option<Raw<AnyTimelineEvent>>,
|
|
) -> crate::Result<Unsigned> {
|
|
// See: https://spec.matrix.org/v1.19/client-server-api/#definition-clientevent_unsigneddata
|
|
let mut unsigned = self
|
|
.unsigned()
|
|
.map_or_else(|| Ok(Unsigned::new()), |u| serde_json::from_str::<Unsigned>(u.get()))?;
|
|
|
|
let now = MilliSecondsSinceUnixEpoch::now().get();
|
|
let now_i: ruma::Int = now.into();
|
|
unsigned.insert(
|
|
"age".to_owned(),
|
|
to_raw_value(&now_i.saturating_sub(self.origin_server_ts.into()))?,
|
|
);
|
|
|
|
if let Some(membership) = membership {
|
|
unsigned.insert("membership".to_owned(), to_raw_value(&membership)?);
|
|
}
|
|
|
|
if let Some(prev_content) = prev_content {
|
|
unsigned.insert("prev_content".to_owned(), to_raw_value(&prev_content)?);
|
|
// TODO(nex): prev_sender is still inserted in append.rs because
|
|
// only the prev *content* is passed to this function.
|
|
// I think ideally passing the actual direct `UnsignedContext` to
|
|
// this function instead of unpacking its parameters would be the
|
|
// proper solution, especially since every callsite has one
|
|
}
|
|
if let Some(redacted_because) = redacted_because {
|
|
unsigned.remove("org.continuwuity.redacted_by");
|
|
unsigned.insert("redacted_because".to_owned(), to_raw_value(&redacted_because)?);
|
|
}
|
|
|
|
// Remove transaction_id unless the user is the sender
|
|
if user_id.is_none_or(|u| u != self.sender()) {
|
|
unsigned.remove("transaction_id");
|
|
}
|
|
|
|
if let Some(expires_at) = self
|
|
.sticky
|
|
.as_deref()
|
|
.and_then(|sticky| sticky::expires_at(self.origin_server_ts, sticky, u64::from(now)))
|
|
{
|
|
unsigned.insert(
|
|
sticky::TTL_UNSIGNED_KEY.to_owned(),
|
|
to_raw_value(&expires_at.saturating_sub(u64::from(now)))?,
|
|
);
|
|
}
|
|
|
|
self.unsigned = Some(to_raw_value(&unsigned)?);
|
|
Ok(unsigned)
|
|
}
|
|
}
|