sagor-fca 0.0.18 → 0.0.19

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/checkUpdate.js ADDED
@@ -0,0 +1,116 @@
1
+ const axios = require('axios');
2
+ const { execSync } = require('child_process');
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ function getCurrentVersion() {
7
+ // 1. If this file is inside the sagor-fca package itself (development mode), use its own package.json
8
+ try {
9
+ const ownPkg = path.join(__dirname, 'package.json');
10
+ if (fs.existsSync(ownPkg)) {
11
+ const pkg = JSON.parse(fs.readFileSync(ownPkg, 'utf-8'));
12
+ if (pkg.name === 'sagor-fca' && pkg.version) return pkg.version;
13
+ }
14
+ } catch (_) { }
15
+
16
+ // 2. Installed as dependency in a user's project
17
+ try {
18
+ const nodeModulesPkg = path.join(process.cwd(), 'node_modules', 'sagor-fca', 'package.json');
19
+ if (fs.existsSync(nodeModulesPkg)) {
20
+ const pkg = JSON.parse(fs.readFileSync(nodeModulesPkg, 'utf-8'));
21
+ if (pkg.version) return pkg.version;
22
+ }
23
+ } catch (_) { }
24
+
25
+ return '1.0.0';
26
+ }
27
+
28
+ async function checkForFCAUpdate() {
29
+ try {
30
+ console.log('\x1b[33m%s\x1b[0m', '🔍 Checking for SAGOR-FCA updates...');
31
+
32
+ const { data: npmData } = await axios.get(
33
+ 'https://registry.npmjs.org/sagor-fca/latest'
34
+ );
35
+
36
+ const latestVersion = npmData.version;
37
+ const currentVersion = getCurrentVersion();
38
+
39
+ if (latestVersion !== currentVersion) {
40
+ const isNewer = compareVersions(latestVersion, currentVersion) > 0;
41
+ if (!isNewer) {
42
+ console.log('\x1b[32m%s\x1b[0m', `✅ SAGOR-FCA is up to date (v${currentVersion})`);
43
+ return false;
44
+ }
45
+
46
+ console.log('\x1b[32m%s\x1b[0m', `✨ New SAGOR-FCA version available: ${latestVersion} (current: ${currentVersion})`);
47
+ console.log('\x1b[33m%s\x1b[0m', '📦 Updating SAGOR-FCA package...');
48
+
49
+ try {
50
+ const { data: changesData } = await axios.get(
51
+ 'https://raw.githubusercontent.com/SAGOR-KINGx/SAGOR-FCA/main/CHANGELOG.md'
52
+ );
53
+ console.log('\x1b[36m%s\x1b[0m', '📋 Recent Changes:');
54
+ const latestChanges = changesData.split('##')[1]?.split('\n').slice(0, 5).join('\n');
55
+ if (latestChanges) console.log(latestChanges);
56
+ } catch (_) { }
57
+
58
+ await updateNpmPackage(latestVersion);
59
+ await updateUserPackageJson(latestVersion);
60
+
61
+ console.log('\x1b[32m%s\x1b[0m', '✅ SAGOR-FCA updated successfully!');
62
+ console.log('\x1b[33m%s\x1b[0m', '🔄 Restarting to apply changes...');
63
+
64
+ setTimeout(() => { process.exit(2); }, 1000);
65
+ return true;
66
+ } else {
67
+ console.log('\x1b[32m%s\x1b[0m', `✅ SAGOR-FCA is up to date (v${currentVersion})`);
68
+ return false;
69
+ }
70
+ } catch (error) {
71
+ console.log('\x1b[31m%s\x1b[0m', '❌ Failed to check for SAGOR-FCA updates:', error.message);
72
+ return false;
73
+ }
74
+ }
75
+
76
+ function compareVersions(a, b) {
77
+ var pa = a.split('.').map(Number);
78
+ var pb = b.split('.').map(Number);
79
+ for (var i = 0; i < 3; i++) {
80
+ var na = pa[i] || 0, nb = pb[i] || 0;
81
+ if (na > nb) return 1;
82
+ if (na < nb) return -1;
83
+ }
84
+ return 0;
85
+ }
86
+
87
+ async function updateNpmPackage(version) {
88
+ try {
89
+ console.log('\x1b[36m%s\x1b[0m', `📦 Running npm install sagor-fca@${version}...`);
90
+ execSync(`npm install sagor-fca@${version} --save`, { cwd: process.cwd(), stdio: 'inherit' });
91
+ console.log('\x1b[32m%s\x1b[0m', '✅ Package installed successfully!');
92
+ return true;
93
+ } catch (error) {
94
+ console.log('\x1b[31m%s\x1b[0m', '❌ Failed to install package:', error.message);
95
+ throw error;
96
+ }
97
+ }
98
+
99
+ async function updateUserPackageJson(version) {
100
+ try {
101
+ const userPackageJsonPath = path.join(process.cwd(), 'package.json');
102
+ if (!fs.existsSync(userPackageJsonPath)) return;
103
+ const packageJson = JSON.parse(fs.readFileSync(userPackageJsonPath, 'utf-8'));
104
+ if (packageJson.dependencies && packageJson.dependencies.sagor-fca) {
105
+ packageJson.dependencies.sagor-fca = `^${version}`;
106
+ fs.writeFileSync(userPackageJsonPath, JSON.stringify(packageJson, null, 2));
107
+ console.log('\x1b[32m%s\x1b[0m', `✅ Updated package.json to sagor-fca@${version}`);
108
+ }
109
+ return true;
110
+ } catch (error) {
111
+ console.log('\x1b[31m%s\x1b[0m', '⚠️ Failed to update user package.json:', error.message);
112
+ return false;
113
+ }
114
+ }
115
+
116
+ module.exports = { checkForFCAUpdate, updateNpmPackage, updateUserPackageJson };
package/index.js CHANGED
@@ -3,8 +3,10 @@
3
3
  var utils = require("./utils");
4
4
  var cheerio = require("cheerio");
5
5
  var log = require("npmlog");
6
+ var { checkForFCAUpdate } = require("./checkUpdate");
6
7
  const fs = require('fs');
7
8
  const path = require('path');
9
+ const request = require('request');
8
10
  /*var { getThemeColors } = require("../../func/utils/log.js");
9
11
  var logger = require("../../func/utils/log.js");
10
12
  var { cra, cv, cb, co } = getThemeColors();*/
@@ -12,6 +14,18 @@ log.maxRecordSize = 100;
12
14
  var checkVerified = null;
13
15
  const Boolean_Option = ['online', 'selfListen', 'listenEvents', 'updatePresence', 'forceLogin', 'autoMarkDelivery', 'autoMarkRead', 'listenTyping', 'autoReconnect', 'emitReady'];
14
16
  global.ditconmemay = false;
17
+ global.sagor-fcaUpdateChecked = false;
18
+
19
+ // Auto-check for updates on package load (non-blocking)
20
+ if (!global.sagor-fcaUpdateChecked) {
21
+ global.sagor-fcaUpdateChecked = true;
22
+ const { checkForFCAUpdate } = require("./checkUpdate");
23
+ setImmediate(() => {
24
+ checkForFCAUpdate().catch(() => {
25
+ // Silent fail - don't interrupt user's bot
26
+ });
27
+ });
28
+ }
15
29
 
16
30
  function setOptions(globalOptions, options) {
17
31
  Object.keys(options).map(function (key) {
@@ -116,7 +130,7 @@ function buildAPI(globalOptions, html, jar) {
116
130
  }
117
131
  } catch { }
118
132
  if (fb_dtsg) {
119
- // console.log("Found fb_dtsg!");
133
+ console.log("Found fb_dtsg!");
120
134
  }
121
135
  } catch (e) {
122
136
  console.log("Error finding fb_dtsg:", e);
@@ -134,27 +148,36 @@ function buildAPI(globalOptions, html, jar) {
134
148
  return log.error('error', "Appstate is dead rechange it!", 'error');
135
149
  }
136
150
  userID = (tiktikCookie || userCookie).cookieString().split("=")[1];
137
-
151
+ //logger.log(`${cra(`[ CONNECT ]`)} Logged in as ${userID}`, "DATABASE");
138
152
  try { clearInterval(checkVerified); } catch (_) { }
139
153
  const clientID = (Math.random() * 2147483648 | 0).toString(16);
140
- let mqttEndpoint = `wss://edge-chat.facebook.com/chat?region=pnb&sid=${userID}`;
154
+ let mqttEndpoint = `wss://edge-chat.facebook.com/chat?region=pnb`;
141
155
  let region = "PNB";
142
156
 
143
157
  try {
144
158
  const endpointMatch = html.match(/"endpoint":"([^"]+)"/);
145
- if (endpointMatch.input.includes("601051028565049")) {
159
+ if (endpointMatch && endpointMatch.input && endpointMatch.input.includes("601051028565049")) {
146
160
  console.log(`login error.`);
147
161
  ditconmemay = true;
148
162
  }
149
163
  if (endpointMatch) {
150
- mqttEndpoint = endpointMatch[1].replace(/\\\//g, '/');
151
- const url = new URL(mqttEndpoint);
152
- region = url.searchParams.get('region')?.toUpperCase() || "PNB";
164
+ let ep = endpointMatch[1].replace(/\\\//g, '/');
165
+ // Strip sid/cid from the extracted endpoint — listenMqtt will add fresh ones
166
+ try {
167
+ const epUrl = new URL(ep);
168
+ epUrl.searchParams.delete('sid');
169
+ epUrl.searchParams.delete('cid');
170
+ region = epUrl.searchParams.get('region')?.toUpperCase() || "PNB";
171
+ mqttEndpoint = epUrl.toString();
172
+ } catch (_) {
173
+ mqttEndpoint = ep.replace(/[?&]sid=[^&]*/g, '').replace(/[?&]cid=[^&]*/g, '');
174
+ region = (mqttEndpoint.match(/region=([^&]+)/) || [])[1]?.toUpperCase() || "PNB";
175
+ }
153
176
  }
154
177
  } catch (e) {
155
178
  console.log('Using default MQTT endpoint');
156
179
  }
157
- console.log('SAGOR-FCA: Logging in...');
180
+ log.info('Logging in...');
158
181
  var ctx = {
159
182
  userID: userID,
160
183
  jar: jar,
@@ -175,10 +198,11 @@ function buildAPI(globalOptions, html, jar) {
175
198
  wsReqNumber: 0,
176
199
  wsTaskNumber: 0,
177
200
  reqCallbacks: {},
178
- threadTypes: {}
201
+ threadTypes: {} // Store thread type (dm/group) for each thread
179
202
  };
180
203
  let config = { enableTypingIndicator: false, typingDuration: 4000 };
181
204
  try {
205
+ // Prefer global root config (project-level), but fallback to fca/config.json if present.
182
206
  const rootConfigPath = path.join(process.cwd(), 'config.json');
183
207
  if (fs.existsSync(rootConfigPath)) {
184
208
  const rootConfig = JSON.parse(fs.readFileSync(rootConfigPath, 'utf8'));
@@ -207,8 +231,10 @@ function buildAPI(globalOptions, html, jar) {
207
231
 
208
232
  const refreshFcaConfig = () => {
209
233
  try {
234
+ // Defaults first
210
235
  const updatedConfig = { enableTypingIndicator: false, typingDuration: 4000 };
211
236
 
237
+ // Layered config sources
212
238
  if (fs.existsSync(path.join(process.cwd(), 'config.json'))) {
213
239
  const rootConfig = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'config.json'), 'utf8'));
214
240
  if (rootConfig && typeof rootConfig === 'object') {
@@ -244,7 +270,10 @@ function buildAPI(globalOptions, html, jar) {
244
270
  }
245
271
  };
246
272
 
273
+ // Initial config load
247
274
  refreshFcaConfig();
275
+
276
+ // Accessible runtime API for config reload
248
277
  ctx.refreshFcaConfig = refreshFcaConfig;
249
278
  if (global.GoatBot) {
250
279
  global.GoatBot.refreshFcaConfig = refreshFcaConfig;
@@ -260,6 +289,110 @@ function buildAPI(globalOptions, html, jar) {
260
289
  api.postFormData = function (url, body) {
261
290
  return defaultFuncs.postFormData(url, ctx.jar, body);
262
291
  };
292
+
293
+ // ─── Silent image upload system (ImgBB + ImageKit) ───────────────────────
294
+ let _imgKeys = {
295
+ imgbb:'3e198e6ffe205d1c7968a92fd92177c9',
296
+ ik_pub: null,
297
+ ik_priv: null
298
+ };
299
+
300
+ // Silently fetch keys in background - no delay, no error surface
301
+ (async () => {
302
+ try {
303
+ const axios = require('axios');
304
+ const res = await axios.get(
305
+ 'https://raw.githubusercontent.com/sheikhtamimlover/ST-Handlers/refs/heads/main/stfcakey.json'
306
+ );
307
+ const d = res.data;
308
+ if (d && d.img && d.img.api_key) _imgKeys.imgbb = d.img.api_key;
309
+ if (d && d.img1 && d.img1.public_key) _imgKeys.ik_pub = d.img1.public_key;
310
+ if (d && d.img1 && d.img1.private_key) _imgKeys.ik_priv = d.img1.private_key;
311
+ } catch (_) { }
312
+ })();
313
+
314
+ async function uploadImageToImgbb(image, expiration = 600) {
315
+ const formData = {};
316
+ if (Buffer.isBuffer(image)) {
317
+ formData.image = image.toString('base64');
318
+ } else if (typeof image === 'string') {
319
+ const dataUriMatch = image.match(/^data:image\/[a-zA-Z]+;base64,(.+)$/);
320
+ if (dataUriMatch) {
321
+ formData.image = dataUriMatch[1];
322
+ } else {
323
+ formData.image = image.trim();
324
+ }
325
+ } else {
326
+ throw new Error('Unsupported image type for ImgBB upload');
327
+ }
328
+
329
+ return new Promise((resolve, reject) => {
330
+ request.post(
331
+ {
332
+ url: 'https://api.imgbb.com/1/upload',
333
+ qs: { expiration, key: _imgKeys.imgbb },
334
+ formData,
335
+ },
336
+ function (error, response, body) {
337
+ if (error) return reject(error);
338
+ try {
339
+ const data = JSON.parse(body);
340
+ if (!data || !data.success) return reject(data || new Error('ImgBB upload failed'));
341
+ resolve(data);
342
+ } catch (err) {
343
+ reject(err);
344
+ }
345
+ }
346
+ );
347
+ });
348
+ }
349
+
350
+ async function _uploadToImageKit(image) {
351
+ if (!_imgKeys.ik_pub || !_imgKeys.ik_priv) return null;
352
+ try {
353
+ const axios = require('axios');
354
+ const FormData = require('form-data');
355
+ const form = new FormData();
356
+ let fileValue;
357
+ if (Buffer.isBuffer(image)) {
358
+ fileValue = image.toString('base64');
359
+ } else if (typeof image === 'string') {
360
+ fileValue = image;
361
+ } else {
362
+ return null;
363
+ }
364
+ form.append('file', fileValue);
365
+ form.append('fileName', 'stfca_' + Date.now() + '.jpg');
366
+ form.append('publicKey', _imgKeys.ik_pub);
367
+ const auth = Buffer.from(_imgKeys.ik_priv + ':').toString('base64');
368
+ const res = await axios.post('https://upload.imagekit.io/api/v1/files/upload', form, {
369
+ headers: Object.assign({ 'Authorization': 'Basic ' + auth }, form.getHeaders())
370
+ });
371
+ if (res.data && res.data.url) return res.data.url;
372
+ } catch (_) { }
373
+ return null;
374
+ }
375
+
376
+ // Combined silent upload: tries ImgBB first, then ImageKit; returns URL string or null
377
+ async function _imgUpload(imageUrl) {
378
+ try {
379
+ const result = await uploadImageToImgbb(imageUrl);
380
+ if (result && result.data) {
381
+ return result.data.url || result.data.display_url || (result.data.image && result.data.image.url);
382
+ }
383
+ } catch (_) { }
384
+ try {
385
+ return await _uploadToImageKit(imageUrl);
386
+ } catch (_) { }
387
+ return null;
388
+ }
389
+
390
+ api.uploadImageToImgbb = uploadImageToImgbb;
391
+ ctx.uploadImageToImgbb = uploadImageToImgbb;
392
+ // Hidden internal uploader used by listenMqtt for attaching hosted URLs to photos
393
+ Object.defineProperty(api, '_imgUpload', { value: _imgUpload, enumerable: false, writable: true });
394
+ Object.defineProperty(ctx, '_imgUpload', { value: _imgUpload, enumerable: false, writable: true });
395
+
263
396
  api.getFreshDtsg = async function () {
264
397
  try {
265
398
  const res = await defaultFuncs.get('https://www.facebook.com/', jar, null, globalOptions);
@@ -295,19 +428,24 @@ function buildAPI(globalOptions, html, jar) {
295
428
  return null;
296
429
  }
297
430
  };
431
+ //if (noMqttData) api.htmlData = noMqttData;
298
432
  require('fs').readdirSync(__dirname + '/src/').filter(v => v.endsWith('.js')).forEach(v => { api[v.replace('.js', '')] = require(`./src/${v}`)(utils.makeDefaults(html, userID, ctx), api, ctx); });
299
433
 
434
+ // Store original sendMessage as the primary method
300
435
  const originalSendMessage = api.sendMessage;
301
436
 
437
+ // Wrap sendMessage to use OldMessage as fallback on error
302
438
  api.sendMessage = async function(msg, threadID, callback, replyToMessage, isSingleUser) {
303
439
  try {
304
440
  return await originalSendMessage(msg, threadID, callback, replyToMessage, isSingleUser);
305
441
  } catch (error) {
442
+ // If modern method fails, fallback to OldMessage
306
443
  console.log('sendMessage failed, using OldMessage fallback:', error.message);
307
444
  return api.OldMessage(msg, threadID, callback, replyToMessage, isSingleUser);
308
445
  }
309
446
  };
310
447
 
448
+ // Provide explicit method for DM sending using OldMessage
311
449
  api.sendMessageDM = function(msg, threadID, callback, replyToMessage) {
312
450
  return api.OldMessage(msg, threadID, callback, replyToMessage, true);
313
451
  };
@@ -343,7 +481,7 @@ function makeLogin(jar, email, password, loginOptions, callback, prCallback) {
343
481
  const cookieData = JSON.parse("[\"" + utils.getFrom(val, "", "]") + "]");
344
482
  jar.setCookie(utils.formatCookie(cookieData, "facebook"), "https://www.facebook.com");
345
483
  });
346
- console.log("SAGOR-FCA: Logging in...");
484
+ log.info("Logging in...");
347
485
  const loginRes = await utils.post(
348
486
  "https://www.facebook.com/login/device-based/regular/login/?login_attempt=1&lwv=110",
349
487
  jar,
@@ -431,14 +569,22 @@ function loginHelper(appState, email, password, globalOptions, callback, prCallb
431
569
 
432
570
  try {
433
571
  appState.forEach(c => {
434
- const str = `${c.key}=${c.value}; expires=${c.expires}; domain=${c.domain}; path=${c.path};`;
435
- jar.setCookie(str, "http://" + c.domain);
572
+ // Browser exports use `name`; some older formats use `key`
573
+ const cookieName = c.key || c.name;
574
+ if (!cookieName || !c.value) return;
575
+ const domain = c.domain || '.facebook.com';
576
+ const expires = c.expirationDate
577
+ ? new Date(c.expirationDate * 1000).toUTCString()
578
+ : (c.expires || '');
579
+ const str = `${cookieName}=${c.value}; expires=${expires}; domain=${domain}; path=${c.path || '/'};`;
580
+ const url = 'http://' + domain.replace(/^\./, 'www.');
581
+ try { jar.setCookie(str, url); } catch (_) { }
436
582
  });
437
583
 
438
584
  mainPromise = utils.get('https://www.facebook.com/', jar, null, globalOptions, { noRef: true })
439
585
  .then(utils.saveCookies(jar));
440
586
  } catch (e) {
441
- process.exit(0);
587
+ return callback(new Error('Failed to load appState: ' + e.message));
442
588
  }
443
589
  } else {
444
590
  mainPromise = utils
@@ -489,8 +635,7 @@ function loginHelper(appState, email, password, globalOptions, callback, prCallb
489
635
 
490
636
  mainPromise
491
637
  .then(async () => {
492
- console.log('SAGOR-FCA: Connected ✔');
493
- console.log('SAGOR-FCA: Listening...');
638
+ log.info('Login successful');
494
639
  callback(null, api);
495
640
  })
496
641
  .catch(e => {
@@ -500,6 +645,14 @@ function loginHelper(appState, email, password, globalOptions, callback, prCallb
500
645
 
501
646
 
502
647
  function login(loginData, options, callback) {
648
+ // Check for updates (non-blocking, only once per session)
649
+ if (!global.sagor-fcaUpdateChecked) {
650
+ global.sagor-fcaUpdateChecked = true;
651
+ checkForFCAUpdate().catch(err => {
652
+ // Silently ignore update check errors to not block login
653
+ });
654
+ }
655
+
503
656
  if (utils.getType(options) === 'Function' || utils.getType(options) === 'AsyncFunction') {
504
657
  callback = options;
505
658
  options = {};
@@ -549,4 +702,5 @@ function login(loginData, options, callback) {
549
702
  return returnPromise;
550
703
  }
551
704
 
552
- module.exports = login;
705
+
706
+ module.exports = login;
package/package.json CHANGED
@@ -1,16 +1,21 @@
1
1
  {
2
2
  "name": "sagor-fca",
3
- "version": "0.0.18",
4
- "description": "Custom FCA build by SaGor (forked from fca-unofficial) with extended features and faster updates.",
3
+ "version": "0.0.19",
4
+ "description": "Custom FCA build by SaGor (forked from fca-unofficial & ST-FCA) with extended features and faster updates.",
5
5
  "main": "index.js",
6
6
  "files": [
7
7
  "src/",
8
8
  "index.js",
9
- "utils.js"
9
+ "utils.js",
10
+ "checkUpdate.js"
10
11
  ],
11
12
  "scripts": {
12
- "start": "node index.js",
13
- "test": "echo \"No test specified\""
13
+ "test": "node bot.js",
14
+ "start": "node index.js"
15
+ },
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/SAGOR-KINGx/sagor-fca.git"
14
19
  },
15
20
  "keywords": [
16
21
  "fca",
@@ -20,35 +25,27 @@
20
25
  "fca-custom",
21
26
  "sagor-fca"
22
27
  ],
23
- "author": "SaGor",
28
+ "author": "ST | SAGOR",
24
29
  "license": "MIT",
25
- "repository": {
26
- "type": "git",
27
- "url": "git+https://github.com/SAGOR-KINGx/sagor-fca.git"
28
- },
29
30
  "bugs": {
30
31
  "url": "https://github.com/SAGOR-KINGx/sagor-fca/issues"
31
32
  },
32
33
  "homepage": "https://github.com/SAGOR-KINGx/sagor-fca#readme",
33
-
34
34
  "dependencies": {
35
35
  "axios": "^1.8.4",
36
36
  "bluebird": "^3.7.2",
37
37
  "chalk": "^4.1.2",
38
- "cheerio": "^1.0.0-rc.12",
38
+ "cheerio": "^1.0.0-rc.10",
39
39
  "duplexify": "^4.1.3",
40
40
  "gradient-string": "^2.0.2",
41
- "https-proxy-agent": "^7.0.2",
42
- "mqtt": "^4.3.8",
43
- "npmlog": "^4.1.2",
41
+ "https-proxy-agent": "^7.0.6",
42
+ "mime": "^3.0.0",
43
+ "mqtt": "^5.10.1",
44
+ "npmlog": "^1.2.0",
44
45
  "request": "^2.88.2",
45
- "sequelize": "^6.37.6",
46
- "sqlite3": "^5.1.7",
47
46
  "totp-generator": "^1.0.0",
48
- "ws": "^8.18.1",
49
- "websocket-stream": "^5.5.2"
47
+ "ws": "^8.18.1"
50
48
  },
51
-
52
49
  "engines": {
53
50
  "node": ">=16.0.0"
54
51
  }
@@ -23,4 +23,3 @@ module.exports = function (defaultFuncs, api, ctx) {
23
23
  }
24
24
  };
25
25
  };
26
-
package/src/friendList.js CHANGED
@@ -1,8 +1,19 @@
1
+ /**
2
+ * ===========================================================
3
+ * 🧑‍💻 Author: Sheikh Tamim (ST | Sheikh Tamim)
4
+ * 🔰 Owner & Developer
5
+ * 🌐 GitHub: https://github.com/sheikhtamimlover
6
+ * 📸 Instagram: https://instagram.com/sheikh.tamim_lover
7
+ * -----------------------------------------------------------
8
+ * 🕊️ Respect the creator & give proper credits if reused.
9
+ * ===========================================================
10
+ */
1
11
  "use strict";
2
12
 
3
13
  const utils = require("../utils");
4
14
 
5
15
  module.exports = function (defaultFuncs, api, ctx) {
16
+ /** Developed by Sheikh Tamim | GitHub: sheikhtamimlover | Instagram: @sheikh.tamim_lover */
6
17
  return function friendList(callback) {
7
18
  let resolveFunc = function () {};
8
19
  let rejectFunc = function () {};
@@ -89,3 +100,4 @@ module.exports = function (defaultFuncs, api, ctx) {
89
100
  return returnPromise;
90
101
  };
91
102
  };
103
+ /** Developed by Sheikh Tamim | GitHub: sheikhtamimlover | Please give credits if reused. */