stfca 1.0.25 → 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 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.25",
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
+ };
@@ -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]) {