How to Build a WeChat Chatbot from Scratch with Wechaty: Auto-Reply & Group Management
A step-by-step practical guide to building a deployable WeChat bot using Wechaty. Covers QR login, message listening, auto-replies, group automation, and common pitfalls with complete production-ready code.

How to Build a WeChat Chatbot from Scratch with Wechaty: Auto-Reply & Group Management
As a backend developer, you've likely encountered scenarios like these: business groups are flooded with hundreds of repetitive questions daily ("How do I use feature X?", "Why is X throwing an error?"), leaving your support team exhausted from manual replies. Or maybe you want to build a scheduled notification assistant, but WeChat lacks an official Bot API, and reverse-engineering protocols risks account bans. When I led a team building internal tools, we faced the exact same headaches—until we discovered Wechaty. It turns out that "writing a WeChat bot" can be as simple as writing just 6 lines of code.
Wechaty is an open-source Conversational RPA SDK that abstracts away the low-level protocol details for platforms like WeChat and WhatsApp. You don't need to worry about protocol reverse-engineering; you just focus on business logic. It supports TypeScript/JavaScript and runs seamlessly on Node.js or Docker. In this guide, I'll walk you through building a fully functional chatbot from scratch with auto-reply and group management capabilities.
By the end of this tutorial, you will:
- Set up a local development environment and run your first bot
- Understand Wechaty's event-driven architecture (QR login, message listening, group events, etc.)
- Write a complete "auto-reply + group welcome + keyword trigger" bot
- Learn deployment best practices and avoid common pitfalls
Prerequisites
Before starting, ensure your environment meets these requirements:
| Dependency | Minimum Version | Why It's Needed |
|---|---|---|
| Node.js | 16+ | Wechaty relies on modern ES Module features |
| npm | 7+ | Package management and dependency resolution |
| TypeScript (Optional) | 4.4+ | Required if you plan to develop using TypeScript |
If you haven't installed Node.js 16+ yet, I highly recommend using nvm (Node Version Manager) to manage versions cleanly:
bash
## After installing nvm
nvm install 16
nvm use 16
node -v # Verify v16.x.x or higher
Why the version matters: Wechaty heavily uses modern ES Module syntax. Running it on Node 14 or older will trigger import/export parsing errors. Learn this the hard way, or just use nvm.
Quick Start: Run Your First Bot in 6 Lines
Wechaty officially claims to have "the world's shortest chatbot code." Let's verify that.
Step 1: Initialize the Project
bash
mkdir my-wechaty-bot && cd my-wechaty-bot
npm init -y
npm install wechaty
npm init -y generates a default package.json, and npm install wechaty pulls in the SDK and its dependencies. Why use an isolated directory? Wechaty's dependency chain is quite long. Placing it in an existing project may cause version conflicts. A clean folder makes debugging much easier.
Step 2: Create the Bot Entry File
In the project root, create bot.js and add the following:
javascript
import { WechatyBuilder } from 'wechaty'
const wechaty = WechatyBuilder.build()
wechaty
.on('scan', (qrcode, status) => {
const url = `https://wechaty.js.org/qrcode/${encodeURIComponent(qrcode)}`
console.log(`Please scan the QR code to log in! Status: ${status}\nQR Link: ${url}`)
})
.on('login', (user) => console.log(`✅ Login successful. Welcome back: ${user}`))
.on('message', (msg) => console.log(`📨 Message received: ${msg}`))
wechaty.start()
This demonstrates Wechaty's core event model:
- scan: Triggered when the bot starts and generates a QR code. You scan it with WeChat on your phone. The
qrcodepayload is raw data; appending it to Wechaty's official URL renders it in a browser. QR codes expire quickly, so you'll need to re-scan if disconnected. - login: Fires after successful QR confirmation. The
userobject contains your logged-in WeChat account info. - message: Triggered for every incoming message (both private chats and group messages).
- start(): Initializes the Puppet (underlying protocol layer) and begins listening.
Step 3: Run It
bash
node bot.js
The terminal will print a QR code link. Open it in your browser and scan it with WeChat. Once confirmed, you'll see ✅ Login successful. Any subsequent messages sent to your account or groups will be logged in the terminal.
Note: By default, this uses the Puppeteer protocol (based on WeChat Web). Due to WeChat's official policies, the Web protocol has restrictions on certain accounts. If login fails, I'll show you how to switch protocols later.
Practical Example: A Complete "Smart Customer Service + Group Manager" Bot
Logging messages is fine, but let's build something that actually works in production. This bot will:
- Auto-reply to common private chat inquiries (keyword matching)
- Send a welcome message when new members join a group
- Reply with a feature list when "help" is mentioned in a group
Full Code
Create smart-bot.js:
javascript
import { WechatyBuilder } from 'wechaty'
// 1. Define auto-reply rules (In production, replace this with a DB lookup or LLM API call)
const FAQ = {
'Pricing': 'Our services come in Basic (Free) and Pro ($99/month). Visit our website for details.',
'Refund': 'We support a 7-day no-questions-asked refund policy. Please submit a request via your order page.',
'Help': 'Reply with these keywords for assistance: Pricing, Refund, Contact Support. Or connect with human support.'
}
// 2. Create Wechaty instance
const wechaty = WechatyBuilder.build({
name: 'smart-customer-bot', // Naming the bot helps with log separation
})
// 3. Listen to QR scan event
wechaty.on('scan', (qrcode, status) => {
const url = `https://wechaty.js.org/qrcode/${encodeURIComponent(qrcode)}`
console.log(`📱 Please scan to log in!\nStatus: ${status}\nOpen link to view QR: ${url}`)
})
// 4. Listen to login event
wechaty.on('login', async (user) => {
console.log(`🎉 Login successful! Bot started. Account: ${user.name()}`)
// Send a confirmation message to yourself upon startup
await user.say('🤖 Smart Customer Bot is online and ready!')
})
// 5. Listen to message event (Core Logic)
wechaty.on('message', async (msg) => {
// Ignore messages sent by the bot itself to prevent infinite loops
if (msg.self()) return
const text = msg.text()
const from = msg.from()
// --- Scenario A: Private Chat Auto-Reply ---
if (!msg.room() && from) {
console.log(`💬 DM - From ${from.name()}: ${text}`)
// Iterate FAQ rules, reply if keyword matches
for (const [keyword, reply] of Object.entries(FAQ)) {
if (text.toLowerCase().includes(keyword.toLowerCase())) {
await msg.say(reply)
console.log(`📤 Auto-replied: ${reply.substring(0, 30)}...`)
return
}
}
// Fallback response if no keyword matches
await msg.say('🤖 Unable to recognize your query. Reply "Help" for common FAQs, or wait for human support.')
}
// --- Scenario B: Group Interaction ---
if (msg.room()) {
const room = await msg.room()
const roomName = await room.topic()
console.log(`👥 Group [${roomName}] - ${from?.name() || 'Unknown'}: ${text}`)
// Reply when "help" keyword is detected in group
if (text.toLowerCase().includes('help')) {
const helpMsg = `📋 Group Bot Features:
1. Reply "Pricing" for service plans
2. Reply "Refund" for refund policies
3. Reply "Contact Support" for human agents`
await room.say(helpMsg)
}
}
})
// 6. Listen to member join event
wechaty.on('room-join', async (room, inviteeList, inviter) => {
const roomName = await room.topic()
const inviteeNames = inviteeList.map(c => c.name()).join(', ')
const inviterName = inviter?.name() || 'Unknown'
console.log(`🎊 New member joined: ${inviteeNames} (invited by ${inviterName}), Group: ${roomName}`)
const welcomeMsg = `👋 Welcome @${inviteeNames} to 【${roomName}】!\nI'm your group assistant. Ask me anything or reply "Help" for features.`
await room.say(welcomeMsg)
})
// 7. Start the bot
wechaty.start()
Code Breakdown
| Module | Purpose | Design Rationale |
|---|---|---|
FAQ Dictionary |
Maps keywords to replies | In production, replace with an LLM API call or knowledge base query. A dictionary is perfect for prototyping. |
msg.self() Check |
Filters bot's own messages | Without it, your bot's replies would trigger the message event again, causing an infinite loop. |
msg.room() |
Distinguishes DMs from group chats | null/undefined means DM. A truthy value means group chat, allowing you to call room.say(), room.topic(), etc. |
room-join Event |
Auto-welcome for new members | Highly efficient for managing large groups compared to manual greetings. |
@ Mention |
Using @Name in room.say() |
Wechaty automatically handles the underlying mention logic for you. |
Common Pitfalls & Troubleshooting
1. Web Protocol Login Fails
Error: login failed is the most common issue with the default Web Puppet. WeChat officially restricts Web login for many accounts. Solution: Switch to an alternative Puppet via environment variables:
bash
## Linux/macOS
export WECHATY_PUPPET=wechaty-puppet-service
## or
export WECHATY_PUPPET=wechaty-puppet-wechat4u
## Windows (PowerShell)
$env:WECHATY_PUPPET="wechaty-puppet-service"
wechaty-puppet-service uses a hosted iPad/Windows protocol. It's highly stable but may require a token (apply on the Wechaty official website).
2. SyntaxError: Cannot use import statement outside a module
This happens when Node.js doesn't recognize ES Modules. Add this to your package.json:
json
{
"type": "module"
}
After adding it, node bot.js will correctly parse import statements.
3. Account Ban Risk from Frequent Auto-Replies
Excessive automated replies can trigger WeChat's anti-bot security. Mitigation strategies:
- Add Random Delays:
await new Promise(r => setTimeout(r, Math.random() * 2000 + 1000))before replying. - Set Daily Caps: Track daily reply counts and stop responding after hitting a threshold.
- Only Respond to Mentions: Use
msg.mentionSelf()to reply only when@mentioned, significantly reducing trigger frequency.
4. Running in Docker
Docker deployment eliminates Node.js environment setup headaches. The official image works out-of-the-box:
bash
docker run -ti --rm --volume="$(pwd)":/bot wechaty/wechaty smart-bot.js
Place smart-bot.js in your current directory. Docker mounts it to /bot inside the container and executes it. Scan the QR link printed in the terminal as usual.
Summary & Next Steps
Let's recap what we've accomplished:
- Set up the environment with
npm init&npm install wechaty - Ran a basic QR login + message listener in 6 lines of code
- Built a fully functional Smart Customer Bot covering DM auto-replies, group welcomes, and keyword recognition
- Navigated Web protocol limits, ES Module errors, and anti-bot risks
Where to go from here:
- Integrate LLMs: Replace the FAQ dictionary with OpenAI/Claude API calls for true AI conversational capabilities.
- Plugin Architecture: Use Wechaty's
WechatyPluginsystem to modularize welcome messages, cron jobs, and keyword handlers. - Database Persistence: Log chats and user interactions to MongoDB or PostgreSQL for analytics and user profiling.
- CI/CD Deployment: Use GitHub Actions + Docker to automate deployments to your VPS on push.
With 23,000+ GitHub stars, multi-platform support (WeChat/WhatsApp), and a 6-line quick start, this toolkit is well worth an afternoon of your time. Writing code always beats reading articles. Drop your questions in the comments, and happy building!