@ezychat/wa 0.1.65 → 0.1.67

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.
@@ -93,6 +93,7 @@ export declare class BaileysAdapter extends EventEmitter implements WaAdapter {
93
93
  downloadMedia(messageId: string): Promise<Buffer | null>;
94
94
  getGroupInfo(groupJid: string): Promise<WaGroupInfo>;
95
95
  checkNumber(phone: string): Promise<string | null>;
96
+ setBlocked(jid: string, blocked: boolean): Promise<void>;
96
97
  getAbout(jid: string): Promise<string | null>;
97
98
  getProfilePicture(jid: string): Promise<string | null>;
98
99
  }
@@ -630,7 +630,9 @@ export class BaileysAdapter extends EventEmitter {
630
630
  return { id, timestamp: toMs(res.messageTimestamp) };
631
631
  }
632
632
  sendText(chatJid, text, opts) {
633
- return this.send(chatJid, { text }, opts);
633
+ // A tag is `@<number>` in the text plus the member's address in `mentions` (contextInfo).
634
+ const mentions = opts?.mentions?.length ? { mentions: [...opts.mentions] } : {};
635
+ return this.send(chatJid, { text, ...mentions }, opts);
634
636
  }
635
637
  sendMedia(chatJid, file, opts) {
636
638
  const { buffer, mime, fileName, caption } = file;
@@ -713,6 +715,12 @@ export class BaileysAdapter extends EventEmitter {
713
715
  this.logger.info({ found: jid !== null }, 'number check');
714
716
  return jid;
715
717
  }
718
+ async setBlocked(jid, blocked) {
719
+ const sock = this.requireOpen();
720
+ await sock.updateBlockStatus(jid, blocked ? 'block' : 'unblock');
721
+ // The action only, never the number.
722
+ this.logger.info({ blocked }, 'block status');
723
+ }
716
724
  async getAbout(jid) {
717
725
  if (!this.sock || this._status.state !== 'open')
718
726
  throw new WaUnavailableError();
@@ -8,6 +8,8 @@ export interface FakeSentRecord {
8
8
  id: string;
9
9
  /** Reply options as the real adapter would receive them. */
10
10
  reply?: WaSendOptions;
11
+ /** Text sends: the members tagged (WhatsApp mentions). */
12
+ mentions?: string[];
11
13
  }
12
14
  /**
13
15
  * In-memory WaAdapter for tests and `--fake-wa` dev mode. Never touches the network.
@@ -107,6 +109,9 @@ export declare class FakeWaAdapter extends EventEmitter implements WaAdapter {
107
109
  getGroupInfo(groupJid: string): Promise<WaGroupInfo>;
108
110
  getAbout(jid: string): Promise<string | null>;
109
111
  checkNumber(phone: string): Promise<string | null>;
112
+ /** Numbers blocked through `setBlocked` (test helper reads it). */
113
+ readonly blocked: Set<string>;
114
+ setBlocked(jid: string, blocked: boolean): Promise<void>;
110
115
  /** checkNumber answers null for this number (every other valid number has an account). */
111
116
  setNotOnWhatsApp(phone: string): void;
112
117
  /** Make the next getAbout calls fail, as a WhatsApp request error would. */
@@ -138,7 +138,13 @@ export class FakeWaAdapter extends EventEmitter {
138
138
  async sendText(chatJid, text, opts) {
139
139
  this.beforeSend();
140
140
  const id = `${this.idPrefix}-OUT-${FAKE_RUN}-${++this.outCounter}`;
141
- this.sent.push({ chatJid, text, id, ...(opts?.quotedId ? { reply: opts } : {}) });
141
+ this.sent.push({
142
+ chatJid,
143
+ text,
144
+ id,
145
+ ...(opts?.quotedId ? { reply: opts } : {}),
146
+ ...(opts?.mentions?.length ? { mentions: [...opts.mentions] } : {}),
147
+ });
142
148
  return this.afterSend(chatJid, id);
143
149
  }
144
150
  async sendMedia(chatJid, file, opts) {
@@ -205,6 +211,16 @@ export class FakeWaAdapter extends EventEmitter {
205
211
  return Promise.resolve(null);
206
212
  return Promise.resolve(`${phone}@s.whatsapp.net`);
207
213
  }
214
+ /** Numbers blocked through `setBlocked` (test helper reads it). */
215
+ blocked = new Set();
216
+ async setBlocked(jid, blocked) {
217
+ if (this._status.state !== 'open')
218
+ throw new WaUnavailableError();
219
+ if (blocked)
220
+ this.blocked.add(jid);
221
+ else
222
+ this.blocked.delete(jid);
223
+ }
208
224
  /** checkNumber answers null for this number (every other valid number has an account). */
209
225
  setNotOnWhatsApp(phone) {
210
226
  this.notOnWhatsApp.add(phone);
package/dist/types.d.ts CHANGED
@@ -134,6 +134,11 @@ export interface WaSendOptions {
134
134
  quotedId?: string;
135
135
  /** Used when the quoted message is no longer in the adapter's live cache. */
136
136
  quoted?: WaQuotedRef;
137
+ /**
138
+ * Text sends in groups: the members tagged in the text (each written as `@<user part of the JID>`).
139
+ * Only for adapters with `capabilities.mentions`.
140
+ */
141
+ mentions?: string[];
137
142
  }
138
143
  /** The message a reaction points at: WhatsApp matches it by chat, id, side and group author. */
139
144
  export interface WaReactionTarget {
@@ -179,6 +184,18 @@ export interface WaCapabilities {
179
184
  templates: boolean;
180
185
  /** Receives messages at a public https address (a webhook over Phone access / the tunnel). */
181
186
  needsPublicUrl: boolean;
187
+ /**
188
+ * Blocks and unblocks numbers (`setBlocked`). Optional so adapters written before WhatsApp Safety
189
+ * keep compiling: absent = cannot block.
190
+ */
191
+ block?: boolean;
192
+ /**
193
+ * Blocking works only within this many hours of the number's last message (Business API: 24);
194
+ * null or absent: any time.
195
+ */
196
+ blockWindowHours?: number | null;
197
+ /** Sends WhatsApp mentions (`WaSendOptions.mentions`); absent: no. */
198
+ mentions?: boolean;
182
199
  }
183
200
  /** The capabilities of an adapter that declares none: a phone linked by QR (Baileys, the fake). */
184
201
  export declare const LINKED_PHONE_CAPABILITIES: WaCapabilities;
@@ -244,6 +261,11 @@ export interface WaAdapter {
244
261
  * JID, or null when it has none. Throws WaUnavailableError when offline.
245
262
  */
246
263
  checkNumber(phone: string): Promise<string | null>;
264
+ /**
265
+ * Block (true) or unblock a number (adapters with `capabilities.block`): it can no longer message
266
+ * this number. Throws WaUnavailableError when offline.
267
+ */
268
+ setBlocked?(jid: string, blocked: boolean): Promise<void>;
247
269
  /** Resolve existing local phone/LID mappings only; must not query WhatsApp. */
248
270
  getContactAliases(jids: string[]): Promise<WaContactAlias[]>;
249
271
  }
package/dist/types.js CHANGED
@@ -6,6 +6,9 @@ export const LINKED_PHONE_CAPABILITIES = Object.freeze({
6
6
  replyWindowHours: null,
7
7
  templates: false,
8
8
  needsPublicUrl: false,
9
+ block: true,
10
+ blockWindowHours: null,
11
+ mentions: true,
9
12
  });
10
13
  export class WaUnavailableError extends Error {
11
14
  code = 'wa_unavailable';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ezychat/wa",
3
- "version": "0.1.65",
3
+ "version": "0.1.67",
4
4
  "type": "module",
5
5
  "description": "EzyChat core: WhatsApp adapter interface, Baileys adapter and fake adapter.",
6
6
  "license": "MIT",
@@ -37,7 +37,7 @@
37
37
  "typecheck": "tsc --noEmit"
38
38
  },
39
39
  "dependencies": {
40
- "@ezychat/shared": "0.1.65",
40
+ "@ezychat/shared": "0.1.67",
41
41
  "baileys": "^7.0.0-rc14",
42
42
  "pino": "^10.4.0",
43
43
  "qrcode": "^1.5.4"