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