atagen

macltok

public
Star0
Watch0
21b9227729a49cccc12924f2ca63c0f1890c6b89
3
ssh://brewery@pls.lobotomise.me:33/atagen/macltok.git
src/transport.rsRust1836 lines56.0 KB
1
use std::{
2
    collections::HashSet,
3
    fmt,
4
    future::Future,
5
    pin::Pin,
6
    sync::{Arc, Mutex, Weak},
7
    task::{Context, Poll},
8
};
9
10
use matrix_sdk::{Client, RoomMemberships};
11
use matrix_sdk_ui::{
12
    notification_client::{
13
        NotificationClient, NotificationProcessSetup, NotificationStatus, RawNotificationEvent,
14
    },
15
    spaces::{SpaceRoomList, SpaceService, room_list::SpaceRoomListPaginationState},
16
    sync_service::SyncService,
17
};
18
use ruma::{
19
    EventId, OwnedDeviceId, OwnedEventId, OwnedMxcUri, OwnedRoomId, OwnedRoomOrAliasId,
20
    OwnedServerName, OwnedTransactionId, OwnedUserId, RoomId, UserId,
21
    events::{
22
        AnyGlobalAccountDataEventContent, AnyRoomAccountDataEvent, AnyRoomAccountDataEventContent,
23
        GlobalAccountDataEventType, RoomAccountDataEventType,
24
    },
25
    serde::Raw,
26
};
27
use tokio::sync::{OnceCell, mpsc, oneshot, watch};
28
29
use crate::{
30
    AccountCommand, AttachmentUpload, Command, CommandOutput, ConnectionState, CreateRoomOptions,
31
    EncryptionCommand, EncryptionState, Error, ErrorKind, Event, EventContextCommand,
32
    EventContextPage, EventContextSession, EventRef, HistoryRepairCommand, ImagePackSource,
33
    ImagePacks, InteractiveAuth, InteractiveAuthOutcome, LifecycleCommand, LinkPreview, Media,
34
    MediaCommand, MediaId, MediaLocation, MediaSize, MemberFilter, MemberSummary, MentionKey,
35
    MentionsBoundary, MentionsCommand, MentionsPage, MessageContent, MessageEditContent,
36
    NotificationCommand, OwnDevice, PackCommand, PackImageInfo, PackUsage, Patch,
37
    PowerLevelChanges, Profile, PushNotification, PushNotificationEvent, PushNotificationStatus,
38
    RoomCommand, RoomListFilter, RoomNotificationMode, RoomNotificationSettings, RoomPowerLevels,
39
    RoomSummary, SavedMentionContext, SpaceChild, ThreadListCommand, TimelineCommand, TimelineId,
40
    VerificationAction, VerificationTarget,
41
    encryption::encryption_state,
42
    engine::{MediaService, StoreLock},
43
    model::{PushNotificationRoom, PushNotificationSender},
44
    normalise::{image_packs, member_summary, room_summaries, space_child, url_previews_allowed},
45
};
46
47
#[derive(Clone)]
48
pub struct ClientHandle {
49
    requests: mpsc::Sender<Request>,
50
    client: Client,
51
    connection: watch::Receiver<ConnectionState>,
52
    notification_client: Arc<OnceCell<NotificationClient>>,
53
    sync_service: Arc<SyncService>,
54
    media: Arc<MediaService>,
55
    spaces: Arc<OnceCell<SpaceService>>,
56
    members_checked: Arc<Mutex<HashSet<OwnedRoomId>>>,
57
    publication: Arc<PublicationRegistry>,
58
    _store_lock: Option<Arc<StoreLock>>,
59
}
60
61
impl fmt::Debug for ClientHandle {
62
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
63
        formatter
64
            .debug_struct("ClientHandle")
65
            .finish_non_exhaustive()
66
    }
67
}
68
69
/// a command's answer. drop it if you don't care, the command still runs
70
#[derive(Debug)]
71
#[must_use = "dropping it is fine but then you'll never know"]
72
pub struct Pending(oneshot::Receiver<Result<CommandOutput, Error>>);
73
74
impl Future for Pending {
75
    type Output = Result<CommandOutput, Error>;
76
77
    fn poll(mut self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Self::Output> {
78
        Pin::new(&mut self.0)
79
            .poll(context)
80
            .map(|result| result.unwrap_or_else(|_| Err(Error::interrupted())))
81
    }
82
}
83
84
impl ClientHandle {
85
    pub(crate) fn register_publication_mirror(&self) -> PublicationRegistration {
86
        self.publication.register_mirror()
87
    }
88
89
    /// current credentials, it holds secrets. the engine saves it for you unless the
90
    /// session was built `without_persistence`
91
    #[cfg(any(feature = "sqlite", feature = "indexeddb"))]
92
    pub fn session_record(&self) -> Result<crate::SessionRecord, crate::AccountError> {
93
        crate::account::session_record(&self.client)
94
    }
95
96
    /// the bearer token. it rotates on every refresh so ask again after a 401 rather than
97
    /// keeping it, and keep it in memory and away from all but the homeserver's media endpoints
98
    pub fn access_token(&self) -> Option<String> {
99
        self.client.access_token()
100
    }
101
102
    /// queue a command, waiting for room in the queue if there isn't any
103
    pub async fn send(&self, command: Command) -> Result<Pending, Error> {
104
        let (request, pending) = Request::new(command);
105
        self.requests
106
            .send(request)
107
            .await
108
            .map_err(|_| Error::interrupted())?;
109
        Ok(pending)
110
    }
111
112
    /// queue a command or fail with `QueueFull` right now
113
    pub fn try_send(&self, command: Command) -> Result<Pending, Error> {
114
        let (request, pending) = Request::new(command);
115
        self.requests
116
            .try_send(request)
117
            .map_err(|error| match error {
118
                mpsc::error::TrySendError::Full(_) => {
119
                    Error::new(ErrorKind::QueueFull, "the command queue is full")
120
                }
121
                mpsc::error::TrySendError::Closed(_) => Error::interrupted(),
122
            })?;
123
        Ok(pending)
124
    }
125
126
    /// queue a command and wait for its answer. timeline sends answer once the
127
    /// sdk send queue has them, delivery shows up later as the item's send state
128
    pub async fn execute(&self, command: Command) -> Result<CommandOutput, Error> {
129
        self.send(command).await?.await
130
    }
131
132
    async fn run(&self, command: Command) -> Result<(), Error> {
133
        self.execute(command).await.map(drop)
134
    }
135
136
    async fn run_for_room(&self, command: RoomCommand) -> Result<OwnedRoomId, Error> {
137
        match self.execute(Command::Room(command)).await? {
138
            CommandOutput::Room(room_id) => Ok(room_id),
139
            output => Err(Error::new(
140
                ErrorKind::Other,
141
                format!("expected a room but got {output:?}"),
142
            )),
143
        }
144
    }
145
146
    pub async fn shutdown(&self) -> Result<(), Error> {
147
        self.run(Command::Lifecycle(LifecycleCommand::Shutdown))
148
            .await
149
    }
150
151
    pub async fn logout(&self) -> Result<(), Error> {
152
        self.run(Command::Account(AccountCommand::Logout)).await
153
    }
154
155
    pub async fn delete_devices(
156
        &self,
157
        device_ids: Vec<OwnedDeviceId>,
158
        auth: Option<InteractiveAuth>,
159
    ) -> Result<InteractiveAuthOutcome, Error> {
160
        match self
161
            .run(Command::Account(AccountCommand::DeleteDevices {
162
                device_ids,
163
                auth,
164
            }))
165
            .await
166
        {
167
            Ok(()) => Ok(InteractiveAuthOutcome::Completed),
168
            Err(Error {
169
                challenge: Some(challenge),
170
                ..
171
            }) => Ok(InteractiveAuthOutcome::Challenge(*challenge)),
172
            Err(error) => Err(error),
173
        }
174
    }
175
176
    pub async fn rename_device(
177
        &self,
178
        device_id: OwnedDeviceId,
179
        display_name: impl Into<String>,
180
    ) -> Result<(), Error> {
181
        self.run(Command::Account(AccountCommand::RenameDevice {
182
            device_id,
183
            display_name: display_name.into(),
184
        }))
185
        .await
186
    }
187
188
    pub async fn set_global_account_data(
189
        &self,
190
        event_type: GlobalAccountDataEventType,
191
        content: Raw<AnyGlobalAccountDataEventContent>,
192
    ) -> Result<(), Error> {
193
        self.run(Command::Account(AccountCommand::SetAccountData {
194
            event_type,
195
            content,
196
        }))
197
        .await
198
    }
199
200
    pub async fn set_room_account_data(
201
        &self,
202
        room_id: OwnedRoomId,
203
        event_type: RoomAccountDataEventType,
204
        content: Raw<AnyRoomAccountDataEventContent>,
205
    ) -> Result<(), Error> {
206
        self.run(Command::Room(RoomCommand::SetAccountData {
207
            room_id,
208
            event_type,
209
            content,
210
        }))
211
        .await
212
    }
213
214
    pub async fn set_room_notification_mode(
215
        &self,
216
        room_id: OwnedRoomId,
217
        mode: RoomNotificationMode,
218
    ) -> Result<(), Error> {
219
        self.run(Command::Notification(NotificationCommand::SetRoomMode {
220
            room_id,
221
            mode,
222
        }))
223
        .await
224
    }
225
226
    pub async fn reset_room_notification_mode(&self, room_id: OwnedRoomId) -> Result<(), Error> {
227
        self.run(Command::Notification(NotificationCommand::ResetRoomMode(
228
            room_id,
229
        )))
230
        .await
231
    }
232
233
    pub async fn set_default_notification_mode(
234
        &self,
235
        encrypted: bool,
236
        one_to_one: bool,
237
        mode: RoomNotificationMode,
238
    ) -> Result<(), Error> {
239
        self.run(Command::Notification(NotificationCommand::SetDefaultMode {
240
            encrypted,
241
            one_to_one,
242
            mode,
243
        }))
244
        .await
245
    }
246
247
    pub async fn add_notification_keyword(&self, keyword: impl Into<String>) -> Result<(), Error> {
248
        self.run(Command::Notification(NotificationCommand::AddKeyword(
249
            keyword.into(),
250
        )))
251
        .await
252
    }
253
254
    pub async fn remove_notification_keyword(
255
        &self,
256
        keyword: impl Into<String>,
257
    ) -> Result<(), Error> {
258
        self.run(Command::Notification(NotificationCommand::RemoveKeyword(
259
            keyword.into(),
260
        )))
261
        .await
262
    }
263
264
    pub async fn open_room(&self, room_id: OwnedRoomId) -> Result<(), Error> {
265
        self.run(Command::Room(RoomCommand::Open(room_id))).await
266
    }
267
268
    pub async fn close_room(&self, room_id: OwnedRoomId) -> Result<(), Error> {
269
        self.run(Command::Room(RoomCommand::Close(room_id))).await
270
    }
271
272
    pub async fn open_thread(
273
        &self,
274
        room_id: OwnedRoomId,
275
        root_event_id: OwnedEventId,
276
    ) -> Result<(), Error> {
277
        self.run(Command::Timeline(TimelineCommand::OpenThread {
278
            room_id,
279
            root_event_id,
280
        }))
281
        .await
282
    }
283
284
    pub async fn close_thread(
285
        &self,
286
        room_id: OwnedRoomId,
287
        root_event_id: OwnedEventId,
288
    ) -> Result<(), Error> {
289
        self.run(Command::Timeline(TimelineCommand::CloseThread {
290
            room_id,
291
            root_event_id,
292
        }))
293
        .await
294
    }
295
296
    /// the room's pins as `TimelineId::Pinned`, kept current as they change. the room has to be open
297
    pub async fn open_pinned(&self, room_id: OwnedRoomId) -> Result<(), Error> {
298
        self.run(Command::Timeline(TimelineCommand::OpenPinned(room_id)))
299
            .await
300
    }
301
302
    pub async fn close_pinned(&self, room_id: OwnedRoomId) -> Result<(), Error> {
303
        self.run(Command::Timeline(TimelineCommand::ClosePinned(room_id)))
304
            .await
305
    }
306
307
    /// the room's threads, newest activity first, kept current as they gain replies.
308
    /// the room needn't be open. it starts empty, `paginate_thread_list` fills it
309
    pub async fn open_thread_list(&self, room_id: OwnedRoomId) -> Result<(), Error> {
310
        self.run(Command::ThreadList(ThreadListCommand::Open(room_id)))
311
            .await
312
    }
313
314
    pub async fn paginate_thread_list(
315
        &self,
316
        room_id: OwnedRoomId,
317
        limit: u16,
318
    ) -> Result<(), Error> {
319
        self.run(Command::ThreadList(ThreadListCommand::Paginate {
320
            room_id,
321
            limit,
322
        }))
323
        .await
324
    }
325
326
    pub async fn close_thread_list(&self, room_id: OwnedRoomId) -> Result<(), Error> {
327
        self.run(Command::ThreadList(ThreadListCommand::Close(room_id)))
328
            .await
329
    }
330
331
    /// reads the server's list so a pin made elsewhere just before isn't dropped
332
    pub async fn set_pinned(
333
        &self,
334
        room_id: OwnedRoomId,
335
        event_id: OwnedEventId,
336
        pinned: bool,
337
    ) -> Result<(), Error> {
338
        self.run(Command::Room(RoomCommand::SetPinned {
339
            room_id,
340
            event_id,
341
            pinned,
342
        }))
343
        .await
344
    }
345
346
    pub async fn resync(&self, timeline_id: TimelineId) -> Result<(), Error> {
347
        self.run(Command::Timeline(TimelineCommand::Resync { timeline_id }))
348
            .await
349
    }
350
351
    pub async fn paginate(&self, timeline_id: TimelineId, limit: u16) -> Result<(), Error> {
352
        self.run(Command::Timeline(TimelineCommand::Paginate {
353
            timeline_id,
354
            limit,
355
        }))
356
        .await
357
    }
358
359
    pub async fn cancel_history_repair(&self, room_id: OwnedRoomId) -> Result<(), Error> {
360
        self.run(Command::HistoryRepair(HistoryRepairCommand::Cancel {
361
            room_id,
362
        }))
363
        .await
364
    }
365
366
    pub async fn fill_history_gap(
367
        &self,
368
        room_id: OwnedRoomId,
369
        gap_id: ruma::OwnedTransactionId,
370
    ) -> Result<(), Error> {
371
        self.run(Command::HistoryRepair(HistoryRepairCommand::FillGap {
372
            room_id,
373
            gap_id,
374
        }))
375
        .await
376
    }
377
378
    pub async fn retry_recent_history(&self, room_id: OwnedRoomId) -> Result<(), Error> {
379
        self.run(Command::HistoryRepair(HistoryRepairCommand::RetryRecent {
380
            room_id,
381
        }))
382
        .await
383
    }
384
385
    /// close the session when the view goes, or its media stay registered
386
    pub async fn event_context(
387
        &self,
388
        room_id: &RoomId,
389
        event_id: &EventId,
390
        before_limit: u16,
391
        after_limit: u16,
392
    ) -> Result<EventContextSession, Error> {
393
        match self
394
            .execute(Command::EventContext(EventContextCommand::Fetch {
395
                room_id: room_id.to_owned(),
396
                event_id: event_id.to_owned(),
397
                before_limit,
398
                after_limit,
399
            }))
400
            .await?
401
        {
402
            CommandOutput::EventContext(session) => Ok(session),
403
            output => Err(unexpected("event context", &output)),
404
        }
405
    }
406
407
    pub async fn event_context_before(
408
        &self,
409
        context_id: &str,
410
        token: &str,
411
        limit: u16,
412
    ) -> Result<EventContextPage, Error> {
413
        self.event_context_page(EventContextCommand::Before {
414
            context_id: context_id.to_owned(),
415
            limit,
416
            token: token.to_owned(),
417
        })
418
        .await
419
    }
420
421
    pub async fn event_context_after(
422
        &self,
423
        context_id: &str,
424
        token: &str,
425
        limit: u16,
426
    ) -> Result<EventContextPage, Error> {
427
        self.event_context_page(EventContextCommand::After {
428
            context_id: context_id.to_owned(),
429
            limit,
430
            token: token.to_owned(),
431
        })
432
        .await
433
    }
434
435
    pub async fn close_event_context(&self, context_id: &str) -> Result<(), Error> {
436
        self.run(Command::EventContext(EventContextCommand::Close {
437
            context_id: context_id.to_owned(),
438
        }))
439
        .await
440
    }
441
442
    async fn event_context_page(
443
        &self,
444
        command: EventContextCommand,
445
    ) -> Result<EventContextPage, Error> {
446
        match self.execute(Command::EventContext(command)).await? {
447
            CommandOutput::EventContextPage(page) => Ok(page),
448
            output => Err(unexpected("event context page", &output)),
449
        }
450
    }
451
452
    pub async fn mentions_page(
453
        &self,
454
        cursor: Option<String>,
455
        limit: u32,
456
    ) -> Result<MentionsPage, Error> {
457
        match self
458
            .execute(Command::Mentions(MentionsCommand::Page {
459
                cursor,
460
                limit: limit.min(50) as u16,
461
            }))
462
            .await?
463
        {
464
            CommandOutput::MentionsPage(page) => Ok(page),
465
            output => Err(unexpected("mentions page", &output)),
466
        }
467
    }
468
469
    pub async fn mention_context(
470
        &self,
471
        room_id: &RoomId,
472
        event_id: &EventId,
473
    ) -> Result<SavedMentionContext, Error> {
474
        self.mention_context_command(MentionsCommand::Context(MentionKey {
475
            room_id: room_id.to_owned(),
476
            event_id: event_id.to_owned(),
477
        }))
478
        .await
479
    }
480
481
    pub async fn retry_mention_context(
482
        &self,
483
        room_id: &RoomId,
484
        event_id: &EventId,
485
    ) -> Result<SavedMentionContext, Error> {
486
        self.mention_context_command(MentionsCommand::RetryContext(MentionKey {
487
            room_id: room_id.to_owned(),
488
            event_id: event_id.to_owned(),
489
        }))
490
        .await
491
    }
492
493
    async fn mention_context_command(
494
        &self,
495
        command: MentionsCommand,
496
    ) -> Result<SavedMentionContext, Error> {
497
        match self.execute(Command::Mentions(command)).await? {
498
            CommandOutput::MentionContext(context) => Ok(context),
499
            output => Err(unexpected("mention context", &output)),
500
        }
501
    }
502
503
    pub async fn set_mentions_read(&self, keys: Vec<MentionKey>, read: bool) -> Result<(), Error> {
504
        self.run(Command::Mentions(MentionsCommand::SetRead { keys, read }))
505
            .await
506
    }
507
508
    pub async fn mark_mentions_read_through(
509
        &self,
510
        boundary: MentionsBoundary,
511
    ) -> Result<(), Error> {
512
        self.run(Command::Mentions(MentionsCommand::ReadThrough(boundary)))
513
            .await
514
    }
515
516
    pub async fn recover_mentions(&self) -> Result<(), Error> {
517
        self.run(Command::Mentions(MentionsCommand::Recover)).await
518
    }
519
520
    pub async fn send_message(
521
        &self,
522
        timeline_id: TimelineId,
523
        content: MessageContent,
524
    ) -> Result<(), Error> {
525
        self.run(Command::Timeline(TimelineCommand::Send {
526
            timeline_id,
527
            content,
528
        }))
529
        .await
530
    }
531
532
    pub async fn edit_message(
533
        &self,
534
        timeline_id: TimelineId,
535
        target: impl Into<EventRef>,
536
        content: MessageEditContent,
537
    ) -> Result<(), Error> {
538
        self.run(Command::Timeline(TimelineCommand::Edit {
539
            timeline_id,
540
            target: target.into(),
541
            content,
542
        }))
543
        .await
544
    }
545
546
    pub async fn redact(
547
        &self,
548
        timeline_id: TimelineId,
549
        target: impl Into<EventRef>,
550
        reason: Option<String>,
551
    ) -> Result<(), Error> {
552
        self.run(Command::Timeline(TimelineCommand::Redact {
553
            timeline_id,
554
            target: target.into(),
555
            reason,
556
        }))
557
        .await
558
    }
559
560
    pub async fn toggle_reaction(
561
        &self,
562
        timeline_id: TimelineId,
563
        target: impl Into<EventRef>,
564
        key: impl Into<String>,
565
        shortcode: Option<String>,
566
    ) -> Result<(), Error> {
567
        self.run(Command::Timeline(TimelineCommand::ToggleReaction {
568
            timeline_id,
569
            target: target.into(),
570
            key: key.into(),
571
            shortcode,
572
        }))
573
        .await
574
    }
575
576
    pub async fn mark_read(
577
        &self,
578
        timeline_id: TimelineId,
579
        event_id: OwnedEventId,
580
    ) -> Result<(), Error> {
581
        self.run(Command::Timeline(TimelineCommand::MarkRead {
582
            timeline_id,
583
            event_id,
584
        }))
585
        .await
586
    }
587
588
    pub async fn set_typing(&self, room_id: OwnedRoomId, typing: bool) -> Result<(), Error> {
589
        self.run(Command::Room(RoomCommand::SetTyping { room_id, typing }))
590
            .await
591
    }
592
593
    pub async fn prepare_to_send(&self, room_id: OwnedRoomId) -> Result<(), Error> {
594
        self.run(Command::Room(RoomCommand::PrepareToSend(room_id)))
595
            .await
596
    }
597
598
    pub async fn join_room(
599
        &self,
600
        room: OwnedRoomOrAliasId,
601
        via: Vec<OwnedServerName>,
602
    ) -> Result<OwnedRoomId, Error> {
603
        self.run_for_room(RoomCommand::Join { room, via }).await
604
    }
605
606
    pub async fn leave_room(&self, room_id: OwnedRoomId) -> Result<(), Error> {
607
        self.run(Command::Room(RoomCommand::Leave(room_id))).await
608
    }
609
610
    pub async fn forget_room(&self, room_id: OwnedRoomId) -> Result<(), Error> {
611
        self.run(Command::Room(RoomCommand::Forget(room_id))).await
612
    }
613
614
    pub async fn accept_invite(&self, room_id: OwnedRoomId) -> Result<(), Error> {
615
        self.run(Command::Room(RoomCommand::AcceptInvite(room_id)))
616
            .await
617
    }
618
619
    pub async fn reject_invite(&self, room_id: OwnedRoomId) -> Result<(), Error> {
620
        self.run(Command::Room(RoomCommand::RejectInvite(room_id)))
621
            .await
622
    }
623
624
    pub async fn invite_user(
625
        &self,
626
        room_id: OwnedRoomId,
627
        user_id: OwnedUserId,
628
    ) -> Result<(), Error> {
629
        self.run(Command::Room(RoomCommand::InviteUser { room_id, user_id }))
630
            .await
631
    }
632
633
    pub async fn kick_user(
634
        &self,
635
        room_id: OwnedRoomId,
636
        user_id: OwnedUserId,
637
        reason: Option<String>,
638
    ) -> Result<(), Error> {
639
        self.run(Command::Room(RoomCommand::KickUser {
640
            room_id,
641
            user_id,
642
            reason,
643
        }))
644
        .await
645
    }
646
647
    pub async fn ban_user(
648
        &self,
649
        room_id: OwnedRoomId,
650
        user_id: OwnedUserId,
651
        reason: Option<String>,
652
    ) -> Result<(), Error> {
653
        self.run(Command::Room(RoomCommand::BanUser {
654
            room_id,
655
            user_id,
656
            reason,
657
        }))
658
        .await
659
    }
660
661
    pub async fn unban_user(
662
        &self,
663
        room_id: OwnedRoomId,
664
        user_id: OwnedUserId,
665
        reason: Option<String>,
666
    ) -> Result<(), Error> {
667
        self.run(Command::Room(RoomCommand::UnbanUser {
668
            room_id,
669
            user_id,
670
            reason,
671
        }))
672
        .await
673
    }
674
675
    pub async fn set_power_levels(
676
        &self,
677
        room_id: OwnedRoomId,
678
        changes: PowerLevelChanges,
679
    ) -> Result<(), Error> {
680
        self.run(Command::Room(RoomCommand::SetPowerLevels {
681
            room_id,
682
            changes,
683
        }))
684
        .await
685
    }
686
687
    pub async fn set_user_power_level(
688
        &self,
689
        room_id: OwnedRoomId,
690
        user_id: OwnedUserId,
691
        level: i64,
692
    ) -> Result<(), Error> {
693
        self.run(Command::Room(RoomCommand::SetUserPowerLevel {
694
            room_id,
695
            user_id,
696
            level,
697
        }))
698
        .await
699
    }
700
701
    pub async fn create_room(&self, options: CreateRoomOptions) -> Result<OwnedRoomId, Error> {
702
        self.run_for_room(RoomCommand::Create(options)).await
703
    }
704
705
    /// hands back the existing dm with them if there is one
706
    pub async fn create_direct(&self, user_id: OwnedUserId) -> Result<OwnedRoomId, Error> {
707
        self.run_for_room(RoomCommand::CreateDirect(user_id)).await
708
    }
709
710
    pub async fn cancel_send(
711
        &self,
712
        room_id: OwnedRoomId,
713
        transaction_id: OwnedTransactionId,
714
    ) -> Result<(), Error> {
715
        self.run(Command::Room(RoomCommand::CancelSend {
716
            room_id,
717
            transaction_id,
718
        }))
719
        .await
720
    }
721
722
    pub async fn retry_send(
723
        &self,
724
        room_id: OwnedRoomId,
725
        transaction_id: OwnedTransactionId,
726
    ) -> Result<(), Error> {
727
        self.run(Command::Room(RoomCommand::RetrySend {
728
            room_id,
729
            transaction_id,
730
        }))
731
        .await
732
    }
733
734
    pub async fn upload(&self, upload: AttachmentUpload) -> Result<(), Error> {
735
        self.run(Command::Media(MediaCommand::Upload(upload))).await
736
    }
737
738
    pub async fn recover(&self, recovery_key: impl Into<String>) -> Result<(), Error> {
739
        self.run(Command::Encryption(EncryptionCommand::Recover(
740
            recovery_key.into(),
741
        )))
742
        .await
743
    }
744
745
    pub async fn create_key_backup(&self) -> Result<(), Error> {
746
        self.run(Command::Encryption(EncryptionCommand::CreateKeyBackup))
747
            .await
748
    }
749
750
    pub async fn enable_key_backup(&self) -> Result<(), Error> {
751
        self.run(Command::Encryption(EncryptionCommand::EnableKeyBackup))
752
            .await
753
    }
754
755
    pub async fn disable_key_backup(&self, delete_from_server: bool) -> Result<(), Error> {
756
        self.run(Command::Encryption(EncryptionCommand::DisableKeyBackup {
757
            delete_from_server,
758
        }))
759
        .await
760
    }
761
762
    pub async fn recover_key_backup(&self, recovery_key: impl Into<String>) -> Result<(), Error> {
763
        self.run(Command::Encryption(EncryptionCommand::RecoverKeyBackup(
764
            recovery_key.into(),
765
        )))
766
        .await
767
    }
768
769
    pub async fn start_verification(&self, target: VerificationTarget) -> Result<(), Error> {
770
        self.run(Command::Encryption(EncryptionCommand::StartVerification(
771
            target,
772
        )))
773
        .await
774
    }
775
776
    pub async fn verify(&self, action: VerificationAction) -> Result<(), Error> {
777
        self.run(Command::Encryption(EncryptionCommand::Verify(action)))
778
            .await
779
    }
780
781
    /// bytes for an avatar, or for media a retained timeline showed you. concurrent asks share
782
    /// one download
783
    pub async fn media(&self, media_id: &MediaId, size: MediaSize) -> Result<Media, Error> {
784
        self.media.timeline_media(media_id, size).await
785
    }
786
787
    /// where `media` would fetch from, for a host streaming big files itself. the byte cap
788
    /// doesn't apply since nothing is buffered
789
    pub async fn media_source(
790
        &self,
791
        media_id: &MediaId,
792
        size: MediaSize,
793
    ) -> Result<MediaLocation, Error> {
794
        self.media.media_location(media_id, size).await
795
    }
796
797
    /// the space graph builds on first use and then keeps itself current
798
    async fn spaces(&self) -> &SpaceService {
799
        self.spaces
800
            .get_or_init(|| SpaceService::new(self.client.clone()))
801
            .await
802
    }
803
804
    pub async fn rooms(&self) -> Vec<RoomSummary> {
805
        room_summaries(&self.client, self.spaces().await).await
806
    }
807
808
    pub async fn image_packs(&self) -> Result<ImagePacks, Error> {
809
        image_packs(&self.client).await
810
    }
811
812
    /// the homeserver's preview of a page, `None` when it has none. the homeserver sees every
813
    /// url asked about, so a host decides whether encrypted rooms ask at all
814
    pub async fn url_preview(&self, url: &str) -> Result<Option<LinkPreview>, Error> {
815
        self.media.url_preview(url).await
816
    }
817
818
    /// whether the room and the user's settings let link previews show here
819
    pub async fn url_previews_allowed(&self, room_id: &RoomId) -> Result<bool, Error> {
820
        url_previews_allowed(&self.room(room_id)?).await
821
    }
822
823
    /// browse a space the way the server sees it, rooms you haven't joined included
824
    pub async fn space_hierarchy(&self, space_id: OwnedRoomId) -> SpaceHierarchy {
825
        SpaceHierarchy(SpaceRoomList::new(self.client.clone(), space_id).await)
826
    }
827
828
    pub async fn own_profile(&self) -> Result<Profile, Error> {
829
        let user_id = self
830
            .client
831
            .user_id()
832
            .ok_or_else(|| Error::new(ErrorKind::Unauthenticated, "the client is not logged in"))?
833
            .to_owned();
834
        let response = self
835
            .client
836
            .account()
837
            .fetch_user_profile()
838
            .await
839
            .map_err(|source| Error::from_sdk("fetching the profile failed", source))?;
840
        profile(user_id, &response)
841
    }
842
843
    pub async fn user_profile(&self, user_id: &UserId) -> Result<Profile, Error> {
844
        let response = self
845
            .client
846
            .account()
847
            .fetch_user_profile_of(user_id)
848
            .await
849
            .map_err(|source| Error::from_sdk("fetching the profile failed", source))?;
850
        profile(user_id.to_owned(), &response)
851
    }
852
853
    pub async fn set_display_name(&self, name: Option<String>) -> Result<(), Error> {
854
        self.run(Command::Account(AccountCommand::SetDisplayName(name)))
855
            .await
856
    }
857
858
    pub async fn set_avatar(&self, avatar: Option<OwnedMxcUri>) -> Result<(), Error> {
859
        self.run(Command::Account(AccountCommand::SetAvatar(avatar)))
860
            .await
861
    }
862
863
    pub async fn upload_avatar(
864
        &self,
865
        mimetype: impl Into<String>,
866
        bytes: Vec<u8>,
867
    ) -> Result<(), Error> {
868
        self.run(Command::Account(AccountCommand::UploadAvatar {
869
            mimetype: mimetype.into(),
870
            bytes,
871
        }))
872
        .await
873
    }
874
875
    /// unencrypted, for room avatars and anything else that only needs the uri
876
    pub async fn upload_media(
877
        &self,
878
        mimetype: impl Into<String>,
879
        bytes: Vec<u8>,
880
    ) -> Result<OwnedMxcUri, Error> {
881
        let command = Command::Account(AccountCommand::UploadMedia {
882
            mimetype: mimetype.into(),
883
            bytes,
884
        });
885
        match self.execute(command).await? {
886
            CommandOutput::Mxc(uri) => Ok(uri),
887
            output => Err(Error::new(
888
                ErrorKind::Other,
889
                format!("expected an mxc but got {output:?}"),
890
            )),
891
        }
892
    }
893
894
    pub async fn ignore_user(&self, user_id: OwnedUserId) -> Result<(), Error> {
895
        self.run(Command::Account(AccountCommand::IgnoreUser(user_id)))
896
            .await
897
    }
898
899
    pub async fn unignore_user(&self, user_id: OwnedUserId) -> Result<(), Error> {
900
        self.run(Command::Account(AccountCommand::UnignoreUser(user_id)))
901
            .await
902
    }
903
904
    /// writes msc3230 orders so these spaces sort in exactly this sequence, touching as few as it can
905
    pub async fn reorder_spaces(&self, order: Vec<OwnedRoomId>) -> Result<(), Error> {
906
        self.run(Command::Account(AccountCommand::ReorderSpaces(order)))
907
            .await
908
    }
909
910
    /// adding m.favourite drops m.lowpriority, the way Element does
911
    pub async fn set_favourite(&self, room_id: OwnedRoomId, favourite: bool) -> Result<(), Error> {
912
        self.run(Command::Room(RoomCommand::SetFavourite {
913
            room_id,
914
            favourite,
915
        }))
916
        .await
917
    }
918
919
    /// None goes back to your global name
920
    pub async fn set_room_display_name(
921
        &self,
922
        room_id: OwnedRoomId,
923
        name: Option<String>,
924
    ) -> Result<(), Error> {
925
        self.run(Command::Room(RoomCommand::SetOwnDisplayName {
926
            room_id,
927
            name,
928
        }))
929
        .await
930
    }
931
932
    /// None goes back to your global avatar
933
    pub async fn set_room_avatar(
934
        &self,
935
        room_id: OwnedRoomId,
936
        avatar: Option<OwnedMxcUri>,
937
    ) -> Result<(), Error> {
938
        self.run(Command::Room(RoomCommand::SetOwnAvatar { room_id, avatar }))
939
            .await
940
    }
941
942
    /// muting drops them below what sending a message takes, unmuting puts a muted user back
943
    /// at users_default and leaves anyone else alone. `ErrorKind::Forbidden` if you can't
944
    pub async fn mute_user(
945
        &self,
946
        room_id: OwnedRoomId,
947
        user_id: OwnedUserId,
948
        muted: bool,
949
    ) -> Result<(), Error> {
950
        self.run(Command::Room(RoomCommand::MuteUser {
951
            room_id,
952
            user_id,
953
            muted,
954
        }))
955
        .await
956
    }
957
958
    /// body defaults to the shortcode
959
    pub async fn send_sticker(
960
        &self,
961
        timeline_id: TimelineId,
962
        pack_id: String,
963
        shortcode: String,
964
    ) -> Result<(), Error> {
965
        self.run(Command::Pack(PackCommand::SendSticker {
966
            timeline_id,
967
            pack_id,
968
            shortcode,
969
        }))
970
        .await
971
    }
972
973
    /// fails if the state key already holds a pack
974
    pub async fn create_room_pack(
975
        &self,
976
        room_id: OwnedRoomId,
977
        state_key: String,
978
        display_name: String,
979
    ) -> Result<(), Error> {
980
        self.run(Command::Pack(PackCommand::CreateRoomPack {
981
            room_id,
982
            state_key,
983
            display_name,
984
        }))
985
        .await
986
    }
987
988
    pub async fn delete_pack(&self, pack: ImagePackSource) -> Result<(), Error> {
989
        self.run(Command::Pack(PackCommand::Delete(pack))).await
990
    }
991
992
    pub async fn set_pack_meta(
993
        &self,
994
        pack: ImagePackSource,
995
        display_name: Patch<String>,
996
        avatar: Patch<OwnedMxcUri>,
997
        usage: Patch<Vec<PackUsage>>,
998
    ) -> Result<(), Error> {
999
        self.run(Command::Pack(PackCommand::SetMeta {
1000
            pack,
1001
            display_name,
1002
            avatar,
1003
            usage,
1004
        }))
1005
        .await
1006
    }
1007
1008
    /// adds or replaces by shortcode. a replaced image keeps its info unless the url changed
1009
    pub async fn put_pack_image(
1010
        &self,
1011
        pack: ImagePackSource,
1012
        shortcode: String,
1013
        mxc: OwnedMxcUri,
1014
        body: Option<String>,
1015
        usage: Option<Vec<PackUsage>>,
1016
        info: Option<PackImageInfo>,
1017
    ) -> Result<(), Error> {
1018
        self.run(Command::Pack(PackCommand::PutImage {
1019
            pack,
1020
            shortcode,
1021
            mxc,
1022
            body,
1023
            usage,
1024
            info,
1025
        }))
1026
        .await
1027
    }
1028
1029
    pub async fn rename_pack_image(
1030
        &self,
1031
        pack: ImagePackSource,
1032
        from: String,
1033
        to: String,
1034
    ) -> Result<(), Error> {
1035
        self.run(Command::Pack(PackCommand::RenameImage { pack, from, to }))
1036
            .await
1037
    }
1038
1039
    pub async fn remove_pack_image(
1040
        &self,
1041
        pack: ImagePackSource,
1042
        shortcode: String,
1043
    ) -> Result<(), Error> {
1044
        self.run(Command::Pack(PackCommand::RemoveImage { pack, shortcode }))
1045
            .await
1046
    }
1047
1048
    pub async fn set_pack_enabled_everywhere(
1049
        &self,
1050
        room_id: OwnedRoomId,
1051
        state_key: String,
1052
        enabled: bool,
1053
    ) -> Result<(), Error> {
1054
        self.run(Command::Pack(PackCommand::SetEnabledEverywhere {
1055
            room_id,
1056
            state_key,
1057
            enabled,
1058
        }))
1059
        .await
1060
    }
1061
1062
    /// the same uri when it already lives on your homeserver
1063
    pub async fn rehost_media(&self, mxc: OwnedMxcUri) -> Result<OwnedMxcUri, Error> {
1064
        match self
1065
            .execute(Command::Pack(PackCommand::Rehost(mxc)))
1066
            .await?
1067
        {
1068
            CommandOutput::Mxc(uri) => Ok(uri),
1069
            output => Err(unexpected("an mxc", &output)),
1070
        }
1071
    }
1072
1073
    /// hands back the shortcode it ended up under. `usage` overrides what the image had
1074
    pub async fn clone_pack_image(
1075
        &self,
1076
        from_pack: String,
1077
        shortcode: String,
1078
        to: ImagePackSource,
1079
        usage: Option<Vec<PackUsage>>,
1080
    ) -> Result<String, Error> {
1081
        let (mut shortcodes, _) = self
1082
            .run_clone(PackCommand::CloneImage {
1083
                from_pack,
1084
                shortcode,
1085
                to,
1086
                usage,
1087
            })
1088
            .await?;
1089
        shortcodes
1090
            .pop()
1091
            .ok_or_else(|| unexpected("a shortcode", &CommandOutput::Done))
1092
    }
1093
1094
    /// hands back the shortcode it ended up under
1095
    pub async fn clone_sticker(
1096
        &self,
1097
        room_id: OwnedRoomId,
1098
        event_id: OwnedEventId,
1099
        to: ImagePackSource,
1100
        shortcode: String,
1101
        usage: Option<Vec<PackUsage>>,
1102
    ) -> Result<String, Error> {
1103
        let (mut shortcodes, _) = self
1104
            .run_clone(PackCommand::CloneSticker {
1105
                room_id,
1106
                event_id,
1107
                to,
1108
                shortcode,
1109
                usage,
1110
            })
1111
            .await?;
1112
        shortcodes
1113
            .pop()
1114
            .ok_or_else(|| unexpected("a shortcode", &CommandOutput::Done))
1115
    }
1116
1117
    /// hands back the shortcodes that couldn't be fetched, everything else is written
1118
    pub async fn clone_pack(
1119
        &self,
1120
        from_pack: String,
1121
        to: ImagePackSource,
1122
        display_name: Option<String>,
1123
        usage: Option<Vec<PackUsage>>,
1124
    ) -> Result<Vec<String>, Error> {
1125
        let (_, skipped) = self
1126
            .run_clone(PackCommand::ClonePack {
1127
                from_pack,
1128
                to,
1129
                display_name,
1130
                usage,
1131
            })
1132
            .await?;
1133
        Ok(skipped)
1134
    }
1135
1136
    async fn run_clone(&self, command: PackCommand) -> Result<(Vec<String>, Vec<String>), Error> {
1137
        match self.execute(Command::Pack(command)).await? {
1138
            CommandOutput::Cloned {
1139
                shortcodes,
1140
                skipped,
1141
            } => Ok((shortcodes, skipped)),
1142
            output => Err(unexpected("a clone", &output)),
1143
        }
1144
    }
1145
1146
    pub async fn rooms_filtered(&self, filter: &RoomListFilter) -> Vec<RoomSummary> {
1147
        let matcher = filter.matcher();
1148
        self.rooms()
1149
            .await
1150
            .into_iter()
1151
            .filter(|room| matcher.matches(room))
1152
            .collect()
1153
    }
1154
1155
    pub async fn devices(&self) -> Result<Vec<OwnDevice>, Error> {
1156
        let current_device_id = self.client.device_id();
1157
        Ok(self
1158
            .client
1159
            .devices()
1160
            .await
1161
            .map_err(|source| Error::from_http("listing devices failed", source))?
1162
            .devices
1163
            .into_iter()
1164
            .map(|device| OwnDevice {
1165
                current: current_device_id == Some(device.device_id.as_ref()),
1166
                device_id: device.device_id,
1167
                display_name: device.display_name,
1168
                last_seen_ip: device.last_seen_ip,
1169
                last_seen_timestamp_ms: device.last_seen_ts.map(|timestamp| timestamp.get().into()),
1170
            })
1171
            .collect())
1172
    }
1173
1174
    fn room(&self, room_id: &RoomId) -> Result<matrix_sdk::Room, Error> {
1175
        self.client
1176
            .get_room(room_id)
1177
            .ok_or_else(Error::room_unknown)
1178
    }
1179
1180
    pub async fn room_notification_settings(
1181
        &self,
1182
        room_id: &RoomId,
1183
    ) -> Result<Option<RoomNotificationSettings>, Error> {
1184
        let room = self.room(room_id)?;
1185
        let custom = room.user_defined_notification_mode().await;
1186
        let mode = match custom {
1187
            Some(mode) => mode,
1188
            None => match room.notification_mode().await {
1189
                Some(mode) => mode,
1190
                None => return Ok(None),
1191
            },
1192
        };
1193
        Ok(Some(RoomNotificationSettings {
1194
            mode: crate::normalise::notification_mode(mode),
1195
            custom: custom.is_some(),
1196
        }))
1197
    }
1198
1199
    pub async fn notification_keywords(&self) -> Vec<String> {
1200
        self.client
1201
            .notification_settings()
1202
            .await
1203
            .enabled_keywords()
1204
            .await
1205
            .into_iter()
1206
            .collect()
1207
    }
1208
1209
    pub async fn resolve_push_notification(
1210
        &self,
1211
        room_id: &RoomId,
1212
        event_id: &EventId,
1213
    ) -> Result<PushNotificationStatus, Error> {
1214
        let notification_failed = |source| {
1215
            Error::with_source(
1216
                ErrorKind::Other,
1217
                "resolving the push notification failed",
1218
                source,
1219
            )
1220
        };
1221
        let client = self
1222
            .notification_client
1223
            .get_or_try_init(|| async {
1224
                NotificationClient::new(
1225
                    self.client.clone(),
1226
                    NotificationProcessSetup::SingleProcess {
1227
                        sync_service: self.sync_service.clone(),
1228
                    },
1229
                )
1230
                .await
1231
            })
1232
            .await
1233
            .map_err(notification_failed)?;
1234
        let status = client
1235
            .get_notification(room_id, event_id)
1236
            .await
1237
            .map_err(notification_failed)?;
1238
        Ok(normalise_push_notification(room_id, event_id, status))
1239
    }
1240
1241
    pub async fn members(
1242
        &self,
1243
        room_id: &RoomId,
1244
        filter: MemberFilter,
1245
    ) -> Result<Vec<MemberSummary>, Error> {
1246
        let room = self.room(room_id)?;
1247
        // stores have been seen with members_synced persisted over a partial
1248
        // roster, and the sdk never asks /members again once it's set
1249
        if self
1250
            .members_checked
1251
            .lock()
1252
            .unwrap()
1253
            .insert(room_id.to_owned())
1254
        {
1255
            room.mark_members_missing();
1256
        }
1257
        list_members(&room, filter).await
1258
    }
1259
1260
    /// drop the cached roster for a room and fetch /members again
1261
    pub async fn refresh_members(
1262
        &self,
1263
        room_id: &RoomId,
1264
        filter: MemberFilter,
1265
    ) -> Result<Vec<MemberSummary>, Error> {
1266
        let room = self.room(room_id)?;
1267
        room.mark_members_missing();
1268
        self.members_checked
1269
            .lock()
1270
            .unwrap()
1271
            .insert(room_id.to_owned());
1272
        list_members(&room, filter).await
1273
    }
1274
1275
    pub async fn room_power_levels(&self, room_id: &RoomId) -> Result<RoomPowerLevels, Error> {
1276
        let levels = self
1277
            .room(room_id)?
1278
            .power_levels()
1279
            .await
1280
            .map_err(|source| Error::from_sdk("reading power levels failed", source.into()))?;
1281
        Ok(RoomPowerLevels {
1282
            ban: levels.ban.into(),
1283
            invite: levels.invite.into(),
1284
            kick: levels.kick.into(),
1285
            redact: levels.redact.into(),
1286
            events_default: levels.events_default.into(),
1287
            state_default: levels.state_default.into(),
1288
            users_default: levels.users_default.into(),
1289
            room_notification: levels.notifications.room.into(),
1290
            users: levels
1291
                .users
1292
                .into_iter()
1293
                .map(|(user_id, level)| (user_id, level.into()))
1294
                .collect(),
1295
            events: levels
1296
                .events
1297
                .into_iter()
1298
                .map(|(event_type, level)| (event_type.to_string(), level.into()))
1299
                .collect(),
1300
            creators: levels
1301
                .rules
1302
                .privileged_creators
1303
                .into_iter()
1304
                .flatten()
1305
                .collect(),
1306
        })
1307
    }
1308
1309
    pub async fn pending_send_ids(
1310
        &self,
1311
        room_id: &RoomId,
1312
    ) -> Result<Vec<OwnedTransactionId>, Error> {
1313
        let (echoes, _) = self
1314
            .room(room_id)?
1315
            .send_queue()
1316
            .subscribe()
1317
            .await
1318
            .map_err(|source| {
1319
                Error::with_source(ErrorKind::Store, "reading the send queue failed", source)
1320
            })?;
1321
        Ok(echoes
1322
            .into_iter()
1323
            .filter(|echo| {
1324
                matches!(
1325
                    echo.content,
1326
                    matrix_sdk::send_queue::LocalEchoContent::Event { .. }
1327
                )
1328
            })
1329
            .map(|echo| echo.transaction_id)
1330
            .collect())
1331
    }
1332
1333
    pub async fn global_account_data(
1334
        &self,
1335
        event_type: GlobalAccountDataEventType,
1336
    ) -> Result<Option<Raw<AnyGlobalAccountDataEventContent>>, Error> {
1337
        self.client
1338
            .account()
1339
            .account_data_raw(event_type)
1340
            .await
1341
            .map_err(|source| Error::from_sdk("reading account data failed", source))
1342
    }
1343
1344
    pub async fn room_account_data(
1345
        &self,
1346
        room_id: &RoomId,
1347
        event_type: RoomAccountDataEventType,
1348
    ) -> Result<Option<Raw<AnyRoomAccountDataEvent>>, Error> {
1349
        self.room(room_id)?
1350
            .account_data(event_type)
1351
            .await
1352
            .map_err(|source| Error::from_sdk("reading room account data failed", source))
1353
    }
1354
1355
    #[must_use]
1356
    pub fn connection_state(&self) -> watch::Receiver<ConnectionState> {
1357
        self.connection.clone()
1358
    }
1359
1360
    #[must_use]
1361
    pub fn encryption_state(&self) -> Option<EncryptionState> {
1362
        let device_id = self.client.device_id()?.to_owned();
1363
        let encryption = self.client.encryption();
1364
        Some(encryption_state(
1365
            device_id,
1366
            encryption.recovery().state(),
1367
            encryption.verification_state().get(),
1368
            encryption.backups().state(),
1369
        ))
1370
    }
1371
}
1372
1373
/// one space's hierarchy, a page at a time. hold on to it while you browse, it remembers where it got to
1374
pub struct SpaceHierarchy(SpaceRoomList);
1375
1376
impl fmt::Debug for SpaceHierarchy {
1377
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1378
        formatter
1379
            .debug_struct("SpaceHierarchy")
1380
            .finish_non_exhaustive()
1381
    }
1382
}
1383
1384
impl SpaceHierarchy {
1385
    /// fetches the next page. a no-op once the end is reached or while a page is in flight
1386
    pub async fn paginate(&self) -> Result<(), Error> {
1387
        self.0.paginate().await.map_err(|source| {
1388
            Error::with_source(
1389
                ErrorKind::Server,
1390
                "fetching the space hierarchy failed",
1391
                source,
1392
            )
1393
        })
1394
    }
1395
1396
    /// everything fetched so far, the space itself excluded
1397
    pub async fn rooms(&self) -> Vec<SpaceChild> {
1398
        self.0.rooms().await.into_iter().map(space_child).collect()
1399
    }
1400
1401
    #[must_use]
1402
    pub fn end_reached(&self) -> bool {
1403
        matches!(
1404
            self.0.pagination_state(),
1405
            SpaceRoomListPaginationState::Idle { end_reached: true }
1406
        )
1407
    }
1408
}
1409
1410
fn profile(
1411
    user_id: OwnedUserId,
1412
    response: &ruma::api::client::profile::get_profile::v3::Response,
1413
) -> Result<Profile, Error> {
1414
    use ruma::api::client::profile::{AvatarUrl, DisplayName};
1415
    let malformed = |source| {
1416
        Error::with_source(
1417
            ErrorKind::Server,
1418
            "the server sent a malformed profile",
1419
            source,
1420
        )
1421
    };
1422
    Ok(Profile {
1423
        user_id,
1424
        display_name: response.get_static::<DisplayName>().map_err(malformed)?,
1425
        avatar: response
1426
            .get_static::<AvatarUrl>()
1427
            .map_err(malformed)?
1428
            .as_deref()
1429
            .map(MediaId::plain),
1430
    })
1431
}
1432
1433
#[derive(Debug)]
1434
pub(crate) struct Request {
1435
    pub(crate) command: Command,
1436
    pub(crate) reply: Responder,
1437
}
1438
1439
impl Request {
1440
    fn new(command: Command) -> (Self, Pending) {
1441
        let (reply, pending) = oneshot::channel();
1442
        (
1443
            Self {
1444
                command,
1445
                reply: Responder(reply),
1446
            },
1447
            Pending(pending),
1448
        )
1449
    }
1450
}
1451
1452
/// whoever holds this owes the caller an answer. dropping it answers `Interrupted`
1453
#[derive(Debug)]
1454
pub(crate) struct Responder(oneshot::Sender<Result<CommandOutput, Error>>);
1455
1456
impl Responder {
1457
    pub(crate) fn channel() -> (Self, Pending) {
1458
        let (reply, pending) = oneshot::channel();
1459
        (Self(reply), Pending(pending))
1460
    }
1461
1462
    pub(crate) fn send(self, result: Result<CommandOutput, Error>) {
1463
        if let Err(error) = &result {
1464
            tracing::debug!(%error, kind = ?error.kind, "command failed");
1465
        }
1466
        let _ = self.0.send(result);
1467
    }
1468
1469
    pub(crate) fn done(self, result: Result<(), Error>) {
1470
        self.send(result.map(|()| CommandOutput::Done));
1471
    }
1472
}
1473
1474
#[derive(Debug)]
1475
pub struct EventStream {
1476
    events: mpsc::Receiver<Event>,
1477
}
1478
1479
impl EventStream {
1480
    pub async fn recv(&mut self) -> Option<Event> {
1481
        self.events.recv().await
1482
    }
1483
1484
    pub fn try_recv(&mut self) -> Result<Event, mpsc::error::TryRecvError> {
1485
        self.events.try_recv()
1486
    }
1487
}
1488
1489
impl futures_util::Stream for EventStream {
1490
    type Item = Event;
1491
1492
    fn poll_next(mut self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Option<Self::Item>> {
1493
        self.events.poll_recv(context)
1494
    }
1495
}
1496
1497
#[derive(Debug)]
1498
pub(crate) struct DriverPort {
1499
    requests: mpsc::Receiver<Request>,
1500
    events: mpsc::Sender<Event>,
1501
    connection: watch::Sender<ConnectionState>,
1502
    publication: Arc<PublicationRegistry>,
1503
}
1504
1505
#[doc(hidden)]
1506
#[derive(Clone, Debug)]
1507
pub struct PublicationAck(Arc<PublicationAckInner>);
1508
1509
#[derive(Debug)]
1510
struct PublicationAckInner {
1511
    sender: Mutex<Option<oneshot::Sender<bool>>>,
1512
}
1513
1514
impl PartialEq for PublicationAck {
1515
    fn eq(&self, other: &Self) -> bool {
1516
        Arc::ptr_eq(&self.0, &other.0)
1517
    }
1518
}
1519
1520
impl Eq for PublicationAck {}
1521
1522
impl PublicationAck {
1523
    pub(crate) fn complete(&self, published: bool) {
1524
        let sender = self
1525
            .0
1526
            .sender
1527
            .lock()
1528
            .unwrap_or_else(|error| error.into_inner())
1529
            .take();
1530
        if let Some(sender) = sender {
1531
            let _ = sender.send(published);
1532
        }
1533
    }
1534
}
1535
1536
#[derive(Debug, Default)]
1537
struct PublicationRegistry {
1538
    state: Mutex<PublicationRegistryState>,
1539
}
1540
1541
#[derive(Debug, Default)]
1542
struct PublicationRegistryState {
1543
    mirrors: usize,
1544
    pending: Vec<Weak<PublicationAckInner>>,
1545
}
1546
1547
impl PublicationRegistry {
1548
    fn register_mirror(self: &Arc<Self>) -> PublicationRegistration {
1549
        self.state
1550
            .lock()
1551
            .unwrap_or_else(|error| error.into_inner())
1552
            .mirrors += 1;
1553
        PublicationRegistration {
1554
            registry: self.clone(),
1555
        }
1556
    }
1557
1558
    fn acknowledgement(&self) -> Option<(PublicationAck, oneshot::Receiver<bool>)> {
1559
        let mut state = self.state.lock().unwrap_or_else(|error| error.into_inner());
1560
        if state.mirrors == 0 {
1561
            return None;
1562
        }
1563
        state.pending.retain(|pending| pending.strong_count() > 0);
1564
        let (sender, receiver) = oneshot::channel();
1565
        let acknowledgement = PublicationAck(Arc::new(PublicationAckInner {
1566
            sender: Mutex::new(Some(sender)),
1567
        }));
1568
        state.pending.push(Arc::downgrade(&acknowledgement.0));
1569
        Some((acknowledgement, receiver))
1570
    }
1571
}
1572
1573
pub(crate) struct PublicationRegistration {
1574
    registry: Arc<PublicationRegistry>,
1575
}
1576
1577
impl Drop for PublicationRegistration {
1578
    fn drop(&mut self) {
1579
        let pending = {
1580
            let mut state = self
1581
                .registry
1582
                .state
1583
                .lock()
1584
                .unwrap_or_else(|error| error.into_inner());
1585
            state.mirrors -= 1;
1586
            if state.mirrors == 0 {
1587
                std::mem::take(&mut state.pending)
1588
            } else {
1589
                Vec::new()
1590
            }
1591
        };
1592
        for acknowledgement in pending.into_iter().filter_map(|ack| ack.upgrade()) {
1593
            PublicationAck(acknowledgement).complete(false);
1594
        }
1595
    }
1596
}
1597
1598
#[derive(Clone, Debug)]
1599
pub(crate) struct EventSender {
1600
    events: mpsc::Sender<Event>,
1601
    publication: Arc<PublicationRegistry>,
1602
}
1603
1604
impl EventSender {
1605
    pub(crate) async fn send(&self, event: Event) -> Result<(), SendEventError> {
1606
        self.events.send(event).await.map_err(|_| SendEventError)
1607
    }
1608
1609
    pub(crate) async fn publish_barrier(&self) -> Result<(), SendEventError> {
1610
        let Some((acknowledgement, published)) = self.publication.acknowledgement() else {
1611
            return Ok(());
1612
        };
1613
        if let Err(error) = self
1614
            .send(Event::PublicationBarrier {
1615
                acknowledgement: Some(acknowledgement.clone()),
1616
            })
1617
            .await
1618
        {
1619
            acknowledgement.complete(false);
1620
            return Err(error);
1621
        }
1622
        drop(acknowledgement);
1623
        match published.await {
1624
            Ok(true) => Ok(()),
1625
            Ok(false) | Err(_) => Err(SendEventError),
1626
        }
1627
    }
1628
1629
    pub(crate) async fn reserve(&self) -> Result<mpsc::Permit<'_, Event>, SendEventError> {
1630
        self.events.reserve().await.map_err(|_| SendEventError)
1631
    }
1632
1633
    /// resolves once every `EventStream` is gone
1634
    pub(crate) async fn closed(&self) {
1635
        self.events.closed().await;
1636
    }
1637
}
1638
1639
impl DriverPort {
1640
    pub(crate) fn event_sender(&self) -> EventSender {
1641
        EventSender {
1642
            events: self.events.clone(),
1643
            publication: self.publication.clone(),
1644
        }
1645
    }
1646
1647
    pub(crate) fn connection_sender(&self) -> watch::Sender<ConnectionState> {
1648
        self.connection.clone()
1649
    }
1650
1651
    pub(crate) async fn recv(&mut self) -> Option<Request> {
1652
        self.requests.recv().await
1653
    }
1654
1655
    pub(crate) fn try_recv(&mut self) -> Option<Request> {
1656
        self.requests.try_recv().ok()
1657
    }
1658
1659
    pub(crate) fn close(&mut self) {
1660
        self.requests.close();
1661
    }
1662
}
1663
1664
#[derive(Clone, Copy, Debug, Eq, PartialEq, thiserror::Error)]
1665
#[error("the event receiver has closed")]
1666
pub(crate) struct SendEventError;
1667
1668
#[must_use]
1669
pub(crate) fn transport_pair(
1670
    client: Client,
1671
    sync_service: Arc<SyncService>,
1672
    media: Arc<MediaService>,
1673
    store_lock: Option<Arc<StoreLock>>,
1674
    command_capacity: usize,
1675
    event_capacity: usize,
1676
) -> (ClientHandle, DriverPort, EventStream) {
1677
    let (request_tx, request_rx) = mpsc::channel(command_capacity);
1678
    let (event_tx, event_rx) = mpsc::channel(event_capacity);
1679
    let (connection_tx, connection_rx) = watch::channel(ConnectionState::Idle);
1680
    let publication = Arc::new(PublicationRegistry::default());
1681
    (
1682
        ClientHandle {
1683
            requests: request_tx,
1684
            client,
1685
            connection: connection_rx,
1686
            notification_client: Arc::new(OnceCell::new()),
1687
            sync_service,
1688
            media,
1689
            spaces: Arc::new(OnceCell::new()),
1690
            members_checked: Arc::default(),
1691
            publication: publication.clone(),
1692
            _store_lock: store_lock,
1693
        },
1694
        DriverPort {
1695
            requests: request_rx,
1696
            events: event_tx,
1697
            connection: connection_tx,
1698
            publication,
1699
        },
1700
        EventStream { events: event_rx },
1701
    )
1702
}
1703
1704
async fn list_members(
1705
    room: &matrix_sdk::Room,
1706
    filter: MemberFilter,
1707
) -> Result<Vec<MemberSummary>, Error> {
1708
    let memberships = match filter {
1709
        MemberFilter::Active => RoomMemberships::ACTIVE,
1710
        MemberFilter::Joined => RoomMemberships::JOIN,
1711
        MemberFilter::Invited => RoomMemberships::INVITE,
1712
        MemberFilter::Banned => RoomMemberships::BAN,
1713
        MemberFilter::Left => RoomMemberships::LEAVE,
1714
        // An empty mask is the SDK's unfiltered wildcard: it matches every
1715
        // membership, including custom states that `all()` would exclude.
1716
        MemberFilter::All => RoomMemberships::empty(),
1717
    };
1718
    Ok(room
1719
        .members(memberships)
1720
        .await
1721
        .map_err(|source| Error::from_sdk("listing members failed", source))?
1722
        .into_iter()
1723
        .map(member_summary)
1724
        .collect())
1725
}
1726
1727
fn normalise_push_notification(
1728
    room_id: &RoomId,
1729
    event_id: &EventId,
1730
    status: NotificationStatus,
1731
) -> PushNotificationStatus {
1732
    match status {
1733
        NotificationStatus::Event(item) => {
1734
            let event = match &item.raw_event {
1735
                RawNotificationEvent::Timeline(event) => {
1736
                    PushNotificationEvent::Timeline(event.clone())
1737
                }
1738
                RawNotificationEvent::Invite(event) => PushNotificationEvent::Invite(event.clone()),
1739
            };
1740
            PushNotificationStatus::Event(Box::new(PushNotification {
1741
                event_id: event_id.to_owned(),
1742
                event,
1743
                sender: PushNotificationSender {
1744
                    user_id: item.event.sender().to_owned(),
1745
                    display_name: item.sender_display_name,
1746
                    avatar_url: item.sender_avatar_url.as_deref().map(MediaId::plain),
1747
                    name_ambiguous: item.is_sender_name_ambiguous,
1748
                },
1749
                room: PushNotificationRoom {
1750
                    room_id: room_id.to_owned(),
1751
                    name: item.room_computed_display_name,
1752
                    avatar_url: item.room_avatar_url.as_deref().map(MediaId::plain),
1753
                    canonical_alias: item
1754
                        .room_canonical_alias
1755
                        .as_deref()
1756
                        .and_then(|alias| alias.parse().ok()),
1757
                    topic: item.room_topic,
1758
                    encrypted: item.is_room_encrypted,
1759
                    direct: item.room_is_dm,
1760
                    joined_members_count: item.joined_members_count,
1761
                    service_members: item
1762
                        .service_members
1763
                        .into_iter()
1764
                        .filter_map(|user_id| user_id.parse().ok())
1765
                        .collect(),
1766
                    active_service_members_count: item.active_service_members_count,
1767
                    space: item.is_space,
1768
                },
1769
                noisy: item.is_noisy,
1770
                has_mention: item.has_mention,
1771
                thread_id: item.thread_id,
1772
            }))
1773
        }
1774
        NotificationStatus::EventNotFound => PushNotificationStatus::NotFound,
1775
        NotificationStatus::EventFilteredOut => PushNotificationStatus::FilteredOut,
1776
        NotificationStatus::EventRedacted => PushNotificationStatus::Redacted,
1777
    }
1778
}
1779
1780
fn unexpected(wanted: &str, output: &CommandOutput) -> Error {
1781
    Error::new(
1782
        ErrorKind::Other,
1783
        format!("expected {wanted} but got {output:?}"),
1784
    )
1785
}
1786
1787
#[cfg(test)]
1788
mod tests {
1789
    use super::*;
1790
1791
    #[tokio::test]
1792
    async fn publish_barrier_waits_for_mirror_publication() {
1793
        let publication = Arc::new(PublicationRegistry::default());
1794
        let _registration = publication.register_mirror();
1795
        let (events, mut received) = mpsc::channel(1);
1796
        let sender = EventSender {
1797
            events,
1798
            publication,
1799
        };
1800
        let publisher = tokio::spawn(async move { sender.publish_barrier().await });
1801
1802
        let Some(Event::PublicationBarrier {
1803
            acknowledgement: Some(acknowledgement),
1804
        }) = received.recv().await
1805
        else {
1806
            panic!("barrier was not published");
1807
        };
1808
        tokio::task::yield_now().await;
1809
        assert!(!publisher.is_finished());
1810
1811
        acknowledgement.complete(true);
1812
        assert!(matches!(publisher.await.unwrap(), Ok(())));
1813
    }
1814
1815
    #[tokio::test]
1816
    async fn publish_barrier_fails_when_last_mirror_drops() {
1817
        let publication = Arc::new(PublicationRegistry::default());
1818
        let registration = publication.register_mirror();
1819
        let (events, mut received) = mpsc::channel(1);
1820
        let sender = EventSender {
1821
            events,
1822
            publication,
1823
        };
1824
        let publisher = tokio::spawn(async move { sender.publish_barrier().await });
1825
1826
        assert!(matches!(
1827
            received.recv().await,
1828
            Some(Event::PublicationBarrier {
1829
                acknowledgement: Some(_),
1830
            })
1831
        ));
1832
        drop(registration);
1833
1834
        assert!(matches!(publisher.await.unwrap(), Err(SendEventError)));
1835
    }
1836
}