import { IncomingEventType } from './types/incomingMessages' ; import { HistoryMessage , HistoryMessageType } from './types/threadHistory' ; import { ThreadInfo } from './types/threads' ; import { UserID } from './types/users' ; export function formatMessagesGraphQLResponse ( data ) { const messageThread = data . o0 . data . message_thread ; if ( ! messageThread ) throw new Error ( `There was an unknown response. Contact the dev team about this (error code 935531). Data: ${ JSON . stringify ( data ) }` ) ; return messageThread . messages . nodes . map ( ( d ) => { const semiformattedMessage = { type : HistoryMessageType . Unknown , messageId : d . message_id , timestamp : parseInt ( d . timestamp_precise ) , senderId : parseInt ( d . message_sender ?. id ) , isUnread : d . unread , OTID : parseInt ( d . offline_threading_id ) , isSponsored : d . is_sponsored , snippet : d . snippet } ; switch ( d . __typename ) { case 'UserMessage' : { let maybeStickerAttachment ; if ( d . sticker && d . sticker . pack ) { maybeStickerAttachment = [ { type : 'sticker' , ID : d . sticker . id , url : d . sticker . url , packID : d . sticker . pack ? d . sticker . pack . id : null , spriteUrl : d . sticker . sprite_image , spriteUrl2x : d . sticker . sprite_image_2x , width : d . sticker . width , height : d . sticker . height , caption : d . snippet , description : d . sticker . label , frameCount : d . sticker . frame_count , frameRate : d . sticker . frame_rate , framesPerRow : d . sticker . frames_per_row , framesPerCol : d . sticker . frames_per_col , stickerID : d . sticker . id , spriteURI : d . sticker . sprite_image , spriteURI2x : d . sticker . sprite_image_2x } ] ; } semiformattedMessage . type = HistoryMessageType . UserMessage ; semiformattedMessage . userMessage = { attachments : maybeStickerAttachment ?? ( d . blob_attachments && d . blob_attachments . length > 0 ? d . blob_attachments . map ( formatAttachmentsGraphQLResponse ) : undefined ) ?? ( d . extensible_attachment ? [ formatExtensibleAttachment ( d . extensible_attachment ) ] : undefined ) ?? [ ] , textBody : d . message ?. text || undefined , mentions : formatMentionsGraphQL ( d . message ?. text , d . message ?. ranges ) , reactions : d . message_reactions ?. map ( formatReactionsGraphQL ) } ; break ; } case 'ThreadNameMessage' : semiformattedMessage . type = HistoryMessageType . CustomisationEvent ; semiformattedMessage . customisationEvent = { eventType : IncomingEventType . ChangeThreadName , newThreadName : d . thread_name } ; break ; case 'ThreadImageMessage' : semiformattedMessage . type = HistoryMessageType . CustomisationEvent ; semiformattedMessage . customisationEvent = { eventType : IncomingEventType . ChangeThreadImage , newImageInfo : d . image_with_metadata ? { previewUri : d . image_with_metadata . preview ?. uri , attachmentId : parseInt ( d . image_with_metadata . legacy_attachment_id ) , originalDimensions : d . image_with_metadata . original_dimensions } : null } ; break ; case 'ParticipantLeftMessage' : semiformattedMessage . type = HistoryMessageType . AdminEvent ; semiformattedMessage . adminEvent = { eventType : IncomingEventType . RemovedParticipant , participantsRemoved : d . participants_removed ?. map ( ( pr ) => parseInt ( pr . id ) ) } ; break ; case 'ParticipantsAddedMessage' : semiformattedMessage . type = HistoryMessageType . AdminEvent ; semiformattedMessage . adminEvent = { eventType : IncomingEventType . AddedParticipants , participantsAdded : d . participants_added ?. map ( ( pr ) => parseInt ( pr . id ) ) } ; break ; case 'GenericAdminTextMessage' : switch ( d . extensible_message_admin_text_type ) { case 'CHANGE_THREAD_THEME' : semiformattedMessage . type = HistoryMessageType . CustomisationEvent ; semiformattedMessage . customisationEvent = { eventType : IncomingEventType . ChangeThreadColorTheme , newThreadColor : d . extensible_message_admin_text ?. theme_color } ; break ; case 'CHANGE_THREAD_ICON' : semiformattedMessage . type = HistoryMessageType . CustomisationEvent ; semiformattedMessage . customisationEvent = { eventType : IncomingEventType . ChangeThreadEmoji , newThreadEmoji : d . extensible_message_admin_text ?. thread_icon } ; break ; case 'CHANGE_THREAD_ADMINS' : semiformattedMessage . type = HistoryMessageType . AdminEvent ; semiformattedMessage . adminEvent = { eventType : IncomingEventType . ChangeAdminStatus } ; break ; default : break ; } break ; default : break ; } return semiformattedMessage ; } ) ; } function formatReactionsGraphQL ( reaction ) { return { reaction : reaction . reaction , userId : parseInt ( reaction . user . id ) } ; } function formatMentionsGraphQL ( messageText , mentionRawData ) { const mentions = { } ; mentionRawData ?. forEach ( e => ( mentions [ e . entity . id ] = messageText . substr ( e . offset , e . length ) ) ) ; return mentions ; } function formatAttachmentsGraphQLResponse ( attachment ) { switch ( attachment . __typename ) { case 'MessageImage' : return { type : 'photo' , ID : attachment . legacy_attachment_id , filename : attachment . filename , thumbnailUrl : attachment . thumbnail . uri , previewUrl : attachment . preview . uri , previewWidth : attachment . preview . width , previewHeight : attachment . preview . height , largePreviewUrl : attachment . large_preview . uri , largePreviewHeight : attachment . large_preview . height , largePreviewWidth : attachment . large_preview . width , url : attachment . large_preview . uri , width : attachment . large_preview . width , height : attachment . large_preview . height , name : attachment . filename , attributionApp : attachment . attribution_app ? { attributionAppID : attachment . attribution_app . id , name : attachment . attribution_app . name , logo : attachment . attribution_app . square_logo } : null } ; case 'MessageAnimatedImage' : return { type : 'animated_image' , ID : attachment . legacy_attachment_id , filename : attachment . filename , previewUrl : attachment . preview_image . uri , previewWidth : attachment . preview_image . width , previewHeight : attachment . preview_image . height , url : attachment . animated_image . uri , width : attachment . animated_image . width , height : attachment . animated_image . height , thumbnailUrl : attachment . preview_image . uri , name : attachment . filename , facebookUrl : attachment . animated_image . uri , rawGifImage : attachment . animated_image . uri , animatedGifUrl : attachment . animated_image . uri , animatedGifPreviewUrl : attachment . preview_image . uri , animatedWebpUrl : attachment . animated_image . uri , animatedWebpPreviewUrl : attachment . preview_image . uri , attributionApp : attachment . attribution_app ? { attributionAppID : attachment . attribution_app . id , name : attachment . attribution_app . name , logo : attachment . attribution_app . square_logo } : null } ; case 'MessageVideo' : return { type : 'video' , filename : attachment . filename , ID : attachment . legacy_attachment_id , thumbnailUrl : attachment . large_image . uri , previewUrl : attachment . large_image . uri , previewWidth : attachment . large_image . width , previewHeight : attachment . large_image . height , url : attachment . playable_url , width : attachment . original_dimensions . x , height : attachment . original_dimensions . y , duration : attachment . playable_duration_in_ms , videoType : attachment . video_type . toLowerCase ( ) } ; break ; case 'MessageFile' : return { type : 'file' , filename : attachment . filename , ID : attachment . message_file_fbid , url : attachment . url , isMalicious : attachment . is_malicious , contentType : attachment . content_type , name : attachment . filename , mimeType : '' , fileSize : - 1 } ; case 'MessageAudio' : return { type : 'audio' , filename : attachment . filename , ID : attachment . url_shimhash , audioType : attachment . audio_type , duration : attachment . playable_duration_in_ms , url : attachment . playable_url , isVoiceMail : attachment . is_voicemail } ; default : return { error : "Don't know about attachment type " + attachment . __typename } ; } } function formatExtensibleAttachment ( attachment ) { if ( attachment . story_attachment ) { return { type : 'share' , ID : attachment . legacy_attachment_id , url : attachment . story_attachment . url , title : attachment . story_attachment . title_with_entities . text , description : attachment . story_attachment . description && attachment . story_attachment . description . text , source : attachment . story_attachment . source == null ? null : attachment . story_attachment . source . text , image : attachment . story_attachment . media == null ? null : attachment . story_attachment . media . animated_image == null && attachment . story_attachment . media . image == null ? null : ( attachment . story_attachment . media . animated_image || attachment . story_attachment . media . image ) . uri , width : attachment . story_attachment . media == null ? null : attachment . story_attachment . media . animated_image == null && attachment . story_attachment . media . image == null ? null : ( attachment . story_attachment . media . animated_image || attachment . story_attachment . media . image ) . width , height : attachment . story_attachment . media == null ? null : attachment . story_attachment . media . animated_image == null && attachment . story_attachment . media . image == null ? null : ( attachment . story_attachment . media . animated_image || attachment . story_attachment . media . image ) . height , playable : attachment . story_attachment . media == null ? null : attachment . story_attachment . media . is_playable , duration : attachment . story_attachment . media == null ? null : attachment . story_attachment . media . playable_duration_in_ms , playableUrl : attachment . story_attachment . media == null ? null : attachment . story_attachment . media . playable_url , subattachments : attachment . story_attachment . subattachments , properties : attachment . story_attachment . properties . reduce ( ( obj , cur ) => { obj [ cur . key ] = cur . value . text ; return obj ; } , { } ) , animatedImageSize : '' , facebookUrl : '' , styleList : '' , target : '' , thumbnailUrl : attachment . story_attachment . media == null ? null : attachment . story_attachment . media . animated_image == null && attachment . story_attachment . media . image == null ? null : ( attachment . story_attachment . media . animated_image || attachment . story_attachment . media . image ) . uri , thumbnailWidth : attachment . story_attachment . media == null ? null : attachment . story_attachment . media . animated_image == null && attachment . story_attachment . media . image == null ? null : ( attachment . story_attachment . media . animated_image || attachment . story_attachment . media . image ) . width , thumbnailHeight : attachment . story_attachment . media == null ? null : attachment . story_attachment . media . animated_image == null && attachment . story_attachment . media . image == null ? null : ( attachment . story_attachment . media . animated_image || attachment . story_attachment . media . image ) . height } ; } else { return { error : "Don't know what to do with extensible_attachment." } ; } } export function formatThreadInfo ( data ) { const messageThread = data . o0 . data . message_thread ; if ( ! messageThread ) throw new Error ( `There was an unknown response. Contact the dev team about this (error code 935528). Data: ${ JSON . stringify ( data ) }` ) ; const isGroup = messageThread . thread_type ?. toUpperCase ( ) == 'GROUP' ; return { threadId : parseInt ( messageThread . thread_key . thread_fbid || messageThread . thread_key . other_user_id ) , threadName : messageThread . name , participantIds : messageThread . all_participants ?. nodes ?. map ( ( d ) => parseInt ( d . messaging_actor ?. id ) ) , groupAdministration : isGroup ? { approvalMode : ! ! parseInt ( messageThread . approval_mode ) , adminIds : messageThread . thread_admins ?. map ( ( obj ) => parseInt ( obj . id ) ) } : null , unreadCount : messageThread . unread_count , messageCount : messageThread . messages_count , lastUpdateTimestamp : parseInt ( messageThread . updated_time_precise ) , lastMessage : { snippetText : messageThread . last_message ?. nodes [ 0 ] ?. snippet , senderId : parseInt ( messageThread . last_message ?. nodes [ 0 ] ?. message_sender ?. messaging_actor . id ) , timestamp : parseInt ( messageThread . last_message ?. nodes [ 0 ] ?. timestamp_precise ) } , lastReadTimestamp : parseInt ( messageThread . last_read_receipt ?. nodes [ 0 ] . timestamp_precise ) , isGroup , isArchived : messageThread . has_viewer_archived , isSubscribed : messageThread . is_viewer_subscribed , folder : messageThread . folder , customisation : { enabledCustomisation : messageThread . customization_enabled , emoji : messageThread . customization_info . emoji , outgoingBubbleColor : messageThread . customization_info . outgoing_bubble_color , imageUri : messageThread . image ?. uri } , cannotReplyReason : messageThread . cannot_reply_reason , eventReminders : messageThread . event_reminders ?. nodes ?. map ( formatEventReminders ) , muteUntil : parseInt ( messageThread . mute_until ) * 1000 || null , reactionsMuteMode : messageThread . reactions_mute_mode , mentionsMuteMode : messageThread . mentions_mute_mode } ; } function formatEventReminders ( reminder ) { return { reminderID : reminder . id , eventCreatorID : reminder . lightweight_event_creator . id , time : reminder . time , eventType : reminder . lightweight_event_type . toLowerCase ( ) , locationName : reminder . location_name , locationCoordinates : reminder . location_coordinates , locationPage : reminder . location_page , eventStatus : reminder . lightweight_event_status . toLowerCase ( ) , note : reminder . note , repeatMode : reminder . repeat_mode . toLowerCase ( ) , eventTitle : reminder . event_title , triggerMessage : reminder . trigger_message , secondsToNotifyBefore : reminder . seconds_to_notify_before , allowsRsvp : reminder . allows_rsvp , relatedEvent : reminder . related_event , members : reminder . event_reminder_members . edges . map ( ( member ) => ( { memberID : member . node . id , state : member . guest_list_state . toLowerCase ( ) } ) ) } ; }