@ -25,6 +25,7 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
// MARK: D a t a b a s e
// MARK: D a t a b a s e
override internal class var authTokenCollection : String { " LokiGroupChatAuthTokenCollection " }
override internal class var authTokenCollection : String { " LokiGroupChatAuthTokenCollection " }
@objc public static let lastMessageServerIDCollection = " LokiGroupChatLastMessageServerIDCollection "
@objc public static let lastMessageServerIDCollection = " LokiGroupChatLastMessageServerIDCollection "
@objc public static let lastDeletionServerIDCollection = " LokiGroupChatLastDeletionServerIDCollection "
@objc public static let lastDeletionServerIDCollection = " LokiGroupChatLastDeletionServerIDCollection "
@ -73,7 +74,7 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
removeLastDeletionServerID ( for : channel , on : server )
removeLastDeletionServerID ( for : channel , on : server )
}
}
// MARK: Pu b l i c A P I
// MARK: Re c e i v i n g
@objc ( getMessagesForGroup : onServer : )
@objc ( getMessagesForGroup : onServer : )
public static func objc_getMessages ( for group : UInt64 , on server : String ) -> AnyPromise {
public static func objc_getMessages ( for group : UInt64 , on server : String ) -> AnyPromise {
return AnyPromise . from ( getMessages ( for : group , on : server ) )
return AnyPromise . from ( getMessages ( for : group , on : server ) )
@ -86,8 +87,10 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
} else {
} else {
queryParameters += " &count= \( fallbackBatchCount ) &include_deleted=0 "
queryParameters += " &count= \( fallbackBatchCount ) &include_deleted=0 "
}
}
return getAuthToken ( for : server ) . then { token -> Promise < [ LokiPublicChatMessage ] > in
let url = URL ( string : " \( server ) /channels/ \( channel ) /messages? \( queryParameters ) " ) !
let url = URL ( string : " \( server ) /channels/ \( channel ) /messages? \( queryParameters ) " ) !
let request = TSRequest ( url : url )
let request = TSRequest ( url : url )
request . allHTTPHeaderFields = [ " Content-Type " : " application/json " , " Authorization " : " Bearer \( token ) " ]
return LokiFileServerProxy ( for : server ) . perform ( request ) . map ( on : DispatchQueue . global ( ) ) { rawResponse in
return LokiFileServerProxy ( for : server ) . perform ( request ) . map ( on : DispatchQueue . global ( ) ) { rawResponse in
guard let json = rawResponse as ? JSON , let rawMessages = json [ " data " ] as ? [ JSON ] else {
guard let json = rawResponse as ? JSON , let rawMessages = json [ " data " ] as ? [ JSON ] else {
print ( " [Loki] Couldn't parse messages for public chat channel with ID: \( channel ) on server: \( server ) from: \( rawResponse ) . " )
print ( " [Loki] Couldn't parse messages for public chat channel with ID: \( channel ) on server: \( server ) from: \( rawResponse ) . " )
@ -158,8 +161,10 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
return result
return result
} . sorted { $0 . timestamp < $1 . timestamp }
} . sorted { $0 . timestamp < $1 . timestamp }
}
}
} . handlingInvalidAuthTokenIfNeeded ( for : server )
}
}
// MARK: S e n d i n g
@objc ( sendMessage : toGroup : onServer : )
@objc ( sendMessage : toGroup : onServer : )
public static func objc_sendMessage ( _ message : LokiPublicChatMessage , to group : UInt64 , on server : String ) -> AnyPromise {
public static func objc_sendMessage ( _ message : LokiPublicChatMessage , to group : UInt64 , on server : String ) -> AnyPromise {
return AnyPromise . from ( sendMessage ( message , to : group , on : server ) )
return AnyPromise . from ( sendMessage ( message , to : group , on : server ) )
@ -190,13 +195,7 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
let timestamp = UInt64 ( date . timeIntervalSince1970 ) * 1000
let timestamp = UInt64 ( date . timeIntervalSince1970 ) * 1000
return LokiPublicChatMessage ( serverID : serverID , hexEncodedPublicKey : getUserHexEncodedPublicKey ( ) , displayName : displayName , profilePicture : signedMessage . profilePicture , body : body , type : publicChatMessageType , timestamp : timestamp , quote : signedMessage . quote , attachments : signedMessage . attachments , signature : signedMessage . signature )
return LokiPublicChatMessage ( serverID : serverID , hexEncodedPublicKey : getUserHexEncodedPublicKey ( ) , displayName : displayName , profilePicture : signedMessage . profilePicture , body : body , type : publicChatMessageType , timestamp : timestamp , quote : signedMessage . quote , attachments : signedMessage . attachments , signature : signedMessage . signature )
}
}
} . recover { error -> Promise < LokiPublicChatMessage > in
} . handlingInvalidAuthTokenIfNeeded ( for : server )
if let error = error as ? NetworkManagerError , error . statusCode = = 401 {
print ( " [Loki] Group chat auth token for: \( server ) expired; dropping it. " )
storage . dbReadWriteConnection . removeObject ( forKey : server , inCollection : authTokenCollection )
}
throw error
}
} . done { message in
} . done { message in
seal . fulfill ( message )
seal . fulfill ( message )
} . catch { error in
} . catch { error in
@ -206,6 +205,7 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
return promise
return promise
}
}
// MARK: D e l e t i o n
public static func getDeletedMessageServerIDs ( for channel : UInt64 , on server : String ) -> Promise < [ UInt64 ] > {
public static func getDeletedMessageServerIDs ( for channel : UInt64 , on server : String ) -> Promise < [ UInt64 ] > {
print ( " [Loki] Getting deleted messages for public chat channel with ID: \( channel ) on server: \( server ) . " )
print ( " [Loki] Getting deleted messages for public chat channel with ID: \( channel ) on server: \( server ) . " )
let queryParameters : String
let queryParameters : String
@ -214,8 +214,10 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
} else {
} else {
queryParameters = " count= \( fallbackBatchCount ) "
queryParameters = " count= \( fallbackBatchCount ) "
}
}
return getAuthToken ( for : server ) . then { token -> Promise < [ UInt64 ] > in
let url = URL ( string : " \( server ) /loki/v1/channel/ \( channel ) /deletes? \( queryParameters ) " ) !
let url = URL ( string : " \( server ) /loki/v1/channel/ \( channel ) /deletes? \( queryParameters ) " ) !
let request = TSRequest ( url : url )
let request = TSRequest ( url : url )
request . allHTTPHeaderFields = [ " Content-Type " : " application/json " , " Authorization " : " Bearer \( token ) " ]
return LokiFileServerProxy ( for : server ) . perform ( request ) . map { rawResponse in
return LokiFileServerProxy ( for : server ) . perform ( request ) . map { rawResponse in
guard let json = rawResponse as ? JSON , let deletions = json [ " data " ] as ? [ JSON ] else {
guard let json = rawResponse as ? JSON , let deletions = json [ " data " ] as ? [ JSON ] else {
print ( " [Loki] Couldn't parse deleted messages for public chat channel with ID: \( channel ) on server: \( server ) from: \( rawResponse ) . " )
print ( " [Loki] Couldn't parse deleted messages for public chat channel with ID: \( channel ) on server: \( server ) from: \( rawResponse ) . " )
@ -231,6 +233,7 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
return messageServerID
return messageServerID
}
}
}
}
} . handlingInvalidAuthTokenIfNeeded ( for : server )
}
}
@objc ( deleteMessageWithID : forGroup : onServer : isSentByUser : )
@objc ( deleteMessageWithID : forGroup : onServer : isSentByUser : )
@ -239,65 +242,22 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
}
}
public static func deleteMessage ( with messageID : UInt , for channel : UInt64 , on server : String , isSentByUser : Bool ) -> Promise < Void > {
public static func deleteMessage ( with messageID : UInt , for channel : UInt64 , on server : String , isSentByUser : Bool ) -> Promise < Void > {
return attempt ( maxRetryCount : maxRetryCount , recoveringOn : DispatchQueue . global ( ) ) {
getAuthToken ( for : server ) . then { token -> Promise < Void > in
let isModerationRequest = ! isSentByUser
let isModerationRequest = ! isSentByUser
print ( " [Loki] Deleting message with ID: \( messageID ) for public chat channel with ID: \( channel ) on server: \( server ) (isModerationRequest = \( isModerationRequest ) ). " )
print ( " [Loki] Deleting message with ID: \( messageID ) for public chat channel with ID: \( channel ) on server: \( server ) (isModerationRequest = \( isModerationRequest ) ). " )
let urlAsString = isSentByUser ? " \( server ) /channels/ \( channel ) /messages/ \( messageID ) " : " \( server ) /loki/v1/moderation/message/ \( messageID ) "
let urlAsString = isSentByUser ? " \( server ) /channels/ \( channel ) /messages/ \( messageID ) " : " \( server ) /loki/v1/moderation/message/ \( messageID ) "
let url = URL ( string : urlAsString ) !
let request = TSRequest ( url : url , method : " DELETE " , parameters : [ : ] )
request . allHTTPHeaderFields = [ " Content-Type " : " application/json " , " Authorization " : " Bearer \( token ) " ]
return LokiFileServerProxy ( for : server ) . perform ( request ) . done { result -> Void in
print ( " [Loki] Deleted message with ID: \( messageID ) on server: \( server ) . " )
}
}
}
}
public static func getModerators ( for channel : UInt64 , on server : String ) -> Promise < Set < String > > {
let url = URL ( string : " \( server ) /loki/v1/channel/ \( channel ) /get_moderators " ) !
let request = TSRequest ( url : url )
return LokiFileServerProxy ( for : server ) . perform ( request ) . map { rawResponse in
guard let json = rawResponse as ? JSON , let moderators = json [ " moderators " ] as ? [ String ] else {
print ( " [Loki] Couldn't parse moderators for public chat channel with ID: \( channel ) on server: \( server ) from: \( rawResponse ) . " )
throw LokiDotNetAPIError . parsingFailed
}
let moderatorAsSet = Set ( moderators ) ;
if self . moderators . keys . contains ( server ) {
self . moderators [ server ] ! [ channel ] = moderatorAsSet
} else {
self . moderators [ server ] = [ channel : moderatorAsSet ]
}
return moderatorAsSet
}
}
public static func join ( _ channel : UInt64 , on server : String ) -> Promise < Void > {
return attempt ( maxRetryCount : maxRetryCount , recoveringOn : DispatchQueue . global ( ) ) {
return attempt ( maxRetryCount : maxRetryCount , recoveringOn : DispatchQueue . global ( ) ) {
getAuthToken ( for : server ) . then { token -> Promise < Void > in
getAuthToken ( for : server ) . then { token -> Promise < Void > in
let url = URL ( string : " \( server ) /channels/ \( channel ) /subscribe " ) !
let url = URL ( string : urlAsString ) !
let request = TSRequest ( url : url , method : " POST " , parameters : [ : ] )
request . allHTTPHeaderFields = [ " Content-Type " : " application/json " , " Authorization " : " Bearer \( token ) " ]
return LokiFileServerProxy ( for : server ) . perform ( request ) . done { result -> Void in
print ( " [Loki] Joined channel with ID: \( channel ) on server: \( server ) . " )
}
}
}
}
public static func leave ( _ channel : UInt64 , on server : String ) -> Promise < Void > {
return attempt ( maxRetryCount : maxRetryCount , recoveringOn : DispatchQueue . global ( ) ) {
getAuthToken ( for : server ) . then { token -> Promise < Void > in
let url = URL ( string : " \( server ) /channels/ \( channel ) /subscribe " ) !
let request = TSRequest ( url : url , method : " DELETE " , parameters : [ : ] )
let request = TSRequest ( url : url , method : " DELETE " , parameters : [ : ] )
request . allHTTPHeaderFields = [ " Content-Type " : " application/json " , " Authorization " : " Bearer \( token ) " ]
request . allHTTPHeaderFields = [ " Content-Type " : " application/json " , " Authorization " : " Bearer \( token ) " ]
return LokiFileServerProxy ( for : server ) . perform ( request ) . done { result -> Void in
return LokiFileServerProxy ( for : server ) . perform ( request ) . done { result -> Void in
print ( " [Loki] Left channel with ID: \( channel ) on server: \( server ) . " )
print ( " [Loki] Deleted message with ID: \( messageID ) on server: \( server ) . " )
}
}
}
} . handlingInvalidAuthTokenIfNeeded ( for : server )
}
}
}
}
// MARK: D i s p l a y N a m e & P r o f i l e P i c t u r e
public static func getDisplayNames ( for channel : UInt64 , on server : String ) -> Promise < Void > {
public static func getDisplayNames ( for channel : UInt64 , on server : String ) -> Promise < Void > {
let publicChatID = " \( server ) . \( channel ) "
let publicChatID = " \( server ) . \( channel ) "
guard let hexEncodedPublicKeys = displayNameUpdatees [ publicChatID ] else { return Promise . value ( ( ) ) }
guard let hexEncodedPublicKeys = displayNameUpdatees [ publicChatID ] else { return Promise . value ( ( ) ) }
@ -322,12 +282,7 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
}
}
}
}
}
}
}
} . handlingInvalidAuthTokenIfNeeded ( for : server )
}
@objc ( isUserModerator : forChannel : onServer : )
public static func isUserModerator ( _ hexEncodedPublicString : String , for channel : UInt64 , on server : String ) -> Bool {
return moderators [ server ] ? [ channel ] ? . contains ( hexEncodedPublicString ) ? ? false
}
}
@objc ( setDisplayName : on : )
@objc ( setDisplayName : on : )
@ -337,9 +292,9 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
public static func setDisplayName ( to newDisplayName : String ? , on server : String ) -> Promise < Void > {
public static func setDisplayName ( to newDisplayName : String ? , on server : String ) -> Promise < Void > {
print ( " [Loki] Updating display name on server: \( server ) . " )
print ( " [Loki] Updating display name on server: \( server ) . " )
let parameters : JSON = [ " name " : ( newDisplayName ? ? " " ) ]
return attempt ( maxRetryCount : maxRetryCount , recoveringOn : DispatchQueue . global ( ) ) {
return attempt ( maxRetryCount : maxRetryCount , recoveringOn : DispatchQueue . global ( ) ) {
getAuthToken ( for : server ) . then { token -> Promise < Void > in
getAuthToken ( for : server ) . then { token -> Promise < Void > in
let parameters : JSON = [ " name " : ( newDisplayName ? ? " " ) ]
let url = URL ( string : " \( server ) /users/me " ) !
let url = URL ( string : " \( server ) /users/me " ) !
let request = TSRequest ( url : url , method : " PATCH " , parameters : parameters )
let request = TSRequest ( url : url , method : " PATCH " , parameters : parameters )
request . allHTTPHeaderFields = [ " Content-Type " : " application/json " , " Authorization " : " Bearer \( token ) " ]
request . allHTTPHeaderFields = [ " Content-Type " : " application/json " , " Authorization " : " Bearer \( token ) " ]
@ -347,7 +302,7 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
print ( " Couldn't update display name due to error: \( error ) . " )
print ( " Couldn't update display name due to error: \( error ) . " )
throw error
throw error
}
}
}
} . handlingInvalidAuthTokenIfNeeded ( for : server )
}
}
}
}
@ -358,13 +313,13 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
public static func setProfilePictureURL ( to url : String ? , using profileKey : Data , on server : String ) -> Promise < Void > {
public static func setProfilePictureURL ( to url : String ? , using profileKey : Data , on server : String ) -> Promise < Void > {
print ( " [Loki] Updating profile picture on server: \( server ) . " )
print ( " [Loki] Updating profile picture on server: \( server ) . " )
return attempt ( maxRetryCount : maxRetryCount , recoveringOn : DispatchQueue . global ( ) ) {
getAuthToken ( for : server ) . then { token -> Promise < Void > in
var annotation : JSON = [ " type " : profilePictureType ]
var annotation : JSON = [ " type " : profilePictureType ]
if let url = url {
if let url = url {
annotation [ " value " ] = [ " profileKey " : profileKey . base64EncodedString ( ) , " url " : url ]
annotation [ " value " ] = [ " profileKey " : profileKey . base64EncodedString ( ) , " url " : url ]
}
}
let parameters : JSON = [ " annotations " : [ annotation ] ]
let parameters : JSON = [ " annotations " : [ annotation ] ]
return attempt ( maxRetryCount : maxRetryCount , recoveringOn : DispatchQueue . global ( ) ) {
getAuthToken ( for : server ) . then { token -> Promise < Void > in
let url = URL ( string : " \( server ) /users/me " ) !
let url = URL ( string : " \( server ) /users/me " ) !
let request = TSRequest ( url : url , method : " PATCH " , parameters : parameters )
let request = TSRequest ( url : url , method : " PATCH " , parameters : parameters )
request . allHTTPHeaderFields = [ " Content-Type " : " application/json " , " Authorization " : " Bearer \( token ) " ]
request . allHTTPHeaderFields = [ " Content-Type " : " application/json " , " Authorization " : " Bearer \( token ) " ]
@ -372,18 +327,22 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
print ( " [Loki] Couldn't update profile picture due to error: \( error ) . " )
print ( " [Loki] Couldn't update profile picture due to error: \( error ) . " )
throw error
throw error
}
}
}
} . handlingInvalidAuthTokenIfNeeded ( for : server )
}
}
}
}
// MARK: J o i n i n g & L e a v i n g
@objc ( getInfoForChannelWithID : onServer : )
@objc ( getInfoForChannelWithID : onServer : )
public static func objc_getInfo ( for channel : UInt64 , on server : String ) -> AnyPromise {
public static func objc_getInfo ( for channel : UInt64 , on server : String ) -> AnyPromise {
return AnyPromise . from ( getInfo ( for : channel , on : server ) )
return AnyPromise . from ( getInfo ( for : channel , on : server ) )
}
}
public static func getInfo ( for channel : UInt64 , on server : String ) -> Promise < LokiPublicChatInfo > {
public static func getInfo ( for channel : UInt64 , on server : String ) -> Promise < LokiPublicChatInfo > {
return attempt ( maxRetryCount : maxRetryCount , recoveringOn : DispatchQueue . global ( ) ) {
getAuthToken ( for : server ) . then { token -> Promise < LokiPublicChatInfo > in
let url = URL ( string : " \( server ) /channels/ \( channel ) ?include_annotations=1 " ) !
let url = URL ( string : " \( server ) /channels/ \( channel ) ?include_annotations=1 " ) !
let request = TSRequest ( url : url )
let request = TSRequest ( url : url )
request . allHTTPHeaderFields = [ " Content-Type " : " application/json " , " Authorization " : " Bearer \( token ) " ]
return LokiFileServerProxy ( for : server ) . perform ( request ) . map { rawResponse in
return LokiFileServerProxy ( for : server ) . perform ( request ) . map { rawResponse in
guard let json = rawResponse as ? JSON ,
guard let json = rawResponse as ? JSON ,
let data = json [ " data " ] as ? JSON ,
let data = json [ " data " ] as ? JSON ,
@ -397,10 +356,16 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
print ( " [Loki] Couldn't parse info for public chat channel with ID: \( channel ) on server: \( server ) from: \( rawResponse ) . " )
print ( " [Loki] Couldn't parse info for public chat channel with ID: \( channel ) on server: \( server ) from: \( rawResponse ) . " )
throw LokiDotNetAPIError . parsingFailed
throw LokiDotNetAPIError . parsingFailed
}
}
let storage = OWSPrimaryStorage . shared ( )
storage . dbReadWriteConnection . readWrite { transaction in
storage . setUserCount ( memberCount , forPublicChatWithID : " \( server ) . \( channel ) " , in : transaction )
}
let publicChatInfo = LokiPublicChatInfo ( displayName : displayName , profilePictureURL : profilePictureURL , memberCount : memberCount )
let publicChatInfo = LokiPublicChatInfo ( displayName : displayName , profilePictureURL : profilePictureURL , memberCount : memberCount )
updateOpenGroupProfileIfNeeded ( for : channel , on : server , with : publicChatInfo )
updateOpenGroupProfileIfNeeded ( for : channel , on : server , with : publicChatInfo )
return publicChatInfo
return publicChatInfo
}
}
} . handlingInvalidAuthTokenIfNeeded ( for : server )
}
}
}
static func updateOpenGroupProfileIfNeeded ( for channel : UInt64 , on server : String , with info : LokiPublicChatInfo ) {
static func updateOpenGroupProfileIfNeeded ( for channel : UInt64 , on server : String , with info : LokiPublicChatInfo ) {
@ -451,6 +416,33 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
}
}
}
}
public static func join ( _ channel : UInt64 , on server : String ) -> Promise < Void > {
return attempt ( maxRetryCount : maxRetryCount , recoveringOn : DispatchQueue . global ( ) ) {
getAuthToken ( for : server ) . then { token -> Promise < Void > in
let url = URL ( string : " \( server ) /channels/ \( channel ) /subscribe " ) !
let request = TSRequest ( url : url , method : " POST " , parameters : [ : ] )
request . allHTTPHeaderFields = [ " Content-Type " : " application/json " , " Authorization " : " Bearer \( token ) " ]
return LokiFileServerProxy ( for : server ) . perform ( request ) . done { result -> Void in
print ( " [Loki] Joined channel with ID: \( channel ) on server: \( server ) . " )
}
} . handlingInvalidAuthTokenIfNeeded ( for : server )
}
}
public static func leave ( _ channel : UInt64 , on server : String ) -> Promise < Void > {
return attempt ( maxRetryCount : maxRetryCount , recoveringOn : DispatchQueue . global ( ) ) {
getAuthToken ( for : server ) . then { token -> Promise < Void > in
let url = URL ( string : " \( server ) /channels/ \( channel ) /subscribe " ) !
let request = TSRequest ( url : url , method : " DELETE " , parameters : [ : ] )
request . allHTTPHeaderFields = [ " Content-Type " : " application/json " , " Authorization " : " Bearer \( token ) " ]
return LokiFileServerProxy ( for : server ) . perform ( request ) . done { result -> Void in
print ( " [Loki] Left channel with ID: \( channel ) on server: \( server ) . " )
}
} . handlingInvalidAuthTokenIfNeeded ( for : server )
}
}
// MARK: R e p o r t i n g
@objc ( reportMessageWithID : inChannel : onServer : )
@objc ( reportMessageWithID : inChannel : onServer : )
public static func objc_reportMessageWithID ( _ messageID : UInt64 , in channel : UInt64 , on server : String ) -> AnyPromise {
public static func objc_reportMessageWithID ( _ messageID : UInt64 , in channel : UInt64 , on server : String ) -> AnyPromise {
return AnyPromise . from ( reportMessageWithID ( messageID , in : channel , on : server ) )
return AnyPromise . from ( reportMessageWithID ( messageID , in : channel , on : server ) )
@ -459,6 +451,34 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
public static func reportMessageWithID ( _ messageID : UInt64 , in channel : UInt64 , on server : String ) -> Promise < Void > {
public static func reportMessageWithID ( _ messageID : UInt64 , in channel : UInt64 , on server : String ) -> Promise < Void > {
let url = URL ( string : " \( server ) /loki/v1/channels/ \( channel ) /messages/ \( messageID ) /report " ) !
let url = URL ( string : " \( server ) /loki/v1/channels/ \( channel ) /messages/ \( messageID ) /report " ) !
let request = TSRequest ( url : url , method : " POST " , parameters : [ : ] )
let request = TSRequest ( url : url , method : " POST " , parameters : [ : ] )
// O n l y u s e d f o r t h e L o k i P u b l i c C h a t w h i c h d o e s n ' t r e q u i r e a u t h e n t i c a t i o n
return LokiFileServerProxy ( for : server ) . perform ( request ) . map { _ in }
return LokiFileServerProxy ( for : server ) . perform ( request ) . map { _ in }
}
}
// MARK: M o d e r a t o r s
public static func getModerators ( for channel : UInt64 , on server : String ) -> Promise < Set < String > > {
return getAuthToken ( for : server ) . then { token -> Promise < Set < String > > in
let url = URL ( string : " \( server ) /loki/v1/channel/ \( channel ) /get_moderators " ) !
let request = TSRequest ( url : url )
request . allHTTPHeaderFields = [ " Content-Type " : " application/json " , " Authorization " : " Bearer \( token ) " ]
return LokiFileServerProxy ( for : server ) . perform ( request ) . map { rawResponse in
guard let json = rawResponse as ? JSON , let moderators = json [ " moderators " ] as ? [ String ] else {
print ( " [Loki] Couldn't parse moderators for public chat channel with ID: \( channel ) on server: \( server ) from: \( rawResponse ) . " )
throw LokiDotNetAPIError . parsingFailed
}
let moderatorsAsSet = Set ( moderators ) ;
if self . moderators . keys . contains ( server ) {
self . moderators [ server ] ! [ channel ] = moderatorsAsSet
} else {
self . moderators [ server ] = [ channel : moderatorsAsSet ]
}
return moderatorsAsSet
}
} . handlingInvalidAuthTokenIfNeeded ( for : server )
}
@objc ( isUserModerator : forChannel : onServer : )
public static func isUserModerator ( _ hexEncodedPublicString : String , for channel : UInt64 , on server : String ) -> Bool {
return moderators [ server ] ? [ channel ] ? . contains ( hexEncodedPublicString ) ? ? false
}
}
}