atagen

macltok

public
Star0
Watch0
31ce2453c82f470aa8b5d3861ab0eec4273afc5d
3
ssh://brewery@pls.lobotomise.me:33/atagen/macltok.git
src/normalise/room.rsRust216 lines7.5 KB
1
use futures_util::{StreamExt, stream};
2
use matrix_sdk::{
3
    Client, Room, RoomState,
4
    deserialized_responses::SyncOrStrippedState,
5
    latest_events::LatestEventValue,
6
    ruma::events::{
7
        AnyMessageLikeEventContent, AnySyncMessageLikeEvent, AnySyncTimelineEvent, SyncStateEvent,
8
        space::child::SpaceChildEventContent,
9
    },
10
};
11
use matrix_sdk_ui::spaces::{SpaceRoom, SpaceService};
12
use ruma::OwnedRoomId;
13
14
use crate::{
15
    LatestEvent, RoomHero, RoomMembership, RoomNotificationMode, RoomSummary, SendState, SpaceChild,
16
};
17
18
pub(crate) async fn room_summaries(client: &Client, spaces: &SpaceService) -> Vec<RoomSummary> {
19
    let mut summaries = stream::iter(client.rooms())
20
        .map(|room| async move { room_summary(&room, spaces).await })
21
        .buffer_unordered(16)
22
        .collect::<Vec<_>>()
23
        .await;
24
    summaries.sort_by(|left, right| {
25
        right
26
            .latest_event_timestamp_ms
27
            .cmp(&left.latest_event_timestamp_ms)
28
            .then_with(|| left.name.to_lowercase().cmp(&right.name.to_lowercase()))
29
            .then_with(|| left.room_id.cmp(&right.room_id))
30
    });
31
    summaries
32
}
33
34
async fn room_summary(room: &Room, spaces: &SpaceService) -> RoomSummary {
35
    let name = room
36
        .display_name()
37
        .await
38
        .map(|name| name.to_string())
39
        .unwrap_or_else(|_| room.room_id().to_string());
40
    let custom_notification_mode = room.user_defined_notification_mode().await;
41
    let effective_notification_mode = match custom_notification_mode {
42
        Some(mode) => Some(mode),
43
        None => room.notification_mode().await,
44
    };
45
    let inviter = match room.state() {
46
        RoomState::Invited => room
47
            .invite_details()
48
            .await
49
            .ok()
50
            .map(|invite| invite.inviter_id),
51
        _ => None,
52
    };
53
    let children = if room.is_space() {
54
        space_children(room).await
55
    } else {
56
        Vec::new()
57
    };
58
    RoomSummary {
59
        room_id: room.room_id().to_owned(),
60
        name,
61
        membership: membership(room.state()),
62
        unread_messages: room.num_unread_messages(),
63
        unread_mentions: room.num_unread_mentions(),
64
        encrypted: room.encryption_state().is_encrypted(),
65
        space: room.is_space(),
66
        direct: room.is_direct().await.unwrap_or(false),
67
        favourite: room.is_favourite(),
68
        low_priority: room.is_low_priority(),
69
        avatar: room.avatar_url(),
70
        latest_event_timestamp_ms: room
71
            .latest_event_timestamp()
72
            .map(|value| value.get().into()),
73
        latest_event: latest_event(room),
74
        notification_mode: effective_notification_mode.map(notification_mode),
75
        notification_mode_custom: custom_notification_mode.is_some(),
76
        topic: room.topic(),
77
        canonical_alias: room.canonical_alias(),
78
        joined_members: room.joined_members_count(),
79
        invited_members: room.invited_members_count(),
80
        heroes: room
81
            .heroes()
82
            .into_iter()
83
            .map(|hero| RoomHero {
84
                user_id: hero.user_id,
85
                display_name: hero.display_name,
86
                avatar: hero.avatar_url,
87
            })
88
            .collect(),
89
        successor: room.successor_room().map(|successor| successor.room_id),
90
        inviter,
91
        parent_spaces: spaces
92
            .joined_parents_of_child(room.room_id())
93
            .await
94
            .into_iter()
95
            .map(|parent| parent.room_id)
96
            .collect(),
97
        children,
98
    }
99
}
100
101
const fn membership(state: RoomState) -> RoomMembership {
102
    match state {
103
        RoomState::Joined => RoomMembership::Joined,
104
        RoomState::Invited => RoomMembership::Invited,
105
        RoomState::Left => RoomMembership::Left,
106
        RoomState::Knocked => RoomMembership::Knocked,
107
        RoomState::Banned => RoomMembership::Banned,
108
    }
109
}
110
111
/// what the space itself lists, straight from local state. redacted children don't count
112
async fn space_children(room: &Room) -> Vec<OwnedRoomId> {
113
    let Ok(events) = room
114
        .get_state_events_static::<SpaceChildEventContent>()
115
        .await
116
    else {
117
        return Vec::new();
118
    };
119
    events
120
        .into_iter()
121
        .filter_map(|event| match event.deserialize().ok()? {
122
            SyncOrStrippedState::Sync(SyncStateEvent::Original(event)) => Some(event.state_key),
123
            SyncOrStrippedState::Stripped(event) => Some(event.state_key),
124
            SyncOrStrippedState::Sync(SyncStateEvent::Redacted(_)) => None,
125
        })
126
        .collect()
127
}
128
129
fn latest_event(room: &Room) -> Option<LatestEvent> {
130
    let (sender, body, timestamp, send_state) = match room.latest_event() {
131
        LatestEventValue::None => return None,
132
        LatestEventValue::Remote(event) => {
133
            let body = match event.raw().deserialize() {
134
                Ok(AnySyncTimelineEvent::MessageLike(AnySyncMessageLikeEvent::RoomMessage(
135
                    message,
136
                ))) => message
137
                    .as_original()
138
                    .map(|message| message.content.body().to_owned()),
139
                _ => None,
140
            };
141
            (event.sender(), body, event.timestamp(), SendState::Sent)
142
        }
143
        LatestEventValue::RemoteInvite {
144
            timestamp, inviter, ..
145
        } => (inviter, None, Some(timestamp), SendState::Sent),
146
        LatestEventValue::LocalIsSending(local) => local_event(room, &local, SendState::Sending),
147
        LatestEventValue::LocalHasBeenSent { value, .. } => {
148
            local_event(room, &value, SendState::Sent)
149
        }
150
        LatestEventValue::LocalCannotBeSent(local) => {
151
            local_event(room, &local, SendState::Failed { retriable: true })
152
        }
153
    };
154
    Some(LatestEvent {
155
        sender,
156
        body,
157
        timestamp_ms: timestamp.map(|timestamp| timestamp.get().into()),
158
        send_state,
159
    })
160
}
161
162
type LatestParts = (
163
    Option<ruma::OwnedUserId>,
164
    Option<String>,
165
    Option<ruma::MilliSecondsSinceUnixEpoch>,
166
    SendState,
167
);
168
169
fn local_event(
170
    room: &Room,
171
    local: &matrix_sdk::latest_events::LocalLatestEventValue,
172
    send_state: SendState,
173
) -> LatestParts {
174
    let body = match local.content.deserialize() {
175
        Ok(AnyMessageLikeEventContent::RoomMessage(content)) => Some(content.body().to_owned()),
176
        _ => None,
177
    };
178
    (
179
        room.own_user_id().to_owned().into(),
180
        body,
181
        Some(local.timestamp),
182
        send_state,
183
    )
184
}
185
186
pub(crate) fn space_child(room: SpaceRoom) -> SpaceChild {
187
    SpaceChild {
188
        name: room.display_name,
189
        room_id: room.room_id,
190
        topic: room.topic,
191
        avatar: room.avatar_url,
192
        canonical_alias: room.canonical_alias,
193
        joined_members: room.num_joined_members,
194
        space: room.room_type == Some(ruma::room::RoomType::Space),
195
        children_count: room.children_count,
196
        membership: room.state.map(membership),
197
        via: room.via,
198
        suggested: room.suggested,
199
        world_readable: room.world_readable,
200
        guest_can_join: room.guest_can_join,
201
    }
202
}
203
204
pub(crate) const fn notification_mode(
205
    mode: matrix_sdk::notification_settings::RoomNotificationMode,
206
) -> RoomNotificationMode {
207
    match mode {
208
        matrix_sdk::notification_settings::RoomNotificationMode::AllMessages => {
209
            RoomNotificationMode::AllMessages
210
        }
211
        matrix_sdk::notification_settings::RoomNotificationMode::MentionsAndKeywordsOnly => {
212
            RoomNotificationMode::MentionsAndKeywordsOnly
213
        }
214
        matrix_sdk::notification_settings::RoomNotificationMode::Mute => RoomNotificationMode::Mute,
215
    }
216
}