mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-25 18:25:19 +00:00
Add anti-spam protection for network availability notifications
- 10 minute cooldown between notifications - 5 minute hysteresis before resetting notification flag after network becomes empty - Prevents spam when peers briefly disconnect/reconnect - Emergency disconnect immediately resets all notification state
This commit is contained in:
@@ -82,6 +82,10 @@ class BluetoothMeshService: NSObject {
|
|||||||
private var announcedToPeers = Set<String>() // Track which peers we've announced to
|
private var announcedToPeers = Set<String>() // Track which peers we've announced to
|
||||||
private var announcedPeers = Set<String>() // Track peers who have already been announced
|
private var announcedPeers = Set<String>() // Track peers who have already been announced
|
||||||
private var hasNotifiedNetworkAvailable = false // Track if we've notified about network availability
|
private var hasNotifiedNetworkAvailable = false // Track if we've notified about network availability
|
||||||
|
private var lastNetworkNotificationTime: Date? // Track when we last sent a network notification
|
||||||
|
private var networkBecameEmptyTime: Date? // Track when the network became empty
|
||||||
|
private let networkNotificationCooldown: TimeInterval = 600 // 10 minutes between notifications
|
||||||
|
private let networkEmptyResetDelay: TimeInterval = 300 // 5 minutes before resetting notification flag
|
||||||
private var intentionalDisconnects = Set<String>() // Track peripherals we're disconnecting intentionally
|
private var intentionalDisconnects = Set<String>() // Track peripherals we're disconnecting intentionally
|
||||||
private var peerLastSeenTimestamps = LRUCache<String, Date>(maxSize: 100) // Bounded cache for peer timestamps
|
private var peerLastSeenTimestamps = LRUCache<String, Date>(maxSize: 100) // Bounded cache for peer timestamps
|
||||||
private var cleanupTimer: Timer? // Timer to clean up stale peers
|
private var cleanupTimer: Timer? // Timer to clean up stale peers
|
||||||
@@ -600,7 +604,8 @@ class BluetoothMeshService: NSObject {
|
|||||||
activePeers.removeAll()
|
activePeers.removeAll()
|
||||||
}
|
}
|
||||||
announcedPeers.removeAll()
|
announcedPeers.removeAll()
|
||||||
hasNotifiedNetworkAvailable = false
|
// For normal disconnect, respect the timing
|
||||||
|
networkBecameEmptyTime = Date()
|
||||||
|
|
||||||
// Clear announcement tracking
|
// Clear announcement tracking
|
||||||
announcedToPeers.removeAll()
|
announcedToPeers.removeAll()
|
||||||
@@ -1233,7 +1238,10 @@ class BluetoothMeshService: NSObject {
|
|||||||
peripheralRSSI.removeAll()
|
peripheralRSSI.removeAll()
|
||||||
announcedToPeers.removeAll()
|
announcedToPeers.removeAll()
|
||||||
announcedPeers.removeAll()
|
announcedPeers.removeAll()
|
||||||
|
// For emergency/panic, reset immediately
|
||||||
hasNotifiedNetworkAvailable = false
|
hasNotifiedNetworkAvailable = false
|
||||||
|
networkBecameEmptyTime = nil
|
||||||
|
lastNetworkNotificationTime = nil
|
||||||
processedMessages.removeAll()
|
processedMessages.removeAll()
|
||||||
incomingFragments.removeAll()
|
incomingFragments.removeAll()
|
||||||
fragmentMetadata.removeAll()
|
fragmentMetadata.removeAll()
|
||||||
@@ -1335,10 +1343,27 @@ class BluetoothMeshService: NSObject {
|
|||||||
if !peersToRemove.isEmpty {
|
if !peersToRemove.isEmpty {
|
||||||
notifyPeerListUpdate()
|
notifyPeerListUpdate()
|
||||||
|
|
||||||
// Reset notification flag if network is now empty
|
// Mark when network became empty, but don't reset flag immediately
|
||||||
|
let currentNetworkSize = collectionsQueue.sync { activePeers.count }
|
||||||
|
if currentNetworkSize == 0 && networkBecameEmptyTime == nil {
|
||||||
|
networkBecameEmptyTime = Date()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we should reset the notification flag
|
||||||
|
if let emptyTime = networkBecameEmptyTime {
|
||||||
let currentNetworkSize = collectionsQueue.sync { activePeers.count }
|
let currentNetworkSize = collectionsQueue.sync { activePeers.count }
|
||||||
if currentNetworkSize == 0 {
|
if currentNetworkSize == 0 {
|
||||||
hasNotifiedNetworkAvailable = false
|
// Network is still empty, check if enough time has passed
|
||||||
|
let timeSinceEmpty = Date().timeIntervalSince(emptyTime)
|
||||||
|
if timeSinceEmpty >= networkEmptyResetDelay {
|
||||||
|
// Reset the flag after network has been empty for the delay period
|
||||||
|
hasNotifiedNetworkAvailable = false
|
||||||
|
// Keep the empty time set so we don't immediately notify again
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Network is no longer empty, clear the empty time
|
||||||
|
networkBecameEmptyTime = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2088,11 +2113,31 @@ class BluetoothMeshService: NSObject {
|
|||||||
}
|
}
|
||||||
self.notifyPeerListUpdate(immediate: true)
|
self.notifyPeerListUpdate(immediate: true)
|
||||||
|
|
||||||
// Send network available notification if this is the first peer we've seen
|
// Send network available notification if appropriate
|
||||||
let currentNetworkSize = collectionsQueue.sync { self.activePeers.count }
|
let currentNetworkSize = collectionsQueue.sync { self.activePeers.count }
|
||||||
if currentNetworkSize > 0 && !hasNotifiedNetworkAvailable {
|
if currentNetworkSize > 0 {
|
||||||
hasNotifiedNetworkAvailable = true
|
// Clear empty time since network is active
|
||||||
NotificationService.shared.sendNetworkAvailableNotification(peerCount: currentNetworkSize)
|
networkBecameEmptyTime = nil
|
||||||
|
|
||||||
|
if !hasNotifiedNetworkAvailable {
|
||||||
|
// Check if enough time has passed since last notification
|
||||||
|
let now = Date()
|
||||||
|
var shouldSendNotification = true
|
||||||
|
|
||||||
|
if let lastNotification = lastNetworkNotificationTime {
|
||||||
|
let timeSinceLastNotification = now.timeIntervalSince(lastNotification)
|
||||||
|
if timeSinceLastNotification < networkNotificationCooldown {
|
||||||
|
// Too soon to send another notification
|
||||||
|
shouldSendNotification = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if shouldSendNotification {
|
||||||
|
hasNotifiedNetworkAvailable = true
|
||||||
|
lastNetworkNotificationTime = now
|
||||||
|
NotificationService.shared.sendNetworkAvailableNotification(peerCount: currentNetworkSize)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
@@ -2945,10 +2990,10 @@ extension BluetoothMeshService: CBCentralManagerDelegate {
|
|||||||
self.delegate?.didDisconnectFromPeer(peerID)
|
self.delegate?.didDisconnectFromPeer(peerID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset notification flag if network is now empty
|
// Mark when network became empty, but don't reset flag immediately
|
||||||
let currentNetworkSize = collectionsQueue.sync { activePeers.count }
|
let currentNetworkSize = collectionsQueue.sync { activePeers.count }
|
||||||
if currentNetworkSize == 0 {
|
if currentNetworkSize == 0 && networkBecameEmptyTime == nil {
|
||||||
hasNotifiedNetworkAvailable = false
|
networkBecameEmptyTime = Date()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.notifyPeerListUpdate()
|
self.notifyPeerListUpdate()
|
||||||
|
|||||||
Reference in New Issue
Block a user