Add update for device last_seen_ts. (closes #40)

Piggyback on presence ping for controlled device seen updates.

Signed-off-by: Jason Volk <jason@zemos.net>
This commit is contained in:
Jason Volk
2025-11-01 21:37:13 +00:00
parent 240b498489
commit 8b1de3d8db
8 changed files with 82 additions and 19 deletions

View File

@@ -49,7 +49,7 @@ pub(crate) async fn set_displayname_route(
// Presence update
services
.presence
.maybe_ping_presence(&body.user_id, &PresenceState::Online)
.maybe_ping_presence(&body.user_id, body.sender_device.as_deref(), &PresenceState::Online)
.await?;
Ok(set_display_name::v3::Response {})
@@ -149,7 +149,7 @@ pub(crate) async fn set_avatar_url_route(
// Presence update
services
.presence
.maybe_ping_presence(&body.user_id, &PresenceState::Online)
.maybe_ping_presence(&body.user_id, body.sender_device.as_deref(), &PresenceState::Online)
.await
.ok();

View File

@@ -93,7 +93,11 @@ pub(crate) async fn set_read_marker_route(
services
.presence
.maybe_ping_presence(sender_user, &ruma::presence::PresenceState::Online)
.maybe_ping_presence(
sender_user,
body.sender_device.as_deref(),
&ruma::presence::PresenceState::Online,
)
.await
.ok();
}
@@ -165,7 +169,11 @@ pub(crate) async fn create_receipt_route(
services
.presence
.maybe_ping_presence(sender_user, &ruma::presence::PresenceState::Online)
.maybe_ping_presence(
sender_user,
body.sender_device.as_deref(),
&ruma::presence::PresenceState::Online,
)
.await
.ok();
},

View File

@@ -130,7 +130,7 @@ pub(crate) async fn sync_events_route(
let ping_presence = services
.presence
.maybe_ping_presence(sender_user, &body.body.set_presence)
.maybe_ping_presence(sender_user, Some(sender_device), &body.body.set_presence)
.inspect_err(inspect_log)
.ok();

View File

@@ -108,7 +108,7 @@ pub(crate) async fn sync_events_v5_route(
let conn = conn_val.lock();
let ping_presence = services
.presence
.maybe_ping_presence(sender_user, &request.set_presence)
.maybe_ping_presence(sender_user, Some(sender_device), &request.set_presence)
.inspect_err(inspect_log)
.ok();

View File

@@ -66,7 +66,11 @@ pub(crate) async fn create_typing_event_route(
// ping presence
services
.presence
.maybe_ping_presence(&body.user_id, &ruma::presence::PresenceState::Online)
.maybe_ping_presence(
&body.user_id,
body.sender_device.as_deref(),
&ruma::presence::PresenceState::Online,
)
.await?;
Ok(create_typing_event::v3::Response {})

View File

@@ -76,7 +76,7 @@ pub(crate) async fn delete_timezone_key_route(
// Presence update
services
.presence
.maybe_ping_presence(&body.user_id, &PresenceState::Online)
.maybe_ping_presence(&body.user_id, body.sender_device.as_deref(), &PresenceState::Online)
.await?;
Ok(delete_timezone_key::unstable::Response {})
@@ -104,7 +104,7 @@ pub(crate) async fn set_timezone_key_route(
// Presence update
services
.presence
.maybe_ping_presence(&body.user_id, &PresenceState::Online)
.maybe_ping_presence(&body.user_id, body.sender_device.as_deref(), &PresenceState::Online)
.await?;
Ok(set_timezone_key::unstable::Response {})
@@ -170,7 +170,7 @@ pub(crate) async fn set_profile_field_route(
// Presence update
services
.presence
.maybe_ping_presence(&body.user_id, &PresenceState::Online)
.maybe_ping_presence(&body.user_id, body.sender_device.as_deref(), &PresenceState::Online)
.await?;
Ok(set_profile_field::v3::Response {})
@@ -224,7 +224,7 @@ pub(crate) async fn delete_profile_field_route(
// Presence update
services
.presence
.maybe_ping_presence(&body.user_id, &PresenceState::Online)
.maybe_ping_presence(&body.user_id, body.sender_device.as_deref(), &PresenceState::Online)
.await?;
Ok(delete_profile_field::v3::Response {})

View File

@@ -4,11 +4,20 @@ mod presence;
use std::{collections::HashMap, sync::Arc, time::Duration};
use async_trait::async_trait;
use futures::{Stream, StreamExt, TryFutureExt, stream::FuturesUnordered};
use futures::{
Stream, StreamExt, TryFutureExt,
future::{OptionFuture, try_join},
stream::FuturesUnordered,
};
use loole::{Receiver, Sender};
use ruma::{OwnedUserId, UInt, UserId, events::presence::PresenceEvent, presence::PresenceState};
use ruma::{
DeviceId, OwnedUserId, UInt, UserId, events::presence::PresenceEvent, presence::PresenceState,
};
use tokio::{sync::RwLock, time::sleep};
use tuwunel_core::{Error, Result, checked, debug, debug_warn, error, result::LogErr, trace};
use tuwunel_core::{
Error, Result, checked, debug, debug_warn, error, result::LogErr, trace,
utils::future::OptionExt,
};
use self::{data::Data, presence::Presence};
@@ -46,7 +55,7 @@ impl crate::Service for Service {
// online
self.unset_all_presence().await;
_ = self
.maybe_ping_presence(&self.services.globals.server_user, &PresenceState::Online)
.maybe_ping_presence(&self.services.globals.server_user, None, &PresenceState::Online)
.await;
let receiver = self.timer_channel.1.clone();
@@ -73,7 +82,11 @@ impl crate::Service for Service {
async fn interrupt(&self) {
// set the server user as offline
_ = self
.maybe_ping_presence(&self.services.globals.server_user, &PresenceState::Offline)
.maybe_ping_presence(
&self.services.globals.server_user,
None,
&PresenceState::Offline,
)
.await;
let (timer_sender, _) = &self.timer_channel;
@@ -118,11 +131,12 @@ impl Service {
.await
}
/// Pings the presence of the given user in the given room, setting the
/// specified state.
/// Pings the presence of the given user, setting the specified state. When
/// device_id is supplied.
pub async fn maybe_ping_presence(
&self,
user_id: &UserId,
device_id: Option<&DeviceId>,
new_state: &PresenceState,
) -> Result {
const REFRESH_TIMEOUT: u64 = 60 * 1000;
@@ -150,6 +164,14 @@ impl Service {
return Ok(());
}
let update_device_seen: OptionFuture<_> = device_id
.map(|device_id| {
self.services
.users
.update_device_last_seen(user_id, device_id, None)
})
.into();
let status_msg = match last_presence {
| Ok((_, ref presence)) => presence.content.status_msg.clone(),
| Err(_) => Some(String::new()),
@@ -157,7 +179,16 @@ impl Service {
let last_active_ago = UInt::new(0);
let currently_active = *new_state == PresenceState::Online;
self.set_presence(user_id, new_state, Some(currently_active), last_active_ago, status_msg)
let set_presence = self.set_presence(
user_id,
new_state,
Some(currently_active),
last_active_ago,
status_msg,
);
try_join(set_presence, update_device_seen.unwrap_or(Ok(())))
.map_ok(|_| ())
.await
}

View File

@@ -362,6 +362,26 @@ pub async fn remove_to_device_events<Until>(
.await;
}
#[implement(super::Service)]
pub async fn update_device_last_seen(
&self,
user_id: &UserId,
device_id: &DeviceId,
last_seen: Option<MilliSecondsSinceUnixEpoch>,
) -> Result {
let mut device = self
.get_device_metadata(user_id, device_id)
.await?;
device
.last_seen_ts
.replace(last_seen.unwrap_or_else(MilliSecondsSinceUnixEpoch::now));
self.put_device_metadata(user_id, false, &device);
Ok(())
}
#[implement(super::Service)]
pub fn put_device_metadata(&self, user_id: &UserId, notify: bool, device: &Device) {
let key = (user_id, &device.device_id);