@ -1,10 +1,10 @@
import PromiseKit
@objc ( LKPublicChatAPI )
public final class Loki PublicChatAPI : DotNetAPI {
public final class PublicChatAPI : DotNetAPI {
private static var moderators : [ String : [ UInt64 : Set < String > ] ] = [ : ] // S e r v e r U R L t o ( c h a n n e l I D t o s e t o f m o d e r a t o r I D s )
@objc public static let defaultChats : [ Loki PublicChat] = [ ] // C u r r e n t l y u n u s e d
@objc public static let defaultChats : [ PublicChat] = [ ] // C u r r e n t l y u n u s e d
public static var displayNameUpdatees : [ String : Set < String > ] = [ : ]
@ -80,14 +80,14 @@ public final class LokiPublicChatAPI : DotNetAPI {
return AnyPromise . from ( getMessages ( for : group , on : server ) )
}
public static func getMessages ( for channel : UInt64 , on server : String ) -> Promise < [ Loki PublicChatMessage] > {
public static func getMessages ( for channel : UInt64 , on server : String ) -> Promise < [ PublicChatMessage] > {
var queryParameters = " include_annotations=1 "
if let lastMessageServerID = getLastMessageServerID ( for : channel , on : server ) {
queryParameters += " &since_id= \( lastMessageServerID ) "
} else {
queryParameters += " &count= \( fallbackBatchCount ) &include_deleted=0 "
}
return getAuthToken ( for : server ) . then ( on : DispatchQueue . global ( qos : . default ) ) { token -> Promise < [ Loki PublicChatMessage] > in
return getAuthToken ( for : server ) . then ( on : DispatchQueue . global ( qos : . default ) ) { token -> Promise < [ PublicChatMessage] > in
let url = URL ( string : " \( server ) /channels/ \( channel ) /messages? \( queryParameters ) " ) !
let request = TSRequest ( url : url )
request . allHTTPHeaderFields = [ " Content-Type " : " application/json " , " Authorization " : " Bearer \( token ) " ]
@ -101,32 +101,32 @@ public final class LokiPublicChatAPI : DotNetAPI {
guard ! isDeleted else { return nil }
guard let annotations = message [ " annotations " ] as ? [ JSON ] , let annotation = annotations . first ( where : { $0 [ " type " ] as ? String = = publicChatMessageType } ) , let value = annotation [ " value " ] as ? JSON ,
let serverID = message [ " id " ] as ? UInt64 , let hexEncodedSignatureData = value [ " sig " ] as ? String , let signatureVersion = value [ " sigver " ] as ? UInt64 ,
let body = message [ " text " ] as ? String , let user = message [ " user " ] as ? JSON , let hexEncoded PublicKey = user [ " username " ] as ? String ,
let body = message [ " text " ] as ? String , let user = message [ " user " ] as ? JSON , let sender PublicKey = user [ " username " ] as ? String ,
let timestamp = value [ " timestamp " ] as ? UInt64 else {
print ( " [Loki] Couldn't parse message for public chat channel with ID: \( channel ) on server: \( server ) from: \( message ) . " )
return nil
}
var profilePicture : Loki PublicChatMessage. ProfilePicture ? = nil
var profilePicture : PublicChatMessage. ProfilePicture ? = nil
let displayName = user [ " name " ] as ? String ? ? NSLocalizedString ( " Anonymous " , comment : " " )
if let userAnnotations = user [ " annotations " ] as ? [ JSON ] , let profilePictureAnnotation = userAnnotations . first ( where : { $0 [ " type " ] as ? String = = profilePictureType } ) ,
let profilePictureValue = profilePictureAnnotation [ " value " ] as ? JSON , let profileKeyString = profilePictureValue [ " profileKey " ] as ? String , let profileKey = Data ( base64Encoded : profileKeyString ) , let url = profilePictureValue [ " url " ] as ? String {
profilePicture = Loki PublicChatMessage. ProfilePicture ( profileKey : profileKey , url : url )
profilePicture = PublicChatMessage. ProfilePicture ( profileKey : profileKey , url : url )
}
let lastMessageServerID = getLastMessageServerID ( for : channel , on : server )
if serverID > ( lastMessageServerID ? ? 0 ) { setLastMessageServerID ( for : channel , on : server , to : serverID ) }
let quote : Loki PublicChatMessage. Quote ?
if let quoteAsJSON = value [ " quote " ] as ? JSON , let quotedMessageTimestamp = quoteAsJSON [ " id " ] as ? UInt64 , let quotee HexEncoded PublicKey = quoteAsJSON [ " author " ] as ? String ,
let quote : PublicChatMessage. Quote ?
if let quoteAsJSON = value [ " quote " ] as ? JSON , let quotedMessageTimestamp = quoteAsJSON [ " id " ] as ? UInt64 , let quotee PublicKey = quoteAsJSON [ " author " ] as ? String ,
let quotedMessageBody = quoteAsJSON [ " text " ] as ? String {
let quotedMessageServerID = message [ " reply_to " ] as ? UInt64
quote = Loki PublicChatMessage. Quote ( quotedMessageTimestamp : quotedMessageTimestamp , quotee HexEncoded PublicKey: quotee HexEncoded PublicKey, quotedMessageBody : quotedMessageBody ,
quote = PublicChatMessage. Quote ( quotedMessageTimestamp : quotedMessageTimestamp , quotee PublicKey: quotee PublicKey, quotedMessageBody : quotedMessageBody ,
quotedMessageServerID : quotedMessageServerID )
} else {
quote = nil
}
let signature = Loki PublicChatMessage. Signature ( data : Data ( hex : hexEncodedSignatureData ) , version : signatureVersion )
let signature = PublicChatMessage. Signature ( data : Data ( hex : hexEncodedSignatureData ) , version : signatureVersion )
let attachmentsAsJSON = annotations . filter { $0 [ " type " ] as ? String = = attachmentType }
let attachments : [ Loki PublicChatMessage. Attachment ] = attachmentsAsJSON . compactMap { attachmentAsJSON in
guard let value = attachmentAsJSON [ " value " ] as ? JSON , let kindAsString = value [ " lokiType " ] as ? String , let kind = Loki PublicChatMessage. Attachment . Kind ( rawValue : kindAsString ) ,
let attachments : [ PublicChatMessage. Attachment ] = attachmentsAsJSON . compactMap { attachmentAsJSON in
guard let value = attachmentAsJSON [ " value " ] as ? JSON , let kindAsString = value [ " lokiType " ] as ? String , let kind = PublicChatMessage. Attachment . Kind ( rawValue : kindAsString ) ,
let serverID = value [ " id " ] as ? UInt64 , let contentType = value [ " contentType " ] as ? String , let size = value [ " size " ] as ? UInt , let url = value [ " url " ] as ? String else { return nil }
let fileName = value [ " fileName " ] as ? String ? ? UUID ( ) . description
let width = value [ " width " ] as ? UInt ? ? 0
@ -141,10 +141,10 @@ public final class LokiPublicChatAPI : DotNetAPI {
return nil
}
}
return Loki PublicChatMessage. Attachment ( kind : kind , server : server , serverID : serverID , contentType : contentType , size : size , fileName : fileName , flags : flags ,
return PublicChatMessage. Attachment ( kind : kind , server : server , serverID : serverID , contentType : contentType , size : size , fileName : fileName , flags : flags ,
width : width , height : height , caption : caption , url : url , linkPreviewURL : linkPreviewURL , linkPreviewTitle : linkPreviewTitle )
}
let result = Loki PublicChatMessage( serverID : serverID , hexEncodedPublicKey: hexEncoded PublicKey, displayName : displayName , profilePicture : profilePicture ,
let result = PublicChatMessage( serverID : serverID , senderPublicKey: sender PublicKey, displayName : displayName , profilePicture : profilePicture ,
body : body , type : publicChatMessageType , timestamp : timestamp , quote : quote , attachments : attachments , signature : signature )
guard result . hasValidSignature ( ) else {
print ( " [Loki] Ignoring public chat message with invalid signature. " )
@ -166,17 +166,17 @@ public final class LokiPublicChatAPI : DotNetAPI {
// MARK: S e n d i n g
@objc ( sendMessage : toGroup : onServer : )
public static func objc_sendMessage ( _ message : Loki PublicChatMessage, to group : UInt64 , on server : String ) -> AnyPromise {
public static func objc_sendMessage ( _ message : PublicChatMessage, to group : UInt64 , on server : String ) -> AnyPromise {
return AnyPromise . from ( sendMessage ( message , to : group , on : server ) )
}
public static func sendMessage ( _ message : Loki PublicChatMessage, to channel : UInt64 , on server : String ) -> Promise < Loki PublicChatMessage> {
public static func sendMessage ( _ message : PublicChatMessage, to channel : UInt64 , on server : String ) -> Promise < PublicChatMessage> {
print ( " [Loki] Sending message to public chat channel with ID: \( channel ) on server: \( server ) . " )
let ( promise , seal ) = Promise < Loki PublicChatMessage> . pending ( )
let ( promise , seal ) = Promise < PublicChatMessage> . pending ( )
DispatchQueue . global ( qos : . userInitiated ) . async { [ privateKey = userKeyPair . privateKey ] in
guard let signedMessage = message . sign ( with : privateKey ) else { return seal . reject ( DotNetAPIError . signingFailed ) }
attempt ( maxRetryCount : maxRetryCount , recoveringOn : DispatchQueue . global ( qos : . default ) ) {
getAuthToken ( for : server ) . then ( on : DispatchQueue . global ( qos : . default ) ) { token -> Promise < Loki PublicChatMessage> in
getAuthToken ( for : server ) . then ( on : DispatchQueue . global ( qos : . default ) ) { token -> Promise < PublicChatMessage> in
let url = URL ( string : " \( server ) /channels/ \( channel ) /messages " ) !
let parameters = signedMessage . toJSON ( )
let request = TSRequest ( url : url , method : " POST " , parameters : parameters )
@ -192,7 +192,7 @@ public final class LokiPublicChatAPI : DotNetAPI {
throw DotNetAPIError . parsingFailed
}
let timestamp = UInt64 ( date . timeIntervalSince1970 ) * 1000
return Loki PublicChatMessage( serverID : serverID , hexEncoded PublicKey: getUserHexEncodedPublicKey ( ) , displayName : displayName , profilePicture : signedMessage . profilePicture , body : body , type : publicChatMessageType , timestamp : timestamp , quote : signedMessage . quote , attachments : signedMessage . attachments , signature : signedMessage . signature )
return PublicChatMessage( serverID : serverID , sender PublicKey: getUserHexEncodedPublicKey ( ) , displayName : displayName , profilePicture : signedMessage . profilePicture , body : body , type : publicChatMessageType , timestamp : timestamp , quote : signedMessage . quote , attachments : signedMessage . attachments , signature : signedMessage . signature )
}
} . handlingInvalidAuthTokenIfNeeded ( for : server )
} . done ( on : DispatchQueue . global ( qos : . default ) ) { message in
@ -330,7 +330,7 @@ public final class LokiPublicChatAPI : DotNetAPI {
}
}
static func updateProfileIfNeeded ( for channel : UInt64 , on server : String , from info : Loki PublicChatInfo) {
static func updateProfileIfNeeded ( for channel : UInt64 , on server : String , from info : PublicChatInfo) {
let storage = OWSPrimaryStorage . shared ( )
let publicChatID = " \( server ) . \( channel ) "
try ! Storage . writeSync { transaction in
@ -381,9 +381,9 @@ public final class LokiPublicChatAPI : DotNetAPI {
return AnyPromise . from ( getInfo ( for : channel , on : server ) )
}
public static func getInfo ( for channel : UInt64 , on server : String ) -> Promise < Loki PublicChatInfo> {
public static func getInfo ( for channel : UInt64 , on server : String ) -> Promise < PublicChatInfo> {
return attempt ( maxRetryCount : maxRetryCount , recoveringOn : DispatchQueue . global ( qos : . default ) ) {
getAuthToken ( for : server ) . then ( on : DispatchQueue . global ( qos : . default ) ) { token -> Promise < Loki PublicChatInfo> in
getAuthToken ( for : server ) . then ( on : DispatchQueue . global ( qos : . default ) ) { token -> Promise < PublicChatInfo> in
let url = URL ( string : " \( server ) /channels/ \( channel ) ?include_annotations=1 " ) !
let request = TSRequest ( url : url )
request . allHTTPHeaderFields = [ " Content-Type " : " application/json " , " Authorization " : " Bearer \( token ) " ]
@ -404,7 +404,7 @@ public final class LokiPublicChatAPI : DotNetAPI {
try ! Storage . writeSync { transaction in
storage . setUserCount ( memberCount , forPublicChatWithID : " \( server ) . \( channel ) " , in : transaction )
}
let publicChatInfo = Loki PublicChatInfo( displayName : displayName , profilePictureURL : profilePictureURL , memberCount : memberCount )
let publicChatInfo = PublicChatInfo( displayName : displayName , profilePictureURL : profilePictureURL , memberCount : memberCount )
updateProfileIfNeeded ( for : channel , on : server , from : publicChatInfo )
return publicChatInfo
}