mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-09 02:38:26 +00:00
refactor: unify DM pairing challenge flows
This commit is contained in:
@@ -35,7 +35,7 @@ import { logVerbose } from "../../globals.js";
|
||||
import { enqueueSystemEvent } from "../../infra/system-events.js";
|
||||
import { logDebug, logError } from "../../logger.js";
|
||||
import { getAgentScopedMediaLocalRoots } from "../../media/local-roots.js";
|
||||
import { buildPairingReply } from "../../pairing/pairing-messages.js";
|
||||
import { issuePairingChallenge } from "../../pairing/pairing-challenge.js";
|
||||
import { upsertChannelPairingRequest } from "../../pairing/pairing-store.js";
|
||||
import { resolveAgentRoute } from "../../routing/resolve-route.js";
|
||||
import { createNonExitingRuntime, type RuntimeEnv } from "../../runtime.js";
|
||||
@@ -519,28 +519,37 @@ async function ensureDmComponentAuthorized(params: {
|
||||
}
|
||||
|
||||
if (dmPolicy === "pairing") {
|
||||
const { code, created } = await upsertChannelPairingRequest({
|
||||
const pairingResult = await issuePairingChallenge({
|
||||
channel: "discord",
|
||||
id: user.id,
|
||||
accountId: ctx.accountId,
|
||||
senderId: user.id,
|
||||
senderIdLine: `Your Discord user id: ${user.id}`,
|
||||
meta: {
|
||||
tag: formatDiscordUserTag(user),
|
||||
name: user.username,
|
||||
},
|
||||
upsertPairingRequest: async ({ id, meta }) =>
|
||||
await upsertChannelPairingRequest({
|
||||
channel: "discord",
|
||||
id,
|
||||
accountId: ctx.accountId,
|
||||
meta,
|
||||
}),
|
||||
sendPairingReply: async (text) => {
|
||||
await interaction.reply({
|
||||
content: text,
|
||||
...replyOpts,
|
||||
});
|
||||
},
|
||||
});
|
||||
try {
|
||||
await interaction.reply({
|
||||
content: created
|
||||
? buildPairingReply({
|
||||
channel: "discord",
|
||||
idLine: `Your Discord user id: ${user.id}`,
|
||||
code,
|
||||
})
|
||||
: "Pairing already requested. Ask the bot owner to approve your code.",
|
||||
...replyOpts,
|
||||
});
|
||||
} catch {
|
||||
// Interaction may have expired
|
||||
if (!pairingResult.created) {
|
||||
try {
|
||||
await interaction.reply({
|
||||
content: "Pairing already requested. Ask the bot owner to approve your code.",
|
||||
...replyOpts,
|
||||
});
|
||||
} catch {
|
||||
// Interaction may have expired
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { issuePairingChallenge } from "../../pairing/pairing-challenge.js";
|
||||
import { upsertChannelPairingRequest } from "../../pairing/pairing-store.js";
|
||||
import type { DiscordDmCommandAccess } from "./dm-command-auth.js";
|
||||
|
||||
@@ -19,17 +20,25 @@ export async function handleDiscordDmCommandDecision(params: {
|
||||
|
||||
if (params.dmAccess.decision === "pairing") {
|
||||
const upsertPairingRequest = params.upsertPairingRequest ?? upsertChannelPairingRequest;
|
||||
const { code, created } = await upsertPairingRequest({
|
||||
const result = await issuePairingChallenge({
|
||||
channel: "discord",
|
||||
id: params.sender.id,
|
||||
accountId: params.accountId,
|
||||
senderId: params.sender.id,
|
||||
senderIdLine: `Your Discord user id: ${params.sender.id}`,
|
||||
meta: {
|
||||
tag: params.sender.tag,
|
||||
name: params.sender.name,
|
||||
},
|
||||
upsertPairingRequest: async ({ id, meta }) =>
|
||||
await upsertPairingRequest({
|
||||
channel: "discord",
|
||||
id,
|
||||
accountId: params.accountId,
|
||||
meta,
|
||||
}),
|
||||
sendPairingReply: async () => {},
|
||||
});
|
||||
if (created) {
|
||||
await params.onPairingCreated(code);
|
||||
if (result.created && result.code) {
|
||||
await params.onPairingCreated(result.code);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ import {
|
||||
resolveIMessageRemoteAttachmentRoots,
|
||||
} from "../../media/inbound-path-policy.js";
|
||||
import { kindFromMime } from "../../media/mime.js";
|
||||
import { buildPairingReply } from "../../pairing/pairing-messages.js";
|
||||
import { issuePairingChallenge } from "../../pairing/pairing-challenge.js";
|
||||
import {
|
||||
readChannelAllowFromStore,
|
||||
upsertChannelPairingRequest,
|
||||
@@ -288,36 +288,36 @@ export async function monitorIMessageProvider(opts: MonitorIMessageOpts = {}): P
|
||||
if (!sender) {
|
||||
return;
|
||||
}
|
||||
const { code, created } = await upsertChannelPairingRequest({
|
||||
await issuePairingChallenge({
|
||||
channel: "imessage",
|
||||
id: decision.senderId,
|
||||
accountId: accountInfo.accountId,
|
||||
senderId: decision.senderId,
|
||||
senderIdLine: `Your iMessage sender id: ${decision.senderId}`,
|
||||
meta: {
|
||||
sender: decision.senderId,
|
||||
chatId: chatId ? String(chatId) : undefined,
|
||||
},
|
||||
});
|
||||
if (created) {
|
||||
logVerbose(`imessage pairing request sender=${decision.senderId}`);
|
||||
try {
|
||||
await sendMessageIMessage(
|
||||
sender,
|
||||
buildPairingReply({
|
||||
channel: "imessage",
|
||||
idLine: `Your iMessage sender id: ${decision.senderId}`,
|
||||
code,
|
||||
}),
|
||||
{
|
||||
client,
|
||||
maxBytes: mediaMaxBytes,
|
||||
accountId: accountInfo.accountId,
|
||||
...(chatId ? { chatId } : {}),
|
||||
},
|
||||
);
|
||||
} catch (err) {
|
||||
upsertPairingRequest: async ({ id, meta }) =>
|
||||
await upsertChannelPairingRequest({
|
||||
channel: "imessage",
|
||||
id,
|
||||
accountId: accountInfo.accountId,
|
||||
meta,
|
||||
}),
|
||||
onCreated: () => {
|
||||
logVerbose(`imessage pairing request sender=${decision.senderId}`);
|
||||
},
|
||||
sendPairingReply: async (text) => {
|
||||
await sendMessageIMessage(sender, text, {
|
||||
client,
|
||||
maxBytes: mediaMaxBytes,
|
||||
accountId: accountInfo.accountId,
|
||||
...(chatId ? { chatId } : {}),
|
||||
});
|
||||
},
|
||||
onReplyError: (err) => {
|
||||
logVerbose(`imessage pairing reply failed for ${decision.senderId}: ${String(err)}`);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,8 +24,8 @@ import {
|
||||
warnMissingProviderGroupPolicyFallbackOnce,
|
||||
} from "../config/runtime-group-policy.js";
|
||||
import { danger, logVerbose } from "../globals.js";
|
||||
import { issuePairingChallenge } from "../pairing/pairing-challenge.js";
|
||||
import { resolvePairingIdLabel } from "../pairing/pairing-labels.js";
|
||||
import { buildPairingReply } from "../pairing/pairing-messages.js";
|
||||
import {
|
||||
readChannelAllowFromStore,
|
||||
upsertChannelPairingRequest,
|
||||
@@ -237,15 +237,6 @@ async function sendLinePairingReply(params: {
|
||||
context: LineHandlerContext;
|
||||
}): Promise<void> {
|
||||
const { senderId, replyToken, context } = params;
|
||||
const { code, created } = await upsertChannelPairingRequest({
|
||||
channel: "line",
|
||||
id: senderId,
|
||||
accountId: context.account.accountId,
|
||||
});
|
||||
if (!created) {
|
||||
return;
|
||||
}
|
||||
logVerbose(`line pairing request sender=${senderId}`);
|
||||
const idLabel = (() => {
|
||||
try {
|
||||
return resolvePairingIdLabel("line");
|
||||
@@ -253,30 +244,42 @@ async function sendLinePairingReply(params: {
|
||||
return "lineUserId";
|
||||
}
|
||||
})();
|
||||
const text = buildPairingReply({
|
||||
await issuePairingChallenge({
|
||||
channel: "line",
|
||||
idLine: `Your ${idLabel}: ${senderId}`,
|
||||
code,
|
||||
});
|
||||
try {
|
||||
if (replyToken) {
|
||||
await replyMessageLine(replyToken, [{ type: "text", text }], {
|
||||
senderId,
|
||||
senderIdLine: `Your ${idLabel}: ${senderId}`,
|
||||
upsertPairingRequest: async ({ id, meta }) =>
|
||||
await upsertChannelPairingRequest({
|
||||
channel: "line",
|
||||
id,
|
||||
accountId: context.account.accountId,
|
||||
channelAccessToken: context.account.channelAccessToken,
|
||||
});
|
||||
return;
|
||||
}
|
||||
} catch (err) {
|
||||
logVerbose(`line pairing reply failed for ${senderId}: ${String(err)}`);
|
||||
}
|
||||
try {
|
||||
await pushMessageLine(`line:${senderId}`, text, {
|
||||
accountId: context.account.accountId,
|
||||
channelAccessToken: context.account.channelAccessToken,
|
||||
});
|
||||
} catch (err) {
|
||||
logVerbose(`line pairing reply failed for ${senderId}: ${String(err)}`);
|
||||
}
|
||||
meta,
|
||||
}),
|
||||
onCreated: () => {
|
||||
logVerbose(`line pairing request sender=${senderId}`);
|
||||
},
|
||||
sendPairingReply: async (text) => {
|
||||
if (replyToken) {
|
||||
try {
|
||||
await replyMessageLine(replyToken, [{ type: "text", text }], {
|
||||
accountId: context.account.accountId,
|
||||
channelAccessToken: context.account.channelAccessToken,
|
||||
});
|
||||
return;
|
||||
} catch (err) {
|
||||
logVerbose(`line pairing reply failed for ${senderId}: ${String(err)}`);
|
||||
}
|
||||
}
|
||||
try {
|
||||
await pushMessageLine(`line:${senderId}`, text, {
|
||||
accountId: context.account.accountId,
|
||||
channelAccessToken: context.account.channelAccessToken,
|
||||
});
|
||||
} catch (err) {
|
||||
logVerbose(`line pairing reply failed for ${senderId}: ${String(err)}`);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async function shouldProcessLineEvent(
|
||||
|
||||
90
src/pairing/pairing-challenge.test.ts
Normal file
90
src/pairing/pairing-challenge.test.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { issuePairingChallenge } from "./pairing-challenge.js";
|
||||
|
||||
describe("issuePairingChallenge", () => {
|
||||
it("creates and sends a pairing reply when request is newly created", async () => {
|
||||
const sent: string[] = [];
|
||||
|
||||
const result = await issuePairingChallenge({
|
||||
channel: "telegram",
|
||||
senderId: "123",
|
||||
senderIdLine: "Your Telegram user id: 123",
|
||||
upsertPairingRequest: async () => ({ code: "ABCD", created: true }),
|
||||
sendPairingReply: async (text) => {
|
||||
sent.push(text);
|
||||
},
|
||||
});
|
||||
|
||||
expect(result).toEqual({ created: true, code: "ABCD" });
|
||||
expect(sent).toHaveLength(1);
|
||||
expect(sent[0]).toContain("ABCD");
|
||||
});
|
||||
|
||||
it("does not send a reply when request already exists", async () => {
|
||||
const sendPairingReply = vi.fn(async () => {});
|
||||
|
||||
const result = await issuePairingChallenge({
|
||||
channel: "telegram",
|
||||
senderId: "123",
|
||||
senderIdLine: "Your Telegram user id: 123",
|
||||
upsertPairingRequest: async () => ({ code: "ABCD", created: false }),
|
||||
sendPairingReply,
|
||||
});
|
||||
|
||||
expect(result).toEqual({ created: false });
|
||||
expect(sendPairingReply).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("supports custom reply text builder", async () => {
|
||||
const sent: string[] = [];
|
||||
|
||||
await issuePairingChallenge({
|
||||
channel: "line",
|
||||
senderId: "u1",
|
||||
senderIdLine: "Your line id: u1",
|
||||
upsertPairingRequest: async () => ({ code: "ZXCV", created: true }),
|
||||
buildReplyText: ({ code }) => `custom ${code}`,
|
||||
sendPairingReply: async (text) => {
|
||||
sent.push(text);
|
||||
},
|
||||
});
|
||||
|
||||
expect(sent).toEqual(["custom ZXCV"]);
|
||||
});
|
||||
|
||||
it("calls onCreated and forwards meta to upsert", async () => {
|
||||
const onCreated = vi.fn();
|
||||
const upsert = vi.fn(async () => ({ code: "1111", created: true }));
|
||||
|
||||
await issuePairingChallenge({
|
||||
channel: "discord",
|
||||
senderId: "42",
|
||||
senderIdLine: "Your Discord user id: 42",
|
||||
meta: { name: "alice" },
|
||||
upsertPairingRequest: upsert,
|
||||
onCreated,
|
||||
sendPairingReply: async () => {},
|
||||
});
|
||||
|
||||
expect(upsert).toHaveBeenCalledWith({ id: "42", meta: { name: "alice" } });
|
||||
expect(onCreated).toHaveBeenCalledWith({ code: "1111" });
|
||||
});
|
||||
|
||||
it("captures reply errors through onReplyError", async () => {
|
||||
const onReplyError = vi.fn();
|
||||
|
||||
const result = await issuePairingChallenge({
|
||||
channel: "signal",
|
||||
senderId: "+1555",
|
||||
senderIdLine: "Your Signal sender id: +1555",
|
||||
upsertPairingRequest: async () => ({ code: "9999", created: true }),
|
||||
sendPairingReply: async () => {
|
||||
throw new Error("send failed");
|
||||
},
|
||||
onReplyError,
|
||||
});
|
||||
|
||||
expect(result).toEqual({ created: true, code: "9999" });
|
||||
expect(onReplyError).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
@@ -86,6 +86,7 @@ export type { WizardPrompter } from "../wizard/prompts.js";
|
||||
export { isAllowedParsedChatSender } from "./allow-from.js";
|
||||
export { readBooleanParam } from "./boolean-param.js";
|
||||
export { createScopedPairingAccess } from "./pairing-access.js";
|
||||
export { issuePairingChallenge } from "../pairing/pairing-challenge.js";
|
||||
export { resolveRequestUrl } from "./request-url.js";
|
||||
export {
|
||||
buildComputedAccountStatusSnapshot,
|
||||
|
||||
@@ -57,6 +57,7 @@ export type { WizardPrompter } from "../wizard/prompts.js";
|
||||
export { buildAgentMediaPayload } from "./agent-media-payload.js";
|
||||
export { readJsonFileWithFallback } from "./json-store.js";
|
||||
export { createScopedPairingAccess } from "./pairing-access.js";
|
||||
export { issuePairingChallenge } from "../pairing/pairing-challenge.js";
|
||||
export { createPersistentDedupe } from "./persistent-dedupe.js";
|
||||
export {
|
||||
buildBaseChannelStatusSummary,
|
||||
|
||||
@@ -63,6 +63,7 @@ export { formatDocsLink } from "../terminal/links.js";
|
||||
export type { WizardPrompter } from "../wizard/prompts.js";
|
||||
export { resolveInboundRouteEnvelopeBuilderWithRuntime } from "./inbound-envelope.js";
|
||||
export { createScopedPairingAccess } from "./pairing-access.js";
|
||||
export { issuePairingChallenge } from "../pairing/pairing-challenge.js";
|
||||
export { extractToolSend } from "./tool-send.js";
|
||||
export { resolveWebhookPath } from "./webhook-path.js";
|
||||
export type { WebhookInFlightLimiter } from "./webhook-request-guards.js";
|
||||
|
||||
@@ -60,6 +60,7 @@ export {
|
||||
export { formatDocsLink } from "../terminal/links.js";
|
||||
export type { WizardPrompter } from "../wizard/prompts.js";
|
||||
export { createScopedPairingAccess } from "./pairing-access.js";
|
||||
export { issuePairingChallenge } from "../pairing/pairing-challenge.js";
|
||||
export { dispatchInboundReplyWithBase } from "./inbound-reply-dispatch.js";
|
||||
export type { OutboundReplyPayload } from "./reply-payload.js";
|
||||
export {
|
||||
|
||||
@@ -84,6 +84,7 @@ export {
|
||||
resolveAccountWithDefaultFallback,
|
||||
} from "./account-resolution.js";
|
||||
export { createScopedPairingAccess } from "./pairing-access.js";
|
||||
export { issuePairingChallenge } from "../pairing/pairing-challenge.js";
|
||||
export { createPersistentDedupe } from "./persistent-dedupe.js";
|
||||
export type { OutboundReplyPayload } from "./reply-payload.js";
|
||||
export {
|
||||
|
||||
@@ -67,6 +67,7 @@ export { evaluateSenderGroupAccess } from "./group-access.js";
|
||||
export type { SenderGroupAccessDecision } from "./group-access.js";
|
||||
export { resolveInboundRouteEnvelopeBuilderWithRuntime } from "./inbound-envelope.js";
|
||||
export { createScopedPairingAccess } from "./pairing-access.js";
|
||||
export { issuePairingChallenge } from "../pairing/pairing-challenge.js";
|
||||
export { buildChannelSendResult } from "./channel-send-result.js";
|
||||
export type { OutboundReplyPayload } from "./reply-payload.js";
|
||||
export {
|
||||
|
||||
@@ -57,6 +57,7 @@ export { resolveSenderCommandAuthorization } from "./command-auth.js";
|
||||
export { resolveChannelAccountConfigBasePath } from "./config-paths.js";
|
||||
export { loadOutboundMediaFromUrl } from "./outbound-media.js";
|
||||
export { createScopedPairingAccess } from "./pairing-access.js";
|
||||
export { issuePairingChallenge } from "../pairing/pairing-challenge.js";
|
||||
export { buildChannelSendResult } from "./channel-send-result.js";
|
||||
export type { OutboundReplyPayload } from "./reply-payload.js";
|
||||
export {
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { Message } from "@grammyjs/types";
|
||||
import type { Bot } from "grammy";
|
||||
import type { DmPolicy } from "../config/types.js";
|
||||
import { logVerbose } from "../globals.js";
|
||||
import { buildPairingReply } from "../pairing/pairing-messages.js";
|
||||
import { issuePairingChallenge } from "../pairing/pairing-challenge.js";
|
||||
import { upsertChannelPairingRequest } from "../pairing/pairing-store.js";
|
||||
import { withTelegramApiErrorLogging } from "./api-logging.js";
|
||||
import { resolveSenderAllowMatch, type NormalizedAllowFrom } from "./bot-access.js";
|
||||
@@ -70,42 +70,46 @@ export async function enforceTelegramDmAccess(params: {
|
||||
if (dmPolicy === "pairing") {
|
||||
try {
|
||||
const telegramUserId = sender.userId ?? sender.candidateId;
|
||||
const { code, created } = await upsertChannelPairingRequest({
|
||||
await issuePairingChallenge({
|
||||
channel: "telegram",
|
||||
id: telegramUserId,
|
||||
accountId,
|
||||
senderId: telegramUserId,
|
||||
senderIdLine: `Your Telegram user id: ${telegramUserId}`,
|
||||
meta: {
|
||||
username: sender.username || undefined,
|
||||
firstName: sender.firstName,
|
||||
lastName: sender.lastName,
|
||||
},
|
||||
upsertPairingRequest: async ({ id, meta }) =>
|
||||
await upsertChannelPairingRequest({
|
||||
channel: "telegram",
|
||||
id,
|
||||
accountId,
|
||||
meta,
|
||||
}),
|
||||
onCreated: () => {
|
||||
logger.info(
|
||||
{
|
||||
chatId: String(chatId),
|
||||
senderUserId: sender.userId ?? undefined,
|
||||
username: sender.username || undefined,
|
||||
firstName: sender.firstName,
|
||||
lastName: sender.lastName,
|
||||
matchKey: allowMatch.matchKey ?? "none",
|
||||
matchSource: allowMatch.matchSource ?? "none",
|
||||
},
|
||||
"telegram pairing request",
|
||||
);
|
||||
},
|
||||
sendPairingReply: async (text) => {
|
||||
await withTelegramApiErrorLogging({
|
||||
operation: "sendMessage",
|
||||
fn: () => bot.api.sendMessage(chatId, text),
|
||||
});
|
||||
},
|
||||
onReplyError: (err) => {
|
||||
logVerbose(`telegram pairing reply failed for chat ${chatId}: ${String(err)}`);
|
||||
},
|
||||
});
|
||||
if (created) {
|
||||
logger.info(
|
||||
{
|
||||
chatId: String(chatId),
|
||||
senderUserId: sender.userId ?? undefined,
|
||||
username: sender.username || undefined,
|
||||
firstName: sender.firstName,
|
||||
lastName: sender.lastName,
|
||||
matchKey: allowMatch.matchKey ?? "none",
|
||||
matchSource: allowMatch.matchSource ?? "none",
|
||||
},
|
||||
"telegram pairing request",
|
||||
);
|
||||
await withTelegramApiErrorLogging({
|
||||
operation: "sendMessage",
|
||||
fn: () =>
|
||||
bot.api.sendMessage(
|
||||
chatId,
|
||||
buildPairingReply({
|
||||
channel: "telegram",
|
||||
idLine: `Your Telegram user id: ${telegramUserId}`,
|
||||
code,
|
||||
}),
|
||||
),
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
logVerbose(`telegram pairing reply failed for chat ${chatId}: ${String(err)}`);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
warnMissingProviderGroupPolicyFallbackOnce,
|
||||
} from "../../config/runtime-group-policy.js";
|
||||
import { logVerbose } from "../../globals.js";
|
||||
import { buildPairingReply } from "../../pairing/pairing-messages.js";
|
||||
import { issuePairingChallenge } from "../../pairing/pairing-challenge.js";
|
||||
import { upsertChannelPairingRequest } from "../../pairing/pairing-store.js";
|
||||
import {
|
||||
readStoreAllowFromForDmPolicy,
|
||||
@@ -171,28 +171,30 @@ export async function checkInboundAccessControl(params: {
|
||||
if (suppressPairingReply) {
|
||||
logVerbose(`Skipping pairing reply for historical DM from ${candidate}.`);
|
||||
} else {
|
||||
const { code, created } = await upsertChannelPairingRequest({
|
||||
await issuePairingChallenge({
|
||||
channel: "whatsapp",
|
||||
id: candidate,
|
||||
accountId: account.accountId,
|
||||
senderId: candidate,
|
||||
senderIdLine: `Your WhatsApp phone number: ${candidate}`,
|
||||
meta: { name: (params.pushName ?? "").trim() || undefined },
|
||||
});
|
||||
if (created) {
|
||||
logVerbose(
|
||||
`whatsapp pairing request sender=${candidate} name=${params.pushName ?? "unknown"}`,
|
||||
);
|
||||
try {
|
||||
await params.sock.sendMessage(params.remoteJid, {
|
||||
text: buildPairingReply({
|
||||
channel: "whatsapp",
|
||||
idLine: `Your WhatsApp phone number: ${candidate}`,
|
||||
code,
|
||||
}),
|
||||
});
|
||||
} catch (err) {
|
||||
upsertPairingRequest: async ({ id, meta }) =>
|
||||
await upsertChannelPairingRequest({
|
||||
channel: "whatsapp",
|
||||
id,
|
||||
accountId: account.accountId,
|
||||
meta,
|
||||
}),
|
||||
onCreated: () => {
|
||||
logVerbose(
|
||||
`whatsapp pairing request sender=${candidate} name=${params.pushName ?? "unknown"}`,
|
||||
);
|
||||
},
|
||||
sendPairingReply: async (text) => {
|
||||
await params.sock.sendMessage(params.remoteJid, { text });
|
||||
},
|
||||
onReplyError: (err) => {
|
||||
logVerbose(`whatsapp pairing reply failed for ${candidate}: ${String(err)}`);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
return {
|
||||
allowed: false,
|
||||
|
||||
Reference in New Issue
Block a user