From f9d10e8f41d636672198f0a1ceabf22c714a2199 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jakub=20Kub=C3=ADk?= <jakub.kubik.it@protonmail.com>
Date: Mon, 21 Nov 2022 21:24:37 +0100
Subject: [PATCH] feat(presence): start work on cleanup task

---
 src/config/mod.rs                             | 17 +++++++++--
 src/database/key_value/rooms/edus/presence.rs | 28 +++++++++++++++----
 src/service/globals/mod.rs                    |  8 ++++++
 src/service/rooms/edus/presence/data.rs       |  2 ++
 src/service/rooms/edus/presence/mod.rs        |  4 +++
 5 files changed, 52 insertions(+), 7 deletions(-)

diff --git a/src/config/mod.rs b/src/config/mod.rs
index b4dbdfb4..7ea551ef 100644
--- a/src/config/mod.rs
+++ b/src/config/mod.rs
@@ -81,6 +81,11 @@ pub struct Config {
     #[serde(default = "default_presence_offline_timeout")]
     pub presence_offline_timeout: u64,
 
+    #[serde(default = "default_presence_cleanup_period")]
+    pub presence_cleanup_period: u64,
+    #[serde(default = "default_presence_cleanup_limit")]
+    pub presence_cleanup_limit: u64,
+
     #[serde(flatten)]
     pub catchall: BTreeMap<String, IgnoredAny>,
 }
@@ -263,11 +268,19 @@ fn default_turn_ttl() -> u64 {
 }
 
 fn default_presence_idle_timeout() -> u64 {
-    1 * 60 * 1000
+    1 * 60
 }
 
 fn default_presence_offline_timeout() -> u64 {
-    15 * 60 * 1000
+    30 * 60
+}
+
+fn default_presence_cleanup_period() -> u64 {
+    24 * 60 * 60
+}
+
+fn default_presence_cleanup_limit() -> u64 {
+    24 * 60 * 60
 }
 
 // I know, it's a great name
diff --git a/src/database/key_value/rooms/edus/presence.rs b/src/database/key_value/rooms/edus/presence.rs
index e23370a0..c2348492 100644
--- a/src/database/key_value/rooms/edus/presence.rs
+++ b/src/database/key_value/rooms/edus/presence.rs
@@ -220,11 +220,13 @@ impl service::rooms::edus::presence::Data for KeyValueDatabase {
     ) -> Result<()> {
         let mut timers = FuturesUnordered::new();
         let mut timers_timestamp: HashMap<OwnedUserId, u64> = HashMap::new();
+        let idle_timeout = Duration::from_secs(services().globals.presence_idle_timeout());
+        let offline_timeout = Duration::from_secs(services().globals.presence_offline_timeout());
 
-        // TODO: Get rid of this hack
+        // TODO: Get rid of this hack (hinting correct types to rustc)
         timers.push(create_presence_timer(
-            Duration::from_secs(60),
-            user_id!("@test:test.com").to_owned(),
+            Duration::from_secs(1),
+            UserId::parse_with_server_name("conduit", services().globals.server_name()).expect("Conduit user always exists")
         ));
 
         tokio::spawn(async move {
@@ -260,6 +262,7 @@ impl service::rooms::edus::presence::Data for KeyValueDatabase {
                     }
                     Some(user_id) = timer_receiver.recv() => {
                         let now = millis_since_unix_epoch();
+                        // Do not create timers if we added timers recently
                         let should_send = match timers_timestamp.entry(user_id.to_owned()) {
                             Entry::Occupied(mut entry) => {
                                 if now - entry.get() > 15 * 1000 {
@@ -280,10 +283,10 @@ impl service::rooms::edus::presence::Data for KeyValueDatabase {
                         }
 
                         // Idle timeout
-                        timers.push(create_presence_timer(Duration::from_secs(60), user_id.clone()));
+                        timers.push(create_presence_timer(idle_timeout, user_id.clone()));
 
                         // Offline timeout
-                        timers.push(create_presence_timer(Duration::from_secs(60*15) , user_id.clone()));
+                        timers.push(create_presence_timer(offline_timeout, user_id.clone()));
 
                         info!("Added timers for user '{}' ({})", user_id, timers.len());
                     }
@@ -293,6 +296,21 @@ impl service::rooms::edus::presence::Data for KeyValueDatabase {
 
         Ok(())
     }
+
+    fn presence_cleanup(&self) -> Result<()> {
+        let period = Duration::from_secs(services().globals.presence_cleanup_period());
+        let age_limit = Duration::from_secs(services().globals.presence_cleanup_limit());
+
+        tokio::spawn(async move {
+            loop {
+                // TODO: Cleanup
+
+                sleep(period).await;
+            }
+        });
+
+        Ok(())
+    }
 }
 
 async fn create_presence_timer(duration: Duration, user_id: OwnedUserId) -> OwnedUserId {
diff --git a/src/service/globals/mod.rs b/src/service/globals/mod.rs
index 94e3fb97..aa9e832d 100644
--- a/src/service/globals/mod.rs
+++ b/src/service/globals/mod.rs
@@ -294,6 +294,14 @@ impl Service {
         self.config.presence_offline_timeout
     }
 
+    pub fn presence_cleanup_period(&self) -> u64 {
+        self.config.presence_cleanup_period
+    }
+
+    pub fn presence_cleanup_limit(&self) -> u64 {
+        self.config.presence_cleanup_limit
+    }
+
     pub fn supported_room_versions(&self) -> Vec<RoomVersionId> {
         let mut room_versions: Vec<RoomVersionId> = vec![];
         room_versions.extend(self.stable_room_versions.clone());
diff --git a/src/service/rooms/edus/presence/data.rs b/src/service/rooms/edus/presence/data.rs
index 02c93714..138258cd 100644
--- a/src/service/rooms/edus/presence/data.rs
+++ b/src/service/rooms/edus/presence/data.rs
@@ -42,4 +42,6 @@ pub trait Data: Send + Sync {
 
     fn presence_maintain(&self, timer_receiver: mpsc::UnboundedReceiver<OwnedUserId>)
         -> Result<()>;
+
+    fn presence_cleanup(&self) -> Result<()>;
 }
diff --git a/src/service/rooms/edus/presence/mod.rs b/src/service/rooms/edus/presence/mod.rs
index 8d3e46aa..7d2520d3 100644
--- a/src/service/rooms/edus/presence/mod.rs
+++ b/src/service/rooms/edus/presence/mod.rs
@@ -98,6 +98,10 @@ impl Service {
         self.db.presence_maintain(timer_receiver)
     }
 
+    fn presence_cleanup(&self) -> Result<()> {
+        self.db.presence_cleanup()
+    }
+
     /// Spawns a timer for the user used by the maintenance task
     fn spawn_timer(&self, user_id: &UserId) -> Result<()> {
         self.timer_sender