dunbot / source /apis /lib /Messenger.js
Duongkum999's picture
Upload 56 files
91d9d20 verified
/*
Origin Source: https://github.com/ntkhang03/Goat-Bot-V2/blob/main/fb-chat-api/
Converter: Khang
*/
var utils = require("../utils");
var allowedProperties = {
attachments: true,
url: true,
sticker: true,
emoji: true,
emojiSize: true,
body: true,
mentions: true,
location: true
}
function removeSpecialChar(inputString) {
if (typeof inputString !== "string")
return inputString;
var buffer = Buffer.from(inputString, "utf8");
let filteredBuffer = Buffer.alloc(0);
for (let i = 0; i < buffer.length; i++) {
if (buffer[i] === 0xEF && buffer[i + 1] === 0xB8 && buffer[i + 2] === 0x8F)
i += 2;
else
filteredBuffer = Buffer.concat([filteredBuffer, buffer.slice(i, i + 1)]);
}
var convertedString = filteredBuffer.toString("utf8");
return convertedString;
}
module.exports = function (http, apis, ctx) {
function edit(message, messageID, callback) {
var pCallback;
var returnPromise = new Promise(function (resolve, reject) {
pCallback = error => error ? reject(error) : resolve();
});
if (typeof message === "function") {
callback = message;
message = "";
}
if (typeof messageID === "function") {
callback = messageID;
messageID = null;
}
if (typeof callback !== "function")
callback = pCallback;
if (typeof messageID !== "string")
callback(new Error("messageID must be an string"));
else if (!ctx.Client)
callback(new Error("You must connect mqtt first"));
else {
var queryPayload = {
message_id: messageID,
text: message
}
var query = {
failure_count: null,
label: "742",
payload: JSON.stringify(queryPayload),
queue_name: "edit_message",
task_id: Math.round(Math.random() * 312312721413).toString()
}
var context = {
app_id: "2220391788200892",
payload: JSON.stringify({
data_trace_id: null,
epoch_id: 0,
tasks: [query],
version_id: "6903494529735864"
}),
request_id: Math.round(Math.random() * 312312721413).toString(),
type: 3
}
ctx.Client.publish("/ls_req", JSON.stringify(context), { qos: 1, retain: false });
callback();
}
return returnPromise;
}
function react(reaction, messageID, callback) {
var pCallback;
var returnPromise = new Promise(function (resolve, reject) {
pCallback = error => error ? reject(error) : resolve();
});
if (typeof reaction === "function") {
callback = reaction;
reaction = "";
}
if (typeof messageID === "function") {
callback = messageID;
messageID = null;
}
if (typeof callback !== "function")
callback = pCallback;
if (typeof messageID !== "string")
callback(new Error("messageID must be an string"));
else {
var variables = {
data: {
client_mutation_id: Math.round(Math.random() * 312312721413).toString(),
actor_id: ctx.userID,
action: reaction === "" ? "REMOVE_REACTION" : "ADD_REACTION",
message_id: messageID,
reaction
}
}
var qs = {
doc_id: "1491398900900362",
variables: JSON.stringify(variables),
dpr: 1
}
http
.postData("https://www.facebook.com/webgraphql/mutation/", ctx.jar, {}, qs)
.then(utils.parseAndCheckLogin(ctx.jar, http))
.then(function (res) {
if (res.error || res.errors)
throw res;
return callback();
})
.catch(function (error) {
if (error.type === "logout.")
ctx.isLogin = false;
return callback(error);
});
}
return returnPromise;
}
function unsend(messageID, callback) {
var pCallback;
var returnPromise = new Promise(function (resolve, reject) {
pCallback = error => error ? reject(error) : resolve();
});
if (typeof messageID === "function") {
callback = messageID;
messageID = null;
}
if (typeof callback !== "function")
callback = pCallback;
if (typeof messageID !== "string")
callback(new Error("messageID must be an string"));
else {
http
.post("https://www.facebook.com/messaging/unsend_message/", ctx.jar, { message_id: messageID })
.then(utils.parseAndCheckLogin(ctx, http))
.then(function (res) {
if (res.error || res.errors)
throw res;
return callback();
})
.catch(function (error) {
if (error.type === "logout.")
ctx.isLogin = false;
return callback(error);
});
}
return returnPromise;
}
function send(message, threadID, messageID, callback, isGroup) {
var pCallback;
var returnPromise = new Promise(function (resolve, reject) {
pCallback = error => error ? reject(error) : resolve();
});
if (typeof message === "function") {
callback = message;
message = null;
}
if (typeof threadID === "function") {
callback = threadID;
threadID = null;
}
if (typeof messageID === "function") {
callback = messageID;
messageID = null;
}
if (typeof callback !== "function")
callback = pCallback;
var messageType = utils.getType(message);
var threadIDType = utils.getType(threadID);
var messageIDType = utils.getType(messageID);
var error;
if (messageType !== "String" && messageType !== "Object")
error = new Error("Message should be of type string or object and not " + messageType + ".");
if (threadIDType !== "Array" && threadIDType !== "Number" && threadIDType !== "String")
error = new Error("ThreadID should be of type number, string, or array and not " + threadIDType + ".");
if (messageID && messageIDType !== "String")
error = new Error("MessageID should be of type string and not " + messageIDType + ".");
if (messageType === "String")
message = {
body: message
}
if (utils.getType(message.body) === "String")
message.body = removeSpecialChar(message.body);
var disallowedProperties = Object.keys(message).filter(prop => !allowedProperties[prop]);
if (disallowedProperties.length > 0)
error = new Error("Dissallowed props: `" + disallowedProperties.join(", ") + "`");
if (error)
callback(error);
else {
var messageAndOTID = utils.generateOfflineThreadingID();
var form = {
client: "mercury",
action_type: "ma-type:user-generated-message",
author: "fbid:" + ctx.userID,
timestamp: Date.now(),
timestamp_absolute: "Today",
timestamp_relative: utils.generateTimestampRelative(),
timestamp_time_passed: "0",
is_unread: false,
is_cleared: 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:chat:web",
"source_tags[0]": "source:chat",
body: message.body ? message.body.toString() : "",
html_body: false,
ui_push_phase: "V3",
status: "0",
offline_threading_id: messageAndOTID,
message_id: messageAndOTID,
threading_id: utils.generateThreadingID(ctx.clientID),
"ephemeral_ttl_mode:": "0",
manual_retry_cnt: "0",
has_attachment: !!(message.attachments || message.url || message.sticker),
signatureID: utils.getSignatureID(),
replied_to_message_id: messageID
}
handleLocation(message, form, threadID, messageAndOTID, callback, isGroup)
.then(handleSticker)
.then(handleAttachment)
.then(handleURL)
.then(handleEmoji)
.then(handleMention)
.then(function (input) {
if (utils.getType(input[2]) === "Array")
input[5] = false;
else {
if (utils.getType(input[5]) !== "Boolean")
input[5] = input[2].toString().length < 16;
else
input[5] = !input[5];
}
return input;
})
.then(sendContent)
.catch(function (error) {
if (error.type === "logout.")
ctx.isLogin = false;
return callback(error);
});
}
return returnPromise;
}
function handleLocation(...input) {
if (input[0].location) {
if (input[0].location.latitude === null || input[0].location.longitude === null)
return Promise.reject(new Error("location property needs both latitude and longitude"));
input[1]["location_attachment[coordinates][latitude]"] = input[0].location.latitude;
input[1]["location_attachment[coordinates][longitude]"] = input[0].location.longitude;
input[1]["location_attachment[is_current_location]"] = !!input[0].location.current;
}
return Promise.resolve(input);
}
function handleEmoji(input) {
if (input[0].emojiSize !== null && input[0].emoji === null)
return Promise.reject(new Error("emoji property is empty"));
if (input[0].emoji) {
if (input[0].emojiSize == null)
input[0].emojiSize = "medium";
if (input[0].emojiSize !== "small" && input[0].emojiSize !== "medium" && input[0].emojiSize !== "large")
return Promise.reject(new Error("emojiSize property is invalid"));
if (input[1].body !== null && input[1].body !== "")
return Promise.reject(new Error("body is not empty"));
input[1].body = input[0].emoji;
input[1]["tags[0]"] = "hot_emoji_size:" + input[0].emojiSize;
}
return Promise.resolve(input);
}
function handleSticker(input) {
if (input[0].sticker)
input[1].sticker_id = input[0].sticker;
return Promise.resolve(input);
}
function handleAttachment(input) {
if (input[0].attachments) {
input[1].image_ids = [];
input[1].gif_ids = [];
input[1].file_ids = [];
input[1].video_ids = [];
input[1].audio_ids = [];
input[0].attachments = Array.isArray(input[0].attachments) ? input[0].attachments : [input[0].attachments];
return handleUpload(input[0].attachments)
.then(function (files) {
files.forEach(function (file) {
var key = Object.keys(file);
var type = key[0];
input[1][type + "s"].push(file[type]);
});
return input;
});
}
return Promise.resolve(input);
}
function handleUpload(attachments) {
var uploads = [];
for (var i = 0; i < attachments.length; i++) {
if (!utils.isReadableStream(attachments[i]))
throw new Error("Attachment should be a readable stream and not " + utils.getType(attachments[i]) + ".");
var form = {
upload_1024: attachments[i],
voice_clip: "true"
}
uploads.push(
http
.postData("https://upload.facebook.com/ajax/mercury/upload.php", ctx.jar, form)
.then(utils.parseAndCheckLogin(ctx, http))
.then(function (res) {
if (res.error || res.errors)
throw res;
return res.payload.metadata[0];
})
);
}
return Promise.all(uploads);
}
function handleURL(input) {
if (input[0].url) {
input[1]["shareable_attachment[share_type]"] = "100";
return getURL()
.then(function (params) {
input[1]["shareable_attachment[share_params]"] = params;
return input;
});
}
return Promise.resolve(input);
}
function getURL(url) {
var form = {
image_height: 960,
image_width: 960,
uri: url
}
return http
.post("https://www.facebook.com/message_share_attachment/fromURI/", ctx.jar, form)
.then(utils.parseAndCheckLogin(ctx, http))
.then(function (res) {
if (res.error || res.errors || !res.payload)
throw res;
return res.payload.share_data.share_params;
});
}
function handleMention(input) {
if (input[0].mentions) {
for (var i = 0; i < input[0].mentions.length; i++) {
var mention = input[0].mentions[i];
var tag = mention.tag;
if (typeof tag !== "string")
return Promise.reject(new Error("Mention tags must be strings."));
var offset = input[0].body.indexOf(tag, mention.fromIndex || 0);
var id = mention.id || 0;
input[1]["profile_xmd[" + i + "][offset]"] = offset;
input[1]["profile_xmd[" + i + "][length]"] = tag.length;
input[1]["profile_xmd[" + i + "][id]"] = id;
input[1]["profile_xmd[" + i + "][type]"] = "p";
}
}
return Promise.resolve(input);
}
function sendContent(input) {
if (utils.getType(input[2]) === "Array") {
for (var i = 0; i < input[2].length; i++)
input[1]["specific_to_list[" + i + "]"] = "fbid:" + input[2][i];
input[1]["specific_to_list[" + input[2].length + "]"] = "fbid:" + ctx.userID;
input[1]["client_thread_id"] = "root:" + input[3];
} else {
if (input[5]) {
input[1]["specific_to_list[0]"] = "fbid:" + input[2];
input[1]["specific_to_list[1]"] = "fbid:" + ctx.userID;
input[1]["other_user_fbid"] = input[2];
} else
input[1]["thread_fbid"] = input[2];
}
return http
.post("https://www.facebook.com/messaging/send/", ctx.jar, input[1])
.then(utils.parseAndCheckLogin(ctx, http))
.then(function (res) {
if (!res || res.error || res.errors)
throw res
var messageInfo = res.payload.actions.reduce(function (p, v) {
return {
threadID: v.thread_fbid || v.other_user_fbid,
messageID: v.message_id,
timestamp: v.timestamp
}
}, null);
return input[4](null, messageInfo);
});
}
return {
send,
edit,
react,
unsend
}
}