atagen

macltok

public
Star0
Watch0
a250c7fa43fbe43fe49c2bb918f23306f9622244
3
ssh://brewery@pls.lobotomise.me:33/atagen/macltok.git
src/transport.rsRust1088 lines33.7 KB
1
use std::{
2
    fmt,
3
    future::Future,
4
    pin::Pin,
5
    sync::Arc,
6
    task::{Context, Poll},
7
};
8
9
use matrix_sdk::{Client, RoomMemberships};
10
use matrix_sdk_ui::{
11
    notification_client::{
12
        NotificationClient, NotificationProcessSetup, NotificationStatus, RawNotificationEvent,
13
    },
14
    spaces::{SpaceRoomList, SpaceService, room_list::SpaceRoomListPaginationState},
15
    sync_service::SyncService,
16
};
17
use ruma::{
18
    EventId, MxcUri, OwnedDeviceId, OwnedEventId, OwnedMxcUri, OwnedRoomId, OwnedRoomOrAliasId,
19
    OwnedServerName, OwnedTransactionId, OwnedUserId, RoomId, UserId,
20
    events::{
21
        AnyGlobalAccountDataEventContent, AnyRoomAccountDataEvent, AnyRoomAccountDataEventContent,
22
        GlobalAccountDataEventType, RoomAccountDataEventType,
23
    },
24
    serde::Raw,
25
};
26
use tokio::sync::{OnceCell, mpsc, oneshot, watch};
27
28
use crate::{
29
    AccountCommand, AttachmentUpload, Command, CommandOutput, ConnectionState, CreateRoomOptions,
30
    EncryptionCommand, EncryptionState, Error, ErrorKind, Event, EventRef, InteractiveAuth,
31
    InteractiveAuthOutcome, LifecycleCommand, Media, MediaCommand, MediaId, MediaSize,
32
    MemberFilter, MemberSummary, MessageContent, MessageEditContent, NotificationCommand,
33
    OwnDevice, PowerLevelChanges, Profile, PushNotification, PushNotificationEvent,
34
    PushNotificationStatus, RoomCommand, RoomListFilter, RoomNotificationMode,
35
    RoomNotificationSettings, RoomPowerLevels, RoomSummary, SpaceChild, TimelineCommand,
36
    TimelineId, VerificationAction, VerificationTarget,
37
    encryption::encryption_state,
38
    engine::{MediaService, StoreLock},
39
    model::{PushNotificationRoom, PushNotificationSender},
40
    normalise::{member_summary, room_summaries, space_child},
41
};
42
43
#[derive(Clone)]
44
pub struct ClientHandle {
45
    requests: mpsc::Sender<Request>,
46
    client: Client,
47
    connection: watch::Receiver<ConnectionState>,
48
    notification_client: Arc<OnceCell<NotificationClient>>,
49
    sync_service: Arc<SyncService>,
50
    media: Arc<MediaService>,
51
    spaces: Arc<OnceCell<SpaceService>>,
52
    _store_lock: Option<Arc<StoreLock>>,
53
}
54
55
impl fmt::Debug for ClientHandle {
56
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
57
        formatter
58
            .debug_struct("ClientHandle")
59
            .finish_non_exhaustive()
60
    }
61
}
62
63
/// a command's answer. drop it if you don't care, the command still runs
64
#[derive(Debug)]
65
#[must_use = "dropping it is fine but then you'll never know"]
66
pub struct Pending(oneshot::Receiver<Result<CommandOutput, Error>>);
67
68
impl Future for Pending {
69
    type Output = Result<CommandOutput, Error>;
70
71
    fn poll(mut self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Self::Output> {
72
        Pin::new(&mut self.0)
73
            .poll(context)
74
            .map(|result| result.unwrap_or_else(|_| Err(Error::interrupted())))
75
    }
76
}
77
78
impl ClientHandle {
79
    /// current credentials, it holds secrets. the engine saves it for you unless the
80
    /// session was built `without_persistence`
81
    #[cfg(any(feature = "sqlite", feature = "indexeddb"))]
82
    pub fn session_record(&self) -> Result<crate::SessionRecord, crate::AccountError> {
83
        crate::account::session_record(&self.client)
84
    }
85
86
    /// queue a command, waiting for room in the queue if there isn't any
87
    pub async fn send(&self, command: Command) -> Result<Pending, Error> {
88
        let (request, pending) = Request::new(command);
89
        self.requests
90
            .send(request)
91
            .await
92
            .map_err(|_| Error::interrupted())?;
93
        Ok(pending)
94
    }
95
96
    /// queue a command or fail with `QueueFull` right now
97
    pub fn try_send(&self, command: Command) -> Result<Pending, Error> {
98
        let (request, pending) = Request::new(command);
99
        self.requests
100
            .try_send(request)
101
            .map_err(|error| match error {
102
                mpsc::error::TrySendError::Full(_) => {
103
                    Error::new(ErrorKind::QueueFull, "the command queue is full")
104
                }
105
                mpsc::error::TrySendError::Closed(_) => Error::interrupted(),
106
            })?;
107
        Ok(pending)
108
    }
109
110
    /// queue a command and wait for its answer. timeline sends answer once the
111
    /// sdk send queue has them, delivery shows up later as the item's send state
112
    pub async fn execute(&self, command: Command) -> Result<CommandOutput, Error> {
113
        self.send(command).await?.await
114
    }
115
116
    async fn run(&self, command: Command) -> Result<(), Error> {
117
        self.execute(command).await.map(drop)
118
    }
119
120
    async fn run_for_room(&self, command: RoomCommand) -> Result<OwnedRoomId, Error> {
121
        match self.execute(Command::Room(command)).await? {
122
            CommandOutput::Room(room_id) => Ok(room_id),
123
            output => Err(Error::new(
124
                ErrorKind::Other,
125
                format!("expected a room but got {output:?}"),
126
            )),
127
        }
128
    }
129
130
    pub async fn shutdown(&self) -> Result<(), Error> {
131
        self.run(Command::Lifecycle(LifecycleCommand::Shutdown))
132
            .await
133
    }
134
135
    pub async fn logout(&self) -> Result<(), Error> {
136
        self.run(Command::Account(AccountCommand::Logout)).await
137
    }
138
139
    pub async fn delete_devices(
140
        &self,
141
        device_ids: Vec<OwnedDeviceId>,
142
        auth: Option<InteractiveAuth>,
143
    ) -> Result<InteractiveAuthOutcome, Error> {
144
        match self
145
            .run(Command::Account(AccountCommand::DeleteDevices {
146
                device_ids,
147
                auth,
148
            }))
149
            .await
150
        {
151
            Ok(()) => Ok(InteractiveAuthOutcome::Completed),
152
            Err(Error {
153
                challenge: Some(challenge),
154
                ..
155
            }) => Ok(InteractiveAuthOutcome::Challenge(*challenge)),
156
            Err(error) => Err(error),
157
        }
158
    }
159
160
    pub async fn rename_device(
161
        &self,
162
        device_id: OwnedDeviceId,
163
        display_name: impl Into<String>,
164
    ) -> Result<(), Error> {
165
        self.run(Command::Account(AccountCommand::RenameDevice {
166
            device_id,
167
            display_name: display_name.into(),
168
        }))
169
        .await
170
    }
171
172
    pub async fn set_global_account_data(
173
        &self,
174
        event_type: GlobalAccountDataEventType,
175
        content: Raw<AnyGlobalAccountDataEventContent>,
176
    ) -> Result<(), Error> {
177
        self.run(Command::Account(AccountCommand::SetAccountData {
178
            event_type,
179
            content,
180
        }))
181
        .await
182
    }
183
184
    pub async fn set_room_account_data(
185
        &self,
186
        room_id: OwnedRoomId,
187
        event_type: RoomAccountDataEventType,
188
        content: Raw<AnyRoomAccountDataEventContent>,
189
    ) -> Result<(), Error> {
190
        self.run(Command::Room(RoomCommand::SetAccountData {
191
            room_id,
192
            event_type,
193
            content,
194
        }))
195
        .await
196
    }
197
198
    pub async fn set_room_notification_mode(
199
        &self,
200
        room_id: OwnedRoomId,
201
        mode: RoomNotificationMode,
202
    ) -> Result<(), Error> {
203
        self.run(Command::Notification(NotificationCommand::SetRoomMode {
204
            room_id,
205
            mode,
206
        }))
207
        .await
208
    }
209
210
    pub async fn reset_room_notification_mode(&self, room_id: OwnedRoomId) -> Result<(), Error> {
211
        self.run(Command::Notification(NotificationCommand::ResetRoomMode(
212
            room_id,
213
        )))
214
        .await
215
    }
216
217
    pub async fn set_default_notification_mode(
218
        &self,
219
        encrypted: bool,
220
        one_to_one: bool,
221
        mode: RoomNotificationMode,
222
    ) -> Result<(), Error> {
223
        self.run(Command::Notification(NotificationCommand::SetDefaultMode {
224
            encrypted,
225
            one_to_one,
226
            mode,
227
        }))
228
        .await
229
    }
230
231
    pub async fn add_notification_keyword(&self, keyword: impl Into<String>) -> Result<(), Error> {
232
        self.run(Command::Notification(NotificationCommand::AddKeyword(
233
            keyword.into(),
234
        )))
235
        .await
236
    }
237
238
    pub async fn remove_notification_keyword(
239
        &self,
240
        keyword: impl Into<String>,
241
    ) -> Result<(), Error> {
242
        self.run(Command::Notification(NotificationCommand::RemoveKeyword(
243
            keyword.into(),
244
        )))
245
        .await
246
    }
247
248
    pub async fn open_room(&self, room_id: OwnedRoomId) -> Result<(), Error> {
249
        self.run(Command::Room(RoomCommand::Open(room_id))).await
250
    }
251
252
    pub async fn close_room(&self, room_id: OwnedRoomId) -> Result<(), Error> {
253
        self.run(Command::Room(RoomCommand::Close(room_id))).await
254
    }
255
256
    pub async fn open_thread(
257
        &self,
258
        room_id: OwnedRoomId,
259
        root_event_id: OwnedEventId,
260
    ) -> Result<(), Error> {
261
        self.run(Command::Timeline(TimelineCommand::OpenThread {
262
            room_id,
263
            root_event_id,
264
        }))
265
        .await
266
    }
267
268
    pub async fn close_thread(
269
        &self,
270
        room_id: OwnedRoomId,
271
        root_event_id: OwnedEventId,
272
    ) -> Result<(), Error> {
273
        self.run(Command::Timeline(TimelineCommand::CloseThread {
274
            room_id,
275
            root_event_id,
276
        }))
277
        .await
278
    }
279
280
    pub async fn resync(&self, timeline_id: TimelineId) -> Result<(), Error> {
281
        self.run(Command::Timeline(TimelineCommand::Resync { timeline_id }))
282
            .await
283
    }
284
285
    pub async fn paginate(&self, timeline_id: TimelineId, limit: u16) -> Result<(), Error> {
286
        self.run(Command::Timeline(TimelineCommand::Paginate {
287
            timeline_id,
288
            limit,
289
        }))
290
        .await
291
    }
292
293
    pub async fn send_message(
294
        &self,
295
        timeline_id: TimelineId,
296
        content: MessageContent,
297
    ) -> Result<(), Error> {
298
        self.run(Command::Timeline(TimelineCommand::Send {
299
            timeline_id,
300
            content,
301
        }))
302
        .await
303
    }
304
305
    pub async fn edit_message(
306
        &self,
307
        timeline_id: TimelineId,
308
        target: impl Into<EventRef>,
309
        content: MessageEditContent,
310
    ) -> Result<(), Error> {
311
        self.run(Command::Timeline(TimelineCommand::Edit {
312
            timeline_id,
313
            target: target.into(),
314
            content,
315
        }))
316
        .await
317
    }
318
319
    pub async fn redact(
320
        &self,
321
        timeline_id: TimelineId,
322
        target: impl Into<EventRef>,
323
        reason: Option<String>,
324
    ) -> Result<(), Error> {
325
        self.run(Command::Timeline(TimelineCommand::Redact {
326
            timeline_id,
327
            target: target.into(),
328
            reason,
329
        }))
330
        .await
331
    }
332
333
    pub async fn toggle_reaction(
334
        &self,
335
        timeline_id: TimelineId,
336
        target: impl Into<EventRef>,
337
        key: impl Into<String>,
338
    ) -> Result<(), Error> {
339
        self.run(Command::Timeline(TimelineCommand::ToggleReaction {
340
            timeline_id,
341
            target: target.into(),
342
            key: key.into(),
343
        }))
344
        .await
345
    }
346
347
    pub async fn mark_read(
348
        &self,
349
        timeline_id: TimelineId,
350
        event_id: OwnedEventId,
351
    ) -> Result<(), Error> {
352
        self.run(Command::Timeline(TimelineCommand::MarkRead {
353
            timeline_id,
354
            event_id,
355
        }))
356
        .await
357
    }
358
359
    pub async fn set_typing(&self, room_id: OwnedRoomId, typing: bool) -> Result<(), Error> {
360
        self.run(Command::Room(RoomCommand::SetTyping { room_id, typing }))
361
            .await
362
    }
363
364
    pub async fn prepare_to_send(&self, room_id: OwnedRoomId) -> Result<(), Error> {
365
        self.run(Command::Room(RoomCommand::PrepareToSend(room_id)))
366
            .await
367
    }
368
369
    pub async fn join_room(
370
        &self,
371
        room: OwnedRoomOrAliasId,
372
        via: Vec<OwnedServerName>,
373
    ) -> Result<OwnedRoomId, Error> {
374
        self.run_for_room(RoomCommand::Join { room, via }).await
375
    }
376
377
    pub async fn leave_room(&self, room_id: OwnedRoomId) -> Result<(), Error> {
378
        self.run(Command::Room(RoomCommand::Leave(room_id))).await
379
    }
380
381
    pub async fn forget_room(&self, room_id: OwnedRoomId) -> Result<(), Error> {
382
        self.run(Command::Room(RoomCommand::Forget(room_id))).await
383
    }
384
385
    pub async fn accept_invite(&self, room_id: OwnedRoomId) -> Result<(), Error> {
386
        self.run(Command::Room(RoomCommand::AcceptInvite(room_id)))
387
            .await
388
    }
389
390
    pub async fn reject_invite(&self, room_id: OwnedRoomId) -> Result<(), Error> {
391
        self.run(Command::Room(RoomCommand::RejectInvite(room_id)))
392
            .await
393
    }
394
395
    pub async fn invite_user(
396
        &self,
397
        room_id: OwnedRoomId,
398
        user_id: OwnedUserId,
399
    ) -> Result<(), Error> {
400
        self.run(Command::Room(RoomCommand::InviteUser { room_id, user_id }))
401
            .await
402
    }
403
404
    pub async fn kick_user(
405
        &self,
406
        room_id: OwnedRoomId,
407
        user_id: OwnedUserId,
408
        reason: Option<String>,
409
    ) -> Result<(), Error> {
410
        self.run(Command::Room(RoomCommand::KickUser {
411
            room_id,
412
            user_id,
413
            reason,
414
        }))
415
        .await
416
    }
417
418
    pub async fn ban_user(
419
        &self,
420
        room_id: OwnedRoomId,
421
        user_id: OwnedUserId,
422
        reason: Option<String>,
423
    ) -> Result<(), Error> {
424
        self.run(Command::Room(RoomCommand::BanUser {
425
            room_id,
426
            user_id,
427
            reason,
428
        }))
429
        .await
430
    }
431
432
    pub async fn unban_user(
433
        &self,
434
        room_id: OwnedRoomId,
435
        user_id: OwnedUserId,
436
        reason: Option<String>,
437
    ) -> Result<(), Error> {
438
        self.run(Command::Room(RoomCommand::UnbanUser {
439
            room_id,
440
            user_id,
441
            reason,
442
        }))
443
        .await
444
    }
445
446
    pub async fn set_power_levels(
447
        &self,
448
        room_id: OwnedRoomId,
449
        changes: PowerLevelChanges,
450
    ) -> Result<(), Error> {
451
        self.run(Command::Room(RoomCommand::SetPowerLevels {
452
            room_id,
453
            changes,
454
        }))
455
        .await
456
    }
457
458
    pub async fn set_user_power_level(
459
        &self,
460
        room_id: OwnedRoomId,
461
        user_id: OwnedUserId,
462
        level: i64,
463
    ) -> Result<(), Error> {
464
        self.run(Command::Room(RoomCommand::SetUserPowerLevel {
465
            room_id,
466
            user_id,
467
            level,
468
        }))
469
        .await
470
    }
471
472
    pub async fn create_room(&self, options: CreateRoomOptions) -> Result<OwnedRoomId, Error> {
473
        self.run_for_room(RoomCommand::Create(options)).await
474
    }
475
476
    /// hands back the existing dm with them if there is one
477
    pub async fn create_direct(&self, user_id: OwnedUserId) -> Result<OwnedRoomId, Error> {
478
        self.run_for_room(RoomCommand::CreateDirect(user_id)).await
479
    }
480
481
    pub async fn cancel_send(
482
        &self,
483
        room_id: OwnedRoomId,
484
        transaction_id: OwnedTransactionId,
485
    ) -> Result<(), Error> {
486
        self.run(Command::Room(RoomCommand::CancelSend {
487
            room_id,
488
            transaction_id,
489
        }))
490
        .await
491
    }
492
493
    pub async fn retry_send(
494
        &self,
495
        room_id: OwnedRoomId,
496
        transaction_id: OwnedTransactionId,
497
    ) -> Result<(), Error> {
498
        self.run(Command::Room(RoomCommand::RetrySend {
499
            room_id,
500
            transaction_id,
501
        }))
502
        .await
503
    }
504
505
    pub async fn upload(&self, upload: AttachmentUpload) -> Result<(), Error> {
506
        self.run(Command::Media(MediaCommand::Upload(upload))).await
507
    }
508
509
    pub async fn recover(&self, recovery_key: impl Into<String>) -> Result<(), Error> {
510
        self.run(Command::Encryption(EncryptionCommand::Recover(
511
            recovery_key.into(),
512
        )))
513
        .await
514
    }
515
516
    pub async fn create_key_backup(&self) -> Result<(), Error> {
517
        self.run(Command::Encryption(EncryptionCommand::CreateKeyBackup))
518
            .await
519
    }
520
521
    pub async fn enable_key_backup(&self) -> Result<(), Error> {
522
        self.run(Command::Encryption(EncryptionCommand::EnableKeyBackup))
523
            .await
524
    }
525
526
    pub async fn disable_key_backup(&self, delete_from_server: bool) -> Result<(), Error> {
527
        self.run(Command::Encryption(EncryptionCommand::DisableKeyBackup {
528
            delete_from_server,
529
        }))
530
        .await
531
    }
532
533
    pub async fn recover_key_backup(&self, recovery_key: impl Into<String>) -> Result<(), Error> {
534
        self.run(Command::Encryption(EncryptionCommand::RecoverKeyBackup(
535
            recovery_key.into(),
536
        )))
537
        .await
538
    }
539
540
    pub async fn start_verification(&self, target: VerificationTarget) -> Result<(), Error> {
541
        self.run(Command::Encryption(EncryptionCommand::StartVerification(
542
            target,
543
        )))
544
        .await
545
    }
546
547
    pub async fn verify(&self, action: VerificationAction) -> Result<(), Error> {
548
        self.run(Command::Encryption(EncryptionCommand::Verify(action)))
549
            .await
550
    }
551
552
    /// bytes for media a retained timeline showed you. concurrent asks share one download
553
    pub async fn media(&self, media_id: &MediaId, size: MediaSize) -> Result<Media, Error> {
554
        self.media.timeline_media(media_id, size).await
555
    }
556
557
    /// bytes for any plain mxc uri, avatars mostly. same byte cap as everything else
558
    pub async fn mxc_media(&self, uri: &MxcUri, size: MediaSize) -> Result<Media, Error> {
559
        self.media.mxc_media(uri, size).await
560
    }
561
562
    /// the space graph builds on first use and then keeps itself current
563
    async fn spaces(&self) -> &SpaceService {
564
        self.spaces
565
            .get_or_init(|| SpaceService::new(self.client.clone()))
566
            .await
567
    }
568
569
    pub async fn rooms(&self) -> Vec<RoomSummary> {
570
        room_summaries(&self.client, self.spaces().await).await
571
    }
572
573
    /// browse a space the way the server sees it, rooms you haven't joined included
574
    pub async fn space_hierarchy(&self, space_id: OwnedRoomId) -> SpaceHierarchy {
575
        SpaceHierarchy(SpaceRoomList::new(self.client.clone(), space_id).await)
576
    }
577
578
    pub async fn own_profile(&self) -> Result<Profile, Error> {
579
        let user_id = self
580
            .client
581
            .user_id()
582
            .ok_or_else(|| Error::new(ErrorKind::Unauthenticated, "the client is not logged in"))?
583
            .to_owned();
584
        let response = self
585
            .client
586
            .account()
587
            .fetch_user_profile()
588
            .await
589
            .map_err(|source| Error::from_sdk("fetching the profile failed", source))?;
590
        profile(user_id, &response)
591
    }
592
593
    pub async fn user_profile(&self, user_id: &UserId) -> Result<Profile, Error> {
594
        let response = self
595
            .client
596
            .account()
597
            .fetch_user_profile_of(user_id)
598
            .await
599
            .map_err(|source| Error::from_sdk("fetching the profile failed", source))?;
600
        profile(user_id.to_owned(), &response)
601
    }
602
603
    pub async fn set_display_name(&self, name: Option<String>) -> Result<(), Error> {
604
        self.run(Command::Account(AccountCommand::SetDisplayName(name)))
605
            .await
606
    }
607
608
    pub async fn set_avatar(&self, avatar: Option<OwnedMxcUri>) -> Result<(), Error> {
609
        self.run(Command::Account(AccountCommand::SetAvatar(avatar)))
610
            .await
611
    }
612
613
    pub async fn upload_avatar(
614
        &self,
615
        mimetype: impl Into<String>,
616
        bytes: Vec<u8>,
617
    ) -> Result<(), Error> {
618
        self.run(Command::Account(AccountCommand::UploadAvatar {
619
            mimetype: mimetype.into(),
620
            bytes,
621
        }))
622
        .await
623
    }
624
625
    pub async fn rooms_filtered(&self, filter: &RoomListFilter) -> Vec<RoomSummary> {
626
        let matcher = filter.matcher();
627
        self.rooms()
628
            .await
629
            .into_iter()
630
            .filter(|room| matcher.matches(room))
631
            .collect()
632
    }
633
634
    pub async fn devices(&self) -> Result<Vec<OwnDevice>, Error> {
635
        let current_device_id = self.client.device_id();
636
        Ok(self
637
            .client
638
            .devices()
639
            .await
640
            .map_err(|source| Error::from_http("listing devices failed", source))?
641
            .devices
642
            .into_iter()
643
            .map(|device| OwnDevice {
644
                current: current_device_id == Some(device.device_id.as_ref()),
645
                device_id: device.device_id,
646
                display_name: device.display_name,
647
                last_seen_ip: device.last_seen_ip,
648
                last_seen_timestamp_ms: device.last_seen_ts.map(|timestamp| timestamp.get().into()),
649
            })
650
            .collect())
651
    }
652
653
    fn room(&self, room_id: &RoomId) -> Result<matrix_sdk::Room, Error> {
654
        self.client
655
            .get_room(room_id)
656
            .ok_or_else(Error::room_unknown)
657
    }
658
659
    pub async fn room_notification_settings(
660
        &self,
661
        room_id: &RoomId,
662
    ) -> Result<Option<RoomNotificationSettings>, Error> {
663
        let room = self.room(room_id)?;
664
        let custom = room.user_defined_notification_mode().await;
665
        let mode = match custom {
666
            Some(mode) => mode,
667
            None => match room.notification_mode().await {
668
                Some(mode) => mode,
669
                None => return Ok(None),
670
            },
671
        };
672
        Ok(Some(RoomNotificationSettings {
673
            mode: crate::normalise::notification_mode(mode),
674
            custom: custom.is_some(),
675
        }))
676
    }
677
678
    pub async fn notification_keywords(&self) -> Vec<String> {
679
        self.client
680
            .notification_settings()
681
            .await
682
            .enabled_keywords()
683
            .await
684
            .into_iter()
685
            .collect()
686
    }
687
688
    pub async fn resolve_push_notification(
689
        &self,
690
        room_id: &RoomId,
691
        event_id: &EventId,
692
    ) -> Result<PushNotificationStatus, Error> {
693
        let notification_failed = |source| {
694
            Error::with_source(
695
                ErrorKind::Other,
696
                "resolving the push notification failed",
697
                source,
698
            )
699
        };
700
        let client = self
701
            .notification_client
702
            .get_or_try_init(|| async {
703
                NotificationClient::new(
704
                    self.client.clone(),
705
                    NotificationProcessSetup::SingleProcess {
706
                        sync_service: self.sync_service.clone(),
707
                    },
708
                )
709
                .await
710
            })
711
            .await
712
            .map_err(notification_failed)?;
713
        let status = client
714
            .get_notification(room_id, event_id)
715
            .await
716
            .map_err(notification_failed)?;
717
        Ok(normalise_push_notification(room_id, event_id, status))
718
    }
719
720
    pub async fn members(
721
        &self,
722
        room_id: &RoomId,
723
        filter: MemberFilter,
724
    ) -> Result<Vec<MemberSummary>, Error> {
725
        let memberships = match filter {
726
            MemberFilter::Active => RoomMemberships::ACTIVE,
727
            MemberFilter::Joined => RoomMemberships::JOIN,
728
            MemberFilter::Invited => RoomMemberships::INVITE,
729
            MemberFilter::Banned => RoomMemberships::BAN,
730
            MemberFilter::Left => RoomMemberships::LEAVE,
731
            // An empty mask is the SDK's unfiltered wildcard: it matches every
732
            // membership, including custom states that `all()` would exclude.
733
            MemberFilter::All => RoomMemberships::empty(),
734
        };
735
        Ok(self
736
            .room(room_id)?
737
            .members(memberships)
738
            .await
739
            .map_err(|source| Error::from_sdk("listing members failed", source))?
740
            .into_iter()
741
            .map(member_summary)
742
            .collect())
743
    }
744
745
    pub async fn room_power_levels(&self, room_id: &RoomId) -> Result<RoomPowerLevels, Error> {
746
        let levels = self
747
            .room(room_id)?
748
            .power_levels()
749
            .await
750
            .map_err(|source| Error::from_sdk("reading power levels failed", source.into()))?;
751
        Ok(RoomPowerLevels {
752
            ban: levels.ban.into(),
753
            invite: levels.invite.into(),
754
            kick: levels.kick.into(),
755
            redact: levels.redact.into(),
756
            events_default: levels.events_default.into(),
757
            state_default: levels.state_default.into(),
758
            users_default: levels.users_default.into(),
759
            room_notification: levels.notifications.room.into(),
760
            users: levels
761
                .users
762
                .into_iter()
763
                .map(|(user_id, level)| (user_id, level.into()))
764
                .collect(),
765
            events: levels
766
                .events
767
                .into_iter()
768
                .map(|(event_type, level)| (event_type.to_string(), level.into()))
769
                .collect(),
770
            creators: levels
771
                .rules
772
                .privileged_creators
773
                .into_iter()
774
                .flatten()
775
                .collect(),
776
        })
777
    }
778
779
    pub async fn pending_send_ids(
780
        &self,
781
        room_id: &RoomId,
782
    ) -> Result<Vec<OwnedTransactionId>, Error> {
783
        let (echoes, _) = self
784
            .room(room_id)?
785
            .send_queue()
786
            .subscribe()
787
            .await
788
            .map_err(|source| {
789
                Error::with_source(ErrorKind::Store, "reading the send queue failed", source)
790
            })?;
791
        Ok(echoes
792
            .into_iter()
793
            .filter(|echo| {
794
                matches!(
795
                    echo.content,
796
                    matrix_sdk::send_queue::LocalEchoContent::Event { .. }
797
                )
798
            })
799
            .map(|echo| echo.transaction_id)
800
            .collect())
801
    }
802
803
    pub async fn global_account_data(
804
        &self,
805
        event_type: GlobalAccountDataEventType,
806
    ) -> Result<Option<Raw<AnyGlobalAccountDataEventContent>>, Error> {
807
        self.client
808
            .account()
809
            .account_data_raw(event_type)
810
            .await
811
            .map_err(|source| Error::from_sdk("reading account data failed", source))
812
    }
813
814
    pub async fn room_account_data(
815
        &self,
816
        room_id: &RoomId,
817
        event_type: RoomAccountDataEventType,
818
    ) -> Result<Option<Raw<AnyRoomAccountDataEvent>>, Error> {
819
        self.room(room_id)?
820
            .account_data(event_type)
821
            .await
822
            .map_err(|source| Error::from_sdk("reading room account data failed", source))
823
    }
824
825
    #[must_use]
826
    pub fn connection_state(&self) -> watch::Receiver<ConnectionState> {
827
        self.connection.clone()
828
    }
829
830
    #[must_use]
831
    pub fn encryption_state(&self) -> Option<EncryptionState> {
832
        let device_id = self.client.device_id()?.to_owned();
833
        let encryption = self.client.encryption();
834
        Some(encryption_state(
835
            device_id,
836
            encryption.recovery().state(),
837
            encryption.verification_state().get(),
838
            encryption.backups().state(),
839
        ))
840
    }
841
}
842
843
/// one space's hierarchy, a page at a time. hold on to it while you browse, it remembers where it got to
844
pub struct SpaceHierarchy(SpaceRoomList);
845
846
impl fmt::Debug for SpaceHierarchy {
847
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
848
        formatter
849
            .debug_struct("SpaceHierarchy")
850
            .finish_non_exhaustive()
851
    }
852
}
853
854
impl SpaceHierarchy {
855
    /// fetches the next page. a no-op once the end is reached or while a page is in flight
856
    pub async fn paginate(&self) -> Result<(), Error> {
857
        self.0.paginate().await.map_err(|source| {
858
            Error::with_source(
859
                ErrorKind::Server,
860
                "fetching the space hierarchy failed",
861
                source,
862
            )
863
        })
864
    }
865
866
    /// everything fetched so far, the space itself excluded
867
    pub async fn rooms(&self) -> Vec<SpaceChild> {
868
        self.0.rooms().await.into_iter().map(space_child).collect()
869
    }
870
871
    #[must_use]
872
    pub fn end_reached(&self) -> bool {
873
        matches!(
874
            self.0.pagination_state(),
875
            SpaceRoomListPaginationState::Idle { end_reached: true }
876
        )
877
    }
878
}
879
880
fn profile(
881
    user_id: OwnedUserId,
882
    response: &ruma::api::client::profile::get_profile::v3::Response,
883
) -> Result<Profile, Error> {
884
    use ruma::api::client::profile::{AvatarUrl, DisplayName};
885
    let malformed = |source| {
886
        Error::with_source(
887
            ErrorKind::Server,
888
            "the server sent a malformed profile",
889
            source,
890
        )
891
    };
892
    Ok(Profile {
893
        user_id,
894
        display_name: response.get_static::<DisplayName>().map_err(malformed)?,
895
        avatar: response.get_static::<AvatarUrl>().map_err(malformed)?,
896
    })
897
}
898
899
#[derive(Debug)]
900
pub(crate) struct Request {
901
    pub(crate) command: Command,
902
    pub(crate) reply: Responder,
903
}
904
905
impl Request {
906
    fn new(command: Command) -> (Self, Pending) {
907
        let (reply, pending) = oneshot::channel();
908
        (
909
            Self {
910
                command,
911
                reply: Responder(reply),
912
            },
913
            Pending(pending),
914
        )
915
    }
916
}
917
918
/// whoever holds this owes the caller an answer. dropping it answers `Interrupted`
919
#[derive(Debug)]
920
pub(crate) struct Responder(oneshot::Sender<Result<CommandOutput, Error>>);
921
922
impl Responder {
923
    pub(crate) fn send(self, result: Result<CommandOutput, Error>) {
924
        if let Err(error) = &result {
925
            tracing::debug!(%error, kind = ?error.kind, "command failed");
926
        }
927
        let _ = self.0.send(result);
928
    }
929
930
    pub(crate) fn done(self, result: Result<(), Error>) {
931
        self.send(result.map(|()| CommandOutput::Done));
932
    }
933
}
934
935
#[derive(Debug)]
936
pub struct EventStream {
937
    events: mpsc::Receiver<Event>,
938
}
939
940
impl EventStream {
941
    pub async fn recv(&mut self) -> Option<Event> {
942
        self.events.recv().await
943
    }
944
945
    pub fn try_recv(&mut self) -> Result<Event, mpsc::error::TryRecvError> {
946
        self.events.try_recv()
947
    }
948
}
949
950
impl futures_util::Stream for EventStream {
951
    type Item = Event;
952
953
    fn poll_next(mut self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Option<Self::Item>> {
954
        self.events.poll_recv(context)
955
    }
956
}
957
958
#[derive(Debug)]
959
pub(crate) struct DriverPort {
960
    requests: mpsc::Receiver<Request>,
961
    events: mpsc::Sender<Event>,
962
    connection: watch::Sender<ConnectionState>,
963
}
964
965
#[derive(Clone, Debug)]
966
pub(crate) struct EventSender(mpsc::Sender<Event>);
967
968
impl EventSender {
969
    pub(crate) async fn send(&self, event: Event) -> Result<(), SendEventError> {
970
        self.0.send(event).await.map_err(|_| SendEventError)
971
    }
972
973
    /// resolves once every `EventStream` is gone
974
    pub(crate) async fn closed(&self) {
975
        self.0.closed().await;
976
    }
977
}
978
979
impl DriverPort {
980
    pub(crate) fn event_sender(&self) -> EventSender {
981
        EventSender(self.events.clone())
982
    }
983
984
    pub(crate) fn connection_sender(&self) -> watch::Sender<ConnectionState> {
985
        self.connection.clone()
986
    }
987
988
    pub(crate) async fn recv(&mut self) -> Option<Request> {
989
        self.requests.recv().await
990
    }
991
992
    pub(crate) fn try_recv(&mut self) -> Option<Request> {
993
        self.requests.try_recv().ok()
994
    }
995
996
    pub(crate) fn close(&mut self) {
997
        self.requests.close();
998
    }
999
}
1000
1001
#[derive(Clone, Copy, Debug, Eq, PartialEq, thiserror::Error)]
1002
#[error("the event receiver has closed")]
1003
pub(crate) struct SendEventError;
1004
1005
#[must_use]
1006
pub(crate) fn transport_pair(
1007
    client: Client,
1008
    sync_service: Arc<SyncService>,
1009
    media: Arc<MediaService>,
1010
    store_lock: Option<Arc<StoreLock>>,
1011
    command_capacity: usize,
1012
    event_capacity: usize,
1013
) -> (ClientHandle, DriverPort, EventStream) {
1014
    let (request_tx, request_rx) = mpsc::channel(command_capacity);
1015
    let (event_tx, event_rx) = mpsc::channel(event_capacity);
1016
    let (connection_tx, connection_rx) = watch::channel(ConnectionState::Idle);
1017
    (
1018
        ClientHandle {
1019
            requests: request_tx,
1020
            client,
1021
            connection: connection_rx,
1022
            notification_client: Arc::new(OnceCell::new()),
1023
            sync_service,
1024
            media,
1025
            spaces: Arc::new(OnceCell::new()),
1026
            _store_lock: store_lock,
1027
        },
1028
        DriverPort {
1029
            requests: request_rx,
1030
            events: event_tx,
1031
            connection: connection_tx,
1032
        },
1033
        EventStream { events: event_rx },
1034
    )
1035
}
1036
1037
fn normalise_push_notification(
1038
    room_id: &RoomId,
1039
    event_id: &EventId,
1040
    status: NotificationStatus,
1041
) -> PushNotificationStatus {
1042
    match status {
1043
        NotificationStatus::Event(item) => {
1044
            let event = match &item.raw_event {
1045
                RawNotificationEvent::Timeline(event) => {
1046
                    PushNotificationEvent::Timeline(event.clone())
1047
                }
1048
                RawNotificationEvent::Invite(event) => PushNotificationEvent::Invite(event.clone()),
1049
            };
1050
            PushNotificationStatus::Event(Box::new(PushNotification {
1051
                event_id: event_id.to_owned(),
1052
                event,
1053
                sender: PushNotificationSender {
1054
                    user_id: item.event.sender().to_owned(),
1055
                    display_name: item.sender_display_name,
1056
                    avatar_url: item.sender_avatar_url.map(Into::into),
1057
                    name_ambiguous: item.is_sender_name_ambiguous,
1058
                },
1059
                room: PushNotificationRoom {
1060
                    room_id: room_id.to_owned(),
1061
                    name: item.room_computed_display_name,
1062
                    avatar_url: item.room_avatar_url.map(Into::into),
1063
                    canonical_alias: item
1064
                        .room_canonical_alias
1065
                        .as_deref()
1066
                        .and_then(|alias| alias.parse().ok()),
1067
                    topic: item.room_topic,
1068
                    encrypted: item.is_room_encrypted,
1069
                    direct: item.room_is_dm,
1070
                    joined_members_count: item.joined_members_count,
1071
                    service_members: item
1072
                        .service_members
1073
                        .into_iter()
1074
                        .filter_map(|user_id| user_id.parse().ok())
1075
                        .collect(),
1076
                    active_service_members_count: item.active_service_members_count,
1077
                    space: item.is_space,
1078
                },
1079
                noisy: item.is_noisy,
1080
                has_mention: item.has_mention,
1081
                thread_id: item.thread_id,
1082
            }))
1083
        }
1084
        NotificationStatus::EventNotFound => PushNotificationStatus::NotFound,
1085
        NotificationStatus::EventFilteredOut => PushNotificationStatus::FilteredOut,
1086
        NotificationStatus::EventRedacted => PushNotificationStatus::Redacted,
1087
    }
1088
}