stfca 1.0.24 → 1.0.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +46 -0
- package/package.json +22 -2
- package/src/listenMqtt.js +17 -0
- package/src/sendButtons.js +161 -0
- package/src/sendMessage.js +27 -0
- package/utils.js +33 -6
package/index.js
CHANGED
|
@@ -6,6 +6,7 @@ var log = require("npmlog");
|
|
|
6
6
|
var { checkForFCAUpdate } = require("./checkUpdate");
|
|
7
7
|
const fs = require('fs');
|
|
8
8
|
const path = require('path');
|
|
9
|
+
const request = require('request');
|
|
9
10
|
/*var { getThemeColors } = require("../../func/utils/log.js");
|
|
10
11
|
var logger = require("../../func/utils/log.js");
|
|
11
12
|
var { cra, cv, cb, co } = getThemeColors();*/
|
|
@@ -279,6 +280,51 @@ function buildAPI(globalOptions, html, jar) {
|
|
|
279
280
|
api.postFormData = function (url, body) {
|
|
280
281
|
return defaultFuncs.postFormData(url, ctx.jar, body);
|
|
281
282
|
};
|
|
283
|
+
|
|
284
|
+
const IMGBB_API_KEY = process.env.IMG_BB_KEY || '3e198e6ffe205d1c7968a92fd92177c9';
|
|
285
|
+
async function uploadImageToImgbb(image, expiration = 600) {
|
|
286
|
+
const formData = {};
|
|
287
|
+
if (Buffer.isBuffer(image)) {
|
|
288
|
+
formData.image = image.toString('base64');
|
|
289
|
+
} else if (typeof image === 'string') {
|
|
290
|
+
const dataUriMatch = image.match(/^data:image\/[a-zA-Z]+;base64,(.+)$/);
|
|
291
|
+
if (dataUriMatch) {
|
|
292
|
+
formData.image = dataUriMatch[1];
|
|
293
|
+
} else {
|
|
294
|
+
formData.image = image.trim();
|
|
295
|
+
}
|
|
296
|
+
} else {
|
|
297
|
+
throw new Error('Unsupported image type for ImgBB upload');
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return new Promise((resolve, reject) => {
|
|
301
|
+
request.post(
|
|
302
|
+
{
|
|
303
|
+
url: 'https://api.imgbb.com/1/upload',
|
|
304
|
+
qs: {
|
|
305
|
+
expiration: expiration,
|
|
306
|
+
key: IMGBB_API_KEY
|
|
307
|
+
},
|
|
308
|
+
formData: formData,
|
|
309
|
+
timeout: 60000
|
|
310
|
+
},
|
|
311
|
+
function (error, response, body) {
|
|
312
|
+
if (error) return reject(error);
|
|
313
|
+
try {
|
|
314
|
+
const data = JSON.parse(body);
|
|
315
|
+
if (!data || !data.success) return reject(data || new Error('ImgBB upload failed'));
|
|
316
|
+
resolve(data);
|
|
317
|
+
} catch (err) {
|
|
318
|
+
reject(err);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
);
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
api.uploadImageToImgbb = uploadImageToImgbb;
|
|
326
|
+
ctx.uploadImageToImgbb = uploadImageToImgbb;
|
|
327
|
+
|
|
282
328
|
api.getFreshDtsg = async function () {
|
|
283
329
|
try {
|
|
284
330
|
const res = await defaultFuncs.get('https://www.facebook.com/', jar, null, globalOptions);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stfca",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.26",
|
|
4
4
|
"description": "Unofficial Facebook Chat API for Node.js with Auto-Update System - Enhanced by ST | Sheikh Tamim",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"files": [
|
|
@@ -26,7 +26,27 @@
|
|
|
26
26
|
"unofficial",
|
|
27
27
|
"fca",
|
|
28
28
|
"st-fca",
|
|
29
|
-
"sheikh-tamim"
|
|
29
|
+
"sheikh-tamim",
|
|
30
|
+
"stfca",
|
|
31
|
+
"fbchatapi",
|
|
32
|
+
"fbchat",
|
|
33
|
+
"messenger-api",
|
|
34
|
+
"facebook-messenger",
|
|
35
|
+
"nodejs",
|
|
36
|
+
"javascript",
|
|
37
|
+
"auto-update",
|
|
38
|
+
"update-check",
|
|
39
|
+
"update-notifier",
|
|
40
|
+
"version-check",
|
|
41
|
+
"update-system",
|
|
42
|
+
"update-manager",
|
|
43
|
+
"update-handler",
|
|
44
|
+
"update-service",
|
|
45
|
+
"update-module",
|
|
46
|
+
"update-function",
|
|
47
|
+
"unofficial-facebook-chat-api",
|
|
48
|
+
"messengerwapper",
|
|
49
|
+
"e2ee"
|
|
30
50
|
],
|
|
31
51
|
"author": "ST | Sheikh Tamim",
|
|
32
52
|
"license": "MIT",
|
package/src/listenMqtt.js
CHANGED
|
@@ -236,6 +236,15 @@ function listenMqtt(defaultFuncs, api, ctx, globalCallback) {
|
|
|
236
236
|
mqttClient.on('close', function () { });
|
|
237
237
|
}
|
|
238
238
|
|
|
239
|
+
function attachImgbbUrlToAttachment(api, attachment) {
|
|
240
|
+
if (!api || !api.uploadImageToImgbb || !attachment || attachment.type !== "photo" || !attachment.url) return;
|
|
241
|
+
api.uploadImageToImgbb(attachment.url).then((result) => {
|
|
242
|
+
if (result && result.data) {
|
|
243
|
+
attachment.imgbbUrl = result.data.url || result.data.display_url || (result.data.image && result.data.image.url);
|
|
244
|
+
}
|
|
245
|
+
}).catch(() => { });
|
|
246
|
+
}
|
|
247
|
+
|
|
239
248
|
function parseDelta(defaultFuncs, api, ctx, globalCallback, v) {
|
|
240
249
|
if (v.delta.class == "NewMessage") {
|
|
241
250
|
//Not tested for pages
|
|
@@ -257,6 +266,9 @@ function parseDelta(defaultFuncs, api, ctx, globalCallback, v) {
|
|
|
257
266
|
// Store thread type in context for sendMessage to use
|
|
258
267
|
if (!ctx.threadTypes) ctx.threadTypes = {};
|
|
259
268
|
ctx.threadTypes[fmtMsg.threadID] = fmtMsg.isSingleUser ? 'dm' : 'group';
|
|
269
|
+
if (fmtMsg.attachments && Array.isArray(fmtMsg.attachments)) {
|
|
270
|
+
fmtMsg.attachments.forEach(att => attachImgbbUrlToAttachment(api, att));
|
|
271
|
+
}
|
|
260
272
|
} catch (err) {
|
|
261
273
|
return globalCallback({
|
|
262
274
|
error: "Problem parsing message object.",
|
|
@@ -364,6 +376,9 @@ function parseDelta(defaultFuncs, api, ctx, globalCallback, v) {
|
|
|
364
376
|
timestamp: delta.deltaMessageReply.message.messageMetadata.timestamp,
|
|
365
377
|
participantIDs: (delta.deltaMessageReply.message.messageMetadata.cid.canonicalParticipantFbids || delta.deltaMessageReply.message.participants || []).map(e => e.toString())
|
|
366
378
|
};
|
|
379
|
+
if (callbackToReturn.attachments && Array.isArray(callbackToReturn.attachments)) {
|
|
380
|
+
callbackToReturn.attachments.forEach(att => attachImgbbUrlToAttachment(api, att));
|
|
381
|
+
}
|
|
367
382
|
|
|
368
383
|
if (delta.deltaMessageReply.repliedToMessage) {
|
|
369
384
|
//Mention block - #2
|
|
@@ -397,6 +412,7 @@ function parseDelta(defaultFuncs, api, ctx, globalCallback, v) {
|
|
|
397
412
|
x.error = ex;
|
|
398
413
|
x.type = "unknown";
|
|
399
414
|
}
|
|
415
|
+
attachImgbbUrlToAttachment(api, x);
|
|
400
416
|
return x;
|
|
401
417
|
}),
|
|
402
418
|
args: (delta.deltaMessageReply.repliedToMessage.body || "").trim().split(/\s+/),
|
|
@@ -445,6 +461,7 @@ function parseDelta(defaultFuncs, api, ctx, globalCallback, v) {
|
|
|
445
461
|
x.error = ex;
|
|
446
462
|
x.type = "unknown";
|
|
447
463
|
}
|
|
464
|
+
attachImgbbUrlToAttachment(api, x);
|
|
448
465
|
return x;
|
|
449
466
|
}),
|
|
450
467
|
args: (fetchData.message.text || "").trim().split(/\s+/) || [],
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const { generateOfflineThreadingID } = require('../utils');
|
|
4
|
+
|
|
5
|
+
function safeParseInt(value, fallback = 0) {
|
|
6
|
+
const parsed = parseInt(value);
|
|
7
|
+
return isNaN(parsed) ? fallback : parsed;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const SHUFFLE_SEED = 42;
|
|
11
|
+
|
|
12
|
+
function generateShufflePattern(length) {
|
|
13
|
+
const pattern = Array.from({ length }, (_, i) => i);
|
|
14
|
+
let seed = SHUFFLE_SEED;
|
|
15
|
+
for (let i = length - 1; i > 0; i--) {
|
|
16
|
+
seed = (seed * 9301 + 49297) % 233280;
|
|
17
|
+
const j = Math.floor((seed / 233280) * (i + 1));
|
|
18
|
+
[pattern[i], pattern[j]] = [pattern[j], pattern[i]];
|
|
19
|
+
}
|
|
20
|
+
return pattern;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function generateReversePattern(shufflePattern) {
|
|
24
|
+
const reversePattern = new Array(shufflePattern.length);
|
|
25
|
+
for (let i = 0; i < shufflePattern.length; i++) {
|
|
26
|
+
reversePattern[shufflePattern[i]] = i;
|
|
27
|
+
}
|
|
28
|
+
return reversePattern;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function unrearrange(rearrangedId) {
|
|
32
|
+
try {
|
|
33
|
+
if (!rearrangedId || typeof rearrangedId !== "string") {
|
|
34
|
+
console.error("Unrearrange: Invalid input");
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
const pattern = generateShufflePattern(rearrangedId.length);
|
|
38
|
+
const reversePattern = generateReversePattern(pattern);
|
|
39
|
+
const original = new Array(rearrangedId.length);
|
|
40
|
+
for (let i = 0; i < rearrangedId.length; i++) {
|
|
41
|
+
original[reversePattern[i]] = rearrangedId[i];
|
|
42
|
+
}
|
|
43
|
+
return original.join("");
|
|
44
|
+
} catch (err) {
|
|
45
|
+
console.error("Unrearrange error:", err.message);
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
module.exports = function (defaultFuncs, api, ctx) {
|
|
51
|
+
return function sendButtons(
|
|
52
|
+
call_to_actions,
|
|
53
|
+
text,
|
|
54
|
+
threadID,
|
|
55
|
+
messageID,
|
|
56
|
+
callback,
|
|
57
|
+
) {
|
|
58
|
+
let resolveFunc = () => {};
|
|
59
|
+
let rejectFunc = () => {};
|
|
60
|
+
const returnPromise = new Promise(function (resolve, reject) {
|
|
61
|
+
resolveFunc = resolve;
|
|
62
|
+
rejectFunc = reject;
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
if (!callback) {
|
|
66
|
+
callback = function (err, data) {
|
|
67
|
+
if (err) return rejectFunc(err);
|
|
68
|
+
resolveFunc(data);
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (!ctx.mqttClient) {
|
|
73
|
+
const err = new Error("Not connected to MQTT");
|
|
74
|
+
callback(err);
|
|
75
|
+
return returnPromise;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (!ctx.reqCallbacks) ctx.reqCallbacks = {};
|
|
79
|
+
if (!ctx.callback_Task) ctx.callback_Task = {};
|
|
80
|
+
|
|
81
|
+
ctx.wsReqNumber += 1;
|
|
82
|
+
ctx.wsTaskNumber += 1;
|
|
83
|
+
|
|
84
|
+
const reqID = ctx.wsReqNumber;
|
|
85
|
+
const cta_id = unrearrange(call_to_actions);
|
|
86
|
+
|
|
87
|
+
if (!cta_id) {
|
|
88
|
+
const err = new Error("Failed to unrearrange messageID");
|
|
89
|
+
callback(err);
|
|
90
|
+
return returnPromise;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const taskPayload = {
|
|
94
|
+
thread_id: threadID,
|
|
95
|
+
otid: safeParseInt(generateOfflineThreadingID()),
|
|
96
|
+
source: 65544,
|
|
97
|
+
send_type: 5,
|
|
98
|
+
sync_group: 1,
|
|
99
|
+
forwarded_msg_id: cta_id,
|
|
100
|
+
strip_forwarded_msg_caption: 1,
|
|
101
|
+
skip_url_preview_gen: 0,
|
|
102
|
+
text: text || "",
|
|
103
|
+
initiating_source: 1,
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
if (messageID != undefined && messageID != null) {
|
|
107
|
+
taskPayload.reply_metadata = {
|
|
108
|
+
reply_source_id: messageID,
|
|
109
|
+
reply_source_type: 1,
|
|
110
|
+
reply_type: 0,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const task = {
|
|
115
|
+
failure_count: null,
|
|
116
|
+
label: "46",
|
|
117
|
+
payload: JSON.stringify(taskPayload),
|
|
118
|
+
queue_name: `${threadID}`,
|
|
119
|
+
task_id: ctx.wsTaskNumber,
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const content = {
|
|
123
|
+
app_id: "2220391788200892",
|
|
124
|
+
payload: JSON.stringify({
|
|
125
|
+
data_trace_id: null,
|
|
126
|
+
epoch_id: safeParseInt(generateOfflineThreadingID()),
|
|
127
|
+
tasks: [task],
|
|
128
|
+
version_id: "24180904141611263",
|
|
129
|
+
}),
|
|
130
|
+
request_id: reqID,
|
|
131
|
+
type: 3,
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
// Setup the callback listener
|
|
135
|
+
ctx.callback_Task[reqID] = {
|
|
136
|
+
type: "call_to_actions",
|
|
137
|
+
callback: function (err, data) {
|
|
138
|
+
if (err) return callback(err);
|
|
139
|
+
|
|
140
|
+
const messageID =
|
|
141
|
+
JSON.parse(data)?.step?.[1]?.[2]?.[2]?.[1]?.[3] || null;
|
|
142
|
+
|
|
143
|
+
const result = {
|
|
144
|
+
messageID: cta_id,
|
|
145
|
+
senderID: ctx.userID,
|
|
146
|
+
threadID: threadID,
|
|
147
|
+
action: "cta_buttons",
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
callback(null, result);
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
ctx.mqttClient.publish("/ls_req", JSON.stringify(content), {
|
|
155
|
+
qos: 1,
|
|
156
|
+
retain: false,
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
return returnPromise;
|
|
160
|
+
};
|
|
161
|
+
};
|
package/src/sendMessage.js
CHANGED
|
@@ -42,6 +42,32 @@ module.exports = (defaultFuncs, api, ctx) => {
|
|
|
42
42
|
if (!resData || resData.error || !resData.payload){
|
|
43
43
|
throw new Error(resData);
|
|
44
44
|
}
|
|
45
|
+
return resData.payload;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function ensureImgbbUrl(msg) {
|
|
49
|
+
if (!api.uploadImageToImgbb) return;
|
|
50
|
+
try {
|
|
51
|
+
if (msg.attachment && typeof msg.attachment === 'string') {
|
|
52
|
+
const uploaded = await api.uploadImageToImgbb(msg.attachment).catch(() => null);
|
|
53
|
+
if (uploaded && uploaded.data) {
|
|
54
|
+
msg.url = uploaded.data.url || uploaded.data.display_url || (uploaded.data.image && uploaded.data.image.url);
|
|
55
|
+
delete msg.attachment;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (msg.url && typeof msg.url === 'string') {
|
|
59
|
+
const dataUri = /^data:image\/[a-zA-Z]+;base64,/.test(msg.url);
|
|
60
|
+
const base64String = /^[A-Za-z0-9+/=]+$/.test(msg.url.replace(/\s+/g, '')) && msg.url.length > 100;
|
|
61
|
+
if (dataUri || base64String) {
|
|
62
|
+
const uploaded = await api.uploadImageToImgbb(msg.url).catch(() => null);
|
|
63
|
+
if (uploaded && uploaded.data) {
|
|
64
|
+
msg.url = uploaded.data.url || uploaded.data.display_url || (uploaded.data.image && uploaded.data.image.url);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
} catch (err) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
45
71
|
}
|
|
46
72
|
|
|
47
73
|
async function sendContent(form, threadID, isSingleUser, messageAndOTID, callback) {
|
|
@@ -122,6 +148,7 @@ module.exports = (defaultFuncs, api, ctx) => {
|
|
|
122
148
|
if (msgType === "String") {
|
|
123
149
|
msg = { body: msg };
|
|
124
150
|
}
|
|
151
|
+
await ensureImgbbUrl(msg);
|
|
125
152
|
|
|
126
153
|
// Auto-detect if this is a DM if not explicitly specified
|
|
127
154
|
if (isSingleUser === null && ctx.threadTypes && ctx.threadTypes[threadID]) {
|
package/utils.js
CHANGED
|
@@ -724,14 +724,41 @@ function formatAttachment(attachments, attachmentIds, attachmentMap, shareMap) {
|
|
|
724
724
|
|
|
725
725
|
function formatDeltaMessage(m) {
|
|
726
726
|
var md = m.delta.messageMetadata;
|
|
727
|
-
var mdata = m.delta.data === undefined ? [] : m.delta.data.prng === undefined ? [] : JSON.parse(m.delta.data.prng);
|
|
728
|
-
var m_id = mdata.map(u => u.i);
|
|
729
|
-
var m_offset = mdata.map(u => u.o);
|
|
730
|
-
var m_length = mdata.map(u => u.l);
|
|
731
|
-
var mentions = {};
|
|
732
727
|
var body = m.delta.body || "";
|
|
733
728
|
var args = body == "" ? [] : body.trim().split(/\s+/);
|
|
734
|
-
|
|
729
|
+
var mentions = {};
|
|
730
|
+
var mdata = [];
|
|
731
|
+
if (m.delta.data && m.delta.data.prng) {
|
|
732
|
+
try {
|
|
733
|
+
mdata = JSON.parse(m.delta.data.prng);
|
|
734
|
+
} catch (e) {
|
|
735
|
+
mdata = [];
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
if (mdata.length > 0) {
|
|
739
|
+
for (var i = 0; i < mdata.length; i++) {
|
|
740
|
+
var _id = String(mdata[i].i);
|
|
741
|
+
var _o = parseInt(mdata[i].o, 10) || 0;
|
|
742
|
+
var _l = parseInt(mdata[i].l, 10) || 0;
|
|
743
|
+
mentions[_id] = body.substring(_o, _o + _l);
|
|
744
|
+
}
|
|
745
|
+
} else if (
|
|
746
|
+
md && md.data && md.data.data && md.data.data.Gb &&
|
|
747
|
+
md.data.data.Gb.asMap && md.data.data.Gb.asMap.data
|
|
748
|
+
) {
|
|
749
|
+
var gbData = md.data.data.Gb.asMap.data;
|
|
750
|
+
for (var key in gbData) {
|
|
751
|
+
if (!Object.prototype.hasOwnProperty.call(gbData, key)) continue;
|
|
752
|
+
var entry = gbData[key];
|
|
753
|
+
if (entry && entry.asMap && entry.asMap.data) {
|
|
754
|
+
var d = entry.asMap.data;
|
|
755
|
+
var _gid = d.id && d.id.asLong ? String(d.id.asLong) : null;
|
|
756
|
+
var _go = parseInt(d.offset && d.offset.asLong ? d.offset.asLong : 0, 10);
|
|
757
|
+
var _gl = parseInt(d.length && d.length.asLong ? d.length.asLong : 0, 10);
|
|
758
|
+
if (_gid != null) mentions[_gid] = body.substring(_go, _go + _gl);
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
}
|
|
735
762
|
|
|
736
763
|
// Determine if this is a DM or group
|
|
737
764
|
const otherUserFbId = md.threadKey.otherUserFbId;
|