@lazyneoaz/nkxchat 1.0.0

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 (123) hide show
  1. package/LICENSE +3 -0
  2. package/README.md +199 -0
  3. package/examples/login-with-cookies.js +102 -0
  4. package/examples/verify.js +70 -0
  5. package/index.js +2 -0
  6. package/package.json +84 -0
  7. package/src/apis/addExternalModule.js +24 -0
  8. package/src/apis/addUserToGroup.js +108 -0
  9. package/src/apis/changeAdminStatus.js +148 -0
  10. package/src/apis/changeArchivedStatus.js +61 -0
  11. package/src/apis/changeAvatar.js +103 -0
  12. package/src/apis/changeBio.js +69 -0
  13. package/src/apis/changeBlockedStatus.js +54 -0
  14. package/src/apis/changeGroupImage.js +136 -0
  15. package/src/apis/changeThreadColor.js +116 -0
  16. package/src/apis/changeThreadEmoji.js +53 -0
  17. package/src/apis/comment.js +207 -0
  18. package/src/apis/createAITheme.js +129 -0
  19. package/src/apis/createNewGroup.js +79 -0
  20. package/src/apis/createPoll.js +73 -0
  21. package/src/apis/deleteMessage.js +44 -0
  22. package/src/apis/deleteThread.js +52 -0
  23. package/src/apis/editMessage.js +70 -0
  24. package/src/apis/emoji.js +124 -0
  25. package/src/apis/enableAutoSaveAppState.js +69 -0
  26. package/src/apis/fetchThemeData.js +113 -0
  27. package/src/apis/follow.js +81 -0
  28. package/src/apis/forwardAttachment.js +178 -0
  29. package/src/apis/forwardMessage.js +52 -0
  30. package/src/apis/friend.js +243 -0
  31. package/src/apis/gcmember.js +122 -0
  32. package/src/apis/gcname.js +123 -0
  33. package/src/apis/gcrule.js +119 -0
  34. package/src/apis/getAccess.js +111 -0
  35. package/src/apis/getBotInfo.js +88 -0
  36. package/src/apis/getBotInitialData.js +43 -0
  37. package/src/apis/getEmojiUrl.js +40 -0
  38. package/src/apis/getFriendsList.js +79 -0
  39. package/src/apis/getMessage.js +423 -0
  40. package/src/apis/getTheme.js +123 -0
  41. package/src/apis/getThemeInfo.js +116 -0
  42. package/src/apis/getThemePictures.js +87 -0
  43. package/src/apis/getThreadColors.js +119 -0
  44. package/src/apis/getThreadHistory.js +239 -0
  45. package/src/apis/getThreadInfo.js +267 -0
  46. package/src/apis/getThreadList.js +232 -0
  47. package/src/apis/getThreadPictures.js +58 -0
  48. package/src/apis/getUserID.js +117 -0
  49. package/src/apis/getUserInfo.js +513 -0
  50. package/src/apis/getUserInfoV2.js +146 -0
  51. package/src/apis/handleFriendRequest.js +66 -0
  52. package/src/apis/handleMessageRequest.js +50 -0
  53. package/src/apis/httpGet.js +63 -0
  54. package/src/apis/httpPost.js +89 -0
  55. package/src/apis/httpPostFormData.js +69 -0
  56. package/src/apis/listenMqtt.js +924 -0
  57. package/src/apis/listenSpeed.js +178 -0
  58. package/src/apis/logout.js +63 -0
  59. package/src/apis/markAsDelivered.js +47 -0
  60. package/src/apis/markAsRead.js +95 -0
  61. package/src/apis/markAsReadAll.js +40 -0
  62. package/src/apis/markAsSeen.js +70 -0
  63. package/src/apis/mqttDeltaValue.js +252 -0
  64. package/src/apis/muteThread.js +45 -0
  65. package/src/apis/nickname.js +132 -0
  66. package/src/apis/notes.js +163 -0
  67. package/src/apis/pinMessage.js +150 -0
  68. package/src/apis/produceMetaTheme.js +160 -0
  69. package/src/apis/realtime.js +182 -0
  70. package/src/apis/refreshFb_dtsg.js +94 -0
  71. package/src/apis/removeUserFromGroup.js +117 -0
  72. package/src/apis/resolvePhotoUrl.js +58 -0
  73. package/src/apis/searchForThread.js +154 -0
  74. package/src/apis/sendEffect.js +306 -0
  75. package/src/apis/sendMessage.js +353 -0
  76. package/src/apis/sendMessageMqtt.js +255 -0
  77. package/src/apis/sendTypingIndicator.js +40 -0
  78. package/src/apis/setMessageReaction.js +27 -0
  79. package/src/apis/setMessageReactionMqtt.js +61 -0
  80. package/src/apis/setPostReaction.js +118 -0
  81. package/src/apis/setThreadTheme.js +210 -0
  82. package/src/apis/setThreadThemeMqtt.js +94 -0
  83. package/src/apis/setTitle.js +26 -0
  84. package/src/apis/share.js +106 -0
  85. package/src/apis/shareContact.js +66 -0
  86. package/src/apis/stickers.js +257 -0
  87. package/src/apis/story.js +181 -0
  88. package/src/apis/theme.js +233 -0
  89. package/src/apis/unfriend.js +47 -0
  90. package/src/apis/unsendMessage.js +17 -0
  91. package/src/apis/uploadAttachment.js +87 -0
  92. package/src/database/appStateBackup.js +189 -0
  93. package/src/database/models/index.js +56 -0
  94. package/src/database/models/thread.js +31 -0
  95. package/src/database/models/user.js +32 -0
  96. package/src/database/threadData.js +101 -0
  97. package/src/database/userData.js +90 -0
  98. package/src/engine/client.js +92 -0
  99. package/src/engine/models/buildAPI.js +118 -0
  100. package/src/engine/models/loginHelper.js +492 -0
  101. package/src/engine/models/setOptions.js +88 -0
  102. package/src/types/index.d.ts +498 -0
  103. package/src/utils/antiSuspension.js +516 -0
  104. package/src/utils/auth-helpers.js +149 -0
  105. package/src/utils/autoReLogin.js +237 -0
  106. package/src/utils/axios.js +368 -0
  107. package/src/utils/cache.js +54 -0
  108. package/src/utils/clients.js +279 -0
  109. package/src/utils/constants.js +525 -0
  110. package/src/utils/formatters/data/formatAttachment.js +370 -0
  111. package/src/utils/formatters/data/formatDelta.js +109 -0
  112. package/src/utils/formatters/index.js +159 -0
  113. package/src/utils/formatters/value/formatCookie.js +91 -0
  114. package/src/utils/formatters/value/formatDate.js +36 -0
  115. package/src/utils/formatters/value/formatID.js +16 -0
  116. package/src/utils/formatters.js +1369 -0
  117. package/src/utils/headers.js +235 -0
  118. package/src/utils/index.js +152 -0
  119. package/src/utils/monitoring.js +333 -0
  120. package/src/utils/rateLimiter.js +251 -0
  121. package/src/utils/tokenRefresh.js +285 -0
  122. package/src/utils/user-agents.js +238 -0
  123. package/src/utils/validation.js +157 -0
@@ -0,0 +1,148 @@
1
+ "use strict";
2
+
3
+ const utils = require('../utils');
4
+
5
+ function generateOfflineThreadingID() {
6
+ return Date.now().toString() + Math.floor(Math.random() * 1000000).toString();
7
+ }
8
+
9
+ function getType(obj) {
10
+ return Object.prototype.toString.call(obj).slice(8, -1);
11
+ }
12
+
13
+ module.exports = function (defaultFuncs, api, ctx) {
14
+ return function changeAdminStatus(threadID, adminID, adminStatus, callback) {
15
+ let resolveFunc = function() {};
16
+ let rejectFunc = function() {};
17
+ const returnPromise = new Promise(function(resolve, reject) {
18
+ resolveFunc = resolve;
19
+ rejectFunc = reject;
20
+ });
21
+
22
+ if (!callback) {
23
+ callback = function(err, data) {
24
+ if (err) return rejectFunc(err);
25
+ resolveFunc(data);
26
+ };
27
+ }
28
+
29
+ if (getType(threadID) !== "String") {
30
+ return callback({ error: "changeAdminStatus: threadID must be a string" });
31
+ }
32
+ if (getType(adminID) !== "String" && getType(adminID) !== "Array") {
33
+ return callback({ error: "changeAdminStatus: adminID must be a string or an array" });
34
+ }
35
+ if (getType(adminStatus) !== "Boolean") {
36
+ return callback({ error: "changeAdminStatus: adminStatus must be true or false" });
37
+ }
38
+
39
+ if (ctx.mqttClient) {
40
+ const tasks = [];
41
+ const isAdmin = adminStatus ? 1 : 0;
42
+ const epochID = generateOfflineThreadingID();
43
+
44
+ if (getType(adminID) === "Array") {
45
+ adminID.forEach((id, index) => {
46
+ tasks.push({
47
+ failure_count: null,
48
+ label: "25",
49
+ payload: JSON.stringify({
50
+ thread_key: threadID,
51
+ contact_id: id,
52
+ is_admin: isAdmin
53
+ }),
54
+ queue_name: "admin_status",
55
+ task_id: index + 1
56
+ });
57
+ });
58
+ } else {
59
+ tasks.push({
60
+ failure_count: null,
61
+ label: "25",
62
+ payload: JSON.stringify({
63
+ thread_key: threadID,
64
+ contact_id: adminID,
65
+ is_admin: isAdmin
66
+ }),
67
+ queue_name: "admin_status",
68
+ task_id: 1
69
+ });
70
+ }
71
+
72
+ let count_req = 0;
73
+ const form = JSON.stringify({
74
+ app_id: "2220391788200892",
75
+ payload: JSON.stringify({
76
+ epoch_id: epochID,
77
+ tasks: tasks,
78
+ version_id: "8798795233522156"
79
+ }),
80
+ request_id: ++count_req,
81
+ type: 3
82
+ });
83
+
84
+ ctx.mqttClient.publish("/ls_req", form, {}, (err, _packet) => {
85
+ if (err) {
86
+ utils.error("changeAdminStatus (MQTT)", err);
87
+ return callback(err);
88
+ } else {
89
+ utils.log("Admin status changed successfully via MQTT");
90
+ return callback(null, { success: true });
91
+ }
92
+ });
93
+ } else {
94
+ utils.warn("MQTT client not available, using HTTP fallback for changeAdminStatus");
95
+ const tasks = [];
96
+ const epochID = generateOfflineThreadingID();
97
+
98
+ if (getType(adminID) === "Array") {
99
+ adminID.forEach((id, index) => {
100
+ tasks.push({
101
+ label: '25',
102
+ payload: JSON.stringify({ thread_key: threadID, contact_id: id, is_admin: adminStatus }),
103
+ queue_name: 'admin_status',
104
+ task_id: index + 1,
105
+ failure_count: null
106
+ });
107
+ });
108
+ } else {
109
+ tasks.push({
110
+ label: '25',
111
+ payload: JSON.stringify({ thread_key: threadID, contact_id: adminID, is_admin: adminStatus }),
112
+ queue_name: 'admin_status',
113
+ task_id: 1,
114
+ failure_count: null
115
+ });
116
+ }
117
+
118
+ const form = {
119
+ fb_dtsg: ctx.fb_dtsg,
120
+ request_id: 1,
121
+ type: 3,
122
+ payload: {
123
+ version_id: '3816854585040595',
124
+ tasks: tasks,
125
+ epoch_id: epochID,
126
+ data_trace_id: null
127
+ },
128
+ app_id: '772021112871879'
129
+ };
130
+
131
+ form.payload = JSON.stringify(form.payload);
132
+
133
+ defaultFuncs
134
+ .post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, form)
135
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
136
+ .then(() => {
137
+ utils.log("Admin status changed successfully via HTTP");
138
+ callback(null, { success: true });
139
+ })
140
+ .catch(err => {
141
+ utils.error("changeAdminStatus (HTTP)", err);
142
+ callback(err);
143
+ });
144
+ }
145
+
146
+ return returnPromise;
147
+ };
148
+ };
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+
3
+ const utils = require('../utils');
4
+
5
+ module.exports = (defaultFuncs, api, ctx) => {
6
+ return async function changeArchivedStatus(threadIDs, archive, callback) {
7
+ let resolveFunc = () => {};
8
+ let rejectFunc = () => {};
9
+ const returnPromise = new Promise((resolve, reject) => {
10
+ resolveFunc = resolve;
11
+ rejectFunc = reject;
12
+ });
13
+
14
+ if (!callback) {
15
+ callback = (err, result) => {
16
+ if (err) return rejectFunc(err);
17
+ resolveFunc(result);
18
+ };
19
+ }
20
+
21
+ try {
22
+ if (utils.getType(archive) === "Function") {
23
+ callback = archive;
24
+ archive = true;
25
+ }
26
+
27
+ if (utils.getType(archive) !== "Boolean") {
28
+ throw new Error("archive parameter must be a boolean");
29
+ }
30
+
31
+ if (!Array.isArray(threadIDs)) {
32
+ threadIDs = [threadIDs];
33
+ }
34
+
35
+ const form = {
36
+ should_archive: archive
37
+ };
38
+
39
+ threadIDs.forEach(id => {
40
+ form[`thread_fbids[${id}]`] = true;
41
+ });
42
+
43
+ const res = await defaultFuncs.post(
44
+ "https://www.facebook.com/ajax/mercury/change_archived_status.php",
45
+ ctx.jar,
46
+ form
47
+ ).then(utils.parseAndCheckLogin(ctx, defaultFuncs));
48
+
49
+ if (res && res.error) {
50
+ throw res;
51
+ }
52
+
53
+ callback(null, { success: true });
54
+ } catch (err) {
55
+ utils.error("changeArchivedStatus", err);
56
+ callback(err);
57
+ }
58
+
59
+ return returnPromise;
60
+ };
61
+ };
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+
3
+ const utils = require('../utils');
4
+
5
+ module.exports = (defaultFuncs, api, ctx) => {
6
+ async function handleUpload(image) {
7
+ const form = {
8
+ profile_id: ctx.userID,
9
+ photo_source: 57,
10
+ av: ctx.userID,
11
+ file: image
12
+ };
13
+
14
+ return defaultFuncs.postFormData(
15
+ "https://www.facebook.com/profile/picture/upload/",
16
+ ctx.jar,
17
+ form,
18
+ {}
19
+ ).then(utils.parseAndCheckLogin(ctx, defaultFuncs))
20
+ .then(resData => {
21
+ if (resData.error) throw resData;
22
+ return resData;
23
+ });
24
+ }
25
+
26
+ return async function changeAvatar(image, caption, timestamp, callback) {
27
+ let resolveFunc = () => {};
28
+ let rejectFunc = () => {};
29
+ const returnPromise = new Promise((resolve, reject) => {
30
+ resolveFunc = resolve;
31
+ rejectFunc = reject;
32
+ });
33
+
34
+ if (!timestamp && utils.getType(caption) === "Number") {
35
+ timestamp = caption;
36
+ caption = "";
37
+ }
38
+
39
+ if (!timestamp && !callback && (utils.getType(caption) === "Function")) {
40
+ callback = caption;
41
+ caption = "";
42
+ timestamp = null;
43
+ }
44
+
45
+ if (!callback) {
46
+ callback = (err, data) => {
47
+ if (err) return rejectFunc(err);
48
+ resolveFunc(data);
49
+ };
50
+ }
51
+
52
+ try {
53
+ if (!utils.isReadableStream(image)) {
54
+ throw new Error("Image is not a readable stream");
55
+ }
56
+
57
+ const payload = await handleUpload(image);
58
+
59
+ const form = {
60
+ av: ctx.i_userID || ctx.userID,
61
+ fb_api_req_friendly_name: "ProfileCometProfilePictureSetMutation",
62
+ fb_api_caller_class: "RelayModern",
63
+ doc_id: "5066134240065849",
64
+ variables: JSON.stringify({
65
+ input: {
66
+ caption: caption || "",
67
+ existing_photo_id: payload.payload.fbid,
68
+ expiration_time: timestamp,
69
+ profile_id: ctx.i_userID || ctx.userID,
70
+ profile_pic_method: "EXISTING",
71
+ profile_pic_source: "TIMELINE",
72
+ scaled_crop_rect: {
73
+ height: 1,
74
+ width: 1,
75
+ x: 0,
76
+ y: 0
77
+ },
78
+ skip_cropping: true,
79
+ actor_id: ctx.i_userID || ctx.userID,
80
+ client_mutation_id: Math.round(Math.random() * 19).toString()
81
+ },
82
+ isPage: false,
83
+ isProfile: true,
84
+ scale: 3
85
+ })
86
+ };
87
+
88
+ const res = await defaultFuncs.post("https://www.facebook.com/api/graphql/", ctx.jar, form)
89
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs));
90
+
91
+ if (res.errors) {
92
+ throw res;
93
+ }
94
+
95
+ callback(null, res[0].data.profile_picture_set);
96
+ } catch (err) {
97
+ utils.error("changeAvatar", err);
98
+ callback(err);
99
+ }
100
+
101
+ return returnPromise;
102
+ };
103
+ };
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+
3
+ const utils = require('../utils');
4
+
5
+ module.exports = (defaultFuncs, api, ctx) => {
6
+ return async function changeBio(bio, publish, callback) {
7
+ let resolveFunc = () => {};
8
+ let rejectFunc = () => {};
9
+ const returnPromise = new Promise((resolve, reject) => {
10
+ resolveFunc = resolve;
11
+ rejectFunc = reject;
12
+ });
13
+
14
+ if (!callback) {
15
+ if (utils.getType(publish) === "Function") {
16
+ callback = publish;
17
+ publish = false;
18
+ } else {
19
+ callback = (err) => {
20
+ if (err) return rejectFunc(err);
21
+ resolveFunc();
22
+ };
23
+ }
24
+ }
25
+
26
+ if (utils.getType(publish) !== "Boolean") {
27
+ publish = false;
28
+ }
29
+
30
+ if (utils.getType(bio) !== "String") {
31
+ bio = "";
32
+ publish = false;
33
+ }
34
+
35
+ try {
36
+ const form = {
37
+ fb_api_caller_class: "RelayModern",
38
+ fb_api_req_friendly_name: "ProfileCometSetBioMutation",
39
+ doc_id: "2725043627607610",
40
+ variables: JSON.stringify({
41
+ input: {
42
+ bio: bio,
43
+ publish_bio_feed_story: publish,
44
+ actor_id: ctx.i_userID || ctx.userID,
45
+ client_mutation_id: Math.round(Math.random() * 1024).toString()
46
+ },
47
+ hasProfileTileViewID: false,
48
+ profileTileViewID: null,
49
+ scale: 1
50
+ }),
51
+ av: ctx.i_userID || ctx.userID
52
+ };
53
+
54
+ const res = await defaultFuncs.post("https://www.facebook.com/api/graphql/", ctx.jar, form)
55
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs));
56
+
57
+ if (res.errors) {
58
+ throw res;
59
+ }
60
+
61
+ callback(null, { success: true });
62
+ } catch (err) {
63
+ utils.error("changeBio", err);
64
+ callback(err);
65
+ }
66
+
67
+ return returnPromise;
68
+ };
69
+ };
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+
3
+ const utils = require('../utils');
4
+
5
+ module.exports = (defaultFuncs, api, ctx) => {
6
+ return async function changeBlockedStatus(userID, block, callback) {
7
+ let resolveFunc = () => {};
8
+ let rejectFunc = () => {};
9
+ const returnPromise = new Promise((resolve, reject) => {
10
+ resolveFunc = resolve;
11
+ rejectFunc = reject;
12
+ });
13
+
14
+ if (!callback) {
15
+ callback = (err, result) => {
16
+ if (err) return rejectFunc(err);
17
+ resolveFunc(result);
18
+ };
19
+ }
20
+
21
+ try {
22
+ if (utils.getType(block) === "Function") {
23
+ callback = block;
24
+ block = true;
25
+ }
26
+
27
+ if (utils.getType(block) !== "Boolean") {
28
+ throw new Error("block parameter must be a boolean");
29
+ }
30
+
31
+ const form = {
32
+ fbid: userID,
33
+ block: block
34
+ };
35
+
36
+ const res = await defaultFuncs.post(
37
+ "https://www.facebook.com/ajax/profile/manage_blocking.php",
38
+ ctx.jar,
39
+ form
40
+ ).then(utils.parseAndCheckLogin(ctx, defaultFuncs));
41
+
42
+ if (res && res.error) {
43
+ throw res;
44
+ }
45
+
46
+ return callback(null, { success: true, blocked: block });
47
+ } catch (err) {
48
+ utils.error("changeBlockedStatus", err);
49
+ callback(err);
50
+ }
51
+
52
+ return returnPromise;
53
+ };
54
+ };
@@ -0,0 +1,136 @@
1
+ "use strict";
2
+
3
+ const utils = require('../utils');
4
+
5
+ module.exports = (defaultFuncs, api, ctx) => {
6
+ async function handleUpload(image) {
7
+ const form = {
8
+ images_only: "true",
9
+ "attachment[]": image
10
+ };
11
+ return defaultFuncs
12
+ .postFormData("https://upload.facebook.com/ajax/mercury/upload.php", ctx.jar, form, {})
13
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
14
+ .then(resData => {
15
+ if (resData.error) throw resData;
16
+ return resData.payload.metadata[0];
17
+ });
18
+ }
19
+
20
+ return async function changeGroupImage(image, threadID, callback) {
21
+ let resolveFunc = () => {};
22
+ let rejectFunc = () => {};
23
+ const returnPromise = new Promise((resolve, reject) => {
24
+ resolveFunc = resolve;
25
+ rejectFunc = reject;
26
+ });
27
+
28
+ if (!callback) {
29
+ callback = (err, result) => {
30
+ if (err) return rejectFunc(err);
31
+ resolveFunc(result);
32
+ };
33
+ }
34
+
35
+ try {
36
+ if (!ctx.mqttClient) {
37
+ throw new Error("Not connected to MQTT. Please use listenMqtt first.");
38
+ }
39
+
40
+ if (!threadID || typeof threadID !== "string") {
41
+ throw new Error("Invalid threadID");
42
+ }
43
+
44
+ if (!utils.isReadableStream(image)) {
45
+ throw new Error("image must be a readable stream");
46
+ }
47
+
48
+ const reqID = ++ctx.wsReqNumber;
49
+ const taskID = ++ctx.wsTaskNumber;
50
+
51
+ let responseHandled = false;
52
+ const onResponse = (topic, message) => {
53
+ if (topic !== "/ls_resp" || responseHandled) return;
54
+ let jsonMsg;
55
+ try {
56
+ jsonMsg = JSON.parse(message.toString());
57
+ jsonMsg.payload = JSON.parse(jsonMsg.payload);
58
+ } catch (err) {
59
+ return;
60
+ }
61
+ if (jsonMsg.request_id !== reqID) return;
62
+ responseHandled = true;
63
+ clearTimeout(timeout);
64
+ ctx.mqttClient.removeListener("message", onResponse);
65
+ callback(null, { success: true, response: jsonMsg.payload });
66
+ resolveFunc({ success: true, response: jsonMsg.payload });
67
+ };
68
+
69
+ const timeout = setTimeout(() => {
70
+ if (!responseHandled) {
71
+ responseHandled = true;
72
+ ctx.mqttClient.removeListener("message", onResponse);
73
+ const err = new Error("MQTT request timeout");
74
+ callback(err);
75
+ rejectFunc(err);
76
+ }
77
+ }, 30000);
78
+
79
+ ctx.mqttClient.on("message", onResponse);
80
+
81
+ const payload = await handleUpload(image);
82
+ const imageID = payload.image_id;
83
+
84
+ const taskPayload = {
85
+ thread_key: threadID,
86
+ image_id: imageID,
87
+ sync_group: 1
88
+ };
89
+
90
+ const mqttPayload = {
91
+ epoch_id: utils.generateOfflineThreadingID(),
92
+ tasks: [
93
+ {
94
+ failure_count: null,
95
+ label: "37",
96
+ payload: JSON.stringify(taskPayload),
97
+ queue_name: "thread_image",
98
+ task_id: taskID
99
+ }
100
+ ],
101
+ version_id: "8798795233522156"
102
+ };
103
+
104
+ const request = {
105
+ app_id: "2220391788200892",
106
+ payload: JSON.stringify(mqttPayload),
107
+ request_id: reqID,
108
+ type: 3
109
+ };
110
+
111
+ ctx.mqttClient.publish("/ls_req", JSON.stringify(request), {
112
+ qos: 1,
113
+ retain: false
114
+ }, (err) => {
115
+ if (err && !responseHandled) {
116
+ responseHandled = true;
117
+ clearTimeout(timeout);
118
+ ctx.mqttClient.removeListener("message", onResponse);
119
+ callback(err);
120
+ rejectFunc(err);
121
+ }
122
+ });
123
+ } catch (err) {
124
+ if (!responseHandled) {
125
+ responseHandled = true;
126
+ clearTimeout(timeout);
127
+ ctx.mqttClient.removeListener("message", onResponse);
128
+ utils.error("changeGroupImage", err);
129
+ callback(err);
130
+ rejectFunc(err);
131
+ }
132
+ }
133
+
134
+ return returnPromise;
135
+ };
136
+ };
@@ -0,0 +1,116 @@
1
+ "use strict";
2
+
3
+ const utils = require('../utils');
4
+
5
+ module.exports = (defaultFuncs, api, ctx) => {
6
+ return async function changeThreadColor(color, threadID, callback) {
7
+ let reqID = ++ctx.wsReqNumber;
8
+ let resolveFunc = () => {};
9
+ let rejectFunc = () => {};
10
+ const returnPromise = new Promise((resolve, reject) => {
11
+ resolveFunc = resolve;
12
+ rejectFunc = reject;
13
+ });
14
+
15
+ if (!callback) {
16
+ callback = (err, data) => {
17
+ if (err) return rejectFunc(err);
18
+ resolveFunc(data);
19
+ };
20
+ }
21
+
22
+ try {
23
+ if (!ctx.mqttClient) {
24
+ throw new Error("Not connected to MQTT. Please use listenMqtt first.");
25
+ }
26
+
27
+ const content = {
28
+ app_id: "2220391788200892",
29
+ payload: JSON.stringify({
30
+ data_trace_id: null,
31
+ epoch_id: parseInt(utils.generateOfflineThreadingID()),
32
+ tasks: [
33
+ {
34
+ failure_count: null,
35
+ label: "43",
36
+ payload: JSON.stringify({
37
+ thread_key: threadID,
38
+ theme_fbid: color,
39
+ source: null,
40
+ sync_group: 1,
41
+ payload: null
42
+ }),
43
+ queue_name: "thread_theme",
44
+ task_id: ++ctx.wsTaskNumber
45
+ }
46
+ ],
47
+ version_id: "8798795233522156"
48
+ }),
49
+ request_id: reqID,
50
+ type: 3
51
+ };
52
+
53
+ let responseHandled = false;
54
+
55
+ const handleRes = (topic, message) => {
56
+ if (responseHandled) return;
57
+ if (topic !== "/ls_resp") return;
58
+ let jsonMsg;
59
+ try {
60
+ jsonMsg = JSON.parse(message.toString());
61
+ jsonMsg.payload = JSON.parse(jsonMsg.payload);
62
+ } catch (err) {
63
+ return;
64
+ }
65
+ if (jsonMsg.request_id !== reqID) return;
66
+ responseHandled = true;
67
+ clearTimeout(timeout);
68
+ ctx.mqttClient.removeListener("message", handleRes);
69
+ try {
70
+ const msgID = jsonMsg.payload.step[1][2][2][1][2];
71
+ const msgReplace = jsonMsg.payload.step[1][2][2][1][4];
72
+ const bodies = {
73
+ body: msgReplace,
74
+ messageID: msgID
75
+ };
76
+ callback(null, bodies);
77
+ resolveFunc(bodies);
78
+ } catch (err) {
79
+ callback(null, { success: true });
80
+ resolveFunc({ success: true });
81
+ }
82
+ };
83
+
84
+ const timeout = setTimeout(() => {
85
+ if (!responseHandled) {
86
+ responseHandled = true;
87
+ ctx.mqttClient.removeListener("message", handleRes);
88
+ const err = new Error("MQTT request timeout");
89
+ callback(err);
90
+ rejectFunc(err);
91
+ }
92
+ }, 30000);
93
+
94
+ ctx.mqttClient.on("message", handleRes);
95
+
96
+ ctx.mqttClient.publish("/ls_req", JSON.stringify(content), {
97
+ qos: 1,
98
+ retain: false
99
+ }, (err) => {
100
+ if (err && !responseHandled) {
101
+ responseHandled = true;
102
+ clearTimeout(timeout);
103
+ ctx.mqttClient.removeListener("message", handleRes);
104
+ callback(err);
105
+ rejectFunc(err);
106
+ }
107
+ });
108
+ } catch (err) {
109
+ utils.error("changeThreadColor", err);
110
+ callback(err);
111
+ rejectFunc(err);
112
+ }
113
+
114
+ return returnPromise;
115
+ };
116
+ };