@whanext/core 0.13.1 → 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 +22 -0
- package/README.md +98 -0
- package/SECURITY.md +1 -1
- package/dist/index.d.ts +70 -3
- package/dist/index.js +287 -5
- 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(
|
|
@@ -2828,9 +2870,9 @@ var BaileysProvider = class {
|
|
|
2828
2870
|
);
|
|
2829
2871
|
}
|
|
2830
2872
|
#browserDescription() {
|
|
2831
|
-
if (this.#options.browser === "macos" /* MacOS */) return Browsers.macOS("
|
|
2873
|
+
if (this.#options.browser === "macos" /* MacOS */) return Browsers.macOS("Safari");
|
|
2832
2874
|
if (this.#options.browser === "ubuntu" /* Ubuntu */) return Browsers.ubuntu("Chrome");
|
|
2833
|
-
return Browsers.windows("
|
|
2875
|
+
return Browsers.windows("Brave");
|
|
2834
2876
|
}
|
|
2835
2877
|
#isTerminal(error) {
|
|
2836
2878
|
const statusCode = this.#statusCode(error);
|
|
@@ -3021,6 +3063,7 @@ async function create(options = {}) {
|
|
|
3021
3063
|
...options.reconnect ? { reconnect: options.reconnect } : {}
|
|
3022
3064
|
});
|
|
3023
3065
|
return new WhaNextApp(provider, {
|
|
3066
|
+
...options.accountId ? { accountId: options.accountId } : {},
|
|
3024
3067
|
...options.phone ? { phone: options.phone } : {},
|
|
3025
3068
|
...options.prefix !== void 0 ? { prefix: options.prefix } : {},
|
|
3026
3069
|
...options.cache ? { cache: options.cache } : {},
|
|
@@ -3030,8 +3073,243 @@ async function create(options = {}) {
|
|
|
3030
3073
|
}, logger);
|
|
3031
3074
|
}
|
|
3032
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
|
+
|
|
3033
3304
|
// src/commands/guards.ts
|
|
3034
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
|
+
},
|
|
3035
3313
|
group() {
|
|
3036
3314
|
return (context) => context.isGroup || {
|
|
3037
3315
|
allowed: false,
|
|
@@ -3072,19 +3350,23 @@ var guards = {
|
|
|
3072
3350
|
}
|
|
3073
3351
|
};
|
|
3074
3352
|
export {
|
|
3353
|
+
AccountService,
|
|
3075
3354
|
ArgsParser,
|
|
3076
3355
|
Browser,
|
|
3077
3356
|
CommandRouter,
|
|
3078
3357
|
DeferredReply,
|
|
3079
3358
|
Logger,
|
|
3080
3359
|
MemoryCache,
|
|
3360
|
+
MultiCommandRouter,
|
|
3081
3361
|
MuteService,
|
|
3082
3362
|
ParsedCommandOptions,
|
|
3083
3363
|
SqliteMuteStore,
|
|
3084
3364
|
User,
|
|
3085
3365
|
WhaNextApp,
|
|
3086
3366
|
WhaNextError,
|
|
3367
|
+
WhaNextMultiApp,
|
|
3087
3368
|
create,
|
|
3369
|
+
createMulti,
|
|
3088
3370
|
defineCommand,
|
|
3089
3371
|
defineCommandGroup,
|
|
3090
3372
|
defineCommands,
|