atagen

macltok

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