atagen

macltok

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