Files
continuwuity/src/service/presence/data.rs
T

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

174 lines
4.5 KiB
Rust
Raw Normal View History

2024-05-27 03:17:20 +00:00
use std::sync::Arc;
2024-08-08 17:18:30 +00:00
use conduit::{
debug_warn, utils,
utils::{stream::TryIgnore, ReadyExt},
Result,
};
use database::{Deserialized, Map};
use futures::Stream;
2024-04-01 20:48:40 -07:00
use ruma::{events::presence::PresenceEvent, presence::PresenceState, OwnedUserId, UInt, UserId};
2024-03-05 19:48:54 -05:00
use super::Presence;
use crate::{globals, users, Dep};
2022-09-06 23:15:09 +02:00
2024-05-27 03:17:20 +00:00
pub struct Data {
2024-06-28 22:51:39 +00:00
presenceid_presence: Arc<Map>,
userid_presenceid: Arc<Map>,
2024-07-18 06:37:47 +00:00
services: Services,
}
struct Services {
globals: Dep<globals::Service>,
users: Dep<users::Service>,
}
2024-05-26 21:29:19 +00:00
2024-05-27 03:17:20 +00:00
impl Data {
2024-07-18 06:37:47 +00:00
pub(super) fn new(args: &crate::Args<'_>) -> Self {
let db = &args.db;
2024-05-27 03:17:20 +00:00
Self {
2024-06-28 22:51:39 +00:00
presenceid_presence: db["presenceid_presence"].clone(),
userid_presenceid: db["userid_presenceid"].clone(),
2024-07-18 06:37:47 +00:00
services: Services {
globals: args.depend::<globals::Service>("globals"),
users: args.depend::<users::Service>("users"),
},
2024-05-27 03:17:20 +00:00
}
}
2024-08-08 17:18:30 +00:00
pub async fn get_presence(&self, user_id: &UserId) -> Result<(u64, PresenceEvent)> {
let count = self
.userid_presenceid
.qry(user_id)
.await
.deserialized::<u64>()?;
let key = presenceid_key(count, user_id);
let bytes = self.presenceid_presence.qry(&key).await?;
let event = Presence::from_json_bytes(&bytes)?
.to_presence_event(user_id, &self.services.users)
.await;
Ok((count, event))
2024-05-26 21:29:19 +00:00
}
2024-08-08 17:18:30 +00:00
pub(super) async fn set_presence(
2024-05-26 21:29:19 +00:00
&self, user_id: &UserId, presence_state: &PresenceState, currently_active: Option<bool>,
last_active_ago: Option<UInt>, status_msg: Option<String>,
) -> Result<()> {
2024-08-08 17:18:30 +00:00
let last_presence = self.get_presence(user_id).await;
2024-05-26 21:29:19 +00:00
let state_changed = match last_presence {
2024-08-08 17:18:30 +00:00
Err(_) => true,
Ok(ref presence) => presence.1.content.presence != *presence_state,
2024-05-26 21:29:19 +00:00
};
let status_msg_changed = match last_presence {
2024-08-08 17:18:30 +00:00
Err(_) => true,
Ok(ref last_presence) => {
let old_msg = last_presence
.1
.content
.status_msg
.clone()
.unwrap_or_default();
let new_msg = status_msg.clone().unwrap_or_default();
new_msg != old_msg
},
};
2024-05-26 21:29:19 +00:00
let now = utils::millis_since_unix_epoch();
let last_last_active_ts = match last_presence {
2024-08-08 17:18:30 +00:00
Err(_) => 0,
Ok((_, ref presence)) => now.saturating_sub(presence.content.last_active_ago.unwrap_or_default().into()),
2024-05-26 21:29:19 +00:00
};
let last_active_ts = match last_active_ago {
None => now,
Some(last_active_ago) => now.saturating_sub(last_active_ago.into()),
};
// TODO: tighten for state flicker?
if !status_msg_changed && !state_changed && last_active_ts < last_last_active_ts {
2024-08-08 17:18:30 +00:00
debug_warn!("presence spam {user_id:?} last_active_ts:{last_active_ts:?} < {last_last_active_ts:?}",);
2024-05-26 21:29:19 +00:00
return Ok(());
}
let status_msg = if status_msg.as_ref().is_some_and(String::is_empty) {
None
} else {
status_msg
};
let presence = Presence::new(
presence_state.to_owned(),
currently_active.unwrap_or(false),
last_active_ts,
status_msg,
);
2024-07-18 06:37:47 +00:00
let count = self.services.globals.next_count()?;
2024-05-26 21:29:19 +00:00
let key = presenceid_key(count, user_id);
self.presenceid_presence
2024-08-08 17:18:30 +00:00
.insert(&key, &presence.to_json_bytes()?);
2024-05-26 21:29:19 +00:00
self.userid_presenceid
2024-08-08 17:18:30 +00:00
.insert(user_id.as_bytes(), &count.to_be_bytes());
2024-05-26 21:29:19 +00:00
2024-08-08 17:18:30 +00:00
if let Ok((last_count, _)) = last_presence {
2024-05-26 21:29:19 +00:00
let key = presenceid_key(last_count, user_id);
2024-08-08 17:18:30 +00:00
self.presenceid_presence.remove(&key);
2024-05-26 21:29:19 +00:00
}
Ok(())
}
2024-08-08 17:18:30 +00:00
pub(super) async fn remove_presence(&self, user_id: &UserId) {
let Ok(count) = self
.userid_presenceid
.qry(user_id)
.await
.deserialized::<u64>()
else {
return;
};
2024-05-26 21:29:19 +00:00
2024-08-08 17:18:30 +00:00
let key = presenceid_key(count, user_id);
self.presenceid_presence.remove(&key);
self.userid_presenceid.remove(user_id.as_bytes());
2024-05-26 21:29:19 +00:00
}
2024-08-08 17:18:30 +00:00
pub fn presence_since(&self, since: u64) -> impl Stream<Item = (OwnedUserId, u64, Vec<u8>)> + Send + '_ {
self.presenceid_presence
.raw_stream()
.ignore_err()
.ready_filter_map(move |(key, presence_bytes)| {
let (count, user_id) = presenceid_parse(key).expect("invalid presenceid_parse");
(count > since).then(|| (user_id.to_owned(), count, presence_bytes.to_vec()))
})
2024-05-26 21:29:19 +00:00
}
}
#[inline]
fn presenceid_key(count: u64, user_id: &UserId) -> Vec<u8> {
[count.to_be_bytes().to_vec(), user_id.as_bytes().to_vec()].concat()
}
#[inline]
2024-06-30 18:20:39 +00:00
fn presenceid_parse(key: &[u8]) -> Result<(u64, &UserId)> {
2024-05-26 21:29:19 +00:00
let (count, user_id) = key.split_at(8);
2024-07-03 00:47:58 +00:00
let user_id = user_id_from_bytes(user_id)?;
2024-08-08 17:18:30 +00:00
let count = utils::u64_from_u8(count);
2024-05-26 21:29:19 +00:00
Ok((count, user_id))
}
2024-07-03 00:47:58 +00:00
/// Parses a `UserId` from bytes.
fn user_id_from_bytes(bytes: &[u8]) -> Result<&UserId> {
let str: &str = utils::str_from_bytes(bytes)?;
let user_id: &UserId = str.try_into()?;
Ok(user_id)
}