fca-priyansh
Version:
Facebook-chat-api made by Priyanshu Rajput
245 lines (219 loc) • 6.87 kB
JavaScript
;
/**
* Comment API Module
* Create comments on Facebook posts with support for attachments, mentions, stickers, and URLs
*
* @author Priyansh Rajput
* @github https://github.com/priyanshufsdev
* @license MIT
*/
var utils = require("../utils");
var log = require("npmlog");
/**
* Handle upload of attachments (images/videos) for comment
* @param {object} defaultFuncs - Default functions for API requests
* @param {object} ctx - Context object
* @param {object} msg - Message object containing attachments
* @param {object} form - Form object to populate
* @returns {Promise<void>}
*/
async function handleUpload(defaultFuncs, ctx, msg, form) {
if (!msg.attachments || msg.attachments.length === 0) {
return;
}
var uploads = msg.attachments.map(function(item) {
if (!utils.isReadableStream(item)) {
throw new Error('Attachments must be a readable stream.');
}
return defaultFuncs
.postFormData('https://www.facebook.com/ajax/ufi/upload/', ctx.jar, {
profile_id: ctx.userID,
source: 19,
target_id: ctx.userID,
file: item
})
.then(utils.parseAndCheckLogin(ctx, defaultFuncs))
.then(function(res) {
if (res.error || !res.payload || !res.payload.fbid) {
throw res;
}
return { media: { id: res.payload.fbid } };
});
});
var results = await Promise.all(uploads);
results.forEach(function(result) {
form.input.attachments.push(result);
});
}
/**
* Handle URL attachment for comment
* @param {object} msg - Message object
* @param {object} form - Form object
*/
function handleURL(msg, form) {
if (typeof msg.url === 'string') {
form.input.attachments.push({
link: {
external: {
url: msg.url
}
}
});
}
}
/**
* Handle mentions in comment body
* @param {object} msg - Message object
* @param {object} form - Form object
*/
function handleMentions(msg, form) {
if (!msg.mentions) return;
for (var i = 0; i < msg.mentions.length; i++) {
var item = msg.mentions[i];
var tag = item.tag;
var id = item.id;
var fromIndex = item.fromIndex;
if (typeof tag !== 'string' || !id) {
log.warn('comment', 'Mentions must have a string "tag" and an "id".');
continue;
}
var offset = msg.body.indexOf(tag, fromIndex || 0);
if (offset < 0) {
log.warn('comment', 'Mention for "' + tag + '" not found in message string.');
continue;
}
form.input.message.ranges.push({
entity: { id: id },
length: tag.length,
offset: offset
});
}
}
/**
* Handle sticker attachment for comment
* @param {object} msg - Message object
* @param {object} form - Form object
*/
function handleSticker(msg, form) {
if (msg.sticker) {
form.input.attachments.push({
media: {
id: String(msg.sticker)
}
});
}
}
/**
* Submit final comment form to GraphQL endpoint
* @param {object} defaultFuncs - Default functions
* @param {object} ctx - Context object
* @param {object} form - Fully constructed form object
* @returns {Promise<object>}
*/
async function createContent(defaultFuncs, ctx, form) {
var res = await defaultFuncs
.post('https://www.facebook.com/api/graphql/', ctx.jar, {
fb_api_caller_class: 'RelayModern',
fb_api_req_friendly_name: 'useCometUFICreateCommentMutation',
variables: JSON.stringify(form),
server_timestamps: true,
doc_id: 6993516810709754
})
.then(utils.parseAndCheckLogin(ctx, defaultFuncs));
if (res.errors) {
throw res;
}
var commentEdge = res.data.comment_create.feedback_comment_edge;
return {
id: commentEdge.node.id,
url: commentEdge.node.feedback.url,
count: res.data.comment_create.feedback.total_comment_count
};
}
module.exports = function(defaultFuncs, api, ctx) {
/**
* Create a comment on a Facebook post
* Can also reply to an existing comment
*
* @param {string|object} msg - Message to post (string or object with body, attachments, mentions, etc.)
* @param {string} postID - ID of the post to comment on
* @param {string} replyCommentID - Optional: ID of comment to reply to
* @param {function} callback - Optional callback function
* @returns {Promise<object>}
*/
return function createCommentPost(msg, postID, replyCommentID, callback) {
var resolveFunc = function() {};
var rejectFunc = function() {};
var returnPromise = new Promise(function(resolve, reject) {
resolveFunc = resolve;
rejectFunc = reject;
});
// Handle optional parameters
if (typeof replyCommentID === 'function') {
callback = replyCommentID;
replyCommentID = null;
}
if (!callback) {
callback = function(err, data) {
if (err) return rejectFunc(err);
resolveFunc(data);
};
}
// Validation
if (typeof msg !== 'string' && typeof msg !== 'object') {
var error = 'Message must be a string or an object.';
log.error('comment', error);
return callback({ error: error });
}
if (typeof postID !== 'string') {
var error2 = 'postID must be a string.';
log.error('comment', error2);
return callback({ error: error2 });
}
// Prepare message object
var messageObject = typeof msg === 'string' ? { body: msg } : msg;
messageObject.mentions = messageObject.mentions || [];
messageObject.attachments = messageObject.attachments || [];
// Build form
var form = {
feedLocation: 'NEWSFEED',
feedbackSource: 1,
groupID: null,
input: {
client_mutation_id: Math.round(Math.random() * 19).toString(),
actor_id: ctx.userID,
attachments: [],
feedback_id: Buffer.from('feedback:' + postID).toString('base64'),
message: {
ranges: [],
text: messageObject.body || ''
},
reply_comment_parent_fbid: replyCommentID || null,
is_tracking_encrypted: true,
tracking: [],
feedback_source: 'NEWS_FEED',
idempotence_token: 'client:' + utils.getGUID(),
session_id: utils.getGUID()
},
scale: 1,
useDefaultActor: false
};
// Process all handlers and create comment
handleUpload(defaultFuncs, ctx, messageObject, form)
.then(function() {
handleURL(messageObject, form);
handleMentions(messageObject, form);
handleSticker(messageObject, form);
return createContent(defaultFuncs, ctx, form);
})
.then(function(info) {
log.info('comment', 'Comment created successfully: ' + info.id);
callback(null, info);
})
.catch(function(err) {
log.error('comment', err);
callback(err);
});
return returnPromise;
};
};