UNPKG

meta-horizonn-fca-huh

Version:

Facebook-Chat-API Protect and Deploy by Kanzu and HZI Team. Kem is redeveloped. Rename package is Meta Horizonn and package supported ChatBot Messenger. and make it lightweight package by jonell huh

538 lines (486 loc) 20.1 kB
"use strict"; var utils = require("../utils"); var log = require("npmlog"); 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, // You have to query for the real image. See below. url: attachment.large_preview.uri, // @Legacy width: attachment.large_preview.width, // @Legacy height: attachment.large_preview.height, // @Legacy name: attachment.filename, // @Legacy // @Undocumented attributionApp: attachment.attribution_app ? { attributionAppID: attachment.attribution_app.id, name: attachment.attribution_app.name, logo: attachment.attribution_app.square_logo } : null // @TODO No idea what this is, should we expose it? // Ben - July 15th 2017 // renderAsSticker: attachment.render_as_sticker, // This is _not_ the real URI, this is still just a large preview. // To get the URL we'll need to support a POST query to // // https://www.facebook.com/webgraphql/query/ // // With the following query params: // // query_id:728987990612546 // variables:{"id":"100009069356507","photoID":"10213724771692996"} // dpr:1 // // No special form though. }; 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, // @Legacy name: attachment.filename, // @Legacy facebookUrl: attachment.animated_image.uri, // @Legacy rawGifImage: attachment.animated_image.uri, // @Legacy animatedGifUrl: attachment.animated_image.uri, // @Legacy animatedGifPreviewUrl: attachment.preview_image.uri, // @Legacy animatedWebpUrl: attachment.animated_image.uri, // @Legacy animatedWebpPreviewUrl: attachment.preview_image.uri, // @Legacy // @Undocumented 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, // @Legacy 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() }; 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, // @Legacy mimeType: "", // @Legacy fileSize: -1 // @Legacy }; case "MessageAudio": return { type: "audio", filename: attachment.filename, ID: attachment.url_shimhash, // Not fowardable 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, // Format example: // // [{ // key: "width", // value: { text: "1280" } // }] // // That we turn into: // // { // width: "1280" // } // properties: attachment.story_attachment.properties.reduce(function (obj, cur) { obj[cur.key] = cur.value.text; return obj; }, {}), // Deprecated fields animatedImageSize: "", // @Legacy facebookUrl: "", // @Legacy styleList: "", // @Legacy target: "", // @Legacy 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, // @Legacy 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, // @Legacy 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 // @Legacy }; } else return { error: "Don't know what to do with extensible_attachment." }; } function formatReactionsGraphQL(reaction) { return { reaction: reaction.reaction, userID: reaction.user.id }; } function formatEventData(event) { if (event == null) return {}; switch (event.__typename) { case "ThemeColorExtensibleMessageAdminText": return { color: event.theme_color }; case "ThreadNicknameExtensibleMessageAdminText": return { nickname: event.nickname, participantID: event.participant_id }; case "ThreadIconExtensibleMessageAdminText": return { threadIcon: event.thread_icon }; case "InstantGameUpdateExtensibleMessageAdminText": return { gameID: (event.game == null ? null : event.game.id), update_type: event.update_type, collapsed_text: event.collapsed_text, expanded_text: event.expanded_text, instant_game_update_data: event.instant_game_update_data }; case "GameScoreExtensibleMessageAdminText": return { game_type: event.game_type }; case "RtcCallLogExtensibleMessageAdminText": return { event: event.event, is_video_call: event.is_video_call, server_info_data: event.server_info_data }; case "GroupPollExtensibleMessageAdminText": return { event_type: event.event_type, total_count: event.total_count, question: event.question }; case "AcceptPendingThreadExtensibleMessageAdminText": return { accepter_id: event.accepter_id, requester_id: event.requester_id }; case "ConfirmFriendRequestExtensibleMessageAdminText": return { friend_request_recipient: event.friend_request_recipient, friend_request_sender: event.friend_request_sender }; case "AddContactExtensibleMessageAdminText": return { contact_added_id: event.contact_added_id, contact_adder_id: event.contact_adder_id }; case "AdExtensibleMessageAdminText": return { ad_client_token: event.ad_client_token, ad_id: event.ad_id, ad_preferences_link: event.ad_preferences_link, ad_properties: event.ad_properties }; // never data case "ParticipantJoinedGroupCallExtensibleMessageAdminText": case "ThreadEphemeralTtlModeExtensibleMessageAdminText": case "StartedSharingVideoExtensibleMessageAdminText": case "LightweightEventCreateExtensibleMessageAdminText": case "LightweightEventNotifyExtensibleMessageAdminText": case "LightweightEventNotifyBeforeEventExtensibleMessageAdminText": case "LightweightEventUpdateTitleExtensibleMessageAdminText": case "LightweightEventUpdateTimeExtensibleMessageAdminText": case "LightweightEventUpdateLocationExtensibleMessageAdminText": case "LightweightEventDeleteExtensibleMessageAdminText": return {}; default: return { error: "Don't know what to with event data type " + event.__typename }; } } function formatMessagesGraphQLResponse(data) { var messageThread = data.o0.data.message_thread; var threadID = messageThread.thread_key.thread_fbid ? messageThread.thread_key.thread_fbid : messageThread.thread_key.other_user_id; var messages = messageThread.messages.nodes.map(function (d) { switch (d.__typename) { case "UserMessage": // Give priority to stickers. They're seen as normal messages but we've // been considering them as attachments. var maybeStickerAttachment; if (d.sticker) { maybeStickerAttachment = [ { type: "sticker", ID: d.sticker.id, url: d.sticker.url, packID: d.sticker.pack.id, spriteUrl: d.sticker.sprite_image, spriteUrl2x: d.sticker.sprite_image_2x, width: d.sticker.width, height: d.sticker.height, caption: d.snippet, // Not sure what the heck caption was. description: d.sticker.label, // Not sure about this one either. 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, // @Legacy spriteURI: d.sticker.sprite_image, // @Legacy spriteURI2x: d.sticker.sprite_image_2x // @Legacy } ]; } var mentionsObj = {}; if (d.message !== null) { d.message.ranges.forEach(e => mentionsObj[e.entity.id] = d.message.text.substr(e.offset, e.length)); } return { type: "message", attachments: maybeStickerAttachment ? maybeStickerAttachment : d.blob_attachments && d.blob_attachments.length > 0 ? d.blob_attachments.map(formatAttachmentsGraphQLResponse) : d.extensible_attachment ? [formatExtensibleAttachment(d.extensible_attachment)] : [], body: d.message !== null ? d.message.text : '', isGroup: messageThread.thread_type === "GROUP", messageID: d.message_id, senderID: d.message_sender.id, threadID: threadID, timestamp: d.timestamp_precise, mentions: mentionsObj, isUnread: d.unread, // New messageReactions: d.message_reactions ? d.message_reactions.map(formatReactionsGraphQL) : null, isSponsored: d.is_sponsored, snippet: d.snippet }; case "ThreadNameMessage": return { type: "event", messageID: d.message_id, threadID: threadID, isGroup: messageThread.thread_type === "GROUP", senderID: d.message_sender.id, timestamp: d.timestamp_precise, eventType: "change_thread_name", snippet: d.snippet, eventData: { threadName: d.thread_name }, // @Legacy author: d.message_sender.id, logMessageType: "log:thread-name", logMessageData: { name: d.thread_name } }; case "ThreadImageMessage": return { type: "event", messageID: d.message_id, threadID: threadID, isGroup: messageThread.thread_type === "GROUP", senderID: d.message_sender.id, timestamp: d.timestamp_precise, eventType: "change_thread_image", snippet: d.snippet, eventData: d.image_with_metadata == null ? {} /* removed image */ : { /* image added */ threadImage: { attachmentID: d.image_with_metadata.legacy_attachment_id, width: d.image_with_metadata.original_dimensions.x, height: d.image_with_metadata.original_dimensions.y, url: d.image_with_metadata.preview.uri } }, // @Legacy logMessageType: "log:thread-icon", logMessageData: { thread_icon: d.image_with_metadata ? d.image_with_metadata.preview.uri : null } }; case "ParticipantLeftMessage": return { type: "event", messageID: d.message_id, threadID: threadID, isGroup: messageThread.thread_type === "GROUP", senderID: d.message_sender.id, timestamp: d.timestamp_precise, eventType: "remove_participants", snippet: d.snippet, eventData: { // Array of IDs. participantsRemoved: d.participants_removed.map(function (p) { return p.id; }) }, // @Legacy logMessageType: "log:unsubscribe", logMessageData: { leftParticipantFbId: d.participants_removed.map(function (p) { return p.id; }) } }; case "ParticipantsAddedMessage": return { type: "event", messageID: d.message_id, threadID: threadID, isGroup: messageThread.thread_type === "GROUP", senderID: d.message_sender.id, timestamp: d.timestamp_precise, eventType: "add_participants", snippet: d.snippet, eventData: { // Array of IDs. participantsAdded: d.participants_added.map(function (p) { return p.id; }) }, // @Legacy logMessageType: "log:subscribe", logMessageData: { addedParticipants: d.participants_added.map(function (p) { return p.id; }) } }; case "VideoCallMessage": return { type: "event", messageID: d.message_id, threadID: threadID, isGroup: messageThread.thread_type === "GROUP", senderID: d.message_sender.id, timestamp: d.timestamp_precise, eventType: "video_call", snippet: d.snippet, // @Legacy logMessageType: "other" }; case "VoiceCallMessage": return { type: "event", messageID: d.message_id, threadID: threadID, isGroup: messageThread.thread_type === "GROUP", senderID: d.message_sender.id, timestamp: d.timestamp_precise, eventType: "voice_call", snippet: d.snippet, // @Legacy logMessageType: "other" }; case "GenericAdminTextMessage": return { type: "event", messageID: d.message_id, threadID: threadID, isGroup: messageThread.thread_type === "GROUP", senderID: d.message_sender.id, timestamp: d.timestamp_precise, snippet: d.snippet, eventType: d.extensible_message_admin_text_type.toLowerCase(), eventData: formatEventData(d.extensible_message_admin_text), // @Legacy logMessageType: utils.getAdminTextMessageType( d.extensible_message_admin_text_type ), logMessageData: d.extensible_message_admin_text // Maybe different? }; default: return { error: "Don't know about message type " + d.__typename }; } }); return messages; } module.exports = function (defaultFuncs, api, ctx) { return function getThreadHistoryGraphQL(threadID, amount, timestamp, callback) { var resolveFunc = function () {}; var rejectFunc = function () {}; var returnPromise = new Promise(function (resolve, reject) { resolveFunc = resolve; rejectFunc = reject; }); if (!callback) { callback = function (err, data) { if (err) return rejectFunc(err); resolveFunc(data); }; } // `queries` has to be a string. I couldn't tell from the dev console. This // took me a really long time to figure out. I deserve a cookie for this. var form = { "av": ctx.globalOptions.pageID, queries: JSON.stringify({ o0: { // This doc_id was valid on February 2nd 2017. doc_id: "1498317363570230", query_params: { id: threadID, message_limit: amount, load_messages: 1, load_read_receipts: false, before: timestamp } } }) }; defaultFuncs .post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, form) .then(utils.parseAndCheckLogin(ctx, defaultFuncs)) .then(function (resData) { if (resData.error) throw resData; // This returns us an array of things. The last one is the success / // failure one. // @TODO What do we do in this case? if (resData[resData.length - 1].error_results !== 0) throw new Error("There was an error_result."); callback(null, formatMessagesGraphQLResponse(resData[0])); }) .catch(function (err) { log.error("getThreadHistoryGraphQL", "Lỗi getThreadHistoryGraphQL Có Thể Do Bạn Spam Quá Nhiều, Hãy Thử Lại !"); return callback(err); }); return returnPromise; }; };