From 3ca3a8325707535fdbc98d681d5e4a47dc313c67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20D=E2=80=99Aquino?= Date: Sun, 4 Aug 2024 11:47:47 -0700 Subject: [PATCH] Add support to "only notifications from following" setting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Testing ------- PASS Devices: Mix of iPhone simulators and real devices notepush: This commit Damus: 4ea6c360e6e33747cb09ecf085049948ec1dadd1 (WIP change from GH issue #2360) Setup: - Account A with push notifications enabled, DM notifications enabled, and "only notifications from following enabled" - Account A follows B but not C Steps: 1. Send DM to A from B. Push notification appears 2. Send DM to A from C. Push notification does not appear Signed-off-by: Daniel D’Aquino Closes: https://github.com/damus-io/damus/issues/2360 --- src/notification_manager/mod.rs | 4 +- ...ute_manager.rs => nostr_network_helper.rs} | 65 ++++++++++++++++++- .../notification_manager.rs | 16 +++-- 3 files changed, 75 insertions(+), 10 deletions(-) rename src/notification_manager/{mute_manager.rs => nostr_network_helper.rs} (69%) diff --git a/src/notification_manager/mod.rs b/src/notification_manager/mod.rs index 653c48e..a3311a0 100644 --- a/src/notification_manager/mod.rs +++ b/src/notification_manager/mod.rs @@ -1,7 +1,7 @@ -pub mod mute_manager; +pub mod nostr_network_helper; mod nostr_event_extensions; pub mod notification_manager; -pub use mute_manager::MuteManager; +pub use nostr_network_helper::NostrNetworkHelper; use nostr_event_extensions::{ExtendedEvent, SqlStringConvertible}; pub use notification_manager::NotificationManager; diff --git a/src/notification_manager/mute_manager.rs b/src/notification_manager/nostr_network_helper.rs similarity index 69% rename from src/notification_manager/mute_manager.rs rename to src/notification_manager/nostr_network_helper.rs index 8b34b97..69ec645 100644 --- a/src/notification_manager/mute_manager.rs +++ b/src/notification_manager/nostr_network_helper.rs @@ -2,16 +2,16 @@ use super::ExtendedEvent; use nostr_sdk::prelude::*; use tokio::time::{timeout, Duration}; -pub struct MuteManager { +pub struct NostrNetworkHelper { client: Client, } -impl MuteManager { +impl NostrNetworkHelper { pub async fn new(relay_url: String) -> Result> { let client = Client::new(&Keys::generate()); client.add_relay(relay_url.clone()).await?; client.connect().await; - Ok(MuteManager { client }) + Ok(NostrNetworkHelper { client }) } pub async fn should_mute_notification_for_pubkey( @@ -125,4 +125,63 @@ impl MuteManager { self.client.unsubscribe(this_subscription_id).await; mute_list } + + pub async fn does_pubkey_follow_pubkey( + &self, + source_pubkey: &PublicKey, + target_pubkey: &PublicKey, + ) -> bool { + log::debug!( + "Checking if pubkey {:?} follows pubkey {:?}", + source_pubkey, + target_pubkey + ); + if let Some(contact_list) = self.get_contact_list(source_pubkey).await { + let tag_contents = contact_list.get_tags_content(TagKind::SingleLetter(SingleLetterTag { + character: Alphabet::P, + uppercase: false, + })); + return tag_contents.iter().any(|t| t == &target_pubkey.to_hex()); + } + false + } + + pub async fn get_contact_list(&self, pubkey: &PublicKey) -> Option { + let subscription_filter = Filter::new() + .kinds(vec![Kind::ContactList]) + .authors(vec![pubkey.clone()]) + .limit(1); + + let this_subscription_id = self + .client + .subscribe(Vec::from([subscription_filter]), None) + .await; + + let mut contact_list: Option = None; + let mut notifications = self.client.notifications(); + + let timeout_duration = Duration::from_secs(10); + while let Ok(result) = timeout(timeout_duration, notifications.recv()).await { + if let Ok(notification) = result { + if let RelayPoolNotification::Event { + subscription_id, + event, + .. + } = notification + { + if this_subscription_id == subscription_id && event.kind == Kind::ContactList { + contact_list = Some((*event).clone()); + break; + } + } + } + } + + if contact_list.is_none() { + log::debug!("Contact list not found for pubkey {:?}", pubkey); + } + + self.client.unsubscribe(this_subscription_id).await; + contact_list + } } diff --git a/src/notification_manager/notification_manager.rs b/src/notification_manager/notification_manager.rs index 4babe9b..f6f1112 100644 --- a/src/notification_manager/notification_manager.rs +++ b/src/notification_manager/notification_manager.rs @@ -13,7 +13,7 @@ use tokio::sync::Mutex; use std::collections::HashSet; use tokio; -use super::mute_manager::MuteManager; +use super::nostr_network_helper::NostrNetworkHelper; use super::ExtendedEvent; use super::SqlStringConvertible; use nostr::Event; @@ -28,7 +28,7 @@ pub struct NotificationManager { apns_topic: String, apns_client: Mutex, - mute_manager: Mutex, + nostr_network_helper: Mutex, } impl NotificationManager { @@ -43,7 +43,7 @@ impl NotificationManager { apns_environment: a2::client::Endpoint, apns_topic: String, ) -> Result> { - let mute_manager = MuteManager::new(relay_url.clone()).await?; + let mute_manager = NostrNetworkHelper::new(relay_url.clone()).await?; let connection = db.get()?; Self::setup_database(&connection)?; @@ -61,7 +61,7 @@ impl NotificationManager { apns_topic, apns_client: Mutex::new(client), db: Mutex::new(db), - mute_manager: Mutex::new(mute_manager), + nostr_network_helper: Mutex::new(mute_manager), }) } @@ -221,7 +221,7 @@ impl NotificationManager { let mut pubkeys_to_notify = HashSet::new(); for pubkey in relevant_pubkeys_yet_to_receive { let should_mute: bool = { - let mute_manager_mutex_guard = self.mute_manager.lock().await; + let mute_manager_mutex_guard = self.nostr_network_helper.lock().await; mute_manager_mutex_guard .should_mute_notification_for_pubkey(event, &pubkey) .await @@ -285,6 +285,12 @@ impl NotificationManager { event: &Event, ) -> Result> { let notification_preferences = self.get_user_notification_settings(pubkey, device_token).await?; + if notification_preferences.only_notifications_from_following_enabled { + let nostr_network_helper_mutex_guard = self.nostr_network_helper.lock().await; + if !nostr_network_helper_mutex_guard.does_pubkey_follow_pubkey(pubkey, &event.author()).await { + return Ok(false); + } + } match event.kind { Kind::TextNote => Ok(notification_preferences.mention_notifications_enabled), // TODO: Not 100% accurate Kind::EncryptedDirectMessage => Ok(notification_preferences.dm_notifications_enabled),