wechatbot
δΈ­ζ–‡

The Node.js SDK is the most feature-rich implementation. 69 tests, zero runtime dependencies. Requires Node.js β‰₯ 22.

Install

npm install @wechatbot/wechatbot

Quick start

import { WeChatBot } from '@wechatbot/wechatbot' const bot = new WeChatBot() await bot.login() bot.onMessage(async (msg) => { await bot.sendTyping(msg.userId) await bot.reply(msg, `Echo: ${msg.text}`) }) await bot.start()

Configuration

const bot = new WeChatBot({ storage: 'file', // 'file' | 'memory' | custom Storage storageDir: '~/.wechatbot', logLevel: 'info', // 'debug' | 'info' | 'warn' | 'error' | 'silent' loginCallbacks: { onQrUrl: (url) => renderQrCode(url), onScanned: () => console.log('Scanned!'), onExpired: () => console.log('Expired...'), }, })

Sending β€” reply() and send()

Two methods handle all outgoing messages. reply() replies to an incoming message (auto context_token, auto cancel typing). send() sends to a user by ID.

Both accept the same SendContent type:

// Text (string shorthand) await bot.reply(msg, 'Hello!') // Text (object) await bot.reply(msg, { text: 'Hello!' }) // Image with optional caption await bot.reply(msg, { image: pngBuffer, caption: 'Screenshot' }) // Video with optional caption await bot.reply(msg, { video: mp4Buffer, caption: 'Check this out' }) // File β€” auto-routes by extension: // .png/.jpg/.gif/.webp β†’ sent as image // .mp4/.mov/.webm β†’ sent as video // everything else β†’ sent as file attachment await bot.reply(msg, { file: data, fileName: 'report.pdf' }) await bot.reply(msg, { file: data, fileName: 'photo.png' }) // β†’ image! // From URL β€” auto-download + auto-detect type from Content-Type / extension await bot.reply(msg, { url: 'https://example.com/photo.jpg' }) await bot.reply(msg, { url: 'https://picsum.photos/400/300', caption: 'Random!' }) // send() works the same way, just takes userId instead of message await bot.send(userId, 'Hello!') await bot.send(userId, { image: buffer, caption: 'Hi!' })

Downloading media

One method downloads any media type from an incoming message:

bot.onMessage(async (msg) => { const media = await bot.download(msg) if (!media) return // no media in this message console.log(media.type) // 'image' | 'file' | 'video' | 'voice' console.log(media.data) // Buffer console.log(media.fileName) // 'report.pdf' (for files) console.log(media.format) // 'wav' | 'silk' (for voice) // Save to disk await writeFile(`/tmp/${media.fileName ?? 'download'}`, media.data) // Echo back if (media.type === 'image') { await bot.reply(msg, { image: media.data, caption: 'Got your image!' }) } })

Priority: image > file > video > voice. Voice is auto-transcoded from SILK to WAV (if silk-wasm is installed).

Middleware

Express/Koa-style composable middleware pipeline. Runs before message handlers.

import { WeChatBot, loggingMiddleware, rateLimitMiddleware, typeFilterMiddleware, filterMiddleware, } from '@wechatbot/wechatbot' const bot = new WeChatBot() bot.use(loggingMiddleware(bot.logger)) bot.use(rateLimitMiddleware({ maxMessages: 10, windowMs: 60_000 })) bot.use(typeFilterMiddleware('text', 'image')) bot.use(filterMiddleware(/^\/\w+/)) // Custom middleware bot.use(async (ctx, next) => { const start = Date.now() await next() console.log(`Processed in ${Date.now() - start}ms`) })

Middleware that doesn't call next() stops the chain β€” perfect for auth, filtering, routing.

Pluggable storage

// File storage (default) β€” survives restarts const bot1 = new WeChatBot({ storage: 'file' }) // Memory storage β€” fast, ephemeral const bot2 = new WeChatBot({ storage: 'memory' }) // Custom β€” implement 5 methods class RedisStorage { async get(key) { } async set(key, value) { } async delete(key) { } async has(key) { } async clear() { } } const bot3 = new WeChatBot({ storage: new RedisStorage() })

Events

bot.on('login', (creds) => console.log(`Logged in: ${creds.accountId}`)) bot.on('session:expired', () => console.log('Session expired')) bot.on('session:restored', (creds) => console.log('Restored')) bot.on('error', (err) => console.error(err)) bot.on('poll:start', () => { }) bot.on('poll:stop', () => { }) bot.on('close', () => { })

Advanced: MessageBuilder

For complex multi-item messages, use the builder directly:

const payload = bot.createMessage(userId) .text("Here's your report:") .file({ media: cdnRef, fileName: 'report.pdf', size: 542188 }) .build() await bot.sendRaw(payload)

Other advanced methods: bot.upload(opts) for CDN upload without sending, bot.downloadRaw(media, aeskey?) for raw CDN references.

API reference

Core methods
MethodDescription
new WeChatBot(opts?)Create instance
bot.login(opts?)QR login (skips if credentials exist)
bot.start()Start long-poll loop
bot.run(opts?)login() + start() in one call
bot.stop()Stop gracefully
bot.onMessage(handler)Register message handler
bot.reply(msg, content)Reply β€” text, image, video, file, or URL
bot.send(userId, content)Send to user β€” same content types
bot.download(msg)Download any media from message
bot.sendTyping(userId)Show "typing..." indicator
bot.stopTyping(userId)Cancel typing indicator
bot.use(middleware)Add middleware to pipeline
bot.sendRaw(payload)Send pre-built MessageBuilder payload
bot.upload(opts)Upload to CDN without sending
bot.downloadRaw(media)Download from raw CDN reference
bot.createMessage(userId)Create MessageBuilder for user
Node.js SDK β€” @wechatbot/wechatbot