@dongdev/fca-unofficial 1.0.19 → 2.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 (128) hide show
  1. package/.gitattributes +1 -0
  2. package/CHANGELOG.md +30 -27
  3. package/DOCS.md +727 -592
  4. package/README.md +94 -97
  5. package/func/logger.js +112 -0
  6. package/html.html +474 -0
  7. package/index.js +1 -379
  8. package/module/config.js +26 -0
  9. package/module/login.js +45 -0
  10. package/module/loginHelper.js +634 -0
  11. package/module/options.js +49 -0
  12. package/package.json +6 -39
  13. package/src/api/action/addExternalModule.js +25 -0
  14. package/src/api/action/changeAvatar.js +136 -0
  15. package/src/api/action/changeBio.js +76 -0
  16. package/src/api/action/getCurrentUserID.js +7 -0
  17. package/src/api/action/handleFriendRequest.js +57 -0
  18. package/src/api/action/logout.js +75 -0
  19. package/src/{refreshFb_dtsg.js → api/action/refreshFb_dtsg.js} +8 -8
  20. package/src/api/action/setPostReaction.js +107 -0
  21. package/src/api/action/unfriend.js +55 -0
  22. package/src/api/http/httpGet.js +65 -0
  23. package/src/api/http/httpPost.js +65 -0
  24. package/src/{postFormData.js → api/http/postFormData.js} +10 -10
  25. package/src/api/messaging/addUserToGroup.js +69 -0
  26. package/src/api/messaging/changeAdminStatus.js +103 -0
  27. package/src/api/messaging/changeArchivedStatus.js +55 -0
  28. package/src/api/messaging/changeBlockedStatus.js +49 -0
  29. package/src/api/messaging/changeGroupImage.js +135 -0
  30. package/src/api/messaging/changeNickname.js +59 -0
  31. package/src/api/messaging/changeThreadColor.js +65 -0
  32. package/src/api/messaging/createNewGroup.js +88 -0
  33. package/src/api/messaging/createPoll.js +70 -0
  34. package/src/api/messaging/deleteMessage.js +56 -0
  35. package/src/api/messaging/deleteThread.js +56 -0
  36. package/src/api/messaging/forwardAttachment.js +60 -0
  37. package/src/api/messaging/getEmojiUrl.js +29 -0
  38. package/src/api/messaging/getFriendsList.js +83 -0
  39. package/src/api/messaging/getMessage.js +834 -0
  40. package/src/api/messaging/getThreadHistory.js +681 -0
  41. package/src/api/messaging/handleMessageRequest.js +65 -0
  42. package/src/api/messaging/markAsDelivered.js +57 -0
  43. package/src/api/messaging/markAsRead.js +88 -0
  44. package/src/api/messaging/markAsReadAll.js +50 -0
  45. package/src/api/messaging/markAsSeen.js +61 -0
  46. package/src/api/messaging/muteThread.js +51 -0
  47. package/src/api/messaging/removeUserFromGroup.js +79 -0
  48. package/src/api/messaging/resolvePhotoUrl.js +44 -0
  49. package/src/api/messaging/searchForThread.js +53 -0
  50. package/src/api/messaging/sendMessage.js +306 -0
  51. package/src/api/messaging/sendMessageMqtt.js +321 -0
  52. package/src/api/messaging/sendTypingIndicator.js +110 -0
  53. package/src/{setMessageReaction.js → api/messaging/setMessageReaction.js} +20 -20
  54. package/src/api/messaging/setTitle.js +90 -0
  55. package/src/api/messaging/shareContact.js +51 -0
  56. package/src/api/messaging/threadColors.js +131 -0
  57. package/src/api/messaging/unsendMessage.js +44 -0
  58. package/src/api/messaging/uploadAttachment.js +93 -0
  59. package/src/api/socket/detail/buildStream.js +100 -0
  60. package/src/{listenMqtt.js → api/socket/listenMqtt.js} +172 -256
  61. package/src/api/threads/changeThreadEmoji.js +55 -0
  62. package/src/api/threads/getThreadInfo.js +572 -0
  63. package/src/{getThreadList.js → api/threads/getThreadList.js} +110 -54
  64. package/src/api/threads/getThreadPictures.js +79 -0
  65. package/src/api/users/getUserID.js +66 -0
  66. package/src/api/users/getUserInfo.js +88 -0
  67. package/src/core/sendReqMqtt.js +63 -0
  68. package/{lib → src}/database/models/index.js +12 -10
  69. package/{lib → src}/database/models/thread.js +5 -5
  70. package/{lib → src}/database/threadData.js +19 -14
  71. package/src/utils/client.js +159 -0
  72. package/src/utils/constants.js +13 -0
  73. package/src/utils/format.js +60 -0
  74. package/src/utils/headers.js +41 -0
  75. package/src/utils/index.js +1497 -0
  76. package/src/utils/request.js +147 -0
  77. package/lib/logger.js +0 -96
  78. package/src/addExternalModule.js +0 -19
  79. package/src/addUserToGroup.js +0 -113
  80. package/src/changeAdminStatus.js +0 -79
  81. package/src/changeArchivedStatus.js +0 -55
  82. package/src/changeAvatar.js +0 -126
  83. package/src/changeBio.js +0 -77
  84. package/src/changeBlockedStatus.js +0 -47
  85. package/src/changeGroupImage.js +0 -132
  86. package/src/changeNickname.js +0 -59
  87. package/src/changeThreadColor.js +0 -65
  88. package/src/changeThreadEmoji.js +0 -55
  89. package/src/createNewGroup.js +0 -86
  90. package/src/createPoll.js +0 -71
  91. package/src/deleteMessage.js +0 -56
  92. package/src/deleteThread.js +0 -56
  93. package/src/forwardAttachment.js +0 -60
  94. package/src/getCurrentUserID.js +0 -7
  95. package/src/getEmojiUrl.js +0 -29
  96. package/src/getFriendsList.js +0 -83
  97. package/src/getMessage.js +0 -796
  98. package/src/getThreadHistory.js +0 -666
  99. package/src/getThreadInfo.js +0 -535
  100. package/src/getThreadPictures.js +0 -79
  101. package/src/getUserID.js +0 -66
  102. package/src/getUserInfo.js +0 -80
  103. package/src/handleFriendRequest.js +0 -61
  104. package/src/handleMessageRequest.js +0 -65
  105. package/src/httpGet.js +0 -57
  106. package/src/httpPost.js +0 -57
  107. package/src/httpPostFormData.js +0 -63
  108. package/src/logout.js +0 -75
  109. package/src/markAsDelivered.js +0 -58
  110. package/src/markAsRead.js +0 -80
  111. package/src/markAsReadAll.js +0 -50
  112. package/src/markAsSeen.js +0 -59
  113. package/src/muteThread.js +0 -52
  114. package/src/removeUserFromGroup.js +0 -79
  115. package/src/resolvePhotoUrl.js +0 -45
  116. package/src/searchForThread.js +0 -53
  117. package/src/sendMessage.js +0 -328
  118. package/src/sendMessageMqtt.js +0 -316
  119. package/src/sendTypingIndicator.js +0 -103
  120. package/src/setPostReaction.js +0 -109
  121. package/src/setTitle.js +0 -86
  122. package/src/shareContact.js +0 -49
  123. package/src/threadColors.js +0 -131
  124. package/src/unfriend.js +0 -52
  125. package/src/unsendMessage.js +0 -49
  126. package/src/uploadAttachment.js +0 -95
  127. package/utils.js +0 -1387
  128. /package/{lib → func}/login.js +0 -0
@@ -1,15 +1,17 @@
1
1
  "use strict";
2
2
 
3
- const utils = require("../utils");
3
+ const utils = require("../../utils");
4
4
  const log = require("npmlog");
5
5
 
6
6
  function createProfileUrl(url, username, id) {
7
7
  if (url) return url;
8
- return "https://www.facebook.com/" + (username || utils.formatID(id.toString()));
8
+ return (
9
+ "https://www.facebook.com/" + (username || utils.formatID(id.toString()))
10
+ );
9
11
  }
10
12
 
11
13
  function formatParticipants(participants) {
12
- return participants.edges.map((p) => {
14
+ return participants.edges.map(p => {
13
15
  p = p.node.messaging_actor;
14
16
  switch (p["__typename"]) {
15
17
  case "User":
@@ -21,7 +23,7 @@ function formatParticipants(participants) {
21
23
  gender: p.gender,
22
24
  url: p.url, // how about making it profileURL
23
25
  profilePicture: p.big_image_src.uri,
24
- username: (p.username || null),
26
+ username: p.username || null,
25
27
  // TODO: maybe better names for these?
26
28
  isViewerFriend: p.is_viewer_friend, // true/false
27
29
  isMessengerUser: p.is_messenger_user, // true/false
@@ -37,13 +39,13 @@ function formatParticipants(participants) {
37
39
  name: p.name,
38
40
  url: p.url,
39
41
  profilePicture: p.big_image_src.uri,
40
- username: (p.username || null),
42
+ username: p.username || null,
41
43
  // uhm... better names maybe?
42
44
  acceptsMessengerUserFeedback: p.accepts_messenger_user_feedback, // true/false
43
45
  isMessengerUser: p.is_messenger_user, // true/false
44
46
  isVerified: p.is_verified, // true/false
45
47
  isMessengerPlatformBot: p.is_messenger_platform_bot, // true/false
46
- isMessageBlockedByViewer: p.is_message_blocked_by_viewer, // true/false
48
+ isMessageBlockedByViewer: p.is_message_blocked_by_viewer // true/false
47
49
  };
48
50
  case "ReducedMessagingActor":
49
51
  case "UnavailableMessagingActor":
@@ -53,15 +55,19 @@ function formatParticipants(participants) {
53
55
  name: p.name,
54
56
  url: createProfileUrl(p.url, p.username, p.id), // in this case p.url is null all the time
55
57
  profilePicture: p.big_image_src.uri, // in this case it is default facebook photo, we could determine gender using it
56
- username: (p.username || null), // maybe we could use it to generate profile URL?
57
- isMessageBlockedByViewer: p.is_message_blocked_by_viewer, // true/false
58
+ username: p.username || null, // maybe we could use it to generate profile URL?
59
+ isMessageBlockedByViewer: p.is_message_blocked_by_viewer // true/false
58
60
  };
59
61
  default:
60
- log.warn("getThreadList", "Found participant with unsupported typename. Please open an issue at https://github.com/Schmavery/facebook-chat-api/issues\n" + JSON.stringify(p, null, 2));
62
+ log.warn(
63
+ "getThreadList",
64
+ "Found participant with unsupported typename. Please open an issue at https://github.com/Schmavery/facebook-chat-api/issues\n" +
65
+ JSON.stringify(p, null, 2)
66
+ );
61
67
  return {
62
68
  accountType: p["__typename"],
63
69
  userID: utils.formatID(p.id.toString()),
64
- name: p.name || `[unknown ${p["__typename"]}]`, // probably it will always be something... but fallback to [unknown], just in case
70
+ name: p.name || `[unknown ${p["__typename"]}]` // probably it will always be something... but fallback to [unknown], just in case
65
71
  };
66
72
  }
67
73
  });
@@ -78,30 +84,40 @@ function getThreadName(t) {
78
84
 
79
85
  for (let po of t.all_participants.edges) {
80
86
  let p = po.node;
81
- if (p.messaging_actor.id === t.thread_key.other_user_id) return p.messaging_actor.name;
87
+ if (p.messaging_actor.id === t.thread_key.other_user_id)
88
+ return p.messaging_actor.name;
82
89
  }
83
90
  }
84
91
 
85
92
  function mapNicknames(customizationInfo) {
86
- return (customizationInfo && customizationInfo.participant_customizations) ? customizationInfo.participant_customizations.map(u => {
87
- return {
88
- "userID": u.participant_id,
89
- "nickname": u.nickname
90
- };
91
- }) : [];
93
+ return customizationInfo && customizationInfo.participant_customizations
94
+ ? customizationInfo.participant_customizations.map(u => {
95
+ return {
96
+ userID: u.participant_id,
97
+ nickname: u.nickname
98
+ };
99
+ })
100
+ : [];
92
101
  }
93
102
 
94
103
  function formatThreadList(data) {
95
104
  return data.map(t => {
96
- let lastMessageNode = (t.last_message && t.last_message.nodes && t.last_message.nodes.length > 0) ? t.last_message.nodes[0] : null;
105
+ let lastMessageNode =
106
+ t.last_message && t.last_message.nodes && t.last_message.nodes.length > 0
107
+ ? t.last_message.nodes[0]
108
+ : null;
97
109
  return {
98
- threadID: t.thread_key ? utils.formatID(t.thread_key.thread_fbid || t.thread_key.other_user_id) : null, // shall never be null
110
+ threadID: t.thread_key
111
+ ? utils.formatID(t.thread_key.thread_fbid || t.thread_key.other_user_id)
112
+ : null, // shall never be null
99
113
  name: getThreadName(t),
100
114
  unreadCount: t.unread_count,
101
115
  messageCount: t.messages_count,
102
116
  imageSrc: t.image ? t.image.uri : null,
103
117
  emoji: t.customization_info ? t.customization_info.emoji : null,
104
- color: formatColor(t.customization_info ? t.customization_info.outgoing_bubble_color : null),
118
+ color: formatColor(
119
+ t.customization_info ? t.customization_info.outgoing_bubble_color : null
120
+ ),
105
121
  threadTheme: t.thread_theme,
106
122
  nicknames: mapNicknames(t.customization_info),
107
123
  muteUntil: t.mute_until,
@@ -111,22 +127,37 @@ function formatThreadList(data) {
111
127
  isGroup: t.thread_type === "GROUP",
112
128
  customizationEnabled: t.customization_enabled, // false for ONE_TO_ONE with Page or ReducedMessagingActor
113
129
  participantAddMode: t.participant_add_mode_as_string, // "ADD" if "GROUP" and null if "ONE_TO_ONE"
114
- montageThread: t.montage_thread ? Buffer.from(t.montage_thread.id, "base64").toString() : null, // base64 encoded string "message_thread:0000000000000000"
130
+ montageThread: t.montage_thread
131
+ ? Buffer.from(t.montage_thread.id, "base64").toString()
132
+ : null, // base64 encoded string "message_thread:0000000000000000"
115
133
  reactionsMuteMode: t.reactions_mute_mode,
116
134
  mentionsMuteMode: t.mentions_mute_mode,
117
135
  isArchived: t.has_viewer_archived,
118
136
  isSubscribed: t.is_viewer_subscribed,
119
137
  timestamp: t.updated_time_precise, // in miliseconds
120
138
  snippet: lastMessageNode ? lastMessageNode.snippet : null,
121
- snippetAttachments: lastMessageNode ? lastMessageNode.extensible_attachment : null, // TODO: not sure if it works
122
- snippetSender: lastMessageNode ? utils.formatID((lastMessageNode.message_sender.messaging_actor.id || "").toString()) : null,
123
- lastMessageTimestamp: lastMessageNode ? lastMessageNode.timestamp_precise : null, // timestamp in miliseconds
124
- lastReadTimestamp: (t.last_read_receipt && t.last_read_receipt.nodes.length > 0)
125
- ? (t.last_read_receipt.nodes[0] ? t.last_read_receipt.nodes[0].timestamp_precise : null)
139
+ snippetAttachments: lastMessageNode
140
+ ? lastMessageNode.extensible_attachment
141
+ : null, // TODO: not sure if it works
142
+ snippetSender: lastMessageNode
143
+ ? utils.formatID(
144
+ (lastMessageNode.message_sender.messaging_actor.id || "").toString()
145
+ )
126
146
  : null,
147
+ lastMessageTimestamp: lastMessageNode
148
+ ? lastMessageNode.timestamp_precise
149
+ : null, // timestamp in miliseconds
150
+ lastReadTimestamp:
151
+ t.last_read_receipt && t.last_read_receipt.nodes.length > 0
152
+ ? t.last_read_receipt.nodes[0]
153
+ ? t.last_read_receipt.nodes[0].timestamp_precise
154
+ : null
155
+ : null,
127
156
  cannotReplyReason: t.cannot_reply_reason,
128
157
  approvalMode: Boolean(t.approval_mode),
129
- participantIDs: formatParticipants(t.all_participants).map(participant => participant.userID),
158
+ participantIDs: formatParticipants(t.all_participants).map(
159
+ participant => participant.userID
160
+ ),
130
161
  threadType: t.thread_type === "GROUP" ? 2 : 1, // "GROUP" or "ONE_TO_ONE"
131
162
  inviteLink: {
132
163
  enable: t.joinable_mode ? t.joinable_mode.mode == 1 : false,
@@ -136,57 +167,82 @@ function formatThreadList(data) {
136
167
  });
137
168
  }
138
169
 
139
- module.exports = function (defaultFuncs, api, ctx) {
170
+ module.exports = function(defaultFuncs, api, ctx) {
140
171
  return function getThreadList(limit, timestamp, tags, callback) {
141
- if (!callback && (utils.getType(tags) === "Function" || utils.getType(tags) === "AsyncFunction")) {
172
+ if (
173
+ !callback &&
174
+ (utils.getType(tags) === "Function" ||
175
+ utils.getType(tags) === "AsyncFunction")
176
+ ) {
142
177
  callback = tags;
143
178
  tags = [""];
144
179
  }
145
- if (utils.getType(limit) !== "Number" || !Number.isInteger(limit) || limit <= 0) throw { error: "getThreadList: limit must be a positive integer" };
146
- if (utils.getType(timestamp) !== "Null" && (utils.getType(timestamp) !== "Number" || !Number.isInteger(timestamp))) throw { error: "getThreadList: timestamp must be an integer or null" };
180
+ if (
181
+ utils.getType(limit) !== "Number" ||
182
+ !Number.isInteger(limit) ||
183
+ limit <= 0
184
+ )
185
+ throw { error: "getThreadList: limit must be a positive integer" };
186
+ if (
187
+ utils.getType(timestamp) !== "Null" &&
188
+ (utils.getType(timestamp) !== "Number" || !Number.isInteger(timestamp))
189
+ )
190
+ throw { error: "getThreadList: timestamp must be an integer or null" };
147
191
  if (utils.getType(tags) === "String") tags = [tags];
148
- if (utils.getType(tags) !== "Array") throw { error: "getThreadList: tags must be an array" };
149
- var resolveFunc = function () { };
150
- var rejectFunc = function () { };
151
- var returnPromise = new Promise(function (resolve, reject) {
192
+ if (utils.getType(tags) !== "Array")
193
+ throw { error: "getThreadList: tags must be an array" };
194
+ var resolveFunc = function() {};
195
+ var rejectFunc = function() {};
196
+ var returnPromise = new Promise(function(resolve, reject) {
152
197
  resolveFunc = resolve;
153
198
  rejectFunc = reject;
154
199
  });
155
- if (utils.getType(callback) !== "Function" && utils.getType(callback) !== "AsyncFunction") {
156
- callback = function (err, data) {
200
+ if (
201
+ utils.getType(callback) !== "Function" &&
202
+ utils.getType(callback) !== "AsyncFunction"
203
+ ) {
204
+ callback = function(err, data) {
157
205
  if (err) return rejectFunc(err);
158
206
  resolveFunc(data);
159
207
  };
160
208
  }
161
209
  const form = {
162
- "av": ctx.globalOptions.pageID,
163
- "queries": JSON.stringify({
164
- "o0": {
165
- "doc_id": "3336396659757871",
166
- "query_params": {
167
- "limit": limit + (timestamp ? 1 : 0),
168
- "before": timestamp,
169
- "tags": tags,
170
- "includeDeliveryReceipts": true,
171
- "includeSeqID": false
210
+ av: ctx.globalOptions.pageID,
211
+ queries: JSON.stringify({
212
+ o0: {
213
+ doc_id: "3336396659757871",
214
+ query_params: {
215
+ limit: limit + (timestamp ? 1 : 0),
216
+ before: timestamp,
217
+ tags: tags,
218
+ includeDeliveryReceipts: true,
219
+ includeSeqID: false
172
220
  }
173
221
  }
174
222
  }),
175
- "batch_name": "MessengerGraphQLThreadlistFetcher"
223
+ batch_name: "MessengerGraphQLThreadlistFetcher"
176
224
  };
177
225
  defaultFuncs
178
226
  .post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, form)
179
227
  .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
180
- .then((resData) => {
181
- if (resData[resData.length - 1].error_results > 0) throw resData[0].o0.errors;
182
- if (resData[resData.length - 1].successful_results === 0) throw { error: "getThreadList: there was no successful_results", res: resData };
228
+ .then(resData => {
229
+ if (resData[resData.length - 1].error_results > 0)
230
+ throw resData[0].o0.errors;
231
+ if (resData[resData.length - 1].successful_results === 0)
232
+ throw {
233
+ error: "getThreadList: there was no successful_results",
234
+ res: resData
235
+ };
183
236
  if (timestamp) resData[0].o0.data.viewer.message_threads.nodes.shift();
184
- callback(null, formatThreadList(resData[0].o0.data.viewer.message_threads.nodes));
237
+ callback(
238
+ null,
239
+ formatThreadList(resData[0].o0.data.viewer.message_threads.nodes)
240
+ );
185
241
  })
186
- .catch((err) => {
242
+ .catch(err => {
187
243
  log.error("getThreadList", err);
188
244
  return callback(err);
189
245
  });
190
246
  return returnPromise;
191
247
  };
192
- };
248
+ };
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+
3
+ const utils = require("../../utils");
4
+ const log = require("npmlog");
5
+
6
+ module.exports = function(defaultFuncs, api, ctx) {
7
+ return function getThreadPictures(threadID, offset, limit, callback) {
8
+ let resolveFunc = function() {};
9
+ let rejectFunc = function() {};
10
+ const returnPromise = new Promise(function(resolve, reject) {
11
+ resolveFunc = resolve;
12
+ rejectFunc = reject;
13
+ });
14
+
15
+ if (!callback) {
16
+ callback = function(err, friendList) {
17
+ if (err) {
18
+ return rejectFunc(err);
19
+ }
20
+ resolveFunc(friendList);
21
+ };
22
+ }
23
+
24
+ let form = {
25
+ thread_id: threadID,
26
+ offset: offset,
27
+ limit: limit
28
+ };
29
+
30
+ defaultFuncs
31
+ .post(
32
+ "https://www.facebook.com/ajax/messaging/attachments/sharedphotos.php",
33
+ ctx.jar,
34
+ form
35
+ )
36
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
37
+ .then(function(resData) {
38
+ if (resData.error) {
39
+ throw resData;
40
+ }
41
+ return Promise.all(
42
+ resData.payload.imagesData.map(function(image) {
43
+ form = {
44
+ thread_id: threadID,
45
+ image_id: image.fbid
46
+ };
47
+ return defaultFuncs
48
+ .post(
49
+ "https://www.facebook.com/ajax/messaging/attachments/sharedphotos.php",
50
+ ctx.jar,
51
+ form
52
+ )
53
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
54
+ .then(function(resData) {
55
+ if (resData.error) {
56
+ throw resData;
57
+ }
58
+ // the response is pretty messy
59
+ const queryThreadID =
60
+ resData.jsmods.require[0][3][1].query_metadata.query_path[0]
61
+ .message_thread;
62
+ const imageData =
63
+ resData.jsmods.require[0][3][1].query_results[queryThreadID]
64
+ .message_images.edges[0].node.image2;
65
+ return imageData;
66
+ });
67
+ })
68
+ );
69
+ })
70
+ .then(function(resData) {
71
+ callback(null, resData);
72
+ })
73
+ .catch(function(err) {
74
+ log.error("Error in getThreadPictures", err);
75
+ callback(err);
76
+ });
77
+ return returnPromise;
78
+ };
79
+ };
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+
3
+ const utils = require("../../utils");
4
+ const log = require("npmlog");
5
+
6
+ function formatData(data) {
7
+ return {
8
+ userID: utils.formatID(data.uid.toString()),
9
+ photoUrl: data.photo,
10
+ indexRank: data.index_rank,
11
+ name: data.text,
12
+ isVerified: data.is_verified,
13
+ profileUrl: data.path,
14
+ category: data.category,
15
+ score: data.score,
16
+ type: data.type
17
+ };
18
+ }
19
+
20
+ module.exports = function(defaultFuncs, api, ctx) {
21
+ return function getUserID(name, callback) {
22
+ let resolveFunc = function() {};
23
+ let rejectFunc = function() {};
24
+ const returnPromise = new Promise(function(resolve, reject) {
25
+ resolveFunc = resolve;
26
+ rejectFunc = reject;
27
+ });
28
+
29
+ if (!callback) {
30
+ callback = function(err, friendList) {
31
+ if (err) {
32
+ return rejectFunc(err);
33
+ }
34
+ resolveFunc(friendList);
35
+ };
36
+ }
37
+
38
+ const form = {
39
+ value: name.toLowerCase(),
40
+ viewer: ctx.i_userID || ctx.userID,
41
+ rsp: "search",
42
+ context: "search",
43
+ path: "/home.php",
44
+ request_id: utils.getGUID()
45
+ };
46
+
47
+ defaultFuncs
48
+ .get("https://www.facebook.com/ajax/typeahead/search.php", ctx.jar, form)
49
+ .then(utils.parseAndCheckLogin(ctx, defaultFuncs))
50
+ .then(function(resData) {
51
+ if (resData.error) {
52
+ throw resData;
53
+ }
54
+
55
+ const data = resData.payload.entries;
56
+
57
+ callback(null, data.map(formatData));
58
+ })
59
+ .catch(function(err) {
60
+ log.error("getUserID", err);
61
+ return callback(err);
62
+ });
63
+
64
+ return returnPromise;
65
+ };
66
+ };
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ const { parseAndCheckLogin } = require("../../utils/client");
3
+ var log = require("npmlog");
4
+ module.exports = function(defaultFuncs, api, ctx) {
5
+ function formatData(data) {
6
+ const retObj = {};
7
+ for (const actor of data.messaging_actors || []) {
8
+ retObj[actor.id] = {
9
+ name: actor.name,
10
+ firstName: actor.short_name || null,
11
+ vanity: actor.username || null,
12
+ thumbSrc: actor.big_image_src?.uri || null,
13
+ profileUrl: actor.url || null,
14
+ gender: actor.gender || null,
15
+ type: actor.__typename || null,
16
+ isFriend: actor.is_viewer_friend || false,
17
+ isMessengerUser: actor.is_messenger_user || false,
18
+ isMessageBlockedByViewer: actor.is_message_blocked_by_viewer || false,
19
+ workInfo: actor.work_info || null,
20
+ messengerStatus: actor.messenger_account_status_category || null
21
+ };
22
+ }
23
+ return retObj;
24
+ }
25
+ return function getUserInfoGraphQL(id, callback) {
26
+ let resolveFunc, rejectFunc;
27
+ const returnPromise = new Promise((resolve, reject) => {
28
+ resolveFunc = resolve;
29
+ rejectFunc = reject;
30
+ });
31
+ if (typeof callback !== "function") {
32
+ callback = (err, data) => {
33
+ if (err) return rejectFunc(err);
34
+ resolveFunc(data);
35
+ };
36
+ }
37
+ const ids = Array.isArray(id) ? id : [id];
38
+ var form = {
39
+ queries: JSON.stringify({
40
+ o0: {
41
+ doc_id: "5009315269112105",
42
+ query_params: {
43
+ ids: ids
44
+ }
45
+ }
46
+ }),
47
+ batch_name: "MessengerParticipantsFetcher"
48
+ };
49
+ defaultFuncs
50
+ .post("https://www.facebook.com/api/graphqlbatch/", ctx.jar, form)
51
+ .then(parseAndCheckLogin(ctx, defaultFuncs))
52
+ .then(function(resData) {
53
+ if (!resData || resData.length === 0) {
54
+ throw new Error("Empty response from server");
55
+ }
56
+ if (resData.error) {
57
+ throw resData.error;
58
+ }
59
+ const response = resData[0];
60
+ console.log(response);
61
+ if (!response || !response.o0) {
62
+ throw new Error("Invalid response format");
63
+ }
64
+ if (response.o0.errors && response.o0.errors.length > 0) {
65
+ throw new Error(response.o0.errors[0].message || "GraphQL error");
66
+ }
67
+ const result = response.o0.data;
68
+ if (
69
+ !result ||
70
+ !result.messaging_actors ||
71
+ result.messaging_actors.length === 0
72
+ ) {
73
+ log.warn("getUserInfo", "No user data found for the provided ID(s)");
74
+ return callback(null, {});
75
+ }
76
+ const formattedData = formatData(result);
77
+ return callback(null, formattedData);
78
+ })
79
+ .catch(err => {
80
+ log.error(
81
+ "getUserInfoGraphQL",
82
+ "Error: " + (err.message || "Unknown error occurred")
83
+ );
84
+ callback(err);
85
+ });
86
+ return returnPromise;
87
+ };
88
+ };
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+
3
+ function lsRequest(ctx, payload, options, callback) {
4
+ return new Promise((resolve, reject) => {
5
+ const cb = typeof options === "function" ? options : callback;
6
+ const opts = typeof options === "object" && options ? options : {};
7
+ if (!ctx || !ctx.mqttClient) {
8
+ const err = new Error("Not connected to MQTT");
9
+ if (cb) cb(err);
10
+ return reject(err);
11
+ }
12
+ if (typeof ctx.wsReqNumber !== "number") ctx.wsReqNumber = 0;
13
+ const reqID = typeof opts.request_id === "number" ? opts.request_id : ++ctx.wsReqNumber;
14
+ const timeoutMs = typeof opts.timeout === "number" ? opts.timeout : 20000;
15
+ const qos = typeof opts.qos === "number" ? opts.qos : 1;
16
+ const retain = !!opts.retain;
17
+ const reqTopic = "/ls_req";
18
+ const respTopic = opts.respTopic || "/ls_resp";
19
+ const form = JSON.stringify({
20
+ app_id: opts.app_id || "",
21
+ payload: typeof payload === "string" ? payload : JSON.stringify(payload),
22
+ request_id: reqID,
23
+ type: opts.type == null ? 3 : opts.type
24
+ });
25
+ let timer = null;
26
+ const handleRes = (topic, message) => {
27
+ if (topic !== respTopic) return;
28
+ let msg;
29
+ try {
30
+ msg = JSON.parse(message.toString());
31
+ } catch {
32
+ return;
33
+ }
34
+ if (msg.request_id !== reqID) return;
35
+ if (typeof opts.filter === "function" && !opts.filter(msg)) return;
36
+ ctx.mqttClient.removeListener("message", handleRes);
37
+ if (timer) clearTimeout(timer);
38
+ try {
39
+ msg.payload = typeof msg.payload === "string" ? JSON.parse(msg.payload) : msg.payload;
40
+ } catch { }
41
+ const out = { success: true, response: msg.payload, raw: msg };
42
+ if (cb) cb(null, out);
43
+ resolve(out);
44
+ };
45
+ ctx.mqttClient.on("message", handleRes);
46
+ timer = setTimeout(() => {
47
+ ctx.mqttClient.removeListener("message", handleRes);
48
+ const err = new Error("MQTT response timeout");
49
+ if (cb) cb(err);
50
+ reject(err);
51
+ }, timeoutMs);
52
+ ctx.mqttClient.publish(reqTopic, form, { qos, retain }, (err) => {
53
+ if (err) {
54
+ if (timer) clearTimeout(timer);
55
+ ctx.mqttClient.removeListener("message", handleRes);
56
+ if (cb) cb(err);
57
+ reject(err);
58
+ }
59
+ });
60
+ });
61
+ };
62
+
63
+ module.exports = sendReqMqtt;
@@ -1,13 +1,13 @@
1
- const { Sequelize } = require('sequelize');
2
- const fs = require('fs');
3
- const path = require('path');
4
- const databasePath = path.join(process.cwd(), 'Fca_Database');
1
+ const { Sequelize } = require("sequelize");
2
+ const fs = require("fs");
3
+ const path = require("path");
4
+ const databasePath = path.join(process.cwd(), "Fca_Database");
5
5
  if (!fs.existsSync(databasePath)) {
6
6
  fs.mkdirSync(databasePath, { recursive: true });
7
7
  }
8
8
  const sequelize = new Sequelize({
9
- dialect: 'sqlite',
10
- storage: path.join(databasePath, 'database.sqlite'),
9
+ dialect: "sqlite",
10
+ storage: path.join(databasePath, "database.sqlite"),
11
11
  logging: false,
12
12
  pool: {
13
13
  max: 5,
@@ -24,7 +24,9 @@ const sequelize = new Sequelize({
24
24
  isolationLevel: Sequelize.Transaction.ISOLATION_LEVELS.READ_COMMITTED
25
25
  });
26
26
  const models = {};
27
- fs.readdirSync(__dirname).filter(file => file.endsWith('.js') && file !== 'index.js').forEach(file => {
27
+ fs.readdirSync(__dirname)
28
+ .filter(file => file.endsWith(".js") && file !== "index.js")
29
+ .forEach(file => {
28
30
  const model = require(path.join(__dirname, file))(sequelize);
29
31
  models[model.name] = model;
30
32
  });
@@ -37,11 +39,11 @@ models.sequelize = sequelize;
37
39
  models.Sequelize = Sequelize;
38
40
  models.syncAll = async () => {
39
41
  try {
40
- await sequelize.sync({ force: false });
42
+ await sequelize.sync({ force: false });
41
43
  } catch (error) {
42
- console.error('Failed to synchronize models:', error);
44
+ console.error("Failed to synchronize models:", error);
43
45
  throw error;
44
46
  }
45
47
  };
46
48
 
47
- module.exports = models;
49
+ module.exports = models;
@@ -9,23 +9,23 @@ module.exports = function(sequelize) {
9
9
  type: DataTypes.INTEGER,
10
10
  allowNull: false,
11
11
  autoIncrement: true,
12
- primaryKey: true,
12
+ primaryKey: true
13
13
  },
14
14
  threadID: {
15
15
  type: DataTypes.STRING,
16
16
  allowNull: false,
17
- unique: true,
17
+ unique: true
18
18
  },
19
19
  data: {
20
20
  type: DataTypes.JSONB,
21
- allowNull: true,
21
+ allowNull: true
22
22
  }
23
23
  },
24
24
  {
25
25
  sequelize,
26
26
  modelName: "Thread",
27
- timestamps: true,
27
+ timestamps: true
28
28
  }
29
29
  );
30
30
  return Thread;
31
- };
31
+ };