@ -24,6 +24,7 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
 
		
	
		
			
				    
 
		
	
		
			
				    //   MARK:   D a t a b a s e 
 
		
	
		
			
				    override  internal  class  var  authTokenCollection :  String  {  " LokiGroupChatAuthTokenCollection "  } 
 
		
	
		
			
				    
 
		
	
		
			
				    @objc  public  static  let  lastMessageServerIDCollection  =  " LokiGroupChatLastMessageServerIDCollection " 
 
		
	
		
			
				    @objc  public  static  let  lastDeletionServerIDCollection  =  " LokiGroupChatLastDeletionServerIDCollection " 
 
		
	
		
			
				    
 
		
	
	
		
			
				
					
						
							
								 
						
						
							
								 
						
						
					 
				
			
			@ -72,7 +73,7 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
 
		
	
		
			
				        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 : ) 
 
		
	
		
			
				    public  static  func  objc_getMessages ( for  group :  UInt64 ,  on  server :  String )  ->  AnyPromise  { 
 
		
	
		
			
				        return  AnyPromise . from ( getMessages ( for :  group ,  on :  server ) ) 
 
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
			
			@ -85,80 +86,84 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
 
		
	
		
			
				        }  else  { 
 
		
	
		
			
				            queryParameters  +=  " &count= \( fallbackBatchCount ) &include_deleted=0 " 
 
		
	
		
			
				        } 
 
		
	
		
			
				        let  url  =  URL ( string :  " \( server ) /channels/ \( channel ) /messages? \( queryParameters ) " ) ! 
 
		
	
		
			
				        let  request  =  TSRequest ( url :  url ) 
 
		
	
		
			
				        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  { 
 
		
	
		
			
				                print ( " [Loki] Couldn't parse messages for public chat channel with ID:  \( channel )  on server:  \( server )  from:  \( rawResponse ) . " ) 
 
		
	
		
			
				                throw  LokiDotNetAPIError . parsingFailed 
 
		
	
		
			
				            } 
 
		
	
		
			
				            return  rawMessages . flatMap  {  message  in 
 
		
	
		
			
				                let  isDeleted  =  ( message [ " is_deleted " ]  as ?  Int  = =  1 ) 
 
		
	
		
			
				                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  hexEncodedPublicKey  =  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 :  LokiPublicChatMessage . 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  =  LokiPublicChatMessage . 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 :  LokiPublicChatMessage . Quote ? 
 
		
	
		
			
				                if  let  quoteAsJSON  =  value [ " quote " ]  as ?  JSON ,  let  quotedMessageTimestamp  =  quoteAsJSON [ " id " ]  as ?  UInt64 ,  let  quoteeHexEncodedPublicKey  =  quoteAsJSON [ " author " ]  as ?  String , 
 
		
	
		
			
				                    let  quotedMessageBody  =  quoteAsJSON [ " text " ]  as ?  String  { 
 
		
	
		
			
				                    let  quotedMessageServerID  =  message [ " reply_to " ]  as ?  UInt64 
 
		
	
		
			
				                    quote  =  LokiPublicChatMessage . Quote ( quotedMessageTimestamp :  quotedMessageTimestamp ,  quoteeHexEncodedPublicKey :  quoteeHexEncodedPublicKey ,  quotedMessageBody :  quotedMessageBody , 
 
		
	
		
			
				                        quotedMessageServerID :  quotedMessageServerID ) 
 
		
	
		
			
				                }  else  { 
 
		
	
		
			
				                    quote  =  nil 
 
		
	
		
			
				        return  getAuthToken ( for :  server ) . then  {  token  ->  Promise < [ LokiPublicChatMessage ] >  in 
 
		
	
		
			
				            let  url  =  URL ( string :  " \( server ) /channels/ \( channel ) /messages? \( queryParameters ) " ) ! 
 
		
	
		
			
				            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 
 
		
	
		
			
				                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 ) . " ) 
 
		
	
		
			
				                    throw  LokiDotNetAPIError . parsingFailed 
 
		
	
		
			
				                } 
 
		
	
		
			
				                let  signature  =  LokiPublicChatMessage . Signature ( data :  Data ( hex :  hexEncodedSignatureData ) ,  version :  signatureVersion ) 
 
		
	
		
			
				                let  attachmentsAsJSON  =  annotations . filter  {  $0 [ " type " ]  as ?  String  = =  attachmentType  } 
 
		
	
		
			
				                let  attachments :  [ LokiPublicChatMessage . Attachment ]  =  attachmentsAsJSON . compactMap  {  attachmentAsJSON  in 
 
		
	
		
			
				                    guard  let  value  =  attachmentAsJSON [ " value " ]  as ?  JSON ,  let  kindAsString  =  value [ " lokiType " ]  as ?  String ,  let  kind  =  LokiPublicChatMessage . 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 
 
		
	
		
			
				                    let  height  =  value [ " height " ]  as ?  UInt  ? ?  0 
 
		
	
		
			
				                    let  flags  =  ( value [ " flags " ]  as ?  UInt )  ? ?  0 
 
		
	
		
			
				                    let  caption  =  value [ " caption " ]  as ?  String 
 
		
	
		
			
				                    let  linkPreviewURL  =  value [ " linkPreviewUrl " ]  as ?  String 
 
		
	
		
			
				                    let  linkPreviewTitle  =  value [ " linkPreviewTitle " ]  as ?  String 
 
		
	
		
			
				                    if  kind  = =  . linkPreview  { 
 
		
	
		
			
				                        guard  linkPreviewURL  !=  nil  &&  linkPreviewTitle  !=  nil  else  { 
 
		
	
		
			
				                            print ( " [Loki] Ignoring public chat message with invalid link preview. " ) 
 
		
	
		
			
				                return  rawMessages . flatMap  {  message  in 
 
		
	
		
			
				                    let  isDeleted  =  ( message [ " is_deleted " ]  as ?  Int  = =  1 ) 
 
		
	
		
			
				                    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  hexEncodedPublicKey  =  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 :  LokiPublicChatMessage . 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  =  LokiPublicChatMessage . 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 :  LokiPublicChatMessage . Quote ? 
 
		
	
		
			
				                    if  let  quoteAsJSON  =  value [ " quote " ]  as ?  JSON ,  let  quotedMessageTimestamp  =  quoteAsJSON [ " id " ]  as ?  UInt64 ,  let  quoteeHexEncodedPublicKey  =  quoteAsJSON [ " author " ]  as ?  String , 
 
		
	
		
			
				                        let  quotedMessageBody  =  quoteAsJSON [ " text " ]  as ?  String  { 
 
		
	
		
			
				                        let  quotedMessageServerID  =  message [ " reply_to " ]  as ?  UInt64 
 
		
	
		
			
				                        quote  =  LokiPublicChatMessage . Quote ( quotedMessageTimestamp :  quotedMessageTimestamp ,  quoteeHexEncodedPublicKey :  quoteeHexEncodedPublicKey ,  quotedMessageBody :  quotedMessageBody , 
 
		
	
		
			
				                            quotedMessageServerID :  quotedMessageServerID ) 
 
		
	
		
			
				                    }  else  { 
 
		
	
		
			
				                        quote  =  nil 
 
		
	
		
			
				                    } 
 
		
	
		
			
				                    let  signature  =  LokiPublicChatMessage . Signature ( data :  Data ( hex :  hexEncodedSignatureData ) ,  version :  signatureVersion ) 
 
		
	
		
			
				                    let  attachmentsAsJSON  =  annotations . filter  {  $0 [ " type " ]  as ?  String  = =  attachmentType  } 
 
		
	
		
			
				                    let  attachments :  [ LokiPublicChatMessage . Attachment ]  =  attachmentsAsJSON . compactMap  {  attachmentAsJSON  in 
 
		
	
		
			
				                        guard  let  value  =  attachmentAsJSON [ " value " ]  as ?  JSON ,  let  kindAsString  =  value [ " lokiType " ]  as ?  String ,  let  kind  =  LokiPublicChatMessage . 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 
 
		
	
		
			
				                        let  height  =  value [ " height " ]  as ?  UInt  ? ?  0 
 
		
	
		
			
				                        let  flags  =  ( value [ " flags " ]  as ?  UInt )  ? ?  0 
 
		
	
		
			
				                        let  caption  =  value [ " caption " ]  as ?  String 
 
		
	
		
			
				                        let  linkPreviewURL  =  value [ " linkPreviewUrl " ]  as ?  String 
 
		
	
		
			
				                        let  linkPreviewTitle  =  value [ " linkPreviewTitle " ]  as ?  String 
 
		
	
		
			
				                        if  kind  = =  . linkPreview  { 
 
		
	
		
			
				                            guard  linkPreviewURL  !=  nil  &&  linkPreviewTitle  !=  nil  else  { 
 
		
	
		
			
				                                print ( " [Loki] Ignoring public chat message with invalid link preview. " ) 
 
		
	
		
			
				                                return  nil 
 
		
	
		
			
				                            } 
 
		
	
		
			
				                        } 
 
		
	
		
			
				                        return  LokiPublicChatMessage . 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 ) 
 
		
	
		
			
				                    } 
 
		
	
		
			
				                    return  LokiPublicChatMessage . 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  =  LokiPublicChatMessage ( serverID :  serverID ,  hexEncodedPublicKey :  hexEncodedPublicKey ,  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. " ) 
 
		
	
		
			
				                    return  nil 
 
		
	
		
			
				                } 
 
		
	
		
			
				                var  existingMessageID :  String ?  =  nil 
 
		
	
		
			
				                storage . dbReadConnection . read  {  transaction  in 
 
		
	
		
			
				                    existingMessageID  =  storage . getIDForMessage ( withServerID :  UInt ( result . serverID ! ) ,  in :  transaction ) 
 
		
	
		
			
				                } 
 
		
	
		
			
				                guard  existingMessageID  = =  nil  else  { 
 
		
	
		
			
				                    print ( " [Loki] Ignoring duplicate public chat message. " ) 
 
		
	
		
			
				                    return  nil 
 
		
	
		
			
				                } 
 
		
	
		
			
				                return  result 
 
		
	
		
			
				            } . sorted  {  $0 . timestamp  <  $1 . timestamp  } 
 
		
	
		
			
				        } 
 
		
	
		
			
				                    let  result  =  LokiPublicChatMessage ( serverID :  serverID ,  hexEncodedPublicKey :  hexEncodedPublicKey ,  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. " ) 
 
		
	
		
			
				                        return  nil 
 
		
	
		
			
				                    } 
 
		
	
		
			
				                    var  existingMessageID :  String ?  =  nil 
 
		
	
		
			
				                    storage . dbReadConnection . read  {  transaction  in 
 
		
	
		
			
				                        existingMessageID  =  storage . getIDForMessage ( withServerID :  UInt ( result . serverID ! ) ,  in :  transaction ) 
 
		
	
		
			
				                    } 
 
		
	
		
			
				                    guard  existingMessageID  = =  nil  else  { 
 
		
	
		
			
				                        print ( " [Loki] Ignoring duplicate public chat message. " ) 
 
		
	
		
			
				                        return  nil 
 
		
	
		
			
				                    } 
 
		
	
		
			
				                    return  result 
 
		
	
		
			
				                } . sorted  {  $0 . timestamp  <  $1 . timestamp  } 
 
		
	
		
			
				            } 
 
		
	
		
			
				        } . handlingInvalidAuthTokenIfNeeded ( for :  server ) 
 
		
	
		
			
				    } 
 
		
	
		
			
				
 
		
	
		
			
				    //   MARK:   S e n d i n g 
 
		
	
		
			
				    @objc ( sendMessage : toGroup : onServer : ) 
 
		
	
		
			
				    public  static  func  objc_sendMessage ( _  message :  LokiPublicChatMessage ,  to  group :  UInt64 ,  on  server :  String )  ->  AnyPromise  { 
 
		
	
		
			
				        return  AnyPromise . from ( sendMessage ( message ,  to :  group ,  on :  server ) ) 
 
		
	
	
		
			
				
					
						
							
								 
						
						
							
								 
						
						
					 
				
			
			@ -189,13 +194,7 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
 
		
	
		
			
				                        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 ) 
 
		
	
		
			
				                    } 
 
		
	
		
			
				                } . recover  {  error  ->  Promise < LokiPublicChatMessage >  in 
 
		
	
		
			
				                    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 
 
		
	
		
			
				                } 
 
		
	
		
			
				                } . handlingInvalidAuthTokenIfNeeded ( for :  server ) 
 
		
	
		
			
				            } . done  {  message  in 
 
		
	
		
			
				                seal . fulfill ( message ) 
 
		
	
		
			
				            } . catch  {  error  in 
 
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
			
			@ -204,7 +203,8 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
 
		
	
		
			
				        } 
 
		
	
		
			
				        return  promise 
 
		
	
		
			
				    } 
 
		
	
		
			
				    
 
		
	
		
			
				
 
		
	
		
			
				    //   MARK:   D e l e t i o n 
 
		
	
		
			
				    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 ) . " ) 
 
		
	
		
			
				        let  queryParameters :  String 
 
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
			
			@ -213,23 +213,26 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
 
		
	
		
			
				        }  else  { 
 
		
	
		
			
				            queryParameters  =  " count= \( fallbackBatchCount ) " 
 
		
	
		
			
				        } 
 
		
	
		
			
				        let  url  =  URL ( string :  " \( server ) /loki/v1/channel/ \( channel ) /deletes? \( queryParameters ) " ) ! 
 
		
	
		
			
				        let  request  =  TSRequest ( url :  url ) 
 
		
	
		
			
				        return  LokiFileServerProxy ( for :  server ) . perform ( request ) . map  {  rawResponse  in 
 
		
	
		
			
				            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 ) . " ) 
 
		
	
		
			
				                throw  LokiDotNetAPIError . parsingFailed 
 
		
	
		
			
				            } 
 
		
	
		
			
				            return  deletions . flatMap  {  deletion  in 
 
		
	
		
			
				                guard  let  serverID  =  deletion [ " id " ]  as ?  UInt64 ,  let  messageServerID  =  deletion [ " message_id " ]  as ?  UInt64  else  { 
 
		
	
		
			
				                    print ( " [Loki] Couldn't parse deleted message for public chat channel with ID:  \( channel )  on server:  \( server )  from:  \( deletion ) . " ) 
 
		
	
		
			
				                    return  nil 
 
		
	
		
			
				        return  getAuthToken ( for :  server ) . then  {  token  ->  Promise < [ UInt64 ] >  in 
 
		
	
		
			
				            let  url  =  URL ( string :  " \( server ) /loki/v1/channel/ \( channel ) /deletes? \( queryParameters ) " ) ! 
 
		
	
		
			
				            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  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 ) . " ) 
 
		
	
		
			
				                    throw  LokiDotNetAPIError . parsingFailed 
 
		
	
		
			
				                } 
 
		
	
		
			
				                return  deletions . flatMap  {  deletion  in 
 
		
	
		
			
				                    guard  let  serverID  =  deletion [ " id " ]  as ?  UInt64 ,  let  messageServerID  =  deletion [ " message_id " ]  as ?  UInt64  else  { 
 
		
	
		
			
				                        print ( " [Loki] Couldn't parse deleted message for public chat channel with ID:  \( channel )  on server:  \( server )  from:  \( deletion ) . " ) 
 
		
	
		
			
				                        return  nil 
 
		
	
		
			
				                    } 
 
		
	
		
			
				                    let  lastDeletionServerID  =  getLastDeletionServerID ( for :  channel ,  on :  server ) 
 
		
	
		
			
				                    if  serverID  >  ( lastDeletionServerID  ? ?  0 )  {  setLastDeletionServerID ( for :  channel ,  on :  server ,  to :  serverID )  } 
 
		
	
		
			
				                    return  messageServerID 
 
		
	
		
			
				                } 
 
		
	
		
			
				                let  lastDeletionServerID  =  getLastDeletionServerID ( for :  channel ,  on :  server ) 
 
		
	
		
			
				                if  serverID  >  ( lastDeletionServerID  ? ?  0 )  {  setLastDeletionServerID ( for :  channel ,  on :  server ,  to :  serverID )  } 
 
		
	
		
			
				                return  messageServerID 
 
		
	
		
			
				            } 
 
		
	
		
			
				        } 
 
		
	
		
			
				        } . handlingInvalidAuthTokenIfNeeded ( for :  server ) 
 
		
	
		
			
				    } 
 
		
	
		
			
				
 
		
	
		
			
				    @objc ( deleteMessageWithID : forGroup : onServer : isSentByUser : ) 
 
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
			
			@ -238,65 +241,22 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
 
		
	
		
			
				    } 
 
		
	
		
			
				    
 
		
	
		
			
				    public  static  func  deleteMessage ( with  messageID :  UInt ,  for  channel :  UInt64 ,  on  server :  String ,  isSentByUser :  Bool )  ->  Promise < Void >  { 
 
		
	
		
			
				        let  isModerationRequest  =  ! isSentByUser 
 
		
	
		
			
				        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 ) " 
 
		
	
		
			
				        return  attempt ( maxRetryCount :  maxRetryCount ,  recoveringOn :  DispatchQueue . global ( ) )  { 
 
		
	
		
			
				            getAuthToken ( for :  server ) . then  {  token  ->  Promise < Void >  in 
 
		
	
		
			
				                let  isModerationRequest  =  ! isSentByUser 
 
		
	
		
			
				                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  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 ( ) )  { 
 
		
	
		
			
				            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 ) . " ) 
 
		
	
		
			
				                } 
 
		
	
		
			
				            } 
 
		
	
		
			
				        } 
 
		
	
		
			
				    } 
 
		
	
		
			
				    
 
		
	
		
			
				    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:   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 >  { 
 
		
	
		
			
				        let  publicChatID  =  " \( server ) . \( channel ) " 
 
		
	
		
			
				        guard  let  hexEncodedPublicKeys  =  displayNameUpdatees [ publicChatID ]  else  {  return  Promise . value ( ( ) )  } 
 
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
			
			@ -321,12 +281,7 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
 
		
	
		
			
				                    } 
 
		
	
		
			
				                } 
 
		
	
		
			
				            } 
 
		
	
		
			
				        } 
 
		
	
		
			
				    } 
 
		
	
		
			
				    
 
		
	
		
			
				    @objc ( isUserModerator : forChannel : onServer : ) 
 
		
	
		
			
				    public  static  func  isUserModerator ( _  hexEncodedPublicString :  String ,  for  channel :  UInt64 ,  on  server :  String )  ->  Bool  { 
 
		
	
		
			
				        return  moderators [ server ] ? [ channel ] ? . contains ( hexEncodedPublicString )  ? ?  false 
 
		
	
		
			
				        } . handlingInvalidAuthTokenIfNeeded ( for :  server ) 
 
		
	
		
			
				    } 
 
		
	
		
			
				
 
		
	
		
			
				    @objc ( setDisplayName : on : ) 
 
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
			
			@ -336,9 +291,9 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
 
		
	
		
			
				
 
		
	
		
			
				    public  static  func  setDisplayName ( to  newDisplayName :  String ? ,  on  server :  String )  ->  Promise < Void >  { 
 
		
	
		
			
				        print ( " [Loki] Updating display name on server:  \( server ) . " ) 
 
		
	
		
			
				        let  parameters :  JSON  =  [  " name "  :  ( newDisplayName  ? ?  " " )  ] 
 
		
	
		
			
				        return  attempt ( maxRetryCount :  maxRetryCount ,  recoveringOn :  DispatchQueue . global ( ) )  { 
 
		
	
		
			
				            getAuthToken ( for :  server ) . then  {  token  ->  Promise < Void >  in 
 
		
	
		
			
				                let  parameters :  JSON  =  [  " name "  :  ( newDisplayName  ? ?  " " )  ] 
 
		
	
		
			
				                let  url  =  URL ( string :  " \( server ) /users/me " ) ! 
 
		
	
		
			
				                let  request  =  TSRequest ( url :  url ,  method :  " PATCH " ,  parameters :  parameters ) 
 
		
	
		
			
				                request . allHTTPHeaderFields  =  [  " Content-Type "  :  " application/json " ,  " Authorization "  :  " Bearer  \( token ) "  ] 
 
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
			
			@ -346,7 +301,7 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
 
		
	
		
			
				                    print ( " Couldn't update display name due to error:  \( error ) . " ) 
 
		
	
		
			
				                    throw  error 
 
		
	
		
			
				                } 
 
		
	
		
			
				            } 
 
		
	
		
			
				            } . handlingInvalidAuthTokenIfNeeded ( for :  server ) 
 
		
	
		
			
				        } 
 
		
	
		
			
				    } 
 
		
	
		
			
				
 
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
			
			@ -357,13 +312,13 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
 
		
	
		
			
				
 
		
	
		
			
				    public  static  func  setProfilePictureURL ( to  url :  String ? ,  using  profileKey :  Data ,  on  server :  String )  ->  Promise < Void >  { 
 
		
	
		
			
				        print ( " [Loki] Updating profile picture on server:  \( server ) . " ) 
 
		
	
		
			
				        var  annotation :  JSON  =  [  " type "  :  profilePictureType  ] 
 
		
	
		
			
				        if  let  url  =  url  { 
 
		
	
		
			
				            annotation [ " value " ]  =  [  " profileKey "  :  profileKey . base64EncodedString ( ) ,  " url "  :  url  ] 
 
		
	
		
			
				        } 
 
		
	
		
			
				        let  parameters :  JSON  =  [  " annotations "  :  [  annotation  ]  ] 
 
		
	
		
			
				        return  attempt ( maxRetryCount :  maxRetryCount ,  recoveringOn :  DispatchQueue . global ( ) )  { 
 
		
	
		
			
				            getAuthToken ( for :  server ) . then  {  token  ->  Promise < Void >  in 
 
		
	
		
			
				                var  annotation :  JSON  =  [  " type "  :  profilePictureType  ] 
 
		
	
		
			
				                if  let  url  =  url  { 
 
		
	
		
			
				                    annotation [ " value " ]  =  [  " profileKey "  :  profileKey . base64EncodedString ( ) ,  " url "  :  url  ] 
 
		
	
		
			
				                } 
 
		
	
		
			
				                let  parameters :  JSON  =  [  " annotations "  :  [  annotation  ]  ] 
 
		
	
		
			
				                let  url  =  URL ( string :  " \( server ) /users/me " ) ! 
 
		
	
		
			
				                let  request  =  TSRequest ( url :  url ,  method :  " PATCH " ,  parameters :  parameters ) 
 
		
	
		
			
				                request . allHTTPHeaderFields  =  [  " Content-Type "  :  " application/json " ,  " Authorization "  :  " Bearer  \( token ) "  ] 
 
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
			
			@ -371,39 +326,72 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
 
		
	
		
			
				                    print ( " [Loki] Couldn't update profile picture due to error:  \( error ) . " ) 
 
		
	
		
			
				                    throw  error 
 
		
	
		
			
				                } 
 
		
	
		
			
				            } 
 
		
	
		
			
				            } . handlingInvalidAuthTokenIfNeeded ( for :  server ) 
 
		
	
		
			
				        } 
 
		
	
		
			
				    } 
 
		
	
		
			
				    
 
		
	
		
			
				
 
		
	
		
			
				    //   MARK:   J o i n i n g   &   L e a v i n g 
 
		
	
		
			
				    @objc ( getInfoForChannelWithID : onServer : ) 
 
		
	
		
			
				    public  static  func  objc_getInfo ( for  channel :  UInt64 ,  on  server :  String )  ->  AnyPromise  { 
 
		
	
		
			
				        return  AnyPromise . from ( getInfo ( for :  channel ,  on :  server ) ) 
 
		
	
		
			
				    } 
 
		
	
		
			
				    
 
		
	
		
			
				    public  static  func  getInfo ( for  channel :  UInt64 ,  on  server :  String )  ->  Promise < LokiPublicChatInfo >  { 
 
		
	
		
			
				        let  url  =  URL ( string :  " \( server ) /channels/ \( channel ) ?include_annotations=1 " ) ! 
 
		
	
		
			
				        let  request  =  TSRequest ( url :  url ) 
 
		
	
		
			
				        return  LokiFileServerProxy ( for :  server ) . perform ( request ) . map  {  rawResponse  in 
 
		
	
		
			
				            guard  let  json  =  rawResponse  as ?  JSON , 
 
		
	
		
			
				                let  data  =  json [ " data " ]  as ?  JSON , 
 
		
	
		
			
				                let  annotations  =  data [ " annotations " ]  as ?  [ JSON ] , 
 
		
	
		
			
				                let  annotation  =  annotations . first , 
 
		
	
		
			
				                let  info  =  annotation [ " value " ]  as ?  JSON , 
 
		
	
		
			
				                let  displayName  =  info [ " name " ]  as ?  String , 
 
		
	
		
			
				                let  countInfo  =  data [ " counts " ]  as ?  JSON , 
 
		
	
		
			
				                let  memberCount  =  countInfo [ " subscribers " ]  as ?  Int  else  { 
 
		
	
		
			
				                print ( " [Loki] Couldn't parse info for public chat channel with ID:  \( channel )  on server:  \( server )  from:  \( rawResponse ) . " ) 
 
		
	
		
			
				                throw  LokiDotNetAPIError . parsingFailed 
 
		
	
		
			
				            } 
 
		
	
		
			
				            let  storage  =  OWSPrimaryStorage . shared ( ) 
 
		
	
		
			
				            storage . dbReadWriteConnection . readWrite  {  transaction  in 
 
		
	
		
			
				                storage . setUserCount ( memberCount ,  forPublicChatWithID :  " \( server ) . \( channel ) " ,  in :  transaction ) 
 
		
	
		
			
				            } 
 
		
	
		
			
				            //   TODO:   U s e   t h i s   t o   u p d a t e   o p e n   g r o u p   n a m e s   a s   n e e d e d 
 
		
	
		
			
				            return  LokiPublicChatInfo ( displayName :  displayName ,  memberCount :  memberCount ) 
 
		
	
		
			
				        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  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  data  =  json [ " data " ]  as ?  JSON , 
 
		
	
		
			
				                        let  annotations  =  data [ " annotations " ]  as ?  [ JSON ] , 
 
		
	
		
			
				                        let  annotation  =  annotations . first , 
 
		
	
		
			
				                        let  info  =  annotation [ " value " ]  as ?  JSON , 
 
		
	
		
			
				                        let  displayName  =  info [ " name " ]  as ?  String , 
 
		
	
		
			
				                        let  countInfo  =  data [ " counts " ]  as ?  JSON , 
 
		
	
		
			
				                        let  memberCount  =  countInfo [ " subscribers " ]  as ?  Int  else  { 
 
		
	
		
			
				                        print ( " [Loki] Couldn't parse info for public chat channel with ID:  \( channel )  on server:  \( server )  from:  \( rawResponse ) . " ) 
 
		
	
		
			
				                        throw  LokiDotNetAPIError . parsingFailed 
 
		
	
		
			
				                    } 
 
		
	
		
			
				                    let  storage  =  OWSPrimaryStorage . shared ( ) 
 
		
	
		
			
				                    storage . dbReadWriteConnection . readWrite  {  transaction  in 
 
		
	
		
			
				                        storage . setUserCount ( memberCount ,  forPublicChatWithID :  " \( server ) . \( channel ) " ,  in :  transaction ) 
 
		
	
		
			
				                    } 
 
		
	
		
			
				                    //   TODO:   U s e   t h i s   t o   u p d a t e   o p e n   g r o u p   n a m e s   a s   n e e d e d 
 
		
	
		
			
				                    return  LokiPublicChatInfo ( displayName :  displayName ,  memberCount :  memberCount ) 
 
		
	
		
			
				                } 
 
		
	
		
			
				            } . handlingInvalidAuthTokenIfNeeded ( for :  server ) 
 
		
	
		
			
				        } 
 
		
	
		
			
				    } 
 
		
	
		
			
				
 
		
	
		
			
				    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 : ) 
 
		
	
		
			
				    public  static  func  objc_reportMessageWithID ( _  messageID :  UInt64 ,  in  channel :  UInt64 ,  on  server :  String )  ->  AnyPromise  { 
 
		
	
		
			
				        return  AnyPromise . from ( reportMessageWithID ( messageID ,  in :  channel ,  on :  server ) ) 
 
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
			
			@ -412,6 +400,34 @@ public final class LokiPublicChatAPI : LokiDotNetAPI {
 
		
	
		
			
				    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  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  } 
 
		
	
		
			
				    } 
 
		
	
		
			
				
 
		
	
		
			
				    //   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 
 
		
	
		
			
				    } 
 
		
	
		
			
				}