1#![allow(missing_docs)]
16
17use std::{
18 collections::BTreeSet,
19 sync::atomic::{AtomicU64, Ordering::SeqCst},
20 time::Duration,
21};
22
23use as_variant::as_variant;
24use matrix_sdk_common::deserialized_responses::{
25 TimelineEvent, UnableToDecryptInfo, UnableToDecryptReason,
26};
27use ruma::{
28 EventId, Int, MilliSecondsSinceUnixEpoch, OwnedDeviceId, OwnedEventId, OwnedMxcUri,
29 OwnedRoomAliasId, OwnedRoomId, OwnedServerName, OwnedTransactionId, OwnedUserId, OwnedVoipId,
30 RoomVersionId, TransactionId, UInt, VoipVersionId,
31 events::{
32 AnyGlobalAccountDataEvent, AnyMessageLikeEvent, AnyRoomAccountDataEvent, AnyStateEvent,
33 AnyStrippedStateEvent, AnySyncEphemeralRoomEvent, AnySyncMessageLikeEvent,
34 AnySyncStateEvent, AnySyncTimelineEvent, AnyTimelineEvent, BundledMessageLikeRelations,
35 EphemeralRoomEventContent, EventContentFromType, False, GlobalAccountDataEventContent,
36 Mentions, MessageLikeEvent, MessageLikeEventContent, PossiblyRedactedStateEventContent,
37 RedactContent, RedactedMessageLikeEventContent, RedactedStateEventContent,
38 RoomAccountDataEventContent, StateEvent, StateEventContent, StaticEventContent,
39 StaticStateEventContent, StrippedStateEvent, SyncMessageLikeEvent, SyncStateEvent,
40 TimelineEventType,
41 beacon::BeaconEventContent,
42 beacon_info::BeaconInfoEventContent,
43 call::{
44 SessionDescription,
45 invite::CallInviteEventContent,
46 member::{
47 ActiveFocus, ActiveLivekitFocus, Application, CallApplicationContent,
48 CallMemberEventContent, CallMemberStateKey, CallScope, Focus, LivekitFocus,
49 },
50 },
51 direct::{DirectEventContent, OwnedDirectUserIdentifier},
52 fully_read::FullyReadEventContent,
53 ignored_user_list::IgnoredUserListEventContent,
54 macros::EventContent,
55 marked_unread::MarkedUnreadEventContent,
56 member_hints::MemberHintsEventContent,
57 poll::{
58 unstable_end::UnstablePollEndEventContent,
59 unstable_response::UnstablePollResponseEventContent,
60 unstable_start::{
61 NewUnstablePollStartEventContent, ReplacementUnstablePollStartEventContent,
62 UnstablePollAnswer, UnstablePollStartContentBlock, UnstablePollStartEventContent,
63 },
64 },
65 presence::{PresenceEvent, PresenceEventContent},
66 push_rules::PushRulesEventContent,
67 reaction::ReactionEventContent,
68 receipt::{Receipt, ReceiptEventContent, ReceiptThread, ReceiptType},
69 relation::{Annotation, BundledThread, Reference, Replacement, Reply, Thread},
70 room::{
71 ImageInfo,
72 avatar::{self, RoomAvatarEventContent},
73 canonical_alias::RoomCanonicalAliasEventContent,
74 create::{PreviousRoom, RoomCreateEventContent},
75 encrypted::{
76 EncryptedEventScheme, MegolmV1AesSha2ContentInit, RoomEncryptedEventContent,
77 },
78 encryption::RoomEncryptionEventContent,
79 history_visibility::{HistoryVisibility, RoomHistoryVisibilityEventContent},
80 join_rules::{JoinRule, RoomJoinRulesEventContent},
81 member::{MembershipState, RoomMemberEventContent},
82 message::{
83 FormattedBody, GalleryItemType, GalleryMessageEventContent,
84 ImageMessageEventContent, MessageType, OriginalSyncRoomMessageEvent, Relation,
85 RelationWithoutReplacement, RoomMessageEventContent,
86 RoomMessageEventContentWithoutRelation,
87 },
88 name::RoomNameEventContent,
89 pinned_events::RoomPinnedEventsEventContent,
90 power_levels::RoomPowerLevelsEventContent,
91 redaction::RoomRedactionEventContent,
92 server_acl::RoomServerAclEventContent,
93 tombstone::RoomTombstoneEventContent,
94 topic::RoomTopicEventContent,
95 },
96 rtc::{
97 decline::RtcDeclineEventContent,
98 notification::{CallIntent, NotificationType, RtcNotificationEventContent},
99 },
100 space::{child::SpaceChildEventContent, parent::SpaceParentEventContent},
101 space_order::SpaceOrderEventContent,
102 sticker::StickerEventContent,
103 tag::{TagEventContent, TagInfo, TagName, Tags},
104 typing::TypingEventContent,
105 },
106 presence::PresenceState,
107 push::Ruleset,
108 room::RoomType,
109 room_version_rules::AuthorizationRules,
110 serde::Raw,
111 server_name,
112};
113use serde::{Deserialize, Serialize, de::DeserializeOwned};
114use serde_json::json;
115
116use crate::{TestRoom, base64_sha256_hash};
117
118pub trait TimestampArg {
119 fn to_milliseconds_since_unix_epoch(self) -> MilliSecondsSinceUnixEpoch;
120}
121
122impl TimestampArg for MilliSecondsSinceUnixEpoch {
123 fn to_milliseconds_since_unix_epoch(self) -> MilliSecondsSinceUnixEpoch {
124 self
125 }
126}
127
128impl TimestampArg for u64 {
129 fn to_milliseconds_since_unix_epoch(self) -> MilliSecondsSinceUnixEpoch {
130 MilliSecondsSinceUnixEpoch(UInt::try_from(self).unwrap())
131 }
132}
133
134#[derive(Debug, Serialize)]
136struct RedactedBecause {
137 content: RoomRedactionEventContent,
139
140 event_id: OwnedEventId,
142
143 sender: OwnedUserId,
145
146 origin_server_ts: MilliSecondsSinceUnixEpoch,
149}
150
151#[derive(Debug, Serialize)]
152struct Unsigned<C: StaticEventContent> {
153 #[serde(skip_serializing_if = "Option::is_none")]
154 prev_content: Option<C>,
155
156 #[serde(skip_serializing_if = "Option::is_none")]
157 transaction_id: Option<OwnedTransactionId>,
158
159 #[serde(rename = "m.relations", skip_serializing_if = "Option::is_none")]
160 relations: Option<BundledMessageLikeRelations<Raw<AnySyncTimelineEvent>>>,
161
162 #[serde(skip_serializing_if = "Option::is_none")]
163 redacted_because: Option<RedactedBecause>,
164
165 #[serde(skip_serializing_if = "Option::is_none")]
166 age: Option<Int>,
167
168 #[serde(skip_serializing_if = "Option::is_none")]
169 invite_room_state: Option<Vec<Raw<AnyStrippedStateEvent>>>,
170}
171
172impl<C: StaticEventContent> Default for Unsigned<C> {
174 fn default() -> Self {
175 Self {
176 prev_content: None,
177 transaction_id: None,
178 relations: None,
179 redacted_because: None,
180 age: None,
181 invite_room_state: None,
182 }
183 }
184}
185
186#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
187enum EventFormat {
188 #[default]
191 Timeline,
192 SyncTimeline,
194 StrippedState,
196 Ephemeral,
198 GlobalAccountData,
200 RoomAccountData,
202}
203
204impl EventFormat {
205 fn has_sender(self) -> bool {
207 matches!(self, Self::Timeline | Self::SyncTimeline | Self::StrippedState)
208 }
209
210 fn has_event_id(self) -> bool {
212 matches!(self, Self::Timeline | Self::SyncTimeline)
213 }
214
215 fn has_room_id(self) -> bool {
217 matches!(self, Self::Timeline)
218 }
219}
220
221#[derive(Debug)]
222pub struct EventBuilder<C: StaticEventContent<IsPrefix = False>> {
223 format: EventFormat,
227 sender: Option<OwnedUserId>,
228 room: Option<OwnedRoomId>,
229 event_id: Option<OwnedEventId>,
230 no_event_id: bool,
232 redacts: Option<OwnedEventId>,
233 content: C,
234 server_ts: MilliSecondsSinceUnixEpoch,
235 unsigned: Option<Unsigned<C>>,
236 state_key: Option<String>,
237}
238
239impl<E: StaticEventContent<IsPrefix = False>> EventBuilder<E> {
240 fn format(mut self, format: EventFormat) -> Self {
241 self.format = format;
242 self
243 }
244
245 pub fn room(mut self, room_id: impl Into<OwnedRoomId>) -> Self {
247 self.room = Some(room_id.into());
248 self
249 }
250
251 pub fn default_room(mut self) -> Self {
253 self.room = Some(TestRoom::Default.into());
254 self
255 }
256
257 pub fn sender(mut self, sender: impl Into<OwnedUserId>) -> Self {
258 self.sender = Some(sender.into());
259 self
260 }
261
262 pub fn event_id(mut self, event_id: impl Into<OwnedEventId>) -> Self {
263 self.event_id = Some(event_id.into());
264 self.no_event_id = false;
265 self
266 }
267
268 pub fn no_event_id(mut self) -> Self {
269 self.event_id = None;
270 self.no_event_id = true;
271 self
272 }
273
274 pub fn server_ts(mut self, ts: impl TimestampArg) -> Self {
275 self.server_ts = ts.to_milliseconds_since_unix_epoch();
276 self
277 }
278
279 pub fn unsigned_transaction_id(mut self, transaction_id: &TransactionId) -> Self {
280 self.unsigned.get_or_insert_with(Default::default).transaction_id =
281 Some(transaction_id.to_owned());
282 self
283 }
284
285 pub fn age(mut self, age: impl Into<Int>) -> Self {
287 self.unsigned.get_or_insert_with(Default::default).age = Some(age.into());
288 self
289 }
290
291 pub fn prev_content(mut self, prev: E) -> Self {
293 self.unsigned.get_or_insert_with(Default::default).prev_content = Some(prev);
294 self
295 }
296
297 pub fn with_bundled_thread_summary(
300 mut self,
301 latest_event: Raw<AnySyncMessageLikeEvent>,
302 count: usize,
303 current_user_participated: bool,
304 ) -> Self {
305 let relations = self
306 .unsigned
307 .get_or_insert_with(Default::default)
308 .relations
309 .get_or_insert_with(BundledMessageLikeRelations::new);
310 relations.thread = Some(Box::new(BundledThread::new(
311 latest_event,
312 UInt::try_from(count).unwrap(),
313 current_user_participated,
314 )));
315 self
316 }
317
318 pub fn with_bundled_edit(mut self, replacement: impl Into<Raw<AnySyncTimelineEvent>>) -> Self {
320 let relations = self
321 .unsigned
322 .get_or_insert_with(Default::default)
323 .relations
324 .get_or_insert_with(BundledMessageLikeRelations::new);
325 relations.replace = Some(Box::new(replacement.into()));
326 self
327 }
328
329 pub fn state_key(mut self, state_key: impl Into<String>) -> Self {
334 self.state_key = Some(state_key.into());
335 self
336 }
337}
338
339impl<E> EventBuilder<E>
340where
341 E: StaticEventContent<IsPrefix = False> + Serialize,
342{
343 #[inline(always)]
344 fn construct_json(self) -> serde_json::Value {
345 let mut json = json!({
346 "type": E::TYPE,
347 "content": self.content,
348 "origin_server_ts": self.server_ts,
349 });
350
351 let map = json.as_object_mut().unwrap();
352
353 if self.format.has_sender() {
354 let sender = self
357 .sender
358 .or_else(|| Some(self.unsigned.as_ref()?.redacted_because.as_ref()?.sender.clone())).expect("the sender must be known when building the JSON for a non read-receipt or global event");
359 map.insert("sender".to_owned(), json!(sender));
360 }
361
362 if self.format.has_room_id() {
363 let room_id = self.room.expect("TimelineEvent requires a room id");
364 map.insert("room_id".to_owned(), json!(room_id));
365 }
366
367 if let Some(redacts) = self.redacts {
368 map.insert("redacts".to_owned(), json!(redacts));
369 }
370
371 if let Some(state_key) = self.state_key {
372 map.insert("state_key".to_owned(), json!(state_key));
373 }
374
375 if self.format.has_event_id() && !self.no_event_id {
376 let event_id = self.event_id.unwrap_or_else(|| {
377 let bytes = serde_json::to_vec(&map).unwrap();
384 EventId::new_v2_or_v3(&base64_sha256_hash(&bytes)).unwrap()
385 });
386
387 map.insert("event_id".to_owned(), json!(event_id));
388 }
389
390 if let Some(unsigned) = self.unsigned {
391 map.insert("unsigned".to_owned(), json!(unsigned));
392 }
393
394 json
395 }
396
397 pub fn into_raw<T>(self) -> Raw<T> {
403 Raw::new(&self.construct_json()).unwrap().cast_unchecked()
404 }
405
406 pub fn into_raw_timeline(self) -> Raw<AnyTimelineEvent> {
407 self.into_raw()
408 }
409
410 pub fn into_any_sync_message_like_event(self) -> AnySyncMessageLikeEvent {
411 self.format(EventFormat::SyncTimeline)
412 .into_raw()
413 .deserialize()
414 .expect("expected message like event")
415 }
416
417 pub fn into_original_sync_room_message_event(self) -> OriginalSyncRoomMessageEvent {
418 self.format(EventFormat::SyncTimeline)
419 .into_raw()
420 .deserialize()
421 .expect("expected original sync room message event")
422 }
423
424 pub fn into_raw_sync(self) -> Raw<AnySyncTimelineEvent> {
425 self.format(EventFormat::SyncTimeline).into_raw()
426 }
427
428 pub fn into_raw_sync_state(self) -> Raw<AnySyncStateEvent> {
429 self.format(EventFormat::SyncTimeline).into_raw()
430 }
431
432 pub fn into_event(self) -> TimelineEvent {
433 TimelineEvent::from_plaintext(self.into_raw_sync())
434 }
435
436 pub fn into_content(self) -> E {
441 self.content
442 }
443}
444
445impl EventBuilder<RoomEncryptedEventContent> {
446 pub fn into_utd_sync_timeline_event(self) -> TimelineEvent {
449 let session_id = as_variant!(&self.content.scheme, EncryptedEventScheme::MegolmV1AesSha2)
450 .map(|content| content.session_id.clone());
451
452 TimelineEvent::from_utd(
453 self.into(),
454 UnableToDecryptInfo {
455 session_id,
456 reason: UnableToDecryptReason::MissingMegolmSession { withheld_code: None },
457 },
458 )
459 }
460}
461
462impl EventBuilder<RoomMessageEventContent> {
463 pub fn reply_to(mut self, event_id: impl Into<OwnedEventId>) -> Self {
465 self.content.relates_to = Some(Relation::Reply(Reply::with_event_id(event_id.into())));
466 self
467 }
468
469 pub fn in_thread(
472 mut self,
473 root: impl Into<OwnedEventId>,
474 latest_thread_event: impl Into<OwnedEventId>,
475 ) -> Self {
476 self.content.relates_to =
477 Some(Relation::Thread(Thread::plain(root.into(), latest_thread_event.into())));
478 self
479 }
480
481 pub fn in_thread_reply(
484 mut self,
485 root: impl Into<OwnedEventId>,
486 replied_to: impl Into<OwnedEventId>,
487 ) -> Self {
488 self.content.relates_to =
489 Some(Relation::Thread(Thread::reply(root.into(), replied_to.into())));
490 self
491 }
492
493 pub fn mentions(mut self, mentions: Mentions) -> Self {
495 self.content.mentions = Some(mentions);
496 self
497 }
498
499 pub fn edit(
502 mut self,
503 edited_event_id: impl Into<OwnedEventId>,
504 new_content: RoomMessageEventContentWithoutRelation,
505 ) -> Self {
506 self.content.relates_to =
507 Some(Relation::Replacement(Replacement::new(edited_event_id.into(), new_content)));
508 self
509 }
510
511 pub fn caption(
515 mut self,
516 caption: Option<String>,
517 formatted_caption: Option<FormattedBody>,
518 ) -> Self {
519 match &mut self.content.msgtype {
520 MessageType::Image(image) => {
521 let filename = image.filename().to_owned();
522 if let Some(caption) = caption {
523 image.body = caption;
524 image.filename = Some(filename);
525 } else {
526 image.body = filename;
527 image.filename = None;
528 }
529 image.formatted = formatted_caption;
530 }
531
532 MessageType::Audio(_) | MessageType::Video(_) | MessageType::File(_) => {
533 unimplemented!();
534 }
535
536 _ => panic!("unexpected event type for a caption"),
537 }
538
539 self
540 }
541}
542
543impl EventBuilder<UnstablePollStartEventContent> {
544 pub fn reply_to(mut self, event_id: impl Into<OwnedEventId>) -> Self {
546 if let UnstablePollStartEventContent::New(content) = &mut self.content {
547 content.relates_to =
548 Some(RelationWithoutReplacement::Reply(Reply::with_event_id(event_id.into())));
549 }
550 self
551 }
552
553 pub fn in_thread(
556 mut self,
557 root: impl Into<OwnedEventId>,
558 reply_to_event_id: impl Into<OwnedEventId>,
559 ) -> Self {
560 let thread = Thread::reply(root.into(), reply_to_event_id.into());
561
562 if let UnstablePollStartEventContent::New(content) = &mut self.content {
563 content.relates_to = Some(RelationWithoutReplacement::Thread(thread));
564 }
565 self
566 }
567}
568
569impl EventBuilder<RoomCreateEventContent> {
570 pub fn predecessor(mut self, room_id: impl Into<OwnedRoomId>) -> Self {
572 self.content.predecessor = Some(PreviousRoom::new(room_id.into()));
573 self
574 }
575
576 pub fn no_predecessor(mut self) -> Self {
578 self.content.predecessor = None;
579 self
580 }
581
582 pub fn with_space_type(mut self) -> Self {
584 self.content.room_type = Some(RoomType::Space);
585 self
586 }
587}
588
589impl EventBuilder<StickerEventContent> {
590 pub fn reply_thread(
592 mut self,
593 root: impl Into<OwnedEventId>,
594 reply_to_event: impl Into<OwnedEventId>,
595 ) -> Self {
596 self.content.relates_to =
597 Some(Relation::Thread(Thread::reply(root.into(), reply_to_event.into())));
598 self
599 }
600}
601
602impl<E: StaticEventContent<IsPrefix = False>> From<EventBuilder<E>> for Raw<AnySyncTimelineEvent>
603where
604 E: Serialize,
605{
606 fn from(val: EventBuilder<E>) -> Self {
607 val.into_raw_sync()
608 }
609}
610
611impl<E: StaticEventContent<IsPrefix = False>> From<EventBuilder<E>> for AnySyncTimelineEvent
612where
613 E: Serialize,
614{
615 fn from(val: EventBuilder<E>) -> Self {
616 Raw::<AnySyncTimelineEvent>::from(val).deserialize().expect("expected sync timeline event")
617 }
618}
619
620impl<E: StaticEventContent<IsPrefix = False>> From<EventBuilder<E>> for Raw<AnyTimelineEvent>
621where
622 E: Serialize,
623{
624 fn from(val: EventBuilder<E>) -> Self {
625 val.into_raw_timeline()
626 }
627}
628
629impl<E: StaticEventContent<IsPrefix = False>> From<EventBuilder<E>> for AnyTimelineEvent
630where
631 E: Serialize,
632{
633 fn from(val: EventBuilder<E>) -> Self {
634 Raw::<AnyTimelineEvent>::from(val).deserialize().expect("expected timeline event")
635 }
636}
637
638impl<E: StaticEventContent<IsPrefix = False>> From<EventBuilder<E>>
639 for Raw<AnyGlobalAccountDataEvent>
640where
641 E: Serialize,
642{
643 fn from(val: EventBuilder<E>) -> Self {
644 val.format(EventFormat::GlobalAccountData).into_raw()
645 }
646}
647
648impl<E: StaticEventContent<IsPrefix = False>> From<EventBuilder<E>> for AnyGlobalAccountDataEvent
649where
650 E: Serialize,
651{
652 fn from(val: EventBuilder<E>) -> Self {
653 Raw::<AnyGlobalAccountDataEvent>::from(val)
654 .deserialize()
655 .expect("expected global account data")
656 }
657}
658
659impl<E: StaticEventContent<IsPrefix = False>> From<EventBuilder<E>> for Raw<AnyRoomAccountDataEvent>
660where
661 E: Serialize,
662{
663 fn from(val: EventBuilder<E>) -> Self {
664 val.format(EventFormat::RoomAccountData).into_raw()
665 }
666}
667
668impl<E: StaticEventContent<IsPrefix = False>> From<EventBuilder<E>> for AnyRoomAccountDataEvent
669where
670 E: Serialize,
671{
672 fn from(val: EventBuilder<E>) -> Self {
673 Raw::<AnyRoomAccountDataEvent>::from(val).deserialize().expect("expected room account data")
674 }
675}
676
677impl<E: StaticEventContent<IsPrefix = False>> From<EventBuilder<E>> for TimelineEvent
678where
679 E: Serialize,
680{
681 fn from(val: EventBuilder<E>) -> Self {
682 val.into_event()
683 }
684}
685
686impl<E: StaticEventContent<IsPrefix = False> + StateEventContent> From<EventBuilder<E>>
687 for Raw<AnySyncStateEvent>
688{
689 fn from(val: EventBuilder<E>) -> Self {
690 val.format(EventFormat::SyncTimeline).into_raw()
691 }
692}
693
694impl<E: StaticEventContent<IsPrefix = False> + StateEventContent> From<EventBuilder<E>>
695 for AnySyncStateEvent
696{
697 fn from(val: EventBuilder<E>) -> Self {
698 Raw::<AnySyncStateEvent>::from(val).deserialize().expect("expected sync state")
699 }
700}
701
702impl<E: StaticEventContent<IsPrefix = False> + StateEventContent> From<EventBuilder<E>>
703 for Raw<SyncStateEvent<E>>
704where
705 E: StaticStateEventContent + RedactContent,
706 E::Redacted: RedactedStateEventContent,
707{
708 fn from(val: EventBuilder<E>) -> Self {
709 val.format(EventFormat::SyncTimeline).into_raw()
710 }
711}
712
713impl<E: StaticEventContent<IsPrefix = False> + StateEventContent> From<EventBuilder<E>>
714 for SyncStateEvent<E>
715where
716 E: StaticStateEventContent + RedactContent + EventContentFromType,
717 E::Redacted: RedactedStateEventContent<StateKey = <E as StateEventContent>::StateKey>
718 + EventContentFromType,
719 E::Unsigned: DeserializeOwned,
720{
721 fn from(val: EventBuilder<E>) -> Self {
722 Raw::<SyncStateEvent<E>>::from(val).deserialize().expect("expected sync state")
723 }
724}
725
726impl<E: StaticEventContent<IsPrefix = False> + StateEventContent> From<EventBuilder<E>>
727 for Raw<AnyStateEvent>
728{
729 fn from(val: EventBuilder<E>) -> Self {
730 val.into_raw()
731 }
732}
733
734impl<E: StaticEventContent<IsPrefix = False> + StateEventContent> From<EventBuilder<E>>
735 for AnyStateEvent
736{
737 fn from(val: EventBuilder<E>) -> Self {
738 Raw::<AnyStateEvent>::from(val).deserialize().expect("expected state")
739 }
740}
741
742impl<E: StaticEventContent<IsPrefix = False> + StateEventContent> From<EventBuilder<E>>
743 for Raw<StateEvent<E>>
744where
745 E: StaticStateEventContent + RedactContent,
746 E::Redacted: RedactedStateEventContent,
747{
748 fn from(val: EventBuilder<E>) -> Self {
749 val.into_raw()
750 }
751}
752
753impl<E: StaticEventContent<IsPrefix = False> + StateEventContent> From<EventBuilder<E>>
754 for StateEvent<E>
755where
756 E: StaticStateEventContent + RedactContent + EventContentFromType,
757 E::Redacted: RedactedStateEventContent<StateKey = <E as StateEventContent>::StateKey>
758 + EventContentFromType,
759 E::Unsigned: DeserializeOwned,
760{
761 fn from(val: EventBuilder<E>) -> Self {
762 Raw::<StateEvent<E>>::from(val).deserialize().expect("expected state")
763 }
764}
765
766impl<E: StaticEventContent<IsPrefix = False> + StateEventContent> From<EventBuilder<E>>
767 for Raw<AnyStrippedStateEvent>
768{
769 fn from(val: EventBuilder<E>) -> Self {
770 val.format(EventFormat::StrippedState).into_raw()
771 }
772}
773
774impl<E: StaticEventContent<IsPrefix = False> + StateEventContent> From<EventBuilder<E>>
775 for AnyStrippedStateEvent
776{
777 fn from(val: EventBuilder<E>) -> Self {
778 Raw::<AnyStrippedStateEvent>::from(val).deserialize().expect("expected stripped state")
779 }
780}
781
782impl<E: StaticEventContent<IsPrefix = False> + StateEventContent> From<EventBuilder<E>>
783 for Raw<StrippedStateEvent<E::PossiblyRedacted>>
784where
785 E: StaticStateEventContent,
786{
787 fn from(val: EventBuilder<E>) -> Self {
788 val.format(EventFormat::StrippedState).into_raw()
789 }
790}
791
792impl<E: StaticEventContent<IsPrefix = False> + StateEventContent> From<EventBuilder<E>>
793 for StrippedStateEvent<E::PossiblyRedacted>
794where
795 E: StaticStateEventContent,
796 E::PossiblyRedacted: PossiblyRedactedStateEventContent + EventContentFromType,
797{
798 fn from(val: EventBuilder<E>) -> Self {
799 Raw::<StrippedStateEvent<E::PossiblyRedacted>>::from(val)
800 .deserialize()
801 .expect("expected stripped state")
802 }
803}
804
805impl<E: StaticEventContent<IsPrefix = False> + EphemeralRoomEventContent> From<EventBuilder<E>>
806 for Raw<AnySyncEphemeralRoomEvent>
807{
808 fn from(val: EventBuilder<E>) -> Self {
809 val.format(EventFormat::Ephemeral).into_raw()
810 }
811}
812
813impl<E: StaticEventContent<IsPrefix = False> + EphemeralRoomEventContent> From<EventBuilder<E>>
814 for AnySyncEphemeralRoomEvent
815{
816 fn from(val: EventBuilder<E>) -> Self {
817 Raw::<AnySyncEphemeralRoomEvent>::from(val).deserialize().expect("expected ephemeral")
818 }
819}
820
821impl<E: StaticEventContent<IsPrefix = False> + MessageLikeEventContent> From<EventBuilder<E>>
822 for Raw<AnySyncMessageLikeEvent>
823{
824 fn from(val: EventBuilder<E>) -> Self {
825 val.format(EventFormat::SyncTimeline).into_raw()
826 }
827}
828
829impl<E: StaticEventContent<IsPrefix = False> + MessageLikeEventContent> From<EventBuilder<E>>
830 for AnySyncMessageLikeEvent
831{
832 fn from(val: EventBuilder<E>) -> Self {
833 Raw::<AnySyncMessageLikeEvent>::from(val).deserialize().expect("expected sync message-like")
834 }
835}
836
837impl<E: StaticEventContent<IsPrefix = False> + MessageLikeEventContent> From<EventBuilder<E>>
838 for Raw<SyncMessageLikeEvent<E>>
839where
840 E: RedactContent,
841 E::Redacted: RedactedMessageLikeEventContent,
842{
843 fn from(val: EventBuilder<E>) -> Self {
844 val.format(EventFormat::SyncTimeline).into_raw()
845 }
846}
847
848impl<E: StaticEventContent<IsPrefix = False> + MessageLikeEventContent> From<EventBuilder<E>>
849 for SyncMessageLikeEvent<E>
850where
851 E: RedactContent + EventContentFromType,
852 E::Redacted: RedactedMessageLikeEventContent + EventContentFromType,
853{
854 fn from(val: EventBuilder<E>) -> Self {
855 Raw::<SyncMessageLikeEvent<E>>::from(val).deserialize().expect("expected sync message-like")
856 }
857}
858
859impl<E: StaticEventContent<IsPrefix = False> + MessageLikeEventContent> From<EventBuilder<E>>
860 for Raw<AnyMessageLikeEvent>
861{
862 fn from(val: EventBuilder<E>) -> Self {
863 val.into_raw()
864 }
865}
866
867impl<E: StaticEventContent<IsPrefix = False> + MessageLikeEventContent> From<EventBuilder<E>>
868 for AnyMessageLikeEvent
869{
870 fn from(val: EventBuilder<E>) -> Self {
871 Raw::<AnyMessageLikeEvent>::from(val).deserialize().expect("expected message-like")
872 }
873}
874
875impl<E: StaticEventContent<IsPrefix = False> + MessageLikeEventContent> From<EventBuilder<E>>
876 for Raw<MessageLikeEvent<E>>
877where
878 E: RedactContent,
879 E::Redacted: RedactedMessageLikeEventContent,
880{
881 fn from(val: EventBuilder<E>) -> Self {
882 val.into_raw()
883 }
884}
885
886impl<E: StaticEventContent<IsPrefix = False> + MessageLikeEventContent> From<EventBuilder<E>>
887 for MessageLikeEvent<E>
888where
889 E: RedactContent + EventContentFromType,
890 E::Redacted: RedactedMessageLikeEventContent + EventContentFromType,
891{
892 fn from(val: EventBuilder<E>) -> Self {
893 Raw::<MessageLikeEvent<E>>::from(val).deserialize().expect("expected message-like")
894 }
895}
896
897#[derive(Debug, Default)]
898pub struct EventFactory {
899 next_ts: AtomicU64,
900 sender: Option<OwnedUserId>,
901 room: Option<OwnedRoomId>,
902}
903
904impl EventFactory {
905 pub fn new() -> Self {
906 Self { next_ts: AtomicU64::new(0), sender: None, room: None }
907 }
908
909 pub fn room(mut self, room_id: impl Into<OwnedRoomId>) -> Self {
911 self.room = Some(room_id.into());
912 self
913 }
914
915 pub fn default_room(mut self) -> Self {
917 self.room = Some(TestRoom::Default.into());
918 self
919 }
920
921 pub fn sender(mut self, sender: impl Into<OwnedUserId>) -> Self {
922 self.sender = Some(sender.into());
923 self
924 }
925
926 pub fn server_ts(self, ts: u64) -> Self {
927 self.next_ts.store(ts, SeqCst);
928 self
929 }
930
931 fn next_server_ts(&self) -> MilliSecondsSinceUnixEpoch {
932 MilliSecondsSinceUnixEpoch(
933 self.next_ts
934 .fetch_add(1, SeqCst)
935 .try_into()
936 .expect("server timestamp should fit in js_int::UInt"),
937 )
938 }
939
940 pub fn event<E: StaticEventContent<IsPrefix = False>>(&self, content: E) -> EventBuilder<E> {
942 EventBuilder {
943 format: EventFormat::Timeline,
944 sender: self.sender.clone(),
945 room: self.room.clone(),
946 server_ts: self.next_server_ts(),
947 event_id: None,
948 no_event_id: false,
949 redacts: None,
950 content,
951 unsigned: None,
952 state_key: None,
953 }
954 }
955
956 pub fn text_msg(&self, content: impl Into<String>) -> EventBuilder<RoomMessageEventContent> {
958 self.event(RoomMessageEventContent::text_plain(content.into()))
959 }
960
961 pub fn emote(&self, content: impl Into<String>) -> EventBuilder<RoomMessageEventContent> {
963 self.event(RoomMessageEventContent::emote_plain(content.into()))
964 }
965
966 pub fn encrypted(
969 &self,
970 ciphertext: impl Into<String>,
971 sender_key: impl Into<String>,
972 device_id: impl Into<OwnedDeviceId>,
973 session_id: impl Into<String>,
974 ) -> EventBuilder<RoomEncryptedEventContent> {
975 self.event(RoomEncryptedEventContent::new(
976 EncryptedEventScheme::MegolmV1AesSha2(
977 MegolmV1AesSha2ContentInit {
978 ciphertext: ciphertext.into(),
979 sender_key: sender_key.into(),
980 device_id: device_id.into(),
981 session_id: session_id.into(),
982 }
983 .into(),
984 ),
985 None,
986 ))
987 }
988
989 pub fn member(&self, member: impl Into<OwnedUserId>) -> EventBuilder<RoomMemberEventContent> {
1019 let member = member.into();
1020 let mut event = self.event(RoomMemberEventContent::new(MembershipState::Join));
1021
1022 if self.sender.is_some() {
1023 event.sender = self.sender.clone();
1024 } else {
1025 event.sender = Some(member.clone());
1026 }
1027
1028 event.state_key = Some(member.into());
1029
1030 event
1031 }
1032
1033 pub fn room_tombstone(
1035 &self,
1036 body: impl Into<String>,
1037 replacement: impl Into<OwnedRoomId>,
1038 ) -> EventBuilder<RoomTombstoneEventContent> {
1039 let mut event = self.event(RoomTombstoneEventContent::new(body.into(), replacement.into()));
1040 event.state_key = Some("".to_owned());
1041 event
1042 }
1043
1044 pub fn room_topic(&self, topic: impl Into<String>) -> EventBuilder<RoomTopicEventContent> {
1046 let mut event = self.event(RoomTopicEventContent::new(topic.into()));
1047 event.state_key = Some("".to_owned());
1049 event
1050 }
1051
1052 pub fn room_name(&self, name: impl Into<String>) -> EventBuilder<RoomNameEventContent> {
1054 let mut event = self.event(RoomNameEventContent::new(name.into()));
1055 event.state_key = Some("".to_owned());
1057 event
1058 }
1059
1060 pub fn room_avatar(&self) -> EventBuilder<RoomAvatarEventContent> {
1062 let mut event = self.event(RoomAvatarEventContent::new());
1063 event.state_key = Some("".to_owned());
1065 event
1066 }
1067
1068 pub fn room_encryption(&self) -> EventBuilder<RoomEncryptionEventContent> {
1073 let mut event = self.event(RoomEncryptionEventContent::with_recommended_defaults());
1074 event.state_key = Some("".to_owned());
1076 event
1077 }
1078
1079 #[cfg(feature = "experimental-encrypted-state-events")]
1082 pub fn room_encryption_with_state_encryption(
1083 &self,
1084 ) -> EventBuilder<RoomEncryptionEventContent> {
1085 let mut content = RoomEncryptionEventContent::with_recommended_defaults();
1086 content.encrypt_state_events = true;
1087 let mut event = self.event(content);
1088 event.state_key = Some("".to_owned());
1089 event
1090 }
1091
1092 pub fn room_history_visibility(
1097 &self,
1098 visibility: HistoryVisibility,
1099 ) -> EventBuilder<RoomHistoryVisibilityEventContent> {
1100 let mut event = self.event(RoomHistoryVisibilityEventContent::new(visibility));
1101 event.state_key = Some("".to_owned());
1102 event
1103 }
1104
1105 pub fn room_join_rules(&self, join_rule: JoinRule) -> EventBuilder<RoomJoinRulesEventContent> {
1109 let mut event = self.event(RoomJoinRulesEventContent::new(join_rule));
1110 event.state_key = Some("".to_owned());
1111 event
1112 }
1113
1114 pub fn room_pinned_events(
1116 &self,
1117 pinned: Vec<OwnedEventId>,
1118 ) -> EventBuilder<RoomPinnedEventsEventContent> {
1119 self.event(RoomPinnedEventsEventContent::new(pinned)).state_key("")
1120 }
1121
1122 pub fn member_hints(
1143 &self,
1144 service_members: BTreeSet<OwnedUserId>,
1145 ) -> EventBuilder<MemberHintsEventContent> {
1146 self.event(MemberHintsEventContent::new(service_members)).state_key("")
1149 }
1150
1151 pub fn text_html(
1153 &self,
1154 plain: impl Into<String>,
1155 html: impl Into<String>,
1156 ) -> EventBuilder<RoomMessageEventContent> {
1157 self.event(RoomMessageEventContent::text_html(plain, html))
1158 }
1159
1160 pub fn notice(&self, content: impl Into<String>) -> EventBuilder<RoomMessageEventContent> {
1162 self.event(RoomMessageEventContent::notice_plain(content))
1163 }
1164
1165 pub fn reaction(
1167 &self,
1168 event_id: impl Into<OwnedEventId>,
1169 annotation: impl Into<String>,
1170 ) -> EventBuilder<ReactionEventContent> {
1171 self.event(ReactionEventContent::new(Annotation::new(event_id.into(), annotation.into())))
1172 }
1173
1174 pub fn redaction(
1179 &self,
1180 event_id: impl Into<OwnedEventId>,
1181 ) -> EventBuilder<RoomRedactionEventContent> {
1182 let event_id = event_id.into();
1183 let mut builder = self.event(RoomRedactionEventContent::new_v11(event_id.clone()));
1184 builder.redacts = Some(event_id);
1185 builder
1186 }
1187
1188 pub fn redacted_with_reason<
1191 T: StaticEventContent<IsPrefix = False> + RedactedMessageLikeEventContent,
1192 >(
1193 &self,
1194 redacter: impl Into<OwnedUserId>,
1195 content: T,
1196 reason: impl Into<String>,
1197 ) -> EventBuilder<T> {
1198 let mut builder = self.redacted(redacter, content);
1199 if let Some(because) =
1200 builder.unsigned.as_mut().and_then(|unsigned| unsigned.redacted_because.as_mut())
1201 {
1202 because.content.reason = Some(reason.into());
1203 }
1204 builder
1205 }
1206
1207 pub fn redacted<T: StaticEventContent<IsPrefix = False> + RedactedMessageLikeEventContent>(
1210 &self,
1211 redacter: impl Into<OwnedUserId>,
1212 content: T,
1213 ) -> EventBuilder<T> {
1214 let mut builder = self.event(content);
1215
1216 let redacted_because = RedactedBecause {
1217 content: RoomRedactionEventContent::default(),
1218 event_id: EventId::new_v1(server_name!("dummy.server")),
1219 sender: redacter.into(),
1220 origin_server_ts: self.next_server_ts(),
1221 };
1222 builder.unsigned.get_or_insert_with(Default::default).redacted_because =
1223 Some(redacted_because);
1224
1225 builder
1226 }
1227
1228 pub fn redacted_state<T: StaticEventContent<IsPrefix = False> + RedactedStateEventContent>(
1231 &self,
1232 redacter: impl Into<OwnedUserId>,
1233 state_key: impl Into<String>,
1234 content: T,
1235 ) -> EventBuilder<T> {
1236 let mut builder = self.event(content);
1237
1238 let redacted_because = RedactedBecause {
1239 content: RoomRedactionEventContent::default(),
1240 event_id: EventId::new_v1(server_name!("dummy.server")),
1241 sender: redacter.into(),
1242 origin_server_ts: self.next_server_ts(),
1243 };
1244 builder.unsigned.get_or_insert_with(Default::default).redacted_because =
1245 Some(redacted_because);
1246 builder.state_key = Some(state_key.into());
1247
1248 builder
1249 }
1250
1251 pub fn poll_start(
1254 &self,
1255 fallback_text: impl Into<String>,
1256 poll_question: impl Into<String>,
1257 answers: Vec<impl Into<String>>,
1258 ) -> EventBuilder<UnstablePollStartEventContent> {
1259 let answers: Vec<UnstablePollAnswer> = answers
1262 .into_iter()
1263 .enumerate()
1264 .map(|(idx, answer)| UnstablePollAnswer::new(idx.to_string(), answer))
1265 .collect();
1266 let poll_answers = answers.try_into().unwrap();
1267 let poll_start_content =
1268 UnstablePollStartEventContent::New(NewUnstablePollStartEventContent::plain_text(
1269 fallback_text,
1270 UnstablePollStartContentBlock::new(poll_question, poll_answers),
1271 ));
1272 self.event(poll_start_content)
1273 }
1274
1275 pub fn poll_edit(
1277 &self,
1278 edited_event_id: impl Into<OwnedEventId>,
1279 poll_question: impl Into<String>,
1280 answers: Vec<impl Into<String>>,
1281 ) -> EventBuilder<ReplacementUnstablePollStartEventContent> {
1282 let answers: Vec<UnstablePollAnswer> = answers
1285 .into_iter()
1286 .enumerate()
1287 .map(|(idx, answer)| UnstablePollAnswer::new(idx.to_string(), answer))
1288 .collect();
1289 let poll_answers = answers.try_into().unwrap();
1290 let poll_start_content_block =
1291 UnstablePollStartContentBlock::new(poll_question, poll_answers);
1292 self.event(ReplacementUnstablePollStartEventContent::new(
1293 poll_start_content_block,
1294 edited_event_id.into(),
1295 ))
1296 }
1297
1298 pub fn poll_response(
1301 &self,
1302 answers: Vec<impl Into<String>>,
1303 poll_start_id: impl Into<OwnedEventId>,
1304 ) -> EventBuilder<UnstablePollResponseEventContent> {
1305 self.event(UnstablePollResponseEventContent::new(
1306 answers.into_iter().map(Into::into).collect(),
1307 poll_start_id.into(),
1308 ))
1309 }
1310
1311 pub fn poll_end(
1314 &self,
1315 content: impl Into<String>,
1316 poll_start_id: impl Into<OwnedEventId>,
1317 ) -> EventBuilder<UnstablePollEndEventContent> {
1318 self.event(UnstablePollEndEventContent::new(content.into(), poll_start_id.into()))
1319 }
1320
1321 pub fn image(
1324 &self,
1325 filename: String,
1326 url: OwnedMxcUri,
1327 ) -> EventBuilder<RoomMessageEventContent> {
1328 let image_event_content = ImageMessageEventContent::plain(filename, url);
1329 self.event(RoomMessageEventContent::new(MessageType::Image(image_event_content)))
1330 }
1331
1332 pub fn gallery(
1335 &self,
1336 body: String,
1337 filename: String,
1338 url: OwnedMxcUri,
1339 ) -> EventBuilder<RoomMessageEventContent> {
1340 let gallery_event_content = GalleryMessageEventContent::new(
1341 body,
1342 None,
1343 vec![GalleryItemType::Image(ImageMessageEventContent::plain(filename, url))],
1344 );
1345 self.event(RoomMessageEventContent::new(MessageType::Gallery(gallery_event_content)))
1346 }
1347
1348 pub fn typing<U>(
1350 &self,
1351 user_ids: impl IntoIterator<Item = U>,
1352 ) -> EventBuilder<TypingEventContent>
1353 where
1354 U: Into<OwnedUserId>,
1355 {
1356 self.event(TypingEventContent::new(user_ids.into_iter().map(Into::into).collect()))
1357 .format(EventFormat::Ephemeral)
1358 }
1359
1360 pub fn read_receipts(&self) -> ReadReceiptBuilder<'_> {
1362 ReadReceiptBuilder { factory: self, content: ReceiptEventContent(Default::default()) }
1363 }
1364
1365 pub fn create(
1367 &self,
1368 creator: impl Into<OwnedUserId>,
1369 room_version: RoomVersionId,
1370 ) -> EventBuilder<RoomCreateEventContent> {
1371 let creator = creator.into();
1372 let mut event = self.event(RoomCreateEventContent::new_v1(creator.clone()));
1373 event.content.room_version = room_version;
1374
1375 if self.sender.is_some() {
1376 event.sender = self.sender.clone();
1377 } else {
1378 event.sender = Some(creator);
1379 }
1380
1381 event.state_key = Some("".to_owned());
1382
1383 event
1384 }
1385
1386 pub fn power_levels(&self) -> EventBuilder<RoomPowerLevelsEventContent> {
1388 let mut event = self.event(RoomPowerLevelsEventContent::new(&AuthorizationRules::V1));
1389 event.state_key = Some("".to_owned());
1390 event
1391 }
1392
1393 pub fn server_acl(
1395 &self,
1396 allow_ip_literals: bool,
1397 allow: Vec<String>,
1398 deny: Vec<String>,
1399 ) -> EventBuilder<RoomServerAclEventContent> {
1400 self.event(RoomServerAclEventContent::new(allow_ip_literals, allow, deny))
1401 }
1402
1403 pub fn canonical_alias(
1405 &self,
1406 alias: Option<OwnedRoomAliasId>,
1407 alt_aliases: Vec<OwnedRoomAliasId>,
1408 ) -> EventBuilder<RoomCanonicalAliasEventContent> {
1409 let mut content = RoomCanonicalAliasEventContent::new();
1410 content.alias = alias;
1411 content.alt_aliases = alt_aliases;
1412 let mut event = self.event(content);
1413 event.state_key = Some("".to_owned());
1415 event
1416 }
1417
1418 pub fn beacon(
1444 &self,
1445 beacon_info_event_id: OwnedEventId,
1446 latitude: f64,
1447 longitude: f64,
1448 uncertainty: u32,
1449 ts: Option<MilliSecondsSinceUnixEpoch>,
1450 ) -> EventBuilder<BeaconEventContent> {
1451 let geo_uri = format!("geo:{latitude},{longitude};u={uncertainty}");
1452 self.event(BeaconEventContent::new(beacon_info_event_id, geo_uri, ts))
1453 }
1454
1455 pub fn beacon_info(
1482 &self,
1483 description: Option<String>,
1484 duration: Duration,
1485 live: bool,
1486 ts: Option<MilliSecondsSinceUnixEpoch>,
1487 ) -> EventBuilder<BeaconInfoEventContent> {
1488 self.event(BeaconInfoEventContent::new(description, duration, live, ts))
1489 }
1490
1491 pub fn sticker(
1493 &self,
1494 body: impl Into<String>,
1495 info: ImageInfo,
1496 url: OwnedMxcUri,
1497 ) -> EventBuilder<StickerEventContent> {
1498 self.event(StickerEventContent::new(body.into(), info, url))
1499 }
1500
1501 pub fn call_invite(
1503 &self,
1504 call_id: OwnedVoipId,
1505 lifetime: UInt,
1506 offer: SessionDescription,
1507 version: VoipVersionId,
1508 ) -> EventBuilder<CallInviteEventContent> {
1509 self.event(CallInviteEventContent::new(call_id, lifetime, offer, version))
1510 }
1511
1512 pub fn rtc_notification(
1514 &self,
1515 notification_type: NotificationType,
1516 ) -> EventBuilder<RtcNotificationEventContent> {
1517 self.event(RtcNotificationEventContent::new(
1518 MilliSecondsSinceUnixEpoch::now(),
1519 Duration::new(30, 0),
1520 notification_type,
1521 ))
1522 }
1523
1524 pub fn call_decline(
1526 &self,
1527 notification_event_id: impl Into<OwnedEventId>,
1528 ) -> EventBuilder<RtcDeclineEventContent> {
1529 self.event(RtcDeclineEventContent::new(notification_event_id))
1530 }
1531
1532 pub fn call_membership_state(
1553 &self,
1554 user_id: impl Into<OwnedUserId>,
1555 device_id: String,
1556 ) -> EventBuilder<CallMemberEventContent> {
1557 let user_id = user_id.into();
1558 let event = self.event(CallMemberEventContent::new(
1559 Application::Call(CallApplicationContent::new("".to_owned(), CallScope::Room)),
1560 device_id.clone().into(),
1561 ActiveFocus::Livekit(ActiveLivekitFocus::new()),
1562 vec![],
1563 Some(MilliSecondsSinceUnixEpoch::now()),
1564 Some(Duration::from_secs(3600)),
1565 ));
1566 event
1567 .sender(user_id.clone())
1568 .state_key(CallMemberStateKey::new(user_id, device_id.into(), true).as_ref())
1569 }
1570
1571 pub fn direct(&self) -> EventBuilder<DirectEventContent> {
1573 self.global_account_data(DirectEventContent::default())
1574 }
1575
1576 pub fn ignored_user_list(
1578 &self,
1579 users: impl IntoIterator<Item = OwnedUserId>,
1580 ) -> EventBuilder<IgnoredUserListEventContent> {
1581 self.global_account_data(IgnoredUserListEventContent::users(users))
1582 }
1583
1584 pub fn push_rules(&self, rules: Ruleset) -> EventBuilder<PushRulesEventContent> {
1586 self.global_account_data(PushRulesEventContent::new(rules))
1587 }
1588
1589 pub fn space_child(
1591 &self,
1592 parent: impl Into<OwnedRoomId>,
1593 child: impl Into<OwnedRoomId> + Into<String>,
1594 ) -> EventBuilder<SpaceChildEventContent> {
1595 let mut event = self.event(SpaceChildEventContent::new(vec![]));
1596 event.room = Some(parent.into());
1597 event.state_key = Some(child.into());
1598 event
1599 }
1600
1601 pub fn space_parent(
1603 &self,
1604 parent: impl Into<OwnedRoomId> + Into<String>,
1605 child: impl Into<OwnedRoomId>,
1606 ) -> EventBuilder<SpaceParentEventContent> {
1607 let mut event = self.event(SpaceParentEventContent::new(vec![]));
1608 event.state_key = Some(parent.into());
1609 event.room = Some(child.into());
1610 event
1611 }
1612
1613 pub fn custom_message_like_event(&self) -> EventBuilder<CustomMessageLikeEventContent> {
1615 self.event(CustomMessageLikeEventContent)
1616 }
1617
1618 pub fn custom_state_event(&self) -> EventBuilder<CustomStateEventContent> {
1620 self.event(CustomStateEventContent).state_key("test")
1621 }
1622
1623 pub fn set_next_ts(&self, value: u64) {
1627 self.next_ts.store(value, SeqCst);
1628 }
1629
1630 pub fn global_account_data<C>(&self, content: C) -> EventBuilder<C>
1632 where
1633 C: GlobalAccountDataEventContent + StaticEventContent<IsPrefix = False>,
1634 {
1635 self.event(content).format(EventFormat::GlobalAccountData)
1636 }
1637
1638 pub fn room_account_data<C>(&self, content: C) -> EventBuilder<C>
1640 where
1641 C: RoomAccountDataEventContent + StaticEventContent<IsPrefix = False>,
1642 {
1643 self.event(content).format(EventFormat::RoomAccountData)
1644 }
1645
1646 pub fn fully_read(
1648 &self,
1649 event_id: impl Into<OwnedEventId>,
1650 ) -> EventBuilder<FullyReadEventContent> {
1651 self.room_account_data(FullyReadEventContent::new(event_id.into()))
1652 }
1653
1654 pub fn marked_unread(&self, unread: bool) -> EventBuilder<MarkedUnreadEventContent> {
1656 self.room_account_data(MarkedUnreadEventContent::new(unread))
1657 }
1658
1659 pub fn tag(&self, tags: Tags) -> EventBuilder<TagEventContent> {
1661 self.room_account_data(tags.into())
1662 }
1663
1664 pub fn tag_from_names(&self, tag_names: Vec<TagName>) -> EventBuilder<TagEventContent> {
1669 self.tag(tag_names.into_iter().map(|name| (name, TagInfo::default())).collect())
1670 }
1671
1672 pub fn space_order(&self, order: &str) -> EventBuilder<SpaceOrderEventContent> {
1675 let order = ruma::SpaceChildOrder::parse(order).expect("order should be valid");
1676 self.room_account_data(SpaceOrderEventContent::new(order))
1677 }
1678
1679 pub fn presence(&self, state: PresenceState) -> PresenceBuilder {
1684 PresenceBuilder { sender: self.sender.clone(), content: PresenceEventContent::new(state) }
1685 }
1686}
1687
1688#[derive(Debug)]
1690pub struct PresenceBuilder {
1691 sender: Option<OwnedUserId>,
1692 content: PresenceEventContent,
1693}
1694
1695impl PresenceBuilder {
1696 pub fn sender(mut self, sender: impl Into<OwnedUserId>) -> Self {
1698 self.sender = Some(sender.into());
1699 self
1700 }
1701
1702 pub fn avatar_url(mut self, url: impl Into<OwnedMxcUri>) -> Self {
1704 self.content.avatar_url = Some(url.into());
1705 self
1706 }
1707
1708 pub fn currently_active(mut self, active: bool) -> Self {
1710 self.content.currently_active = Some(active);
1711 self
1712 }
1713
1714 pub fn last_active_ago(mut self, ms: u64) -> Self {
1716 self.content.last_active_ago = Some(UInt::try_from(ms).unwrap());
1717 self
1718 }
1719
1720 pub fn status_msg(mut self, msg: impl Into<String>) -> Self {
1722 self.content.status_msg = Some(msg.into());
1723 self
1724 }
1725
1726 pub fn display_name(mut self, name: impl Into<String>) -> Self {
1728 self.content.displayname = Some(name.into());
1729 self
1730 }
1731}
1732
1733impl From<PresenceBuilder> for Raw<PresenceEvent> {
1734 fn from(builder: PresenceBuilder) -> Self {
1735 let sender = builder.sender.expect("sender must be set for presence events");
1736 let event = PresenceEvent { content: builder.content, sender };
1737 Raw::new(&event).unwrap().cast_unchecked()
1738 }
1739}
1740
1741impl EventBuilder<DirectEventContent> {
1742 pub fn add_user(
1744 mut self,
1745 user_id: impl Into<OwnedDirectUserIdentifier>,
1746 room_id: impl Into<OwnedRoomId>,
1747 ) -> Self {
1748 self.content.0.entry(user_id.into()).or_default().push(room_id.into());
1749 self
1750 }
1751}
1752
1753impl EventBuilder<RoomRedactionEventContent> {
1754 pub fn reason(mut self, reason: impl Into<String>) -> Self {
1756 self.content.reason = Some(reason.into());
1757 self
1758 }
1759}
1760
1761impl EventBuilder<RoomMemberEventContent> {
1762 pub fn membership(mut self, state: MembershipState) -> Self {
1767 self.content.membership = state;
1768 self
1769 }
1770
1771 pub fn invited(mut self, invited_user: impl Into<OwnedUserId> + Into<String>) -> Self {
1774 let invited_user: String = invited_user.into();
1775 assert_ne!(
1776 self.sender.as_deref().unwrap(),
1777 invited_user.as_str(),
1778 "invited user and sender can't be the same person"
1779 );
1780 self.content.membership = MembershipState::Invite;
1781 self.state_key = Some(invited_user);
1782 self
1783 }
1784
1785 pub fn leave(mut self) -> Self {
1789 self.content.membership = MembershipState::Leave;
1790 self.state_key = Some(self.sender.as_ref().expect("sender must be set").to_string());
1791 self
1792 }
1793
1794 pub fn kicked(mut self, kicked_user: impl Into<OwnedUserId> + Into<String>) -> Self {
1797 let kicked_user: String = kicked_user.into();
1798 assert_ne!(
1799 self.sender.as_deref().unwrap(),
1800 kicked_user.as_str(),
1801 "kicked user and sender can't be the same person, otherwise it's just a Leave"
1802 );
1803 self.content.membership = MembershipState::Leave;
1804 self.state_key = Some(kicked_user);
1805 self
1806 }
1807
1808 pub fn banned(mut self, banned_user: impl Into<OwnedUserId> + Into<String>) -> Self {
1811 let banned_user: String = banned_user.into();
1812 assert_ne!(
1813 self.sender.as_deref().unwrap(),
1814 banned_user.as_str(),
1815 "a user can't ban itself" );
1817 self.content.membership = MembershipState::Ban;
1818 self.state_key = Some(banned_user);
1819 self
1820 }
1821
1822 pub fn display_name(mut self, display_name: impl Into<String>) -> Self {
1824 self.content.displayname = Some(display_name.into());
1825 self
1826 }
1827
1828 pub fn avatar_url(mut self, url: impl Into<OwnedMxcUri>) -> Self {
1830 self.content.avatar_url = Some(url.into());
1831 self
1832 }
1833
1834 pub fn reason(mut self, reason: impl Into<String>) -> Self {
1836 self.content.reason = Some(reason.into());
1837 self
1838 }
1839
1840 pub fn previous(mut self, previous: impl Into<PreviousMembership>) -> Self {
1842 let previous = previous.into();
1843
1844 let mut prev_content = RoomMemberEventContent::new(previous.state);
1845 if let Some(avatar_url) = previous.avatar_url {
1846 prev_content.avatar_url = Some(avatar_url);
1847 }
1848 if let Some(display_name) = previous.display_name {
1849 prev_content.displayname = Some(display_name);
1850 }
1851
1852 self.unsigned.get_or_insert_with(Default::default).prev_content = Some(prev_content);
1853 self
1854 }
1855
1856 pub fn invite_room_state<I, E>(mut self, events: I) -> Self
1861 where
1862 I: IntoIterator<Item = E>,
1863 E: Into<Raw<AnyStrippedStateEvent>>,
1864 {
1865 self.unsigned.get_or_insert_with(Default::default).invite_room_state =
1866 Some(events.into_iter().map(Into::into).collect());
1867 self
1868 }
1869}
1870
1871impl EventBuilder<RoomAvatarEventContent> {
1872 pub fn url(mut self, url: impl Into<OwnedMxcUri>) -> Self {
1874 self.content.url = Some(url.into());
1875 self
1876 }
1877
1878 pub fn info(mut self, image: avatar::ImageInfo) -> Self {
1880 self.content.info = Some(Box::new(image));
1881 self
1882 }
1883}
1884
1885impl EventBuilder<RtcNotificationEventContent> {
1886 pub fn mentions(mut self, users: impl IntoIterator<Item = OwnedUserId>) -> Self {
1887 self.content.mentions = Some(Mentions::with_user_ids(users));
1888 self
1889 }
1890
1891 pub fn call_intent(mut self, call_intent: CallIntent) -> Self {
1892 self.content.call_intent = Some(call_intent);
1893 self
1894 }
1895
1896 pub fn relates_to_membership_state_event(mut self, event_id: OwnedEventId) -> Self {
1897 self.content.relates_to = Some(Reference::new(event_id));
1898 self
1899 }
1900
1901 pub fn lifetime(mut self, time_in_seconds: u64) -> Self {
1902 self.content.lifetime = Duration::from_secs(time_in_seconds);
1903 self
1904 }
1905}
1906
1907impl EventBuilder<RoomPowerLevelsEventContent> {
1908 pub fn set_users<I, U, P>(mut self, users: I) -> Self
1910 where
1911 I: IntoIterator<Item = (U, P)>,
1912 U: Into<OwnedUserId>,
1913 P: Into<Int>,
1914 {
1915 self.content.users = users.into_iter().map(|(user, pl)| (user.into(), pl.into())).collect();
1916 self
1917 }
1918
1919 pub fn set_events<I, P>(mut self, events: I) -> Self
1921 where
1922 I: IntoIterator<Item = (TimelineEventType, P)>,
1923 P: Into<Int>,
1924 {
1925 self.content.events =
1926 events.into_iter().map(|(event_type, pl)| (event_type, pl.into())).collect();
1927 self
1928 }
1929}
1930
1931impl EventBuilder<CallMemberEventContent> {
1932 #[allow(deprecated)]
1934 pub fn set_livekit_focus(
1935 mut self,
1936 alias: Option<String>,
1937 url: Option<String>,
1938 server_name: Option<OwnedServerName>,
1939 service_url: Option<String>,
1940 ) -> Self {
1941 if let CallMemberEventContent::SessionContent(session_data) = &mut self.content {
1942 let mut livekit = if let Some(url) = url {
1943 let mut focus = LivekitFocus::new(url);
1944 focus.service_url = service_url;
1945 focus
1946 } else {
1947 if let Some(service_url) = service_url {
1948 let mut focus = LivekitFocus::with_service_url(service_url);
1949 focus.url = url;
1950 focus
1951 } else {
1952 panic!("need to provide either url or service_url")
1953 }
1954 };
1955 livekit.alias = alias;
1956 livekit.server_name = server_name;
1957 session_data.foci_preferred = vec![Focus::Livekit(livekit)];
1958 } else {
1959 panic!("focus() called on a non-SessionContent call member event");
1960 }
1961 self
1962 }
1963
1964 pub fn application(mut self, call_id: String, scope: CallScope) -> Self {
1966 if let CallMemberEventContent::SessionContent(session_data) = &mut self.content {
1967 session_data.application =
1968 Application::Call(CallApplicationContent::new(call_id, scope));
1969 } else {
1970 panic!("application() called on a non-SessionContent call member event");
1971 }
1972 self
1973 }
1974
1975 pub fn leave(mut self) -> Self {
1977 self.content = CallMemberEventContent::new_empty(None);
1978 self
1979 }
1980}
1981pub struct ReadReceiptBuilder<'a> {
1982 factory: &'a EventFactory,
1983 content: ReceiptEventContent,
1984}
1985
1986impl ReadReceiptBuilder<'_> {
1987 pub fn add(
1989 self,
1990 event_id: impl Into<OwnedEventId>,
1991 user_id: impl Into<OwnedUserId>,
1992 tyype: ReceiptType,
1993 thread: ReceiptThread,
1994 ) -> Self {
1995 let ts = self.factory.next_server_ts();
1996 self.add_with_timestamp(event_id, user_id, tyype, thread, Some(ts))
1997 }
1998
1999 pub fn add_with_timestamp(
2001 mut self,
2002 event_id: impl Into<OwnedEventId>,
2003 user_id: impl Into<OwnedUserId>,
2004 tyype: ReceiptType,
2005 thread: ReceiptThread,
2006 ts: Option<MilliSecondsSinceUnixEpoch>,
2007 ) -> Self {
2008 let by_event = self.content.0.entry(event_id.into()).or_default();
2009 let by_type = by_event.entry(tyype).or_default();
2010
2011 let mut receipt = Receipt::default();
2012 if let Some(ts) = ts {
2013 receipt.ts = Some(ts);
2014 }
2015 receipt.thread = thread;
2016
2017 by_type.insert(user_id.into(), receipt);
2018 self
2019 }
2020
2021 pub fn into_content(self) -> ReceiptEventContent {
2023 self.content
2024 }
2025
2026 pub fn into_event(self) -> EventBuilder<ReceiptEventContent> {
2028 self.factory.event(self.into_content()).format(EventFormat::Ephemeral)
2029 }
2030}
2031
2032pub struct PreviousMembership {
2033 state: MembershipState,
2034 avatar_url: Option<OwnedMxcUri>,
2035 display_name: Option<String>,
2036}
2037
2038impl PreviousMembership {
2039 pub fn new(state: MembershipState) -> Self {
2040 Self { state, avatar_url: None, display_name: None }
2041 }
2042
2043 pub fn avatar_url(mut self, url: impl Into<OwnedMxcUri>) -> Self {
2044 self.avatar_url = Some(url.into());
2045 self
2046 }
2047
2048 pub fn display_name(mut self, name: impl Into<String>) -> Self {
2049 self.display_name = Some(name.into());
2050 self
2051 }
2052}
2053
2054impl From<MembershipState> for PreviousMembership {
2055 fn from(state: MembershipState) -> Self {
2056 Self::new(state)
2057 }
2058}
2059
2060#[derive(Clone, Default, Debug, Serialize, EventContent)]
2061#[ruma_event(type = "rs.matrix-sdk.custom.test", kind = MessageLike)]
2062pub struct CustomMessageLikeEventContent;
2063
2064#[derive(Clone, Default, Debug, Serialize, Deserialize, EventContent)]
2065#[ruma_event(type = "rs.matrix-sdk.custom-state.test", kind = State, state_key_type = String)]
2066pub struct CustomStateEventContent;