A rule-based WhatsApp chatbot with a PHP web dashboard. The bot handles incoming messages by matching keywords and navigating numbered menus, then auto-replies with text or images. The dashboard lets you manage keywords, menus, AI config, logs, and bot settings through a browser — no code editing required.
- Bot Engine: Node.js +
@whiskeysockets/baileys - Database: SQLite3 via
sql.js - Dashboard: PHP 7.4 + HTML/CSS/JS vanilla
- Internal API: Express.js (Node.js) ↔ PHP via cURL
- AI: OpenAI-compatible API (Groq, OpenRouter, OpenAI, dll)
npm installcp .env.example .envThen edit .env as needed:
# Port used by the Node.js bot API (default: 3001)
BOT_API_PORT=3001
# URL used by PHP to call the bot API
# Must be 127.0.0.1 (loopback) — NOT 0.0.0.0
BOT_API_URL=http://127.0.0.1:3001
# API key shared between the PHP dashboard and the bot — MUST be changed in production
BOT_API_KEY=changeme-secret-key
# Dashboard login password — MUST be changed in production
DASHBOARD_PASSWORD=admin123
# Optional: bind host (default 127.0.0.1)
# BIND_HOST=127.0.0.1
⚠️ Important: Always changeBOT_API_KEYandDASHBOARD_PASSWORDbefore deploying to a public server.
chmod +x start.sh
bash start.shThe script will automatically:
- Check that
node,npm, andphpare installed - Run
npm installifnode_modulesis missing - Start the Node.js bot (port
BOT_API_PORT, default3001) - Start the PHP dashboard (port
8080) - Save logs to the
logs/folder - Shut down both services cleanly on
Ctrl+C
# Terminal 1 — Node.js bot
npm start
# or for development (auto-restart on change):
npm run dev
# Terminal 2 — PHP dashboard
php -S localhost:8080 -t web/The bot will automatically create the SQLite database and seed sample data on first run.
- Open your browser at
http://localhost:8080 - Log in with the password from
.env(default:admin123) - Scan the QR code shown on the dashboard with WhatsApp on your phone
walini/
├── bot/
│ ├── index.js # Main bot engine (Baileys)
│ ├── api.js # REST API for the dashboard
│ ├── database.js # SQLite helper functions
│ └── ai.js # AI chat handler (OpenAI-compatible)
├── database/
│ ├── schema.sql # Database schema + sample data
│ ├── ai.json # AI config (url, model, key, prefixes)
│ ├── ai.example.json # AI config template
│ └── chatbot.db # Database file (auto-generated)
├── web/ # PHP Dashboard
│ ├── config.php # PHP config + shared API helpers
│ ├── index.php # Main dashboard + stats
│ ├── keywords.php # Manage keyword auto-replies
│ ├── menus.php # Manage interactive menus
│ ├── ai.php # AI configuration
│ ├── placeholders.php # Placeholder reference
│ ├── logs.php # Message log viewer
│ ├── settings.php # Bot settings
│ ├── login.php # Login page
│ ├── logout.php # Logout
│ ├── debug.php # Connection diagnostics (delete after setup)
│ ├── api/
│ │ └── proxy.php # PHP → Node.js API proxy
│ ├── partials/
│ │ ├── sidebar.php
│ │ └── topbar.php
│ └── assets/
│ ├── css/style.css
│ └── js/app.js
├── logs/ # Runtime logs (auto-generated by start.sh)
│ ├── bot.log
│ └── php.log
├── uploads/ # Uploaded images + received media
│ └── received/ # Files saved by #update keyword
├── .baileys_auth/ # Baileys session files (auto-generated)
├── .env # Environment config (create from .env.example)
├── .env.example # Environment template
├── start.sh # One-command startup script
├── package.json
└── README.md
Incoming message
│
├─ Location message?
│ └─ Save to session + send location_reply (if set) → STOP
│
├─ Caption matches #update + media attached?
│ └─ Save file to uploads/received/ → reply success/failure → STOP
│
├─ Starts with AI prefix? (e.g. "bot: ", "ai: ")
│ └─ Forward to configured AI → send reply → STOP
│
├─ Menu trigger word? (default: "menu")
│ └─ Show main menu
│
├─ User is in an active menu session?
│ └─ Process number selection
│
├─ Matches a keyword?
│ └─ Send keyword reply
│
├─ Number at root level?
│ └─ Process menu selection
│
├─ AI fallback enabled?
│ └─ Forward to AI → send reply
│
└─ Send static fallback message
Messages are silently ignored if:
- Sent by the bot itself
- From
status@broadcast - From a group chat (
@g.us) bot_activeis set to0in the dashboard
- Partial — message contains the keyword (case-insensitive)
- Exact — message is exactly the keyword
- Number
0always means "Back" or "Exit" - A menu item with no reply text = it has sub-menu children
- Menu sessions time out after a configurable period (default: 30 minutes)
All reply texts (keywords, menus, AI output, location reply, #update replies) support placeholders. Full list at web/placeholders.php. Examples:
| Placeholder | Value |
|---|---|
{nama} |
Sender's WhatsApp display name |
{nomor} |
Sender's phone number |
{jam} / {tanggal} / {hari} |
Current time / date / day |
{lokasi_url} |
Google Maps link from location message |
{lokasi_terakhir_url} |
Google Maps link from last saved location |
{bot_name} |
Bot name from settings |
{foto} |
Sender's profile picture URL |
{status} |
Sender's WhatsApp about/status |
- Prefix trigger — user starts message with a configured prefix (e.g.
bot: apa itu AI?) - Fallback AI — if no keyword/menu matches, the message is forwarded to AI instead of showing the static fallback
- Configured via dashboard → AI Chat, or directly in
database/ai.json - Compatible with any OpenAI-format API (Groq, OpenRouter, OpenAI, local LLM, etc)
- User sends any media (image/video/audio/document) with caption
#update - Bot saves the file to
uploads/received/and replies with a configurable success/failure message - Configurable per allowed file type, max 20MB
- Voice notes (PTT) are always skipped
- Change
BOT_API_KEYandDASHBOARD_PASSWORDin.envbefore deploying - Keep
BOT_API_PORT(default3001) private — it is for internal PHP ↔ Node communication only - Only expose ports
80/443(the PHP dashboard) to the public - Never commit
.envordatabase/ai.json(contains your AI API key) to version control - Delete
web/debug.phponce setup is confirmed working
Bot won't connect / need to re-scan QR:
# Delete the Baileys session and scan again
rm -rf .baileys_auth/
bash start.shDashboard can't reach the bot:
- Make sure the bot is running (
bash start.shornpm start) - Open
http://localhost:8080/debug.phpfor a full connection diagnostic - Check that
BOT_API_URLandBOT_API_PORTin.envmatch what is actually running - Check that port
3001is not blocked by a firewall
AI not responding:
- Check that
database/ai.jsonexists and hasurl,model, andkeyset - Configure via dashboard → AI Chat
- Test the API key directly with
curlif unsure
Port already in use:
# Check what is using port 3001 or 8080
ss -tlnp | grep -E '3001|8080'
# Or check the PID files from the last run
cat .bot.pid
cat .php.pidDuplicate keywords on startup:
node bot/cleanup-db.js