@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/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(
@@ -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 normalized = normalizeMessageContent(input.message);
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 normalized = normalizeMessageContent(quoted);
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("Chrome");
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("Chrome");
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,