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