atagen

macltok

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