@lazyneoaz/metachat 1.0.9 → 1.0.11

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.
Files changed (46) hide show
  1. package/index.js +55 -1
  2. package/package.json +1 -1
  3. package/src/apis/addUserToGroup.js +1 -1
  4. package/src/apis/changeAdminStatus.js +11 -9
  5. package/src/apis/changeGroupImage.js +1 -1
  6. package/src/apis/changeThreadColor.js +1 -1
  7. package/src/apis/createNewGroup.js +12 -4
  8. package/src/apis/createPoll.js +5 -2
  9. package/src/apis/editMessage.js +59 -48
  10. package/src/apis/emoji.js +2 -2
  11. package/src/apis/follow.js +1 -1
  12. package/src/apis/forwardAttachment.js +3 -2
  13. package/src/apis/gcmember.js +20 -12
  14. package/src/apis/gcname.js +2 -2
  15. package/src/apis/gcrule.js +16 -10
  16. package/src/apis/getAccess.js +3 -1
  17. package/src/apis/getBotInitialData.js +1 -1
  18. package/src/apis/getThreadInfo.js +1 -1
  19. package/src/apis/listenMqtt.js +72 -3
  20. package/src/apis/markAsSeen.js +7 -9
  21. package/src/apis/nickname.js +67 -50
  22. package/src/apis/pinMessage.js +1 -1
  23. package/src/apis/removeUserFromGroup.js +7 -0
  24. package/src/apis/sendEffect.js +7 -6
  25. package/src/apis/sendMessage.js +6 -2
  26. package/src/apis/setMessageReaction.js +59 -20
  27. package/src/apis/setMessageReactionMqtt.js +96 -56
  28. package/src/apis/setThreadThemeMqtt.js +4 -2
  29. package/src/apis/shareContact.js +37 -31
  30. package/src/apis/theme.js +1 -1
  31. package/src/app/MessengerBot.js +239 -0
  32. package/src/app/broadcast.js +48 -0
  33. package/src/app/config.js +97 -0
  34. package/src/app/createFcaClient.js +179 -0
  35. package/src/app/state.js +117 -0
  36. package/src/app/threadSync.js +106 -0
  37. package/src/app/updateCheck.js +92 -0
  38. package/src/engine/client.js +76 -28
  39. package/src/engine/models/loginHelper.js +5 -0
  40. package/src/utils/antiSuspension.js +10 -10
  41. package/src/utils/auth-helpers.js +17 -1
  42. package/src/utils/clients.js +21 -0
  43. package/src/utils/headers.js +1 -1
  44. package/src/utils/lsRequest.js +176 -0
  45. package/src/utils/rateLimiter.js +7 -3
  46. package/src/utils/tokenRefresh.js +14 -1
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.9",
3
+ "version": "1.0.11",
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.",
@@ -19,7 +19,7 @@ module.exports = (defaultFuncs, api, ctx) => {
19
19
  }
20
20
 
21
21
  try {
22
- if (!ctx.mqttClient) {
22
+ if (!ctx.mqttClient || !ctx.mqttClient.connected) {
23
23
  throw new Error("Not connected to MQTT. Please use listenMqtt first.");
24
24
  }
25
25
 
@@ -37,8 +37,11 @@ module.exports = function (defaultFuncs, api, ctx) {
37
37
  const isAdmin = adminStatus ? 1 : 0;
38
38
  const epochID = utils.generateOfflineThreadingID();
39
39
 
40
+ if (typeof ctx.wsReqNumber !== "number") ctx.wsReqNumber = 0;
41
+ if (typeof ctx.wsTaskNumber !== "number") ctx.wsTaskNumber = 0;
42
+
40
43
  if (getType(adminID) === "Array") {
41
- adminID.forEach((id, index) => {
44
+ adminID.forEach((id) => {
42
45
  tasks.push({
43
46
  failure_count: null,
44
47
  label: "25",
@@ -48,7 +51,7 @@ module.exports = function (defaultFuncs, api, ctx) {
48
51
  is_admin: isAdmin
49
52
  }),
50
53
  queue_name: "admin_status",
51
- task_id: index + 1
54
+ task_id: ++ctx.wsTaskNumber
52
55
  });
53
56
  });
54
57
  } else {
@@ -61,11 +64,10 @@ module.exports = function (defaultFuncs, api, ctx) {
61
64
  is_admin: isAdmin
62
65
  }),
63
66
  queue_name: "admin_status",
64
- task_id: 1
67
+ task_id: ++ctx.wsTaskNumber
65
68
  });
66
69
  }
67
70
 
68
- let count_req = 0;
69
71
  const form = JSON.stringify({
70
72
  app_id: "2220391788200892",
71
73
  payload: JSON.stringify({
@@ -73,7 +75,7 @@ module.exports = function (defaultFuncs, api, ctx) {
73
75
  tasks: tasks,
74
76
  version_id: "8798795233522156"
75
77
  }),
76
- request_id: ++count_req,
78
+ request_id: ++ctx.wsReqNumber,
77
79
  type: 3
78
80
  });
79
81
 
@@ -92,12 +94,12 @@ module.exports = function (defaultFuncs, api, ctx) {
92
94
  const epochID = utils.generateOfflineThreadingID();
93
95
 
94
96
  if (getType(adminID) === "Array") {
95
- adminID.forEach((id, index) => {
97
+ adminID.forEach((id) => {
96
98
  tasks.push({
97
99
  label: '25',
98
100
  payload: JSON.stringify({ thread_key: threadID, contact_id: id, is_admin: adminStatus }),
99
101
  queue_name: 'admin_status',
100
- task_id: index + 1,
102
+ task_id: ++ctx.wsTaskNumber,
101
103
  failure_count: null
102
104
  });
103
105
  });
@@ -106,14 +108,14 @@ module.exports = function (defaultFuncs, api, ctx) {
106
108
  label: '25',
107
109
  payload: JSON.stringify({ thread_key: threadID, contact_id: adminID, is_admin: adminStatus }),
108
110
  queue_name: 'admin_status',
109
- task_id: 1,
111
+ task_id: ++ctx.wsTaskNumber,
110
112
  failure_count: null
111
113
  });
112
114
  }
113
115
 
114
116
  const form = {
115
117
  fb_dtsg: ctx.fb_dtsg,
116
- request_id: 1,
118
+ request_id: ++ctx.wsReqNumber,
117
119
  type: 3,
118
120
  payload: {
119
121
  version_id: '3816854585040595',
@@ -33,7 +33,7 @@ module.exports = (defaultFuncs, api, ctx) => {
33
33
  }
34
34
 
35
35
  try {
36
- if (!ctx.mqttClient) {
36
+ if (!ctx.mqttClient || !ctx.mqttClient.connected) {
37
37
  throw new Error("Not connected to MQTT. Please use listenMqtt first.");
38
38
  }
39
39
 
@@ -20,7 +20,7 @@ module.exports = (defaultFuncs, api, ctx) => {
20
20
  }
21
21
 
22
22
  try {
23
- if (!ctx.mqttClient) {
23
+ if (!ctx.mqttClient || !ctx.mqttClient.connected) {
24
24
  throw new Error("Not connected to MQTT. Please use listenMqtt first.");
25
25
  }
26
26
 
@@ -11,18 +11,26 @@ module.exports = (defaultFuncs, api, ctx) => {
11
11
  rejectFunc = reject;
12
12
  });
13
13
 
14
+ if (utils.getType(groupTitle) === "Function") {
15
+ callback = groupTitle;
16
+ groupTitle = null;
17
+ }
18
+
14
19
  if (!callback) {
15
20
  callback = (err, result) => {
16
21
  if (err) return rejectFunc(err);
17
22
  resolveFunc(result);
18
23
  };
24
+ } else {
25
+ const _userCb = callback;
26
+ callback = (err, result) => {
27
+ if (err) { _userCb(err); return rejectFunc(err); }
28
+ _userCb(null, result);
29
+ resolveFunc(result);
30
+ };
19
31
  }
20
32
 
21
33
  try {
22
- if (utils.getType(groupTitle) === "Function") {
23
- callback = groupTitle;
24
- groupTitle = null;
25
- }
26
34
 
27
35
  if (utils.getType(participantIDs) !== "Array") {
28
36
  throw new Error("createNewGroup: participantIDs should be an array.");
@@ -19,7 +19,7 @@ module.exports = (defaultFuncs, api, ctx) => {
19
19
  }
20
20
 
21
21
  try {
22
- if (!ctx.mqttClient) {
22
+ if (!ctx.mqttClient || !ctx.mqttClient.connected) {
23
23
  throw new Error("Not connected to MQTT. Please use listenMqtt first.");
24
24
  }
25
25
 
@@ -35,6 +35,9 @@ module.exports = (defaultFuncs, api, ctx) => {
35
35
  throw new Error("options must be an array with at least 2 options");
36
36
  }
37
37
 
38
+ if (typeof ctx.wsReqNumber !== 'number') ctx.wsReqNumber = 0;
39
+ if (typeof ctx.wsTaskNumber !== 'number') ctx.wsTaskNumber = 0;
40
+
38
41
  const payload = {
39
42
  epoch_id: utils.generateOfflineThreadingID(),
40
43
  tasks: [
@@ -48,7 +51,7 @@ module.exports = (defaultFuncs, api, ctx) => {
48
51
  sync_group: 1
49
52
  }),
50
53
  queue_name: "poll_creation",
51
- task_id: Math.floor(Math.random() * 1001)
54
+ task_id: ++ctx.wsTaskNumber
52
55
  }
53
56
  ],
54
57
  version_id: "8768858626531631"
@@ -1,70 +1,81 @@
1
1
  "use strict";
2
- /**
3
- * @author RFS-ADRENO
4
- * @rewrittenBy Isai Ivanov
5
- */
6
2
 
7
3
  const utils = require('../utils');
8
-
9
- function canBeCalled(func) {
10
- try {
11
- Reflect.apply(func, null, []);
12
- return true;
13
- } catch (error) {
14
- return false;
15
- }
16
- }
4
+ const { publishLsRequestWithAck } = require('../utils/lsRequest');
17
5
 
18
6
  /**
19
- * A function for editing bot's messages.
20
- * @param {string} text - The text with which the bot will edit its messages.
21
- * @param {string} messageID - The message ID of the message the bot will edit.
22
- * @param {Object} callback - Callback for the function.
23
- */
24
-
7
+ * Edits a previously sent bot message via MQTT with ACK confirmation.
8
+ *
9
+ * @param {string} text New text content for the message.
10
+ * @param {string} messageID The ID of the message to edit.
11
+ * @param {Function} [callback] Optional callback(err, result).
12
+ * @returns {Promise}
13
+ */
25
14
  module.exports = function (defaultFuncs, api, ctx) {
26
15
  return function editMessage(text, messageID, callback) {
27
- if (!ctx.mqttClient) {
28
- throw new Error('Not connected to MQTT');
16
+ let resolveFunc, rejectFunc;
17
+ const returnPromise = new Promise((resolve, reject) => {
18
+ resolveFunc = resolve;
19
+ rejectFunc = reject;
20
+ });
21
+
22
+ const cb = (typeof callback === "function")
23
+ ? (err, data) => { callback(err, data); if (err) rejectFunc(err); else resolveFunc(data); }
24
+ : (err, data) => { if (err) rejectFunc(err); else resolveFunc(data); };
25
+
26
+ if (!ctx.mqttClient || !ctx.mqttClient.connected) {
27
+ return cb(new Error("Not connected to MQTT — call listenMqtt() first")), returnPromise;
29
28
  }
30
29
 
31
- ctx.wsReqNumber += 1;
32
- ctx.wsTaskNumber += 1;
30
+ if (typeof ctx.wsReqNumber !== "number") ctx.wsReqNumber = 0;
31
+ if (typeof ctx.wsTaskNumber !== "number") ctx.wsTaskNumber = 0;
33
32
 
34
- const queryPayload = {
35
- message_id: messageID,
36
- text
37
- };
33
+ const requestId = ++ctx.wsReqNumber;
34
+ const taskId = ++ctx.wsTaskNumber;
38
35
 
39
- const query = {
36
+ const task = {
40
37
  failure_count: null,
41
- label: '742',
42
- payload: JSON.stringify(queryPayload),
43
- queue_name: 'edit_message',
44
- task_id: ctx.wsTaskNumber
38
+ label: "742",
39
+ payload: JSON.stringify({ message_id: messageID, text }),
40
+ queue_name: "edit_message",
41
+ task_id: taskId
45
42
  };
46
43
 
47
- const context = {
48
- app_id: '2220391788200892',
49
- payload: {
44
+ const content = {
45
+ app_id: "2220391788200892",
46
+ payload: JSON.stringify({
50
47
  data_trace_id: null,
51
48
  epoch_id: parseInt(utils.generateOfflineThreadingID()),
52
- tasks: [query],
53
- version_id: '6903494529735864'
54
- },
55
- request_id: ctx.wsReqNumber,
49
+ tasks: [task],
50
+ version_id: "6903494529735864"
51
+ }),
52
+ request_id: requestId,
56
53
  type: 3
57
54
  };
58
55
 
59
- context.payload = JSON.stringify(context.payload);
60
-
61
-
62
-
63
-
64
-
65
- ctx.mqttClient.publish('/ls_req', JSON.stringify(context), {
66
- qos: 1, retain: false
56
+ publishLsRequestWithAck({
57
+ client: ctx.mqttClient,
58
+ content,
59
+ requestId,
60
+ timeoutMs: 10000,
61
+ extract: (message) => ({
62
+ success: true,
63
+ messageID,
64
+ text,
65
+ ack: message.payload
66
+ })
67
+ }).then((result) => {
68
+ cb(null, result);
69
+ }).catch((err) => {
70
+ if (err && err.message && err.message.includes("Timeout waiting for LS ACK")) {
71
+ utils.warn("editMessage", "ACK timed out — edit may still have been applied");
72
+ cb(null, { success: true, messageID, text, ackTimeout: true });
73
+ } else {
74
+ utils.error("editMessage", err && err.message ? err.message : err);
75
+ cb(err instanceof Error ? err : new Error(String(err && err.message ? err.message : err)));
76
+ }
67
77
  });
78
+
79
+ return returnPromise;
68
80
  };
69
81
  };
70
-
package/src/apis/emoji.js CHANGED
@@ -69,7 +69,7 @@ module.exports = function (defaultFuncs, api, ctx) {
69
69
  return _callback(new Error("An emoji character is required."));
70
70
  }
71
71
 
72
- if (!ctx.mqttClient) {
72
+ if (!ctx.mqttClient || !ctx.mqttClient.connected) {
73
73
  return _callback(new Error("Not connected to MQTT"));
74
74
  }
75
75
 
@@ -92,7 +92,7 @@ module.exports = function (defaultFuncs, api, ctx) {
92
92
  };
93
93
 
94
94
  const context = {
95
- app_id: ctx.appID,
95
+ app_id: ctx.appID || '2220391788200892',
96
96
  payload: {
97
97
  epoch_id: parseInt(utils.generateOfflineThreadingID()),
98
98
  tasks: [query],
@@ -48,7 +48,7 @@ module.exports = function (defaultFuncs, api, ctx) {
48
48
  av: ctx.userID,
49
49
  fb_api_req_friendly_name: "CometUserUnfollowMutation",
50
50
  fb_api_caller_class: "RelayModern",
51
- doc_id: "25472099855769847",
51
+ doc_id: "7033020913412778",
52
52
  variables: JSON.stringify({
53
53
  action_render_location: "WWW_COMET_FRIEND_MENU",
54
54
  input: {
@@ -48,6 +48,7 @@ module.exports = function (defaultFuncs, api, ctx) {
48
48
  const timestamp = Date.now();
49
49
  const tid = String(threadID);
50
50
  if (typeof ctx.wsReqNumber !== 'number') ctx.wsReqNumber = 0;
51
+ if (typeof ctx.wsTaskNumber !== 'number') ctx.wsTaskNumber = 0;
51
52
  const reqID = ++ctx.wsReqNumber;
52
53
 
53
54
  const sendPayload = {
@@ -73,7 +74,7 @@ module.exports = function (defaultFuncs, api, ctx) {
73
74
  label: "46",
74
75
  payload: JSON.stringify(sendPayload),
75
76
  queue_name: tid,
76
- task_id: 400,
77
+ task_id: ++ctx.wsTaskNumber,
77
78
  failure_count: null,
78
79
  },
79
80
  {
@@ -84,7 +85,7 @@ module.exports = function (defaultFuncs, api, ctx) {
84
85
  sync_group: 1,
85
86
  }),
86
87
  queue_name: tid,
87
- task_id: 401,
88
+ task_id: ++ctx.wsTaskNumber,
88
89
  failure_count: null,
89
90
  },
90
91
  ],
@@ -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
- if (!ctx.mqttClient) {
55
- return _callback(null, { type: "error_gc", error: "Not connected to MQTT" });
57
+ if (!ctx.mqttClient || !ctx.mqttClient.connected) {
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 };
@@ -91,7 +99,7 @@ module.exports = function (defaultFuncs, api, ctx) {
91
99
  }
92
100
 
93
101
  const context = {
94
- app_id: ctx.appID,
102
+ app_id: ctx.appID || '2220391788200892',
95
103
  payload: { epoch_id: parseInt(utils.generateOfflineThreadingID()), tasks: [query], version_id: "24631415369801570" },
96
104
  request_id: ctx.wsReqNumber,
97
105
  type: 3
@@ -113,8 +121,8 @@ 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
+ utils.error("gcmember", err);
125
+ _callback(err instanceof Error ? err : new Error(err.message || "An unknown error occurred."));
118
126
  }
119
127
 
120
128
  return returnPromise;
@@ -69,7 +69,7 @@ module.exports = function (defaultFuncs, api, ctx) {
69
69
  return _callback(new Error("newName must be a string."));
70
70
  }
71
71
 
72
- if (!ctx.mqttClient) {
72
+ if (!ctx.mqttClient || !ctx.mqttClient.connected) {
73
73
  return _callback(new Error("Not connected to MQTT"));
74
74
  }
75
75
 
@@ -91,7 +91,7 @@ module.exports = function (defaultFuncs, api, ctx) {
91
91
  };
92
92
 
93
93
  const context = {
94
- app_id: ctx.appID,
94
+ app_id: ctx.appID || '2220391788200892',
95
95
  payload: {
96
96
  epoch_id: parseInt(utils.generateOfflineThreadingID()),
97
97
  tasks: [query],
@@ -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 || !ctx.mqttClient.connected) { _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
 
@@ -85,7 +90,7 @@ module.exports = function (defaultFuncs, api, ctx) {
85
90
  task_id: ctx.wsTaskNumber
86
91
  };
87
92
  const context = {
88
- app_id: ctx.appID,
93
+ app_id: ctx.appID || '2220391788200892',
89
94
  payload: {
90
95
  epoch_id: parseInt(utils.generateOfflineThreadingID()),
91
96
  tasks: [query],
@@ -111,7 +116,8 @@ 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
+ utils.error("gcrule", err);
120
+ _callback(err instanceof Error ? err : new Error(err.message || "An unknown error occurred."));
115
121
  }
116
122
 
117
123
  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]}}`);
@@ -263,7 +263,7 @@ module.exports = function (defaultFuncs, api, ctx) {
263
263
 
264
264
  const threadInfo = formatThreadGraphQLResponse(responseData.data);
265
265
  if (threadInfo) {
266
- threadInfos[threadInfo.threadID || threadID[threadID.length - 1 - i]] = threadInfo;
266
+ threadInfos[threadInfo.threadID || threadIDs[i]] = threadInfo;
267
267
  }
268
268
  }
269
269