@lazyneoaz/nkxchat 1.0.2 → 1.0.3

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/nkxchat",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
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.",
@@ -40,131 +40,6 @@ function resolveEffect(name) {
40
40
  return EFFECTS[key] ? EFFECTS[key].tag : name.toUpperCase();
41
41
  }
42
42
 
43
- function buildForm(effectTag, msgObj, threadID, ctx) {
44
- const messageAndOTID = utils.generateOfflineThreadingID();
45
- const timestamp = Date.now();
46
- const messagingTag = 'fb.messaging.effects.' + effectTag;
47
- const body = typeof msgObj === 'string' ? msgObj : (msgObj.body || '');
48
-
49
- const form = {
50
- client: 'mercury',
51
- action_type: 'ma-type:user-generated-message',
52
- author: 'fbid:' + ctx.userID,
53
- timestamp,
54
- timestamp_absolute: 'Today',
55
- timestamp_relative: utils.generateTimestampRelative(),
56
- timestamp_time_passed:'0',
57
- is_unread: false,
58
- is_cleared: false,
59
- is_forward: false,
60
- is_filtered_content: false,
61
- is_filtered_content_bh: false,
62
- is_filtered_content_account: false,
63
- is_filtered_content_quasar: false,
64
- is_filtered_content_invalid_app: false,
65
- is_spoof_warning: false,
66
- source: 'source:chat:web',
67
- 'source_tags[0]': 'source:chat',
68
- body,
69
- html_body: false,
70
- ui_push_phase: 'V3',
71
- status: '0',
72
- offline_threading_id: messageAndOTID,
73
- message_id: messageAndOTID,
74
- threading_id: utils.generateThreadingID(ctx.clientID),
75
- 'ephemeral_ttl_mode:':'0',
76
- manual_retry_cnt: '0',
77
- has_attachment: !!(msgObj && msgObj.sticker),
78
- signatureID: utils.getSignatureID(),
79
- has_lightweight_action: true,
80
- 'lightweight_action_attached[message_id]': messageAndOTID,
81
- 'lightweight_action_attached[messaging_tag]': messagingTag,
82
- };
83
-
84
- if (msgObj && msgObj.sticker) {
85
- form.sticker_id = msgObj.sticker;
86
- }
87
-
88
- if (typeof msgObj === 'object' && msgObj.reply_to_message_id) {
89
- form.replied_to_message_id = msgObj.reply_to_message_id;
90
- }
91
-
92
- const tid = String(threadID);
93
- const isGroup = tid.length >= 16;
94
- if (isGroup) {
95
- form.thread_fbid = tid;
96
- } else {
97
- form['specific_to_list[0]'] = 'fbid:' + tid;
98
- form['specific_to_list[1]'] = 'fbid:' + ctx.userID;
99
- form.other_user_fbid = tid;
100
- form.client_thread_id = 'root:' + messageAndOTID;
101
- }
102
-
103
- return { form, messageAndOTID, timestamp, messagingTag };
104
- }
105
-
106
- function buildMqttPayload(effectTag, msgObj, threadID, ctx) {
107
- const otid = utils.generateOfflineThreadingID();
108
- const epoch_id = utils.generateOfflineThreadingID();
109
- const timestamp = Date.now();
110
- const body = typeof msgObj === 'string' ? msgObj : (msgObj && msgObj.body || '');
111
- const tid = String(threadID);
112
-
113
- const sendPayload = {
114
- thread_id: tid,
115
- otid: otid.toString(),
116
- source: 0,
117
- send_type: 1,
118
- sync_group: 1,
119
- text: body,
120
- initiating_source: 1,
121
- skip_url_preview_gen: 0,
122
- lightweight_action_attached: {
123
- messaging_tag: 'fb.messaging.effects.' + effectTag,
124
- },
125
- };
126
-
127
- if (typeof msgObj === 'object' && msgObj && msgObj.sticker) {
128
- sendPayload.send_type = 2;
129
- sendPayload.sticker_id = msgObj.sticker;
130
- sendPayload.text = null;
131
- }
132
-
133
- const request_id = ++ctx.wsReqNumber;
134
- const content = {
135
- app_id: '2220391788200892',
136
- payload: JSON.stringify({
137
- tasks: [
138
- {
139
- label: '46',
140
- payload: JSON.stringify(sendPayload),
141
- queue_name: tid,
142
- task_id: ++ctx.wsTaskNumber,
143
- failure_count: null,
144
- },
145
- {
146
- label: '21',
147
- payload: JSON.stringify({
148
- thread_id: tid,
149
- last_read_watermark_ts: timestamp,
150
- sync_group: 1,
151
- }),
152
- queue_name: tid,
153
- task_id: ++ctx.wsTaskNumber,
154
- failure_count: null,
155
- },
156
- ],
157
- epoch_id,
158
- version_id: '6120284488008082',
159
- data_trace_id: null,
160
- }),
161
- request_id,
162
- type: 3,
163
- };
164
-
165
- return { content, request_id, otid, timestamp };
166
- }
167
-
168
43
  module.exports = function (defaultFuncs, api, ctx) {
169
44
  /**
170
45
  * Lists all available send effects.
@@ -214,68 +89,19 @@ module.exports = function (defaultFuncs, api, ctx) {
214
89
  const effectTag = resolveEffect(effectName);
215
90
  utils.log('sendEffect', `Effect "${effectTag}" → thread ${threadID}`);
216
91
 
217
- // ── Try HTTP first ───────────────────────────────────────────────────
218
- try {
219
- const { form, messageAndOTID, timestamp } = buildForm(effectTag, message, threadID, ctx);
220
-
221
- const resData = await defaultFuncs
222
- .post('https://www.facebook.com/messaging/send/', ctx.jar, form, { ...ctx, requestThreadID: String(threadID) })
223
- .then(utils.parseAndCheckLogin(ctx, defaultFuncs));
224
-
225
- if (!resData) throw new Error('Empty response from messaging/send');
226
- if (resData.error) throw new Error(JSON.stringify(resData));
227
-
228
- const actions = (resData.payload && resData.payload.actions) || [];
229
- const msgInfo = actions.reduce((p, v) => ({
230
- threadID: v.thread_fbid || p.threadID,
231
- messageID: v.message_id || p.messageID,
232
- timestamp: v.timestamp || p.timestamp,
233
- }), { threadID, messageID: messageAndOTID, timestamp });
234
-
235
- return callback(null, { ...msgInfo, effect: effectTag, method: 'http' });
236
- } catch (httpErr) {
237
- utils.warn('sendEffect', `HTTP failed: ${httpErr.message}. Falling back to MQTT.`);
238
- }
239
-
240
- // ── MQTT fallback ────────────────────────────────────────────────────
92
+ // Effects only work via MQTT (label-46 task). The legacy /messaging/send/
93
+ // HTTP endpoint silently drops lightweight_action_attached fields and
94
+ // delivers a plain message with no animation.
241
95
  if (!ctx.mqttClient) {
242
96
  throw new Error('MQTT is not connected. Call api.listenMqtt() first.');
243
97
  }
244
98
 
245
- const { content, request_id, otid, timestamp } = buildMqttPayload(effectTag, message, threadID, ctx);
99
+ const msg = typeof message === 'string'
100
+ ? { body: message, effect: effectTag }
101
+ : { ...message, effect: effectTag };
246
102
 
247
- await new Promise((res, rej) => {
248
- let done = false;
249
- const timer = setTimeout(() => {
250
- if (done) return;
251
- done = true;
252
- ctx.mqttClient.removeListener('message', onMsg);
253
- rej(new Error('MQTT effect send timeout'));
254
- }, 15000);
255
-
256
- const onMsg = (topic, raw) => {
257
- if (topic !== '/ls_resp') return;
258
- let parsed;
259
- try { parsed = JSON.parse(raw.toString()); parsed.payload = JSON.parse(parsed.payload); } catch { return; }
260
- if (parsed.request_id !== request_id) return;
261
- if (done) return;
262
- done = true;
263
- clearTimeout(timer);
264
- ctx.mqttClient.removeListener('message', onMsg);
265
- callback(null, { threadID, messageID: otid, timestamp, effect: effectTag, method: 'mqtt' });
266
- res();
267
- };
268
-
269
- ctx.mqttClient.on('message', onMsg);
270
- ctx.mqttClient.publish('/ls_req', JSON.stringify(content), { qos: 1, retain: false }, (err) => {
271
- if (err && !done) {
272
- done = true;
273
- clearTimeout(timer);
274
- ctx.mqttClient.removeListener('message', onMsg);
275
- rej(err);
276
- }
277
- });
278
- });
103
+ const result = await api.sendMessageMqtt(msg, threadID);
104
+ return callback(null, { ...result, effect: effectTag, method: 'mqtt' });
279
105
 
280
106
  } catch (err) {
281
107
  utils.error('sendEffect', err.message || err);