@whanext/core 0.13.0 → 0.14.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 +27 -0
- package/README.md +98 -0
- package/SECURITY.md +1 -1
- package/dist/index.d.ts +70 -3
- package/dist/index.js +317 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -370,7 +370,7 @@ var CommandConcurrencyController = class {
|
|
|
370
370
|
recoverable: true
|
|
371
371
|
});
|
|
372
372
|
} else if (strategy === "queue" && state.active >= max) {
|
|
373
|
-
await new Promise((
|
|
373
|
+
await new Promise((resolve3) => state.queue.push(resolve3));
|
|
374
374
|
}
|
|
375
375
|
const controller = new AbortController();
|
|
376
376
|
state.controllers.add(controller);
|
|
@@ -392,6 +392,8 @@ var CommandConcurrencyController = class {
|
|
|
392
392
|
var CommandContextImplementation = class {
|
|
393
393
|
message;
|
|
394
394
|
user;
|
|
395
|
+
account;
|
|
396
|
+
isOwner;
|
|
395
397
|
chat;
|
|
396
398
|
group;
|
|
397
399
|
command;
|
|
@@ -413,6 +415,8 @@ var CommandContextImplementation = class {
|
|
|
413
415
|
constructor(options) {
|
|
414
416
|
this.message = options.message;
|
|
415
417
|
this.user = options.message.sender;
|
|
418
|
+
this.account = options.services.account;
|
|
419
|
+
this.isOwner = this.account.isOwner(options.message);
|
|
416
420
|
this.chat = { id: options.message.chatId, isGroup: options.message.isGroup };
|
|
417
421
|
this.command = options.command;
|
|
418
422
|
this.commands = options.commands;
|
|
@@ -979,6 +983,12 @@ _Nenhum comando dispon\xEDvel._`;
|
|
|
979
983
|
}
|
|
980
984
|
}
|
|
981
985
|
async #authorizeLegacy(command, context) {
|
|
986
|
+
if (command.onlyOwner && !context.isOwner) {
|
|
987
|
+
throw new WhaNextError(
|
|
988
|
+
"COMMAND_NOT_ALLOWED",
|
|
989
|
+
"This command can only be used by the connected WhatsApp account."
|
|
990
|
+
);
|
|
991
|
+
}
|
|
982
992
|
if (command.onlyGroup && !context.isGroup) {
|
|
983
993
|
throw new WhaNextError("COMMAND_NOT_ALLOWED", "This command can only be used in groups.");
|
|
984
994
|
}
|
|
@@ -1158,6 +1168,15 @@ function createLegacyServices(group) {
|
|
|
1158
1168
|
}
|
|
1159
1169
|
});
|
|
1160
1170
|
return {
|
|
1171
|
+
account: {
|
|
1172
|
+
id: void 0,
|
|
1173
|
+
get ids() {
|
|
1174
|
+
return [];
|
|
1175
|
+
},
|
|
1176
|
+
isOwner(message) {
|
|
1177
|
+
return message.keys.fromMe;
|
|
1178
|
+
}
|
|
1179
|
+
},
|
|
1161
1180
|
groups: group,
|
|
1162
1181
|
users: new UserService(group),
|
|
1163
1182
|
messages: unavailable,
|
|
@@ -1696,6 +1715,26 @@ var TypedEventEmitter = class {
|
|
|
1696
1715
|
}
|
|
1697
1716
|
};
|
|
1698
1717
|
|
|
1718
|
+
// src/services/account-service.ts
|
|
1719
|
+
var AccountService = class {
|
|
1720
|
+
id;
|
|
1721
|
+
#provider;
|
|
1722
|
+
constructor(provider, id) {
|
|
1723
|
+
this.#provider = provider;
|
|
1724
|
+
this.id = id;
|
|
1725
|
+
}
|
|
1726
|
+
get ids() {
|
|
1727
|
+
return uniqueIdentities(this.#provider.getCurrentUserIds());
|
|
1728
|
+
}
|
|
1729
|
+
isOwner(message) {
|
|
1730
|
+
if (message.keys.fromMe) {
|
|
1731
|
+
return true;
|
|
1732
|
+
}
|
|
1733
|
+
const currentIds = this.ids;
|
|
1734
|
+
return message.senderIds.some((senderId) => currentIds.some((currentId) => identitiesMatch(senderId, currentId)));
|
|
1735
|
+
}
|
|
1736
|
+
};
|
|
1737
|
+
|
|
1699
1738
|
// src/services/chat-service.ts
|
|
1700
1739
|
var ChatService = class {
|
|
1701
1740
|
#provider;
|
|
@@ -1982,6 +2021,7 @@ var MessageService = class {
|
|
|
1982
2021
|
|
|
1983
2022
|
// src/app/whanext-app.ts
|
|
1984
2023
|
var WhaNextApp = class {
|
|
2024
|
+
account;
|
|
1985
2025
|
message;
|
|
1986
2026
|
media;
|
|
1987
2027
|
group;
|
|
@@ -2000,6 +2040,7 @@ var WhaNextApp = class {
|
|
|
2000
2040
|
constructor(provider, options = {}, logger = new Logger(options.logger)) {
|
|
2001
2041
|
this.#provider = provider;
|
|
2002
2042
|
this.#phone = options.phone;
|
|
2043
|
+
this.account = new AccountService(provider, options.accountId);
|
|
2003
2044
|
this.logger = logger;
|
|
2004
2045
|
const cache = options.cache?.store ?? new MemoryCache(
|
|
2005
2046
|
options.cache?.memoryMaxEntries === void 0 ? void 0 : { maxEntries: options.cache.memoryMaxEntries }
|
|
@@ -2014,6 +2055,7 @@ var WhaNextApp = class {
|
|
|
2014
2055
|
const muteStore = muteEnabled ? options.mute?.store ?? new SqliteMuteStore(options.mute?.database) : void 0;
|
|
2015
2056
|
this.mute = new MuteService(provider, muteStore);
|
|
2016
2057
|
this.commands = new CommandRouter({
|
|
2058
|
+
account: this.account,
|
|
2017
2059
|
messages: this.message,
|
|
2018
2060
|
media: this.media,
|
|
2019
2061
|
groups: this.group,
|
|
@@ -2062,9 +2104,9 @@ var WhaNextApp = class {
|
|
|
2062
2104
|
this.logger.info("Login started");
|
|
2063
2105
|
let unsubscribe = () => void 0;
|
|
2064
2106
|
let timer;
|
|
2065
|
-
const connected = new Promise((
|
|
2107
|
+
const connected = new Promise((resolve3, reject) => {
|
|
2066
2108
|
unsubscribe = this.#provider.on("connection", (update) => {
|
|
2067
|
-
if (update.state === "connected")
|
|
2109
|
+
if (update.state === "connected") resolve3();
|
|
2068
2110
|
if (update.state === "closed") {
|
|
2069
2111
|
reject(
|
|
2070
2112
|
new WhaNextError(
|
|
@@ -2281,14 +2323,39 @@ import {
|
|
|
2281
2323
|
getContentType,
|
|
2282
2324
|
normalizeMessageContent
|
|
2283
2325
|
} from "@whiskeysockets/baileys";
|
|
2326
|
+
function unwrapMessageContent(input) {
|
|
2327
|
+
if (!input) return void 0;
|
|
2328
|
+
const normalized = normalizeMessageContent(input);
|
|
2329
|
+
const content = extractMessageContent(normalized);
|
|
2330
|
+
if (!content) return void 0;
|
|
2331
|
+
const nested = content.ephemeralMessage?.message ?? content.viewOnceMessage?.message ?? content.viewOnceMessageV2?.message ?? content.viewOnceMessageV2Extension?.message;
|
|
2332
|
+
return nested ? unwrapMessageContent(nested) : content;
|
|
2333
|
+
}
|
|
2334
|
+
function extractQuotedBaileysMessage(input) {
|
|
2335
|
+
const chatId = input.key.remoteJid;
|
|
2336
|
+
const content = unwrapMessageContent(input.message);
|
|
2337
|
+
if (!chatId || !content) return void 0;
|
|
2338
|
+
const type = getContentType(content);
|
|
2339
|
+
const node = type ? content[type] : void 0;
|
|
2340
|
+
const context = getContextInfo(node);
|
|
2341
|
+
if (!context?.stanzaId || !context.quotedMessage) return void 0;
|
|
2342
|
+
return {
|
|
2343
|
+
key: {
|
|
2344
|
+
id: context.stanzaId,
|
|
2345
|
+
remoteJid: context.remoteJid ?? chatId,
|
|
2346
|
+
fromMe: false,
|
|
2347
|
+
...context.participant ? { participant: context.participant } : {}
|
|
2348
|
+
},
|
|
2349
|
+
message: context.quotedMessage
|
|
2350
|
+
};
|
|
2351
|
+
}
|
|
2284
2352
|
function normalizeBaileysMessage(input) {
|
|
2285
2353
|
const chatId = input.key.remoteJid;
|
|
2286
2354
|
const id = input.key.id;
|
|
2287
2355
|
if (!chatId || !id || !input.message) {
|
|
2288
2356
|
return void 0;
|
|
2289
2357
|
}
|
|
2290
|
-
const
|
|
2291
|
-
const content = extractMessageContent(normalized);
|
|
2358
|
+
const content = unwrapMessageContent(input.message);
|
|
2292
2359
|
if (!content) {
|
|
2293
2360
|
return void 0;
|
|
2294
2361
|
}
|
|
@@ -2429,8 +2496,7 @@ function getQuoted(context, chatId) {
|
|
|
2429
2496
|
return void 0;
|
|
2430
2497
|
}
|
|
2431
2498
|
const quoted = context.quotedMessage;
|
|
2432
|
-
const
|
|
2433
|
-
const content = extractMessageContent(normalized);
|
|
2499
|
+
const content = unwrapMessageContent(quoted);
|
|
2434
2500
|
const result = {
|
|
2435
2501
|
key: {
|
|
2436
2502
|
id: context.stanzaId,
|
|
@@ -2731,6 +2797,8 @@ var BaileysProvider = class {
|
|
|
2731
2797
|
if (type !== "notify") return;
|
|
2732
2798
|
for (const raw of messages) {
|
|
2733
2799
|
if (raw.key.id && raw.message) this.#remember(raw);
|
|
2800
|
+
const quoted = extractQuotedBaileysMessage(raw);
|
|
2801
|
+
if (quoted?.key.id && quoted.message) this.#remember(quoted);
|
|
2734
2802
|
const message = normalizeBaileysMessage(raw);
|
|
2735
2803
|
if (message) void this.#events.emit("message", message);
|
|
2736
2804
|
}
|
|
@@ -2802,9 +2870,9 @@ var BaileysProvider = class {
|
|
|
2802
2870
|
);
|
|
2803
2871
|
}
|
|
2804
2872
|
#browserDescription() {
|
|
2805
|
-
if (this.#options.browser === "macos" /* MacOS */) return Browsers.macOS("
|
|
2873
|
+
if (this.#options.browser === "macos" /* MacOS */) return Browsers.macOS("Safari");
|
|
2806
2874
|
if (this.#options.browser === "ubuntu" /* Ubuntu */) return Browsers.ubuntu("Chrome");
|
|
2807
|
-
return Browsers.windows("
|
|
2875
|
+
return Browsers.windows("Brave");
|
|
2808
2876
|
}
|
|
2809
2877
|
#isTerminal(error) {
|
|
2810
2878
|
const statusCode = this.#statusCode(error);
|
|
@@ -2995,6 +3063,7 @@ async function create(options = {}) {
|
|
|
2995
3063
|
...options.reconnect ? { reconnect: options.reconnect } : {}
|
|
2996
3064
|
});
|
|
2997
3065
|
return new WhaNextApp(provider, {
|
|
3066
|
+
...options.accountId ? { accountId: options.accountId } : {},
|
|
2998
3067
|
...options.phone ? { phone: options.phone } : {},
|
|
2999
3068
|
...options.prefix !== void 0 ? { prefix: options.prefix } : {},
|
|
3000
3069
|
...options.cache ? { cache: options.cache } : {},
|
|
@@ -3004,8 +3073,243 @@ async function create(options = {}) {
|
|
|
3004
3073
|
}, logger);
|
|
3005
3074
|
}
|
|
3006
3075
|
|
|
3076
|
+
// src/app/multi-app.ts
|
|
3077
|
+
import {
|
|
3078
|
+
join,
|
|
3079
|
+
resolve as resolve2
|
|
3080
|
+
} from "path";
|
|
3081
|
+
var MultiCommandRouter = class {
|
|
3082
|
+
#apps;
|
|
3083
|
+
constructor(apps) {
|
|
3084
|
+
this.#apps = apps;
|
|
3085
|
+
}
|
|
3086
|
+
command(definition) {
|
|
3087
|
+
for (const app of this.#apps.values()) {
|
|
3088
|
+
app.commands.command(definition);
|
|
3089
|
+
}
|
|
3090
|
+
return this;
|
|
3091
|
+
}
|
|
3092
|
+
use(middleware) {
|
|
3093
|
+
for (const app of this.#apps.values()) {
|
|
3094
|
+
app.commands.use(middleware);
|
|
3095
|
+
}
|
|
3096
|
+
return this;
|
|
3097
|
+
}
|
|
3098
|
+
onError(handler) {
|
|
3099
|
+
const unsubscribe = [...this.#apps.values()].map((app) => app.commands.onError(handler));
|
|
3100
|
+
return () => {
|
|
3101
|
+
for (const remove of unsubscribe) remove();
|
|
3102
|
+
};
|
|
3103
|
+
}
|
|
3104
|
+
async load(dirPath, options = {}) {
|
|
3105
|
+
return Promise.all([...this.#apps].map(async ([accountId, app]) => ({
|
|
3106
|
+
accountId,
|
|
3107
|
+
result: await app.commands.load(dirPath, options)
|
|
3108
|
+
})));
|
|
3109
|
+
}
|
|
3110
|
+
};
|
|
3111
|
+
var WhaNextMultiApp = class {
|
|
3112
|
+
commands;
|
|
3113
|
+
#apps;
|
|
3114
|
+
constructor(apps) {
|
|
3115
|
+
this.#apps = apps;
|
|
3116
|
+
this.commands = new MultiCommandRouter(apps);
|
|
3117
|
+
}
|
|
3118
|
+
get size() {
|
|
3119
|
+
return this.#apps.size;
|
|
3120
|
+
}
|
|
3121
|
+
get isReady() {
|
|
3122
|
+
return this.#apps.size > 0 && [...this.#apps.values()].every((app) => app.isReady);
|
|
3123
|
+
}
|
|
3124
|
+
ids() {
|
|
3125
|
+
return [...this.#apps.keys()];
|
|
3126
|
+
}
|
|
3127
|
+
has(accountId) {
|
|
3128
|
+
return this.#apps.has(accountId);
|
|
3129
|
+
}
|
|
3130
|
+
get(accountId) {
|
|
3131
|
+
return this.#apps.get(accountId);
|
|
3132
|
+
}
|
|
3133
|
+
values() {
|
|
3134
|
+
return [...this.#apps.values()];
|
|
3135
|
+
}
|
|
3136
|
+
router() {
|
|
3137
|
+
return this.commands;
|
|
3138
|
+
}
|
|
3139
|
+
on(event, listener) {
|
|
3140
|
+
const unsubscribe = [...this.#apps].map(([accountId, app]) => app.on(event, (payload) => listener({ accountId, app, payload })));
|
|
3141
|
+
return () => {
|
|
3142
|
+
for (const remove of unsubscribe) remove();
|
|
3143
|
+
};
|
|
3144
|
+
}
|
|
3145
|
+
health() {
|
|
3146
|
+
return [...this.#apps].map(([accountId, app]) => ({
|
|
3147
|
+
accountId,
|
|
3148
|
+
...app.health()
|
|
3149
|
+
}));
|
|
3150
|
+
}
|
|
3151
|
+
async login(options = {}) {
|
|
3152
|
+
const results = await Promise.allSettled(
|
|
3153
|
+
[...this.#apps].map(async ([accountId, app]) => {
|
|
3154
|
+
await app.login({
|
|
3155
|
+
...options.timeoutMs !== void 0 ? { timeoutMs: options.timeoutMs } : {},
|
|
3156
|
+
...options.onCode ? { onCode: (code) => options.onCode?.(accountId, code) } : {}
|
|
3157
|
+
});
|
|
3158
|
+
})
|
|
3159
|
+
);
|
|
3160
|
+
const failures = results.map((result, index) => ({ result, accountId: this.ids()[index] })).filter((entry) => entry.result.status === "rejected" && entry.accountId !== void 0);
|
|
3161
|
+
if (failures.length === 0) {
|
|
3162
|
+
return;
|
|
3163
|
+
}
|
|
3164
|
+
throw new WhaNextError(
|
|
3165
|
+
"CONNECTION_FAILED",
|
|
3166
|
+
"One or more WhatsApp accounts could not complete login.",
|
|
3167
|
+
{
|
|
3168
|
+
cause: new AggregateError(
|
|
3169
|
+
failures.map(({ result }) => result.reason),
|
|
3170
|
+
"Multi-account login failed."
|
|
3171
|
+
),
|
|
3172
|
+
context: {
|
|
3173
|
+
accounts: failures.map(({ accountId }) => accountId)
|
|
3174
|
+
},
|
|
3175
|
+
recoverable: true
|
|
3176
|
+
}
|
|
3177
|
+
);
|
|
3178
|
+
}
|
|
3179
|
+
async disconnect() {
|
|
3180
|
+
const results = await Promise.allSettled(
|
|
3181
|
+
[...this.#apps.values()].map((app) => app.disconnect())
|
|
3182
|
+
);
|
|
3183
|
+
const failures = results.filter((result) => result.status === "rejected");
|
|
3184
|
+
if (failures.length > 0) {
|
|
3185
|
+
throw new WhaNextError(
|
|
3186
|
+
"CONNECTION_FAILED",
|
|
3187
|
+
"One or more WhatsApp accounts could not disconnect cleanly.",
|
|
3188
|
+
{
|
|
3189
|
+
cause: new AggregateError(
|
|
3190
|
+
failures.map((result) => result.reason),
|
|
3191
|
+
"Multi-account disconnect failed."
|
|
3192
|
+
),
|
|
3193
|
+
recoverable: true
|
|
3194
|
+
}
|
|
3195
|
+
);
|
|
3196
|
+
}
|
|
3197
|
+
}
|
|
3198
|
+
};
|
|
3199
|
+
async function createMulti(options) {
|
|
3200
|
+
if (options.accounts.length === 0) {
|
|
3201
|
+
throw new WhaNextError(
|
|
3202
|
+
"ARGUMENT_INVALID",
|
|
3203
|
+
"At least one WhatsApp account is required."
|
|
3204
|
+
);
|
|
3205
|
+
}
|
|
3206
|
+
const {
|
|
3207
|
+
accounts,
|
|
3208
|
+
authRoot = "./sessions",
|
|
3209
|
+
...shared
|
|
3210
|
+
} = options;
|
|
3211
|
+
const normalizedIds = /* @__PURE__ */ new Set();
|
|
3212
|
+
const authPaths = /* @__PURE__ */ new Set();
|
|
3213
|
+
for (const account of accounts) {
|
|
3214
|
+
validateAccountId(account.id);
|
|
3215
|
+
const normalizedId = account.id.toLowerCase();
|
|
3216
|
+
if (normalizedIds.has(normalizedId)) {
|
|
3217
|
+
throw new WhaNextError(
|
|
3218
|
+
"ARGUMENT_INVALID",
|
|
3219
|
+
`The multi-account id "${account.id}" is duplicated.`
|
|
3220
|
+
);
|
|
3221
|
+
}
|
|
3222
|
+
normalizedIds.add(normalizedId);
|
|
3223
|
+
if (account.provider === void 0) {
|
|
3224
|
+
const authPath = resolve2(account.auth ?? join(authRoot, account.id));
|
|
3225
|
+
if (authPaths.has(authPath)) {
|
|
3226
|
+
throw new WhaNextError(
|
|
3227
|
+
"ARGUMENT_INVALID",
|
|
3228
|
+
"Each WhatsApp account must use a different auth directory.",
|
|
3229
|
+
{ context: { auth: authPath } }
|
|
3230
|
+
);
|
|
3231
|
+
}
|
|
3232
|
+
authPaths.add(authPath);
|
|
3233
|
+
}
|
|
3234
|
+
}
|
|
3235
|
+
const apps = /* @__PURE__ */ new Map();
|
|
3236
|
+
for (const account of accounts) {
|
|
3237
|
+
const {
|
|
3238
|
+
id,
|
|
3239
|
+
...overrides
|
|
3240
|
+
} = account;
|
|
3241
|
+
const merged = mergeCreateOptions(shared, overrides);
|
|
3242
|
+
if (merged.mute?.enabled === true && merged.mute.store === void 0 && merged.mute.database === void 0) {
|
|
3243
|
+
merged.mute = {
|
|
3244
|
+
...merged.mute,
|
|
3245
|
+
database: join("./data", `whanext-${id}.sqlite`)
|
|
3246
|
+
};
|
|
3247
|
+
}
|
|
3248
|
+
const app = await create({
|
|
3249
|
+
...merged,
|
|
3250
|
+
accountId: id,
|
|
3251
|
+
auth: overrides.auth ?? join(authRoot, id)
|
|
3252
|
+
});
|
|
3253
|
+
apps.set(id, app);
|
|
3254
|
+
}
|
|
3255
|
+
return new WhaNextMultiApp(apps);
|
|
3256
|
+
}
|
|
3257
|
+
function validateAccountId(accountId) {
|
|
3258
|
+
if (!/^[a-z0-9][a-z0-9_-]{0,63}$/.test(accountId)) {
|
|
3259
|
+
throw new WhaNextError(
|
|
3260
|
+
"ARGUMENT_INVALID",
|
|
3261
|
+
"Multi-account ids must use lowercase letters, numbers, underscores or hyphens.",
|
|
3262
|
+
{ context: { accountId } }
|
|
3263
|
+
);
|
|
3264
|
+
}
|
|
3265
|
+
}
|
|
3266
|
+
function mergeCreateOptions(shared, account) {
|
|
3267
|
+
const merged = {
|
|
3268
|
+
...shared,
|
|
3269
|
+
...account
|
|
3270
|
+
};
|
|
3271
|
+
if (shared.cache || account.cache) {
|
|
3272
|
+
merged.cache = {
|
|
3273
|
+
...shared.cache,
|
|
3274
|
+
...account.cache
|
|
3275
|
+
};
|
|
3276
|
+
}
|
|
3277
|
+
if (shared.mute || account.mute) {
|
|
3278
|
+
merged.mute = {
|
|
3279
|
+
...shared.mute,
|
|
3280
|
+
...account.mute
|
|
3281
|
+
};
|
|
3282
|
+
}
|
|
3283
|
+
if (shared.router || account.router) {
|
|
3284
|
+
merged.router = {
|
|
3285
|
+
...shared.router,
|
|
3286
|
+
...account.router
|
|
3287
|
+
};
|
|
3288
|
+
}
|
|
3289
|
+
if (shared.reconnect || account.reconnect) {
|
|
3290
|
+
merged.reconnect = {
|
|
3291
|
+
...shared.reconnect,
|
|
3292
|
+
...account.reconnect
|
|
3293
|
+
};
|
|
3294
|
+
}
|
|
3295
|
+
if (shared.logger && account.logger && typeof shared.logger === "object" && typeof account.logger === "object") {
|
|
3296
|
+
merged.logger = {
|
|
3297
|
+
...shared.logger,
|
|
3298
|
+
...account.logger
|
|
3299
|
+
};
|
|
3300
|
+
}
|
|
3301
|
+
return merged;
|
|
3302
|
+
}
|
|
3303
|
+
|
|
3007
3304
|
// src/commands/guards.ts
|
|
3008
3305
|
var guards = {
|
|
3306
|
+
owner() {
|
|
3307
|
+
return (context) => context.isOwner || {
|
|
3308
|
+
allowed: false,
|
|
3309
|
+
code: "COMMAND_NOT_ALLOWED",
|
|
3310
|
+
message: "This command can only be used by the connected WhatsApp account."
|
|
3311
|
+
};
|
|
3312
|
+
},
|
|
3009
3313
|
group() {
|
|
3010
3314
|
return (context) => context.isGroup || {
|
|
3011
3315
|
allowed: false,
|
|
@@ -3046,19 +3350,23 @@ var guards = {
|
|
|
3046
3350
|
}
|
|
3047
3351
|
};
|
|
3048
3352
|
export {
|
|
3353
|
+
AccountService,
|
|
3049
3354
|
ArgsParser,
|
|
3050
3355
|
Browser,
|
|
3051
3356
|
CommandRouter,
|
|
3052
3357
|
DeferredReply,
|
|
3053
3358
|
Logger,
|
|
3054
3359
|
MemoryCache,
|
|
3360
|
+
MultiCommandRouter,
|
|
3055
3361
|
MuteService,
|
|
3056
3362
|
ParsedCommandOptions,
|
|
3057
3363
|
SqliteMuteStore,
|
|
3058
3364
|
User,
|
|
3059
3365
|
WhaNextApp,
|
|
3060
3366
|
WhaNextError,
|
|
3367
|
+
WhaNextMultiApp,
|
|
3061
3368
|
create,
|
|
3369
|
+
createMulti,
|
|
3062
3370
|
defineCommand,
|
|
3063
3371
|
defineCommandGroup,
|
|
3064
3372
|
defineCommands,
|