From 23eecb2b54e5b4c45cdb4de3a117e35ad6f7dd56 Mon Sep 17 00:00:00 2001 From: hopokuan Date: Tue, 28 May 2019 00:06:40 -0400 Subject: [PATCH 01/10] Added functionality to add attachments --- lib/interactive.js | 62 +++++++----- lib/listeners.js | 14 +++ lib/messenger.js | 238 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 288 insertions(+), 26 deletions(-) diff --git a/lib/interactive.js b/lib/interactive.js index 1493bcb..1593912 100644 --- a/lib/interactive.js +++ b/lib/interactive.js @@ -60,14 +60,14 @@ function getDisplayName(author) { if(!author) author = { name: 'unknown' }; if(currentConversation && currentConversation.isGroup && Settings.properties.groupColors) { - colorPosition = author.name.length % colorList.length; + colorPosition = author.name.length % colorList.length; } - - let displayName; + + let displayName; if (!Settings.properties.useCustomNicknames) { displayName = author.name; } else displayName = author.custom_nickname || author.name; - + if (author.id === messenger.userId) return displayName; else { @@ -77,9 +77,9 @@ function getDisplayName(author) { function renderMessage(author, message) { let msg = `${getDisplayName(author)}: `; - + if (Settings.properties.showTimestamps) { - + const timeDifference = Date.now() - message.timestamp; const daysAgo = Math.round(timeDifference / msInADay); @@ -152,11 +152,11 @@ function renderMessage(author, message) { const a = message.attachment; let uri; if (a.preview && a.preview.uri) { - uri = a.preview.uri; + uri = a.preview.uri; } else if (a.preview_image && a.preview_image.uri) { - uri = a.preview_image.uri; + uri = a.preview_image.uri; } - + if (uri) { atts[attsNo] = uri; const x = `${attsNo}`; @@ -420,17 +420,18 @@ InteractiveCli.prototype.handleCommands = function(command) { case '/h': case '/?': case '/help': - console.log('/b /back /menu .... Get back to conversation selection'.cyan); - console.log('/q /exit /quit .... Quit the application'.cyan); - console.log('/logout ........... Exit and flush credentials'.cyan); - console.log('/s /switch [#] .... Quick switch to conversation number #'.cyan); - console.log('/search [query] ... Search your friends to chat'.cyan); - console.log('/v /view [#] ...... View the attachment by the number given after the type'.cyan); - console.log('/r /refresh ....... Refresh the current converation'.cyan); - console.log('/timestamp ........ Toggle timestamp for messages'.cyan); - console.log('/linelimit [#] .... Set max number of messages to display in a conversation'.cyan); - console.log('/nolimit .......... Unset a line limit, display all available messages'.cyan); - console.log('/help ............. Print this message'.cyan); + console.log('/b /back /menu ...... Get back to conversation selection'.cyan); + console.log('/q /exit /quit ...... Quit the application'.cyan); + console.log('/logout ............. Exit and flush credentials'.cyan); + console.log('/s /switch [#] ...... Quick switch to conversation number #'.cyan); + console.log('/a /attach [path] ... Attach file from path (if has space, must wrap with "")'.cyan); + console.log('/search [query] ..... Search your friends to chat'.cyan); + console.log('/v /view [#] ........ View the attachment by the number given after the type'.cyan); + console.log('/r /refresh ......... Refresh the current converation'.cyan); + console.log('/timestamp .......... Toggle timestamp for messages'.cyan); + console.log('/linelimit [#] ...... Set max number of messages to display in a conversation'.cyan); + console.log('/nolimit ............ Unset a line limit, display all available messages'.cyan); + console.log('/help ............... Print this message'.cyan); rlInterface.prompt(true); break; @@ -486,6 +487,14 @@ InteractiveCli.prototype.handleCommands = function(command) { interactive.handleCommands("/refresh"); } break; + case '/a': + case '/attach': + rlInterface.prompt(true); + let filePath = options[1]; + if (action === 1) { + emitter.emit('sendAttachment', filePath, recipientId); + } + break; default: console.log('Unknown command. Type /help for commands.'.cyan); @@ -552,8 +561,9 @@ InteractiveCli.prototype.run = function(){ emitter.on('sendMessage', listeners.sendMessageListener.bind(listeners)); emitter.on('getThreadId', listeners.getThreadIdListener.bind(listeners)); emitter.on('startSearch', listeners.searchListener.bind(listeners)); - - console.log("Fetching conversations...".cyan); + emitter.on('sendAttachment', listeners.sendAttachmentListener.bind(listeners)); + + console.log("Fetching conversations...".cyan); messenger.getFriends((err, friends) => { if (err) { console.log(`An error occured initially fetching friends list: ${err}`); @@ -562,7 +572,7 @@ InteractiveCli.prototype.run = function(){ } const entry = { - id: userId, + id: userId, firstName: "Me", name: "Me", vanity: "unknown", @@ -577,10 +587,10 @@ InteractiveCli.prototype.run = function(){ rlInterface.prompt(true); }); - // Set up the line reader + // Set up the line reader rlInterface.on("line", interactive.handler); rlInterface.on("close", interactive.exit); - rlInterface.prompt(true); + rlInterface.prompt(true); }); }); }; @@ -599,7 +609,7 @@ InteractiveCli.prototype.handleExit = function(logout){ InteractiveCli.prototype.exit = function(){ console.log('Thanks for using fb-messenger-cli'.cyan); console.log('Bye!'.cyan); - + process.exit(0); }; diff --git a/lib/listeners.js b/lib/listeners.js index c18fc58..1d9190a 100644 --- a/lib/listeners.js +++ b/lib/listeners.js @@ -94,6 +94,20 @@ class Listeners { } } + sendAttachmentListener(filePath, recipientId) { + const errorHandler = (err) => { + if (err) { + console.log('Attachment did not send properly'); + } + } + if (this.messenger.users[recipientId]) { + this.messenger.sendAttachment(this.messenger.users[recipientId].vanity, recipientId, filePath, errorHandler); + } else { + // This is a group and not a single user + this.messenger.sendGroupAttachment(recipientId, filePath, errorHandler); + } + } + searchListener(searchStr, callback) { const parts = searchStr.split(' '); diff --git a/lib/messenger.js b/lib/messenger.js index ddf947b..eff2e51 100644 --- a/lib/messenger.js +++ b/lib/messenger.js @@ -8,6 +8,7 @@ const request = require('request'); // For making HTTP requests const Settings = require('./settings'); +const fs = require('fs'); function getThreadName(thread, participant) { if (!Settings.properties.useCustomNicknames) { @@ -152,6 +153,132 @@ class Messenger { }); } + // Send an attachment in a thread + // recipient: Url name of recipient, also called vanity (eg: alexandre.rose) + // recipientId : Facebook numeric id of recipient. Can be a person or a thread + // filePath : path of the file + // callback(err) does not get any data + sendAttachment(recipient, recipientId, filePath, callback) { + const recipientUrl = `${this.baseUrl }/t/${ recipient}`; // Your recipient; + const utcTimestamp = new Date().getTime(); + const localTime = new Date().toLocaleTimeString().replace(/\s+/g, '').toLowerCase(); + const messageId = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER); + + request.post("https://upload.messenger.com/ajax/mercury/upload.php", { + headers: { + 'origin': 'https://www.messenger.com', + 'accept-encoding': 'gzip, deflate', + 'x-msgr-region': 'ATN', + 'accept-language': 'en-US,en;q=0.8', + 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36', + 'content-type': 'multipart/form-data; boundary=----WebKitFormBoundary', + 'accept': '*/*', + 'referer': recipientUrl, + 'cookie': this.cookie, + 'authority': 'www.messenger.com' + }, + formData: { + '__a': '1', + '__be': '1', + '__dyn': '7AzkXh8Z398jgDxKy1l0BwRyaF3oyfJLFwgoqwWhEoyUnwgU9GGEcVovkwy3eE99XDG4UiwExW14DwPxSFEW2O9xicG4EnwkUC9z8Kew', + '__pc': 'EXP1:messengerdotcom_pkg', + '__req': '2q', + '__rev': '2335431', + '__user': this.userId, + 'dpr': '1.5', + 'fb_dtsg': this.fbdtsg, + 'jazoest': '21948', + 'upload': fs.createReadStream(filePath) + }, + gzip: true, + }, (err, res, body) => { + // issue at network level + if (err) { + console.log("Attachment upload failed"); + return; + } + let bodyJson = JSON.parse(body.replace('for (;;);', '')); + + // if Facebook denies the file + if (bodyJson.error) { + // error details: bodyJson.errorSummary, bodyJson.errorDescription + console.log("Attachment denied by Facebook"); + return; + } + + let formData = { + 'action_type': 'ma-type:user-generated-message', + 'author': `fbid:${this.userId}`, + 'timestamp': utcTimestamp, + 'timestamp_absolute': 'Today', + 'timestamp_relative': localTime, + 'timestamp_time_passed': '0', + 'is_unread': 'false', + 'is_forward': 'false', + 'is_filtered_content': 'false', + 'is_filtered_content_bh': 'false', + 'is_filtered_content_account': 'false', + 'is_filtered_content_quasar': 'false', + 'is_filtered_content_invalid_app': 'false', + 'is_spoof_warning': 'false', + 'source': 'source:messenger:web', + 'has_attachment': 'true', + 'specific_to_list][0]': `fbid:${recipientId}`, + 'specific_to_list[1]': `fbid:${this.userId}`, + 'status': '0', + 'offline_threading_id': messageId, + 'message_id': messageId, + 'ephemeral_ttl_mode': '0', + 'manual_retry_cnt': '0', + 'other_user_fbid': recipientId, + 'client': 'mercury', + '__user': this.userId, + '__a': '1', + '__req': '2q', + '__be': '0', + '__pc': 'EXP1:messengerdotcom_pkg', + 'fb_dtsg': this.fbdtsg, + 'ttstamp': '265817073691196867855211811758658172458277511215256110114', + '__rev': '2335431' + } + + let metadata = bodyJson.payload.metadata[0]; + if (metadata.image_id) { + formData['image_ids[0]'] = metadata.image_id; + } + if (metadata.video_id) { + formData['video_ids[0]'] = metadata.video_id; + } + if (metadata.audio_id) { + formData['audio_ids[0]'] = metadata.audio_id; + } + if (metadata.file_id) { + formData['file_ids[0]'] = metadata.file_id; + } + + request.post("https://www.messenger.com/messaging/send/?dpr=1", { + headers: { + 'origin': 'https://www.messenger.com', + 'accept-encoding': 'gzip, deflate', + 'x-msgr-region': 'ATN', + 'accept-language': 'en-US,en;q=0.8', + 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36', + 'content-type': 'application/x-www-form-urlencoded', + 'accept': '*/*', + 'referer': recipientUrl, + 'cookie': this.cookie, + 'authority': 'www.messenger.com' + }, + formData: formData + }, (err) => { + if (err) { + callback(err); + } + callback(); + }); + }); + } + sendGroupMessage(recipientId, body, callback) { const recipientUrl = `${this.baseUrl }/t/${ recipientId}`; // Your recipient; const utcTimestamp = new Date().getTime(); @@ -198,6 +325,117 @@ class Messenger { }); } + // Send an attachment in a Group thread + // recipientId : Facebook numeric id of recipient. Can be a person or a thread + // filePath : path of the file + // callback(err) does not get any data + sendGroupAttachment(recipientId, filePath, callback) { + const recipientUrl = `${this.baseUrl }/t/${ recipientId}`; // Your recipient; + const utcTimestamp = new Date().getTime(); + const localTime = new Date().toLocaleTimeString().replace(/\s+/g, '').toLowerCase(); + const messageId = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER); + + request.post("https://upload.messenger.com/ajax/mercury/upload.php", { + headers: { + 'origin': 'https://www.messenger.com', + 'accept-encoding': 'gzip, deflate', + 'x-msgr-region': 'ATN', + 'accept-language': 'en-US,en;q=0.8', + 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36', + 'content-type': 'multipart/form-data; boundary=----WebKitFormBoundary', + 'accept': '*/*', + 'referer': recipientUrl, + 'cookie': this.cookie, + 'authority': 'www.messenger.com' + }, + formData: { + '__a': '1', + '__be': '1', + '__dyn': '7AzkXh8Z398jgDxKy1l0BwRyaF3oyfJLFwgoqwWhEoyUnwgU9GGEcVovkwy3eE99XDG4UiwExW14DwPxSFEW2O9xicG4EnwkUC9z8Kew', + '__pc': 'EXP1:messengerdotcom_pkg', + '__req': '2q', + '__rev': '2335431', + '__user': this.userId, + 'dpr': '1.5', + 'fb_dtsg': this.fbdtsg, + 'jazoest': '21948', + 'upload': fs.createReadStream(filePath), + }, + gzip: true, + }, (err, res, body) => { + // issue at network level + if (err) { + console.log("Attachment upload failed"); + return; + } + let bodyJson = JSON.parse(body.replace('for (;;);', '')); + + // if Facebook denies the file + if (bodyJson.error) { + // error details: bodyJson.errorSummary, bodyJson.errorDescription + console.log("Attachment denied by Facebook"); + return; + } + + let formData = { + 'action_type': 'ma-type:user-generated-message', + 'author': `fbid:${this.userId}`, + 'timestamp': utcTimestamp, + 'source': 'source:messenger:web', + 'has_attachment': 'true', + 'specific_to_list][0]': `fbid:${recipientId}`, + 'specific_to_list[1]': `fbid:${this.userId}`, + 'status': '0', + 'offline_threading_id': messageId, + 'message_id': messageId, + 'thread_fbid': recipientId, + '__user': this.userId, + '__a': '1', + '__req': '2q', + '__be': '0', + '__pc': 'EXP1:messengerdotcom_pkg', + 'fb_dtsg': this.fbdtsg, + 'ttstamp': '265817073691196867855211811758658172458277511215256110114', + '__rev': '2335431' + } + + let metadata = bodyJson.payload.metadata[0]; + if (metadata.image_id) { + formData['image_ids[0]'] = metadata.image_id; + } + if (metadata.video_id) { + formData['video_ids[0]'] = metadata.video_id; + } + if (metadata.audio_id) { + formData['audio_ids[0]'] = metadata.audio_id; + } + if (metadata.file_id) { + formData['file_ids[0]'] = metadata.file_id; + } + + request.post("https://www.messenger.com/messaging/send/?dpr=1", { + headers: { + 'origin': 'https://www.messenger.com', + 'accept-encoding': 'gzip, deflate', + 'x-msgr-region': 'ATN', + 'accept-language': 'en-US,en;q=0.8', + 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36', + 'content-type': 'application/x-www-form-urlencoded', + 'accept': '*/*', + 'referer': recipientUrl, + 'cookie': this.cookie, + 'authority': 'www.messenger.com' + }, + formData: formData + }, (err) => { + if (err) { + callback(err); + } + callback(); + }); + }); + } + getMessages(recipient, recipientId, count, callback) { const recipientUrl = `${this.baseUrl }/t/${ recipient}`; let offSetString, limitString; From 3189cc04872e0e7157053e5915efa89697176406 Mon Sep 17 00:00:00 2001 From: hopokuan Date: Tue, 28 May 2019 09:31:38 -0400 Subject: [PATCH 02/10] Improved user error handling --- lib/interactive.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/interactive.js b/lib/interactive.js index 1593912..346103e 100644 --- a/lib/interactive.js +++ b/lib/interactive.js @@ -14,6 +14,7 @@ const events = require('events'); const path = require('path'); const notifier = require('node-notifier'); const readline = require('readline'); +const { existsSync } = require('fs'); let messenger; @@ -492,7 +493,18 @@ InteractiveCli.prototype.handleCommands = function(command) { rlInterface.prompt(true); let filePath = options[1]; if (action === 1) { - emitter.emit('sendAttachment', filePath, recipientId); + let filePath = options[1]; + if (filePath) { + if (existsSync(filePath)) { + emitter.emit('sendAttachment', filePath, recipientId); + } else { + console.log("File not found!".cyan); + } + } else { + console.log("Missing file to be attached!".cyan); + } + } else { + console.log("This command is only available in a conversation!".cyan); } break; From f020cf1703019a6acc7dd6e487ee763067afc9f5 Mon Sep 17 00:00:00 2001 From: hopokuan Date: Wed, 29 May 2019 12:07:06 -0400 Subject: [PATCH 03/10] Fixed if/else --- lib/messenger.js | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/lib/messenger.js b/lib/messenger.js index eff2e51..7e9d0a1 100644 --- a/lib/messenger.js +++ b/lib/messenger.js @@ -245,14 +245,11 @@ class Messenger { let metadata = bodyJson.payload.metadata[0]; if (metadata.image_id) { formData['image_ids[0]'] = metadata.image_id; - } - if (metadata.video_id) { + } else if (metadata.video_id) { formData['video_ids[0]'] = metadata.video_id; - } - if (metadata.audio_id) { + } else if (metadata.audio_id) { formData['audio_ids[0]'] = metadata.audio_id; - } - if (metadata.file_id) { + } else { formData['file_ids[0]'] = metadata.file_id; } @@ -402,14 +399,11 @@ class Messenger { let metadata = bodyJson.payload.metadata[0]; if (metadata.image_id) { formData['image_ids[0]'] = metadata.image_id; - } - if (metadata.video_id) { + } else if (metadata.video_id) { formData['video_ids[0]'] = metadata.video_id; - } - if (metadata.audio_id) { + } else if (metadata.audio_id) { formData['audio_ids[0]'] = metadata.audio_id; - } - if (metadata.file_id) { + } else { formData['file_ids[0]'] = metadata.file_id; } From 118dd7dfe512ad1a13896e92200c3c637978ff8a Mon Sep 17 00:00:00 2001 From: hopokuan Date: Wed, 29 May 2019 12:45:47 -0400 Subject: [PATCH 04/10] refactored for less redundant code, removed unnecessary query parameters, added some comments --- lib/messenger.js | 123 +++++++++++++++++++++++------------------------ 1 file changed, 60 insertions(+), 63 deletions(-) diff --git a/lib/messenger.js b/lib/messenger.js index 7e9d0a1..e457c01 100644 --- a/lib/messenger.js +++ b/lib/messenger.js @@ -164,30 +164,35 @@ class Messenger { const localTime = new Date().toLocaleTimeString().replace(/\s+/g, '').toLowerCase(); const messageId = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER); + // header is identical in both the upload endpoint and message-send endpoint + const headers = { + 'origin': 'https://www.messenger.com', + 'accept-encoding': 'gzip, deflate', + 'x-msgr-region': 'ATN', + 'accept-language': 'en-US,en;q=0.8', + 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36', + 'content-type': 'application/x-www-form-urlencoded', + 'accept': '*/*', + 'referer': recipientUrl, + 'cookie': this.cookie, + 'authority': 'www.messenger.com' + }; + request.post("https://upload.messenger.com/ajax/mercury/upload.php", { - headers: { - 'origin': 'https://www.messenger.com', - 'accept-encoding': 'gzip, deflate', - 'x-msgr-region': 'ATN', - 'accept-language': 'en-US,en;q=0.8', - 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36', - 'content-type': 'multipart/form-data; boundary=----WebKitFormBoundary', - 'accept': '*/*', - 'referer': recipientUrl, - 'cookie': this.cookie, - 'authority': 'www.messenger.com' - }, + headers: headers, formData: { + // the ones commented out were parameters I wasn't sure whether + // they were needed but are confirmed unneeded now. '__a': '1', - '__be': '1', - '__dyn': '7AzkXh8Z398jgDxKy1l0BwRyaF3oyfJLFwgoqwWhEoyUnwgU9GGEcVovkwy3eE99XDG4UiwExW14DwPxSFEW2O9xicG4EnwkUC9z8Kew', - '__pc': 'EXP1:messengerdotcom_pkg', - '__req': '2q', - '__rev': '2335431', + // '__be': '0', + // '__dyn': '7AzkXh8Z398jgDxKy1l0BwRyaF3oyfJLFwgoqwWhEoyUnwgU9GGEcVovkwy3eE99XDG4UiwExW14DwPxSFEW2O9xicG4EnwkUC9z8Kew', + // '__pc': 'EXP1:messengerdotcom_pkg', + // '__req': '2q', + // '__rev': '2335431', '__user': this.userId, - 'dpr': '1.5', + // 'dpr': '1.5', 'fb_dtsg': this.fbdtsg, - 'jazoest': '21948', + // 'jazoest': '21948', 'upload': fs.createReadStream(filePath) }, gzip: true, @@ -207,6 +212,11 @@ class Messenger { } let formData = { + // everything's identical to the formData in sendMessage except: + // 1. no "body" + // 2. "has_attachment" set to true + // 3. no "html_body" + // 4. the id to the attachment is added in the if/else block below 'action_type': 'ma-type:user-generated-message', 'author': `fbid:${this.userId}`, 'timestamp': utcTimestamp, @@ -254,18 +264,7 @@ class Messenger { } request.post("https://www.messenger.com/messaging/send/?dpr=1", { - headers: { - 'origin': 'https://www.messenger.com', - 'accept-encoding': 'gzip, deflate', - 'x-msgr-region': 'ATN', - 'accept-language': 'en-US,en;q=0.8', - 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36', - 'content-type': 'application/x-www-form-urlencoded', - 'accept': '*/*', - 'referer': recipientUrl, - 'cookie': this.cookie, - 'authority': 'www.messenger.com' - }, + headers: headers, formData: formData }, (err) => { if (err) { @@ -332,31 +331,36 @@ class Messenger { const localTime = new Date().toLocaleTimeString().replace(/\s+/g, '').toLowerCase(); const messageId = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER); + // header is identical in both the upload endpoint and message-send endpoint + const headers = { + 'origin': 'https://www.messenger.com', + 'accept-encoding': 'gzip, deflate', + 'x-msgr-region': 'ATN', + 'accept-language': 'en-US,en;q=0.8', + 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36', + 'content-type': 'application/x-www-form-urlencoded', + 'accept': '*/*', + 'referer': recipientUrl, + 'cookie': this.cookie, + 'authority': 'www.messenger.com' + }; + request.post("https://upload.messenger.com/ajax/mercury/upload.php", { - headers: { - 'origin': 'https://www.messenger.com', - 'accept-encoding': 'gzip, deflate', - 'x-msgr-region': 'ATN', - 'accept-language': 'en-US,en;q=0.8', - 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36', - 'content-type': 'multipart/form-data; boundary=----WebKitFormBoundary', - 'accept': '*/*', - 'referer': recipientUrl, - 'cookie': this.cookie, - 'authority': 'www.messenger.com' - }, + headers: headers, formData: { + // the ones commented out were parameters I wasn't sure whether + // they were needed but are confirmed unneeded now. '__a': '1', - '__be': '1', - '__dyn': '7AzkXh8Z398jgDxKy1l0BwRyaF3oyfJLFwgoqwWhEoyUnwgU9GGEcVovkwy3eE99XDG4UiwExW14DwPxSFEW2O9xicG4EnwkUC9z8Kew', - '__pc': 'EXP1:messengerdotcom_pkg', - '__req': '2q', - '__rev': '2335431', + // '__be': '0', + // '__dyn': '7AzkXh8Z398jgDxKy1l0BwRyaF3oyfJLFwgoqwWhEoyUnwgU9GGEcVovkwy3eE99XDG4UiwExW14DwPxSFEW2O9xicG4EnwkUC9z8Kew', + // '__pc': 'EXP1:messengerdotcom_pkg', + // '__req': '2q', + // '__rev': '2335431', '__user': this.userId, - 'dpr': '1.5', + // 'dpr': '1.5', 'fb_dtsg': this.fbdtsg, - 'jazoest': '21948', - 'upload': fs.createReadStream(filePath), + // 'jazoest': '21948', + 'upload': fs.createReadStream(filePath) }, gzip: true, }, (err, res, body) => { @@ -375,6 +379,10 @@ class Messenger { } let formData = { + // everything's identical to the formData in sendMessage except: + // 1. no "body" + // 2. "has_attachment" set to true + // 3. the id to the attachment is added in the if/else block below 'action_type': 'ma-type:user-generated-message', 'author': `fbid:${this.userId}`, 'timestamp': utcTimestamp, @@ -408,18 +416,7 @@ class Messenger { } request.post("https://www.messenger.com/messaging/send/?dpr=1", { - headers: { - 'origin': 'https://www.messenger.com', - 'accept-encoding': 'gzip, deflate', - 'x-msgr-region': 'ATN', - 'accept-language': 'en-US,en;q=0.8', - 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36', - 'content-type': 'application/x-www-form-urlencoded', - 'accept': '*/*', - 'referer': recipientUrl, - 'cookie': this.cookie, - 'authority': 'www.messenger.com' - }, + headers: headers, formData: formData }, (err) => { if (err) { From 8a625ce85db5e014269eee9e80022de57d9c8cc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20L=C3=A9ger?= Date: Wed, 7 Aug 2019 22:33:25 -0400 Subject: [PATCH 05/10] Local user gets their own color instead of white (#211) Updated getDisplayName() to help distinguish where messages start when the fb-messenger-cli user writes consecutive messages that all wrap around the screen. --- lib/interactive.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/lib/interactive.js b/lib/interactive.js index 1493bcb..2943744 100644 --- a/lib/interactive.js +++ b/lib/interactive.js @@ -59,8 +59,11 @@ function getDisplayName(author) { if(!author) author = { name: 'unknown' }; - if(currentConversation && currentConversation.isGroup && Settings.properties.groupColors) { - colorPosition = author.name.length % colorList.length; + if(currentConversation && Settings.properties.groupColors) { + if (currentConversation.isGroup) + colorPosition = author.name.length % colorList.length; + else if (author.id === messenger.userId) + colorPosition = 1; } let displayName; @@ -68,11 +71,7 @@ function getDisplayName(author) { displayName = author.name; } else displayName = author.custom_nickname || author.name; - if (author.id === messenger.userId) - return displayName; - else { - return colorList[colorPosition](displayName); - } + return colorList[colorPosition](displayName); } function renderMessage(author, message) { From 078f4be835c3f1b1dafaa501f05cab16573c16e9 Mon Sep 17 00:00:00 2001 From: Alexandre Rose Date: Wed, 7 Aug 2019 19:36:24 -0700 Subject: [PATCH 06/10] Change the color order so that at least the 1:1 convos are readable on powershell Powershell users will have to disable colors in group convos or change their color scheme --- lib/interactive.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/interactive.js b/lib/interactive.js index 2943744..94d1bd9 100644 --- a/lib/interactive.js +++ b/lib/interactive.js @@ -42,9 +42,9 @@ const rlInterface = readline.createInterface({ const colorList = [ safeColors.green, + safeColors.cyan, safeColors.red, safeColors.blue, - safeColors.cyan, safeColors.magenta, safeColors.yellow ]; From 0408674d3c6926566fea669639079c73d6fe9f5a Mon Sep 17 00:00:00 2001 From: hopokuan Date: Tue, 28 May 2019 00:06:40 -0400 Subject: [PATCH 07/10] Added functionality to add attachments --- lib/interactive.js | 58 ++++++----- lib/listeners.js | 14 +++ lib/messenger.js | 238 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 286 insertions(+), 24 deletions(-) diff --git a/lib/interactive.js b/lib/interactive.js index 94d1bd9..2300343 100644 --- a/lib/interactive.js +++ b/lib/interactive.js @@ -65,8 +65,8 @@ function getDisplayName(author) { else if (author.id === messenger.userId) colorPosition = 1; } - - let displayName; + + let displayName; if (!Settings.properties.useCustomNicknames) { displayName = author.name; } else displayName = author.custom_nickname || author.name; @@ -76,9 +76,9 @@ function getDisplayName(author) { function renderMessage(author, message) { let msg = `${getDisplayName(author)}: `; - + if (Settings.properties.showTimestamps) { - + const timeDifference = Date.now() - message.timestamp; const daysAgo = Math.round(timeDifference / msInADay); @@ -151,11 +151,11 @@ function renderMessage(author, message) { const a = message.attachment; let uri; if (a.preview && a.preview.uri) { - uri = a.preview.uri; + uri = a.preview.uri; } else if (a.preview_image && a.preview_image.uri) { - uri = a.preview_image.uri; + uri = a.preview_image.uri; } - + if (uri) { atts[attsNo] = uri; const x = `${attsNo}`; @@ -419,17 +419,18 @@ InteractiveCli.prototype.handleCommands = function(command) { case '/h': case '/?': case '/help': - console.log('/b /back /menu .... Get back to conversation selection'.cyan); - console.log('/q /exit /quit .... Quit the application'.cyan); - console.log('/logout ........... Exit and flush credentials'.cyan); - console.log('/s /switch [#] .... Quick switch to conversation number #'.cyan); - console.log('/search [query] ... Search your friends to chat'.cyan); - console.log('/v /view [#] ...... View the attachment by the number given after the type'.cyan); - console.log('/r /refresh ....... Refresh the current converation'.cyan); - console.log('/timestamp ........ Toggle timestamp for messages'.cyan); - console.log('/linelimit [#] .... Set max number of messages to display in a conversation'.cyan); - console.log('/nolimit .......... Unset a line limit, display all available messages'.cyan); - console.log('/help ............. Print this message'.cyan); + console.log('/b /back /menu ...... Get back to conversation selection'.cyan); + console.log('/q /exit /quit ...... Quit the application'.cyan); + console.log('/logout ............. Exit and flush credentials'.cyan); + console.log('/s /switch [#] ...... Quick switch to conversation number #'.cyan); + console.log('/a /attach [path] ... Attach file from path (if has space, must wrap with "")'.cyan); + console.log('/search [query] ..... Search your friends to chat'.cyan); + console.log('/v /view [#] ........ View the attachment by the number given after the type'.cyan); + console.log('/r /refresh ......... Refresh the current converation'.cyan); + console.log('/timestamp .......... Toggle timestamp for messages'.cyan); + console.log('/linelimit [#] ...... Set max number of messages to display in a conversation'.cyan); + console.log('/nolimit ............ Unset a line limit, display all available messages'.cyan); + console.log('/help ............... Print this message'.cyan); rlInterface.prompt(true); break; @@ -485,6 +486,14 @@ InteractiveCli.prototype.handleCommands = function(command) { interactive.handleCommands("/refresh"); } break; + case '/a': + case '/attach': + rlInterface.prompt(true); + let filePath = options[1]; + if (action === 1) { + emitter.emit('sendAttachment', filePath, recipientId); + } + break; default: console.log('Unknown command. Type /help for commands.'.cyan); @@ -551,8 +560,9 @@ InteractiveCli.prototype.run = function(){ emitter.on('sendMessage', listeners.sendMessageListener.bind(listeners)); emitter.on('getThreadId', listeners.getThreadIdListener.bind(listeners)); emitter.on('startSearch', listeners.searchListener.bind(listeners)); - - console.log("Fetching conversations...".cyan); + emitter.on('sendAttachment', listeners.sendAttachmentListener.bind(listeners)); + + console.log("Fetching conversations...".cyan); messenger.getFriends((err, friends) => { if (err) { console.log(`An error occured initially fetching friends list: ${err}`); @@ -561,7 +571,7 @@ InteractiveCli.prototype.run = function(){ } const entry = { - id: userId, + id: userId, firstName: "Me", name: "Me", vanity: "unknown", @@ -576,10 +586,10 @@ InteractiveCli.prototype.run = function(){ rlInterface.prompt(true); }); - // Set up the line reader + // Set up the line reader rlInterface.on("line", interactive.handler); rlInterface.on("close", interactive.exit); - rlInterface.prompt(true); + rlInterface.prompt(true); }); }); }; @@ -598,7 +608,7 @@ InteractiveCli.prototype.handleExit = function(logout){ InteractiveCli.prototype.exit = function(){ console.log('Thanks for using fb-messenger-cli'.cyan); console.log('Bye!'.cyan); - + process.exit(0); }; diff --git a/lib/listeners.js b/lib/listeners.js index c18fc58..1d9190a 100644 --- a/lib/listeners.js +++ b/lib/listeners.js @@ -94,6 +94,20 @@ class Listeners { } } + sendAttachmentListener(filePath, recipientId) { + const errorHandler = (err) => { + if (err) { + console.log('Attachment did not send properly'); + } + } + if (this.messenger.users[recipientId]) { + this.messenger.sendAttachment(this.messenger.users[recipientId].vanity, recipientId, filePath, errorHandler); + } else { + // This is a group and not a single user + this.messenger.sendGroupAttachment(recipientId, filePath, errorHandler); + } + } + searchListener(searchStr, callback) { const parts = searchStr.split(' '); diff --git a/lib/messenger.js b/lib/messenger.js index ddf947b..eff2e51 100644 --- a/lib/messenger.js +++ b/lib/messenger.js @@ -8,6 +8,7 @@ const request = require('request'); // For making HTTP requests const Settings = require('./settings'); +const fs = require('fs'); function getThreadName(thread, participant) { if (!Settings.properties.useCustomNicknames) { @@ -152,6 +153,132 @@ class Messenger { }); } + // Send an attachment in a thread + // recipient: Url name of recipient, also called vanity (eg: alexandre.rose) + // recipientId : Facebook numeric id of recipient. Can be a person or a thread + // filePath : path of the file + // callback(err) does not get any data + sendAttachment(recipient, recipientId, filePath, callback) { + const recipientUrl = `${this.baseUrl }/t/${ recipient}`; // Your recipient; + const utcTimestamp = new Date().getTime(); + const localTime = new Date().toLocaleTimeString().replace(/\s+/g, '').toLowerCase(); + const messageId = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER); + + request.post("https://upload.messenger.com/ajax/mercury/upload.php", { + headers: { + 'origin': 'https://www.messenger.com', + 'accept-encoding': 'gzip, deflate', + 'x-msgr-region': 'ATN', + 'accept-language': 'en-US,en;q=0.8', + 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36', + 'content-type': 'multipart/form-data; boundary=----WebKitFormBoundary', + 'accept': '*/*', + 'referer': recipientUrl, + 'cookie': this.cookie, + 'authority': 'www.messenger.com' + }, + formData: { + '__a': '1', + '__be': '1', + '__dyn': '7AzkXh8Z398jgDxKy1l0BwRyaF3oyfJLFwgoqwWhEoyUnwgU9GGEcVovkwy3eE99XDG4UiwExW14DwPxSFEW2O9xicG4EnwkUC9z8Kew', + '__pc': 'EXP1:messengerdotcom_pkg', + '__req': '2q', + '__rev': '2335431', + '__user': this.userId, + 'dpr': '1.5', + 'fb_dtsg': this.fbdtsg, + 'jazoest': '21948', + 'upload': fs.createReadStream(filePath) + }, + gzip: true, + }, (err, res, body) => { + // issue at network level + if (err) { + console.log("Attachment upload failed"); + return; + } + let bodyJson = JSON.parse(body.replace('for (;;);', '')); + + // if Facebook denies the file + if (bodyJson.error) { + // error details: bodyJson.errorSummary, bodyJson.errorDescription + console.log("Attachment denied by Facebook"); + return; + } + + let formData = { + 'action_type': 'ma-type:user-generated-message', + 'author': `fbid:${this.userId}`, + 'timestamp': utcTimestamp, + 'timestamp_absolute': 'Today', + 'timestamp_relative': localTime, + 'timestamp_time_passed': '0', + 'is_unread': 'false', + 'is_forward': 'false', + 'is_filtered_content': 'false', + 'is_filtered_content_bh': 'false', + 'is_filtered_content_account': 'false', + 'is_filtered_content_quasar': 'false', + 'is_filtered_content_invalid_app': 'false', + 'is_spoof_warning': 'false', + 'source': 'source:messenger:web', + 'has_attachment': 'true', + 'specific_to_list][0]': `fbid:${recipientId}`, + 'specific_to_list[1]': `fbid:${this.userId}`, + 'status': '0', + 'offline_threading_id': messageId, + 'message_id': messageId, + 'ephemeral_ttl_mode': '0', + 'manual_retry_cnt': '0', + 'other_user_fbid': recipientId, + 'client': 'mercury', + '__user': this.userId, + '__a': '1', + '__req': '2q', + '__be': '0', + '__pc': 'EXP1:messengerdotcom_pkg', + 'fb_dtsg': this.fbdtsg, + 'ttstamp': '265817073691196867855211811758658172458277511215256110114', + '__rev': '2335431' + } + + let metadata = bodyJson.payload.metadata[0]; + if (metadata.image_id) { + formData['image_ids[0]'] = metadata.image_id; + } + if (metadata.video_id) { + formData['video_ids[0]'] = metadata.video_id; + } + if (metadata.audio_id) { + formData['audio_ids[0]'] = metadata.audio_id; + } + if (metadata.file_id) { + formData['file_ids[0]'] = metadata.file_id; + } + + request.post("https://www.messenger.com/messaging/send/?dpr=1", { + headers: { + 'origin': 'https://www.messenger.com', + 'accept-encoding': 'gzip, deflate', + 'x-msgr-region': 'ATN', + 'accept-language': 'en-US,en;q=0.8', + 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36', + 'content-type': 'application/x-www-form-urlencoded', + 'accept': '*/*', + 'referer': recipientUrl, + 'cookie': this.cookie, + 'authority': 'www.messenger.com' + }, + formData: formData + }, (err) => { + if (err) { + callback(err); + } + callback(); + }); + }); + } + sendGroupMessage(recipientId, body, callback) { const recipientUrl = `${this.baseUrl }/t/${ recipientId}`; // Your recipient; const utcTimestamp = new Date().getTime(); @@ -198,6 +325,117 @@ class Messenger { }); } + // Send an attachment in a Group thread + // recipientId : Facebook numeric id of recipient. Can be a person or a thread + // filePath : path of the file + // callback(err) does not get any data + sendGroupAttachment(recipientId, filePath, callback) { + const recipientUrl = `${this.baseUrl }/t/${ recipientId}`; // Your recipient; + const utcTimestamp = new Date().getTime(); + const localTime = new Date().toLocaleTimeString().replace(/\s+/g, '').toLowerCase(); + const messageId = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER); + + request.post("https://upload.messenger.com/ajax/mercury/upload.php", { + headers: { + 'origin': 'https://www.messenger.com', + 'accept-encoding': 'gzip, deflate', + 'x-msgr-region': 'ATN', + 'accept-language': 'en-US,en;q=0.8', + 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36', + 'content-type': 'multipart/form-data; boundary=----WebKitFormBoundary', + 'accept': '*/*', + 'referer': recipientUrl, + 'cookie': this.cookie, + 'authority': 'www.messenger.com' + }, + formData: { + '__a': '1', + '__be': '1', + '__dyn': '7AzkXh8Z398jgDxKy1l0BwRyaF3oyfJLFwgoqwWhEoyUnwgU9GGEcVovkwy3eE99XDG4UiwExW14DwPxSFEW2O9xicG4EnwkUC9z8Kew', + '__pc': 'EXP1:messengerdotcom_pkg', + '__req': '2q', + '__rev': '2335431', + '__user': this.userId, + 'dpr': '1.5', + 'fb_dtsg': this.fbdtsg, + 'jazoest': '21948', + 'upload': fs.createReadStream(filePath), + }, + gzip: true, + }, (err, res, body) => { + // issue at network level + if (err) { + console.log("Attachment upload failed"); + return; + } + let bodyJson = JSON.parse(body.replace('for (;;);', '')); + + // if Facebook denies the file + if (bodyJson.error) { + // error details: bodyJson.errorSummary, bodyJson.errorDescription + console.log("Attachment denied by Facebook"); + return; + } + + let formData = { + 'action_type': 'ma-type:user-generated-message', + 'author': `fbid:${this.userId}`, + 'timestamp': utcTimestamp, + 'source': 'source:messenger:web', + 'has_attachment': 'true', + 'specific_to_list][0]': `fbid:${recipientId}`, + 'specific_to_list[1]': `fbid:${this.userId}`, + 'status': '0', + 'offline_threading_id': messageId, + 'message_id': messageId, + 'thread_fbid': recipientId, + '__user': this.userId, + '__a': '1', + '__req': '2q', + '__be': '0', + '__pc': 'EXP1:messengerdotcom_pkg', + 'fb_dtsg': this.fbdtsg, + 'ttstamp': '265817073691196867855211811758658172458277511215256110114', + '__rev': '2335431' + } + + let metadata = bodyJson.payload.metadata[0]; + if (metadata.image_id) { + formData['image_ids[0]'] = metadata.image_id; + } + if (metadata.video_id) { + formData['video_ids[0]'] = metadata.video_id; + } + if (metadata.audio_id) { + formData['audio_ids[0]'] = metadata.audio_id; + } + if (metadata.file_id) { + formData['file_ids[0]'] = metadata.file_id; + } + + request.post("https://www.messenger.com/messaging/send/?dpr=1", { + headers: { + 'origin': 'https://www.messenger.com', + 'accept-encoding': 'gzip, deflate', + 'x-msgr-region': 'ATN', + 'accept-language': 'en-US,en;q=0.8', + 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36', + 'content-type': 'application/x-www-form-urlencoded', + 'accept': '*/*', + 'referer': recipientUrl, + 'cookie': this.cookie, + 'authority': 'www.messenger.com' + }, + formData: formData + }, (err) => { + if (err) { + callback(err); + } + callback(); + }); + }); + } + getMessages(recipient, recipientId, count, callback) { const recipientUrl = `${this.baseUrl }/t/${ recipient}`; let offSetString, limitString; From d8939115fdce2bd60068cff21b0bf8e0c9590127 Mon Sep 17 00:00:00 2001 From: hopokuan Date: Tue, 28 May 2019 09:31:38 -0400 Subject: [PATCH 08/10] Improved user error handling --- lib/interactive.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/interactive.js b/lib/interactive.js index 2300343..669faa1 100644 --- a/lib/interactive.js +++ b/lib/interactive.js @@ -14,6 +14,7 @@ const events = require('events'); const path = require('path'); const notifier = require('node-notifier'); const readline = require('readline'); +const { existsSync } = require('fs'); let messenger; @@ -491,7 +492,18 @@ InteractiveCli.prototype.handleCommands = function(command) { rlInterface.prompt(true); let filePath = options[1]; if (action === 1) { - emitter.emit('sendAttachment', filePath, recipientId); + let filePath = options[1]; + if (filePath) { + if (existsSync(filePath)) { + emitter.emit('sendAttachment', filePath, recipientId); + } else { + console.log("File not found!".cyan); + } + } else { + console.log("Missing file to be attached!".cyan); + } + } else { + console.log("This command is only available in a conversation!".cyan); } break; From 367f7d63de4c1e7e02345381d68434408eaa1695 Mon Sep 17 00:00:00 2001 From: hopokuan Date: Wed, 29 May 2019 12:07:06 -0400 Subject: [PATCH 09/10] Fixed if/else --- lib/messenger.js | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/lib/messenger.js b/lib/messenger.js index eff2e51..7e9d0a1 100644 --- a/lib/messenger.js +++ b/lib/messenger.js @@ -245,14 +245,11 @@ class Messenger { let metadata = bodyJson.payload.metadata[0]; if (metadata.image_id) { formData['image_ids[0]'] = metadata.image_id; - } - if (metadata.video_id) { + } else if (metadata.video_id) { formData['video_ids[0]'] = metadata.video_id; - } - if (metadata.audio_id) { + } else if (metadata.audio_id) { formData['audio_ids[0]'] = metadata.audio_id; - } - if (metadata.file_id) { + } else { formData['file_ids[0]'] = metadata.file_id; } @@ -402,14 +399,11 @@ class Messenger { let metadata = bodyJson.payload.metadata[0]; if (metadata.image_id) { formData['image_ids[0]'] = metadata.image_id; - } - if (metadata.video_id) { + } else if (metadata.video_id) { formData['video_ids[0]'] = metadata.video_id; - } - if (metadata.audio_id) { + } else if (metadata.audio_id) { formData['audio_ids[0]'] = metadata.audio_id; - } - if (metadata.file_id) { + } else { formData['file_ids[0]'] = metadata.file_id; } From 62db01e704356e07640c57bf3e363ee7c87efd48 Mon Sep 17 00:00:00 2001 From: hopokuan Date: Wed, 29 May 2019 12:45:47 -0400 Subject: [PATCH 10/10] refactored for less redundant code, removed unnecessary query parameters, added some comments --- lib/messenger.js | 123 +++++++++++++++++++++++------------------------ 1 file changed, 60 insertions(+), 63 deletions(-) diff --git a/lib/messenger.js b/lib/messenger.js index 7e9d0a1..e457c01 100644 --- a/lib/messenger.js +++ b/lib/messenger.js @@ -164,30 +164,35 @@ class Messenger { const localTime = new Date().toLocaleTimeString().replace(/\s+/g, '').toLowerCase(); const messageId = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER); + // header is identical in both the upload endpoint and message-send endpoint + const headers = { + 'origin': 'https://www.messenger.com', + 'accept-encoding': 'gzip, deflate', + 'x-msgr-region': 'ATN', + 'accept-language': 'en-US,en;q=0.8', + 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36', + 'content-type': 'application/x-www-form-urlencoded', + 'accept': '*/*', + 'referer': recipientUrl, + 'cookie': this.cookie, + 'authority': 'www.messenger.com' + }; + request.post("https://upload.messenger.com/ajax/mercury/upload.php", { - headers: { - 'origin': 'https://www.messenger.com', - 'accept-encoding': 'gzip, deflate', - 'x-msgr-region': 'ATN', - 'accept-language': 'en-US,en;q=0.8', - 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36', - 'content-type': 'multipart/form-data; boundary=----WebKitFormBoundary', - 'accept': '*/*', - 'referer': recipientUrl, - 'cookie': this.cookie, - 'authority': 'www.messenger.com' - }, + headers: headers, formData: { + // the ones commented out were parameters I wasn't sure whether + // they were needed but are confirmed unneeded now. '__a': '1', - '__be': '1', - '__dyn': '7AzkXh8Z398jgDxKy1l0BwRyaF3oyfJLFwgoqwWhEoyUnwgU9GGEcVovkwy3eE99XDG4UiwExW14DwPxSFEW2O9xicG4EnwkUC9z8Kew', - '__pc': 'EXP1:messengerdotcom_pkg', - '__req': '2q', - '__rev': '2335431', + // '__be': '0', + // '__dyn': '7AzkXh8Z398jgDxKy1l0BwRyaF3oyfJLFwgoqwWhEoyUnwgU9GGEcVovkwy3eE99XDG4UiwExW14DwPxSFEW2O9xicG4EnwkUC9z8Kew', + // '__pc': 'EXP1:messengerdotcom_pkg', + // '__req': '2q', + // '__rev': '2335431', '__user': this.userId, - 'dpr': '1.5', + // 'dpr': '1.5', 'fb_dtsg': this.fbdtsg, - 'jazoest': '21948', + // 'jazoest': '21948', 'upload': fs.createReadStream(filePath) }, gzip: true, @@ -207,6 +212,11 @@ class Messenger { } let formData = { + // everything's identical to the formData in sendMessage except: + // 1. no "body" + // 2. "has_attachment" set to true + // 3. no "html_body" + // 4. the id to the attachment is added in the if/else block below 'action_type': 'ma-type:user-generated-message', 'author': `fbid:${this.userId}`, 'timestamp': utcTimestamp, @@ -254,18 +264,7 @@ class Messenger { } request.post("https://www.messenger.com/messaging/send/?dpr=1", { - headers: { - 'origin': 'https://www.messenger.com', - 'accept-encoding': 'gzip, deflate', - 'x-msgr-region': 'ATN', - 'accept-language': 'en-US,en;q=0.8', - 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36', - 'content-type': 'application/x-www-form-urlencoded', - 'accept': '*/*', - 'referer': recipientUrl, - 'cookie': this.cookie, - 'authority': 'www.messenger.com' - }, + headers: headers, formData: formData }, (err) => { if (err) { @@ -332,31 +331,36 @@ class Messenger { const localTime = new Date().toLocaleTimeString().replace(/\s+/g, '').toLowerCase(); const messageId = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER); + // header is identical in both the upload endpoint and message-send endpoint + const headers = { + 'origin': 'https://www.messenger.com', + 'accept-encoding': 'gzip, deflate', + 'x-msgr-region': 'ATN', + 'accept-language': 'en-US,en;q=0.8', + 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36', + 'content-type': 'application/x-www-form-urlencoded', + 'accept': '*/*', + 'referer': recipientUrl, + 'cookie': this.cookie, + 'authority': 'www.messenger.com' + }; + request.post("https://upload.messenger.com/ajax/mercury/upload.php", { - headers: { - 'origin': 'https://www.messenger.com', - 'accept-encoding': 'gzip, deflate', - 'x-msgr-region': 'ATN', - 'accept-language': 'en-US,en;q=0.8', - 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36', - 'content-type': 'multipart/form-data; boundary=----WebKitFormBoundary', - 'accept': '*/*', - 'referer': recipientUrl, - 'cookie': this.cookie, - 'authority': 'www.messenger.com' - }, + headers: headers, formData: { + // the ones commented out were parameters I wasn't sure whether + // they were needed but are confirmed unneeded now. '__a': '1', - '__be': '1', - '__dyn': '7AzkXh8Z398jgDxKy1l0BwRyaF3oyfJLFwgoqwWhEoyUnwgU9GGEcVovkwy3eE99XDG4UiwExW14DwPxSFEW2O9xicG4EnwkUC9z8Kew', - '__pc': 'EXP1:messengerdotcom_pkg', - '__req': '2q', - '__rev': '2335431', + // '__be': '0', + // '__dyn': '7AzkXh8Z398jgDxKy1l0BwRyaF3oyfJLFwgoqwWhEoyUnwgU9GGEcVovkwy3eE99XDG4UiwExW14DwPxSFEW2O9xicG4EnwkUC9z8Kew', + // '__pc': 'EXP1:messengerdotcom_pkg', + // '__req': '2q', + // '__rev': '2335431', '__user': this.userId, - 'dpr': '1.5', + // 'dpr': '1.5', 'fb_dtsg': this.fbdtsg, - 'jazoest': '21948', - 'upload': fs.createReadStream(filePath), + // 'jazoest': '21948', + 'upload': fs.createReadStream(filePath) }, gzip: true, }, (err, res, body) => { @@ -375,6 +379,10 @@ class Messenger { } let formData = { + // everything's identical to the formData in sendMessage except: + // 1. no "body" + // 2. "has_attachment" set to true + // 3. the id to the attachment is added in the if/else block below 'action_type': 'ma-type:user-generated-message', 'author': `fbid:${this.userId}`, 'timestamp': utcTimestamp, @@ -408,18 +416,7 @@ class Messenger { } request.post("https://www.messenger.com/messaging/send/?dpr=1", { - headers: { - 'origin': 'https://www.messenger.com', - 'accept-encoding': 'gzip, deflate', - 'x-msgr-region': 'ATN', - 'accept-language': 'en-US,en;q=0.8', - 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36', - 'content-type': 'application/x-www-form-urlencoded', - 'accept': '*/*', - 'referer': recipientUrl, - 'cookie': this.cookie, - 'authority': 'www.messenger.com' - }, + headers: headers, formData: formData }, (err) => { if (err) {