atagen

macltok

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