Skip to main content

matrix_sdk_test/
event_factory.rs

1// Copyright 2024 The Matrix.org Foundation C.I.C.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#![allow(missing_docs)]
16
17use std::{
18    collections::{BTreeMap, 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, MxcUri, OwnedDeviceId, OwnedEventId, OwnedMxcUri,
29    OwnedRoomAliasId, OwnedRoomId, OwnedTransactionId, OwnedUserId, OwnedVoipId, RoomId,
30    RoomVersionId, TransactionId, UInt, UserId, 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        beacon::BeaconEventContent,
41        beacon_info::BeaconInfoEventContent,
42        call::{
43            SessionDescription,
44            invite::CallInviteEventContent,
45            member::{
46                ActiveFocus, ActiveLivekitFocus, Application, CallApplicationContent,
47                CallMemberEventContent, CallMemberStateKey, CallScope, Focus, LivekitFocus,
48            },
49        },
50        direct::{DirectEventContent, OwnedDirectUserIdentifier},
51        fully_read::FullyReadEventContent,
52        ignored_user_list::IgnoredUserListEventContent,
53        macros::EventContent,
54        marked_unread::MarkedUnreadEventContent,
55        member_hints::MemberHintsEventContent,
56        poll::{
57            unstable_end::UnstablePollEndEventContent,
58            unstable_response::UnstablePollResponseEventContent,
59            unstable_start::{
60                NewUnstablePollStartEventContent, ReplacementUnstablePollStartEventContent,
61                UnstablePollAnswer, UnstablePollStartContentBlock, UnstablePollStartEventContent,
62            },
63        },
64        presence::{PresenceEvent, PresenceEventContent},
65        push_rules::PushRulesEventContent,
66        reaction::ReactionEventContent,
67        receipt::{Receipt, ReceiptEventContent, ReceiptThread, ReceiptType},
68        relation::{Annotation, BundledThread, Reference, Replacement, Reply, Thread},
69        room::{
70            ImageInfo,
71            avatar::{self, RoomAvatarEventContent},
72            canonical_alias::RoomCanonicalAliasEventContent,
73            create::{PreviousRoom, RoomCreateEventContent},
74            encrypted::{
75                EncryptedEventScheme, MegolmV1AesSha2ContentInit, RoomEncryptedEventContent,
76            },
77            encryption::RoomEncryptionEventContent,
78            history_visibility::{HistoryVisibility, RoomHistoryVisibilityEventContent},
79            join_rules::{JoinRule, RoomJoinRulesEventContent},
80            member::{MembershipState, RoomMemberEventContent},
81            message::{
82                FormattedBody, GalleryItemType, GalleryMessageEventContent,
83                ImageMessageEventContent, MessageType, OriginalSyncRoomMessageEvent, Relation,
84                RelationWithoutReplacement, RoomMessageEventContent,
85                RoomMessageEventContentWithoutRelation,
86            },
87            name::RoomNameEventContent,
88            pinned_events::RoomPinnedEventsEventContent,
89            power_levels::RoomPowerLevelsEventContent,
90            redaction::RoomRedactionEventContent,
91            server_acl::RoomServerAclEventContent,
92            tombstone::RoomTombstoneEventContent,
93            topic::RoomTopicEventContent,
94        },
95        rtc::{
96            decline::RtcDeclineEventContent,
97            notification::{CallIntent, NotificationType, RtcNotificationEventContent},
98        },
99        space::{child::SpaceChildEventContent, parent::SpaceParentEventContent},
100        space_order::SpaceOrderEventContent,
101        sticker::StickerEventContent,
102        tag::{TagEventContent, Tags},
103        typing::TypingEventContent,
104    },
105    owned_device_id,
106    presence::PresenceState,
107    push::Ruleset,
108    room::RoomType,
109    room_version_rules::AuthorizationRules,
110    serde::Raw,
111    server_name,
112};
113use serde::Serialize;
114use serde_json::json;
115
116use crate::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/// A thin copy of [`ruma::events::UnsignedRoomRedactionEvent`].
135#[derive(Debug, Serialize)]
136struct RedactedBecause {
137    /// Data specific to the event type.
138    content: RoomRedactionEventContent,
139
140    /// The globally unique event identifier for the user who sent the event.
141    event_id: OwnedEventId,
142
143    /// The fully-qualified ID of the user who sent this event.
144    sender: OwnedUserId,
145
146    /// Timestamp in milliseconds on originating homeserver when this event was
147    /// sent.
148    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
172// rustc can't derive Default because C isn't marked as `Default` 🤔 oh well.
173impl<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    /// An event that can be received in the timeline, via `/messages` for
189    /// example.
190    #[default]
191    Timeline,
192    /// An event that can be received in the timeline, via `/sync`.
193    SyncTimeline,
194    /// An event that is received in stripped state.
195    StrippedState,
196    /// An ephemeral event, like a read receipt.
197    Ephemeral,
198    /// A global account data.
199    GlobalAccountData,
200    /// A room account data.
201    RoomAccountData,
202}
203
204impl EventFormat {
205    /// Whether this format has a `sender` field.
206    fn has_sender(self) -> bool {
207        matches!(self, Self::Timeline | Self::SyncTimeline | Self::StrippedState)
208    }
209
210    /// Whether this format has an `event_id` field.
211    fn has_event_id(self) -> bool {
212        matches!(self, Self::Timeline | Self::SyncTimeline)
213    }
214
215    /// Whether this format has an `room_id` field.
216    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    /// The format of the event.
224    ///
225    /// It will decide which fields are added to the JSON.
226    format: EventFormat,
227    sender: Option<OwnedUserId>,
228    room: Option<OwnedRoomId>,
229    event_id: Option<OwnedEventId>,
230    /// Whether the event should *not* have an event id. False by default.
231    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: &RoomId) -> Self {
246        self.room = Some(room_id.to_owned());
247        self
248    }
249
250    pub fn sender(mut self, sender: &UserId) -> Self {
251        self.sender = Some(sender.to_owned());
252        self
253    }
254
255    pub fn event_id(mut self, event_id: &EventId) -> Self {
256        self.event_id = Some(event_id.to_owned());
257        self.no_event_id = false;
258        self
259    }
260
261    pub fn no_event_id(mut self) -> Self {
262        self.event_id = None;
263        self.no_event_id = true;
264        self
265    }
266
267    pub fn server_ts(mut self, ts: impl TimestampArg) -> Self {
268        self.server_ts = ts.to_milliseconds_since_unix_epoch();
269        self
270    }
271
272    pub fn unsigned_transaction_id(mut self, transaction_id: &TransactionId) -> Self {
273        self.unsigned.get_or_insert_with(Default::default).transaction_id =
274            Some(transaction_id.to_owned());
275        self
276    }
277
278    /// Add age to unsigned data in this event.
279    pub fn age(mut self, age: impl Into<Int>) -> Self {
280        self.unsigned.get_or_insert_with(Default::default).age = Some(age.into());
281        self
282    }
283
284    /// Set the previous content for this state event (in the unsigned section).
285    pub fn prev_content(mut self, prev: E) -> Self {
286        self.unsigned.get_or_insert_with(Default::default).prev_content = Some(prev);
287        self
288    }
289
290    /// Create a bundled thread summary in the unsigned bundled relations of
291    /// this event.
292    pub fn with_bundled_thread_summary(
293        mut self,
294        latest_event: Raw<AnySyncMessageLikeEvent>,
295        count: usize,
296        current_user_participated: bool,
297    ) -> Self {
298        let relations = self
299            .unsigned
300            .get_or_insert_with(Default::default)
301            .relations
302            .get_or_insert_with(BundledMessageLikeRelations::new);
303        relations.thread = Some(Box::new(BundledThread::new(
304            latest_event,
305            UInt::try_from(count).unwrap(),
306            current_user_participated,
307        )));
308        self
309    }
310
311    /// Create a bundled edit in the unsigned bundled relations of this event.
312    pub fn with_bundled_edit(mut self, replacement: impl Into<Raw<AnySyncTimelineEvent>>) -> Self {
313        let relations = self
314            .unsigned
315            .get_or_insert_with(Default::default)
316            .relations
317            .get_or_insert_with(BundledMessageLikeRelations::new);
318        relations.replace = Some(Box::new(replacement.into()));
319        self
320    }
321
322    /// For state events manually created, define the state key.
323    ///
324    /// For other state events created in the [`EventFactory`], this is
325    /// automatically filled upon creation or update of the events.
326    pub fn state_key(mut self, state_key: impl Into<String>) -> Self {
327        self.state_key = Some(state_key.into());
328        self
329    }
330}
331
332impl<E> EventBuilder<E>
333where
334    E: StaticEventContent<IsPrefix = False> + Serialize,
335{
336    #[inline(always)]
337    fn construct_json(self) -> serde_json::Value {
338        let mut json = json!({
339            "type": E::TYPE,
340            "content": self.content,
341            "origin_server_ts": self.server_ts,
342        });
343
344        let map = json.as_object_mut().unwrap();
345
346        if self.format.has_sender() {
347            // Use the `sender` preferably, or resort to the `redacted_because` sender if
348            // none has been set.
349            let sender = self
350                .sender
351                .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");
352            map.insert("sender".to_owned(), json!(sender));
353        }
354
355        if self.format.has_room_id() {
356            let room_id = self.room.expect("TimelineEvent requires a room id");
357            map.insert("room_id".to_owned(), json!(room_id));
358        }
359
360        if let Some(redacts) = self.redacts {
361            map.insert("redacts".to_owned(), json!(redacts));
362        }
363
364        if let Some(state_key) = self.state_key {
365            map.insert("state_key".to_owned(), json!(state_key));
366        }
367
368        if self.format.has_event_id() && !self.no_event_id {
369            let event_id = self.event_id.unwrap_or_else(|| {
370                // Compute a hash of the event to use it as the event ID, similar to how a
371                // server would. This is a little bit different since a server would redact the
372                // event before hashing, but at least the event ID construction will be
373                // deterministic and have the same format as in recent room versions.
374                let bytes = serde_json::to_vec(&map).unwrap();
375                EventId::new_v2_or_v3(&base64_sha256_hash(&bytes)).unwrap()
376            });
377
378            map.insert("event_id".to_owned(), json!(event_id));
379        }
380
381        if let Some(unsigned) = self.unsigned {
382            map.insert("unsigned".to_owned(), json!(unsigned));
383        }
384
385        json
386    }
387
388    /// Build an event from the [`EventBuilder`] and convert it into a
389    /// serialized and [`Raw`] event.
390    ///
391    /// The generic argument `T` allows you to automatically cast the [`Raw`]
392    /// event into any desired type.
393    pub fn into_raw<T>(self) -> Raw<T> {
394        Raw::new(&self.construct_json()).unwrap().cast_unchecked()
395    }
396
397    pub fn into_raw_timeline(self) -> Raw<AnyTimelineEvent> {
398        self.into_raw()
399    }
400
401    pub fn into_any_sync_message_like_event(self) -> AnySyncMessageLikeEvent {
402        self.format(EventFormat::SyncTimeline)
403            .into_raw()
404            .deserialize()
405            .expect("expected message like event")
406    }
407
408    pub fn into_original_sync_room_message_event(self) -> OriginalSyncRoomMessageEvent {
409        self.format(EventFormat::SyncTimeline)
410            .into_raw()
411            .deserialize()
412            .expect("expected original sync room message event")
413    }
414
415    pub fn into_raw_sync(self) -> Raw<AnySyncTimelineEvent> {
416        self.format(EventFormat::SyncTimeline).into_raw()
417    }
418
419    pub fn into_raw_sync_state(self) -> Raw<AnySyncStateEvent> {
420        self.format(EventFormat::SyncTimeline).into_raw()
421    }
422
423    pub fn into_event(self) -> TimelineEvent {
424        TimelineEvent::from_plaintext(self.into_raw_sync())
425    }
426
427    /// Returns just the event content as a JSON value.
428    ///
429    /// This is useful when you need only the content portion of an event,
430    /// for example when mocking HTTP responses that return event content.
431    pub fn into_content(self) -> serde_json::Value {
432        json!(self.content)
433    }
434}
435
436impl EventBuilder<RoomEncryptedEventContent> {
437    /// Turn this event into a [`TimelineEvent`] representing a decryption
438    /// failure
439    pub fn into_utd_sync_timeline_event(self) -> TimelineEvent {
440        let session_id = as_variant!(&self.content.scheme, EncryptedEventScheme::MegolmV1AesSha2)
441            .map(|content| content.session_id.clone());
442
443        TimelineEvent::from_utd(
444            self.into(),
445            UnableToDecryptInfo {
446                session_id,
447                reason: UnableToDecryptReason::MissingMegolmSession { withheld_code: None },
448            },
449        )
450    }
451}
452
453impl EventBuilder<RoomMessageEventContent> {
454    /// Adds a reply relation to the current event.
455    pub fn reply_to(mut self, event_id: &EventId) -> Self {
456        self.content.relates_to = Some(Relation::Reply(Reply::with_event_id(event_id.to_owned())));
457        self
458    }
459
460    /// Adds a thread relation to the root event, setting the reply fallback to
461    /// the latest in-thread event.
462    pub fn in_thread(mut self, root: &EventId, latest_thread_event: &EventId) -> Self {
463        self.content.relates_to =
464            Some(Relation::Thread(Thread::plain(root.to_owned(), latest_thread_event.to_owned())));
465        self
466    }
467
468    /// Adds a thread relation to the root event, that's a non-fallback reply to
469    /// another thread event.
470    pub fn in_thread_reply(mut self, root: &EventId, replied_to: &EventId) -> Self {
471        self.content.relates_to =
472            Some(Relation::Thread(Thread::reply(root.to_owned(), replied_to.to_owned())));
473        self
474    }
475
476    /// Adds the given mentions to the current event.
477    pub fn mentions(mut self, mentions: Mentions) -> Self {
478        self.content.mentions = Some(mentions);
479        self
480    }
481
482    /// Adds a replacement relation to the current event, with the new content
483    /// passed.
484    pub fn edit(
485        mut self,
486        edited_event_id: &EventId,
487        new_content: RoomMessageEventContentWithoutRelation,
488    ) -> Self {
489        self.content.relates_to =
490            Some(Relation::Replacement(Replacement::new(edited_event_id.to_owned(), new_content)));
491        self
492    }
493
494    /// Adds a caption to a media event.
495    ///
496    /// Will crash if the event isn't a media room message.
497    pub fn caption(
498        mut self,
499        caption: Option<String>,
500        formatted_caption: Option<FormattedBody>,
501    ) -> Self {
502        match &mut self.content.msgtype {
503            MessageType::Image(image) => {
504                let filename = image.filename().to_owned();
505                if let Some(caption) = caption {
506                    image.body = caption;
507                    image.filename = Some(filename);
508                } else {
509                    image.body = filename;
510                    image.filename = None;
511                }
512                image.formatted = formatted_caption;
513            }
514
515            MessageType::Audio(_) | MessageType::Video(_) | MessageType::File(_) => {
516                unimplemented!();
517            }
518
519            _ => panic!("unexpected event type for a caption"),
520        }
521
522        self
523    }
524}
525
526impl EventBuilder<UnstablePollStartEventContent> {
527    /// Adds a reply relation to the current event.
528    pub fn reply_to(mut self, event_id: &EventId) -> Self {
529        if let UnstablePollStartEventContent::New(content) = &mut self.content {
530            content.relates_to =
531                Some(RelationWithoutReplacement::Reply(Reply::with_event_id(event_id.to_owned())));
532        }
533        self
534    }
535
536    /// Adds a thread relation to the root event, setting the reply to
537    /// event id as well.
538    pub fn in_thread(mut self, root: &EventId, reply_to_event_id: &EventId) -> Self {
539        let thread = Thread::reply(root.to_owned(), reply_to_event_id.to_owned());
540
541        if let UnstablePollStartEventContent::New(content) = &mut self.content {
542            content.relates_to = Some(RelationWithoutReplacement::Thread(thread));
543        }
544        self
545    }
546}
547
548impl EventBuilder<RoomCreateEventContent> {
549    /// Define the predecessor fields.
550    pub fn predecessor(mut self, room_id: &RoomId) -> Self {
551        self.content.predecessor = Some(PreviousRoom::new(room_id.to_owned()));
552        self
553    }
554
555    /// Erase the predecessor if any.
556    pub fn no_predecessor(mut self) -> Self {
557        self.content.predecessor = None;
558        self
559    }
560
561    /// Sets the `m.room.create` `type` field to `m.space`.
562    pub fn with_space_type(mut self) -> Self {
563        self.content.room_type = Some(RoomType::Space);
564        self
565    }
566}
567
568impl EventBuilder<StickerEventContent> {
569    /// Add reply [`Thread`] relation to root event and set replied-to event id.
570    pub fn reply_thread(mut self, root: &EventId, reply_to_event: &EventId) -> Self {
571        self.content.relates_to =
572            Some(Relation::Thread(Thread::reply(root.to_owned(), reply_to_event.to_owned())));
573        self
574    }
575}
576
577impl<E: StaticEventContent<IsPrefix = False>> From<EventBuilder<E>> for Raw<AnySyncTimelineEvent>
578where
579    E: Serialize,
580{
581    fn from(val: EventBuilder<E>) -> Self {
582        val.into_raw_sync()
583    }
584}
585
586impl<E: StaticEventContent<IsPrefix = False>> From<EventBuilder<E>> for AnySyncTimelineEvent
587where
588    E: Serialize,
589{
590    fn from(val: EventBuilder<E>) -> Self {
591        Raw::<AnySyncTimelineEvent>::from(val).deserialize().expect("expected sync timeline event")
592    }
593}
594
595impl<E: StaticEventContent<IsPrefix = False>> From<EventBuilder<E>> for Raw<AnyTimelineEvent>
596where
597    E: Serialize,
598{
599    fn from(val: EventBuilder<E>) -> Self {
600        val.into_raw_timeline()
601    }
602}
603
604impl<E: StaticEventContent<IsPrefix = False>> From<EventBuilder<E>> for AnyTimelineEvent
605where
606    E: Serialize,
607{
608    fn from(val: EventBuilder<E>) -> Self {
609        Raw::<AnyTimelineEvent>::from(val).deserialize().expect("expected timeline event")
610    }
611}
612
613impl<E: StaticEventContent<IsPrefix = False>> From<EventBuilder<E>>
614    for Raw<AnyGlobalAccountDataEvent>
615where
616    E: Serialize,
617{
618    fn from(val: EventBuilder<E>) -> Self {
619        val.format(EventFormat::GlobalAccountData).into_raw()
620    }
621}
622
623impl<E: StaticEventContent<IsPrefix = False>> From<EventBuilder<E>> for AnyGlobalAccountDataEvent
624where
625    E: Serialize,
626{
627    fn from(val: EventBuilder<E>) -> Self {
628        Raw::<AnyGlobalAccountDataEvent>::from(val)
629            .deserialize()
630            .expect("expected global account data")
631    }
632}
633
634impl<E: StaticEventContent<IsPrefix = False>> From<EventBuilder<E>> for Raw<AnyRoomAccountDataEvent>
635where
636    E: Serialize,
637{
638    fn from(val: EventBuilder<E>) -> Self {
639        val.format(EventFormat::RoomAccountData).into_raw()
640    }
641}
642
643impl<E: StaticEventContent<IsPrefix = False>> From<EventBuilder<E>> for AnyRoomAccountDataEvent
644where
645    E: Serialize,
646{
647    fn from(val: EventBuilder<E>) -> Self {
648        Raw::<AnyRoomAccountDataEvent>::from(val).deserialize().expect("expected room account data")
649    }
650}
651
652impl<E: StaticEventContent<IsPrefix = False>> From<EventBuilder<E>> for TimelineEvent
653where
654    E: Serialize,
655{
656    fn from(val: EventBuilder<E>) -> Self {
657        val.into_event()
658    }
659}
660
661impl<E: StaticEventContent<IsPrefix = False> + StateEventContent> From<EventBuilder<E>>
662    for Raw<AnySyncStateEvent>
663{
664    fn from(val: EventBuilder<E>) -> Self {
665        val.format(EventFormat::SyncTimeline).into_raw()
666    }
667}
668
669impl<E: StaticEventContent<IsPrefix = False> + StateEventContent> From<EventBuilder<E>>
670    for AnySyncStateEvent
671{
672    fn from(val: EventBuilder<E>) -> Self {
673        Raw::<AnySyncStateEvent>::from(val).deserialize().expect("expected sync state")
674    }
675}
676
677impl<E: StaticEventContent<IsPrefix = False> + StateEventContent> From<EventBuilder<E>>
678    for Raw<SyncStateEvent<E>>
679where
680    E: StaticStateEventContent + RedactContent,
681    E::Redacted: RedactedStateEventContent,
682{
683    fn from(val: EventBuilder<E>) -> Self {
684        val.format(EventFormat::SyncTimeline).into_raw()
685    }
686}
687
688impl<E: StaticEventContent<IsPrefix = False> + StateEventContent> From<EventBuilder<E>>
689    for SyncStateEvent<E>
690where
691    E: StaticStateEventContent + RedactContent + EventContentFromType,
692    E::Redacted: RedactedStateEventContent<StateKey = <E as StateEventContent>::StateKey>
693        + EventContentFromType,
694{
695    fn from(val: EventBuilder<E>) -> Self {
696        Raw::<SyncStateEvent<E>>::from(val).deserialize().expect("expected sync state")
697    }
698}
699
700impl<E: StaticEventContent<IsPrefix = False> + StateEventContent> From<EventBuilder<E>>
701    for Raw<AnyStateEvent>
702{
703    fn from(val: EventBuilder<E>) -> Self {
704        val.into_raw()
705    }
706}
707
708impl<E: StaticEventContent<IsPrefix = False> + StateEventContent> From<EventBuilder<E>>
709    for AnyStateEvent
710{
711    fn from(val: EventBuilder<E>) -> Self {
712        Raw::<AnyStateEvent>::from(val).deserialize().expect("expected state")
713    }
714}
715
716impl<E: StaticEventContent<IsPrefix = False> + StateEventContent> From<EventBuilder<E>>
717    for Raw<StateEvent<E>>
718where
719    E: StaticStateEventContent + RedactContent,
720    E::Redacted: RedactedStateEventContent,
721{
722    fn from(val: EventBuilder<E>) -> Self {
723        val.into_raw()
724    }
725}
726
727impl<E: StaticEventContent<IsPrefix = False> + StateEventContent> From<EventBuilder<E>>
728    for StateEvent<E>
729where
730    E: StaticStateEventContent + RedactContent + EventContentFromType,
731    E::Redacted: RedactedStateEventContent<StateKey = <E as StateEventContent>::StateKey>
732        + EventContentFromType,
733{
734    fn from(val: EventBuilder<E>) -> Self {
735        Raw::<StateEvent<E>>::from(val).deserialize().expect("expected state")
736    }
737}
738
739impl<E: StaticEventContent<IsPrefix = False> + StateEventContent> From<EventBuilder<E>>
740    for Raw<AnyStrippedStateEvent>
741{
742    fn from(val: EventBuilder<E>) -> Self {
743        val.format(EventFormat::StrippedState).into_raw()
744    }
745}
746
747impl<E: StaticEventContent<IsPrefix = False> + StateEventContent> From<EventBuilder<E>>
748    for AnyStrippedStateEvent
749{
750    fn from(val: EventBuilder<E>) -> Self {
751        Raw::<AnyStrippedStateEvent>::from(val).deserialize().expect("expected stripped state")
752    }
753}
754
755impl<E: StaticEventContent<IsPrefix = False> + StateEventContent> From<EventBuilder<E>>
756    for Raw<StrippedStateEvent<E::PossiblyRedacted>>
757where
758    E: StaticStateEventContent,
759{
760    fn from(val: EventBuilder<E>) -> Self {
761        val.format(EventFormat::StrippedState).into_raw()
762    }
763}
764
765impl<E: StaticEventContent<IsPrefix = False> + StateEventContent> From<EventBuilder<E>>
766    for StrippedStateEvent<E::PossiblyRedacted>
767where
768    E: StaticStateEventContent,
769    E::PossiblyRedacted: PossiblyRedactedStateEventContent + EventContentFromType,
770{
771    fn from(val: EventBuilder<E>) -> Self {
772        Raw::<StrippedStateEvent<E::PossiblyRedacted>>::from(val)
773            .deserialize()
774            .expect("expected stripped state")
775    }
776}
777
778impl<E: StaticEventContent<IsPrefix = False> + EphemeralRoomEventContent> From<EventBuilder<E>>
779    for Raw<AnySyncEphemeralRoomEvent>
780{
781    fn from(val: EventBuilder<E>) -> Self {
782        val.format(EventFormat::Ephemeral).into_raw()
783    }
784}
785
786impl<E: StaticEventContent<IsPrefix = False> + EphemeralRoomEventContent> From<EventBuilder<E>>
787    for AnySyncEphemeralRoomEvent
788{
789    fn from(val: EventBuilder<E>) -> Self {
790        Raw::<AnySyncEphemeralRoomEvent>::from(val).deserialize().expect("expected ephemeral")
791    }
792}
793
794impl<E: StaticEventContent<IsPrefix = False> + MessageLikeEventContent> From<EventBuilder<E>>
795    for Raw<AnySyncMessageLikeEvent>
796{
797    fn from(val: EventBuilder<E>) -> Self {
798        val.format(EventFormat::SyncTimeline).into_raw()
799    }
800}
801
802impl<E: StaticEventContent<IsPrefix = False> + MessageLikeEventContent> From<EventBuilder<E>>
803    for AnySyncMessageLikeEvent
804{
805    fn from(val: EventBuilder<E>) -> Self {
806        Raw::<AnySyncMessageLikeEvent>::from(val).deserialize().expect("expected sync message-like")
807    }
808}
809
810impl<E: StaticEventContent<IsPrefix = False> + MessageLikeEventContent> From<EventBuilder<E>>
811    for Raw<SyncMessageLikeEvent<E>>
812where
813    E: RedactContent,
814    E::Redacted: RedactedMessageLikeEventContent,
815{
816    fn from(val: EventBuilder<E>) -> Self {
817        val.format(EventFormat::SyncTimeline).into_raw()
818    }
819}
820
821impl<E: StaticEventContent<IsPrefix = False> + MessageLikeEventContent> From<EventBuilder<E>>
822    for SyncMessageLikeEvent<E>
823where
824    E: RedactContent + EventContentFromType,
825    E::Redacted: RedactedMessageLikeEventContent + EventContentFromType,
826{
827    fn from(val: EventBuilder<E>) -> Self {
828        Raw::<SyncMessageLikeEvent<E>>::from(val).deserialize().expect("expected sync message-like")
829    }
830}
831
832impl<E: StaticEventContent<IsPrefix = False> + MessageLikeEventContent> From<EventBuilder<E>>
833    for Raw<AnyMessageLikeEvent>
834{
835    fn from(val: EventBuilder<E>) -> Self {
836        val.into_raw()
837    }
838}
839
840impl<E: StaticEventContent<IsPrefix = False> + MessageLikeEventContent> From<EventBuilder<E>>
841    for AnyMessageLikeEvent
842{
843    fn from(val: EventBuilder<E>) -> Self {
844        Raw::<AnyMessageLikeEvent>::from(val).deserialize().expect("expected message-like")
845    }
846}
847
848impl<E: StaticEventContent<IsPrefix = False> + MessageLikeEventContent> From<EventBuilder<E>>
849    for Raw<MessageLikeEvent<E>>
850where
851    E: RedactContent,
852    E::Redacted: RedactedMessageLikeEventContent,
853{
854    fn from(val: EventBuilder<E>) -> Self {
855        val.into_raw()
856    }
857}
858
859impl<E: StaticEventContent<IsPrefix = False> + MessageLikeEventContent> From<EventBuilder<E>>
860    for MessageLikeEvent<E>
861where
862    E: RedactContent + EventContentFromType,
863    E::Redacted: RedactedMessageLikeEventContent + EventContentFromType,
864{
865    fn from(val: EventBuilder<E>) -> Self {
866        Raw::<MessageLikeEvent<E>>::from(val).deserialize().expect("expected message-like")
867    }
868}
869
870#[derive(Debug, Default)]
871pub struct EventFactory {
872    next_ts: AtomicU64,
873    sender: Option<OwnedUserId>,
874    room: Option<OwnedRoomId>,
875}
876
877impl EventFactory {
878    pub fn new() -> Self {
879        Self { next_ts: AtomicU64::new(0), sender: None, room: None }
880    }
881
882    pub fn room(mut self, room_id: &RoomId) -> Self {
883        self.room = Some(room_id.to_owned());
884        self
885    }
886
887    pub fn sender(mut self, sender: &UserId) -> Self {
888        self.sender = Some(sender.to_owned());
889        self
890    }
891
892    pub fn server_ts(self, ts: u64) -> Self {
893        self.next_ts.store(ts, SeqCst);
894        self
895    }
896
897    fn next_server_ts(&self) -> MilliSecondsSinceUnixEpoch {
898        MilliSecondsSinceUnixEpoch(
899            self.next_ts
900                .fetch_add(1, SeqCst)
901                .try_into()
902                .expect("server timestamp should fit in js_int::UInt"),
903        )
904    }
905
906    /// Create an event from any event content.
907    pub fn event<E: StaticEventContent<IsPrefix = False>>(&self, content: E) -> EventBuilder<E> {
908        EventBuilder {
909            format: EventFormat::Timeline,
910            sender: self.sender.clone(),
911            room: self.room.clone(),
912            server_ts: self.next_server_ts(),
913            event_id: None,
914            no_event_id: false,
915            redacts: None,
916            content,
917            unsigned: None,
918            state_key: None,
919        }
920    }
921
922    /// Create a new plain text `m.room.message`.
923    pub fn text_msg(&self, content: impl Into<String>) -> EventBuilder<RoomMessageEventContent> {
924        self.event(RoomMessageEventContent::text_plain(content.into()))
925    }
926
927    /// Create a new plain emote `m.room.message`.
928    pub fn emote(&self, content: impl Into<String>) -> EventBuilder<RoomMessageEventContent> {
929        self.event(RoomMessageEventContent::emote_plain(content.into()))
930    }
931
932    /// Create a new `m.room.encrypted` event using the `m.megolm.v1.aes-sha2`
933    /// algorithm.
934    pub fn encrypted(
935        &self,
936        ciphertext: impl Into<String>,
937        sender_key: impl Into<String>,
938        device_id: impl Into<OwnedDeviceId>,
939        session_id: impl Into<String>,
940    ) -> EventBuilder<RoomEncryptedEventContent> {
941        self.event(RoomEncryptedEventContent::new(
942            EncryptedEventScheme::MegolmV1AesSha2(
943                MegolmV1AesSha2ContentInit {
944                    ciphertext: ciphertext.into(),
945                    sender_key: sender_key.into(),
946                    device_id: device_id.into(),
947                    session_id: session_id.into(),
948                }
949                .into(),
950            ),
951            None,
952        ))
953    }
954
955    /// Create a new `m.room.member` event for the given member.
956    ///
957    /// The given member will be used as the `sender` as well as the `state_key`
958    /// of the `m.room.member` event, unless the `sender` was already using
959    /// [`EventFactory::sender()`], in that case only the state key will be
960    /// set to the given `member`.
961    ///
962    /// The `membership` field of the content is set to
963    /// [`MembershipState::Join`].
964    ///
965    /// ```
966    /// use matrix_sdk_test::event_factory::EventFactory;
967    /// use ruma::{
968    ///     events::{
969    ///         SyncStateEvent,
970    ///         room::member::{MembershipState, RoomMemberEventContent},
971    ///     },
972    ///     room_id,
973    ///     serde::Raw,
974    ///     user_id,
975    /// };
976    ///
977    /// let factory = EventFactory::new().room(room_id!("!test:localhost"));
978    ///
979    /// let event: Raw<SyncStateEvent<RoomMemberEventContent>> = factory
980    ///     .member(user_id!("@alice:localhost"))
981    ///     .display_name("Alice")
982    ///     .into_raw();
983    /// ```
984    pub fn member(&self, member: &UserId) -> EventBuilder<RoomMemberEventContent> {
985        let mut event = self.event(RoomMemberEventContent::new(MembershipState::Join));
986
987        if self.sender.is_some() {
988            event.sender = self.sender.clone();
989        } else {
990            event.sender = Some(member.to_owned());
991        }
992
993        event.state_key = Some(member.to_string());
994
995        event
996    }
997
998    /// Create a tombstone state event for the room.
999    pub fn room_tombstone(
1000        &self,
1001        body: impl Into<String>,
1002        replacement: &RoomId,
1003    ) -> EventBuilder<RoomTombstoneEventContent> {
1004        let mut event =
1005            self.event(RoomTombstoneEventContent::new(body.into(), replacement.to_owned()));
1006        event.state_key = Some("".to_owned());
1007        event
1008    }
1009
1010    /// Create a state event for the topic.
1011    pub fn room_topic(&self, topic: impl Into<String>) -> EventBuilder<RoomTopicEventContent> {
1012        let mut event = self.event(RoomTopicEventContent::new(topic.into()));
1013        // The state key is empty for a room topic state event.
1014        event.state_key = Some("".to_owned());
1015        event
1016    }
1017
1018    /// Create a state event for the room name.
1019    pub fn room_name(&self, name: impl Into<String>) -> EventBuilder<RoomNameEventContent> {
1020        let mut event = self.event(RoomNameEventContent::new(name.into()));
1021        // The state key is empty for a room name state event.
1022        event.state_key = Some("".to_owned());
1023        event
1024    }
1025
1026    /// Create an empty state event for the room avatar.
1027    pub fn room_avatar(&self) -> EventBuilder<RoomAvatarEventContent> {
1028        let mut event = self.event(RoomAvatarEventContent::new());
1029        // The state key is empty for a room avatar state event.
1030        event.state_key = Some("".to_owned());
1031        event
1032    }
1033
1034    /// Create a state event for room encryption with recommended defaults.
1035    ///
1036    /// This creates an `m.room.encryption` event with the
1037    /// `m.megolm.v1.aes-sha2` algorithm and recommended rotation settings.
1038    pub fn room_encryption(&self) -> EventBuilder<RoomEncryptionEventContent> {
1039        let mut event = self.event(RoomEncryptionEventContent::with_recommended_defaults());
1040        // The state key is empty for a room encryption state event.
1041        event.state_key = Some("".to_owned());
1042        event
1043    }
1044
1045    /// Create a state event for room encryption with state event encryption
1046    /// enabled.
1047    #[cfg(feature = "experimental-encrypted-state-events")]
1048    pub fn room_encryption_with_state_encryption(
1049        &self,
1050    ) -> EventBuilder<RoomEncryptionEventContent> {
1051        let mut content = RoomEncryptionEventContent::with_recommended_defaults();
1052        content.encrypt_state_events = true;
1053        let mut event = self.event(content);
1054        event.state_key = Some("".to_owned());
1055        event
1056    }
1057
1058    /// Create a room history visibility state event.
1059    ///
1060    /// This creates an `m.room.history_visibility` event with the given
1061    /// visibility setting.
1062    pub fn room_history_visibility(
1063        &self,
1064        visibility: HistoryVisibility,
1065    ) -> EventBuilder<RoomHistoryVisibilityEventContent> {
1066        let mut event = self.event(RoomHistoryVisibilityEventContent::new(visibility));
1067        event.state_key = Some("".to_owned());
1068        event
1069    }
1070
1071    /// Create a room join rules state event.
1072    ///
1073    /// This creates an `m.room.join_rules` event with the given join rule.
1074    pub fn room_join_rules(&self, join_rule: JoinRule) -> EventBuilder<RoomJoinRulesEventContent> {
1075        let mut event = self.event(RoomJoinRulesEventContent::new(join_rule));
1076        event.state_key = Some("".to_owned());
1077        event
1078    }
1079
1080    /// Create a state event for the room's pinned events.
1081    pub fn room_pinned_events(
1082        &self,
1083        pinned: Vec<OwnedEventId>,
1084    ) -> EventBuilder<RoomPinnedEventsEventContent> {
1085        self.event(RoomPinnedEventsEventContent::new(pinned)).state_key("")
1086    }
1087
1088    /// Create a new `m.member_hints` event with the given service members.
1089    ///
1090    /// ```
1091    /// use std::collections::BTreeSet;
1092    ///
1093    /// use matrix_sdk_test::event_factory::EventFactory;
1094    /// use ruma::{
1095    ///     events::{SyncStateEvent, member_hints::MemberHintsEventContent},
1096    ///     owned_user_id, room_id,
1097    ///     serde::Raw,
1098    ///     user_id,
1099    /// };
1100    ///
1101    /// let factory = EventFactory::new().room(room_id!("!test:localhost"));
1102    ///
1103    /// let event: Raw<SyncStateEvent<MemberHintsEventContent>> = factory
1104    ///     .member_hints(BTreeSet::from([owned_user_id!("@alice:localhost")]))
1105    ///     .sender(user_id!("@alice:localhost"))
1106    ///     .into_raw();
1107    /// ```
1108    pub fn member_hints(
1109        &self,
1110        service_members: BTreeSet<OwnedUserId>,
1111    ) -> EventBuilder<MemberHintsEventContent> {
1112        // The `m.member_hints` event always has an empty state key, so let's set it.
1113        self.event(MemberHintsEventContent::new(service_members)).state_key("")
1114    }
1115
1116    /// Create a new plain/html `m.room.message`.
1117    pub fn text_html(
1118        &self,
1119        plain: impl Into<String>,
1120        html: impl Into<String>,
1121    ) -> EventBuilder<RoomMessageEventContent> {
1122        self.event(RoomMessageEventContent::text_html(plain, html))
1123    }
1124
1125    /// Create a new plain notice `m.room.message`.
1126    pub fn notice(&self, content: impl Into<String>) -> EventBuilder<RoomMessageEventContent> {
1127        self.event(RoomMessageEventContent::notice_plain(content))
1128    }
1129
1130    /// Add a reaction to an event.
1131    pub fn reaction(
1132        &self,
1133        event_id: &EventId,
1134        annotation: impl Into<String>,
1135    ) -> EventBuilder<ReactionEventContent> {
1136        self.event(ReactionEventContent::new(Annotation::new(
1137            event_id.to_owned(),
1138            annotation.into(),
1139        )))
1140    }
1141
1142    /// Create a live redaction for the given event id.
1143    ///
1144    /// Note: this is not a redacted event, but a redaction event, that will
1145    /// cause another event to be redacted.
1146    pub fn redaction(&self, event_id: &EventId) -> EventBuilder<RoomRedactionEventContent> {
1147        let mut builder = self.event(RoomRedactionEventContent::new_v11(event_id.to_owned()));
1148        builder.redacts = Some(event_id.to_owned());
1149        builder
1150    }
1151
1152    /// Create a redacted event, with extra information in the unsigned section
1153    /// about the redaction itself.
1154    pub fn redacted<T: StaticEventContent<IsPrefix = False> + RedactedMessageLikeEventContent>(
1155        &self,
1156        redacter: &UserId,
1157        content: T,
1158    ) -> EventBuilder<T> {
1159        let mut builder = self.event(content);
1160
1161        let redacted_because = RedactedBecause {
1162            content: RoomRedactionEventContent::default(),
1163            event_id: EventId::new_v1(server_name!("dummy.server")),
1164            sender: redacter.to_owned(),
1165            origin_server_ts: self.next_server_ts(),
1166        };
1167        builder.unsigned.get_or_insert_with(Default::default).redacted_because =
1168            Some(redacted_because);
1169
1170        builder
1171    }
1172
1173    /// Create a redacted state event, with extra information in the unsigned
1174    /// section about the redaction itself.
1175    pub fn redacted_state<T: StaticEventContent<IsPrefix = False> + RedactedStateEventContent>(
1176        &self,
1177        redacter: &UserId,
1178        state_key: impl Into<String>,
1179        content: T,
1180    ) -> EventBuilder<T> {
1181        let mut builder = self.event(content);
1182
1183        let redacted_because = RedactedBecause {
1184            content: RoomRedactionEventContent::default(),
1185            event_id: EventId::new_v1(server_name!("dummy.server")),
1186            sender: redacter.to_owned(),
1187            origin_server_ts: self.next_server_ts(),
1188        };
1189        builder.unsigned.get_or_insert_with(Default::default).redacted_because =
1190            Some(redacted_because);
1191        builder.state_key = Some(state_key.into());
1192
1193        builder
1194    }
1195
1196    /// Create a poll start event given a text, the question and the possible
1197    /// answers.
1198    pub fn poll_start(
1199        &self,
1200        fallback_text: impl Into<String>,
1201        poll_question: impl Into<String>,
1202        answers: Vec<impl Into<String>>,
1203    ) -> EventBuilder<UnstablePollStartEventContent> {
1204        // PollAnswers 'constructor' is not public, so we need to deserialize them
1205        let answers: Vec<UnstablePollAnswer> = answers
1206            .into_iter()
1207            .enumerate()
1208            .map(|(idx, answer)| UnstablePollAnswer::new(idx.to_string(), answer))
1209            .collect();
1210        let poll_answers = answers.try_into().unwrap();
1211        let poll_start_content =
1212            UnstablePollStartEventContent::New(NewUnstablePollStartEventContent::plain_text(
1213                fallback_text,
1214                UnstablePollStartContentBlock::new(poll_question, poll_answers),
1215            ));
1216        self.event(poll_start_content)
1217    }
1218
1219    /// Create a poll edit event given the new question and possible answers.
1220    pub fn poll_edit(
1221        &self,
1222        edited_event_id: &EventId,
1223        poll_question: impl Into<String>,
1224        answers: Vec<impl Into<String>>,
1225    ) -> EventBuilder<ReplacementUnstablePollStartEventContent> {
1226        // PollAnswers 'constructor' is not public, so we need to deserialize them
1227        let answers: Vec<UnstablePollAnswer> = answers
1228            .into_iter()
1229            .enumerate()
1230            .map(|(idx, answer)| UnstablePollAnswer::new(idx.to_string(), answer))
1231            .collect();
1232        let poll_answers = answers.try_into().unwrap();
1233        let poll_start_content_block =
1234            UnstablePollStartContentBlock::new(poll_question, poll_answers);
1235        self.event(ReplacementUnstablePollStartEventContent::new(
1236            poll_start_content_block,
1237            edited_event_id.to_owned(),
1238        ))
1239    }
1240
1241    /// Create a poll response with the given answer id and the associated poll
1242    /// start event id.
1243    pub fn poll_response(
1244        &self,
1245        answers: Vec<impl Into<String>>,
1246        poll_start_id: &EventId,
1247    ) -> EventBuilder<UnstablePollResponseEventContent> {
1248        self.event(UnstablePollResponseEventContent::new(
1249            answers.into_iter().map(Into::into).collect(),
1250            poll_start_id.to_owned(),
1251        ))
1252    }
1253
1254    /// Create a poll response with the given text and the associated poll start
1255    /// event id.
1256    pub fn poll_end(
1257        &self,
1258        content: impl Into<String>,
1259        poll_start_id: &EventId,
1260    ) -> EventBuilder<UnstablePollEndEventContent> {
1261        self.event(UnstablePollEndEventContent::new(content.into(), poll_start_id.to_owned()))
1262    }
1263
1264    /// Creates a plain (unencrypted) image event content referencing the given
1265    /// MXC ID.
1266    pub fn image(
1267        &self,
1268        filename: String,
1269        url: OwnedMxcUri,
1270    ) -> EventBuilder<RoomMessageEventContent> {
1271        let image_event_content = ImageMessageEventContent::plain(filename, url);
1272        self.event(RoomMessageEventContent::new(MessageType::Image(image_event_content)))
1273    }
1274
1275    /// Create a gallery event containing a single plain (unencrypted) image
1276    /// referencing the given MXC ID.
1277    pub fn gallery(
1278        &self,
1279        body: String,
1280        filename: String,
1281        url: OwnedMxcUri,
1282    ) -> EventBuilder<RoomMessageEventContent> {
1283        let gallery_event_content = GalleryMessageEventContent::new(
1284            body,
1285            None,
1286            vec![GalleryItemType::Image(ImageMessageEventContent::plain(filename, url))],
1287        );
1288        self.event(RoomMessageEventContent::new(MessageType::Gallery(gallery_event_content)))
1289    }
1290
1291    /// Create a typing notification event.
1292    pub fn typing(&self, user_ids: Vec<&UserId>) -> EventBuilder<TypingEventContent> {
1293        self.event(TypingEventContent::new(user_ids.into_iter().map(ToOwned::to_owned).collect()))
1294            .format(EventFormat::Ephemeral)
1295    }
1296
1297    /// Create a read receipt event.
1298    pub fn read_receipts(&self) -> ReadReceiptBuilder<'_> {
1299        ReadReceiptBuilder { factory: self, content: ReceiptEventContent(Default::default()) }
1300    }
1301
1302    /// Create a new `m.room.create` event.
1303    pub fn create(
1304        &self,
1305        creator_user_id: &UserId,
1306        room_version: RoomVersionId,
1307    ) -> EventBuilder<RoomCreateEventContent> {
1308        let mut event = self.event(RoomCreateEventContent::new_v1(creator_user_id.to_owned()));
1309        event.content.room_version = room_version;
1310
1311        if self.sender.is_some() {
1312            event.sender = self.sender.clone();
1313        } else {
1314            event.sender = Some(creator_user_id.to_owned());
1315        }
1316
1317        event.state_key = Some("".to_owned());
1318
1319        event
1320    }
1321
1322    /// Create a new `m.room.power_levels` event.
1323    pub fn power_levels(
1324        &self,
1325        map: &mut BTreeMap<OwnedUserId, Int>,
1326    ) -> EventBuilder<RoomPowerLevelsEventContent> {
1327        let mut content = RoomPowerLevelsEventContent::new(&AuthorizationRules::V1);
1328        content.users.append(map);
1329        let mut event = self.event(content);
1330        event.state_key = Some("".to_owned());
1331        event
1332    }
1333
1334    /// Create a new `m.room.power_levels` event with default values.
1335    pub fn default_power_levels(&self) -> EventBuilder<RoomPowerLevelsEventContent> {
1336        self.power_levels(&mut BTreeMap::new())
1337    }
1338
1339    /// Create a new `m.room.server_acl` event.
1340    pub fn server_acl(
1341        &self,
1342        allow_ip_literals: bool,
1343        allow: Vec<String>,
1344        deny: Vec<String>,
1345    ) -> EventBuilder<RoomServerAclEventContent> {
1346        self.event(RoomServerAclEventContent::new(allow_ip_literals, allow, deny))
1347    }
1348
1349    /// Create a new `m.room.canonical_alias` event.
1350    pub fn canonical_alias(
1351        &self,
1352        alias: Option<OwnedRoomAliasId>,
1353        alt_aliases: Vec<OwnedRoomAliasId>,
1354    ) -> EventBuilder<RoomCanonicalAliasEventContent> {
1355        let mut content = RoomCanonicalAliasEventContent::new();
1356        content.alias = alias;
1357        content.alt_aliases = alt_aliases;
1358        let mut event = self.event(content);
1359        // The state key is empty for a canonical alias state event.
1360        event.state_key = Some("".to_owned());
1361        event
1362    }
1363
1364    /// Create a new `org.matrix.msc3672.beacon` event.
1365    ///
1366    /// ```
1367    /// use matrix_sdk_test::event_factory::EventFactory;
1368    /// use ruma::{
1369    ///     MilliSecondsSinceUnixEpoch,
1370    ///     events::{MessageLikeEvent, beacon::BeaconEventContent},
1371    ///     owned_event_id, room_id,
1372    ///     serde::Raw,
1373    ///     user_id,
1374    /// };
1375    ///
1376    /// let factory = EventFactory::new().room(room_id!("!test:localhost"));
1377    ///
1378    /// let event: Raw<MessageLikeEvent<BeaconEventContent>> = factory
1379    ///     .beacon(
1380    ///         owned_event_id!("$123456789abc:localhost"),
1381    ///         10.1,
1382    ///         15.2,
1383    ///         5,
1384    ///         Some(MilliSecondsSinceUnixEpoch(1000u32.into())),
1385    ///     )
1386    ///     .sender(user_id!("@alice:localhost"))
1387    ///     .into_raw();
1388    /// ```
1389    pub fn beacon(
1390        &self,
1391        beacon_info_event_id: OwnedEventId,
1392        latitude: f64,
1393        longitude: f64,
1394        uncertainty: u32,
1395        ts: Option<MilliSecondsSinceUnixEpoch>,
1396    ) -> EventBuilder<BeaconEventContent> {
1397        let geo_uri = format!("geo:{latitude},{longitude};u={uncertainty}");
1398        self.event(BeaconEventContent::new(beacon_info_event_id, geo_uri, ts))
1399    }
1400
1401    /// Create a new `org.matrix.msc3672.beacon_info` state event.
1402    ///
1403    /// # Arguments
1404    ///
1405    /// * `description` - An optional human-readable label for the sharing
1406    ///   session.
1407    /// * `duration` - How long the location share is active.
1408    /// * `live` - Whether the sharing session is active. Pass `true` to start
1409    ///   and `false` to stop.
1410    /// * `ts` - The start timestamp; if `None` the current time is used.
1411    ///
1412    /// # Example
1413    ///
1414    /// ```rust
1415    /// use std::time::Duration;
1416    ///
1417    /// use matrix_sdk_test::event_factory::EventFactory;
1418    /// use ruma::{room_id, user_id};
1419    ///
1420    /// let factory = EventFactory::new().room(room_id!("!test:localhost"));
1421    ///
1422    /// let event = factory
1423    ///     .beacon_info(None, Duration::from_secs(60), true, None)
1424    ///     .sender(user_id!("@alice:localhost"))
1425    ///     .state_key(user_id!("@alice:localhost"));
1426    /// ```
1427    pub fn beacon_info(
1428        &self,
1429        description: Option<String>,
1430        duration: Duration,
1431        live: bool,
1432        ts: Option<MilliSecondsSinceUnixEpoch>,
1433    ) -> EventBuilder<BeaconInfoEventContent> {
1434        self.event(BeaconInfoEventContent::new(description, duration, live, ts))
1435    }
1436
1437    /// Create a new `m.sticker` event.
1438    pub fn sticker(
1439        &self,
1440        body: impl Into<String>,
1441        info: ImageInfo,
1442        url: OwnedMxcUri,
1443    ) -> EventBuilder<StickerEventContent> {
1444        self.event(StickerEventContent::new(body.into(), info, url))
1445    }
1446
1447    /// Create a new `m.call.invite` event.
1448    pub fn call_invite(
1449        &self,
1450        call_id: OwnedVoipId,
1451        lifetime: UInt,
1452        offer: SessionDescription,
1453        version: VoipVersionId,
1454    ) -> EventBuilder<CallInviteEventContent> {
1455        self.event(CallInviteEventContent::new(call_id, lifetime, offer, version))
1456    }
1457
1458    /// Create a new `m.rtc.notification` event.
1459    pub fn rtc_notification(
1460        &self,
1461        notification_type: NotificationType,
1462    ) -> EventBuilder<RtcNotificationEventContent> {
1463        self.event(RtcNotificationEventContent::new(
1464            MilliSecondsSinceUnixEpoch::now(),
1465            Duration::new(30, 0),
1466            notification_type,
1467        ))
1468    }
1469
1470    // Creates a new `org.matrix.msc4310.rtc.decline` event.
1471    pub fn call_decline(
1472        &self,
1473        notification_event_id: &EventId,
1474    ) -> EventBuilder<RtcDeclineEventContent> {
1475        self.event(RtcDeclineEventContent::new(notification_event_id))
1476    }
1477
1478    /// Creates a rtc membership state event.
1479    ///
1480    /// ```
1481    /// use matrix_sdk_test::event_factory::EventFactory;
1482    /// use ruma::{
1483    ///     events::{SyncStateEvent, call::member::CallMemberEventContent},
1484    ///     owned_user_id, room_id,
1485    ///     serde::Raw,
1486    /// };
1487    ///
1488    /// let factory = EventFactory::new().room(room_id!("!test:localhost"));
1489    ///
1490    /// let event: Raw<SyncStateEvent<CallMemberEventContent>> = factory
1491    ///     .call_membership_state(
1492    ///         owned_user_id!("@alice:localhost"),
1493    ///         "ABCDEF".to_owned(),
1494    ///     )
1495    ///     .lk_focus("alias".to_owned(), "https://livekit2.com".to_owned())
1496    ///     .into_raw();
1497    /// ```
1498    pub fn call_membership_state(
1499        &self,
1500        user_id: OwnedUserId,
1501        device_id: String,
1502    ) -> EventBuilder<CallMemberEventContent> {
1503        let event = self.event(CallMemberEventContent::new(
1504            Application::Call(CallApplicationContent::new("".to_owned(), CallScope::Room)),
1505            owned_device_id!(device_id.clone()),
1506            ActiveFocus::Livekit(ActiveLivekitFocus::new()),
1507            vec![],
1508            Some(MilliSecondsSinceUnixEpoch::now()),
1509            Some(Duration::from_secs(3600)),
1510        ));
1511        event
1512            .sender(&user_id)
1513            .state_key(CallMemberStateKey::new(user_id, device_id.into(), true).as_ref())
1514    }
1515
1516    /// Create a new `m.direct` global account data event.
1517    pub fn direct(&self) -> EventBuilder<DirectEventContent> {
1518        self.global_account_data(DirectEventContent::default())
1519    }
1520
1521    /// Create a new `m.ignored_user_list` global account data event.
1522    pub fn ignored_user_list(
1523        &self,
1524        users: impl IntoIterator<Item = OwnedUserId>,
1525    ) -> EventBuilder<IgnoredUserListEventContent> {
1526        self.global_account_data(IgnoredUserListEventContent::users(users))
1527    }
1528
1529    /// Create a new `m.push_rules` global account data event.
1530    pub fn push_rules(&self, rules: Ruleset) -> EventBuilder<PushRulesEventContent> {
1531        self.global_account_data(PushRulesEventContent::new(rules))
1532    }
1533
1534    /// Create a new `m.space.child` state event.
1535    pub fn space_child(
1536        &self,
1537        parent: OwnedRoomId,
1538        child: OwnedRoomId,
1539    ) -> EventBuilder<SpaceChildEventContent> {
1540        let mut event = self.event(SpaceChildEventContent::new(vec![]));
1541        event.room = Some(parent);
1542        event.state_key = Some(child.to_string());
1543        event
1544    }
1545
1546    /// Create a new `m.space.parent` state event.
1547    pub fn space_parent(
1548        &self,
1549        parent: OwnedRoomId,
1550        child: OwnedRoomId,
1551    ) -> EventBuilder<SpaceParentEventContent> {
1552        let mut event = self.event(SpaceParentEventContent::new(vec![]));
1553        event.state_key = Some(parent.to_string());
1554        event.room = Some(child);
1555        event
1556    }
1557
1558    /// Create a new `rs.matrix-sdk.custom.test` custom event
1559    pub fn custom_message_like_event(&self) -> EventBuilder<CustomMessageLikeEventContent> {
1560        self.event(CustomMessageLikeEventContent)
1561    }
1562
1563    /// Set the next server timestamp.
1564    ///
1565    /// Timestamps will continue to increase by 1 (millisecond) from that value.
1566    pub fn set_next_ts(&self, value: u64) {
1567        self.next_ts.store(value, SeqCst);
1568    }
1569
1570    /// Create a new global account data event of the given `C` content type.
1571    pub fn global_account_data<C>(&self, content: C) -> EventBuilder<C>
1572    where
1573        C: GlobalAccountDataEventContent + StaticEventContent<IsPrefix = False>,
1574    {
1575        self.event(content).format(EventFormat::GlobalAccountData)
1576    }
1577
1578    /// Create a new room account data event of the given `C` content type.
1579    pub fn room_account_data<C>(&self, content: C) -> EventBuilder<C>
1580    where
1581        C: RoomAccountDataEventContent + StaticEventContent<IsPrefix = False>,
1582    {
1583        self.event(content).format(EventFormat::RoomAccountData)
1584    }
1585
1586    /// Create a new `m.fully_read` room account data event.
1587    pub fn fully_read(&self, event_id: &EventId) -> EventBuilder<FullyReadEventContent> {
1588        self.room_account_data(FullyReadEventContent::new(event_id.to_owned()))
1589    }
1590
1591    /// Create a new `m.marked_unread` room account data event.
1592    pub fn marked_unread(&self, unread: bool) -> EventBuilder<MarkedUnreadEventContent> {
1593        self.room_account_data(MarkedUnreadEventContent::new(unread))
1594    }
1595
1596    /// Create a new `m.tag` room account data event with the given tags.
1597    pub fn tag(&self, tags: Tags) -> EventBuilder<TagEventContent> {
1598        self.room_account_data(tags.into())
1599    }
1600
1601    /// Create a new `m.space_order` room account data event with the given
1602    /// order.
1603    pub fn space_order(&self, order: &str) -> EventBuilder<SpaceOrderEventContent> {
1604        let order = ruma::SpaceChildOrder::parse(order).expect("order should be valid");
1605        self.room_account_data(SpaceOrderEventContent::new(order))
1606    }
1607
1608    /// Create a new `m.presence` event.
1609    ///
1610    /// This is a special event type that has its own structure different from
1611    /// regular Matrix events.
1612    pub fn presence(&self, state: PresenceState) -> PresenceBuilder {
1613        PresenceBuilder { sender: self.sender.clone(), content: PresenceEventContent::new(state) }
1614    }
1615}
1616
1617/// Builder for presence events.
1618#[derive(Debug)]
1619pub struct PresenceBuilder {
1620    sender: Option<OwnedUserId>,
1621    content: PresenceEventContent,
1622}
1623
1624impl PresenceBuilder {
1625    /// Set the sender of the presence event.
1626    pub fn sender(mut self, sender: &UserId) -> Self {
1627        self.sender = Some(sender.to_owned());
1628        self
1629    }
1630
1631    /// Set the avatar URL.
1632    pub fn avatar_url(mut self, url: &MxcUri) -> Self {
1633        self.content.avatar_url = Some(url.to_owned());
1634        self
1635    }
1636
1637    /// Set whether the user is currently active.
1638    pub fn currently_active(mut self, active: bool) -> Self {
1639        self.content.currently_active = Some(active);
1640        self
1641    }
1642
1643    /// Set the last active time in milliseconds.
1644    pub fn last_active_ago(mut self, ms: u64) -> Self {
1645        self.content.last_active_ago = Some(UInt::try_from(ms).unwrap());
1646        self
1647    }
1648
1649    /// Set the status message.
1650    pub fn status_msg(mut self, msg: impl Into<String>) -> Self {
1651        self.content.status_msg = Some(msg.into());
1652        self
1653    }
1654
1655    /// Set the display name.
1656    pub fn display_name(mut self, name: impl Into<String>) -> Self {
1657        self.content.displayname = Some(name.into());
1658        self
1659    }
1660}
1661
1662impl From<PresenceBuilder> for Raw<PresenceEvent> {
1663    fn from(builder: PresenceBuilder) -> Self {
1664        let sender = builder.sender.expect("sender must be set for presence events");
1665        let event = PresenceEvent { content: builder.content, sender };
1666        Raw::new(&event).unwrap().cast_unchecked()
1667    }
1668}
1669
1670impl EventBuilder<DirectEventContent> {
1671    /// Add a user/room pair to the `m.direct` event.
1672    pub fn add_user(mut self, user_id: OwnedDirectUserIdentifier, room_id: &RoomId) -> Self {
1673        self.content.0.entry(user_id).or_default().push(room_id.to_owned());
1674        self
1675    }
1676}
1677
1678impl EventBuilder<RoomMemberEventContent> {
1679    /// Set the `membership` of the `m.room.member` event to the given
1680    /// [`MembershipState`].
1681    ///
1682    /// The default is [`MembershipState::Join`].
1683    pub fn membership(mut self, state: MembershipState) -> Self {
1684        self.content.membership = state;
1685        self
1686    }
1687
1688    /// Set that the sender of this event invited the user passed as a parameter
1689    /// here.
1690    pub fn invited(mut self, invited_user: &UserId) -> Self {
1691        assert_ne!(
1692            self.sender.as_deref().unwrap(),
1693            invited_user,
1694            "invited user and sender can't be the same person"
1695        );
1696        self.content.membership = MembershipState::Invite;
1697        self.state_key = Some(invited_user.to_string());
1698        self
1699    }
1700
1701    /// Set that the sender of this event left the room (self-leave).
1702    ///
1703    /// This sets the membership to Leave and uses the sender as the state_key.
1704    pub fn leave(mut self) -> Self {
1705        self.content.membership = MembershipState::Leave;
1706        self.state_key = Some(self.sender.as_ref().expect("sender must be set").to_string());
1707        self
1708    }
1709
1710    /// Set that the sender of this event kicked the user passed as a parameter
1711    /// here.
1712    pub fn kicked(mut self, kicked_user: &UserId) -> Self {
1713        assert_ne!(
1714            self.sender.as_deref().unwrap(),
1715            kicked_user,
1716            "kicked user and sender can't be the same person, otherwise it's just a Leave"
1717        );
1718        self.content.membership = MembershipState::Leave;
1719        self.state_key = Some(kicked_user.to_string());
1720        self
1721    }
1722
1723    /// Set that the sender of this event banned the user passed as a parameter
1724    /// here.
1725    pub fn banned(mut self, banned_user: &UserId) -> Self {
1726        assert_ne!(
1727            self.sender.as_deref().unwrap(),
1728            banned_user,
1729            "a user can't ban itself" // hopefully
1730        );
1731        self.content.membership = MembershipState::Ban;
1732        self.state_key = Some(banned_user.to_string());
1733        self
1734    }
1735
1736    /// Set the display name of the `m.room.member` event.
1737    pub fn display_name(mut self, display_name: impl Into<String>) -> Self {
1738        self.content.displayname = Some(display_name.into());
1739        self
1740    }
1741
1742    /// Set the avatar URL of the `m.room.member` event.
1743    pub fn avatar_url(mut self, url: &MxcUri) -> Self {
1744        self.content.avatar_url = Some(url.to_owned());
1745        self
1746    }
1747
1748    /// Set the reason field of the `m.room.member` event.
1749    pub fn reason(mut self, reason: impl Into<String>) -> Self {
1750        self.content.reason = Some(reason.into());
1751        self
1752    }
1753
1754    /// Set the previous membership state (in the unsigned section).
1755    pub fn previous(mut self, previous: impl Into<PreviousMembership>) -> Self {
1756        let previous = previous.into();
1757
1758        let mut prev_content = RoomMemberEventContent::new(previous.state);
1759        if let Some(avatar_url) = previous.avatar_url {
1760            prev_content.avatar_url = Some(avatar_url);
1761        }
1762        if let Some(display_name) = previous.display_name {
1763            prev_content.displayname = Some(display_name);
1764        }
1765
1766        self.unsigned.get_or_insert_with(Default::default).prev_content = Some(prev_content);
1767        self
1768    }
1769
1770    /// Set the invite room state (in the unsigned section).
1771    ///
1772    /// This is used to provide context about the room when a user is invited,
1773    /// such as the room name and join rules.
1774    pub fn invite_room_state<I, E>(mut self, events: I) -> Self
1775    where
1776        I: IntoIterator<Item = E>,
1777        E: Into<Raw<AnyStrippedStateEvent>>,
1778    {
1779        self.unsigned.get_or_insert_with(Default::default).invite_room_state =
1780            Some(events.into_iter().map(Into::into).collect());
1781        self
1782    }
1783}
1784
1785impl EventBuilder<RoomAvatarEventContent> {
1786    /// Defines the URL for the room avatar.
1787    pub fn url(mut self, url: &MxcUri) -> Self {
1788        self.content.url = Some(url.to_owned());
1789        self
1790    }
1791
1792    /// Defines the image info for the avatar.
1793    pub fn info(mut self, image: avatar::ImageInfo) -> Self {
1794        self.content.info = Some(Box::new(image));
1795        self
1796    }
1797}
1798
1799impl EventBuilder<RtcNotificationEventContent> {
1800    pub fn mentions(mut self, users: impl IntoIterator<Item = OwnedUserId>) -> Self {
1801        self.content.mentions = Some(Mentions::with_user_ids(users));
1802        self
1803    }
1804
1805    pub fn call_intent(mut self, call_intent: CallIntent) -> Self {
1806        self.content.call_intent = Some(call_intent);
1807        self
1808    }
1809
1810    pub fn relates_to_membership_state_event(mut self, event_id: OwnedEventId) -> Self {
1811        self.content.relates_to = Some(Reference::new(event_id));
1812        self
1813    }
1814
1815    pub fn lifetime(mut self, time_in_seconds: u64) -> Self {
1816        self.content.lifetime = Duration::from_secs(time_in_seconds);
1817        self
1818    }
1819}
1820
1821impl EventBuilder<CallMemberEventContent> {
1822    /// Sets the livekit focus to the call membership event
1823    pub fn set_livekit_focus(mut self, alias: String, service_url: String) -> Self {
1824        if let CallMemberEventContent::SessionContent(session_data) = &mut self.content {
1825            session_data.foci_preferred =
1826                vec![Focus::Livekit(LivekitFocus::new(alias, service_url))];
1827        } else {
1828            panic!("focus() called on a non-SessionContent call member event");
1829        }
1830        self
1831    }
1832
1833    /// Sets the application for the call membership event.
1834    pub fn application(mut self, call_id: String, scope: CallScope) -> Self {
1835        if let CallMemberEventContent::SessionContent(session_data) = &mut self.content {
1836            session_data.application =
1837                Application::Call(CallApplicationContent::new(call_id, scope));
1838        } else {
1839            panic!("application() called on a non-SessionContent call member event");
1840        }
1841        self
1842    }
1843
1844    /// Make the membership event as a membership event.
1845    pub fn leave(mut self) -> Self {
1846        self.content = CallMemberEventContent::new_empty(None);
1847        self
1848    }
1849}
1850pub struct ReadReceiptBuilder<'a> {
1851    factory: &'a EventFactory,
1852    content: ReceiptEventContent,
1853}
1854
1855impl ReadReceiptBuilder<'_> {
1856    /// Add a single read receipt to the event.
1857    pub fn add(
1858        self,
1859        event_id: &EventId,
1860        user_id: &UserId,
1861        tyype: ReceiptType,
1862        thread: ReceiptThread,
1863    ) -> Self {
1864        let ts = self.factory.next_server_ts();
1865        self.add_with_timestamp(event_id, user_id, tyype, thread, Some(ts))
1866    }
1867
1868    /// Add a single read receipt to the event, with an optional timestamp.
1869    pub fn add_with_timestamp(
1870        mut self,
1871        event_id: &EventId,
1872        user_id: &UserId,
1873        tyype: ReceiptType,
1874        thread: ReceiptThread,
1875        ts: Option<MilliSecondsSinceUnixEpoch>,
1876    ) -> Self {
1877        let by_event = self.content.0.entry(event_id.to_owned()).or_default();
1878        let by_type = by_event.entry(tyype).or_default();
1879
1880        let mut receipt = Receipt::default();
1881        if let Some(ts) = ts {
1882            receipt.ts = Some(ts);
1883        }
1884        receipt.thread = thread;
1885
1886        by_type.insert(user_id.to_owned(), receipt);
1887        self
1888    }
1889
1890    /// Finalize the builder into the receipt event content.
1891    pub fn into_content(self) -> ReceiptEventContent {
1892        self.content
1893    }
1894
1895    /// Finalize the builder into an event builder.
1896    pub fn into_event(self) -> EventBuilder<ReceiptEventContent> {
1897        self.factory.event(self.into_content()).format(EventFormat::Ephemeral)
1898    }
1899}
1900
1901pub struct PreviousMembership {
1902    state: MembershipState,
1903    avatar_url: Option<OwnedMxcUri>,
1904    display_name: Option<String>,
1905}
1906
1907impl PreviousMembership {
1908    pub fn new(state: MembershipState) -> Self {
1909        Self { state, avatar_url: None, display_name: None }
1910    }
1911
1912    pub fn avatar_url(mut self, url: &MxcUri) -> Self {
1913        self.avatar_url = Some(url.to_owned());
1914        self
1915    }
1916
1917    pub fn display_name(mut self, name: impl Into<String>) -> Self {
1918        self.display_name = Some(name.into());
1919        self
1920    }
1921}
1922
1923impl From<MembershipState> for PreviousMembership {
1924    fn from(state: MembershipState) -> Self {
1925        Self::new(state)
1926    }
1927}
1928
1929#[derive(Clone, Default, Debug, Serialize, EventContent)]
1930#[ruma_event(type = "rs.matrix-sdk.custom.test", kind = MessageLike)]
1931pub struct CustomMessageLikeEventContent;