Merge ed87b1dd91 into da71eaebd2
This commit is contained in:
commit
87085a8324
@ -47,6 +47,12 @@ export type WhatsAppConfig = {
|
||||
* - "allowlist": only allow group messages from senders in groupAllowFrom/allowFrom
|
||||
*/
|
||||
groupPolicy?: GroupPolicy;
|
||||
/**
|
||||
* When true, messages from group members not in groupAllowFrom are still
|
||||
* stored as pending context (visible in "[Chat messages since your last reply]")
|
||||
* even though they cannot trigger a reply. Default: false (old behavior: drop entirely).
|
||||
*/
|
||||
groupContextFromAll?: boolean;
|
||||
/** Max group messages to keep as history context (0 disables). */
|
||||
historyLimit?: number;
|
||||
/** Max DM turns to keep as history context. */
|
||||
@ -118,6 +124,8 @@ export type WhatsAppAccountConfig = {
|
||||
allowFrom?: string[];
|
||||
groupAllowFrom?: string[];
|
||||
groupPolicy?: GroupPolicy;
|
||||
/** Store context from non-allowlisted group senders (default: false). */
|
||||
groupContextFromAll?: boolean;
|
||||
/** Max group messages to keep as history context (0 disables). */
|
||||
historyLimit?: number;
|
||||
/** Max DM turns to keep as history context. */
|
||||
|
||||
@ -28,6 +28,7 @@ export const WhatsAppAccountSchema = z
|
||||
allowFrom: z.array(z.string()).optional(),
|
||||
groupAllowFrom: z.array(z.string()).optional(),
|
||||
groupPolicy: GroupPolicySchema.optional().default("allowlist"),
|
||||
groupContextFromAll: z.boolean().optional().default(false),
|
||||
historyLimit: z.number().int().min(0).optional(),
|
||||
dmHistoryLimit: z.number().int().min(0).optional(),
|
||||
dms: z.record(z.string(), DmConfigSchema.optional()).optional(),
|
||||
@ -85,6 +86,7 @@ export const WhatsAppConfigSchema = z
|
||||
allowFrom: z.array(z.string()).optional(),
|
||||
groupAllowFrom: z.array(z.string()).optional(),
|
||||
groupPolicy: GroupPolicySchema.optional().default("allowlist"),
|
||||
groupContextFromAll: z.boolean().optional().default(false),
|
||||
historyLimit: z.number().int().min(0).optional(),
|
||||
dmHistoryLimit: z.number().int().min(0).optional(),
|
||||
dms: z.record(z.string(), DmConfigSchema.optional()).optional(),
|
||||
|
||||
@ -20,6 +20,7 @@ export type ResolvedWhatsAppAccount = {
|
||||
allowFrom?: string[];
|
||||
groupAllowFrom?: string[];
|
||||
groupPolicy?: GroupPolicy;
|
||||
groupContextFromAll?: boolean;
|
||||
dmPolicy?: DmPolicy;
|
||||
textChunkLimit?: number;
|
||||
chunkMode?: "length" | "newline";
|
||||
@ -150,6 +151,7 @@ export function resolveWhatsAppAccount(params: {
|
||||
allowFrom: accountCfg?.allowFrom ?? rootCfg?.allowFrom,
|
||||
groupAllowFrom: accountCfg?.groupAllowFrom ?? rootCfg?.groupAllowFrom,
|
||||
groupPolicy: accountCfg?.groupPolicy ?? rootCfg?.groupPolicy,
|
||||
groupContextFromAll: accountCfg?.groupContextFromAll ?? rootCfg?.groupContextFromAll,
|
||||
textChunkLimit: accountCfg?.textChunkLimit ?? rootCfg?.textChunkLimit,
|
||||
chunkMode: accountCfg?.chunkMode ?? rootCfg?.chunkMode,
|
||||
mediaMaxMb: accountCfg?.mediaMaxMb ?? rootCfg?.mediaMaxMb,
|
||||
|
||||
@ -94,6 +94,32 @@ export function createWebOnMessageHandler(params: {
|
||||
return;
|
||||
}
|
||||
|
||||
// Context-only messages: store for group history but never trigger a reply.
|
||||
// This happens when a group message passes group allowlist checks for the group
|
||||
// itself but the sender is not in groupAllowFrom.
|
||||
if (msg.contextOnly && msg.chatType === "group") {
|
||||
const sender =
|
||||
msg.senderName && msg.senderE164
|
||||
? `${msg.senderName} (${msg.senderE164})`
|
||||
: (msg.senderName ?? msg.senderE164 ?? "Unknown");
|
||||
logVerbose(`Storing context-only group message from ${sender} in ${conversationId}`);
|
||||
const { recordPendingHistoryEntryIfEnabled } =
|
||||
await import("../../../auto-reply/reply/history.js");
|
||||
recordPendingHistoryEntryIfEnabled({
|
||||
historyMap: params.groupHistories,
|
||||
historyKey: groupHistoryKey,
|
||||
limit: params.groupHistoryLimit,
|
||||
entry: {
|
||||
sender,
|
||||
body: msg.body,
|
||||
timestamp: msg.timestamp,
|
||||
id: msg.id,
|
||||
senderJid: msg.senderJid,
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg.chatType === "group") {
|
||||
const metaCtx = {
|
||||
From: msg.from,
|
||||
|
||||
@ -10,6 +10,8 @@ import { resolveWhatsAppAccount } from "../accounts.js";
|
||||
|
||||
export type InboundAccessControlResult = {
|
||||
allowed: boolean;
|
||||
/** When true the message should be stored as pending group context even though it did not pass sender allowlists. */
|
||||
storeForContext: boolean;
|
||||
shouldMarkRead: boolean;
|
||||
isSelfChat: boolean;
|
||||
resolvedAccountId: string;
|
||||
@ -51,6 +53,8 @@ export async function checkInboundAccessControl(params: {
|
||||
const groupAllowFrom =
|
||||
account.groupAllowFrom ??
|
||||
(configuredAllowFrom && configuredAllowFrom.length > 0 ? configuredAllowFrom : undefined);
|
||||
const groupContextFromAll =
|
||||
account.groupContextFromAll ?? cfg.channels?.whatsapp?.groupContextFromAll ?? false;
|
||||
const isSamePhone = params.from === params.selfE164;
|
||||
const isSelfChat = isSelfChatMode(params.selfE164, configuredAllowFrom);
|
||||
const pairingGraceMs =
|
||||
@ -84,6 +88,7 @@ export async function checkInboundAccessControl(params: {
|
||||
logVerbose("Blocked group message (groupPolicy: disabled)");
|
||||
return {
|
||||
allowed: false,
|
||||
storeForContext: false,
|
||||
shouldMarkRead: false,
|
||||
isSelfChat,
|
||||
resolvedAccountId: account.accountId,
|
||||
@ -94,6 +99,7 @@ export async function checkInboundAccessControl(params: {
|
||||
logVerbose("Blocked group message (groupPolicy: allowlist, no groupAllowFrom)");
|
||||
return {
|
||||
allowed: false,
|
||||
storeForContext: false,
|
||||
shouldMarkRead: false,
|
||||
isSelfChat,
|
||||
resolvedAccountId: account.accountId,
|
||||
@ -103,11 +109,24 @@ export async function checkInboundAccessControl(params: {
|
||||
groupHasWildcard ||
|
||||
(params.senderE164 != null && normalizedGroupAllowFrom.includes(params.senderE164));
|
||||
if (!senderAllowed) {
|
||||
if (groupContextFromAll) {
|
||||
logVerbose(
|
||||
`Group message from ${params.senderE164 ?? "unknown sender"} not in groupAllowFrom (storing for context)`,
|
||||
);
|
||||
return {
|
||||
allowed: false,
|
||||
storeForContext: true,
|
||||
shouldMarkRead: false,
|
||||
isSelfChat,
|
||||
resolvedAccountId: account.accountId,
|
||||
};
|
||||
}
|
||||
logVerbose(
|
||||
`Blocked group message from ${params.senderE164 ?? "unknown sender"} (groupPolicy: allowlist)`,
|
||||
);
|
||||
return {
|
||||
allowed: false,
|
||||
storeForContext: false,
|
||||
shouldMarkRead: false,
|
||||
isSelfChat,
|
||||
resolvedAccountId: account.accountId,
|
||||
@ -121,6 +140,7 @@ export async function checkInboundAccessControl(params: {
|
||||
logVerbose("Skipping outbound DM (fromMe); no pairing reply needed.");
|
||||
return {
|
||||
allowed: false,
|
||||
storeForContext: false,
|
||||
shouldMarkRead: false,
|
||||
isSelfChat,
|
||||
resolvedAccountId: account.accountId,
|
||||
@ -130,6 +150,7 @@ export async function checkInboundAccessControl(params: {
|
||||
logVerbose("Blocked dm (dmPolicy: disabled)");
|
||||
return {
|
||||
allowed: false,
|
||||
storeForContext: false,
|
||||
shouldMarkRead: false,
|
||||
isSelfChat,
|
||||
resolvedAccountId: account.accountId,
|
||||
@ -172,6 +193,7 @@ export async function checkInboundAccessControl(params: {
|
||||
}
|
||||
return {
|
||||
allowed: false,
|
||||
storeForContext: false,
|
||||
shouldMarkRead: false,
|
||||
isSelfChat,
|
||||
resolvedAccountId: account.accountId,
|
||||
@ -182,6 +204,7 @@ export async function checkInboundAccessControl(params: {
|
||||
|
||||
return {
|
||||
allowed: true,
|
||||
storeForContext: false,
|
||||
shouldMarkRead: true,
|
||||
isSelfChat,
|
||||
resolvedAccountId: account.accountId,
|
||||
|
||||
@ -190,9 +190,10 @@ export async function monitorWebInbox(options: {
|
||||
sock: { sendMessage: (jid, content) => sock.sendMessage(jid, content) },
|
||||
remoteJid,
|
||||
});
|
||||
if (!access.allowed) continue;
|
||||
if (!access.allowed && !access.storeForContext) continue;
|
||||
const contextOnly = !access.allowed && access.storeForContext;
|
||||
|
||||
if (id && !access.isSelfChat && options.sendReadReceipts !== false) {
|
||||
if (id && !contextOnly && !access.isSelfChat && options.sendReadReceipts !== false) {
|
||||
const participant = msg.key?.participant;
|
||||
try {
|
||||
await sock.readMessages([{ remoteJid, id, participant, fromMe: false }]);
|
||||
@ -298,6 +299,7 @@ export async function monitorWebInbox(options: {
|
||||
sendMedia,
|
||||
mediaPath,
|
||||
mediaType,
|
||||
contextOnly,
|
||||
};
|
||||
try {
|
||||
const task = Promise.resolve(debouncer.enqueue(inboundMessage));
|
||||
|
||||
@ -39,4 +39,6 @@ export type WebInboundMessage = {
|
||||
mediaType?: string;
|
||||
mediaUrl?: string;
|
||||
wasMentioned?: boolean;
|
||||
/** When true, message should only be stored for group context — not trigger a reply. */
|
||||
contextOnly?: boolean;
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user