@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/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((resolve2) => state.queue.push(resolve2));
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((resolve2, reject) => {
2107
+ const connected = new Promise((resolve3, reject) => {
2066
2108
  unsubscribe = this.#provider.on("connection", (update) => {
2067
- if (update.state === "connected") resolve2();
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("Chrome");
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("Chrome");
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,