@lazyneoaz/metachat 1.0.8 → 1.0.10

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
@@ -1,2 +1,56 @@
1
1
  "use strict";
2
- module.exports = require('./src/engine/client');
2
+
3
+ const { login, loginAsync, loginLegacy, DEFAULT_OPTIONS } = require('./src/engine/client');
4
+
5
+ const { MessengerBot, MessengerContext, createMessengerBot } = require('./src/app/MessengerBot');
6
+ const { createFcaClient, attachClientFacade, createMessagesDomain, createThreadsDomain, createUsersDomain, createAccountDomain, createRealtimeDomain, createHttpDomain, createSchedulerDomain } = require('./src/app/createFcaClient');
7
+ const { defaultConfig, loadConfig, resolveConfig, writeConfigTemplate } = require('./src/app/config');
8
+ const { broadcast } = require('./src/app/broadcast');
9
+ const { attachThreadInfoRealtimeSync } = require('./src/app/threadSync');
10
+ const { checkForPackageUpdate, runConfiguredUpdateCheck } = require('./src/app/updateCheck');
11
+ const { createDefaultContext, createFcaState, createApiFacade, createRequestHelper } = require('./src/app/state');
12
+
13
+ const { normalizeCookieHeaderString, setJarFromPairs } = require('./src/utils/formatters/value/formatCookie');
14
+ const { createAuthCore } = require('./src/utils/auth-helpers');
15
+
16
+ module.exports = login;
17
+
18
+ module.exports.login = login;
19
+ module.exports.loginAsync = loginAsync;
20
+ module.exports.loginLegacy = loginLegacy;
21
+ module.exports.DEFAULT_OPTIONS = DEFAULT_OPTIONS;
22
+
23
+ module.exports.MessengerBot = MessengerBot;
24
+ module.exports.MessengerContext = MessengerContext;
25
+ module.exports.createMessengerBot = createMessengerBot;
26
+
27
+ module.exports.createFcaClient = createFcaClient;
28
+ module.exports.attachClientFacade = attachClientFacade;
29
+
30
+ module.exports.createMessagesDomain = createMessagesDomain;
31
+ module.exports.createThreadsDomain = createThreadsDomain;
32
+ module.exports.createUsersDomain = createUsersDomain;
33
+ module.exports.createAccountDomain = createAccountDomain;
34
+ module.exports.createRealtimeDomain = createRealtimeDomain;
35
+ module.exports.createHttpDomain = createHttpDomain;
36
+ module.exports.createSchedulerDomain = createSchedulerDomain;
37
+
38
+ module.exports.defaultConfig = defaultConfig;
39
+ module.exports.loadConfig = loadConfig;
40
+ module.exports.resolveConfig = resolveConfig;
41
+ module.exports.writeConfigTemplate = writeConfigTemplate;
42
+
43
+ module.exports.broadcast = broadcast;
44
+ module.exports.attachThreadInfoRealtimeSync = attachThreadInfoRealtimeSync;
45
+
46
+ module.exports.checkForPackageUpdate = checkForPackageUpdate;
47
+ module.exports.runConfiguredUpdateCheck = runConfiguredUpdateCheck;
48
+
49
+ module.exports.createDefaultContext = createDefaultContext;
50
+ module.exports.createFcaState = createFcaState;
51
+ module.exports.createApiFacade = createApiFacade;
52
+ module.exports.createRequestHelper = createRequestHelper;
53
+
54
+ module.exports.normalizeCookieHeaderString = normalizeCookieHeaderString;
55
+ module.exports.setJarFromPairs = setJarFromPairs;
56
+ module.exports.createAuthCore = createAuthCore;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lazyneoaz/metachat",
3
- "version": "1.0.8",
3
+ "version": "1.0.10",
4
4
  "type": "commonjs",
5
5
  "types": "src/types/index.d.ts",
6
6
  "description": "Advanced Facebook Chat API client for building Messenger bots — real-time messaging, thread management, MQTT, and session stability.",
@@ -2,10 +2,6 @@
2
2
 
3
3
  const utils = require('../utils');
4
4
 
5
- function generateOfflineThreadingID() {
6
- return Date.now().toString() + Math.floor(Math.random() * 1000000).toString();
7
- }
8
-
9
5
  function getType(obj) {
10
6
  return Object.prototype.toString.call(obj).slice(8, -1);
11
7
  }
@@ -39,7 +35,7 @@ module.exports = function (defaultFuncs, api, ctx) {
39
35
  if (ctx.mqttClient) {
40
36
  const tasks = [];
41
37
  const isAdmin = adminStatus ? 1 : 0;
42
- const epochID = generateOfflineThreadingID();
38
+ const epochID = utils.generateOfflineThreadingID();
43
39
 
44
40
  if (getType(adminID) === "Array") {
45
41
  adminID.forEach((id, index) => {
@@ -93,7 +89,7 @@ module.exports = function (defaultFuncs, api, ctx) {
93
89
  } else {
94
90
  utils.warn("MQTT client not available, using HTTP fallback for changeAdminStatus");
95
91
  const tasks = [];
96
- const epochID = generateOfflineThreadingID();
92
+ const epochID = utils.generateOfflineThreadingID();
97
93
 
98
94
  if (getType(adminID) === "Array") {
99
95
  adminID.forEach((id, index) => {
@@ -61,7 +61,7 @@ module.exports = (defaultFuncs, api, ctx) => {
61
61
  type: 3
62
62
  });
63
63
 
64
- ctx.mqttClient.publish("/ls_req", form);
64
+ ctx.mqttClient.publish("/ls_req", form, { qos: 1, retain: false });
65
65
  callback(null, { success: true });
66
66
  } catch (err) {
67
67
  utils.error("createPoll", err);
@@ -43,24 +43,30 @@ module.exports = function (defaultFuncs, api, ctx) {
43
43
 
44
44
 
45
45
  if (!validActions.includes(action)) {
46
- return _callback(null, { type: "error_gc", error: `Invalid action. Must be one of: ${validActions.join(", ")}` });
46
+ _callback(null, { type: "error_gc", error: `Invalid action. Must be one of: ${validActions.join(", ")}` });
47
+ return returnPromise;
47
48
  }
48
49
  if (!userIDs || userIDs.length === 0) {
49
- return _callback(null, { type: "error_gc", error: "userIDs is required." });
50
+ _callback(null, { type: "error_gc", error: "userIDs is required." });
51
+ return returnPromise;
50
52
  }
51
53
  if (!threadID) {
52
- return _callback(null, { type: "error_gc", error: "threadID is required." });
54
+ _callback(null, { type: "error_gc", error: "threadID is required." });
55
+ return returnPromise;
53
56
  }
54
57
  if (!ctx.mqttClient) {
55
- return _callback(null, { type: "error_gc", error: "Not connected to MQTT" });
58
+ _callback(null, { type: "error_gc", error: "Not connected to MQTT" });
59
+ return returnPromise;
56
60
  }
57
61
 
58
62
  const threadInfo = await api.getThreadInfo(threadID);
59
63
  if (!threadInfo) {
60
- return _callback(null, { type: "error_gc", error: "Could not retrieve thread information." });
64
+ _callback(null, { type: "error_gc", error: "Could not retrieve thread information." });
65
+ return returnPromise;
61
66
  }
62
67
  if (threadInfo.isGroup === false) {
63
- return _callback(null, { type: "error_gc", error: "This feature is only for group chats, not private messages." });
68
+ _callback(null, { type: "error_gc", error: "This feature is only for group chats, not private messages." });
69
+ return returnPromise;
64
70
  }
65
71
 
66
72
  const currentMembers = threadInfo.participantIDs;
@@ -74,7 +80,8 @@ module.exports = function (defaultFuncs, api, ctx) {
74
80
  if (action === 'add') {
75
81
  const usersToAdd = usersToModify.filter(id => !currentMembers.includes(id));
76
82
  if (usersToAdd.length === 0) {
77
- return _callback(null, { type: "error_gc", error: "All specified users are already in the group." });
83
+ _callback(null, { type: "error_gc", error: "All specified users are already in the group." });
84
+ return returnPromise;
78
85
  }
79
86
  finalUsers = usersToAdd;
80
87
  queryPayload = { thread_key: parseInt(threadID), contact_ids: finalUsers.map(id => parseInt(id)), sync_group: 1 };
@@ -83,7 +90,8 @@ module.exports = function (defaultFuncs, api, ctx) {
83
90
  } else { // action is 'remove'
84
91
  const userToRemove = usersToModify[0];
85
92
  if (!currentMembers.includes(userToRemove)) {
86
- return _callback(null, { type: "error_gc", error: `User with ID ${userToRemove} is not in this group chat.` });
93
+ _callback(null, { type: "error_gc", error: `User with ID ${userToRemove} is not in this group chat.` });
94
+ return returnPromise;
87
95
  }
88
96
  finalUsers = [userToRemove];
89
97
  queryPayload = { thread_id: threadID, contact_id: userToRemove, sync_group: 1 };
@@ -113,8 +121,7 @@ module.exports = function (defaultFuncs, api, ctx) {
113
121
  return _callback(null, gcmemberInfo);
114
122
  });
115
123
  } catch (err) {
116
-
117
- return _callback(null, { type: "error_gc", error: err.message || "An unknown error occurred." });
124
+ _callback(null, { type: "error_gc", error: err.message || "An unknown error occurred." });
118
125
  }
119
126
 
120
127
  return returnPromise;
@@ -41,18 +41,21 @@ module.exports = function (defaultFuncs, api, ctx) {
41
41
  action = action ? action.toLowerCase() : "";
42
42
 
43
43
  if (!validActions.includes(action)) {
44
- return _callback(null, { type: "error_gc_rule", error: `Invalid action. Must be one of: ${validActions.join(", ")}` });
44
+ _callback(null, { type: "error_gc_rule", error: `Invalid action. Must be one of: ${validActions.join(", ")}` });
45
+ return returnPromise;
45
46
  }
46
- if (!userID) return _callback(null, { type: "error_gc_rule", error: "userID is required." });
47
- if (!threadID) return _callback(null, { type: "error_gc_rule", error: "threadID is required." });
48
- if (!ctx.mqttClient) return _callback(null, { type: "error_gc_rule", error: "Not connected to MQTT" });
47
+ if (!userID) { _callback(null, { type: "error_gc_rule", error: "userID is required." }); return returnPromise; }
48
+ if (!threadID) { _callback(null, { type: "error_gc_rule", error: "threadID is required." }); return returnPromise; }
49
+ if (!ctx.mqttClient) { _callback(null, { type: "error_gc_rule", error: "Not connected to MQTT" }); return returnPromise; }
49
50
 
50
51
  const threadInfo = await api.getThreadInfo(threadID);
51
52
  if (!threadInfo) {
52
- return _callback(null, { type: "error_gc_rule", error: "Could not retrieve thread information." });
53
+ _callback(null, { type: "error_gc_rule", error: "Could not retrieve thread information." });
54
+ return returnPromise;
53
55
  }
54
56
  if (threadInfo.isGroup === false) {
55
- return _callback(null, { type: "error_gc_rule", error: "This feature is only for group chats." });
57
+ _callback(null, { type: "error_gc_rule", error: "This feature is only for group chats." });
58
+ return returnPromise;
56
59
  }
57
60
 
58
61
  const adminIDs = threadInfo.adminIDs || [];
@@ -60,11 +63,13 @@ module.exports = function (defaultFuncs, api, ctx) {
60
63
 
61
64
  if (action === 'admin') {
62
65
  if (isCurrentlyAdmin) {
63
- return _callback(null, { type: "error_gc_rule", error: `User is already an admin.` });
66
+ _callback(null, { type: "error_gc_rule", error: `User is already an admin.` });
67
+ return returnPromise;
64
68
  }
65
69
  } else { // action is 'unadmin'
66
70
  if (!isCurrentlyAdmin) {
67
- return _callback(null, { type: "error_gc_rule", error: `User is not an admin.` });
71
+ _callback(null, { type: "error_gc_rule", error: `User is not an admin.` });
72
+ return returnPromise;
68
73
  }
69
74
  }
70
75
 
@@ -111,7 +116,7 @@ module.exports = function (defaultFuncs, api, ctx) {
111
116
  });
112
117
 
113
118
  } catch (err) {
114
- return _callback(null, { type: "error_gc_rule", error: err.message || "An unknown error occurred." });
119
+ _callback(null, { type: "error_gc_rule", error: err.message || "An unknown error occurred." });
115
120
  }
116
121
 
117
122
  return returnPromise;
@@ -52,7 +52,9 @@ module.exports = function (defaultFuncs, api, ctx) {
52
52
  Origin: url
53
53
  })
54
54
  .then(function (res) {
55
- var { payload } = JSON.parse(res.body.split(';').pop() || '{}');
55
+ var parsed;
56
+ try { parsed = JSON.parse(res.body.split(';').pop() || '{}'); } catch(e) { parsed = {}; }
57
+ var { payload } = parsed;
56
58
  if (payload && !payload.codeConfirmed)
57
59
  throw {
58
60
  error: 'submitCode',
@@ -23,7 +23,7 @@ module.exports = (defaultFuncs, api, ctx) => {
23
23
  customUserAgent: utils.windowsUserAgent
24
24
  }, (err, data) => {
25
25
 
26
- if (err) throw err;
26
+ if (err) return callback(err);
27
27
  const profileMatch = data.match(/"CurrentUserInitialData",\[\],\{(.*?)\},(.*?)\]/);
28
28
  if (profileMatch && profileMatch[1]){
29
29
  const accountJson = JSON.parse(`{${profileMatch[1]}}`);
@@ -4,26 +4,72 @@ const utils = require('../utils');
4
4
  const { _formatAttachment } = require('../utils/formatters/data/formatAttachment');
5
5
 
6
6
  const THEME_COLORS = [
7
- { theme_color: "FF000000", theme_id: "788274591712841", theme_emoji: "🖤", gradient: '["FFF0F0F0"]', should_show_icon: "", theme_name_with_subtitle: "Monochrome" },
8
- { theme_color: "FFFF5CA1", theme_id: "169463077092846", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Hot Pink" },
9
- { theme_color: "FF2825B5", theme_id: "271607034185782", theme_emoji: null, gradient: '["FF5E007E","FF331290","FF2825B5"]', should_show_icon: "1", theme_name_with_subtitle: "Shadow" },
10
- { theme_color: "FFD9A900", theme_id: "2533652183614000", theme_emoji: null, gradient: '["FF550029","FFAA3232","FFD9A900"]', should_show_icon: "1", theme_name_with_subtitle: "Maple" },
11
- { theme_color: "FFFB45DE", theme_id: "2873642949430623", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Tulip" },
12
- { theme_color: "FF5E007E", theme_id: "193497045377796", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Grape" },
13
- { theme_color: "FF7AA286", theme_id: "1455149831518874", theme_emoji: "🌑", gradient: '["FF25C0E1","FFCE832A"]', should_show_icon: "", theme_name_with_subtitle: "Dune" },
14
- { theme_color: "FFFAAF00", theme_id: "672058580051520", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Honey" },
15
- { theme_color: "FF0084FF", theme_id: "196241301102133", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Default Blue" },
16
- { theme_color: "FFFFC300", theme_id: "174636906462322", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Yellow" },
17
- { theme_color: "FF44BEC7", theme_id: "1928399724138152", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Teal Blue" },
18
- { theme_color: "FF7646FF", theme_id: "234137870477637", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Bright Purple" },
19
- { theme_color: "FFF25C54", theme_id: "3022526817824329", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Peach" },
20
- { theme_color: "FFF01D6A", theme_id: "724096885023603", theme_emoji: null, gradient: '["FF005FFF","FF9200FF","FFFF2E19"]', should_show_icon: "1", theme_name_with_subtitle: "Berry" },
21
- { theme_color: "FFFF7CA8", theme_id: "624266884847972", theme_emoji: null, gradient: '["FFFF8FB2","FFA797FF","FF00E5FF"]', should_show_icon: "1", theme_name_with_subtitle: "Candy" },
22
- { theme_color: "FF0084FF", theme_id: "196241301102133", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Classic" },
23
- { theme_color: "FF0099FF", theme_id: "3273938616164733", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Classic" },
24
- { theme_color: "FFFA3C4C", theme_id: "2129984390566328", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Red" },
25
- { theme_color: "FF13CF13", theme_id: "2136751179887052", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Green" },
26
- { theme_color: "FFFF7E29", theme_id: "175615189761153", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Orange" }
7
+ // ── Core solid colours ────────────────────────────────────────────────────
8
+ { theme_color: "FF0084FF", theme_id: "196241301102133", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Default Blue" },
9
+ { theme_color: "FF0099FF", theme_id: "3273938616164733", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Classic" },
10
+ { theme_color: "FF44BEC7", theme_id: "1928399724138152", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Teal Blue" },
11
+ { theme_color: "FFFFC300", theme_id: "174636906462322", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Yellow" },
12
+ { theme_color: "FFFA3C4C", theme_id: "2129984390566328", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Red" },
13
+ { theme_color: "FF7646FF", theme_id: "234137870477637", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Bright Purple" },
14
+ { theme_color: "FF13CF13", theme_id: "2136751179887052", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Green" },
15
+ { theme_color: "FFFF7E29", theme_id: "175615189761153", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Orange" },
16
+ { theme_color: "FFFF5CA1", theme_id: "169463077092846", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Hot Pink" },
17
+ { theme_color: "FF25D366", theme_id: "2442142322678320", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Deep Sky Blue" },
18
+ { theme_color: "FF7646FF", theme_id: "2058653964378557", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Lavender Purple" },
19
+ { theme_color: "FFFF4500", theme_id: "980963458735625", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Light Coral" },
20
+ { theme_color: "FF00C9C9", theme_id: "417639218648241", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Aqua" },
21
+ // ── Named / gradient themes ───────────────────────────────────────────────
22
+ { theme_color: "FF000000", theme_id: "788274591712841", theme_emoji: "🖤", gradient: '["FFF0F0F0"]', should_show_icon: "", theme_name_with_subtitle: "Monochrome" },
23
+ { theme_color: "FF2825B5", theme_id: "271607034185782", theme_emoji: null, gradient: '["FF5E007E","FF331290","FF2825B5"]', should_show_icon: "1", theme_name_with_subtitle: "Shadow" },
24
+ { theme_color: "FFD9A900", theme_id: "2533652183614000", theme_emoji: null, gradient: '["FF550029","FFAA3232","FFD9A900"]', should_show_icon: "1", theme_name_with_subtitle: "Maple" },
25
+ { theme_color: "FFFB45DE", theme_id: "2873642949430623", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Tulip" },
26
+ { theme_color: "FF5E007E", theme_id: "193497045377796", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Grape" },
27
+ { theme_color: "FF7AA286", theme_id: "1455149831518874", theme_emoji: "🌑", gradient: '["FF25C0E1","FFCE832A"]', should_show_icon: "", theme_name_with_subtitle: "Dune" },
28
+ { theme_color: "FFFAAF00", theme_id: "672058580051520", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Honey" },
29
+ { theme_color: "FFF25C54", theme_id: "3022526817824329", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Peach" },
30
+ { theme_color: "FFF01D6A", theme_id: "724096885023603", theme_emoji: null, gradient: '["FF005FFF","FF9200FF","FFFF2E19"]', should_show_icon: "1", theme_name_with_subtitle: "Berry" },
31
+ { theme_color: "FFFF7CA8", theme_id: "624266884847972", theme_emoji: null, gradient: '["FFFF8FB2","FFA797FF","FF00E5FF"]', should_show_icon: "1", theme_name_with_subtitle: "Candy" },
32
+ { theme_color: "FF930099", theme_id: "930060997172551", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Mango" },
33
+ { theme_color: "FF4267B2", theme_id: "164535220883264", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Berry" },
34
+ { theme_color: "FF00C400", theme_id: "370940413392601", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Citrus" },
35
+ { theme_color: "FF50C878", theme_id: "557344741607350", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Citrus 2" },
36
+ { theme_color: "FFFF0000", theme_id: "205488546921017", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Candy" },
37
+ { theme_color: "FF8B4513", theme_id: "1833559466821043", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Earth" },
38
+ { theme_color: "FF0084FF", theme_id: "365557122117011", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Support" },
39
+ { theme_color: "FFFF6B6B", theme_id: "339021464972092", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Music" },
40
+ { theme_color: "FFFF69B4", theme_id: "1652456634878319", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Pride" },
41
+ { theme_color: "FF8B0000", theme_id: "538280997628317", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Doctor Strange" },
42
+ { theme_color: "FF6C63FF", theme_id: "1060619084701625", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Lo-Fi" },
43
+ { theme_color: "FF87CEEB", theme_id: "3190514984517598", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Sky" },
44
+ { theme_color: "FFFF4500", theme_id: "357833546030778", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Lunar New Year" },
45
+ { theme_color: "FFFF6347", theme_id: "627144732056021", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Celebration" },
46
+ { theme_color: "FF4682B4", theme_id: "390127158985345", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Chill" },
47
+ { theme_color: "FFFF0000", theme_id: "1059859811490132", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Stranger Things" },
48
+ { theme_color: "FFD4A574", theme_id: "275041734441112", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Care" },
49
+ { theme_color: "FF9B59B6", theme_id: "3082966625307060", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Astrology" },
50
+ { theme_color: "FFFF8C00", theme_id: "184305226956268", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "J Balvin" },
51
+ { theme_color: "FFFF69B4", theme_id: "621630955405500", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Birthday" },
52
+ { theme_color: "FF228B22", theme_id: "539927563794799", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Cottagecore" },
53
+ { theme_color: "FF006994", theme_id: "736591620215564", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Ocean" },
54
+ { theme_color: "FFFF1493", theme_id: "741311439775765", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Love" },
55
+ { theme_color: "FFFF7F7F", theme_id: "230032715012014", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Tie Dye" },
56
+ { theme_color: "FF808080", theme_id: "262191918210707", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Tropical" },
57
+ { theme_color: "FF228B22", theme_id: "909695489504566", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Sushi" },
58
+ { theme_color: "FFFF69B4", theme_id: "280333826736184", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Lollipop" },
59
+ { theme_color: "FFFF007F", theme_id: "1257453361255152", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Rose" },
60
+ { theme_color: "FFE6E6FA", theme_id: "571193503540759", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Lavender" },
61
+ { theme_color: "FFFFC0CB", theme_id: "3151463484918004", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Kiwi" },
62
+ { theme_color: "FF6F2DA8", theme_id: "810978360551741", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Parenthood" },
63
+ { theme_color: "FF4169E1", theme_id: "1438011086532622", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Star Wars" },
64
+ { theme_color: "FF6B8E23", theme_id: "101275642962533", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Guardians of the Galaxy" },
65
+ { theme_color: "FFFF69B4", theme_id: "158263147151440", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Bloom" },
66
+ { theme_color: "FF9B59B6", theme_id: "195296273246380", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Bubble Tea" },
67
+ { theme_color: "FFFF8C00", theme_id: "6026716157422736", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Basketball" },
68
+ { theme_color: "FF4B0082", theme_id: "737761000603635", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Non-Binary" },
69
+ { theme_color: "FF55CDFC", theme_id: "504518465021637", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Transgender" },
70
+ { theme_color: "FFFC0080", theme_id: "769129927636836", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Taylor Swift" },
71
+ { theme_color: "FFFF7700", theme_id: "822549609168155", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Autumn" },
72
+ { theme_color: "FFFF007F", theme_id: "693996545771691", theme_emoji: null, gradient: null, should_show_icon: "1", theme_name_with_subtitle: "Elephants and Flowers" },
27
73
  ];
28
74
 
29
75
  function formatMessage(threadID, data) {
@@ -63,20 +109,28 @@ function formatMessage(threadID, data) {
63
109
  const adminType = data.extensible_message_admin_text_type;
64
110
 
65
111
  if (adminType === "CHANGE_THREAD_THEME") {
66
- const themeColor = data.extensible_message_admin_text.theme_color;
67
- const colorMatch = THEME_COLORS.find(color => color.theme_color === themeColor);
68
-
112
+ const ext = data.extensible_message_admin_text || {};
113
+ const themeColor = ext.theme_color || null;
114
+ // Prefer the theme_id field returned directly by GraphQL.
115
+ // Fall back to a reverse lookup in THEME_COLORS for older API
116
+ // responses that omit theme_id.
117
+ const directThemeId = ext.theme_id || ext.theme_fbid || null;
118
+ const colorMatch = directThemeId
119
+ ? THEME_COLORS.find(c => c.theme_id === directThemeId)
120
+ : (themeColor ? THEME_COLORS.find(c => c.theme_color === themeColor) : null);
121
+
69
122
  return {
70
123
  ...baseMessage,
71
124
  type: "event",
72
125
  logMessageType: "log:thread-color",
73
- logMessageData: colorMatch || {
126
+ logMessageData: {
74
127
  theme_color: themeColor,
75
- theme_id: null,
76
- theme_emoji: null,
77
- gradient: null,
78
- should_show_icon: null,
79
- theme_name_with_subtitle: null
128
+ theme_id: directThemeId || (colorMatch ? colorMatch.theme_id : null),
129
+ theme_fbid: directThemeId || (colorMatch ? colorMatch.theme_id : null),
130
+ theme_emoji: ext.theme_emoji || (colorMatch ? colorMatch.theme_emoji : null),
131
+ gradient: ext.gradient || (colorMatch ? colorMatch.gradient : null),
132
+ should_show_icon: ext.should_show_icon != null ? ext.should_show_icon : (colorMatch ? colorMatch.should_show_icon : null),
133
+ theme_name_with_subtitle: ext.theme_name_with_subtitle || (colorMatch ? colorMatch.theme_name_with_subtitle : null)
80
134
  },
81
135
  logMessageBody: data.snippet
82
136
  };
@@ -84,15 +84,19 @@ module.exports = function (defaultFuncs, api, ctx) {
84
84
 
85
85
  const info = Array.isArray(threadInfo) ? threadInfo[0] : threadInfo;
86
86
 
87
+ // theme_id comes from threadTheme.id (extracted at getThreadInfo level)
88
+ const themeId = info.theme_id || (info.threadTheme && info.threadTheme.id) || info.themeID || null;
87
89
  const themeInfo = {
88
90
  threadID: identifier,
89
91
  threadName: info.threadName || info.name || '',
90
92
  color: info.color || null,
91
93
  emoji: info.emoji || '👍',
92
- theme_id: info.theme_id || info.themeID || null,
94
+ theme_id: themeId,
95
+ theme_fbid: themeId,
93
96
  theme_color: info.theme_color || info.color || null,
94
97
  gradient_colors: info.gradient_colors || null,
95
- is_default: !info.color && !info.theme_id
98
+ threadTheme: info.threadTheme || null,
99
+ is_default: !info.color && !themeId
96
100
  };
97
101
 
98
102
  if (callback) {
@@ -203,7 +203,7 @@ module.exports = function (defaultFuncs, api, ctx) {
203
203
  }
204
204
 
205
205
  const form = {
206
- av: ctx.globalOptions.pageID,
206
+ av: ctx.globalOptions.pageID || ctx.userID,
207
207
  queries: JSON.stringify({
208
208
  o0: {
209
209
  doc_id: "1498317363570230",
@@ -112,12 +112,22 @@ function formatThreadGraphQLResponse(data) {
112
112
  emoji: messageThread.customization_info
113
113
  ? messageThread.customization_info.emoji
114
114
  : null,
115
- color:
116
- messageThread.customization_info &&
117
- messageThread.customization_info.outgoing_bubble_color
118
- ? messageThread.customization_info.outgoing_bubble_color.slice(2)
119
- : null,
115
+ color: (function() {
116
+ const raw = messageThread.customization_info &&
117
+ messageThread.customization_info.outgoing_bubble_color;
118
+ if (!raw) return null;
119
+ const s = String(raw);
120
+ // Format is FFRRGGBB (8 hex chars, ARGB). Strip the FF alpha prefix.
121
+ // Validate before slicing to avoid returning garbage for unexpected formats.
122
+ if (/^[0-9a-fA-F]{8}$/.test(s)) return s.slice(2);
123
+ // Handle #RRGGBB or #AARRGGBB
124
+ if (/^#[0-9a-fA-F]{6}$/.test(s)) return s.slice(1);
125
+ if (/^#[0-9a-fA-F]{8}$/.test(s)) return s.slice(3);
126
+ // Fallback: return as-is
127
+ return s;
128
+ })(),
120
129
  threadTheme: messageThread.thread_theme,
130
+ theme_id: messageThread.thread_theme ? (messageThread.thread_theme.id || null) : null,
121
131
  nicknames:
122
132
  messageThread.customization_info &&
123
133
  messageThread.customization_info.participant_customizations
@@ -253,7 +263,7 @@ module.exports = function (defaultFuncs, api, ctx) {
253
263
 
254
264
  const threadInfo = formatThreadGraphQLResponse(responseData.data);
255
265
  if (threadInfo) {
256
- threadInfos[threadInfo.threadID || threadID[threadID.length - 1 - i]] = threadInfo;
266
+ threadInfos[threadInfo.threadID || threadIDs[i]] = threadInfo;
257
267
  }
258
268
  }
259
269
 
@@ -102,12 +102,18 @@ function formatThreadGraphQLResponse(messageThread) {
102
102
  emoji: messageThread.customization_info
103
103
  ? messageThread.customization_info.emoji
104
104
  : null,
105
- color:
106
- messageThread.customization_info &&
107
- messageThread.customization_info.outgoing_bubble_color
108
- ? messageThread.customization_info.outgoing_bubble_color.slice(2)
109
- : null,
105
+ color: (function() {
106
+ const raw = messageThread.customization_info &&
107
+ messageThread.customization_info.outgoing_bubble_color;
108
+ if (!raw) return null;
109
+ const s = String(raw);
110
+ if (/^[0-9a-fA-F]{8}$/.test(s)) return s.slice(2);
111
+ if (/^#[0-9a-fA-F]{6}$/.test(s)) return s.slice(1);
112
+ if (/^#[0-9a-fA-F]{8}$/.test(s)) return s.slice(3);
113
+ return s;
114
+ })(),
110
115
  threadTheme: messageThread.thread_theme,
116
+ theme_id: messageThread.thread_theme ? (messageThread.thread_theme.id || null) : null,
111
117
  nicknames:
112
118
  messageThread.customization_info &&
113
119
  messageThread.customization_info.participant_customizations
@@ -449,15 +449,17 @@ async function listenMqtt(defaultFuncs, api, ctx, globalCallback, scheduleReconn
449
449
  }
450
450
  }
451
451
  } else if (topic === "/thread_typing" || topic === "/orca_typing_notifications") {
452
- const typ = {
453
- type: "typ",
454
- isTyping: !!jsonMessage.state,
455
- from: jsonMessage.sender_fbid.toString(),
456
- threadID: utils.formatID((jsonMessage.thread || jsonMessage.sender_fbid).toString())
457
- };
458
- globalCallback(null, typ);
452
+ if (ctx.globalOptions.listenTyping) {
453
+ const typ = {
454
+ type: "typ",
455
+ isTyping: !!jsonMessage.state,
456
+ from: jsonMessage.sender_fbid.toString(),
457
+ threadID: utils.formatID((jsonMessage.thread || jsonMessage.sender_fbid).toString())
458
+ };
459
+ globalCallback(null, typ);
460
+ }
459
461
  } else if (topic === "/orca_presence") {
460
- if (!ctx.globalOptions.updatePresence && jsonMessage.list) {
462
+ if (ctx.globalOptions.updatePresence && jsonMessage.list) {
461
463
  for (const data of jsonMessage.list) {
462
464
  globalCallback(null, {
463
465
  type: "presence",
@@ -808,39 +810,16 @@ module.exports = (defaultFuncs, api, ctx, opts) => {
808
810
  } catch (err) {
809
811
  const detail = (err && err.detail && err.detail.message) ? ` | detail=${err.detail.message}` : "";
810
812
  const msg = ((err && err.error) || (err && err.message) || String(err || "")) + detail;
811
-
812
- const isNotLoggedIn = /Not logged in/i.test(msg);
813
- const isLoginBlocked = /blocked the login|checkpoint|security check|session.*expir|invalid.*session|authentication.*fail|auth.*fail|login.*block|account.*lock|verification.*requir/i.test(msg);
814
-
815
- if (isNotLoggedIn || isLoginBlocked) {
816
- const reason = isLoginBlocked ? "login_blocked" : "not_logged_in";
817
- utils.error("MQTT", `Auth error in getSeqID: ${reason} — attempting auto re-login`);
818
-
819
- // Mirror the close-handler re-login pattern: try handleSessionExpiry first.
820
- // If it succeeds we schedule a reconnect; only fall back to emitAuthError
821
- // (which kills listening) when re-login is unavailable or already in progress.
822
- if (!ctx._mqttReauthing && globalAutoReLoginManager && globalAutoReLoginManager.isEnabled && globalAutoReLoginManager.isEnabled()) {
823
- ctx._mqttReauthing = true;
824
- globalAutoReLoginManager.handleSessionExpiry(api, 'https://www.facebook.com', msg)
825
- .then((ok) => {
826
- ctx._mqttReauthing = false;
827
- if (ok && ctx.globalOptions.autoReconnect) {
828
- ctx._reconnectAttempts = 0;
829
- scheduleReconnect((ctx._mqttOpt && ctx._mqttOpt.reconnectDelayMs) || 2000);
830
- } else if (!ok) {
831
- emitAuthError(reason, msg);
832
- }
833
- })
834
- .catch(() => {
835
- ctx._mqttReauthing = false;
836
- emitAuthError(reason, msg);
837
- });
838
- return;
839
- }
840
-
841
- return emitAuthError(reason, msg);
813
+
814
+ if (/Not logged in/i.test(msg)) {
815
+ utils.error("MQTT", "Auth error in getSeqID: Not logged in");
816
+ return emitAuthError("not_logged_in", msg);
842
817
  }
843
-
818
+ if (/blocked the login|checkpoint|security check|session.*expir|invalid.*session|authentication.*fail|auth.*fail|login.*block|account.*lock|verification.*requir/i.test(msg)) {
819
+ utils.error("MQTT", "Auth error in getSeqID: Session/Login blocked");
820
+ return emitAuthError("login_blocked", msg);
821
+ }
822
+
844
823
  utils.error("MQTT", "getSeqID error:", msg);
845
824
  if (ctx.globalOptions.autoReconnect) {
846
825
  const baseDelay = (ctx._mqttOpt && ctx._mqttOpt.reconnectDelayMs) || 2000;
@@ -50,23 +50,20 @@ module.exports = function (defaultFuncs, api, ctx) {
50
50
 
51
51
  callback(null);
52
52
  } else {
53
- if (!ctx.mqttClient || !ctx.mqttClient.connected) {
54
- const err = new Error("markAsRead requires an active MQTT connection. Call api.listenMqtt() first.");
55
- callback(err);
56
- return returnPromise;
57
- }
53
+ const form = {
54
+ ["ids[" + threadID + "]"]: read,
55
+ watermarkTimestamp: Date.now(),
56
+ shouldSendReadReceipt: true,
57
+ };
58
58
 
59
- const publishErr = await new Promise((r) =>
60
- ctx.mqttClient.publish(
61
- "/mark_thread",
62
- JSON.stringify({ threadID, mark: "read", state: read }),
63
- { qos: 1, retain: false },
64
- r,
65
- )
66
- );
59
+ const resData = await defaultFuncs
60
+ .post("https://www.facebook.com/ajax/mercury/change_read_status.php", ctx.jar, form)
61
+ .then(utils.saveCookies(ctx.jar))
62
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs));
67
63
 
68
- if (publishErr) {
69
- callback(publishErr instanceof Error ? publishErr : new Error(String(publishErr)));
64
+ if (resData && resData.error) {
65
+ const err = new Error(String(resData.error.message || resData.error));
66
+ callback(err);
70
67
  return returnPromise;
71
68
  }
72
69