arminius_lib_room_service_messages/event.rs
1// SPDX-FileCopyrightText: 2025 BWI GmbH
2//
3// SPDX-License-Identifier: Apache-2.0
4
5//! Facts the room service publishes about what happened in a room.
6//!
7//! These state what occurred; they say nothing about who should be told. A
8//! consumer decides that for itself — the federation output service works out
9//! which homeservers to deliver to, and the sync service will read the same
10//! facts without the room service changing again.
11
12use arminius_lib_message_bus::Event;
13use ruma::{
14 MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedRoomId, OwnedServerName, OwnedUserId,
15 events::receipt::{ReceiptThread, ReceiptType},
16};
17use serde::{Deserialize, Serialize};
18use serde_json::value::RawValue;
19
20use crate::{DedupKey, Deduplicate, KEY_SEPARATOR};
21
22/// Where a room event came from.
23#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
24pub enum EventProvenance {
25 /// Created on this homeserver.
26 Local,
27 /// Received in a transaction from `origin`.
28 Federated {
29 /// The homeserver that sent it to us.
30 origin: OwnedServerName,
31 },
32 /// Submitted to us via `/send_join` and friends. The sender's homeserver
33 /// does not yet know enough about the room to distribute the event, so we
34 /// are the resident server and delivery falls to us.
35 ResidentServerFor {
36 /// The homeserver on whose behalf we are acting.
37 origin: OwnedServerName,
38 },
39}
40
41/// An event was added to a room's timeline.
42#[derive(Debug, Clone, Serialize, Deserialize, Event)]
43#[subject(name = "room-service.events.room_event_added", version = "1")]
44pub struct RoomEventAdded {
45 /// The room the event belongs to.
46 pub room_id: OwnedRoomId,
47 /// The event's ID.
48 pub event_id: OwnedEventId,
49 /// The event itself, as canonical JSON.
50 pub pdu: Box<RawValue>,
51 /// Where the event came from.
52 pub provenance: EventProvenance,
53 /// The homeservers with a member in the room at this event, including our
54 /// own.
55 pub member_servers: Vec<OwnedServerName>,
56}
57
58impl Deduplicate for RoomEventAdded {
59 /// A room event is uniquely identified by the event id.
60 fn dedup_key(&self) -> DedupKey {
61 DedupKey(self.event_id.to_string())
62 }
63}
64
65/// A user's read receipt was recorded in a room.
66#[derive(Debug, Clone, Serialize, Deserialize, Event)]
67#[subject(name = "room-service.events.read_receipt_added", version = "1")]
68pub struct ReadReceiptAdded {
69 /// The room the receipt was recorded in.
70 pub room_id: OwnedRoomId,
71 /// The user the receipt belongs to.
72 pub user_id: OwnedUserId,
73 /// The event acknowledged "up to".
74 pub event_id: OwnedEventId,
75 /// Whether the receipt is public or private.
76 /// Only public receipts are federated.
77 pub receipt_type: ReceiptType,
78 /// The thread the receipt applies to.
79 ///
80 /// [`ReceiptThread::Unthreaded`] has no string form and serialises as
81 /// `null`, which its own `Deserialize` rejects. The unthreaded case
82 /// therefore has to travel as the field's absence, which is the same shape
83 /// ruma gives `Receipt::thread`.
84 #[serde(default, skip_serializing_if = "ruma::serde::is_default")]
85 pub thread: ReceiptThread,
86 /// When the receipt was made.
87 pub ts: MilliSecondsSinceUnixEpoch,
88 /// Where the receipt came from.
89 pub provenance: EventProvenance,
90 /// The homeservers with a member in the room, including our own.
91 pub member_servers: Vec<OwnedServerName>,
92}
93
94impl Deduplicate for ReadReceiptAdded {
95 /// A receipt has no unique ID of its own. Two receipts agreeing on all of
96 /// these describe the same receipt state.
97 fn dedup_key(&self) -> DedupKey {
98 DedupKey(
99 [
100 self.room_id.as_str(),
101 self.user_id.as_str(),
102 self.receipt_type.as_str(),
103 // `Unthreaded` has no string form. Empty is unambiguous here:
104 // every other variant is either `main` or an event ID.
105 self.thread.as_str().unwrap_or_default(),
106 self.event_id.as_str(),
107 ]
108 .join(&KEY_SEPARATOR.to_string()),
109 )
110 }
111}