atagen

macltok

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