fix(whatsapp): upgrade Baileys 6.7→6.17, fix proto import and 515 restart
Baileys 6.7.21 silently failed the pairing handshake. Upgrade to 6.17.16 which fixes this. Three related issues: 1. proto is no longer a named ESM export in 6.17.x — use createRequire to import via CJS (matching the proven v1 pattern). 2. Setup auth script didn't handle the 515 stream restart that WhatsApp sends after successful pairing. Refactored to reconnect (matching v1's connectSocket(isReconnect) pattern) instead of hanging until timeout. 3. Added succeeded guard and process.exit(0) to prevent timeout race after successful auth. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -20,11 +20,7 @@ export interface ChannelSetup {
|
||||
conversations: ConversationConfig[];
|
||||
|
||||
/** Called when an inbound message arrives from the platform. */
|
||||
onInbound(
|
||||
platformId: string,
|
||||
threadId: string | null,
|
||||
message: InboundMessage,
|
||||
): void | Promise<void>;
|
||||
onInbound(platformId: string, threadId: string | null, message: InboundMessage): void | Promise<void>;
|
||||
|
||||
/** Called when the adapter discovers metadata about a conversation. */
|
||||
onMetadata(platformId: string, name?: string, isGroup?: boolean): void;
|
||||
|
||||
@@ -181,7 +181,12 @@ export function createChatSdkBridge(config: ChatSdkBridgeConfig): ChannelAdapter
|
||||
// (is_group=0 short-circuits the per-thread escalation).
|
||||
chat.onDirectMessage(async (thread, message) => {
|
||||
const channelId = adapter.channelIdFromThreadId(thread.id);
|
||||
log.info('Inbound DM received', { adapter: adapter.name, channelId, sender: (message.author as any)?.fullName ?? (message.author as any)?.userId ?? 'unknown', threadId: thread.id });
|
||||
log.info('Inbound DM received', {
|
||||
adapter: adapter.name,
|
||||
channelId,
|
||||
sender: (message.author as any)?.fullName ?? (message.author as any)?.userId ?? 'unknown',
|
||||
threadId: thread.id,
|
||||
});
|
||||
await setupConfig.onInbound(channelId, thread.id, await messageToInbound(message));
|
||||
await thread.subscribe();
|
||||
});
|
||||
|
||||
@@ -310,9 +310,7 @@ export async function waitForPairing(code: string, opts: WaitForPairingOptions =
|
||||
?.slice()
|
||||
.reverse()
|
||||
.find((a) => !a.matched);
|
||||
reject(new Error(
|
||||
`Pairing ${code} invalidated by wrong code${lastMiss ? ` (${lastMiss.candidate})` : ''}`
|
||||
));
|
||||
reject(new Error(`Pairing ${code} invalidated by wrong code${lastMiss ? ` (${lastMiss.candidate})` : ''}`));
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* getMessage fallback, outgoing queue, group metadata cache, LID mapping,
|
||||
* reconnection with backoff.
|
||||
*
|
||||
* Auth credentials persist in data/whatsapp-auth/. On first run:
|
||||
* Auth credentials persist in store/auth/. On first run:
|
||||
* - If WHATSAPP_PHONE_NUMBER is set → pairing code (printed to log)
|
||||
* - Otherwise → QR code (printed to log)
|
||||
* Subsequent restarts reuse the saved session automatically.
|
||||
@@ -24,7 +24,6 @@ import {
|
||||
makeCacheableSignalKeyStore,
|
||||
normalizeMessageContent,
|
||||
useMultiFileAuthState,
|
||||
proto,
|
||||
} from '@whiskeysockets/baileys';
|
||||
import type { GroupMetadata, WAMessageKey, WAMessage, WASocket } from '@whiskeysockets/baileys';
|
||||
|
||||
@@ -46,8 +45,10 @@ import type {
|
||||
// Fixed in Baileys 7.x but not backported. Without this, pairing codes fail with
|
||||
// "couldn't link device" because WhatsApp receives an invalid platform ID.
|
||||
// Must use createRequire — ESM `import *` creates a read-only namespace.
|
||||
// proto is not available as a named ESM export — use createRequire (same as v1)
|
||||
import { createRequire } from 'module';
|
||||
const _require = createRequire(import.meta.url);
|
||||
const { proto } = _require('@whiskeysockets/baileys') as { proto: any };
|
||||
try {
|
||||
const _generics = _require('@whiskeysockets/baileys/lib/Utils/generics') as Record<string, unknown>;
|
||||
_generics.getPlatformId = (browser: string): string => {
|
||||
@@ -63,7 +64,7 @@ try {
|
||||
|
||||
const baileysLogger = pino({ level: 'silent' });
|
||||
|
||||
const AUTH_DIR_NAME = 'whatsapp-auth';
|
||||
const AUTH_DIR = path.join(process.cwd(), 'store', 'auth');
|
||||
const GROUP_SYNC_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24h
|
||||
const GROUP_METADATA_CACHE_TTL_MS = 60_000; // 1 min for outbound sends
|
||||
const SENT_MESSAGE_CACHE_MAX = 256;
|
||||
@@ -148,13 +149,13 @@ function buildMediaMessage(data: Buffer, filename: string, ext: string, caption?
|
||||
|
||||
registerChannelAdapter('whatsapp', {
|
||||
factory: () => {
|
||||
const env = readEnvFile(['WHATSAPP_PHONE_NUMBER']);
|
||||
const env = readEnvFile(['WHATSAPP_PHONE_NUMBER', 'WHATSAPP_ENABLED']);
|
||||
const phoneNumber = env.WHATSAPP_PHONE_NUMBER;
|
||||
const authDir = path.join(DATA_DIR, AUTH_DIR_NAME);
|
||||
const authDir = AUTH_DIR;
|
||||
|
||||
// Skip if no existing auth and no phone number for pairing
|
||||
// Skip if no existing auth, no phone number for pairing, and not explicitly enabled (QR mode)
|
||||
const hasAuth = fs.existsSync(path.join(authDir, 'creds.json'));
|
||||
if (!hasAuth && !phoneNumber) return null;
|
||||
if (!hasAuth && !phoneNumber && !env.WHATSAPP_ENABLED) return null;
|
||||
|
||||
fs.mkdirSync(authDir, { recursive: true });
|
||||
|
||||
@@ -173,7 +174,7 @@ registerChannelAdapter('whatsapp', {
|
||||
let flushing = false;
|
||||
|
||||
// Sent message cache for retry/re-encrypt requests
|
||||
const sentMessageCache = new Map<string, proto.IMessage>();
|
||||
const sentMessageCache = new Map<string, any>();
|
||||
|
||||
// Group metadata cache with TTL
|
||||
const groupMetadataCache = new Map<string, { metadata: GroupMetadata; expiresAt: number }>();
|
||||
@@ -197,7 +198,7 @@ registerChannelAdapter('whatsapp', {
|
||||
let rejectFirstOpen: ((err: Error) => void) | undefined;
|
||||
|
||||
// Pairing code file for the setup skill to poll
|
||||
const pairingCodeFile = path.join(DATA_DIR, 'whatsapp-pairing-code.txt');
|
||||
const pairingCodeFile = path.join(process.cwd(), 'store', 'pairing-code.txt');
|
||||
|
||||
// --- Helpers ---
|
||||
|
||||
|
||||
@@ -15,7 +15,15 @@ export interface Migration {
|
||||
up: (db: Database.Database) => void;
|
||||
}
|
||||
|
||||
const migrations: Migration[] = [migration001, migration002, migration003, migration004, migration005, migration007, migration008];
|
||||
const migrations: Migration[] = [
|
||||
migration001,
|
||||
migration002,
|
||||
migration003,
|
||||
migration004,
|
||||
migration005,
|
||||
migration007,
|
||||
migration008,
|
||||
];
|
||||
|
||||
export function runMigrations(db: Database.Database): void {
|
||||
db.exec(`
|
||||
|
||||
@@ -314,4 +314,3 @@ function safeParseContent(raw: string): { text?: string; sender?: string; sender
|
||||
return { text: raw };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user