@ezychat/wa 0.1.49 → 0.1.51
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/baileys/adapter.d.ts +2 -1
- package/dist/baileys/adapter.js +31 -3
- package/dist/baileys/mapping.d.ts +15 -3
- package/dist/baileys/mapping.js +86 -2
- package/dist/fake/fake-adapter.d.ts +17 -1
- package/dist/fake/fake-adapter.js +37 -0
- package/dist/types.d.ts +42 -0
- package/package.json +2 -2
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { EventEmitter } from 'node:events';
|
|
2
2
|
import { type BaileysEventMap, type MessageUpsertType, type MessageUserReceiptUpdate, type WAMessageUpdate, type WAMessage } from 'baileys';
|
|
3
3
|
import type { WaStatus } from '@ezychat/shared';
|
|
4
|
-
import { type SendResult, type WaAdapter, type WaAdapterEvents, type WaAdapterOptions, type WaContactAlias, type WaGroupInfo, type WaMessageStatusUpdate, type WaSendFile, type WaSendOptions } from '../types.js';
|
|
4
|
+
import { type SendResult, type WaAdapter, type WaAdapterEvents, type WaAdapterOptions, type WaContactAlias, type WaGroupInfo, type WaMessageStatusUpdate, type WaReactionTarget, type WaSendFile, type WaSendOptions } from '../types.js';
|
|
5
5
|
/** True when a send error means "the connection went away" (retry later) rather than a real rejection. */
|
|
6
6
|
export declare function isConnectionError(err: unknown): boolean;
|
|
7
7
|
/** Per-participant group receipt → our status (read wins over delivered). */
|
|
@@ -87,6 +87,7 @@ export declare class BaileysAdapter extends EventEmitter implements WaAdapter {
|
|
|
87
87
|
private send;
|
|
88
88
|
sendText(chatJid: string, text: string, opts?: WaSendOptions): Promise<SendResult>;
|
|
89
89
|
sendMedia(chatJid: string, file: WaSendFile, opts?: WaSendOptions): Promise<SendResult>;
|
|
90
|
+
sendReaction(chatJid: string, target: WaReactionTarget, emoji: string): Promise<SendResult>;
|
|
90
91
|
markRead(chatJid: string, messageIds: string[]): Promise<void>;
|
|
91
92
|
sendPresence(chatJid: string, presence: 'composing' | 'paused'): Promise<void>;
|
|
92
93
|
downloadMedia(messageId: string): Promise<Buffer | null>;
|
package/dist/baileys/adapter.js
CHANGED
|
@@ -5,7 +5,7 @@ import { WaUnavailableError, } from '../types.js';
|
|
|
5
5
|
import { createAuthStore } from './auth-store.js';
|
|
6
6
|
import { backoffMs, classifyDisconnect } from './disconnect.js';
|
|
7
7
|
import { Lru } from './lru.js';
|
|
8
|
-
import { jidType, mapWAMessage, toMs } from './mapping.js';
|
|
8
|
+
import { jidType, mapStoredReactions, mapWAMessage, mapWAReaction, toMs } from './mapping.js';
|
|
9
9
|
import { quotedForChat, quotedFromRef } from './quoted.js';
|
|
10
10
|
import { contactAliasPair, normalizeContactJid } from './contact-aliases.js';
|
|
11
11
|
import { checkedNumberJid } from './number-check.js';
|
|
@@ -429,8 +429,22 @@ export class BaileysAdapter extends EventEmitter {
|
|
|
429
429
|
const id = m.key?.id;
|
|
430
430
|
if (!id)
|
|
431
431
|
return;
|
|
432
|
+
// A reaction arrives as its own message in messages.upsert (live) and in history batches.
|
|
433
|
+
// Baileys also emits messages.reaction for the same live reaction: that event is not
|
|
434
|
+
// subscribed, so a reaction is counted once.
|
|
435
|
+
const reaction = mapWAReaction(m);
|
|
436
|
+
if (reaction) {
|
|
437
|
+
if (source === 'history' && reaction.timestamp < this.historyCutoff())
|
|
438
|
+
return;
|
|
439
|
+
this.emitTyped('reaction', reaction, { source });
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
432
442
|
if (m.message)
|
|
433
443
|
this.raw.set(id, m);
|
|
444
|
+
// History sync keeps earlier reactions on the reacted message itself.
|
|
445
|
+
if (source === 'history')
|
|
446
|
+
for (const r of mapStoredReactions(m))
|
|
447
|
+
this.emitTyped('reaction', r, { source });
|
|
434
448
|
const mapped = mapWAMessage(m, (raw) => this.download(raw));
|
|
435
449
|
if (!mapped)
|
|
436
450
|
return;
|
|
@@ -585,7 +599,9 @@ export class BaileysAdapter extends EventEmitter {
|
|
|
585
599
|
throw new WaUnavailableError();
|
|
586
600
|
return this.sock;
|
|
587
601
|
}
|
|
588
|
-
async send(chatJid, content, opts
|
|
602
|
+
async send(chatJid, content, opts,
|
|
603
|
+
/** keep the sent message for quotes and media (not for reactions) */
|
|
604
|
+
cache = true) {
|
|
589
605
|
const sock = this.requireOpen();
|
|
590
606
|
// The live cache is small and cleared on reconnect; rebuild older quotes from the saved copy.
|
|
591
607
|
// Either way the quote points at the chat the reply goes to (phone number or WhatsApp ID).
|
|
@@ -609,7 +625,8 @@ export class BaileysAdapter extends EventEmitter {
|
|
|
609
625
|
const id = res?.key?.id;
|
|
610
626
|
if (!res || !id)
|
|
611
627
|
throw new Error('send returned no message id');
|
|
612
|
-
|
|
628
|
+
if (cache)
|
|
629
|
+
this.raw.set(id, res);
|
|
613
630
|
return { id, timestamp: toMs(res.messageTimestamp) };
|
|
614
631
|
}
|
|
615
632
|
sendText(chatJid, text, opts) {
|
|
@@ -647,6 +664,17 @@ export class BaileysAdapter extends EventEmitter {
|
|
|
647
664
|
}
|
|
648
665
|
return this.send(chatJid, content, opts);
|
|
649
666
|
}
|
|
667
|
+
sendReaction(chatJid, target, emoji) {
|
|
668
|
+
// The cached key carries the group author exactly as WhatsApp delivered it.
|
|
669
|
+
const participant = this.raw.get(target.id)?.key?.participant ?? target.participant;
|
|
670
|
+
const key = {
|
|
671
|
+
remoteJid: chatJid,
|
|
672
|
+
id: target.id,
|
|
673
|
+
fromMe: target.fromMe,
|
|
674
|
+
...(participant && chatJid.endsWith('@g.us') ? { participant } : {}),
|
|
675
|
+
};
|
|
676
|
+
return this.send(chatJid, { react: { text: emoji, key } }, undefined, false);
|
|
677
|
+
}
|
|
650
678
|
async markRead(chatJid, messageIds) {
|
|
651
679
|
const sock = this.requireOpen();
|
|
652
680
|
const keys = messageIds.map((id) => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type proto, type WAMessage } from 'baileys';
|
|
2
|
-
import type { WaIncomingMessage } from '../types.js';
|
|
2
|
+
import type { WaIncomingMessage, WaReaction } from '../types.js';
|
|
3
3
|
export type JidKind = 'dm' | 'group' | 'other';
|
|
4
4
|
export declare function jidType(jid: string): JidKind;
|
|
5
5
|
type LongLike = {
|
|
@@ -10,12 +10,24 @@ type LongLike = {
|
|
|
10
10
|
};
|
|
11
11
|
/** Convert a protobuf timestamp (number | Long | null) in seconds to epoch ms. */
|
|
12
12
|
export declare function toMs(ts: number | LongLike | null | undefined, fallback?: number): number;
|
|
13
|
+
/**
|
|
14
|
+
* A live or history reaction message (`reactionMessage`): who reacted with which emoji to which
|
|
15
|
+
* message. Empty text = the reaction was removed. Null for anything else, for reactions without a
|
|
16
|
+
* target or reactor, and for status/broadcast/newsletter chats. Encrypted reactions
|
|
17
|
+
* (`encReactionMessage`, community announcements) are not read.
|
|
18
|
+
*/
|
|
19
|
+
export declare function mapWAReaction(msg: proto.IWebMessageInfo): WaReaction | null;
|
|
20
|
+
/**
|
|
21
|
+
* Reactions WhatsApp attached to a stored message (history sync keeps them on the message itself,
|
|
22
|
+
* `WebMessageInfo.reactions`, instead of separate reaction messages). Removed ones are skipped.
|
|
23
|
+
*/
|
|
24
|
+
export declare function mapStoredReactions(msg: proto.IWebMessageInfo): WaReaction[];
|
|
13
25
|
/** Optional hook for media downloads that need a live socket (reupload of expired media). */
|
|
14
26
|
export type MediaDownloader = (msg: WAMessage) => Promise<Buffer>;
|
|
15
27
|
/**
|
|
16
28
|
* Map a raw Baileys message to our adapter shape. Returns null for messages
|
|
17
|
-
* that should not appear in the inbox (reactions
|
|
18
|
-
* status/newsletter/broadcast, empty).
|
|
29
|
+
* that should not appear in the inbox (reactions — read by mapWAReaction —, protocol, key
|
|
30
|
+
* distribution, status/newsletter/broadcast, empty).
|
|
19
31
|
*/
|
|
20
32
|
export declare function mapWAMessage(msg: proto.IWebMessageInfo, download?: MediaDownloader): WaIncomingMessage | null;
|
|
21
33
|
export {};
|
package/dist/baileys/mapping.js
CHANGED
|
@@ -20,11 +20,95 @@ export function toMs(ts, fallback = Date.now()) {
|
|
|
20
20
|
return ((ts.high >>> 0) * 2 ** 32 + (ts.low >>> 0)) * 1000;
|
|
21
21
|
return fallback;
|
|
22
22
|
}
|
|
23
|
+
/** A protobuf millisecond timestamp (number | Long | null) as epoch ms, or `fallback`. */
|
|
24
|
+
function msToNumber(ts, fallback) {
|
|
25
|
+
if (ts == null)
|
|
26
|
+
return fallback;
|
|
27
|
+
if (typeof ts === 'number')
|
|
28
|
+
return ts;
|
|
29
|
+
if ('toNumber' in ts && typeof ts.toNumber === 'function')
|
|
30
|
+
return ts.toNumber();
|
|
31
|
+
if ('low' in ts)
|
|
32
|
+
return (ts.high >>> 0) * 2 ** 32 + (ts.low >>> 0);
|
|
33
|
+
return fallback;
|
|
34
|
+
}
|
|
35
|
+
/** Who reacted, from the reaction's own message key (group participant, DM partner or us). */
|
|
36
|
+
function reactor(key, chatJid) {
|
|
37
|
+
const fromMe = !!key.fromMe;
|
|
38
|
+
if (fromMe)
|
|
39
|
+
return { senderJid: null, senderJidAlt: null, fromMe };
|
|
40
|
+
if (jidType(chatJid) !== 'group')
|
|
41
|
+
return { senderJid: normalizeContactJid(chatJid) ?? chatJid, senderJidAlt: null, fromMe };
|
|
42
|
+
return {
|
|
43
|
+
senderJid: normalizeContactJid(key.participant),
|
|
44
|
+
senderJidAlt: normalizeContactJid(key.participantAlt),
|
|
45
|
+
fromMe,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* A live or history reaction message (`reactionMessage`): who reacted with which emoji to which
|
|
50
|
+
* message. Empty text = the reaction was removed. Null for anything else, for reactions without a
|
|
51
|
+
* target or reactor, and for status/broadcast/newsletter chats. Encrypted reactions
|
|
52
|
+
* (`encReactionMessage`, community announcements) are not read.
|
|
53
|
+
*/
|
|
54
|
+
export function mapWAReaction(msg) {
|
|
55
|
+
const key = msg.key;
|
|
56
|
+
const chatJid = key?.remoteJid;
|
|
57
|
+
if (!key || !chatJid || jidType(chatJid) === 'other')
|
|
58
|
+
return null;
|
|
59
|
+
const reaction = normalizeMessageContent(msg.message)?.reactionMessage;
|
|
60
|
+
const targetId = reaction?.key?.id;
|
|
61
|
+
if (!reaction || !targetId)
|
|
62
|
+
return null;
|
|
63
|
+
const who = reactor(key, chatJid);
|
|
64
|
+
if (!who.fromMe && !who.senderJid)
|
|
65
|
+
return null;
|
|
66
|
+
const isGroup = jidType(chatJid) === 'group';
|
|
67
|
+
const alt = isGroup ? null : normalizeContactJid(key.remoteJidAlt);
|
|
68
|
+
return {
|
|
69
|
+
targetId,
|
|
70
|
+
chatJid,
|
|
71
|
+
chatJidAlt: alt && alt.endsWith('@lid') !== chatJid.endsWith('@lid') ? alt : null,
|
|
72
|
+
...who,
|
|
73
|
+
senderName: who.fromMe ? null : (msg.pushName ?? null),
|
|
74
|
+
emoji: reaction.text ?? '',
|
|
75
|
+
timestamp: msToNumber(reaction.senderTimestampMs, toMs(msg.messageTimestamp)),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Reactions WhatsApp attached to a stored message (history sync keeps them on the message itself,
|
|
80
|
+
* `WebMessageInfo.reactions`, instead of separate reaction messages). Removed ones are skipped.
|
|
81
|
+
*/
|
|
82
|
+
export function mapStoredReactions(msg) {
|
|
83
|
+
const chatJid = msg.key?.remoteJid;
|
|
84
|
+
const targetId = msg.key?.id;
|
|
85
|
+
if (!chatJid || !targetId || jidType(chatJid) === 'other' || !msg.reactions?.length)
|
|
86
|
+
return [];
|
|
87
|
+
const fallback = toMs(msg.messageTimestamp);
|
|
88
|
+
return msg.reactions.flatMap((r) => {
|
|
89
|
+
if (!r.key || !r.text)
|
|
90
|
+
return [];
|
|
91
|
+
const who = reactor(r.key, chatJid);
|
|
92
|
+
if (!who.fromMe && !who.senderJid)
|
|
93
|
+
return [];
|
|
94
|
+
return [
|
|
95
|
+
{
|
|
96
|
+
targetId,
|
|
97
|
+
chatJid,
|
|
98
|
+
chatJidAlt: null,
|
|
99
|
+
...who,
|
|
100
|
+
senderName: null,
|
|
101
|
+
emoji: r.text,
|
|
102
|
+
timestamp: msToNumber(r.senderTimestampMs, fallback),
|
|
103
|
+
},
|
|
104
|
+
];
|
|
105
|
+
});
|
|
106
|
+
}
|
|
23
107
|
const defaultDownloader = async (msg) => (await downloadMediaMessage(msg, 'buffer', {}));
|
|
24
108
|
/**
|
|
25
109
|
* Map a raw Baileys message to our adapter shape. Returns null for messages
|
|
26
|
-
* that should not appear in the inbox (reactions
|
|
27
|
-
* status/newsletter/broadcast, empty).
|
|
110
|
+
* that should not appear in the inbox (reactions — read by mapWAReaction —, protocol, key
|
|
111
|
+
* distribution, status/newsletter/broadcast, empty).
|
|
28
112
|
*/
|
|
29
113
|
export function mapWAMessage(msg, download = defaultDownloader) {
|
|
30
114
|
const key = msg.key;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { EventEmitter } from 'node:events';
|
|
2
2
|
import type { WaStatus } from '@ezychat/shared';
|
|
3
|
-
import { type SendResult, type WaAdapter, type WaAdapterEvents, type WaContactAlias, type WaGroupInfo, type WaIncomingMessage, type WaSendFile, type WaSendOptions } from '../types.js';
|
|
3
|
+
import { type SendResult, type WaAdapter, type WaAdapterEvents, type WaContactAlias, type WaGroupInfo, type WaIncomingMessage, type WaReaction, type WaReactionTarget, type WaSendFile, type WaSendOptions } from '../types.js';
|
|
4
4
|
export interface FakeSentRecord {
|
|
5
5
|
chatJid: string;
|
|
6
6
|
text?: string;
|
|
@@ -31,6 +31,11 @@ export declare class FakeWaAdapter extends EventEmitter implements WaAdapter {
|
|
|
31
31
|
readonly groupInfoCalls: string[];
|
|
32
32
|
readonly aboutCalls: string[];
|
|
33
33
|
readonly sent: FakeSentRecord[];
|
|
34
|
+
readonly reactions: Array<{
|
|
35
|
+
chatJid: string;
|
|
36
|
+
target: WaReactionTarget;
|
|
37
|
+
emoji: string;
|
|
38
|
+
}>;
|
|
34
39
|
readonly reads: Array<{
|
|
35
40
|
chatJid: string;
|
|
36
41
|
messageIds: string[];
|
|
@@ -70,6 +75,8 @@ export declare class FakeWaAdapter extends EventEmitter implements WaAdapter {
|
|
|
70
75
|
private afterSend;
|
|
71
76
|
sendText(chatJid: string, text: string, opts?: WaSendOptions): Promise<SendResult>;
|
|
72
77
|
sendMedia(chatJid: string, file: WaSendFile, opts?: WaSendOptions): Promise<SendResult>;
|
|
78
|
+
/** Like WhatsApp, the linked number's own reaction comes back as a `reaction` event first. */
|
|
79
|
+
sendReaction(chatJid: string, target: WaReactionTarget, emoji: string): Promise<SendResult>;
|
|
73
80
|
markRead(chatJid: string, messageIds: string[]): Promise<void>;
|
|
74
81
|
sendPresence(chatJid: string, presence: 'composing' | 'paused'): Promise<void>;
|
|
75
82
|
downloadMedia(messageId: string): Promise<Buffer | null>;
|
|
@@ -97,6 +104,15 @@ export declare class FakeWaAdapter extends EventEmitter implements WaAdapter {
|
|
|
97
104
|
chatJid: string;
|
|
98
105
|
body: string;
|
|
99
106
|
}): WaIncomingMessage;
|
|
107
|
+
/**
|
|
108
|
+
* Emit a reaction (live unless `source` says history): `emoji` '' removes the person's reaction.
|
|
109
|
+
* In a DM the reactor defaults to the chat partner; `fromMe` means the linked number reacted.
|
|
110
|
+
*/
|
|
111
|
+
simulateReaction(p: Partial<WaReaction> & {
|
|
112
|
+
chatJid: string;
|
|
113
|
+
targetId: string;
|
|
114
|
+
emoji: string;
|
|
115
|
+
}, source?: 'live' | 'history'): WaReaction;
|
|
100
116
|
simulateStatus(s: Partial<WaStatus>): void;
|
|
101
117
|
failNextSend(err?: Error): void;
|
|
102
118
|
setConnected(connected: boolean): void;
|
|
@@ -23,6 +23,7 @@ export class FakeWaAdapter extends EventEmitter {
|
|
|
23
23
|
groupInfoCalls = [];
|
|
24
24
|
aboutCalls = [];
|
|
25
25
|
sent = [];
|
|
26
|
+
reactions = [];
|
|
26
27
|
reads = [];
|
|
27
28
|
presences = [];
|
|
28
29
|
/**
|
|
@@ -111,6 +112,15 @@ export class FakeWaAdapter extends EventEmitter {
|
|
|
111
112
|
this.media.set(id, file.buffer);
|
|
112
113
|
return this.afterSend(chatJid, id);
|
|
113
114
|
}
|
|
115
|
+
/** Like WhatsApp, the linked number's own reaction comes back as a `reaction` event first. */
|
|
116
|
+
async sendReaction(chatJid, target, emoji) {
|
|
117
|
+
this.beforeSend();
|
|
118
|
+
const id = `${this.idPrefix}-REACT-${FAKE_RUN}-${++this.outCounter}`;
|
|
119
|
+
const timestamp = Date.now();
|
|
120
|
+
this.reactions.push({ chatJid, target: { ...target }, emoji });
|
|
121
|
+
this.simulateReaction({ chatJid, targetId: target.id, emoji, fromMe: true, timestamp });
|
|
122
|
+
return { id, timestamp };
|
|
123
|
+
}
|
|
114
124
|
async markRead(chatJid, messageIds) {
|
|
115
125
|
this.reads.push({ chatJid, messageIds: [...messageIds] });
|
|
116
126
|
}
|
|
@@ -209,6 +219,33 @@ export class FakeWaAdapter extends EventEmitter {
|
|
|
209
219
|
this.emitTyped('message', msg, { source: 'live' });
|
|
210
220
|
return msg;
|
|
211
221
|
}
|
|
222
|
+
/**
|
|
223
|
+
* Emit a reaction (live unless `source` says history): `emoji` '' removes the person's reaction.
|
|
224
|
+
* In a DM the reactor defaults to the chat partner; `fromMe` means the linked number reacted.
|
|
225
|
+
*/
|
|
226
|
+
simulateReaction(p, source = 'live') {
|
|
227
|
+
const isGroup = p.chatJid.endsWith('@g.us');
|
|
228
|
+
const fromMe = p.fromMe ?? false;
|
|
229
|
+
const reaction = {
|
|
230
|
+
targetId: p.targetId,
|
|
231
|
+
chatJid: p.chatJid,
|
|
232
|
+
chatJidAlt: p.chatJidAlt ?? null,
|
|
233
|
+
senderJid: fromMe
|
|
234
|
+
? null
|
|
235
|
+
: p.senderJid !== undefined
|
|
236
|
+
? p.senderJid
|
|
237
|
+
: isGroup
|
|
238
|
+
? null
|
|
239
|
+
: p.chatJid,
|
|
240
|
+
senderJidAlt: p.senderJidAlt ?? null,
|
|
241
|
+
senderName: p.senderName ?? null,
|
|
242
|
+
fromMe,
|
|
243
|
+
emoji: p.emoji,
|
|
244
|
+
timestamp: p.timestamp ?? Date.now(),
|
|
245
|
+
};
|
|
246
|
+
this.emitTyped('reaction', reaction, { source });
|
|
247
|
+
return reaction;
|
|
248
|
+
}
|
|
212
249
|
simulateStatus(s) {
|
|
213
250
|
this.setStatus(s);
|
|
214
251
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -26,6 +26,30 @@ export interface WaIncomingMessage {
|
|
|
26
26
|
download: () => Promise<Buffer>;
|
|
27
27
|
};
|
|
28
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* One person's emoji reaction to a message. WhatsApp keeps one reaction per person and message: a
|
|
31
|
+
* new emoji replaces the previous one, an empty `emoji` removes it.
|
|
32
|
+
*/
|
|
33
|
+
export interface WaReaction {
|
|
34
|
+
/** WhatsApp id of the message that was reacted to. */
|
|
35
|
+
targetId: string;
|
|
36
|
+
/** The chat of both the reaction and the reacted message (as WhatsApp delivered it). */
|
|
37
|
+
chatJid: string;
|
|
38
|
+
/** DM only: the same person's other address (`key.remoteJidAlt`), as for messages. */
|
|
39
|
+
chatJidAlt?: string | null;
|
|
40
|
+
/** Who reacted: the group participant or the DM partner; null when `fromMe`. */
|
|
41
|
+
senderJid: string | null;
|
|
42
|
+
/** Group reactor's explicitly associated alternate phone/LID address. */
|
|
43
|
+
senderJidAlt?: string | null;
|
|
44
|
+
/** WhatsApp profile name delivered with the reaction (never inferred). */
|
|
45
|
+
senderName: string | null;
|
|
46
|
+
/** Reacted from the linked number (its phone or another linked device). */
|
|
47
|
+
fromMe: boolean;
|
|
48
|
+
/** The emoji; '' = the reaction was removed. */
|
|
49
|
+
emoji: string;
|
|
50
|
+
/** epoch ms */
|
|
51
|
+
timestamp: number;
|
|
52
|
+
}
|
|
29
53
|
export interface WaChatInfo {
|
|
30
54
|
jid: string;
|
|
31
55
|
type: 'dm' | 'group';
|
|
@@ -79,6 +103,10 @@ export interface WaAdapterEvents {
|
|
|
79
103
|
message: [WaIncomingMessage, {
|
|
80
104
|
source: 'live' | 'history';
|
|
81
105
|
}];
|
|
106
|
+
/** Someone reacted to a message, changed their reaction or removed it (live or history). */
|
|
107
|
+
reaction: [WaReaction, {
|
|
108
|
+
source: 'live' | 'history';
|
|
109
|
+
}];
|
|
82
110
|
messageStatus: [WaMessageStatusUpdate];
|
|
83
111
|
chats: [WaChatInfo[]];
|
|
84
112
|
contacts: [WaContactInfo[]];
|
|
@@ -107,6 +135,15 @@ export interface WaSendOptions {
|
|
|
107
135
|
/** Used when the quoted message is no longer in the adapter's live cache. */
|
|
108
136
|
quoted?: WaQuotedRef;
|
|
109
137
|
}
|
|
138
|
+
/** The message a reaction points at: WhatsApp matches it by chat, id, side and group author. */
|
|
139
|
+
export interface WaReactionTarget {
|
|
140
|
+
/** WhatsApp id of the message. */
|
|
141
|
+
id: string;
|
|
142
|
+
/** The message was sent from the linked number. */
|
|
143
|
+
fromMe: boolean;
|
|
144
|
+
/** Group messages from someone else: who wrote it. */
|
|
145
|
+
participant?: string | null;
|
|
146
|
+
}
|
|
110
147
|
export interface SendResult {
|
|
111
148
|
id: string;
|
|
112
149
|
timestamp: number;
|
|
@@ -138,6 +175,11 @@ export interface WaAdapter {
|
|
|
138
175
|
requestPairingCode(phone: string): Promise<string>;
|
|
139
176
|
sendText(chatJid: string, text: string, opts?: WaSendOptions): Promise<SendResult>;
|
|
140
177
|
sendMedia(chatJid: string, file: WaSendFile, opts?: WaSendOptions): Promise<SendResult>;
|
|
178
|
+
/**
|
|
179
|
+
* React to a message as the linked number (`emoji` '' removes the reaction). WhatsApp keeps one
|
|
180
|
+
* reaction per number, so this replaces one made on the phone. Throws WaUnavailableError offline.
|
|
181
|
+
*/
|
|
182
|
+
sendReaction(chatJid: string, target: WaReactionTarget, emoji: string): Promise<SendResult>;
|
|
141
183
|
markRead(chatJid: string, messageIds: string[]): Promise<void>;
|
|
142
184
|
sendPresence(chatJid: string, presence: 'composing' | 'paused'): Promise<void>;
|
|
143
185
|
/** re-download by id if still cached */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ezychat/wa",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.51",
|
|
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.
|
|
40
|
+
"@ezychat/shared": "0.1.51",
|
|
41
41
|
"baileys": "^7.0.0-rc14",
|
|
42
42
|
"pino": "^10.4.0",
|
|
43
43
|
"qrcode": "^1.5.4"
|