|
|
|
@ -1,11 +1,13 @@ |
|
|
|
use crate::{utils, Result}; |
|
|
|
use crate::{utils, Error, Result}; |
|
|
|
use ruma_events::{collections::only::Event as EduEvent, EventJson}; |
|
|
|
use ruma_events::{collections::only::Event as EduEvent, EventJson}; |
|
|
|
use ruma_identifiers::{RoomId, UserId}; |
|
|
|
use ruma_identifiers::{RoomId, UserId}; |
|
|
|
|
|
|
|
use std::convert::TryFrom; |
|
|
|
|
|
|
|
|
|
|
|
pub struct RoomEdus { |
|
|
|
pub struct RoomEdus { |
|
|
|
pub(in super::super) roomuserid_lastread: sled::Tree, // RoomUserId = Room + User
|
|
|
|
pub(in super::super) roomuserid_lastread: sled::Tree, // RoomUserId = Room + User
|
|
|
|
pub(in super::super) roomlatestid_roomlatest: sled::Tree, // Read Receipts, RoomLatestId = RoomId + Count + UserId
|
|
|
|
pub(in super::super) roomlatestid_roomlatest: sled::Tree, // Read Receipts, RoomLatestId = RoomId + Count + UserId
|
|
|
|
pub(in super::super) roomactiveid_roomactive: sled::Tree, // Typing, RoomActiveId = RoomId + TimeoutTime + Count
|
|
|
|
pub(in super::super) roomactiveid_userid: sled::Tree, // Typing, RoomActiveId = RoomId + TimeoutTime + Count
|
|
|
|
|
|
|
|
pub(in super::super) roomid_lastroomactiveupdate: sled::Tree, // LastRoomActiveUpdate = Count
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
impl RoomEdus { |
|
|
|
impl RoomEdus { |
|
|
|
@ -79,10 +81,11 @@ impl RoomEdus { |
|
|
|
.map(|(_, v)| Ok(serde_json::from_slice(&v)?))) |
|
|
|
.map(|(_, v)| Ok(serde_json::from_slice(&v)?))) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Adds an event that will be saved until the `timeout` timestamp (e.g. typing notifications).
|
|
|
|
/// Sets a user as typing until the timeout timestamp is reached or roomactive_remove is
|
|
|
|
|
|
|
|
/// called.
|
|
|
|
pub fn roomactive_add( |
|
|
|
pub fn roomactive_add( |
|
|
|
&self, |
|
|
|
&self, |
|
|
|
event: EduEvent, |
|
|
|
user_id: &UserId, |
|
|
|
room_id: &RoomId, |
|
|
|
room_id: &RoomId, |
|
|
|
timeout: u64, |
|
|
|
timeout: u64, |
|
|
|
globals: &super::super::globals::Globals, |
|
|
|
globals: &super::super::globals::Globals, |
|
|
|
@ -90,71 +93,134 @@ impl RoomEdus { |
|
|
|
let mut prefix = room_id.to_string().as_bytes().to_vec(); |
|
|
|
let mut prefix = room_id.to_string().as_bytes().to_vec(); |
|
|
|
prefix.push(0xff); |
|
|
|
prefix.push(0xff); |
|
|
|
|
|
|
|
|
|
|
|
// Cleanup all outdated edus before inserting a new one
|
|
|
|
let count = globals.next_count()?.to_be_bytes(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let mut room_active_id = prefix; |
|
|
|
|
|
|
|
room_active_id.extend_from_slice(&timeout.to_be_bytes()); |
|
|
|
|
|
|
|
room_active_id.push(0xff); |
|
|
|
|
|
|
|
room_active_id.extend_from_slice(&count); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.roomactiveid_userid |
|
|
|
|
|
|
|
.insert(&room_active_id, &*user_id.to_string().as_bytes())?; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.roomid_lastroomactiveupdate |
|
|
|
|
|
|
|
.insert(&room_id.to_string().as_bytes(), &count)?; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Ok(()) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Removes a user from typing before the timeout is reached.
|
|
|
|
|
|
|
|
pub fn roomactive_remove( |
|
|
|
|
|
|
|
&self, |
|
|
|
|
|
|
|
user_id: &UserId, |
|
|
|
|
|
|
|
room_id: &RoomId, |
|
|
|
|
|
|
|
globals: &super::super::globals::Globals, |
|
|
|
|
|
|
|
) -> Result<()> { |
|
|
|
|
|
|
|
let mut prefix = room_id.to_string().as_bytes().to_vec(); |
|
|
|
|
|
|
|
prefix.push(0xff); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let user_id = user_id.to_string(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let mut found_outdated = false; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Maybe there are multiple ones from calling roomactive_add multiple times
|
|
|
|
for outdated_edu in self |
|
|
|
for outdated_edu in self |
|
|
|
.roomactiveid_roomactive |
|
|
|
.roomactiveid_userid |
|
|
|
.scan_prefix(&prefix) |
|
|
|
.scan_prefix(&prefix) |
|
|
|
.keys() |
|
|
|
|
|
|
|
.filter_map(|r| r.ok()) |
|
|
|
.filter_map(|r| r.ok()) |
|
|
|
.take_while(|k| { |
|
|
|
.filter(|(_, v)| v == user_id.as_bytes()) |
|
|
|
utils::u64_from_bytes( |
|
|
|
|
|
|
|
k.split(|&c| c == 0xff) |
|
|
|
|
|
|
|
.nth(1) |
|
|
|
|
|
|
|
.expect("roomactive has valid timestamp and delimiters"), |
|
|
|
|
|
|
|
) < utils::millis_since_unix_epoch() |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
{ |
|
|
|
{ |
|
|
|
// This is an outdated edu (time > timestamp)
|
|
|
|
self.roomactiveid_userid.remove(outdated_edu.0)?; |
|
|
|
self.roomlatestid_roomlatest.remove(outdated_edu)?; |
|
|
|
found_outdated = true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
let mut room_active_id = prefix; |
|
|
|
if found_outdated { |
|
|
|
room_active_id.extend_from_slice(&timeout.to_be_bytes()); |
|
|
|
self.roomid_lastroomactiveupdate.insert( |
|
|
|
room_active_id.push(0xff); |
|
|
|
&room_id.to_string().as_bytes(), |
|
|
|
room_active_id.extend_from_slice(&globals.next_count()?.to_be_bytes()); |
|
|
|
&globals.next_count()?.to_be_bytes(), |
|
|
|
|
|
|
|
)?; |
|
|
|
self.roomactiveid_roomactive |
|
|
|
} |
|
|
|
.insert(room_active_id, &*serde_json::to_string(&event)?)?; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Ok(()) |
|
|
|
Ok(()) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Removes an active event manually (before the timeout is reached).
|
|
|
|
/// Makes sure that typing events with old timestamps get removed.
|
|
|
|
pub fn roomactive_remove(&self, event: EduEvent, room_id: &RoomId) -> Result<()> { |
|
|
|
fn roomactives_maintain( |
|
|
|
|
|
|
|
&self, |
|
|
|
|
|
|
|
room_id: &RoomId, |
|
|
|
|
|
|
|
globals: &super::super::globals::Globals, |
|
|
|
|
|
|
|
) -> Result<()> { |
|
|
|
let mut prefix = room_id.to_string().as_bytes().to_vec(); |
|
|
|
let mut prefix = room_id.to_string().as_bytes().to_vec(); |
|
|
|
prefix.push(0xff); |
|
|
|
prefix.push(0xff); |
|
|
|
|
|
|
|
|
|
|
|
let json = serde_json::to_string(&event)?; |
|
|
|
let current_timestamp = utils::millis_since_unix_epoch(); |
|
|
|
|
|
|
|
|
|
|
|
// Remove outdated entries
|
|
|
|
let mut found_outdated = false; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Find all outdated edus before inserting a new one
|
|
|
|
for outdated_edu in self |
|
|
|
for outdated_edu in self |
|
|
|
.roomactiveid_roomactive |
|
|
|
.roomactiveid_userid |
|
|
|
.scan_prefix(&prefix) |
|
|
|
.scan_prefix(&prefix) |
|
|
|
|
|
|
|
.keys() |
|
|
|
.filter_map(|r| r.ok()) |
|
|
|
.filter_map(|r| r.ok()) |
|
|
|
.filter(|(_, v)| v == json.as_bytes()) |
|
|
|
.take_while(|k| { |
|
|
|
|
|
|
|
utils::u64_from_bytes( |
|
|
|
|
|
|
|
k.split(|&c| c == 0xff) |
|
|
|
|
|
|
|
.nth(1) |
|
|
|
|
|
|
|
.expect("roomactive has valid timestamp and delimiters"), |
|
|
|
|
|
|
|
) < current_timestamp |
|
|
|
|
|
|
|
}) |
|
|
|
{ |
|
|
|
{ |
|
|
|
self.roomactiveid_roomactive.remove(outdated_edu.0)?; |
|
|
|
// This is an outdated edu (time > timestamp)
|
|
|
|
|
|
|
|
self.roomlatestid_roomlatest.remove(outdated_edu)?; |
|
|
|
|
|
|
|
found_outdated = true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if found_outdated { |
|
|
|
|
|
|
|
self.roomid_lastroomactiveupdate.insert( |
|
|
|
|
|
|
|
&room_id.to_string().as_bytes(), |
|
|
|
|
|
|
|
&globals.next_count()?.to_be_bytes(), |
|
|
|
|
|
|
|
)?; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
Ok(()) |
|
|
|
Ok(()) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Returns an iterator over all active events (e.g. typing notifications).
|
|
|
|
/// Returns an iterator over all active events (e.g. typing notifications).
|
|
|
|
pub fn roomactives_all( |
|
|
|
pub fn last_roomactive_update( |
|
|
|
&self, |
|
|
|
&self, |
|
|
|
room_id: &RoomId, |
|
|
|
room_id: &RoomId, |
|
|
|
) -> impl Iterator<Item = Result<EventJson<EduEvent>>> { |
|
|
|
globals: &super::super::globals::Globals, |
|
|
|
|
|
|
|
) -> Result<u64> { |
|
|
|
|
|
|
|
self.roomactives_maintain(room_id, globals)?; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Ok(self |
|
|
|
|
|
|
|
.roomid_lastroomactiveupdate |
|
|
|
|
|
|
|
.get(&room_id.to_string().as_bytes())? |
|
|
|
|
|
|
|
.map(|bytes| utils::u64_from_bytes(&bytes)) |
|
|
|
|
|
|
|
.unwrap_or(0)) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Returns an iterator over all active events (e.g. typing notifications).
|
|
|
|
|
|
|
|
pub fn roomactives_all(&self, room_id: &RoomId) -> Result<ruma_events::typing::TypingEvent> { |
|
|
|
let mut prefix = room_id.to_string().as_bytes().to_vec(); |
|
|
|
let mut prefix = room_id.to_string().as_bytes().to_vec(); |
|
|
|
prefix.push(0xff); |
|
|
|
prefix.push(0xff); |
|
|
|
|
|
|
|
|
|
|
|
let mut first_active_edu = prefix.clone(); |
|
|
|
let mut user_ids = Vec::new(); |
|
|
|
first_active_edu.extend_from_slice(&utils::millis_since_unix_epoch().to_be_bytes()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.roomactiveid_roomactive |
|
|
|
for user_id in self |
|
|
|
.range(first_active_edu..) |
|
|
|
.roomactiveid_userid |
|
|
|
.filter_map(|r| r.ok()) |
|
|
|
.scan_prefix(prefix) |
|
|
|
.take_while(move |(k, _)| k.starts_with(&prefix)) |
|
|
|
.values() |
|
|
|
.map(|(_, v)| Ok(serde_json::from_slice(&v)?)) |
|
|
|
.map(|user_id| Ok::<_, Error>(UserId::try_from(utils::string_from_bytes(&user_id?)?)?)) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
user_ids.push(user_id?); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Ok(ruma_events::typing::TypingEvent { |
|
|
|
|
|
|
|
content: ruma_events::typing::TypingEventContent { user_ids }, |
|
|
|
|
|
|
|
room_id: None, // Can be inferred
|
|
|
|
|
|
|
|
}) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Sets a private read marker at `count`.
|
|
|
|
/// Sets a private read marker at `count`.
|
|
|
|
|