session-ios/SessionMessagingKit/Sending & Receiving/Pollers/ClosedGroupPoller.swift

108 lines
4.0 KiB
Swift

// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
import Combine
import GRDB
import SessionSnodeKit
import SessionUtilitiesKit
public final class ClosedGroupPoller: Poller {
public static var namespaces: [SnodeAPI.Namespace] = [.legacyClosedGroup]
// MARK: - Settings
override var namespaces: [SnodeAPI.Namespace] { ClosedGroupPoller.namespaces }
override var maxNodePollCount: UInt { 0 }
private static let minPollInterval: Double = 2
private static let maxPollInterval: Double = 30
// MARK: - Initialization
public static let shared: ClosedGroupPoller = ClosedGroupPoller()
// MARK: - Public API
public func start() {
// Fetch all closed groups (excluding any don't contain the current user as a
// GroupMemeber as the user is no longer a member of those)
Storage.shared
.read { db in
try ClosedGroup
.select(.threadId)
.joining(
required: ClosedGroup.members
.filter(GroupMember.Columns.profileId == getUserHexEncodedPublicKey(db))
)
.asRequest(of: String.self)
.fetchAll(db)
}
.defaulting(to: [])
.forEach { [weak self] publicKey in
self?.startIfNeeded(for: publicKey)
}
}
// MARK: - Abstract Methods
override func pollerName(for publicKey: String) -> String {
return "closed group with public key: \(publicKey)"
}
override func nextPollDelay(for publicKey: String) -> TimeInterval {
// Get the received date of the last message in the thread. If we don't have
// any messages yet, pick some reasonable fake time interval to use instead
let lastMessageDate: Date = Storage.shared
.read { db in
try Interaction
.filter(Interaction.Columns.threadId == publicKey)
.select(.receivedAtTimestampMs)
.order(Interaction.Columns.timestampMs.desc)
.asRequest(of: Int64.self)
.fetchOne(db)
}
.map { receivedAtTimestampMs -> Date? in
guard receivedAtTimestampMs > 0 else { return nil }
return Date(timeIntervalSince1970: (TimeInterval(receivedAtTimestampMs) / 1000))
}
.defaulting(to: Date().addingTimeInterval(-5 * 60))
let timeSinceLastMessage: TimeInterval = Date().timeIntervalSince(lastMessageDate)
let minPollInterval: Double = ClosedGroupPoller.minPollInterval
let limit: Double = (12 * 60 * 60)
let a: TimeInterval = ((ClosedGroupPoller.maxPollInterval - minPollInterval) / limit)
let nextPollInterval: TimeInterval = a * min(timeSinceLastMessage, limit) + minPollInterval
SNLog("Next poll interval for closed group with public key: \(publicKey) is \(nextPollInterval) s.")
return nextPollInterval
}
override func getSnodeForPolling(
for publicKey: String
) -> AnyPublisher<Snode, Error> {
return SnodeAPI.getSwarm(for: publicKey)
.tryMap { swarm -> Snode in
guard let snode: Snode = swarm.randomElement() else {
throw OnionRequestAPIError.insufficientSnodes
}
return snode
}
.eraseToAnyPublisher()
}
override func handlePollError(
_ error: Error,
for publicKey: String,
using dependencies: SMKDependencies = SMKDependencies()
) {
SNLog("Polling failed for closed group with public key: \(publicKey) due to error: \(error).")
// Try to restart the poller from scratch
Threading.pollerQueue.async { [weak self] in
self?.setUpPolling(for: publicKey, using: dependencies)
}
}
}