fca-riyad 37.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.
- package/CHANGELOG.md +325 -0
- package/DOCS.md +2712 -0
- package/README.md +410 -0
- package/func/checkUpdate.js +7 -0
- package/func/logAdapter.js +33 -0
- package/func/logger.js +48 -0
- package/index.d.ts +751 -0
- package/index.js +22 -0
- package/module/config.js +40 -0
- package/module/login.js +133 -0
- package/module/loginHelper.js +1359 -0
- package/module/options.js +44 -0
- package/package.json +95 -0
- package/src/api/action/addExternalModule.js +25 -0
- package/src/api/action/changeAvatar.js +137 -0
- package/src/api/action/changeBio.js +75 -0
- package/src/api/action/enableAutoSaveAppState.js +73 -0
- package/src/api/action/getCurrentUserID.js +7 -0
- package/src/api/action/handleFriendRequest.js +57 -0
- package/src/api/action/logout.js +76 -0
- package/src/api/action/refreshFb_dtsg.js +48 -0
- package/src/api/action/setPostReaction.js +106 -0
- package/src/api/action/unfriend.js +54 -0
- package/src/api/http/httpGet.js +46 -0
- package/src/api/http/httpPost.js +52 -0
- package/src/api/http/postFormData.js +47 -0
- package/src/api/messaging/addUserToGroup.js +68 -0
- package/src/api/messaging/changeAdminStatus.js +126 -0
- package/src/api/messaging/changeArchivedStatus.js +55 -0
- package/src/api/messaging/changeBlockedStatus.js +48 -0
- package/src/api/messaging/changeGroupImage.js +91 -0
- package/src/api/messaging/changeNickname.js +70 -0
- package/src/api/messaging/changeThreadColor.js +79 -0
- package/src/api/messaging/changeThreadEmoji.js +111 -0
- package/src/api/messaging/createNewGroup.js +88 -0
- package/src/api/messaging/createPoll.js +46 -0
- package/src/api/messaging/createThemeAI.js +98 -0
- package/src/api/messaging/deleteMessage.js +136 -0
- package/src/api/messaging/deleteThread.js +56 -0
- package/src/api/messaging/editMessage.js +90 -0
- package/src/api/messaging/forwardAttachment.js +57 -0
- package/src/api/messaging/forwardMessage.js +134 -0
- package/src/api/messaging/getEmojiUrl.js +29 -0
- package/src/api/messaging/getFriendsList.js +82 -0
- package/src/api/messaging/getMessage.js +829 -0
- package/src/api/messaging/getThemePictures.js +62 -0
- package/src/api/messaging/handleMessageRequest.js +65 -0
- package/src/api/messaging/markAsDelivered.js +57 -0
- package/src/api/messaging/markAsRead.js +88 -0
- package/src/api/messaging/markAsReadAll.js +49 -0
- package/src/api/messaging/markAsSeen.js +61 -0
- package/src/api/messaging/muteThread.js +50 -0
- package/src/api/messaging/pinMessage.js +115 -0
- package/src/api/messaging/removeUserFromGroup.js +62 -0
- package/src/api/messaging/resolvePhotoUrl.js +43 -0
- package/src/api/messaging/scheduler.js +264 -0
- package/src/api/messaging/searchForThread.js +53 -0
- package/src/api/messaging/sendBroadcast.js +93 -0
- package/src/api/messaging/sendMessage.js +269 -0
- package/src/api/messaging/sendTypingIndicator.js +74 -0
- package/src/api/messaging/sessionGuard.js +130 -0
- package/src/api/messaging/setMessageReaction.js +95 -0
- package/src/api/messaging/setTitle.js +124 -0
- package/src/api/messaging/shareContact.js +49 -0
- package/src/api/messaging/threadColors.js +128 -0
- package/src/api/messaging/unsendMessage.js +81 -0
- package/src/api/messaging/uploadAttachment.js +492 -0
- package/src/api/socket/OldMessage.js +186 -0
- package/src/api/socket/core/connectMqtt.js +269 -0
- package/src/api/socket/core/emitAuth.js +103 -0
- package/src/api/socket/core/getSeqID.js +321 -0
- package/src/api/socket/core/getTaskResponseData.js +25 -0
- package/src/api/socket/core/parseDelta.js +387 -0
- package/src/api/socket/detail/buildStream.js +215 -0
- package/src/api/socket/detail/constants.js +28 -0
- package/src/api/socket/e2ee/crypto.js +173 -0
- package/src/api/socket/e2ee/e2ee/crypto.js +173 -0
- package/src/api/socket/e2ee/e2ee/index.js +421 -0
- package/src/api/socket/e2ee/e2ee/proto/ArmadilloApplication.proto +281 -0
- package/src/api/socket/e2ee/e2ee/proto/ArmadilloICDC.proto +14 -0
- package/src/api/socket/e2ee/e2ee/proto/ConsumerApplication.proto +232 -0
- package/src/api/socket/e2ee/e2ee/proto/MessageApplication.proto +82 -0
- package/src/api/socket/e2ee/e2ee/proto/MessageTransport.proto +77 -0
- package/src/api/socket/e2ee/e2ee/proto/WACommon.proto +66 -0
- package/src/api/socket/e2ee/e2ee/proto/WAMediaTransport.proto +176 -0
- package/src/api/socket/e2ee/e2ee/proto/proto-writer.ts +76 -0
- package/src/api/socket/e2ee/e2ee/protocol.js +196 -0
- package/src/api/socket/e2ee/e2ee/ratchet.js +219 -0
- package/src/api/socket/e2ee/e2ee/store.js +182 -0
- package/src/api/socket/e2ee/e2ee/vendor/fb-e2ee.cjs +9162 -0
- package/src/api/socket/e2ee/e2ee/vendor/proto/ArmadilloApplication.proto +281 -0
- package/src/api/socket/e2ee/e2ee/vendor/proto/ArmadilloICDC.proto +14 -0
- package/src/api/socket/e2ee/e2ee/vendor/proto/ConsumerApplication.proto +232 -0
- package/src/api/socket/e2ee/e2ee/vendor/proto/MessageApplication.proto +82 -0
- package/src/api/socket/e2ee/e2ee/vendor/proto/MessageTransport.proto +77 -0
- package/src/api/socket/e2ee/e2ee/vendor/proto/WACommon.proto +66 -0
- package/src/api/socket/e2ee/e2ee/vendor/proto/WAMediaTransport.proto +176 -0
- package/src/api/socket/e2ee/e2ee/vendor/proto/proto-writer.ts +76 -0
- package/src/api/socket/e2ee/index.js +437 -0
- package/src/api/socket/e2ee/proto/ArmadilloApplication.proto +281 -0
- package/src/api/socket/e2ee/proto/ArmadilloICDC.proto +14 -0
- package/src/api/socket/e2ee/proto/ConsumerApplication.proto +232 -0
- package/src/api/socket/e2ee/proto/MessageApplication.proto +82 -0
- package/src/api/socket/e2ee/proto/MessageTransport.proto +77 -0
- package/src/api/socket/e2ee/proto/WACommon.proto +66 -0
- package/src/api/socket/e2ee/proto/WAMediaTransport.proto +176 -0
- package/src/api/socket/e2ee/proto/proto-writer.ts +76 -0
- package/src/api/socket/e2ee/protocol.js +196 -0
- package/src/api/socket/e2ee/ratchet.js +219 -0
- package/src/api/socket/e2ee/store.js +182 -0
- package/src/api/socket/e2ee/vendor/fb-e2ee.cjs +9162 -0
- package/src/api/socket/e2ee/vendor/proto/ArmadilloApplication.proto +281 -0
- package/src/api/socket/e2ee/vendor/proto/ArmadilloICDC.proto +14 -0
- package/src/api/socket/e2ee/vendor/proto/ConsumerApplication.proto +232 -0
- package/src/api/socket/e2ee/vendor/proto/MessageApplication.proto +82 -0
- package/src/api/socket/e2ee/vendor/proto/MessageTransport.proto +77 -0
- package/src/api/socket/e2ee/vendor/proto/WACommon.proto +66 -0
- package/src/api/socket/e2ee/vendor/proto/WAMediaTransport.proto +176 -0
- package/src/api/socket/e2ee/vendor/proto/proto-writer.ts +76 -0
- package/src/api/socket/listenE2EE.js +75 -0
- package/src/api/socket/listenMqtt.js +388 -0
- package/src/api/socket/middleware/index.js +216 -0
- package/src/api/socket/sendMessage.js +309 -0
- package/src/api/socket/sendMessageMqtt.js +69 -0
- package/src/api/threads/getThreadHistory.js +664 -0
- package/src/api/threads/getThreadInfo.js +296 -0
- package/src/api/threads/getThreadList.js +293 -0
- package/src/api/threads/getThreadPictures.js +78 -0
- package/src/api/users/getUserID.js +65 -0
- package/src/api/users/getUserInfo.js +402 -0
- package/src/api/users/getUserInfoV2.js +134 -0
- package/src/app/MessengerBot.js +209 -0
- package/src/app/MessengerContext.js +32 -0
- package/src/app/createFcaClient.js +136 -0
- package/src/app/threadInfoRealtimeSync.js +284 -0
- package/src/core/sendReqMqtt.js +96 -0
- package/src/database/helpers.js +53 -0
- package/src/database/models/index.js +88 -0
- package/src/database/models/thread.js +50 -0
- package/src/database/models/user.js +46 -0
- package/src/database/threadData.js +94 -0
- package/src/database/userData.js +98 -0
- package/src/remote/remoteClient.js +123 -0
- package/src/utils/broadcast.js +51 -0
- package/src/utils/client.js +10 -0
- package/src/utils/constants.js +23 -0
- package/src/utils/cookies.js +68 -0
- package/src/utils/format/attachment.js +357 -0
- package/src/utils/format/cookie.js +9 -0
- package/src/utils/format/date.js +50 -0
- package/src/utils/format/decode.js +44 -0
- package/src/utils/format/delta.js +194 -0
- package/src/utils/format/ids.js +64 -0
- package/src/utils/format/index.js +64 -0
- package/src/utils/format/message.js +88 -0
- package/src/utils/format/presence.js +132 -0
- package/src/utils/format/readTyp.js +44 -0
- package/src/utils/format/thread.js +42 -0
- package/src/utils/format/utils.js +141 -0
- package/src/utils/headers.js +115 -0
- package/src/utils/loginParser/autoLogin.js +125 -0
- package/src/utils/loginParser/helpers.js +43 -0
- package/src/utils/loginParser/index.js +10 -0
- package/src/utils/loginParser/parseAndCheckLogin.js +220 -0
- package/src/utils/loginParser/textUtils.js +28 -0
- package/src/utils/nexca-logger.js +144 -0
- package/src/utils/nexca-utils.js +686 -0
- package/src/utils/request/client.js +26 -0
- package/src/utils/request/config.js +23 -0
- package/src/utils/request/defaults.js +46 -0
- package/src/utils/request/helpers.js +46 -0
- package/src/utils/request/index.js +17 -0
- package/src/utils/request/methods.js +163 -0
- package/src/utils/request/proxy.js +21 -0
- package/src/utils/request/retry.js +77 -0
- package/src/utils/request/sanitize.js +49 -0
- package/src/utils/versionCheck.js +47 -0
- package/src/vendor/fca-unofficial/.github/workflows/nodejs.yml +26 -0
- package/src/vendor/fca-unofficial/.github/workflows/npmpublish.yml +30 -0
- package/src/vendor/fca-unofficial/.travis.yml +6 -0
- package/src/vendor/fca-unofficial/DOCS.md +1731 -0
- package/src/vendor/fca-unofficial/LICENSE-MIT +21 -0
- package/src/vendor/fca-unofficial/README.md +225 -0
- package/src/vendor/fca-unofficial/index.js +619 -0
- package/src/vendor/fca-unofficial/package.json +76 -0
- package/src/vendor/fca-unofficial/src/addExternalModule.js +19 -0
- package/src/vendor/fca-unofficial/src/addUserToGroup.js +113 -0
- package/src/vendor/fca-unofficial/src/changeAdminStatus.js +79 -0
- package/src/vendor/fca-unofficial/src/changeArchivedStatus.js +55 -0
- package/src/vendor/fca-unofficial/src/changeBio.js +77 -0
- package/src/vendor/fca-unofficial/src/changeBlockedStatus.js +47 -0
- package/src/vendor/fca-unofficial/src/changeGroupImage.js +129 -0
- package/src/vendor/fca-unofficial/src/changeNickname.js +59 -0
- package/src/vendor/fca-unofficial/src/changeThreadColor.js +71 -0
- package/src/vendor/fca-unofficial/src/changeThreadEmoji.js +55 -0
- package/src/vendor/fca-unofficial/src/createNewGroup.js +86 -0
- package/src/vendor/fca-unofficial/src/createPoll.js +71 -0
- package/src/vendor/fca-unofficial/src/deleteMessage.js +56 -0
- package/src/vendor/fca-unofficial/src/deleteThread.js +56 -0
- package/src/vendor/fca-unofficial/src/forwardAttachment.js +60 -0
- package/src/vendor/fca-unofficial/src/getCurrentUserID.js +7 -0
- package/src/vendor/fca-unofficial/src/getEmojiUrl.js +29 -0
- package/src/vendor/fca-unofficial/src/getFriendsList.js +84 -0
- package/src/vendor/fca-unofficial/src/getThreadHistory.js +645 -0
- package/src/vendor/fca-unofficial/src/getThreadHistoryDeprecated.js +93 -0
- package/src/vendor/fca-unofficial/src/getThreadInfo.js +201 -0
- package/src/vendor/fca-unofficial/src/getThreadInfoDeprecated.js +80 -0
- package/src/vendor/fca-unofficial/src/getThreadList.js +238 -0
- package/src/vendor/fca-unofficial/src/getThreadListDeprecated.js +75 -0
- package/src/vendor/fca-unofficial/src/getThreadPictures.js +79 -0
- package/src/vendor/fca-unofficial/src/getUserID.js +66 -0
- package/src/vendor/fca-unofficial/src/getUserInfo.js +72 -0
- package/src/vendor/fca-unofficial/src/handleFriendRequest.js +61 -0
- package/src/vendor/fca-unofficial/src/handleMessageRequest.js +65 -0
- package/src/vendor/fca-unofficial/src/httpGet.js +52 -0
- package/src/vendor/fca-unofficial/src/httpPost.js +52 -0
- package/src/vendor/fca-unofficial/src/listenMqtt.js +788 -0
- package/src/vendor/fca-unofficial/src/logout.js +75 -0
- package/src/vendor/fca-unofficial/src/markAsDelivered.js +58 -0
- package/src/vendor/fca-unofficial/src/markAsRead.js +80 -0
- package/src/vendor/fca-unofficial/src/markAsReadAll.js +50 -0
- package/src/vendor/fca-unofficial/src/markAsSeen.js +59 -0
- package/src/vendor/fca-unofficial/src/muteThread.js +52 -0
- package/src/vendor/fca-unofficial/src/removeUserFromGroup.js +79 -0
- package/src/vendor/fca-unofficial/src/resolvePhotoUrl.js +45 -0
- package/src/vendor/fca-unofficial/src/searchForThread.js +53 -0
- package/src/vendor/fca-unofficial/src/sendMessage.js +459 -0
- package/src/vendor/fca-unofficial/src/sendTypingIndicator.js +103 -0
- package/src/vendor/fca-unofficial/src/setMessageReaction.js +117 -0
- package/src/vendor/fca-unofficial/src/setPostReaction.js +76 -0
- package/src/vendor/fca-unofficial/src/setTitle.js +86 -0
- package/src/vendor/fca-unofficial/src/threadColors.js +57 -0
- package/src/vendor/fca-unofficial/src/unfriend.js +52 -0
- package/src/vendor/fca-unofficial/src/unsendMessage.js +49 -0
- package/src/vendor/fca-unofficial/test/data/shareAttach.js +146 -0
- package/src/vendor/fca-unofficial/test/data/something.mov +0 -0
- package/src/vendor/fca-unofficial/test/data/test.png +0 -0
- package/src/vendor/fca-unofficial/test/data/test.txt +7 -0
- package/src/vendor/fca-unofficial/test/example-config.json +18 -0
- package/src/vendor/fca-unofficial/test/test-page.js +140 -0
- package/src/vendor/fca-unofficial/test/test.js +385 -0
- package/src/vendor/fca-unofficial/utils.js +1358 -0
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const EventEmitter = require("events");
|
|
4
|
+
const { MessengerContext } = require("./MessengerContext");
|
|
5
|
+
const { createFcaClient } = require("./createFcaClient");
|
|
6
|
+
|
|
7
|
+
function escapeRegex(s) {
|
|
8
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function emitIf(bot, channel, payload) {
|
|
12
|
+
if (bot.listenerCount(channel) > 0) bot.emit(channel, payload);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function emitGatewayEvents(bot, event) {
|
|
16
|
+
emitIf(bot, "update", event);
|
|
17
|
+
emitIf(bot, "raw", event);
|
|
18
|
+
const t = event.type;
|
|
19
|
+
if (!t) return;
|
|
20
|
+
if (t === "message" || t === "message_reply") {
|
|
21
|
+
emitIf(bot, "message", event);
|
|
22
|
+
emitIf(bot, "messageCreate", event);
|
|
23
|
+
}
|
|
24
|
+
if (t === "message_reply") {
|
|
25
|
+
emitIf(bot, "message_reply", event);
|
|
26
|
+
} else if (t !== "message") {
|
|
27
|
+
emitIf(bot, t, event);
|
|
28
|
+
}
|
|
29
|
+
switch (t) {
|
|
30
|
+
case "message_reaction": emitIf(bot, "messageReactionAdd", event); break;
|
|
31
|
+
case "message_unsend": emitIf(bot, "messageDelete", event); break;
|
|
32
|
+
case "typ": emitIf(bot, event.isTyping ? "typingStart" : "typingStop", event); break;
|
|
33
|
+
case "event": emitIf(bot, "threadUpdate", event); break;
|
|
34
|
+
case "ready":
|
|
35
|
+
emitIf(bot, "ready", event);
|
|
36
|
+
emitIf(bot, "shardReady", event);
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
class MessengerBot extends EventEmitter {
|
|
42
|
+
constructor(api, options = {}) {
|
|
43
|
+
super();
|
|
44
|
+
const cap = options.maxEventListeners ?? 64;
|
|
45
|
+
this.setMaxListeners(cap === 0 ? 0 : cap);
|
|
46
|
+
this.api = api;
|
|
47
|
+
this._commandPrefix = options.commandPrefix || "/";
|
|
48
|
+
this._enableComposer = options.enableComposer !== false;
|
|
49
|
+
this._stopOnSignals = options.stopOnSignals || false;
|
|
50
|
+
this._middlewares = [];
|
|
51
|
+
this._catchHandler = null;
|
|
52
|
+
this._facade = null;
|
|
53
|
+
this._mqtt = null;
|
|
54
|
+
this._listening = false;
|
|
55
|
+
this._signalsBound = false;
|
|
56
|
+
this._onStopSignal = null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
get commandPrefix() { return this._commandPrefix; }
|
|
60
|
+
set commandPrefix(value) { this._commandPrefix = value || "/"; }
|
|
61
|
+
|
|
62
|
+
get client() {
|
|
63
|
+
if (!this._facade) this._facade = createFcaClient(this.api);
|
|
64
|
+
return this._facade;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
use(middleware) {
|
|
68
|
+
this._middlewares.push(middleware);
|
|
69
|
+
return this;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
command(name, handler) {
|
|
73
|
+
const n = name.toLowerCase();
|
|
74
|
+
this.use(async (ctx, next) => {
|
|
75
|
+
const text = ctx.text;
|
|
76
|
+
if (!text) { await next(); return; }
|
|
77
|
+
const prefix = escapeRegex(this._commandPrefix);
|
|
78
|
+
const re = new RegExp(`^${prefix}${escapeRegex(n)}(?:\\s|$)`, "i");
|
|
79
|
+
if (re.test(text)) { await handler(ctx); return; }
|
|
80
|
+
await next();
|
|
81
|
+
});
|
|
82
|
+
return this;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
hears(trigger, handler) {
|
|
86
|
+
const match = typeof trigger === "string"
|
|
87
|
+
? (text) => text.toLowerCase().includes(trigger.toLowerCase())
|
|
88
|
+
: (text) => trigger.test(text);
|
|
89
|
+
this.use(async (ctx, next) => {
|
|
90
|
+
const text = ctx.text;
|
|
91
|
+
if (!text) { await next(); return; }
|
|
92
|
+
if (match(text)) { await handler(ctx); return; }
|
|
93
|
+
await next();
|
|
94
|
+
});
|
|
95
|
+
return this;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
catch(handler) {
|
|
99
|
+
this._catchHandler = handler;
|
|
100
|
+
return this;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
startListening() {
|
|
104
|
+
if (this._listening) return this;
|
|
105
|
+
if (typeof this.api.listenMqtt !== "function") {
|
|
106
|
+
throw new Error("listenMqtt is not available on API");
|
|
107
|
+
}
|
|
108
|
+
const mqtt = this.api.listenMqtt();
|
|
109
|
+
this._mqtt = mqtt;
|
|
110
|
+
this._listening = true;
|
|
111
|
+
mqtt.on("message", (event) => {
|
|
112
|
+
emitGatewayEvents(this, event);
|
|
113
|
+
this._enqueueComposerIfNeeded(event);
|
|
114
|
+
});
|
|
115
|
+
mqtt.on("error", (err) => this.emit("error", err));
|
|
116
|
+
return this;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async launch(opts = {}) {
|
|
120
|
+
this.startListening();
|
|
121
|
+
const bind = opts.stopOnSignals ?? this._stopOnSignals;
|
|
122
|
+
if (bind) this._attachStopSignals();
|
|
123
|
+
return this;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
_attachStopSignals() {
|
|
127
|
+
if (this._signalsBound) return;
|
|
128
|
+
this._signalsBound = true;
|
|
129
|
+
this._onStopSignal = () => {
|
|
130
|
+
this.stop().then(() => process.exit(0)).catch(() => process.exit(1));
|
|
131
|
+
};
|
|
132
|
+
process.once("SIGINT", this._onStopSignal);
|
|
133
|
+
process.once("SIGTERM", this._onStopSignal);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
_detachStopSignals() {
|
|
137
|
+
if (!this._signalsBound || !this._onStopSignal) return;
|
|
138
|
+
process.off("SIGINT", this._onStopSignal);
|
|
139
|
+
process.off("SIGTERM", this._onStopSignal);
|
|
140
|
+
this._signalsBound = false;
|
|
141
|
+
this._onStopSignal = null;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async stop() {
|
|
145
|
+
this._detachStopSignals();
|
|
146
|
+
if (!this._mqtt) return;
|
|
147
|
+
const mqtt = this._mqtt;
|
|
148
|
+
if (typeof mqtt.stopListeningAsync === "function") {
|
|
149
|
+
await mqtt.stopListeningAsync();
|
|
150
|
+
} else if (typeof mqtt.stopListening === "function") {
|
|
151
|
+
mqtt.stopListening();
|
|
152
|
+
}
|
|
153
|
+
if (typeof mqtt.removeAllListeners === "function") mqtt.removeAllListeners();
|
|
154
|
+
this._mqtt = null;
|
|
155
|
+
this._listening = false;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
_enqueueComposerIfNeeded(event) {
|
|
159
|
+
if (!this._enableComposer || this._middlewares.length === 0) return;
|
|
160
|
+
if (event.type !== "message" && event.type !== "message_reply") return;
|
|
161
|
+
const ctx = new MessengerContext(this, event);
|
|
162
|
+
queueMicrotask(() => { this._runComposer(ctx); });
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async _runComposer(ctx) {
|
|
166
|
+
const dispatch = async (index) => {
|
|
167
|
+
if (index >= this._middlewares.length) return;
|
|
168
|
+
const mw = this._middlewares[index];
|
|
169
|
+
await mw(ctx, () => dispatch(index + 1));
|
|
170
|
+
};
|
|
171
|
+
try {
|
|
172
|
+
await dispatch(0);
|
|
173
|
+
} catch (err) {
|
|
174
|
+
if (this._catchHandler) this._catchHandler(err, ctx);
|
|
175
|
+
else this.emit("error", err);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
static async connect(credentials, options = {}) {
|
|
180
|
+
const login = require("../../module/login");
|
|
181
|
+
const {
|
|
182
|
+
autoListen = true,
|
|
183
|
+
enableComposer = true,
|
|
184
|
+
commandPrefix = "/",
|
|
185
|
+
stopOnSignals = false,
|
|
186
|
+
maxEventListeners = 64,
|
|
187
|
+
...fcaOptions
|
|
188
|
+
} = options;
|
|
189
|
+
|
|
190
|
+
return new Promise((resolve, reject) => {
|
|
191
|
+
login(credentials, fcaOptions, (err, api) => {
|
|
192
|
+
if (err) return reject(err);
|
|
193
|
+
const bot = new MessengerBot(api, { enableComposer, commandPrefix, stopOnSignals, maxEventListeners });
|
|
194
|
+
if (autoListen) {
|
|
195
|
+
bot.launch({ stopOnSignals }).then(() => resolve(bot)).catch(reject);
|
|
196
|
+
} else {
|
|
197
|
+
if (stopOnSignals) bot._attachStopSignals();
|
|
198
|
+
resolve(bot);
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function createMessengerBot(credentials, options) {
|
|
206
|
+
return MessengerBot.connect(credentials, options);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
module.exports = { MessengerBot, createMessengerBot };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
class MessengerContext {
|
|
4
|
+
constructor(bot, event) {
|
|
5
|
+
this.bot = bot;
|
|
6
|
+
this.event = event;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
get threadID() { return this.event.threadID; }
|
|
10
|
+
get senderID() { return this.event.senderID; }
|
|
11
|
+
get messageID() { return this.event.messageID; }
|
|
12
|
+
get body() { return this.event.body; }
|
|
13
|
+
get message() { return this.event; }
|
|
14
|
+
|
|
15
|
+
get text() {
|
|
16
|
+
return (this.event.body ?? "").trim();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
reply(payload, callback) {
|
|
20
|
+
const tid = this.event.threadID;
|
|
21
|
+
if (tid == null) throw new Error("MessengerContext.reply: threadID is missing");
|
|
22
|
+
return this.bot.api.sendMessage(payload, tid, callback);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async replyAsync(payload) {
|
|
26
|
+
const r = this.reply(payload);
|
|
27
|
+
if (r && typeof r.then === "function") return r;
|
|
28
|
+
return Promise.resolve(r);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
module.exports = { MessengerContext };
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
function bindOptionalMethod(api, key) {
|
|
4
|
+
const candidate = api[key];
|
|
5
|
+
return typeof candidate === "function" ? candidate.bind(api) : undefined;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function readOptionalMember(api, key) {
|
|
9
|
+
return typeof api[key] === "undefined" ? undefined : api[key];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function bindLiveMethod(api, key) {
|
|
13
|
+
return (...args) => {
|
|
14
|
+
const candidate = api[key];
|
|
15
|
+
if (typeof candidate !== "function") {
|
|
16
|
+
throw new Error(`API method "${key}" is not available`);
|
|
17
|
+
}
|
|
18
|
+
return candidate.apply(api, args);
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function compactNamespace(namespace) {
|
|
23
|
+
return Object.fromEntries(
|
|
24
|
+
Object.entries(namespace).filter(([, value]) => value !== undefined)
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function readNamespace(api, key) {
|
|
29
|
+
const value = api[key];
|
|
30
|
+
return value && typeof value === "object" ? value : undefined;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function createFallbackNamespaces(api) {
|
|
34
|
+
return {
|
|
35
|
+
messages: compactNamespace({
|
|
36
|
+
send: bindOptionalMethod(api, "sendMessage"),
|
|
37
|
+
edit: bindOptionalMethod(api, "editMessage"),
|
|
38
|
+
delete: bindOptionalMethod(api, "deleteMessage"),
|
|
39
|
+
unsend: bindOptionalMethod(api, "unsendMessage"),
|
|
40
|
+
get: bindOptionalMethod(api, "getMessage"),
|
|
41
|
+
markRead: bindOptionalMethod(api, "markAsRead"),
|
|
42
|
+
markReadAll: bindOptionalMethod(api, "markAsReadAll"),
|
|
43
|
+
markSeen: bindOptionalMethod(api, "markAsSeen"),
|
|
44
|
+
markDelivered: bindOptionalMethod(api, "markAsDelivered"),
|
|
45
|
+
typing: bindOptionalMethod(api, "sendTypingIndicator"),
|
|
46
|
+
react: bindOptionalMethod(api, "setMessageReaction"),
|
|
47
|
+
shareContact: bindOptionalMethod(api, "shareContact"),
|
|
48
|
+
getEmojiUrl: bindOptionalMethod(api, "getEmojiUrl"),
|
|
49
|
+
resolvePhotoUrl: bindOptionalMethod(api, "resolvePhotoUrl"),
|
|
50
|
+
uploadAttachment: bindOptionalMethod(api, "uploadAttachment"),
|
|
51
|
+
forwardAttachment: bindOptionalMethod(api, "forwardAttachment")
|
|
52
|
+
}),
|
|
53
|
+
threads: compactNamespace({
|
|
54
|
+
createGroup: bindOptionalMethod(api, "createNewGroup"),
|
|
55
|
+
getInfo: bindOptionalMethod(api, "getThreadInfo"),
|
|
56
|
+
getList: bindOptionalMethod(api, "getThreadList"),
|
|
57
|
+
getHistory: bindOptionalMethod(api, "getThreadHistory"),
|
|
58
|
+
getPictures: bindOptionalMethod(api, "getThreadPictures"),
|
|
59
|
+
addUsers: bindOptionalMethod(api, "addUserToGroup"),
|
|
60
|
+
archive: bindOptionalMethod(api, "changeArchivedStatus"),
|
|
61
|
+
removeUser: bindOptionalMethod(api, "removeUserFromGroup"),
|
|
62
|
+
setAdmin: bindOptionalMethod(api, "changeAdminStatus"),
|
|
63
|
+
setImage: bindOptionalMethod(api, "changeGroupImage"),
|
|
64
|
+
setColor: bindOptionalMethod(api, "changeThreadColor"),
|
|
65
|
+
setEmoji: bindOptionalMethod(api, "changeThreadEmoji"),
|
|
66
|
+
setNickname: bindOptionalMethod(api, "changeNickname"),
|
|
67
|
+
createPoll: bindOptionalMethod(api, "createPoll"),
|
|
68
|
+
createThemeAI: bindOptionalMethod(api, "createThemeAI"),
|
|
69
|
+
getThemePictures: bindOptionalMethod(api, "getThemePictures"),
|
|
70
|
+
delete: bindOptionalMethod(api, "deleteThread"),
|
|
71
|
+
colors: readOptionalMember(api, "threadColors"),
|
|
72
|
+
handleMessageRequest: bindOptionalMethod(api, "handleMessageRequest"),
|
|
73
|
+
mute: bindOptionalMethod(api, "muteThread"),
|
|
74
|
+
setTitle: bindOptionalMethod(api, "setTitle"),
|
|
75
|
+
search: bindOptionalMethod(api, "searchForThread")
|
|
76
|
+
}),
|
|
77
|
+
users: compactNamespace({
|
|
78
|
+
getID: bindOptionalMethod(api, "getUserID"),
|
|
79
|
+
getInfo: bindOptionalMethod(api, "getUserInfo"),
|
|
80
|
+
getInfoV2: bindOptionalMethod(api, "getUserInfoV2"),
|
|
81
|
+
getFriends: bindOptionalMethod(api, "getFriendsList")
|
|
82
|
+
}),
|
|
83
|
+
account: compactNamespace({
|
|
84
|
+
addExternalModule: bindOptionalMethod(api, "addExternalModule"),
|
|
85
|
+
changeAvatar: bindOptionalMethod(api, "changeAvatar"),
|
|
86
|
+
changeBio: bindOptionalMethod(api, "changeBio"),
|
|
87
|
+
enableAutoSaveAppState: bindOptionalMethod(api, "enableAutoSaveAppState"),
|
|
88
|
+
getCurrentUserID: bindOptionalMethod(api, "getCurrentUserID"),
|
|
89
|
+
handleFriendRequest: bindOptionalMethod(api, "handleFriendRequest"),
|
|
90
|
+
logout: bindOptionalMethod(api, "logout"),
|
|
91
|
+
refreshDtsg: bindOptionalMethod(api, "refreshFb_dtsg"),
|
|
92
|
+
changeBlockedStatus: bindOptionalMethod(api, "changeBlockedStatus"),
|
|
93
|
+
setOptions: bindOptionalMethod(api, "setOptions"),
|
|
94
|
+
setPostReaction: bindOptionalMethod(api, "setPostReaction"),
|
|
95
|
+
unfriend: bindOptionalMethod(api, "unfriend"),
|
|
96
|
+
getAppState: bindOptionalMethod(api, "getAppState"),
|
|
97
|
+
getCookies: bindOptionalMethod(api, "getCookies")
|
|
98
|
+
}),
|
|
99
|
+
realtime: compactNamespace({
|
|
100
|
+
listen: bindLiveMethod(api, "listenMqtt"),
|
|
101
|
+
stop: bindLiveMethod(api, "stopListening"),
|
|
102
|
+
stopAsync: bindLiveMethod(api, "stopListeningAsync"),
|
|
103
|
+
useMiddleware: bindLiveMethod(api, "useMiddleware"),
|
|
104
|
+
removeMiddleware: bindLiveMethod(api, "removeMiddleware"),
|
|
105
|
+
clearMiddleware: bindLiveMethod(api, "clearMiddleware"),
|
|
106
|
+
listMiddleware: bindLiveMethod(api, "listMiddleware"),
|
|
107
|
+
setMiddlewareEnabled: bindLiveMethod(api, "setMiddlewareEnabled")
|
|
108
|
+
}),
|
|
109
|
+
http: compactNamespace({
|
|
110
|
+
get: bindOptionalMethod(api, "httpGet"),
|
|
111
|
+
post: bindOptionalMethod(api, "httpPost"),
|
|
112
|
+
postFormData: bindOptionalMethod(api, "postFormData")
|
|
113
|
+
}),
|
|
114
|
+
scheduler: compactNamespace(readOptionalMember(api, "scheduler") || {})
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function mergeNamespace(fallback, existing) {
|
|
119
|
+
return compactNamespace({ ...fallback, ...(existing || {}) });
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function createFcaClient(api) {
|
|
123
|
+
const fallback = createFallbackNamespaces(api);
|
|
124
|
+
return {
|
|
125
|
+
raw: api,
|
|
126
|
+
messages: mergeNamespace(fallback.messages, readNamespace(api, "messages")),
|
|
127
|
+
threads: mergeNamespace(fallback.threads, readNamespace(api, "threads")),
|
|
128
|
+
users: mergeNamespace(fallback.users, readNamespace(api, "users")),
|
|
129
|
+
account: mergeNamespace(fallback.account, readNamespace(api, "account")),
|
|
130
|
+
realtime: mergeNamespace(fallback.realtime, readNamespace(api, "realtime")),
|
|
131
|
+
http: mergeNamespace(fallback.http, readNamespace(api, "http")),
|
|
132
|
+
scheduler: mergeNamespace(fallback.scheduler, readNamespace(api, "scheduler"))
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
module.exports = { createFcaClient };
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
function parseRowData(raw) {
|
|
4
|
+
if (raw == null) return null;
|
|
5
|
+
if (typeof raw === "object" && !Array.isArray(raw)) return raw;
|
|
6
|
+
if (typeof raw === "string") {
|
|
7
|
+
try {
|
|
8
|
+
const o = JSON.parse(raw);
|
|
9
|
+
return typeof o === "object" && o !== null ? o : null;
|
|
10
|
+
} catch { return null; }
|
|
11
|
+
}
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function normalizeParticipantId(v) {
|
|
16
|
+
if (v == null) return "";
|
|
17
|
+
if (typeof v === "object" && v && "id" in v) return String(v.id);
|
|
18
|
+
return String(v);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function normalizeAddedParticipants(raw) {
|
|
22
|
+
if (!raw || !Array.isArray(raw)) return [];
|
|
23
|
+
return raw.map(normalizeParticipantId).filter(Boolean);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function mergedEventData(ev) {
|
|
27
|
+
const a = ev.logMessageData || {};
|
|
28
|
+
const b = ev.eventData || {};
|
|
29
|
+
return { ...b, ...a };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async function invalidateThreadCacheRow(Thread, threadID) {
|
|
33
|
+
await Thread.update({ data: null }, { where: { threadID } });
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function toThreadParticipant(entry, idFallback) {
|
|
37
|
+
const id = String(entry?.id ?? idFallback ?? "");
|
|
38
|
+
return {
|
|
39
|
+
id,
|
|
40
|
+
name: entry?.name ?? null,
|
|
41
|
+
firstName: entry?.firstName ?? null,
|
|
42
|
+
vanity: entry?.vanity ?? null,
|
|
43
|
+
url: entry?.profileUrl ?? entry?.url ?? null,
|
|
44
|
+
thumbSrc: entry?.thumbSrc ?? null,
|
|
45
|
+
profileUrl: entry?.profileUrl ?? null,
|
|
46
|
+
gender: entry?.gender ?? null,
|
|
47
|
+
type: entry?.type ?? null,
|
|
48
|
+
isFriend: Boolean(entry?.isFriend),
|
|
49
|
+
isBirthday: Boolean(entry?.isBirthday)
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function minimalParticipant(id) {
|
|
54
|
+
return { id, name: null, firstName: null, vanity: null, url: null, thumbSrc: null, profileUrl: null, gender: null, type: null, isFriend: false, isBirthday: false };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function fetchAndMergeParticipants(api, info, idsToFetch, log) {
|
|
58
|
+
const uniq = [...new Set(idsToFetch.map(String).filter(Boolean))];
|
|
59
|
+
if (!uniq.length || !api || typeof api.getUserInfo !== "function") return false;
|
|
60
|
+
try {
|
|
61
|
+
const map = await api.getUserInfo(uniq);
|
|
62
|
+
if (!map || typeof map !== "object") return false;
|
|
63
|
+
const prev = new Map();
|
|
64
|
+
for (const u of Array.isArray(info.userInfo) ? info.userInfo : []) {
|
|
65
|
+
if (u?.id != null) prev.set(String(u.id), u);
|
|
66
|
+
}
|
|
67
|
+
for (const id of uniq) {
|
|
68
|
+
const entry = map[id];
|
|
69
|
+
if (entry && typeof entry === "object") prev.set(id, toThreadParticipant(entry, id));
|
|
70
|
+
}
|
|
71
|
+
const pids = (info.participantIDs || []).map(String);
|
|
72
|
+
info.userInfo = pids.map(pid => {
|
|
73
|
+
const fresh = map[pid];
|
|
74
|
+
if (fresh && typeof fresh === "object") return toThreadParticipant(fresh, pid);
|
|
75
|
+
const old = prev.get(pid);
|
|
76
|
+
if (old) return old;
|
|
77
|
+
return minimalParticipant(pid);
|
|
78
|
+
});
|
|
79
|
+
return true;
|
|
80
|
+
} catch (e) {
|
|
81
|
+
log?.(`thread-info-realtime-sync getUserInfo: ${e?.message || e}`, "warn");
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function syncParticipantListFromEvent(ev, info) {
|
|
87
|
+
const p = ev.participantIDs;
|
|
88
|
+
if (!Array.isArray(p) || !p.length) return false;
|
|
89
|
+
const next = p.map(String).filter(Boolean);
|
|
90
|
+
if (!next.length) return false;
|
|
91
|
+
const prev = JSON.stringify((info.participantIDs || []).map(String).sort());
|
|
92
|
+
if (prev === JSON.stringify([...next].sort())) return false;
|
|
93
|
+
info.participantIDs = next;
|
|
94
|
+
if (Array.isArray(info.userInfo)) {
|
|
95
|
+
const set = new Set(next);
|
|
96
|
+
info.userInfo = info.userInfo.filter(u => set.has(String(u?.id)));
|
|
97
|
+
}
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function tryPatchThemeFromUntyped(data, info) {
|
|
102
|
+
if (!data || typeof data !== "object") return false;
|
|
103
|
+
let mutated = false;
|
|
104
|
+
const stack = [{ obj: data, depth: 0 }];
|
|
105
|
+
const seen = new Set();
|
|
106
|
+
while (stack.length) {
|
|
107
|
+
const { obj, depth } = stack.pop();
|
|
108
|
+
if (!obj || seen.has(obj)) continue;
|
|
109
|
+
seen.add(obj);
|
|
110
|
+
if (depth > 10) continue;
|
|
111
|
+
for (const k of Object.keys(obj)) {
|
|
112
|
+
const v = obj[k];
|
|
113
|
+
const kl = k.toLowerCase();
|
|
114
|
+
if (typeof v === "string") {
|
|
115
|
+
if (kl.includes("emoji") && !kl.includes("unicode")) { info.emoji = v; mutated = true; }
|
|
116
|
+
if (kl.includes("bubble_color") || kl.includes("theme_color") || kl.includes("outgoing_bubble") || kl === "gradient_color" || kl === "accessory_color") {
|
|
117
|
+
const hex = v.replace(/^#/, "").trim();
|
|
118
|
+
if (/^[0-9a-f]{6,12}$/i.test(hex)) { info.color = hex.length >= 8 ? hex.slice(0, 8) : hex; mutated = true; }
|
|
119
|
+
}
|
|
120
|
+
} else if (v && typeof v === "object" && !Array.isArray(v)) {
|
|
121
|
+
stack.push({ obj: v, depth: depth + 1 });
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return mutated;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function tryPatchAdminsFromUntyped(data, info) {
|
|
129
|
+
if (!data || typeof data !== "object") return false;
|
|
130
|
+
const tryArray = (arr) => {
|
|
131
|
+
if (!Array.isArray(arr) || !arr.length) return false;
|
|
132
|
+
const ids = arr.map(normalizeParticipantId).filter(Boolean);
|
|
133
|
+
if (!ids.length) return false;
|
|
134
|
+
info.adminIDs = ids;
|
|
135
|
+
return true;
|
|
136
|
+
};
|
|
137
|
+
const o = data;
|
|
138
|
+
for (const key of Object.keys(o)) {
|
|
139
|
+
if (key.toLowerCase().includes("admin") && Array.isArray(o[key])) {
|
|
140
|
+
if (tryArray(o[key])) return true;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
let mutated = false;
|
|
144
|
+
const walk = (node, depth) => {
|
|
145
|
+
if (!node || typeof node !== "object" || depth > 8) return;
|
|
146
|
+
if (Array.isArray(node)) {
|
|
147
|
+
if (node.length && node.every(x => typeof x === "string" || typeof x === "number")) {
|
|
148
|
+
if (tryArray(node)) mutated = true;
|
|
149
|
+
}
|
|
150
|
+
node.forEach(x => walk(x, depth + 1));
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
for (const k of Object.keys(node)) {
|
|
154
|
+
const kl = k.toLowerCase();
|
|
155
|
+
const v = node[k];
|
|
156
|
+
if (kl.includes("thread_admin") || kl === "admin_ids" || kl === "admins") {
|
|
157
|
+
if (tryArray(v)) mutated = true;
|
|
158
|
+
}
|
|
159
|
+
if (v && typeof v === "object") walk(v, depth + 1);
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
walk(data, 0);
|
|
163
|
+
return mutated;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function tryPatchNickname(data, info) {
|
|
167
|
+
if (!data || typeof data !== "object") return false;
|
|
168
|
+
const o = data;
|
|
169
|
+
const pid = o.participant_id ?? o.participantId ?? o.user_id ?? o.actor_id ?? o.target_id;
|
|
170
|
+
const nn = o.nickname ?? o.new_nickname ?? o.name;
|
|
171
|
+
if (pid == null || nn == null || typeof nn !== "string" || !nn.trim()) return false;
|
|
172
|
+
if (!info.nicknames || typeof info.nicknames !== "object") info.nicknames = {};
|
|
173
|
+
info.nicknames[String(pid)] = nn.trim();
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function tryPatchApprovalMode(data, info) {
|
|
178
|
+
if (!data || typeof data !== "object") return false;
|
|
179
|
+
for (const k of ["approval_mode", "approvalMode", "is_approval_mode_enabled"]) {
|
|
180
|
+
if (typeof data[k] === "boolean") { info.approvalMode = data[k]; return true; }
|
|
181
|
+
if (data[k] === "1" || data[k] === "0" || data[k] === 1 || data[k] === 0) { info.approvalMode = Boolean(Number(data[k])); return true; }
|
|
182
|
+
}
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function tryPatchInviteLink(data, info) {
|
|
187
|
+
if (!data || typeof data !== "object") return false;
|
|
188
|
+
const jm = data.joinable_mode ?? data.joinableMode;
|
|
189
|
+
if (!jm || typeof jm !== "object") return false;
|
|
190
|
+
if (!info.inviteLink || typeof info.inviteLink !== "object") info.inviteLink = {};
|
|
191
|
+
if (jm.mode !== undefined) info.inviteLink.enable = jm.mode === 1 || jm.mode === true;
|
|
192
|
+
if (typeof jm.link === "string") info.inviteLink.link = jm.link;
|
|
193
|
+
return true;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async function applyThreadInfoRealtimeEvent(Thread, threadID, ev, log, api) {
|
|
197
|
+
if (!Thread || !threadID || !ev || ev.type !== "event") return;
|
|
198
|
+
const logType = ev.logMessageType != null ? String(ev.logMessageType) : "";
|
|
199
|
+
const data = mergedEventData(ev);
|
|
200
|
+
try {
|
|
201
|
+
const row = await Thread.findOne({ where: { threadID } });
|
|
202
|
+
const rawData = row && typeof row.get === "function" ? row.get("data") : row?.data;
|
|
203
|
+
let info = parseRowData(rawData);
|
|
204
|
+
if (!row || !info) { await invalidateThreadCacheRow(Thread, threadID); return; }
|
|
205
|
+
let mutated = false;
|
|
206
|
+
if (syncParticipantListFromEvent(ev, info)) mutated = true;
|
|
207
|
+
switch (logType) {
|
|
208
|
+
case "log:thread-name": {
|
|
209
|
+
const name = data.name != null ? String(data.name) : "";
|
|
210
|
+
if (name) { info.threadName = name; info.name = name; mutated = true; }
|
|
211
|
+
break;
|
|
212
|
+
}
|
|
213
|
+
case "log:unsubscribe": {
|
|
214
|
+
const left = normalizeParticipantId(data.leftParticipantFbId);
|
|
215
|
+
if (left) {
|
|
216
|
+
if (Array.isArray(info.participantIDs)) { info.participantIDs = info.participantIDs.map(String).filter(id => id !== left); mutated = true; }
|
|
217
|
+
if (Array.isArray(info.userInfo)) { info.userInfo = info.userInfo.filter(u => String(u?.id) !== left); mutated = true; }
|
|
218
|
+
}
|
|
219
|
+
if (mutated && Array.isArray(info.participantIDs) && info.participantIDs.length) {
|
|
220
|
+
if (await fetchAndMergeParticipants(api, info, info.participantIDs.map(String), log)) mutated = true;
|
|
221
|
+
}
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
224
|
+
case "log:subscribe": {
|
|
225
|
+
const added = normalizeAddedParticipants(data.addedParticipants);
|
|
226
|
+
if (added.length) {
|
|
227
|
+
if (!Array.isArray(info.participantIDs)) info.participantIDs = [];
|
|
228
|
+
const set = new Set(info.participantIDs.map(String));
|
|
229
|
+
for (const id of added) { if (!set.has(id)) { info.participantIDs.push(id); set.add(id); mutated = true; } }
|
|
230
|
+
if (await fetchAndMergeParticipants(api, info, added, log)) mutated = true;
|
|
231
|
+
}
|
|
232
|
+
break;
|
|
233
|
+
}
|
|
234
|
+
case "log:thread-icon":
|
|
235
|
+
case "log:thread-image": {
|
|
236
|
+
const img = data.image;
|
|
237
|
+
const url = img && (img.url || img.uri);
|
|
238
|
+
if (url) { info.imageSrc = String(url); mutated = true; }
|
|
239
|
+
break;
|
|
240
|
+
}
|
|
241
|
+
case "log:thread-color": if (tryPatchThemeFromUntyped(data, info)) mutated = true; break;
|
|
242
|
+
case "log:thread-admins": if (tryPatchAdminsFromUntyped(data, info)) mutated = true; break;
|
|
243
|
+
case "log:user-nickname": if (tryPatchNickname(data, info)) mutated = true; break;
|
|
244
|
+
case "log:thread-approval-mode": if (tryPatchApprovalMode(data, info)) mutated = true; break;
|
|
245
|
+
case "log:link-status": if (tryPatchInviteLink(data, info)) mutated = true; break;
|
|
246
|
+
case "log:approval-queue": {
|
|
247
|
+
const q = data.approvalQueue;
|
|
248
|
+
if (q && typeof q === "object") { info.approvalQueue = [...(Array.isArray(info.approvalQueue) ? info.approvalQueue : []), q]; mutated = true; }
|
|
249
|
+
break;
|
|
250
|
+
}
|
|
251
|
+
case "log:magic-words":
|
|
252
|
+
case "log:thread-poll":
|
|
253
|
+
case "log:thread-pinned":
|
|
254
|
+
case "log:unpin-message":
|
|
255
|
+
case "log:thread-call":
|
|
256
|
+
case "log:user-location":
|
|
257
|
+
break;
|
|
258
|
+
default:
|
|
259
|
+
if (tryPatchThemeFromUntyped(data, info)) mutated = true;
|
|
260
|
+
if (tryPatchAdminsFromUntyped(data, info)) mutated = true;
|
|
261
|
+
if (tryPatchNickname(data, info)) mutated = true;
|
|
262
|
+
if (tryPatchApprovalMode(data, info)) mutated = true;
|
|
263
|
+
if (tryPatchInviteLink(data, info)) mutated = true;
|
|
264
|
+
break;
|
|
265
|
+
}
|
|
266
|
+
if (mutated && typeof row.update === "function") await row.update({ data: info });
|
|
267
|
+
else await invalidateThreadCacheRow(Thread, threadID);
|
|
268
|
+
} catch (e) {
|
|
269
|
+
log?.(`thread-info-realtime-sync: ${e?.message || e}`, "warn");
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function attachThreadInfoRealtimeSync(ctx, models, logger, api) {
|
|
274
|
+
const Thread = models?.Thread;
|
|
275
|
+
if (!Thread || typeof Thread.findOne !== "function") return false;
|
|
276
|
+
const resolveApi = () => api || ctx.api;
|
|
277
|
+
ctx._syncThreadInfoFromEvent = (ev) => {
|
|
278
|
+
if (!ev || ev.type !== "event" || ev.threadID == null) return;
|
|
279
|
+
applyThreadInfoRealtimeEvent(Thread, String(ev.threadID), ev, logger, resolveApi());
|
|
280
|
+
};
|
|
281
|
+
return true;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
module.exports = { attachThreadInfoRealtimeSync, applyThreadInfoRealtimeEvent };
|