cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 1 | use ruma::{ |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 2 | OwnedEventId, UserId, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 3 | events::{ |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 4 | poll::{start::PollStartEventContent, unstable_start::UnstablePollStartEventContent}, |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 5 | room::{ |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 6 | member::{MembershipChange as RumaMembershipChange, OriginalSyncRoomMemberEvent}, |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 7 | message::{ |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 8 | MessageType, Relation, RoomMessageEventContent, |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 9 | RoomMessageEventContentWithoutRelation, |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 10 | }, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 11 | }, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 12 | sticker::StickerEventContent, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 13 | }, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 14 | }; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 15 | use serde_json::Value; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 16 | |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 17 | use super::{ |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 18 | link_preview::sender_previews, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 19 | media::{ |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 20 | MediaRegistration, media_descriptor_for_sticker, media_registration, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 21 | media_sticker_registration, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 22 | }, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 23 | timeline::formatted_body, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 24 | }; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 25 | use crate::{ |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 26 | Change, MediaId, MembershipChange, Message, MessageMentions, ReplyDetails, ReplyReference, |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 27 | SendState, Sender, TimelineContent, TimelineEvent, TimelineItem, TimelineItemId, UtdCause, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 28 | }; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 29 | |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 30 | pub(crate) fn normalise_observed( |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 31 | value: &Value, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 32 | current_user_id: Option<&UserId>, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 33 | identity: &str, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 34 | ) -> Option<(TimelineItem, Vec<MediaRegistration>)> { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 35 | let event_id: OwnedEventId = serde_json::from_value(value.get("event_id")?.clone()).ok()?; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 36 | let sender_id: ruma::OwnedUserId = serde_json::from_value(value.get("sender")?.clone()).ok()?; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 37 | let timestamp_ms = value.get("origin_server_ts")?.as_u64()?; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 38 | let event_type = value.get("type")?.as_str()?.to_owned(); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 39 | let own = current_user_id.is_some_and(|user_id| user_id.as_str() == sender_id.as_str()); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 40 | let redacted = value |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 41 | .get("unsigned") |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 42 | .and_then(|unsigned| unsigned.get("redacted_because")) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 43 | .is_some(); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 44 | let mut media = Vec::new(); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 45 | let (content, reply_to, thread_root) = if redacted { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 46 | (TimelineContent::Redacted, None, None) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 47 | } else if event_type == "m.room.message" { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 48 | match value |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 49 | .get("content") |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 50 | .cloned() |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 51 | .and_then(|content| serde_json::from_value::<RoomMessageEventContent>(content).ok()) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 52 | { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 53 | Some(mut message) => { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 54 | let (reply_to, thread_root) = relation_refs(message.relates_to.as_ref()); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 55 | let relation_replacement = match &message.relates_to { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 56 | Some(Relation::Replacement(replacement)) => { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 57 | Some(replacement.new_content.clone()) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 58 | } |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 59 | _ => None, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 60 | }; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 61 | let bundled_replacement = value |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 62 | .get("unsigned") |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 63 | .and_then(|unsigned| unsigned.get("m.relations")) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 64 | .and_then(|relations| relations.get("m.replace")) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 65 | .and_then(|edit| { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 66 | let edit_sender = edit.get("sender")?.as_str()?; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 67 | if edit_sender != sender_id.as_str() { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 68 | return None; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 69 | } |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 70 | edit.get("content")?.get("m.new_content").cloned() |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 71 | }) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 72 | .and_then(|content| { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 73 | serde_json::from_value::<RoomMessageEventContentWithoutRelation>(content) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 74 | .ok() |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 75 | }); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 76 | let edited = relation_replacement.is_some() |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 77 | || bundled_replacement.is_some() |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 78 | || value |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 79 | .get("unsigned") |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 80 | .and_then(|unsigned| unsigned.get("macltok_edited")) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 81 | .and_then(Value::as_bool) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 82 | .unwrap_or(false); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 83 | if let Some(replacement) = relation_replacement.or(bundled_replacement) { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 84 | message.apply_replacement(replacement); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 85 | } |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 86 | let message_type = &message.msgtype; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 87 | media.extend(media_registration(identity, message_type)); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 88 | let previews = sender_previews(Some(identity), message_type); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 89 | media.extend( |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 90 | previews |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 91 | .iter() |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 92 | .filter_map(|(_, registration)| registration.clone()), |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 93 | ); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 94 | ( |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 95 | TimelineContent::Message(Message { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 96 | kind: super::message_kind(message_type), |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 97 | body: message.body().to_owned(), |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 98 | geo_uri: match message_type { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 99 | MessageType::Location(location) => Some(location.geo_uri.clone()), |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 100 | _ => None, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 101 | }, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 102 | formatted_body: formatted_body(message_type), |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 103 | mentions: message.mentions.as_ref().map(|mentions| MessageMentions { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 104 | user_ids: mentions.user_ids.iter().cloned().collect(), |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 105 | room: mentions.room, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 106 | }), |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 107 | edited, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 108 | media: super::media::media_from_message(identity, message_type), |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 109 | link_previews: previews.into_iter().map(|(preview, _)| preview).collect(), |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 110 | }), |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 111 | reply_to, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 112 | thread_root, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 113 | ) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 114 | } |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 115 | None => (TimelineContent::Other { event_type }, None, None), |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 116 | } |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 117 | } else if event_type == "m.room.encrypted" { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 118 | ( |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 119 | TimelineContent::UnableToDecrypt(UtdCause::Unknown), |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 120 | None, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 121 | None, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 122 | ) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 123 | } else if event_type == "m.sticker" { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 124 | match value |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 125 | .get("content") |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 126 | .cloned() |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 127 | .and_then(|content| serde_json::from_value::<StickerEventContent>(content).ok()) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 128 | { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 129 | Some(sticker) => { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 130 | let (reply_to, thread_root) = relation_refs(sticker.relates_to.as_ref()); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 131 | media.extend(media_sticker_registration(identity, &sticker)); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 132 | ( |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 133 | TimelineContent::Sticker { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 134 | body: sticker.body.clone(), |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 135 | media: media_descriptor_for_sticker(identity, &sticker), |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 136 | }, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 137 | reply_to, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 138 | thread_root, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 139 | ) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 140 | } |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 141 | None => (TimelineContent::Other { event_type }, None, None), |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 142 | } |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 143 | } else if event_type == "m.poll.start" || event_type == "org.matrix.msc3381.poll.start" { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 144 | let poll_content = value.get("content"); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 145 | let question = poll_content.and_then(|content| poll_question(content, &event_type)); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 146 | match question { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 147 | Some(question) => { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 148 | let (reply_to, thread_root) = |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 149 | poll_content.map(raw_relation_refs).unwrap_or_default(); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 150 | (TimelineContent::Poll { question }, reply_to, thread_root) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 151 | } |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 152 | None => (TimelineContent::Other { event_type }, None, None), |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 153 | } |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 154 | } else if let Some(state_key) = value.get("state_key").and_then(Value::as_str) { |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 155 | let content = state_content(value, &event_type).unwrap_or(TimelineContent::OtherState { |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 156 | event_type, |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 157 | state_key: state_key.to_owned(), |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 158 | }); |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 159 | (content, None, None) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 160 | } else { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 161 | (TimelineContent::Other { event_type }, None, None) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 162 | }; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 163 | let event = TimelineEvent { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 164 | item_id: TimelineItemId::new(identity.to_owned()), |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 165 | event_id: Some(event_id), |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 166 | transaction_id: None, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 167 | sender: Sender { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 168 | user_id: sender_id, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 169 | display_name: None, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 170 | avatar: None, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 171 | name_ambiguous: false, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 172 | }, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 173 | timestamp_ms, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 174 | own, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 175 | send_state: SendState::Sent, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 176 | content, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 177 | reply_to: reply_to.map(|event_id| ReplyReference { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 178 | event_id, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 179 | details: ReplyDetails::Pending, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 180 | }), |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 181 | thread_root, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 182 | thread_summary: None, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 183 | reactions: Vec::new(), |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 184 | read_by: Vec::new(), |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 185 | editable: false, |
913832a327a8 | atagen | 2026-10-25 09:33 UTC | 186 | highlight: false, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 187 | }; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 188 | Some((TimelineItem::Event(Box::new(event)), media)) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 189 | } |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 190 | |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 191 | fn relation_refs<C>( |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 192 | relation: Option<&Relation<C>>, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 193 | ) -> (Option<OwnedEventId>, Option<OwnedEventId>) { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 194 | match relation { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 195 | Some(Relation::Reply(reply)) => (Some(reply.in_reply_to.event_id.clone()), None), |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 196 | Some(Relation::Thread(thread)) => ( |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 197 | thread |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 198 | .in_reply_to |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 199 | .as_ref() |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 200 | .map(|reply| reply.event_id.clone()), |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 201 | Some(thread.event_id.clone()), |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 202 | ), |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 203 | _ => (None, None), |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 204 | } |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 205 | } |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 206 | |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 207 | fn raw_relation_refs(content: &Value) -> (Option<OwnedEventId>, Option<OwnedEventId>) { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 208 | let Some(relation) = content.get("m.relates_to") else { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 209 | return (None, None); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 210 | }; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 211 | let reply_to = relation |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 212 | .get("m.in_reply_to") |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 213 | .and_then(|reply| reply.get("event_id")) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 214 | .and_then(Value::as_str) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 215 | .and_then(|event_id| event_id.parse().ok()); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 216 | let thread_root = (relation.get("rel_type").and_then(Value::as_str) == Some("m.thread")) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 217 | .then(|| relation.get("event_id").and_then(Value::as_str)) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 218 | .flatten() |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 219 | .and_then(|event_id| event_id.parse().ok()); |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 220 | (reply_to, thread_root) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 221 | } |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 222 | |
c9effeac326a | atagen | 2026-10-25 10:32 UTC | 223 | pub(crate) fn poll_question(content: &Value, event_type: &str) -> Option<String> { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 224 | let typed_question = match event_type { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 225 | "m.poll.start" => serde_json::from_value::<PollStartEventContent>(content.clone()) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 226 | .ok() |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 227 | .and_then(|poll| poll.poll.question.text.find_plain().map(str::to_owned)), |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 228 | "org.matrix.msc3381.poll.start" => { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 229 | serde_json::from_value::<UnstablePollStartEventContent>(content.clone()) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 230 | .ok() |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 231 | .map(|poll| poll.poll_start().question.text.clone()) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 232 | } |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 233 | _ => None, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 234 | }; |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 235 | typed_question |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 236 | .or_else(|| { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 237 | content |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 238 | .get("m.poll") |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 239 | .and_then(|poll| poll.get("question")) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 240 | .and_then(|question| question.get("m.text")) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 241 | .and_then(generic_text) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 242 | }) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 243 | .or_else(|| { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 244 | content |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 245 | .get("org.matrix.msc3381.poll.start") |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 246 | .and_then(|poll| poll.get("question")) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 247 | .and_then(|question| question.get("org.matrix.msc1767.text")) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 248 | .and_then(generic_text) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 249 | }) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 250 | .or_else(|| { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 251 | ["org.matrix.msc1767.text", "m.text", "question", "body"] |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 252 | .iter() |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 253 | .find_map(|key| content.get(*key).and_then(generic_text)) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 254 | }) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 255 | } |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 256 | |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 257 | fn generic_text(value: &Value) -> Option<String> { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 258 | match value { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 259 | Value::String(text) => Some(text.clone()), |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 260 | Value::Array(representations) => representations |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 261 | .iter() |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 262 | .find(|representation| { |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 263 | representation.get("mimetype").and_then(Value::as_str) == Some("text/plain") |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 264 | }) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 265 | .or_else(|| representations.first()) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 266 | .and_then(|representation| representation.get("body")) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 267 | .and_then(Value::as_str) |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 268 | .map(str::to_owned), |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 269 | Value::Object(_) => value.get("body").and_then(Value::as_str).map(str::to_owned), |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 270 | _ => None, |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 271 | } |
cbb715e11963 | atagen | 2026-10-24 23:24 UTC | 272 | } |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 273 | |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 274 | /// the typed state rows the live timeline gives these events |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 275 | fn state_content(value: &Value, event_type: &str) -> Option<TimelineContent> { |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 276 | let content = value.get("content")?; |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 277 | let text = |key: &str| { |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 278 | content |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 279 | .get(key) |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 280 | .and_then(Value::as_str) |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 281 | .filter(|text| !text.is_empty()) |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 282 | .map(str::to_owned) |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 283 | }; |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 284 | Some(match event_type { |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 285 | "m.room.member" => { |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 286 | let event: OriginalSyncRoomMemberEvent = serde_json::from_value(value.clone()).ok()?; |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 287 | let user_id = event.state_key.clone(); |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 288 | match event.membership_change() { |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 289 | RumaMembershipChange::ProfileChanged { |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 290 | displayname_change, |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 291 | avatar_url_change, |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 292 | } => TimelineContent::Profile { |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 293 | user_id, |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 294 | display_name: displayname_change.map(|change| Change { |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 295 | old: change.old.map(str::to_owned), |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 296 | new: change.new.map(str::to_owned), |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 297 | }), |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 298 | avatar: avatar_url_change.map(|change| Change { |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 299 | old: change.old.map(MediaId::plain), |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 300 | new: change.new.map(MediaId::plain), |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 301 | }), |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 302 | }, |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 303 | change => TimelineContent::Membership { |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 304 | user_id, |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 305 | display_name: event.content.displayname.clone(), |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 306 | change: membership_change(&change), |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 307 | }, |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 308 | } |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 309 | } |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 310 | "m.room.name" => TimelineContent::RoomName(text("name")), |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 311 | "m.room.topic" => TimelineContent::RoomTopic(text("topic")), |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 312 | "m.room.avatar" => TimelineContent::RoomAvatar(text("url").map(|url| MediaId::plain(&url))), |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 313 | "m.room.encryption" => TimelineContent::EncryptionEnabled, |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 314 | "m.room.create" => TimelineContent::RoomCreated, |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 315 | "m.room.tombstone" => TimelineContent::RoomUpgraded { |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 316 | successor: serde_json::from_value(content.get("replacement_room")?.clone()).ok()?, |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 317 | }, |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 318 | _ => return None, |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 319 | }) |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 320 | } |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 321 | |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 322 | const fn membership_change(change: &RumaMembershipChange<'_>) -> MembershipChange { |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 323 | match change { |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 324 | RumaMembershipChange::Joined => MembershipChange::Joined, |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 325 | RumaMembershipChange::Left => MembershipChange::Left, |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 326 | RumaMembershipChange::Banned => MembershipChange::Banned, |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 327 | RumaMembershipChange::Unbanned => MembershipChange::Unbanned, |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 328 | RumaMembershipChange::Kicked => MembershipChange::Kicked, |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 329 | RumaMembershipChange::KickedAndBanned => MembershipChange::KickedAndBanned, |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 330 | RumaMembershipChange::Invited => MembershipChange::Invited, |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 331 | RumaMembershipChange::InvitationAccepted => MembershipChange::InvitationAccepted, |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 332 | RumaMembershipChange::InvitationRejected => MembershipChange::InvitationRejected, |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 333 | RumaMembershipChange::InvitationRevoked => MembershipChange::InvitationRevoked, |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 334 | RumaMembershipChange::Knocked => MembershipChange::Knocked, |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 335 | RumaMembershipChange::KnockAccepted => MembershipChange::KnockAccepted, |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 336 | RumaMembershipChange::KnockRetracted => MembershipChange::KnockRetracted, |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 337 | RumaMembershipChange::KnockDenied => MembershipChange::KnockDenied, |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 338 | _ => MembershipChange::Other, |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 339 | } |
068bfa92dd36 | atagen | 2026-10-25 13:53 UTC | 340 | } |