atagen

macltok

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