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::{SessionDescription, invite::CallInviteEventContent},
43        direct::{DirectEventContent, OwnedDirectUserIdentifier},
44        fully_read::FullyReadEventContent,
45        ignored_user_list::IgnoredUserListEventContent,
46        macros::EventContent,
47        marked_unread::MarkedUnreadEventContent,
48        member_hints::MemberHintsEventContent,
49        poll::{
50            unstable_end::UnstablePollEndEventContent,
51            unstable_response::UnstablePollResponseEventContent,
52            unstable_start::{
53                NewUnstablePollStartEventContent, ReplacementUnstablePollStartEventContent,
54                UnstablePollAnswer, UnstablePollStartContentBlock, UnstablePollStartEventContent,
55            },
56        },
57        presence::{PresenceEvent, PresenceEventContent},
58        push_rules::PushRulesEventContent,
59        reaction::ReactionEventContent,
60        receipt::{Receipt, ReceiptEventContent, ReceiptThread, ReceiptType},
61        relation::{Annotation, BundledThread, Reference, Replacement, Reply, Thread},
62        room::{
63            ImageInfo,
64            avatar::{self, RoomAvatarEventContent},
65            canonical_alias::RoomCanonicalAliasEventContent,
66            create::{PreviousRoom, RoomCreateEventContent},
67            encrypted::{
68                EncryptedEventScheme, MegolmV1AesSha2ContentInit, RoomEncryptedEventContent,
69            },
70            encryption::RoomEncryptionEventContent,
71            history_visibility::{HistoryVisibility, RoomHistoryVisibilityEventContent},
72            join_rules::{JoinRule, RoomJoinRulesEventContent},
73            member::{MembershipState, RoomMemberEventContent},
74            message::{
75                FormattedBody, GalleryItemType, GalleryMessageEventContent,
76                ImageMessageEventContent, MessageType, OriginalSyncRoomMessageEvent, Relation,
77                RelationWithoutReplacement, RoomMessageEventContent,
78                RoomMessageEventContentWithoutRelation,
79            },
80            name::RoomNameEventContent,
81            pinned_events::RoomPinnedEventsEventContent,
82            power_levels::RoomPowerLevelsEventContent,
83            redaction::RoomRedactionEventContent,
84            server_acl::RoomServerAclEventContent,
85            tombstone::RoomTombstoneEventContent,
86            topic::RoomTopicEventContent,
87        },
88        rtc::{
89            decline::RtcDeclineEventContent,
90            notification::{CallIntent, NotificationType, RtcNotificationEventContent},
91        },
92        space::{child::SpaceChildEventContent, parent::SpaceParentEventContent},
93        space_order::SpaceOrderEventContent,
94        sticker::StickerEventContent,
95        tag::{TagEventContent, Tags},
96        typing::TypingEventContent,
97    },
98    presence::PresenceState,
99    push::Ruleset,
100    room::RoomType,
101    room_version_rules::AuthorizationRules,
102    serde::Raw,
103    server_name,
104};
105use serde::Serialize;
106use serde_json::json;
107
108use crate::base64_sha256_hash;
109
110pub trait TimestampArg {
111    fn to_milliseconds_since_unix_epoch(self) -> MilliSecondsSinceUnixEpoch;
112}
113
114impl TimestampArg for MilliSecondsSinceUnixEpoch {
115    fn to_milliseconds_since_unix_epoch(self) -> MilliSecondsSinceUnixEpoch {
116        self
117    }
118}
119
120impl TimestampArg for u64 {
121    fn to_milliseconds_since_unix_epoch(self) -> MilliSecondsSinceUnixEpoch {
122        MilliSecondsSinceUnixEpoch(UInt::try_from(self).unwrap())
123    }
124}
125
126/// A thin copy of [`ruma::events::UnsignedRoomRedactionEvent`].
127#[derive(Debug, Serialize)]
128struct RedactedBecause {
129    /// Data specific to the event type.
130    content: RoomRedactionEventContent,
131
132    /// The globally unique event identifier for the user who sent the event.
133    event_id: OwnedEventId,
134
135    /// The fully-qualified ID of the user who sent this event.
136    sender: OwnedUserId,
137
138    /// Timestamp in milliseconds on originating homeserver when this event was
139    /// sent.
140    origin_server_ts: MilliSecondsSinceUnixEpoch,
141}
142
143#[derive(Debug, Serialize)]
144struct Unsigned<C: StaticEventContent> {
145    #[serde(skip_serializing_if = "Option::is_none")]
146    prev_content: Option<C>,
147
148    #[serde(skip_serializing_if = "Option::is_none")]
149    transaction_id: Option<OwnedTransactionId>,
150
151    #[serde(rename = "m.relations", skip_serializing_if = "Option::is_none")]
152    relations: Option<BundledMessageLikeRelations<Raw<AnySyncTimelineEvent>>>,
153
154    #[serde(skip_serializing_if = "Option::is_none")]
155    redacted_because: Option<RedactedBecause>,
156
157    #[serde(skip_serializing_if = "Option::is_none")]
158    age: Option<Int>,
159
160    #[serde(skip_serializing_if = "Option::is_none")]
161    invite_room_state: Option<Vec<Raw<AnyStrippedStateEvent>>>,
162}
163
164// rustc can't derive Default because C isn't marked as `Default` 🤔 oh well.
165impl<C: StaticEventContent> Default for Unsigned<C> {
166    fn default() -> Self {
167        Self {
168            prev_content: None,
169            transaction_id: None,
170            relations: None,
171            redacted_because: None,
172            age: None,
173            invite_room_state: None,
174        }
175    }
176}
177
178#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
179enum EventFormat {
180    /// An event that can be received in the timeline, via `/messages` for
181    /// example.
182    #[default]
183    Timeline,
184    /// An event that can be received in the timeline, via `/sync`.
185    SyncTimeline,
186    /// An event that is received in stripped state.
187    StrippedState,
188    /// An ephemeral event, like a read receipt.
189    Ephemeral,
190    /// A global account data.
191    GlobalAccountData,
192    /// A room account data.
193    RoomAccountData,
194}
195
196impl EventFormat {
197    /// Whether this format has a `sender` field.
198    fn has_sender(self) -> bool {
199        matches!(self, Self::Timeline | Self::SyncTimeline | Self::StrippedState)
200    }
201
202    /// Whether this format has an `event_id` field.
203    fn has_event_id(self) -> bool {
204        matches!(self, Self::Timeline | Self::SyncTimeline)
205    }
206
207    /// Whether this format has an `room_id` field.
208    fn has_room_id(self) -> bool {
209        matches!(self, Self::Timeline)
210    }
211}
212
213#[derive(Debug)]
214pub struct EventBuilder<C: StaticEventContent<IsPrefix = False>> {
215    /// The format of the event.
216    ///
217    /// It will decide which fields are added to the JSON.
218    format: EventFormat,
219    sender: Option<OwnedUserId>,
220    room: Option<OwnedRoomId>,
221    event_id: Option<OwnedEventId>,
222    /// Whether the event should *not* have an event id. False by default.
223    no_event_id: bool,
224    redacts: Option<OwnedEventId>,
225    content: C,
226    server_ts: MilliSecondsSinceUnixEpoch,
227    unsigned: Option<Unsigned<C>>,
228    state_key: Option<String>,
229}
230
231impl<E: StaticEventContent<IsPrefix = False>> EventBuilder<E> {
232    fn format(mut self, format: EventFormat) -> Self {
233        self.format = format;
234        self
235    }
236
237    pub fn room(mut self, room_id: &RoomId) -> Self {
238        self.room = Some(room_id.to_owned());
239        self
240    }
241
242    pub fn sender(mut self, sender: &UserId) -> Self {
243        self.sender = Some(sender.to_owned());
244        self
245    }
246
247    pub fn event_id(mut self, event_id: &EventId) -> Self {
248        self.event_id = Some(event_id.to_owned());
249        self.no_event_id = false;
250        self
251    }
252
253    pub fn no_event_id(mut self) -> Self {
254        self.event_id = None;
255        self.no_event_id = true;
256        self
257    }
258
259    pub fn server_ts(mut self, ts: impl TimestampArg) -> Self {
260        self.server_ts = ts.to_milliseconds_since_unix_epoch();
261        self
262    }
263
264    pub fn unsigned_transaction_id(mut self, transaction_id: &TransactionId) -> Self {
265        self.unsigned.get_or_insert_with(Default::default).transaction_id =
266            Some(transaction_id.to_owned());
267        self
268    }
269
270    /// Add age to unsigned data in this event.
271    pub fn age(mut self, age: impl Into<Int>) -> Self {
272        self.unsigned.get_or_insert_with(Default::default).age = Some(age.into());
273        self
274    }
275
276    /// Set the previous content for this state event (in the unsigned section).
277    pub fn prev_content(mut self, prev: E) -> Self {
278        self.unsigned.get_or_insert_with(Default::default).prev_content = Some(prev);
279        self
280    }
281
282    /// Create a bundled thread summary in the unsigned bundled relations of
283    /// this event.
284    pub fn with_bundled_thread_summary(
285        mut self,
286        latest_event: Raw<AnySyncMessageLikeEvent>,
287        count: usize,
288        current_user_participated: bool,
289    ) -> Self {
290        let relations = self
291            .unsigned
292            .get_or_insert_with(Default::default)
293            .relations
294            .get_or_insert_with(BundledMessageLikeRelations::new);
295        relations.thread = Some(Box::new(BundledThread::new(
296            latest_event,
297            UInt::try_from(count).unwrap(),
298            current_user_participated,
299        )));
300        self
301    }
302
303    /// Create a bundled edit in the unsigned bundled relations of this event.
304    pub fn with_bundled_edit(mut self, replacement: impl Into<Raw<AnySyncTimelineEvent>>) -> Self {
305        let relations = self
306            .unsigned
307            .get_or_insert_with(Default::default)
308            .relations
309            .get_or_insert_with(BundledMessageLikeRelations::new);
310        relations.replace = Some(Box::new(replacement.into()));
311        self
312    }
313
314    /// For state events manually created, define the state key.
315    ///
316    /// For other state events created in the [`EventFactory`], this is
317    /// automatically filled upon creation or update of the events.
318    pub fn state_key(mut self, state_key: impl Into<String>) -> Self {
319        self.state_key = Some(state_key.into());
320        self
321    }
322}
323
324impl<E> EventBuilder<E>
325where
326    E: StaticEventContent<IsPrefix = False> + Serialize,
327{
328    #[inline(always)]
329    fn construct_json(self) -> serde_json::Value {
330        let mut json = json!({
331            "type": E::TYPE,
332            "content": self.content,
333            "origin_server_ts": self.server_ts,
334        });
335
336        let map = json.as_object_mut().unwrap();
337
338        if self.format.has_sender() {
339            // Use the `sender` preferably, or resort to the `redacted_because` sender if
340            // none has been set.
341            let sender = self
342                .sender
343                .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");
344            map.insert("sender".to_owned(), json!(sender));
345        }
346
347        if self.format.has_room_id() {
348            let room_id = self.room.expect("TimelineEvent requires a room id");
349            map.insert("room_id".to_owned(), json!(room_id));
350        }
351
352        if let Some(redacts) = self.redacts {
353            map.insert("redacts".to_owned(), json!(redacts));
354        }
355
356        if let Some(state_key) = self.state_key {
357            map.insert("state_key".to_owned(), json!(state_key));
358        }
359
360        if self.format.has_event_id() && !self.no_event_id {
361            let event_id = self.event_id.unwrap_or_else(|| {
362                // Compute a hash of the event to use it as the event ID, similar to how a
363                // server would. This is a little bit different since a server would redact the
364                // event before hashing, but at least the event ID construction will be
365                // deterministic and have the same format as in recent room versions.
366                let bytes = serde_json::to_vec(&map).unwrap();
367                EventId::new_v2_or_v3(&base64_sha256_hash(&bytes)).unwrap()
368            });
369
370            map.insert("event_id".to_owned(), json!(event_id));
371        }
372
373        if let Some(unsigned) = self.unsigned {
374            map.insert("unsigned".to_owned(), json!(unsigned));
375        }
376
377        json
378    }
379
380    /// Build an event from the [`EventBuilder`] and convert it into a
381    /// serialized and [`Raw`] event.
382    ///
383    /// The generic argument `T` allows you to automatically cast the [`Raw`]
384    /// event into any desired type.
385    pub fn into_raw<T>(self) -> Raw<T> {
386        Raw::new(&self.construct_json()).unwrap().cast_unchecked()
387    }
388
389    pub fn into_raw_timeline(self) -> Raw<AnyTimelineEvent> {
390        self.into_raw()
391    }
392
393    pub fn into_any_sync_message_like_event(self) -> AnySyncMessageLikeEvent {
394        self.format(EventFormat::SyncTimeline)
395            .into_raw()
396            .deserialize()
397            .expect("expected message like event")
398    }
399
400    pub fn into_original_sync_room_message_event(self) -> OriginalSyncRoomMessageEvent {
401        self.format(EventFormat::SyncTimeline)
402            .into_raw()
403            .deserialize()
404            .expect("expected original sync room message event")
405    }
406
407    pub fn into_raw_sync(self) -> Raw<AnySyncTimelineEvent> {
408        self.format(EventFormat::SyncTimeline).into_raw()
409    }
410
411    pub fn into_raw_sync_state(self) -> Raw<AnySyncStateEvent> {
412        self.format(EventFormat::SyncTimeline).into_raw()
413    }
414
415    pub fn into_event(self) -> TimelineEvent {
416        TimelineEvent::from_plaintext(self.into_raw_sync())
417    }
418
419    /// Returns just the event content as a JSON value.
420    ///
421    /// This is useful when you need only the content portion of an event,
422    /// for example when mocking HTTP responses that return event content.
423    pub fn into_content(self) -> serde_json::Value {
424        json!(self.content)
425    }
426}
427
428impl EventBuilder<RoomEncryptedEventContent> {
429    /// Turn this event into a [`TimelineEvent`] representing a decryption
430    /// failure
431    pub fn into_utd_sync_timeline_event(self) -> TimelineEvent {
432        let session_id = as_variant!(&self.content.scheme, EncryptedEventScheme::MegolmV1AesSha2)
433            .map(|content| content.session_id.clone());
434
435        TimelineEvent::from_utd(
436            self.into(),
437            UnableToDecryptInfo {
438                session_id,
439                reason: UnableToDecryptReason::MissingMegolmSession { withheld_code: None },
440            },
441        )
442    }
443}
444
445impl EventBuilder<RoomMessageEventContent> {
446    /// Adds a reply relation to the current event.
447    pub fn reply_to(mut self, event_id: &EventId) -> Self {
448        self.content.relates_to = Some(Relation::Reply(Reply::with_event_id(event_id.to_owned())));
449        self
450    }
451
452    /// Adds a thread relation to the root event, setting the reply fallback to
453    /// the latest in-thread event.
454    pub fn in_thread(mut self, root: &EventId, latest_thread_event: &EventId) -> Self {
455        self.content.relates_to =
456            Some(Relation::Thread(Thread::plain(root.to_owned(), latest_thread_event.to_owned())));
457        self
458    }
459
460    /// Adds a thread relation to the root event, that's a non-fallback reply to
461    /// another thread event.
462    pub fn in_thread_reply(mut self, root: &EventId, replied_to: &EventId) -> Self {
463        self.content.relates_to =
464            Some(Relation::Thread(Thread::reply(root.to_owned(), replied_to.to_owned())));
465        self
466    }
467
468    /// Adds the given mentions to the current event.
469    pub fn mentions(mut self, mentions: Mentions) -> Self {
470        self.content.mentions = Some(mentions);
471        self
472    }
473
474    /// Adds a replacement relation to the current event, with the new content
475    /// passed.
476    pub fn edit(
477        mut self,
478        edited_event_id: &EventId,
479        new_content: RoomMessageEventContentWithoutRelation,
480    ) -> Self {
481        self.content.relates_to =
482            Some(Relation::Replacement(Replacement::new(edited_event_id.to_owned(), new_content)));
483        self
484    }
485
486    /// Adds a caption to a media event.
487    ///
488    /// Will crash if the event isn't a media room message.
489    pub fn caption(
490        mut self,
491        caption: Option<String>,
492        formatted_caption: Option<FormattedBody>,
493    ) -> Self {
494        match &mut self.content.msgtype {
495            MessageType::Image(image) => {
496                let filename = image.filename().to_owned();
497                if let Some(caption) = caption {
498                    image.body = caption;
499                    image.filename = Some(filename);
500                } else {
501                    image.body = filename;
502                    image.filename = None;
503                }
504                image.formatted = formatted_caption;
505            }
506
507            MessageType::Audio(_) | MessageType::Video(_) | MessageType::File(_) => {
508                unimplemented!();
509            }
510
511            _ => panic!("unexpected event type for a caption"),
512        }
513
514        self
515    }
516}
517
518impl EventBuilder<UnstablePollStartEventContent> {
519    /// Adds a reply relation to the current event.
520    pub fn reply_to(mut self, event_id: &EventId) -> Self {
521        if let UnstablePollStartEventContent::New(content) = &mut self.content {
522            content.relates_to =
523                Some(RelationWithoutReplacement::Reply(Reply::with_event_id(event_id.to_owned())));
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_v1(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_v1(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 `org.matrix.msc3672.beacon_info` state event.
1394    ///
1395    /// # Arguments
1396    ///
1397    /// * `description` - An optional human-readable label for the sharing
1398    ///   session.
1399    /// * `duration` - How long the location share is active.
1400    /// * `live` - Whether the sharing session is active. Pass `true` to start
1401    ///   and `false` to stop.
1402    /// * `ts` - The start timestamp; if `None` the current time is used.
1403    ///
1404    /// # Example
1405    ///
1406    /// ```rust
1407    /// use std::time::Duration;
1408    ///
1409    /// use matrix_sdk_test::event_factory::EventFactory;
1410    /// use ruma::{room_id, user_id};
1411    ///
1412    /// let factory = EventFactory::new().room(room_id!("!test:localhost"));
1413    ///
1414    /// let event = factory
1415    ///     .beacon_info(None, Duration::from_secs(60), true, None)
1416    ///     .sender(user_id!("@alice:localhost"))
1417    ///     .state_key(user_id!("@alice:localhost"));
1418    /// ```
1419    pub fn beacon_info(
1420        &self,
1421        description: Option<String>,
1422        duration: Duration,
1423        live: bool,
1424        ts: Option<MilliSecondsSinceUnixEpoch>,
1425    ) -> EventBuilder<BeaconInfoEventContent> {
1426        self.event(BeaconInfoEventContent::new(description, duration, live, ts))
1427    }
1428
1429    /// Create a new `m.sticker` event.
1430    pub fn sticker(
1431        &self,
1432        body: impl Into<String>,
1433        info: ImageInfo,
1434        url: OwnedMxcUri,
1435    ) -> EventBuilder<StickerEventContent> {
1436        self.event(StickerEventContent::new(body.into(), info, url))
1437    }
1438
1439    /// Create a new `m.call.invite` event.
1440    pub fn call_invite(
1441        &self,
1442        call_id: OwnedVoipId,
1443        lifetime: UInt,
1444        offer: SessionDescription,
1445        version: VoipVersionId,
1446    ) -> EventBuilder<CallInviteEventContent> {
1447        self.event(CallInviteEventContent::new(call_id, lifetime, offer, version))
1448    }
1449
1450    /// Create a new `m.rtc.notification` event.
1451    pub fn rtc_notification(
1452        &self,
1453        notification_type: NotificationType,
1454    ) -> EventBuilder<RtcNotificationEventContent> {
1455        self.event(RtcNotificationEventContent::new(
1456            MilliSecondsSinceUnixEpoch::now(),
1457            Duration::new(30, 0),
1458            notification_type,
1459        ))
1460    }
1461
1462    // Creates a new `org.matrix.msc4310.rtc.decline` event.
1463    pub fn call_decline(
1464        &self,
1465        notification_event_id: &EventId,
1466    ) -> EventBuilder<RtcDeclineEventContent> {
1467        self.event(RtcDeclineEventContent::new(notification_event_id))
1468    }
1469
1470    /// Create a new `m.direct` global account data event.
1471    pub fn direct(&self) -> EventBuilder<DirectEventContent> {
1472        self.global_account_data(DirectEventContent::default())
1473    }
1474
1475    /// Create a new `m.ignored_user_list` global account data event.
1476    pub fn ignored_user_list(
1477        &self,
1478        users: impl IntoIterator<Item = OwnedUserId>,
1479    ) -> EventBuilder<IgnoredUserListEventContent> {
1480        self.global_account_data(IgnoredUserListEventContent::users(users))
1481    }
1482
1483    /// Create a new `m.push_rules` global account data event.
1484    pub fn push_rules(&self, rules: Ruleset) -> EventBuilder<PushRulesEventContent> {
1485        self.global_account_data(PushRulesEventContent::new(rules))
1486    }
1487
1488    /// Create a new `m.space.child` state event.
1489    pub fn space_child(
1490        &self,
1491        parent: OwnedRoomId,
1492        child: OwnedRoomId,
1493    ) -> EventBuilder<SpaceChildEventContent> {
1494        let mut event = self.event(SpaceChildEventContent::new(vec![]));
1495        event.room = Some(parent);
1496        event.state_key = Some(child.to_string());
1497        event
1498    }
1499
1500    /// Create a new `m.space.parent` state event.
1501    pub fn space_parent(
1502        &self,
1503        parent: OwnedRoomId,
1504        child: OwnedRoomId,
1505    ) -> EventBuilder<SpaceParentEventContent> {
1506        let mut event = self.event(SpaceParentEventContent::new(vec![]));
1507        event.state_key = Some(parent.to_string());
1508        event.room = Some(child);
1509        event
1510    }
1511
1512    /// Create a new `rs.matrix-sdk.custom.test` custom event
1513    pub fn custom_message_like_event(&self) -> EventBuilder<CustomMessageLikeEventContent> {
1514        self.event(CustomMessageLikeEventContent)
1515    }
1516
1517    /// Set the next server timestamp.
1518    ///
1519    /// Timestamps will continue to increase by 1 (millisecond) from that value.
1520    pub fn set_next_ts(&self, value: u64) {
1521        self.next_ts.store(value, SeqCst);
1522    }
1523
1524    /// Create a new global account data event of the given `C` content type.
1525    pub fn global_account_data<C>(&self, content: C) -> EventBuilder<C>
1526    where
1527        C: GlobalAccountDataEventContent + StaticEventContent<IsPrefix = False>,
1528    {
1529        self.event(content).format(EventFormat::GlobalAccountData)
1530    }
1531
1532    /// Create a new room account data event of the given `C` content type.
1533    pub fn room_account_data<C>(&self, content: C) -> EventBuilder<C>
1534    where
1535        C: RoomAccountDataEventContent + StaticEventContent<IsPrefix = False>,
1536    {
1537        self.event(content).format(EventFormat::RoomAccountData)
1538    }
1539
1540    /// Create a new `m.fully_read` room account data event.
1541    pub fn fully_read(&self, event_id: &EventId) -> EventBuilder<FullyReadEventContent> {
1542        self.room_account_data(FullyReadEventContent::new(event_id.to_owned()))
1543    }
1544
1545    /// Create a new `m.marked_unread` room account data event.
1546    pub fn marked_unread(&self, unread: bool) -> EventBuilder<MarkedUnreadEventContent> {
1547        self.room_account_data(MarkedUnreadEventContent::new(unread))
1548    }
1549
1550    /// Create a new `m.tag` room account data event with the given tags.
1551    pub fn tag(&self, tags: Tags) -> EventBuilder<TagEventContent> {
1552        self.room_account_data(tags.into())
1553    }
1554
1555    /// Create a new `m.space_order` room account data event with the given
1556    /// order.
1557    pub fn space_order(&self, order: &str) -> EventBuilder<SpaceOrderEventContent> {
1558        let order = ruma::SpaceChildOrder::parse(order).expect("order should be valid");
1559        self.room_account_data(SpaceOrderEventContent::new(order))
1560    }
1561
1562    /// Create a new `m.presence` event.
1563    ///
1564    /// This is a special event type that has its own structure different from
1565    /// regular Matrix events.
1566    pub fn presence(&self, state: PresenceState) -> PresenceBuilder {
1567        PresenceBuilder { sender: self.sender.clone(), content: PresenceEventContent::new(state) }
1568    }
1569}
1570
1571/// Builder for presence events.
1572#[derive(Debug)]
1573pub struct PresenceBuilder {
1574    sender: Option<OwnedUserId>,
1575    content: PresenceEventContent,
1576}
1577
1578impl PresenceBuilder {
1579    /// Set the sender of the presence event.
1580    pub fn sender(mut self, sender: &UserId) -> Self {
1581        self.sender = Some(sender.to_owned());
1582        self
1583    }
1584
1585    /// Set the avatar URL.
1586    pub fn avatar_url(mut self, url: &MxcUri) -> Self {
1587        self.content.avatar_url = Some(url.to_owned());
1588        self
1589    }
1590
1591    /// Set whether the user is currently active.
1592    pub fn currently_active(mut self, active: bool) -> Self {
1593        self.content.currently_active = Some(active);
1594        self
1595    }
1596
1597    /// Set the last active time in milliseconds.
1598    pub fn last_active_ago(mut self, ms: u64) -> Self {
1599        self.content.last_active_ago = Some(UInt::try_from(ms).unwrap());
1600        self
1601    }
1602
1603    /// Set the status message.
1604    pub fn status_msg(mut self, msg: impl Into<String>) -> Self {
1605        self.content.status_msg = Some(msg.into());
1606        self
1607    }
1608
1609    /// Set the display name.
1610    pub fn display_name(mut self, name: impl Into<String>) -> Self {
1611        self.content.displayname = Some(name.into());
1612        self
1613    }
1614}
1615
1616impl From<PresenceBuilder> for Raw<PresenceEvent> {
1617    fn from(builder: PresenceBuilder) -> Self {
1618        let sender = builder.sender.expect("sender must be set for presence events");
1619        let event = PresenceEvent { content: builder.content, sender };
1620        Raw::new(&event).unwrap().cast_unchecked()
1621    }
1622}
1623
1624impl EventBuilder<DirectEventContent> {
1625    /// Add a user/room pair to the `m.direct` event.
1626    pub fn add_user(mut self, user_id: OwnedDirectUserIdentifier, room_id: &RoomId) -> Self {
1627        self.content.0.entry(user_id).or_default().push(room_id.to_owned());
1628        self
1629    }
1630}
1631
1632impl EventBuilder<RoomMemberEventContent> {
1633    /// Set the `membership` of the `m.room.member` event to the given
1634    /// [`MembershipState`].
1635    ///
1636    /// The default is [`MembershipState::Join`].
1637    pub fn membership(mut self, state: MembershipState) -> Self {
1638        self.content.membership = state;
1639        self
1640    }
1641
1642    /// Set that the sender of this event invited the user passed as a parameter
1643    /// here.
1644    pub fn invited(mut self, invited_user: &UserId) -> Self {
1645        assert_ne!(
1646            self.sender.as_deref().unwrap(),
1647            invited_user,
1648            "invited user and sender can't be the same person"
1649        );
1650        self.content.membership = MembershipState::Invite;
1651        self.state_key = Some(invited_user.to_string());
1652        self
1653    }
1654
1655    /// Set that the sender of this event left the room (self-leave).
1656    ///
1657    /// This sets the membership to Leave and uses the sender as the state_key.
1658    pub fn leave(mut self) -> Self {
1659        self.content.membership = MembershipState::Leave;
1660        self.state_key = Some(self.sender.as_ref().expect("sender must be set").to_string());
1661        self
1662    }
1663
1664    /// Set that the sender of this event kicked the user passed as a parameter
1665    /// here.
1666    pub fn kicked(mut self, kicked_user: &UserId) -> Self {
1667        assert_ne!(
1668            self.sender.as_deref().unwrap(),
1669            kicked_user,
1670            "kicked user and sender can't be the same person, otherwise it's just a Leave"
1671        );
1672        self.content.membership = MembershipState::Leave;
1673        self.state_key = Some(kicked_user.to_string());
1674        self
1675    }
1676
1677    /// Set that the sender of this event banned the user passed as a parameter
1678    /// here.
1679    pub fn banned(mut self, banned_user: &UserId) -> Self {
1680        assert_ne!(
1681            self.sender.as_deref().unwrap(),
1682            banned_user,
1683            "a user can't ban itself" // hopefully
1684        );
1685        self.content.membership = MembershipState::Ban;
1686        self.state_key = Some(banned_user.to_string());
1687        self
1688    }
1689
1690    /// Set the display name of the `m.room.member` event.
1691    pub fn display_name(mut self, display_name: impl Into<String>) -> Self {
1692        self.content.displayname = Some(display_name.into());
1693        self
1694    }
1695
1696    /// Set the avatar URL of the `m.room.member` event.
1697    pub fn avatar_url(mut self, url: &MxcUri) -> Self {
1698        self.content.avatar_url = Some(url.to_owned());
1699        self
1700    }
1701
1702    /// Set the reason field of the `m.room.member` event.
1703    pub fn reason(mut self, reason: impl Into<String>) -> Self {
1704        self.content.reason = Some(reason.into());
1705        self
1706    }
1707
1708    /// Set the previous membership state (in the unsigned section).
1709    pub fn previous(mut self, previous: impl Into<PreviousMembership>) -> Self {
1710        let previous = previous.into();
1711
1712        let mut prev_content = RoomMemberEventContent::new(previous.state);
1713        if let Some(avatar_url) = previous.avatar_url {
1714            prev_content.avatar_url = Some(avatar_url);
1715        }
1716        if let Some(display_name) = previous.display_name {
1717            prev_content.displayname = Some(display_name);
1718        }
1719
1720        self.unsigned.get_or_insert_with(Default::default).prev_content = Some(prev_content);
1721        self
1722    }
1723
1724    /// Set the invite room state (in the unsigned section).
1725    ///
1726    /// This is used to provide context about the room when a user is invited,
1727    /// such as the room name and join rules.
1728    pub fn invite_room_state<I, E>(mut self, events: I) -> Self
1729    where
1730        I: IntoIterator<Item = E>,
1731        E: Into<Raw<AnyStrippedStateEvent>>,
1732    {
1733        self.unsigned.get_or_insert_with(Default::default).invite_room_state =
1734            Some(events.into_iter().map(Into::into).collect());
1735        self
1736    }
1737}
1738
1739impl EventBuilder<RoomAvatarEventContent> {
1740    /// Defines the URL for the room avatar.
1741    pub fn url(mut self, url: &MxcUri) -> Self {
1742        self.content.url = Some(url.to_owned());
1743        self
1744    }
1745
1746    /// Defines the image info for the avatar.
1747    pub fn info(mut self, image: avatar::ImageInfo) -> Self {
1748        self.content.info = Some(Box::new(image));
1749        self
1750    }
1751}
1752
1753impl EventBuilder<RtcNotificationEventContent> {
1754    pub fn mentions(mut self, users: impl IntoIterator<Item = OwnedUserId>) -> Self {
1755        self.content.mentions = Some(Mentions::with_user_ids(users));
1756        self
1757    }
1758
1759    pub fn call_intent(mut self, call_intent: CallIntent) -> Self {
1760        self.content.call_intent = Some(call_intent);
1761        self
1762    }
1763
1764    pub fn relates_to_membership_state_event(mut self, event_id: OwnedEventId) -> Self {
1765        self.content.relates_to = Some(Reference::new(event_id));
1766        self
1767    }
1768
1769    pub fn lifetime(mut self, time_in_seconds: u64) -> Self {
1770        self.content.lifetime = Duration::from_secs(time_in_seconds);
1771        self
1772    }
1773}
1774
1775pub struct ReadReceiptBuilder<'a> {
1776    factory: &'a EventFactory,
1777    content: ReceiptEventContent,
1778}
1779
1780impl ReadReceiptBuilder<'_> {
1781    /// Add a single read receipt to the event.
1782    pub fn add(
1783        self,
1784        event_id: &EventId,
1785        user_id: &UserId,
1786        tyype: ReceiptType,
1787        thread: ReceiptThread,
1788    ) -> Self {
1789        let ts = self.factory.next_server_ts();
1790        self.add_with_timestamp(event_id, user_id, tyype, thread, Some(ts))
1791    }
1792
1793    /// Add a single read receipt to the event, with an optional timestamp.
1794    pub fn add_with_timestamp(
1795        mut self,
1796        event_id: &EventId,
1797        user_id: &UserId,
1798        tyype: ReceiptType,
1799        thread: ReceiptThread,
1800        ts: Option<MilliSecondsSinceUnixEpoch>,
1801    ) -> Self {
1802        let by_event = self.content.0.entry(event_id.to_owned()).or_default();
1803        let by_type = by_event.entry(tyype).or_default();
1804
1805        let mut receipt = Receipt::default();
1806        if let Some(ts) = ts {
1807            receipt.ts = Some(ts);
1808        }
1809        receipt.thread = thread;
1810
1811        by_type.insert(user_id.to_owned(), receipt);
1812        self
1813    }
1814
1815    /// Finalize the builder into the receipt event content.
1816    pub fn into_content(self) -> ReceiptEventContent {
1817        self.content
1818    }
1819
1820    /// Finalize the builder into an event builder.
1821    pub fn into_event(self) -> EventBuilder<ReceiptEventContent> {
1822        self.factory.event(self.into_content()).format(EventFormat::Ephemeral)
1823    }
1824}
1825
1826pub struct PreviousMembership {
1827    state: MembershipState,
1828    avatar_url: Option<OwnedMxcUri>,
1829    display_name: Option<String>,
1830}
1831
1832impl PreviousMembership {
1833    pub fn new(state: MembershipState) -> Self {
1834        Self { state, avatar_url: None, display_name: None }
1835    }
1836
1837    pub fn avatar_url(mut self, url: &MxcUri) -> Self {
1838        self.avatar_url = Some(url.to_owned());
1839        self
1840    }
1841
1842    pub fn display_name(mut self, name: impl Into<String>) -> Self {
1843        self.display_name = Some(name.into());
1844        self
1845    }
1846}
1847
1848impl From<MembershipState> for PreviousMembership {
1849    fn from(state: MembershipState) -> Self {
1850        Self::new(state)
1851    }
1852}
1853
1854#[derive(Clone, Default, Debug, Serialize, EventContent)]
1855#[ruma_event(type = "rs.matrix-sdk.custom.test", kind = MessageLike)]
1856pub struct CustomMessageLikeEventContent;