mirror of https://github.com/oxen-io/session-ios
Clean up ahead of protocol changes
parent
6f17baa7ac
commit
e34e66ccfd
@ -1,104 +0,0 @@
|
||||
import GCDWebServer
|
||||
|
||||
// TODO: Clean
|
||||
|
||||
private extension GCDWebServerResponse {
|
||||
|
||||
convenience init<E: RawRepresentable>(statusCode: E) where E.RawValue == Int {
|
||||
self.init(statusCode: statusCode.rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
private extension GCDWebServerDataRequest {
|
||||
|
||||
var truncatedContentType: String? {
|
||||
guard let contentType = contentType else { return nil }
|
||||
guard let substring = contentType.split(separator: ";").first else { return contentType }
|
||||
return String(substring)
|
||||
}
|
||||
|
||||
// GCDWebServerDataRequest already provides this implementation
|
||||
// However it will abort when running in DEBUG, we don't want that so we just override it with a version which doesn't abort
|
||||
var jsonObject: JSON? {
|
||||
let acceptedMimeTypes = [ "application/json", "text/json", "text/javascript" ]
|
||||
guard let mimeType = truncatedContentType, acceptedMimeTypes.contains(mimeType) else { return nil }
|
||||
|
||||
do {
|
||||
let object = try JSONSerialization.jsonObject(with: data, options: .allowFragments)
|
||||
return object as? JSON
|
||||
} catch let error {
|
||||
print("[Loki] Failed to serialize JSON: \(error).")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@objc(LKP2PServer)
|
||||
final class LokiP2PServer : NSObject {
|
||||
|
||||
private enum StatusCode : Int {
|
||||
case ok = 200
|
||||
case badRequest = 400
|
||||
case notFound = 404
|
||||
case methodNotAllowed = 405
|
||||
case internalServerError = 500
|
||||
}
|
||||
|
||||
private lazy var webServer: GCDWebServer = {
|
||||
let webServer = GCDWebServer()
|
||||
|
||||
// Don't allow specific methods
|
||||
let invalidMethodProcessBlock: (GCDWebServerRequest) -> GCDWebServerResponse? = { _ in
|
||||
return GCDWebServerResponse(statusCode: StatusCode.methodNotAllowed)
|
||||
}
|
||||
|
||||
let invalidMethods = [ "GET", "PUT", "DELETE" ]
|
||||
for method in invalidMethods {
|
||||
webServer.addDefaultHandler(forMethod: method, request: GCDWebServerRequest.self, processBlock: invalidMethodProcessBlock)
|
||||
}
|
||||
|
||||
// By default send 404 for any path
|
||||
webServer.addDefaultHandler(forMethod: "POST", request: GCDWebServerRequest.self, processBlock: { _ in
|
||||
return GCDWebServerResponse(statusCode: StatusCode.notFound)
|
||||
})
|
||||
|
||||
// Handle our specific storage path
|
||||
webServer.addHandler(forMethod: "POST", path: "/storage_rpc/v1", request: GCDWebServerDataRequest.self, processBlock: { request in
|
||||
// Make sure we were sent a good request
|
||||
guard let dataRequest = request as? GCDWebServerDataRequest, let json = dataRequest.jsonObject else {
|
||||
return GCDWebServerResponse(statusCode: StatusCode.badRequest)
|
||||
}
|
||||
|
||||
// Only allow the store method
|
||||
guard let method = json["method"] as? String, method == "store" else {
|
||||
return GCDWebServerResponse(statusCode: StatusCode.notFound)
|
||||
}
|
||||
|
||||
// Make sure we have the data
|
||||
guard let params = json["params"] as? [String: String], let data = params["data"] else {
|
||||
return GCDWebServerResponse(statusCode: StatusCode.badRequest)
|
||||
}
|
||||
|
||||
// Pass it off to the message handler
|
||||
LokiP2PAPI.handleReceivedMessage(base64EncodedData: data)
|
||||
|
||||
// Send a response back
|
||||
return GCDWebServerResponse(statusCode: StatusCode.ok.rawValue)
|
||||
})
|
||||
|
||||
return webServer
|
||||
}()
|
||||
|
||||
@objc public var serverURL: URL? { return webServer.serverURL }
|
||||
@objc public var isRunning: Bool { return webServer.isRunning }
|
||||
|
||||
@discardableResult
|
||||
@objc func start(onPort port: UInt) -> Bool {
|
||||
guard !webServer.isRunning else { return false }
|
||||
webServer.start(withPort: port, bonjourName: nil)
|
||||
return webServer.isRunning
|
||||
}
|
||||
|
||||
@objc func stop() { webServer.stop() }
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
#import "LKFriendRequestMessage.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
// TODO: This is just a friend request message with a flag set. Not sure if it needs to be its own type.
|
||||
|
||||
NS_SWIFT_NAME(SessionRequestMessage)
|
||||
@interface LKSessionRequestMessage : LKFriendRequestMessage
|
||||
|
||||
- (instancetype)initWithThread:(TSThread *)thread;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
@ -1,27 +0,0 @@
|
||||
#import "LKSessionRequestMessage.h"
|
||||
#import <SessionCoreKit/NSDate+OWS.h>
|
||||
#import <SessionServiceKit/SessionServiceKit-Swift.h>
|
||||
|
||||
@implementation LKSessionRequestMessage
|
||||
|
||||
#pragma mark Initialization
|
||||
- (instancetype)initWithThread:(TSThread *)thread {
|
||||
return [self initOutgoingMessageWithTimestamp:NSDate.ows_millisecondTimeStamp inThread:thread messageBody:@"" attachmentIds:[NSMutableArray<NSString *> new]
|
||||
expiresInSeconds:0 expireStartedAt:0 isVoiceMessage:NO groupMetaMessage:TSGroupMetaMessageUnspecified quotedMessage:nil contactShare:nil linkPreview:nil];
|
||||
}
|
||||
|
||||
#pragma mark Building
|
||||
- (nullable SSKProtoDataMessageBuilder *)dataMessageBuilder
|
||||
{
|
||||
SSKProtoDataMessageBuilder *builder = super.dataMessageBuilder;
|
||||
if (builder == nil) { return nil; }
|
||||
[builder setFlags:SSKProtoDataMessageFlagsSessionRequest];
|
||||
return builder;
|
||||
}
|
||||
|
||||
#pragma mark Settings
|
||||
- (BOOL)shouldBeSaved { return NO; }
|
||||
- (uint)ttl { return (uint)[LKTTLUtilities getTTLFor:LKMessageTypeSessionRequest]; }
|
||||
- (BOOL)shouldSyncTranscript { return NO; }
|
||||
|
||||
@end
|
@ -0,0 +1,23 @@
|
||||
|
||||
@objc(LKEphemeralMessage)
|
||||
internal final class EphemeralMessage : TSOutgoingMessage {
|
||||
|
||||
@objc internal override var ttl: UInt32 { return UInt32(TTLUtilities.getTTL(for: .ephemeral)) }
|
||||
|
||||
@objc internal override func shouldBeSaved() -> Bool { return false }
|
||||
@objc internal override func shouldSyncTranscript() -> Bool { return false }
|
||||
|
||||
@objc internal init(thread: TSThread) {
|
||||
super.init(outgoingMessageWithTimestamp: NSDate.ows_millisecondTimeStamp(), in: thread, messageBody: "",
|
||||
attachmentIds: NSMutableArray(), expiresInSeconds: 0, expireStartedAt: 0, isVoiceMessage: false,
|
||||
groupMetaMessage: .unspecified, quotedMessage: nil, contactShare: nil, linkPreview: nil)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
}
|
||||
|
||||
required init(dictionary: [String:Any]) throws {
|
||||
try super.init(dictionary: dictionary)
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
|
||||
@objc(LKSessionRequestMessage)
|
||||
internal final class SessionRequestMessage : TSOutgoingMessage {
|
||||
|
||||
@objc internal override var ttl: UInt32 { return UInt32(TTLUtilities.getTTL(for: .sessionRequest)) }
|
||||
|
||||
@objc internal override func shouldBeSaved() -> Bool { return false }
|
||||
@objc internal override func shouldSyncTranscript() -> Bool { return false }
|
||||
|
||||
@objc internal init(thread: TSThread) {
|
||||
super.init(outgoingMessageWithTimestamp: NSDate.ows_millisecondTimeStamp(), in: thread, messageBody: "",
|
||||
attachmentIds: NSMutableArray(), expiresInSeconds: 0, expireStartedAt: 0, isVoiceMessage: false,
|
||||
groupMetaMessage: .unspecified, quotedMessage: nil, contactShare: nil, linkPreview: nil)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
}
|
||||
|
||||
required init(dictionary: [String:Any]) throws {
|
||||
try super.init(dictionary: dictionary)
|
||||
}
|
||||
|
||||
@objc internal override func dataMessageBuilder() -> Any? {
|
||||
guard let builder = super.dataMessageBuilder() as? SSKProtoDataMessage.SSKProtoDataMessageBuilder else { return nil }
|
||||
builder.setFlags(UInt32(SSKProtoDataMessage.SSKProtoDataMessageFlags.sessionRequest.rawValue))
|
||||
return builder
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
#import "LKEphemeralMessage.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
NS_SWIFT_NAME(LokiAddressMessage)
|
||||
@interface LKAddressMessage : LKEphemeralMessage
|
||||
|
||||
@property (nonatomic, readonly) NSString *address;
|
||||
@property (nonatomic, readonly) uint16_t port;
|
||||
@property (nonatomic, readonly) BOOL isPing;
|
||||
|
||||
- (instancetype)initInThread:(nullable TSThread *)thread address:(NSString *)address port:(uint16_t)port isPing:(BOOL)isPing;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
@ -1,49 +0,0 @@
|
||||
#import "LKAddressMessage.h"
|
||||
#import "NSDate+OWS.h"
|
||||
#import "SignalRecipient.h"
|
||||
#import <SessionServiceKit/SessionServiceKit-Swift.h>
|
||||
|
||||
@interface LKAddressMessage ()
|
||||
|
||||
@property (nonatomic) NSString *address;
|
||||
@property (nonatomic) uint16_t port;
|
||||
@property (nonatomic) BOOL isPing;
|
||||
|
||||
@end
|
||||
|
||||
@implementation LKAddressMessage
|
||||
|
||||
#pragma mark Initialization
|
||||
- (instancetype)initInThread:(nullable TSThread *)thread address:(NSString *)address port:(uint16_t)port isPing:(bool)isPing
|
||||
{
|
||||
self = [super initInThread:thread];
|
||||
if (self) {
|
||||
_address = address;
|
||||
_port = port;
|
||||
_isPing = isPing;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark Building
|
||||
- (SSKProtoContentBuilder *)prepareCustomContentBuilder:(SignalRecipient *)recipient {
|
||||
SSKProtoContentBuilder *contentBuilder = [super prepareCustomContentBuilder:recipient];
|
||||
SSKProtoLokiAddressMessageBuilder *addressMessageBuilder = SSKProtoLokiAddressMessage.builder;
|
||||
[addressMessageBuilder setPtpAddress:self.address];
|
||||
uint32_t portAsUInt32 = self.port;
|
||||
[addressMessageBuilder setPtpPort:portAsUInt32];
|
||||
NSError *error;
|
||||
SSKProtoLokiAddressMessage *addressMessage = [addressMessageBuilder buildAndReturnError:&error];
|
||||
if (error || addressMessage == nil) {
|
||||
OWSFailDebug(@"Failed to build Loki address message for: %@ due to error: %@.", recipient.recipientId, error);
|
||||
return nil;
|
||||
} else {
|
||||
[contentBuilder setLokiAddressMessage:addressMessage];
|
||||
}
|
||||
return contentBuilder;
|
||||
}
|
||||
|
||||
#pragma mark Settings
|
||||
- (uint)ttl { return (uint)[LKTTLUtilities getTTLFor:LKMessageTypeAddress]; }
|
||||
|
||||
@end
|
@ -1,12 +0,0 @@
|
||||
#import "TSOutgoingMessage.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
NS_SWIFT_NAME(EphemeralMessage)
|
||||
@interface LKEphemeralMessage : TSOutgoingMessage
|
||||
|
||||
- (instancetype)initInThread:(nullable TSThread *)thread;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
@ -1,18 +0,0 @@
|
||||
#import "LKEphemeralMessage.h"
|
||||
#import <SessionCoreKit/NSDate+OWS.h>
|
||||
#import <SessionServiceKit/SessionServiceKit-Swift.h>
|
||||
|
||||
@implementation LKEphemeralMessage
|
||||
|
||||
#pragma mark Initialization
|
||||
- (instancetype)initInThread:(nullable TSThread *)thread {
|
||||
return [self initOutgoingMessageWithTimestamp:NSDate.ows_millisecondTimeStamp inThread:thread messageBody:@"" attachmentIds:[NSMutableArray<NSString *> new]
|
||||
expiresInSeconds:0 expireStartedAt:0 isVoiceMessage:NO groupMetaMessage:TSGroupMetaMessageUnspecified quotedMessage:nil contactShare:nil linkPreview:nil];
|
||||
}
|
||||
|
||||
#pragma mark Settings
|
||||
- (BOOL)shouldSyncTranscript { return NO; }
|
||||
- (BOOL)shouldBeSaved { return NO; }
|
||||
- (uint)ttl { return (uint)[LKTTLUtilities getTTLFor:LKMessageTypeEphemeral]; }
|
||||
|
||||
@end
|
@ -1,233 +0,0 @@
|
||||
|
||||
// TODO: Match Android design
|
||||
|
||||
@objc(LKP2PAPI)
|
||||
public class LokiP2PAPI : NSObject {
|
||||
private static let storage = OWSPrimaryStorage.shared()
|
||||
private static let messageSender = SSKEnvironment.shared.messageSender
|
||||
private static let messageReceiver = SSKEnvironment.shared.messageReceiver
|
||||
private static let ourHexEncodedPubKey = getUserHexEncodedPublicKey()
|
||||
|
||||
/// The amount of time before pinging when a user is set to offline
|
||||
private static let offlinePingTime = 2 * kMinuteInterval
|
||||
|
||||
/// A p2p state struct
|
||||
public struct PeerInfo {
|
||||
public var address: String
|
||||
public var port: UInt16
|
||||
public var isOnline: Bool
|
||||
public var timerDuration: Double
|
||||
public var pingTimer: Timer? = nil
|
||||
}
|
||||
|
||||
/// Our p2p address
|
||||
private static var ourP2PAddress: LokiAPITarget? = nil
|
||||
|
||||
/// This is where we store the p2p details of our contacts
|
||||
private static var peerInfo = [String:PeerInfo]()
|
||||
|
||||
// MARK: - Public functions
|
||||
|
||||
/// Set our local P2P address
|
||||
///
|
||||
/// - Parameter url: The url to our local server
|
||||
@objc public static func setOurP2PAddress(url: URL) {
|
||||
guard let scheme = url.scheme, let host = url.host, let port = url.port else { return }
|
||||
let target = LokiAPITarget(address: "\(scheme)://\(host)", port: UInt16(port), publicKeySet: nil)
|
||||
ourP2PAddress = target
|
||||
}
|
||||
|
||||
/// Ping a contact
|
||||
///
|
||||
/// - Parameter pubKey: The contact hex pubkey
|
||||
@objc(pingContact:)
|
||||
public static func ping(contact pubKey: String) {
|
||||
// Dispatch on the main queue so we escape any transaction blocks
|
||||
DispatchQueue.main.async {
|
||||
var contactThread: TSThread? = nil
|
||||
storage.dbReadConnection.read { transaction in
|
||||
contactThread = TSContactThread.getWithContactId(pubKey, transaction: transaction)
|
||||
}
|
||||
|
||||
guard let thread = contactThread else {
|
||||
print("[Loki] Failed to fetch thread when attempting to ping: \(pubKey).")
|
||||
return
|
||||
}
|
||||
|
||||
guard let message = createLokiAddressMessage(for: thread, isPing: true) else {
|
||||
print("[Loki] Failed to build ping message for: \(pubKey).")
|
||||
return
|
||||
}
|
||||
|
||||
messageSender.sendPromise(message: message).retainUntilComplete()
|
||||
}
|
||||
}
|
||||
|
||||
/// Broadcast an online message to all our friends.
|
||||
/// This shouldn't be called inside a transaction.
|
||||
@objc public static func broadcastOnlineStatus() {
|
||||
// Escape any transaction blocks
|
||||
DispatchQueue.main.async {
|
||||
let friendThreads = getAllFriendThreads()
|
||||
for thread in friendThreads {
|
||||
sendOnlineBroadcastMessage(forThread: thread)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static func handleReceivedMessage(base64EncodedData: String) {
|
||||
guard let data = Data(base64Encoded: base64EncodedData) else {
|
||||
print("[Loki] Failed to decode data for P2P message.")
|
||||
return
|
||||
}
|
||||
guard let envelope = try? LokiMessageWrapper.unwrap(data: data) else {
|
||||
print("[Loki] Failed to unwrap data for P2P message.")
|
||||
return
|
||||
}
|
||||
// We need to set the P2P field on the envelope
|
||||
let builder = envelope.asBuilder()
|
||||
builder.setIsPtpMessage(true)
|
||||
// Send it to the message receiver
|
||||
do {
|
||||
let newEnvelope = try builder.build()
|
||||
let envelopeData = try newEnvelope.serializedData()
|
||||
messageReceiver.handleReceivedEnvelopeData(envelopeData)
|
||||
} catch let error {
|
||||
print("[Loki] Something went wrong during proto conversion: \(error).")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Internal functions
|
||||
|
||||
/// Get the P2P details for the given contact.
|
||||
///
|
||||
/// - Parameter pubKey: The contact hex pubkey
|
||||
/// - Returns: The P2P Details or nil if they don't exist
|
||||
public static func getInfo(for hexEncodedPublicKey: String) -> PeerInfo? {
|
||||
return peerInfo[hexEncodedPublicKey]
|
||||
}
|
||||
|
||||
/// Get the `LokiAddressMessage` for the given thread.
|
||||
///
|
||||
/// - Parameter thread: The contact thread.
|
||||
/// - Returns: The `LokiAddressMessage` for that thread.
|
||||
@objc public static func onlineBroadcastMessage(forThread thread: TSThread) -> LokiAddressMessage? {
|
||||
return createLokiAddressMessage(for: thread, isPing: false)
|
||||
}
|
||||
|
||||
/// Handle P2P logic when we receive a `LokiAddressMessage`
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - pubKey: The other users pubKey
|
||||
/// - address: The pther users p2p address
|
||||
/// - port: The other users p2p port
|
||||
/// - receivedThroughP2P: Wether we received the message through p2p
|
||||
@objc internal static func didReceiveLokiAddressMessage(forContact pubKey: String, address: String, port: UInt16, receivedThroughP2P: Bool) {
|
||||
// Stagger the ping timers so that contacts don't ping each other at the same time
|
||||
let timerDuration = pubKey < ourHexEncodedPubKey ? 1 * kMinuteInterval : 2 * kMinuteInterval
|
||||
|
||||
// Get out current contact details
|
||||
let oldContactInfo = peerInfo[pubKey]
|
||||
|
||||
// Set the new contact details
|
||||
// A contact is always assumed to be offline unless the specific conditions below are met
|
||||
let info = PeerInfo(address: address, port: port, isOnline: false, timerDuration: timerDuration, pingTimer: nil)
|
||||
peerInfo[pubKey] = info
|
||||
|
||||
// Set up our checks
|
||||
let oldContactExists = oldContactInfo != nil
|
||||
let wasOnline = oldContactInfo?.isOnline ?? false
|
||||
let isPeerInfoMatching = oldContactInfo?.address == address && oldContactInfo?.port == port
|
||||
|
||||
/*
|
||||
We need to check if we should ping the user.
|
||||
We don't ping the user IF:
|
||||
- We had old contact details
|
||||
- We got a P2P message
|
||||
- The old contact was set as `Online`
|
||||
- The new p2p details match the old one
|
||||
*/
|
||||
if oldContactExists && receivedThroughP2P && wasOnline && isPeerInfoMatching {
|
||||
setOnline(true, forContact: pubKey)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Ping the contact.
|
||||
This happens in the following scenarios:
|
||||
1. We didn't have the contact, we need to ping them to let them know our details.
|
||||
2. wasP2PMessage = false, so we assume the contact doesn't have our details.
|
||||
3. We had the contact marked as offline, we need to make sure that we can reach their server.
|
||||
4. The other contact details have changed, we need to make sure that we can reach their new server.
|
||||
*/
|
||||
ping(contact: pubKey)
|
||||
}
|
||||
|
||||
internal static func markOnline(_ hexEncodedPublicKey: String) {
|
||||
setOnline(true, forContact: hexEncodedPublicKey)
|
||||
}
|
||||
|
||||
internal static func markOffline(_ hexEncodedPublicKey: String) {
|
||||
setOnline(false, forContact: hexEncodedPublicKey)
|
||||
}
|
||||
|
||||
/// Mark a contact as online or offline.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - isOnline: Whether to set the contact to online or offline.
|
||||
/// - pubKey: The contact hex pubKey
|
||||
@objc internal static func setOnline(_ isOnline: Bool, forContact pubKey: String) {
|
||||
// Make sure we are on the main thread
|
||||
DispatchQueue.main.async {
|
||||
guard var info = peerInfo[pubKey] else { return }
|
||||
|
||||
let interval = isOnline ? info.timerDuration : offlinePingTime
|
||||
|
||||
// Setup a new timer
|
||||
info.pingTimer?.invalidate()
|
||||
info.pingTimer = WeakTimer.scheduledTimer(timeInterval: interval, target: self, userInfo: nil, repeats: true) { _ in ping(contact: pubKey) }
|
||||
info.isOnline = isOnline
|
||||
|
||||
peerInfo[pubKey] = info
|
||||
|
||||
NotificationCenter.default.post(name: .contactOnlineStatusChanged, object: pubKey)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private functions
|
||||
|
||||
private static func sendOnlineBroadcastMessage(forThread thread: TSContactThread) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
guard let message = onlineBroadcastMessage(forThread: thread) else {
|
||||
// print("[Loki] P2P address not set.")
|
||||
return
|
||||
}
|
||||
|
||||
messageSender.sendPromise(message: message).catch2 { error in
|
||||
Logger.warn("Failed to send online status to \(thread.contactIdentifier()).")
|
||||
}.retainUntilComplete()
|
||||
}
|
||||
|
||||
private static func getAllFriendThreads() -> [TSContactThread] {
|
||||
var friendThreadIDs: [String] = []
|
||||
storage.dbReadConnection.read { transaction in
|
||||
TSContactThread.enumerateCollectionObjects(with: transaction) { object, _ in
|
||||
guard let thread = object as? TSContactThread, let uniqueID = thread.uniqueId, thread.contactIdentifier() != ourHexEncodedPubKey else { return }
|
||||
let status = storage.getFriendRequestStatus(for: thread.contactIdentifier(), transaction: transaction)
|
||||
guard status == .friends else { return }
|
||||
friendThreadIDs.append(uniqueID)
|
||||
}
|
||||
}
|
||||
return friendThreadIDs.compactMap { TSContactThread.fetch(uniqueId: $0) }
|
||||
}
|
||||
|
||||
private static func createLokiAddressMessage(for thread: TSThread, isPing: Bool) -> LokiAddressMessage? {
|
||||
guard let ourAddress = ourP2PAddress else {
|
||||
// print("[Loki] P2P address not set.")
|
||||
return nil
|
||||
}
|
||||
|
||||
return LokiAddressMessage(in: thread, address: ourAddress.address, port: ourAddress.port, isPing: isPing)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue