mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-25 20:45:19 +00:00
Logs/robustness: debounce disconnect notifications (1.5s), debounce 'reconnected' logs (2s), add weak-link cooldown after timeouts on very weak RSSI (<= -90)
This commit is contained in:
@@ -328,7 +328,6 @@
|
|||||||
A2E8C336FA1ADBEC03261DFD /* bitchatShareExtension */,
|
A2E8C336FA1ADBEC03261DFD /* bitchatShareExtension */,
|
||||||
C3D98EB3E1B455E321F519F4 /* bitchatTests */,
|
C3D98EB3E1B455E321F519F4 /* bitchatTests */,
|
||||||
9F37F9F2C353B58AC809E93B /* Products */,
|
9F37F9F2C353B58AC809E93B /* Products */,
|
||||||
048A4BE52E5CCC5C00162C4A /* Recovered References */,
|
|
||||||
);
|
);
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
@@ -743,14 +742,13 @@
|
|||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
048A4BE72E5CCCC300162C4A /* TransportConfig.swift in Sources */,
|
048A4BE72E5CCCC300162C4A /* TransportConfig.swift in Sources */,
|
||||||
1234567890ABCDEFFEDCBA13 /* PeerDisplayNameResolver.swift in Sources */,
|
1234567890ABCDEFFEDCBA13 /* PeerDisplayNameResolver.swift in Sources */,
|
||||||
AA77BB12CC22DD33EE44FF56 /* VerificationService.swift in Sources */,
|
AA77BB12CC22DD33EE44FF56 /* VerificationService.swift in Sources */,
|
||||||
AA77BB15CC22DD33EE44FF59 /* VerificationViews.swift in Sources */,
|
AA77BB15CC22DD33EE44FF59 /* VerificationViews.swift in Sources */,
|
||||||
A1B2C3D54E5F60718293A4B6 /* XChaCha20Poly1305Compat.swift in Sources */,
|
A1B2C3D54E5F60718293A4B6 /* XChaCha20Poly1305Compat.swift in Sources */,
|
||||||
AD11E46940D742AEAF547EB2 /* AppInfoView.swift in Sources */,
|
AD11E46940D742AEAF547EB2 /* AppInfoView.swift in Sources */,
|
||||||
9B51E9B63A3EA59B1A7874BD /* BinaryEncodingUtils.swift in Sources */,
|
9B51E9B63A3EA59B1A7874BD /* BinaryEncodingUtils.swift in Sources */,
|
||||||
|
|
||||||
049BD3B42E51F319001A566B /* NostrTransport.swift in Sources */,
|
049BD3B42E51F319001A566B /* NostrTransport.swift in Sources */,
|
||||||
049BD3B52E51F319001A566B /* MessageRouter.swift in Sources */,
|
049BD3B52E51F319001A566B /* MessageRouter.swift in Sources */,
|
||||||
4B747085D07A1BCE0F5BA612 /* BinaryProtocol.swift in Sources */,
|
4B747085D07A1BCE0F5BA612 /* BinaryProtocol.swift in Sources */,
|
||||||
@@ -803,14 +801,13 @@
|
|||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
048A4BE82E5CCCC300162C4A /* TransportConfig.swift in Sources */,
|
048A4BE82E5CCCC300162C4A /* TransportConfig.swift in Sources */,
|
||||||
1234567890ABCDEFFEDCBA14 /* PeerDisplayNameResolver.swift in Sources */,
|
1234567890ABCDEFFEDCBA14 /* PeerDisplayNameResolver.swift in Sources */,
|
||||||
AA77BB11CC22DD33EE44FF55 /* VerificationService.swift in Sources */,
|
AA77BB11CC22DD33EE44FF55 /* VerificationService.swift in Sources */,
|
||||||
AA77BB14CC22DD33EE44FF58 /* VerificationViews.swift in Sources */,
|
AA77BB14CC22DD33EE44FF58 /* VerificationViews.swift in Sources */,
|
||||||
A1B2C3D44E5F60718293A4B5 /* XChaCha20Poly1305Compat.swift in Sources */,
|
A1B2C3D44E5F60718293A4B5 /* XChaCha20Poly1305Compat.swift in Sources */,
|
||||||
ABAF130D88561F4A646F0430 /* AppInfoView.swift in Sources */,
|
ABAF130D88561F4A646F0430 /* AppInfoView.swift in Sources */,
|
||||||
AFB6AEFCABBE97441CB3102B /* BinaryEncodingUtils.swift in Sources */,
|
AFB6AEFCABBE97441CB3102B /* BinaryEncodingUtils.swift in Sources */,
|
||||||
|
|
||||||
049BD3B22E51F319001A566B /* NostrTransport.swift in Sources */,
|
049BD3B22E51F319001A566B /* NostrTransport.swift in Sources */,
|
||||||
049BD3B32E51F319001A566B /* MessageRouter.swift in Sources */,
|
049BD3B32E51F319001A566B /* MessageRouter.swift in Sources */,
|
||||||
F455F011B3B648ADA233F998 /* BinaryProtocol.swift in Sources */,
|
F455F011B3B648ADA233F998 /* BinaryProtocol.swift in Sources */,
|
||||||
@@ -1007,7 +1004,7 @@
|
|||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.3.2;
|
MARKETING_VERSION = 1.3.3;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = chat.bitchat.ShareExtension;
|
PRODUCT_BUNDLE_IDENTIFIER = chat.bitchat.ShareExtension;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||||
@@ -1038,7 +1035,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.3.2;
|
MARKETING_VERSION = 1.3.3;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = chat.bitchat;
|
PRODUCT_BUNDLE_IDENTIFIER = chat.bitchat;
|
||||||
PRODUCT_NAME = bitchat;
|
PRODUCT_NAME = bitchat;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
@@ -1093,7 +1090,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.3.2;
|
MARKETING_VERSION = 1.3.3;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = chat.bitchat;
|
PRODUCT_BUNDLE_IDENTIFIER = chat.bitchat;
|
||||||
PRODUCT_NAME = bitchat;
|
PRODUCT_NAME = bitchat;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
@@ -1125,7 +1122,7 @@
|
|||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||||
MARKETING_VERSION = 1.3.1;
|
MARKETING_VERSION = 1.3.3;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = chat.bitchat;
|
PRODUCT_BUNDLE_IDENTIFIER = chat.bitchat;
|
||||||
PRODUCT_NAME = bitchat;
|
PRODUCT_NAME = bitchat;
|
||||||
REGISTER_APP_GROUPS = YES;
|
REGISTER_APP_GROUPS = YES;
|
||||||
@@ -1214,7 +1211,7 @@
|
|||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||||
MARKETING_VERSION = 1.3.1;
|
MARKETING_VERSION = 1.3.3;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = chat.bitchat;
|
PRODUCT_BUNDLE_IDENTIFIER = chat.bitchat;
|
||||||
PRODUCT_NAME = bitchat;
|
PRODUCT_NAME = bitchat;
|
||||||
REGISTER_APP_GROUPS = YES;
|
REGISTER_APP_GROUPS = YES;
|
||||||
@@ -1307,7 +1304,7 @@
|
|||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.3.2;
|
MARKETING_VERSION = 1.3.3;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = chat.bitchat.ShareExtension;
|
PRODUCT_BUNDLE_IDENTIFIER = chat.bitchat.ShareExtension;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||||
|
|||||||
@@ -128,9 +128,13 @@ final class BLEService: NSObject {
|
|||||||
|
|
||||||
// Backpressure-aware write queue per peripheral
|
// Backpressure-aware write queue per peripheral
|
||||||
private var pendingPeripheralWrites: [String: [Data]] = [:]
|
private var pendingPeripheralWrites: [String: [Data]] = [:]
|
||||||
|
// Debounce duplicate disconnect notifies
|
||||||
|
private var recentDisconnectNotifies: [String: Date] = [:]
|
||||||
// Store-and-forward for directed messages when we have no links
|
// Store-and-forward for directed messages when we have no links
|
||||||
// Keyed by recipient short peerID -> messageID -> (packet, enqueuedAt)
|
// Keyed by recipient short peerID -> messageID -> (packet, enqueuedAt)
|
||||||
private var pendingDirectedRelays: [String: [String: (packet: BitchatPacket, enqueuedAt: Date)]] = [:]
|
private var pendingDirectedRelays: [String: [String: (packet: BitchatPacket, enqueuedAt: Date)]] = [:]
|
||||||
|
// Debounce for 'reconnected' logs
|
||||||
|
private var lastReconnectLogAt: [String: Date] = [:]
|
||||||
|
|
||||||
// MARK: - Maintenance Timer
|
// MARK: - Maintenance Timer
|
||||||
|
|
||||||
@@ -1494,12 +1498,19 @@ final class BLEService: NSObject {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log connection status only for direct connectivity changes to avoid flapping on relayed announces
|
// Log connection status only for direct connectivity changes; debounce to reduce spam
|
||||||
if isDirectAnnounce || hasPeripheralConnection || hasCentralSubscription {
|
if isDirectAnnounce || hasPeripheralConnection || hasCentralSubscription {
|
||||||
|
let now = Date()
|
||||||
if existingPeer == nil {
|
if existingPeer == nil {
|
||||||
SecureLogger.log("🆕 New peer: \(announcement.nickname)", category: SecureLogger.session, level: .debug)
|
SecureLogger.log("🆕 New peer: \(announcement.nickname)", category: SecureLogger.session, level: .debug)
|
||||||
} else if wasDisconnected {
|
} else if wasDisconnected {
|
||||||
SecureLogger.log("🔄 Peer \(announcement.nickname) reconnected", category: SecureLogger.session, level: .debug)
|
// Debounce 'reconnected' logs within short window
|
||||||
|
if let last = lastReconnectLogAt[peerID], now.timeIntervalSince(last) < TransportConfig.bleReconnectLogDebounceSeconds {
|
||||||
|
// Skip duplicate log
|
||||||
|
} else {
|
||||||
|
SecureLogger.log("🔄 Peer \(announcement.nickname) reconnected", category: SecureLogger.session, level: .debug)
|
||||||
|
lastReconnectLogAt[peerID] = now
|
||||||
|
}
|
||||||
} else if existingPeer?.nickname != announcement.nickname {
|
} else if existingPeer?.nickname != announcement.nickname {
|
||||||
SecureLogger.log("🔄 Peer \(peerID) changed nickname: \(existingPeer?.nickname ?? "Unknown") -> \(announcement.nickname)", category: SecureLogger.session, level: .debug)
|
SecureLogger.log("🔄 Peer \(peerID) changed nickname: \(existingPeer?.nickname ?? "Unknown") -> \(announcement.nickname)", category: SecureLogger.session, level: .debug)
|
||||||
}
|
}
|
||||||
@@ -1898,6 +1909,18 @@ final class BLEService: NSObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Debounced disconnect notifier to avoid duplicate disconnect callbacks within a short window
|
||||||
|
private func notifyPeerDisconnectedDebounced(_ peerID: String) {
|
||||||
|
let now = Date()
|
||||||
|
let last = recentDisconnectNotifies[peerID]
|
||||||
|
if last == nil || now.timeIntervalSince(last!) >= TransportConfig.bleDisconnectNotifyDebounceSeconds {
|
||||||
|
delegate?.didDisconnectFromPeer(peerID)
|
||||||
|
recentDisconnectNotifies[peerID] = now
|
||||||
|
} else {
|
||||||
|
// Suppressed duplicate disconnect notification
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// NEW: Publish peer snapshots to subscribers and notify Transport delegates
|
// NEW: Publish peer snapshots to subscribers and notify Transport delegates
|
||||||
private func publishFullPeerData() {
|
private func publishFullPeerData() {
|
||||||
let transportPeers: [TransportPeerSnapshot] = collectionsQueue.sync {
|
let transportPeers: [TransportPeerSnapshot] = collectionsQueue.sync {
|
||||||
@@ -2422,7 +2445,7 @@ func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeriph
|
|||||||
let currentPeerIDs = self.collectionsQueue.sync { Array(self.peers.keys) }
|
let currentPeerIDs = self.collectionsQueue.sync { Array(self.peers.keys) }
|
||||||
|
|
||||||
if let peerID = peerID {
|
if let peerID = peerID {
|
||||||
self.delegate?.didDisconnectFromPeer(peerID)
|
self.notifyPeerDisconnectedDebounced(peerID)
|
||||||
}
|
}
|
||||||
self.publishFullPeerData()
|
self.publishFullPeerData()
|
||||||
self.delegate?.didUpdatePeerList(currentPeerIDs)
|
self.delegate?.didUpdatePeerList(currentPeerIDs)
|
||||||
@@ -2474,6 +2497,18 @@ extension BLEService {
|
|||||||
guard candidate.isConnectable else { return }
|
guard candidate.isConnectable else { return }
|
||||||
let peripheral = candidate.peripheral
|
let peripheral = candidate.peripheral
|
||||||
let peripheralID = peripheral.identifier.uuidString
|
let peripheralID = peripheral.identifier.uuidString
|
||||||
|
// Weak-link cooldown: if we recently timed out and RSSI is very weak, delay retries
|
||||||
|
if let lastTO = recentConnectTimeouts[peripheralID] {
|
||||||
|
let elapsed = Date().timeIntervalSince(lastTO)
|
||||||
|
if elapsed < TransportConfig.bleWeakLinkCooldownSeconds && candidate.rssi <= TransportConfig.bleWeakLinkRSSICutoff {
|
||||||
|
// Requeue the candidate and try again later
|
||||||
|
connectionCandidates.append(candidate)
|
||||||
|
let remaining = TransportConfig.bleWeakLinkCooldownSeconds - elapsed
|
||||||
|
let delay = min(max(2.0, remaining), 15.0)
|
||||||
|
bleQueue.asyncAfter(deadline: .now() + delay) { [weak self] in self?.tryConnectFromQueue() }
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
if peripherals[peripheralID]?.isConnected == true || peripherals[peripheralID]?.isConnecting == true {
|
if peripherals[peripheralID]?.isConnected == true || peripherals[peripheralID]?.isConnecting == true {
|
||||||
// Already in progress; skip
|
// Already in progress; skip
|
||||||
bleQueue.async { [weak self] in self?.tryConnectFromQueue() }
|
bleQueue.async { [weak self] in self?.tryConnectFromQueue() }
|
||||||
@@ -2811,7 +2846,7 @@ extension BLEService: CBPeripheralManagerDelegate {
|
|||||||
// Get current peer list (after removal)
|
// Get current peer list (after removal)
|
||||||
let currentPeerIDs = self.collectionsQueue.sync { Array(self.peers.keys) }
|
let currentPeerIDs = self.collectionsQueue.sync { Array(self.peers.keys) }
|
||||||
|
|
||||||
self.delegate?.didDisconnectFromPeer(peerID)
|
self.notifyPeerDisconnectedDebounced(peerID)
|
||||||
self.delegate?.didUpdatePeerList(currentPeerIDs)
|
self.delegate?.didUpdatePeerList(currentPeerIDs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -143,6 +143,14 @@ enum TransportConfig {
|
|||||||
// Store-and-forward for directed packets at relays
|
// Store-and-forward for directed packets at relays
|
||||||
static let bleDirectedSpoolWindowSeconds: TimeInterval = 15.0
|
static let bleDirectedSpoolWindowSeconds: TimeInterval = 15.0
|
||||||
|
|
||||||
|
// Log/UI debounce windows
|
||||||
|
static let bleDisconnectNotifyDebounceSeconds: TimeInterval = 1.5
|
||||||
|
static let bleReconnectLogDebounceSeconds: TimeInterval = 2.0
|
||||||
|
|
||||||
|
// Weak-link cooldown after connection timeouts
|
||||||
|
static let bleWeakLinkCooldownSeconds: TimeInterval = 30.0
|
||||||
|
static let bleWeakLinkRSSICutoff: Int = -90
|
||||||
|
|
||||||
// Content hashing / formatting
|
// Content hashing / formatting
|
||||||
static let contentKeyPrefixLength: Int = 256
|
static let contentKeyPrefixLength: Int = 256
|
||||||
static let uiLongMessageLengthThreshold: Int = 2000
|
static let uiLongMessageLengthThreshold: Int = 2000
|
||||||
|
|||||||
Reference in New Issue
Block a user