@lazyneoaz/metachat 1.1.3 → 1.1.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lazyneoaz/metachat",
3
- "version": "1.1.3",
3
+ "version": "1.1.6",
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.",
@@ -37,7 +37,6 @@
37
37
  "metachat"
38
38
  ],
39
39
  "dependencies": {
40
- "@dongdev/fca-unofficial": "^4.0.3",
41
40
  "axios": "^1.13.5",
42
41
  "axios-cookiejar-support": "^4.0.7",
43
42
  "bluebird": "^3.7.2",
@@ -67,7 +67,7 @@ module.exports = function (defaultFuncs, api, ctx) {
67
67
  });
68
68
 
69
69
  const form = JSON.stringify({
70
- app_id: "2220391788200892",
70
+ app_id: String(ctx.appID || ctx.mqttAppID || "2220391788200892"),
71
71
  payload: JSON.stringify({
72
72
  epoch_id: epochID,
73
73
  tasks: tasks,
@@ -114,7 +114,7 @@ module.exports = function (defaultFuncs, api, ctx) {
114
114
  epoch_id: epochID,
115
115
  data_trace_id: null
116
116
  }),
117
- app_id: "2220391788200892"
117
+ app_id: String(ctx.appID || ctx.mqttAppID || "772021112871879")
118
118
  };
119
119
 
120
120
  defaultFuncs
@@ -110,7 +110,7 @@ module.exports = (defaultFuncs, api, ctx) => {
110
110
  };
111
111
 
112
112
  const request = {
113
- app_id: "2220391788200892",
113
+ app_id: String(ctx.appID || ctx.mqttAppID || "2220391788200892"),
114
114
  payload: JSON.stringify(mqttPayload),
115
115
  request_id: reqID,
116
116
  type: 3
@@ -58,7 +58,7 @@ module.exports = (defaultFuncs, api, ctx) => {
58
58
  };
59
59
 
60
60
  const form = JSON.stringify({
61
- app_id: "2220391788200892",
61
+ app_id: "772021112871879",
62
62
  payload: JSON.stringify(payload),
63
63
  request_id: ++ctx.wsReqNumber,
64
64
  type: 3
@@ -1,20 +1,9 @@
1
1
  "use strict";
2
2
 
3
3
  const utils = require('../utils');
4
- const { publishLsRequestWithAck } = require('../utils/lsRequest');
5
4
 
6
5
  module.exports = (defaultFuncs, api, ctx) => {
7
- /**
8
- * Deletes one or more messages.
9
- *
10
- * Uses MQTT label "146" (version "25909428212080747") when the MQTT client is
11
- * connected — this matches dongdev's deleteMessage implementation.
12
- * Falls back to the legacy HTTP delete_messages.php endpoint otherwise.
13
- *
14
- * @param {string|string[]} messageOrMessages
15
- * @param {Function} [callback]
16
- */
17
- return async function deleteMessage(messageOrMessages, callback) {
6
+ return async function deleteMessage(messageID, callback) {
18
7
  let resolveFunc = () => {};
19
8
  let rejectFunc = () => {};
20
9
  const returnPromise = new Promise((resolve, reject) => {
@@ -37,66 +26,17 @@ module.exports = (defaultFuncs, api, ctx) => {
37
26
  }
38
27
 
39
28
  try {
40
- const messages = Array.isArray(messageOrMessages)
41
- ? messageOrMessages
42
- : [messageOrMessages];
43
-
44
- if (messages.length === 0 || messages.some(v => v === null || v === undefined || v === "")) {
45
- throw new Error("messageOrMessages must contain at least one non-empty message ID");
46
- }
47
-
48
- const mqttReady = !!(ctx.mqttClient && ctx.mqttClient.connected);
49
-
50
- if (mqttReady) {
51
- // MQTT path: label "146", version "25909428212080747" (dongdev confirmed)
52
- if (typeof ctx.wsReqNumber !== "number") ctx.wsReqNumber = 0;
53
- if (typeof ctx.wsTaskNumber !== "number") ctx.wsTaskNumber = 0;
54
-
55
- const requestId = ++ctx.wsReqNumber;
56
- const tasks = messages.map((messageID) => {
57
- const queueName = String(messageID);
58
- const taskId = ++ctx.wsTaskNumber;
59
- return {
60
- failure_count: null,
61
- label: "146",
62
- payload: JSON.stringify({
63
- thread_key: queueName,
64
- remove_type: 0,
65
- sync_group: 1
66
- }),
67
- queue_name: queueName,
68
- task_id: taskId
69
- };
70
- });
71
-
72
- publishLsRequestWithAck({
73
- client: ctx.mqttClient,
74
- requestId,
75
- timeoutMs: 20000,
76
- content: {
77
- app_id: "2220391788200892",
78
- payload: JSON.stringify({
79
- epoch_id: parseInt(utils.generateOfflineThreadingID()),
80
- tasks,
81
- version_id: "25909428212080747"
82
- }),
83
- request_id: requestId,
84
- type: 3
85
- },
86
- extract: (message) => ({ success: true, response: message.payload })
87
- }).then((result) => {
88
- callback(null, result);
89
- }).catch((mqttErr) => {
90
- utils.error("deleteMessage/mqtt", mqttErr);
91
- // MQTT failed → fall back to HTTP
92
- _deleteViaHttp(messages, callback, defaultFuncs, ctx);
93
- });
29
+ const res = await defaultFuncs.post(
30
+ "https://www.facebook.com/ajax/mercury/delete_messages.php",
31
+ ctx.jar,
32
+ { message_id: messageID }
33
+ ).then(utils.parseAndCheckLogin(ctx, defaultFuncs));
94
34
 
95
- } else {
96
- // HTTP fallback
97
- await _deleteViaHttp(messages, callback, defaultFuncs, ctx);
35
+ if (res && res.error) {
36
+ throw new Error(String(res.error_msg || res.error || "deleteMessage failed"));
98
37
  }
99
38
 
39
+ callback(null, { success: true });
100
40
  } catch (err) {
101
41
  utils.error("deleteMessage", err);
102
42
  callback(err instanceof Error ? err : new Error(String(err && err.message ? err.message : err)));
@@ -105,24 +45,3 @@ module.exports = (defaultFuncs, api, ctx) => {
105
45
  return returnPromise;
106
46
  };
107
47
  };
108
-
109
- async function _deleteViaHttp(messages, callback, defaultFuncs, ctx) {
110
- try {
111
- // The legacy endpoint only accepts one messageID at a time
112
- for (const messageID of messages) {
113
- const res = await defaultFuncs.post(
114
- "https://www.facebook.com/ajax/mercury/delete_messages.php",
115
- ctx.jar,
116
- { message_id: messageID }
117
- ).then(utils.parseAndCheckLogin(ctx, defaultFuncs));
118
-
119
- if (res && res.error) {
120
- throw new Error(String(res.error_msg || res.error || "deleteMessage HTTP failed"));
121
- }
122
- }
123
- callback(null, { success: true });
124
- } catch (err) {
125
- utils.error("deleteMessage/http", err);
126
- callback(err instanceof Error ? err : new Error(String(err && err.message ? err.message : err)));
127
- }
128
- }
package/src/apis/emoji.js CHANGED
@@ -92,11 +92,11 @@ module.exports = function (defaultFuncs, api, ctx) {
92
92
  };
93
93
 
94
94
  const context = {
95
- app_id: '2220391788200892',
95
+ app_id: ctx.appID || '2220391788200892',
96
96
  payload: {
97
97
  epoch_id: parseInt(utils.generateOfflineThreadingID()),
98
98
  tasks: [query],
99
- version_id: '8798795233522156',
99
+ version_id: '24631415369801570',
100
100
  },
101
101
  request_id: ctx.wsReqNumber,
102
102
  type: 3,
@@ -1,172 +1,128 @@
1
1
  "use strict";
2
2
 
3
3
  const utils = require('../utils');
4
- const { publishLsRequestWithAck } = require('../utils/lsRequest');
5
4
 
6
5
  module.exports = function (defaultFuncs, api, ctx) {
7
6
  /**
8
- * Adds or removes members from a group chat.
7
+ * Made by ChoruOfficial
8
+ * Mqtt
9
+ * Adds or removes members from a group chat with pre-checking.
9
10
  *
10
- * Add uses publishLsRequestWithAck (label "23", app_id "772021112871879",
11
- * version "24502707779384158") matches dongdev addUserToGroup.
12
- * Remove uses fire-and-forget publish (label "140", app_id "2220391788200892",
13
- * version "25002366262773827") — matches dongdev removeUserFromGroup.
14
- *
15
- * @param {"add"|"remove"} action
16
- * @param {string|string[]} userIDs
17
- * @param {string} threadID
18
- * @param {Function} [callback]
11
+ * @param {"add" | "remove"} action The action to perform.
12
+ * @param {string|string[]} userIDs The user ID or array of user IDs.
13
+ * @param {string} threadID The ID of the group chat.
14
+ * @param {Function} [callback] Optional callback function.
15
+ * @returns {Promise<object>} A promise that resolves with information about the action.
19
16
  */
20
17
  return async function gcmember(action, userIDs, threadID, callback) {
21
18
  let _callback;
22
19
  if (typeof threadID === 'function') {
23
- _callback = threadID;
24
- threadID = null;
20
+ _callback = threadID;
21
+ threadID = null;
25
22
  } else if (typeof callback === 'function') {
26
- _callback = callback;
23
+ _callback = callback;
27
24
  }
28
-
25
+
29
26
  let resolvePromise, rejectPromise;
30
27
  const returnPromise = new Promise((resolve, reject) => {
31
- resolvePromise = resolve;
32
- rejectPromise = reject;
28
+ resolvePromise = resolve;
29
+ rejectPromise = reject;
33
30
  });
34
31
 
35
- if (typeof _callback !== "function") {
32
+ if (typeof _callback != "function") {
36
33
  _callback = (err, data) => {
34
+ // Note: We will now rarely use the 'err' parameter for validation errors
37
35
  if (err) return rejectPromise(err);
38
36
  resolvePromise(data);
39
- };
37
+ }
40
38
  }
41
39
 
42
40
  try {
43
- const validActions = ["add", "remove"];
44
- action = action ? action.toLowerCase() : "";
45
-
46
- if (!validActions.includes(action)) {
47
- _callback(null, { type: "error_gc", error: `Invalid action. Must be one of: ${validActions.join(", ")}` });
48
- return returnPromise;
49
- }
50
- if (!userIDs || (Array.isArray(userIDs) ? userIDs.length === 0 : false)) {
51
- _callback(null, { type: "error_gc", error: "userIDs is required." });
52
- return returnPromise;
53
- }
54
- if (!threadID) {
55
- _callback(null, { type: "error_gc", error: "threadID is required." });
56
- return returnPromise;
57
- }
58
- if (!ctx.mqttClient || !ctx.mqttClient.connected) {
59
- _callback(null, { type: "error_gc", error: "Not connected to MQTT" });
60
- return returnPromise;
61
- }
62
-
63
- const threadInfo = await api.getThreadInfo(threadID);
64
- if (!threadInfo) {
65
- _callback(null, { type: "error_gc", error: "Could not retrieve thread information." });
66
- return returnPromise;
67
- }
68
- if (threadInfo.isGroup === false) {
69
- _callback(null, { type: "error_gc", error: "This feature is only for group chats, not private messages." });
70
- return returnPromise;
71
- }
72
-
73
- const currentMembers = threadInfo.participantIDs || [];
74
- const usersToModify = Array.isArray(userIDs) ? userIDs : [userIDs];
41
+ const validActions = ["add", "remove"];
42
+ action = action ? action.toLowerCase() : "";
75
43
 
76
- if (typeof ctx.wsReqNumber !== "number") ctx.wsReqNumber = 0;
77
- if (typeof ctx.wsTaskNumber !== "number") ctx.wsTaskNumber = 0;
78
44
 
79
- if (action === 'add') {
80
- // Filter out users already in the group
81
- const usersToAdd = usersToModify.filter(id => !currentMembers.includes(String(id)));
82
- if (usersToAdd.length === 0) {
83
- _callback(null, { type: "error_gc", error: "All specified users are already in the group." });
84
- return returnPromise;
45
+ if (!validActions.includes(action)) {
46
+ _callback(null, { type: "error_gc", error: `Invalid action. Must be one of: ${validActions.join(", ")}` });
47
+ return returnPromise;
48
+ }
49
+ if (!userIDs || userIDs.length === 0) {
50
+ _callback(null, { type: "error_gc", error: "userIDs is required." });
51
+ return returnPromise;
52
+ }
53
+ if (!threadID) {
54
+ _callback(null, { type: "error_gc", error: "threadID is required." });
55
+ return returnPromise;
56
+ }
57
+ if (!ctx.mqttClient || !ctx.mqttClient.connected) {
58
+ _callback(null, { type: "error_gc", error: "Not connected to MQTT" });
59
+ return returnPromise;
85
60
  }
86
61
 
87
- const requestId = ++ctx.wsReqNumber;
88
- const taskId = ++ctx.wsTaskNumber;
89
-
90
- // addUserToGroup: app_id "772021112871879", version "24502707779384158" (dongdev confirmed)
91
- publishLsRequestWithAck({
92
- client: ctx.mqttClient,
93
- requestId,
94
- content: {
95
- app_id: "772021112871879",
96
- payload: JSON.stringify({
97
- epoch_id: parseInt(utils.generateOfflineThreadingID()),
98
- tasks: [{
99
- failure_count: null,
100
- label: "23",
101
- payload: JSON.stringify({
102
- thread_key: threadID,
103
- contact_ids: usersToAdd.map(id => String(id)),
104
- sync_group: 1
105
- }),
106
- queue_name: String(threadID),
107
- task_id: taskId
108
- }],
109
- version_id: "24502707779384158"
110
- }),
111
- request_id: requestId,
112
- type: 3
113
- },
114
- extract: (message) => ({ success: true, response: message.payload })
115
- }).then((result) => {
116
- _callback(null, {
117
- type: "gc_member_update",
118
- threadID, userIDs: usersToAdd, action,
119
- senderID: ctx.userID, BotID: ctx.userID, timestamp: Date.now()
120
- });
121
- }).catch((err) => {
122
- utils.error("gcmember/add", err);
123
- _callback(err instanceof Error ? err : new Error(err.message || "gcmember add failed"));
124
- });
125
-
126
- } else {
127
- // action === 'remove'
128
- const userToRemove = String(usersToModify[0]);
129
- if (!currentMembers.includes(userToRemove)) {
130
- _callback(null, { type: "error_gc", error: `User ${userToRemove} is not in this group.` });
131
- return returnPromise;
62
+ const threadInfo = await api.getThreadInfo(threadID);
63
+ if (!threadInfo) {
64
+ _callback(null, { type: "error_gc", error: "Could not retrieve thread information." });
65
+ return returnPromise;
66
+ }
67
+ if (threadInfo.isGroup === false) {
68
+ _callback(null, { type: "error_gc", error: "This feature is only for group chats, not private messages." });
69
+ return returnPromise;
70
+ }
71
+
72
+ const currentMembers = threadInfo.participantIDs;
73
+ const usersToModify = Array.isArray(userIDs) ? userIDs : [userIDs];
74
+ let queryPayload, query;
75
+ let finalUsers = usersToModify;
76
+
77
+ ctx.wsReqNumber = (ctx.wsReqNumber || 0) + 1;
78
+ ctx.wsTaskNumber = (ctx.wsTaskNumber || 0) + 1;
79
+
80
+ if (action === 'add') {
81
+ const usersToAdd = usersToModify.filter(id => !currentMembers.includes(id));
82
+ if (usersToAdd.length === 0) {
83
+ _callback(null, { type: "error_gc", error: "All specified users are already in the group." });
84
+ return returnPromise;
85
+ }
86
+ finalUsers = usersToAdd;
87
+ queryPayload = { thread_key: parseInt(threadID), contact_ids: finalUsers.map(id => parseInt(id)), sync_group: 1 };
88
+ query = { label: "23", payload: JSON.stringify(queryPayload), queue_name: threadID, task_id: ctx.wsTaskNumber };
89
+
90
+ } else { // action is 'remove'
91
+ const userToRemove = usersToModify[0];
92
+ if (!currentMembers.includes(userToRemove)) {
93
+ _callback(null, { type: "error_gc", error: `User with ID ${userToRemove} is not in this group chat.` });
94
+ return returnPromise;
95
+ }
96
+ finalUsers = [userToRemove];
97
+ queryPayload = { thread_id: threadID, contact_id: userToRemove, sync_group: 1 };
98
+ query = { label: "140", payload: JSON.stringify(queryPayload), queue_name: "remove_participant_v2", task_id: ctx.wsTaskNumber };
132
99
  }
133
100
 
134
- // removeUserFromGroup: app_id "2220391788200892", version "25002366262773827" (dongdev confirmed)
135
- // Fire-and-forget (no ACK needed) — matches dongdev's publishRealtimeMessage pattern
136
101
  const context = {
137
- app_id: "2220391788200892",
138
- payload: JSON.stringify({
139
- epoch_id: parseInt(utils.generateOfflineThreadingID()),
140
- tasks: [{
141
- failure_count: null,
142
- label: "140",
143
- payload: JSON.stringify({
144
- thread_id: threadID,
145
- contact_id: userToRemove,
146
- sync_group: 1
147
- }),
148
- queue_name: "remove_participant_v2",
149
- task_id: Math.floor(Math.random() * 1001)
150
- }],
151
- version_id: "25002366262773827"
152
- }),
153
- request_id: ++ctx.wsReqNumber,
102
+ app_id: ctx.appID || '2220391788200892',
103
+ payload: { epoch_id: parseInt(utils.generateOfflineThreadingID()), tasks: [query], version_id: "24631415369801570" },
104
+ request_id: ctx.wsReqNumber,
154
105
  type: 3
155
106
  };
107
+ context.payload = JSON.stringify(context.payload);
156
108
 
157
109
  ctx.mqttClient.publish('/ls_req', JSON.stringify(context), { qos: 1, retain: false }, (err) => {
158
110
  if (err) return _callback(err);
159
- return _callback(null, {
111
+
112
+ const gcmemberInfo = {
160
113
  type: "gc_member_update",
161
- threadID, userIDs: [userToRemove], action,
162
- senderID: ctx.userID, BotID: ctx.userID, timestamp: Date.now()
163
- });
114
+ threadID: threadID,
115
+ userIDs: finalUsers,
116
+ action: action,
117
+ senderID: ctx.userID,
118
+ BotID: ctx.userID,
119
+ timestamp: Date.now(),
120
+ };
121
+ return _callback(null, gcmemberInfo);
164
122
  });
165
- }
166
-
167
123
  } catch (err) {
168
- utils.error("gcmember", err);
169
- _callback(err instanceof Error ? err : new 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."));
170
126
  }
171
127
 
172
128
  return returnPromise;
@@ -91,7 +91,7 @@ module.exports = function (defaultFuncs, api, ctx) {
91
91
  };
92
92
 
93
93
  const context = {
94
- app_id: '2220391788200892',
94
+ app_id: ctx.appID || '2220391788200892',
95
95
  payload: {
96
96
  epoch_id: parseInt(utils.generateOfflineThreadingID()),
97
97
  tasks: [query],
@@ -74,33 +74,36 @@ module.exports = function (defaultFuncs, api, ctx) {
74
74
  }
75
75
 
76
76
  const isAdminStatus = action === 'admin' ? 1 : 0;
77
- if (typeof ctx.wsReqNumber !== "number") ctx.wsReqNumber = 0;
77
+ ctx.wsReqNumber = (ctx.wsReqNumber || 0) + 1;
78
+ ctx.wsTaskNumber = (ctx.wsTaskNumber || 0) + 1;
78
79
 
79
- // Dongdev pattern: task_id starts at 1 (index + 1), fire-and-forget publish
80
+ const queryPayload = {
81
+ thread_key: parseInt(threadID),
82
+ contact_id: parseInt(userID),
83
+ is_admin: isAdminStatus
84
+ };
85
+ const query = {
86
+ failure_count: null,
87
+ label: "25",
88
+ payload: JSON.stringify(queryPayload),
89
+ queue_name: "admin_status",
90
+ task_id: ctx.wsTaskNumber
91
+ };
80
92
  const context = {
81
- app_id: '2220391788200892',
82
- payload: JSON.stringify({
93
+ app_id: ctx.appID || '2220391788200892',
94
+ payload: {
83
95
  epoch_id: parseInt(utils.generateOfflineThreadingID()),
84
- tasks: [{
85
- failure_count: null,
86
- label: "25",
87
- payload: JSON.stringify({
88
- thread_key: threadID,
89
- contact_id: userID,
90
- is_admin: isAdminStatus
91
- }),
92
- queue_name: "admin_status",
93
- task_id: 1
94
- }],
95
- version_id: "8798795233522156"
96
- }),
97
- request_id: ++ctx.wsReqNumber,
96
+ tasks: [query],
97
+ version_id: "24631415369801570"
98
+ },
99
+ request_id: ctx.wsReqNumber,
98
100
  type: 3
99
101
  };
102
+ context.payload = JSON.stringify(context.payload);
100
103
 
101
104
  ctx.mqttClient.publish('/ls_req', JSON.stringify(context), { qos: 1, retain: false }, (err) => {
102
105
  if (err) return _callback(err);
103
- return _callback(null, {
106
+ const gcruleInfo = {
104
107
  type: "gc_rule_update",
105
108
  threadID: threadID,
106
109
  userID: userID,
@@ -108,7 +111,8 @@ module.exports = function (defaultFuncs, api, ctx) {
108
111
  senderID: ctx.userID,
109
112
  BotID: ctx.userID,
110
113
  timestamp: Date.now(),
111
- });
114
+ };
115
+ return _callback(null, gcruleInfo);
112
116
  });
113
117
 
114
118
  } catch (err) {
@@ -107,12 +107,6 @@ async function listenMqtt(defaultFuncs, api, ctx, globalCallback, scheduleReconn
107
107
  const sessionID = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER) + 1;
108
108
  const cid = ctx.clientID;
109
109
  const cachedUA = ctx.globalOptions.cachedUserAgent || ctx.globalOptions.userAgent;
110
- // Use app_id from Facebook's own page data (populated by buildAPI) when
111
- // available. Hard-coded fallback is the Messenger web app ID.
112
- const rawAppId = ctx.appID || ctx.mqttAppID || ctx.userAppID;
113
- // Coerce to number safely — Number() of a non-numeric string yields NaN which
114
- // serialises as null in JSON and breaks the MQTT CONNECT packet.
115
- const mqttAid = rawAppId && !isNaN(Number(rawAppId)) ? Number(rawAppId) : 219994525426954;
116
110
  const username = {
117
111
  u: ctx.userID,
118
112
  s: sessionID,
@@ -120,7 +114,7 @@ async function listenMqtt(defaultFuncs, api, ctx, globalCallback, scheduleReconn
120
114
  fg: false,
121
115
  d: cid,
122
116
  ct: 'websocket',
123
- aid: mqttAid,
117
+ aid: 219994525426954,
124
118
  aids: null,
125
119
  mqtt_sid: '',
126
120
  cp: 3,
@@ -94,7 +94,7 @@ module.exports = function (defaultFuncs, api, ctx) {
94
94
  });
95
95
 
96
96
  const envelope = {
97
- app_id: "2220391788200892",
97
+ app_id: String(ctx.appID || ctx.mqttAppID || "2220391788200892"),
98
98
  payload: JSON.stringify({
99
99
  epoch_id: generateEpochId(),
100
100
  tasks: [task],
@@ -54,7 +54,7 @@ module.exports = function (defaultFuncs, api, ctx) {
54
54
 
55
55
  const epoch_id = parseInt(utils.generateOfflineThreadingID());
56
56
  const version_id = "9523201934447612";
57
- const app_id = "2220391788200892";
57
+ const app_id = String(ctx.appID || ctx.mqttAppID || "2220391788200892");
58
58
 
59
59
  const createMqttRequest = (tasks, increment = 0) => ({
60
60
  app_id,