
























@@ -1,6 +1,6 @@
11import type { proto } from "@whiskeysockets/baileys";
22import { describe, expect, it } from "vitest";
3-import { extractMentionedJids } from "./extract.js";
3+import { extractMentionedJids, hasInboundUserContent } from "./extract.js";
4455describe("extractMentionedJids", () => {
66const botJid = "5511999999999@s.whatsapp.net";
@@ -101,3 +101,182 @@ describe("extractMentionedJids", () => {
101101expect(extractMentionedJids(message)).toEqual([botJid]);
102102});
103103});
104+105+describe("hasInboundUserContent", () => {
106+it("returns true for plain text conversation", () => {
107+expect(hasInboundUserContent({ conversation: "hello" })).toBe(true);
108+});
109+110+it("returns true for extendedTextMessage", () => {
111+expect(
112+hasInboundUserContent({ extendedTextMessage: { text: "hello" } } as proto.IMessage),
113+).toBe(true);
114+});
115+116+it("returns true for image message", () => {
117+expect(
118+hasInboundUserContent({ imageMessage: { mimetype: "image/png" } } as proto.IMessage),
119+).toBe(true);
120+});
121+122+it("returns true for video message", () => {
123+expect(
124+hasInboundUserContent({ videoMessage: { mimetype: "video/mp4" } } as proto.IMessage),
125+).toBe(true);
126+});
127+128+it("returns true for audio message", () => {
129+expect(
130+hasInboundUserContent({ audioMessage: { mimetype: "audio/ogg" } } as proto.IMessage),
131+).toBe(true);
132+});
133+134+it("returns true for document message", () => {
135+expect(
136+hasInboundUserContent({
137+documentMessage: { fileName: "x.pdf" },
138+} as proto.IMessage),
139+).toBe(true);
140+});
141+142+it("returns true for sticker message", () => {
143+expect(
144+hasInboundUserContent({ stickerMessage: { mimetype: "image/webp" } } as proto.IMessage),
145+).toBe(true);
146+});
147+148+it("returns true for location message with valid coords", () => {
149+expect(
150+hasInboundUserContent({
151+locationMessage: { degreesLatitude: 1, degreesLongitude: 2 },
152+} as proto.IMessage),
153+).toBe(true);
154+});
155+156+it("returns true for live location message with valid coords", () => {
157+expect(
158+hasInboundUserContent({
159+liveLocationMessage: { degreesLatitude: 1, degreesLongitude: 2 },
160+} as proto.IMessage),
161+).toBe(true);
162+});
163+164+it("returns true for contact message", () => {
165+expect(
166+hasInboundUserContent({
167+contactMessage: { displayName: "Alice", vcard: "BEGIN:VCARD\nEND:VCARD" },
168+} as proto.IMessage),
169+).toBe(true);
170+});
171+172+it("returns true for contactsArrayMessage via contact placeholder extraction", () => {
173+expect(
174+hasInboundUserContent({
175+contactsArrayMessage: {
176+contacts: [{ displayName: "Alice", vcard: "BEGIN:VCARD\nEND:VCARD" }],
177+},
178+} as proto.IMessage),
179+).toBe(true);
180+});
181+182+it("returns true for buttons response (user button click)", () => {
183+expect(
184+hasInboundUserContent({
185+buttonsResponseMessage: {
186+selectedButtonId: "yes",
187+selectedDisplayText: "Yes",
188+},
189+} as proto.IMessage),
190+).toBe(true);
191+});
192+193+it("returns true for list response (user list selection)", () => {
194+expect(
195+hasInboundUserContent({
196+listResponseMessage: {
197+title: "Option A",
198+singleSelectReply: { selectedRowId: "a" },
199+} as unknown as proto.Message.IListResponseMessage,
200+} as proto.IMessage),
201+).toBe(true);
202+});
203+204+it("returns true for template button reply", () => {
205+expect(
206+hasInboundUserContent({
207+templateButtonReplyMessage: {
208+selectedId: "btn-1",
209+selectedDisplayText: "Click",
210+} as unknown as proto.Message.ITemplateButtonReplyMessage,
211+} as proto.IMessage),
212+).toBe(true);
213+});
214+215+it("returns true for interactive response", () => {
216+expect(
217+hasInboundUserContent({
218+interactiveResponseMessage: {
219+body: { text: "x" },
220+nativeFlowResponseMessage: { name: "n", paramsJson: "{}" },
221+} as unknown as proto.Message.IInteractiveResponseMessage,
222+} as proto.IMessage),
223+).toBe(true);
224+});
225+226+it("returns true for buttons response wrapped in ephemeralMessage (regression for #73797 + greptile review)", () => {
227+expect(
228+hasInboundUserContent({
229+ephemeralMessage: {
230+message: {
231+buttonsResponseMessage: {
232+selectedButtonId: "ok",
233+selectedDisplayText: "OK",
234+},
235+},
236+},
237+} as proto.IMessage),
238+).toBe(true);
239+});
240+241+it("returns false for undefined message (regression for #73797)", () => {
242+expect(hasInboundUserContent(undefined)).toBe(false);
243+});
244+245+it("returns false for empty message object (no content keys)", () => {
246+expect(hasInboundUserContent({} as proto.IMessage)).toBe(false);
247+});
248+249+it("returns false for protocol message envelope without inner content (regression for #73797)", () => {
250+expect(
251+hasInboundUserContent({
252+protocolMessage: {
253+type: 0,
254+} as unknown as proto.Message.IProtocolMessage,
255+} as proto.IMessage),
256+).toBe(false);
257+});
258+259+it("returns false for receipt-style senderKeyDistribution-only payload (regression for #73797)", () => {
260+expect(
261+hasInboundUserContent({
262+senderKeyDistributionMessage: {
263+groupId: "g@example",
264+} as unknown as proto.Message.ISenderKeyDistributionMessage,
265+} as proto.IMessage),
266+).toBe(false);
267+});
268+269+it("returns false when location coords are missing (incomplete event, regression for #73797)", () => {
270+expect(
271+hasInboundUserContent({
272+locationMessage: { name: "no coords" },
273+} as proto.IMessage),
274+).toBe(false);
275+});
276+277+it("returns false when extendedTextMessage has only empty text", () => {
278+expect(hasInboundUserContent({ extendedTextMessage: { text: " " } } as proto.IMessage)).toBe(
279+false,
280+);
281+});
282+});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。