WhatsApp AI Bot is a TypeScript-based chatbot that integrates multiple AI models with WhatsApp using the Baileys library. The bot supports text-to-text models (ChatGPT, Gemini, Ollama) and text-to-image models (DALL-E, Flux, Stability AI), with a flexible custom model system.
Key Technologies:
- TypeScript (strict mode)
- Baileys (WhatsApp Web API)
- Multiple AI Provider SDKs (OpenAI, Google Generative AI, Stability AI, etc.)
- MongoDB (optional session storage)
- vite-node (development runtime)
whatsapp-ai-bot/
├── src/
│ ├── index.ts # Entry point
│ ├── whatsapp-ai.config.ts # Bot configuration
│ ├── baileys/ # WhatsApp integration layer
│ │ ├── index.ts # Connection setup
│ │ ├── env.ts # Environment variables
│ │ ├── handlers/
│ │ │ ├── messages.ts # Message batch handler
│ │ │ └── message.ts # Individual message handler
│ │ ├── hooks/
│ │ │ └── useMessageParser.ts # Message metadata parser
│ │ └── database/
│ │ └── mongo.ts # MongoDB connection
│ ├── models/ # AI model implementations
│ │ ├── BaseAiModel.ts # Abstract base class
│ │ ├── GeminiModel.ts # Google Gemini
│ │ ├── OpenAIModel.ts # ChatGPT & DALL-E
│ │ ├── StabilityModel.ts # Stability AI
│ │ ├── FluxModel.ts # Hugging Face Flux
│ │ ├── OllamaModel.ts # Ollama
│ │ └── CustomModel.ts # Context-aware custom models
│ ├── types/ # TypeScript type definitions
│ │ ├── AiModels.d.ts # Model type unions
│ │ └── Config.d.ts # Configuration interfaces
│ └── util/ # Utility functions
│ ├── Util.ts # Prefix matching, file reading
│ └── MessageTemplates.ts # Response templates
├── docs/ # Documentation
├── .env.example # Environment template
├── package.json # Dependencies & scripts
└── tsconfig.json # TypeScript config (strict mode)
- Abstract Base Model Pattern: All AI models extend
AIModel<AIArguments, CallBack>abstract class (src/models/BaseAiModel.ts:63) - Configuration-Driven: Models are enabled/disabled via .env and configured in whatsapp-ai.config.ts
- Prefix-Based Routing: Messages are routed to models based on prefix matching (!gemini, !chatgpt, etc.)
- Session Management: Each user gets isolated conversation history per model
- Metadata-Rich Processing: Messages are parsed into rich metadata objects before processing
WhatsApp Message
↓
[baileys/index.ts] connectToWhatsApp() - Event listener setup
↓
[handlers/messages.ts] messagesHandler() - Batch processing
↓
[hooks/useMessageParser.ts] useMessageParser() - Extract metadata
↓
[handlers/message.ts] handleMessage() - Route to model
↓
[util/Util.ts] getModelByPrefix() - Match prefix to model
↓
[models/*Model.ts] sendMessage() - Generate response
↓
[baileys] client.sendMessage() - Send reply
All models are disabled by default for security. Enable explicitly:
# Enable a model
GEMINI_ENABLED=True
GEMINI_PREFIX=!gemini
API_KEY_GEMINI=your_api_key_here
# Optional: Customize icon prefix
GEMINI_ICON_PREFIX=🔮Important ENV patterns:
- Boolean values use string "True" (case-sensitive):
process.env.GEMINI_ENABLED === 'True'(src/baileys/env.ts:83) - All API keys are optional but required if model is enabled
- Processing message customizable via
PROCESSINGenv var (src/baileys/env.ts:58)
const config: Config = {
sendWelcomeMessage: true,
models: {
ChatGPT: { prefix: ENV.OPENAI_PREFIX, enable: ENV.OPENAI_ENABLED },
Gemini: { prefix: ENV.GEMINI_PREFIX, enable: ENV.GEMINI_ENABLED },
// ... other models
Custom: [
{
modelName: 'whatsapp-ai-bot',
prefix: '!wa',
enable: true,
context: './docs/wa-ai-bot.md', // Can be file path, URL, or text
baseModel: 'Gemini' // or 'ChatGPT'
}
]
},
prefix: {
enabled: true, // If false, uses defaultModel for all messages
defaultModel: 'ChatGPT'
},
sessionStorage: { enable: true, wwjsPath: './' },
selfMessage: { skipPrefix: false }
};All models must implement:
abstract class AIModel<AIArguments, CallBack> {
// Session management
public sessionCreate(user: string): void
public sessionRemove(user: string): void
public sessionExists(user: string): boolean
public sessionAddMessage(user: string, args: any): void
// Required implementation
abstract sendMessage(args: AIArguments, handle: CallBack): Promise<any>
// Icon prefix for responses
public addPrefixIcon(text: string): string
}interface AIArguments {
sender: string; // User ID
prompt: string; // User message (prefix removed)
metadata: AIMetaData; // Rich message metadata
prefix: string; // Model prefix used
}Contains comprehensive message information:
- Basic:
remoteJid,sender,senderName,fromMe,timeStamp - Message type:
msgType(text/image/video/audio/document/contact/location) - Quote handling:
isQuoted,quoteMetaData(for replied messages) - Media:
hasImage,imgMetaData,hasAudio,audioMetaData - Group info:
isGroup,groupMetaData
- Create model file in
src/models/YourModel.ts:
import { AIModel, AIArguments, AIHandle } from './BaseAiModel';
import { ENV } from '../baileys/env';
class YourModel extends AIModel<AIArguments, AIHandle> {
public constructor() {
super(ENV.API_KEY_YOUR_MODEL, 'YourModel', ENV.YOUR_MODEL_ICON_PREFIX);
}
async sendMessage({ sender, prompt, metadata }: AIArguments, handle: AIHandle) {
try {
// Create session if needed
if (!this.sessionExists(sender)) {
this.sessionCreate(sender);
}
// Call your AI API
const response = await yourApiCall(prompt);
// Return response with icon prefix
handle({ text: this.addPrefixIcon(response) });
} catch (err) {
handle('', 'Error: ' + err);
}
}
}
export { YourModel };- Add environment configuration in
src/baileys/env.ts:
interface EnvInterface {
// ... existing fields
YOUR_MODEL_PREFIX?: string;
YOUR_MODEL_ENABLED: boolean;
API_KEY_YOUR_MODEL?: string;
}
export const ENV: EnvInterface = {
// ... existing values
YOUR_MODEL_PREFIX: process.env.YOUR_MODEL_PREFIX,
YOUR_MODEL_ENABLED: process.env.YOUR_MODEL_ENABLED === 'True',
API_KEY_YOUR_MODEL: process.env.API_KEY_YOUR_MODEL,
};- Add to type definitions in
src/types/AiModels.d.ts:
export type AIModels = 'ChatGPT' | 'Gemini' | 'FLUX' | 'Stability' | 'Dalle' | 'Ollama' | 'YourModel' | 'Custom';- Register in config (src/whatsapp-ai.config.ts):
const config: Config = {
models: {
// ... existing models
YourModel: {
prefix: ENV.YOUR_MODEL_PREFIX,
enable: ENV.YOUR_MODEL_ENABLED
}
}
};- Add to model table in
src/baileys/handlers/message.ts:
import { YourModel } from '../../models/YourModel';
const modelTable: Record<AIModels, any> = {
// ... existing models
YourModel: ENV.YOUR_MODEL_ENABLED ? new YourModel() : null,
};- Update .env.example:
YOUR_MODEL_PREFIX=!yourmodel
YOUR_MODEL_ENABLED=False
API_KEY_YOUR_MODEL=ADD_YOUR_KEYCustom models allow context-aware responses without creating new model classes:
Custom: [
{
modelName: 'product-expert',
prefix: '!product',
enable: true,
context: './docs/product-knowledge.md', // File path
baseModel: 'Gemini',
dangerouslyAllowFewShotApproach: false // Use system instructions vs prompt injection
},
{
modelName: 'support-bot',
prefix: '!support',
enable: true,
context: 'https://example.com/support-docs', // URL
baseModel: 'ChatGPT'
},
{
modelName: 'simple-bot',
prefix: '!simple',
enable: true,
context: 'You are a helpful assistant that...', // Plain text
baseModel: 'Gemini'
}
]Context sources (src/models/CustomModel.ts:75-101):
- File path:
.md,.txt,.textfiles - URL: Starts with
http://,https://,ftp://, etc. - Plain text: Any other string
System instruction modes:
dangerouslyAllowFewShotApproach: false(default): Uses model's system instruction APIdangerouslyAllowFewShotApproach: true: Injects context into prompt (less reliable)
# Clone repository
git clone https://github.com/Zain-ul-din/WhatsApp-Ai-bot.git
cd WhatsApp-Ai-bot
# Install dependencies
yarn install # or npm install
# Configure environment
cp .env.example .env
# Edit .env with your API keys and enable desired models# Start development server (auto-reload)
yarn dev
# Format code with Prettier
yarn format
# Run tests
yarn test
# Build (just runs yarn install)
yarn build
# Start (install + dev)
yarn startThe project uses strict mode TypeScript (tsconfig.json:8):
strict: true- All strict type checks enablednoImplicitAny: true- No implicit any typesstrictNullChecks: true- Null/undefined must be explicitnoImplicitReturns: true- All code paths must returnnoUnusedParameters: true- Unused params are errors
-
Imports: Organize as Third-party → Local modules → Types/Utils
/* Third-party modules */ import { makeWASocket } from '@whiskeysockets/baileys'; /* Local modules */ import { ENV } from './env'; import config from '../whatsapp-ai.config';
-
Error Handling: Always use try-catch in model sendMessage()
async sendMessage(args: AIArguments, handle: AIHandle) { try { // ... implementation handle({ text: response }); } catch (err) { handle('', 'Error: ' + err); } }
-
Async/Await: Prefer async/await over .then() chains
-
Type Safety: Use defined interfaces, avoid
anyexcept for Baileys types -
Session Management: Check session exists before using:
if (!this.sessionExists(sender)) { this.sessionCreate(sender); }
The Util.getModelByPrefix() method:
- Case-insensitive prefix matching
- Skips disabled models
- Returns model metadata for routing
- Custom models handled separately with
getModelByCustomPrefix()
Extracts comprehensive metadata:
- Message type detection (text/image/video/audio/document/contact/location)
- Quote/reply handling with full context
- Group metadata (name, locked status)
- Image handling with URL, mimeType, caption
- Audio handling with URL, mimeType
- Timestamp conversion
Models can handle images in two ways:
- Direct image message:
metadata.hasImage = true - Quoted image:
metadata.isQuoted && metadata.quoteMetaData.hasImage = true
Example from GeminiModel (src/models/GeminiModel.ts:83-114):
if (metadata.isQuoted && metadata.quoteMetaData.hasImage) {
message = await this.generateImageCompletion(
prompt,
metadata.quoteMetaData.imgMetaData,
metadata.quoteMetaData.message
);
} else if (metadata.hasImage) {
message = await this.generateImageCompletion(
prompt,
metadata.imgMetaData,
metadata.message.message
);
}Models can return:
-
Text response:
handle({ text: 'Response message' });
-
Image response (text-to-image models):
handle({ image: { url: 'https://...' }, caption: 'Generated image' });
The message handler automatically:
- Deletes "Processing..." message for images
- Edits "Processing..." message for text responses
- Handles errors gracefully
Two modes supported (src/baileys/index.ts:16-24):
-
File-based (default):
await useMultiFileAuthState('auth_info_baileys');
- Stores in
./auth_info_baileys/directory - QR code shown on first run
- Session persists across restarts
- Stores in
-
MongoDB-based:
MONGO_ENABLED=True MONGO_URL=mongodb://localhost:27017/whatsapp-bot
- Stores in MongoDB collection
- Useful for multi-instance deployments
Auto-reconnect logic:
- Reconnects on connection close (unless logged out)
- Deletes credentials on logout and reconnects (shows new QR)
- Saves credentials on updates
- Update model configuration in
whatsapp-ai.config.ts - Set prefix in
.envfile - No code changes needed - prefix routing is automatic
Each model class manages its own parameters:
-
Gemini: Model selection in constructor (src/models/GeminiModel.ts:33)
this.generativeModel = this.Gemini.getGenerativeModel({ model: 'gemini-1.5-flash', // Change here systemInstruction: this.instructions });
-
ChatGPT: Model in env (src/baileys/env.ts:71)
OPENAI_MODEL=gpt-4 # or gpt-3.5-turbo
Enable debug mode in .env:
DEBUG=TrueDebug logs appear in:
src/baileys/handlers/message.ts:41- Model not foundsrc/baileys/handlers/message.ts:62- Model disabled
IGNORE_SELF_MESSAGES=TrueUseful when bot shouldn't respond to messages you send.
Groups automatically detected by JID ending with @g.us (src/baileys/hooks/useMessageParser.ts:128)
Locked groups (restrict mode) are ignored (src/baileys/handlers/messages.ts:24)
- Start bot:
yarn dev - Scan QR code with WhatsApp
- Send test messages with prefixes:
!gemini Hello- Test Gemini!chatgpt What is TypeScript?- Test ChatGPT!dalle A sunset over mountains- Test image generation
test/index.test.ts- Main tests (run withyarn test)baileys/index.ts- Baileys integration tests (run withyarn test:baileys)
- Node.js 18+ (for native fetch support in CustomModel)
- Yarn or npm
- Valid API keys for enabled models
- (Optional) MongoDB instance for distributed sessions
Dockerfile included in repository. Build and run:
docker build -t whatsapp-ai-bot .
docker run -d --env-file .env whatsapp-ai-botSee docs/deployment.md for GitHub Codespaces and cloud deployment guides.
Important: On first deployment, scan QR code from logs to authenticate.
-
"Model not found" in debug logs
- Check model is enabled in
.env(MODELNAME_ENABLED=True) - Check prefix matches in
whatsapp-ai.config.ts - Verify prefix in message is lowercase-matched
- Check model is enabled in
-
API key errors
- Verify API key in
.envmatches provider requirements - Check API key has proper permissions
- For DALL-E: Can use separate key with
API_KEY_OPENAI_DALLEor share with ChatGPT
- Verify API key in
-
Connection issues
- Delete
auth_info_baileys/folder and re-scan QR - Check WhatsApp Web is not open elsewhere
- Verify internet connection stability
- Delete
-
TypeScript errors
- Run
yarn installto ensure dependencies match - Check strict mode compliance
- Verify all required fields in interfaces are provided
- Run
-
Image processing fails
- Verify mimeType is in validMimeTypes (src/models/GeminiModel.ts:21)
- Check image is properly downloaded with
downloadMediaMessage - Ensure model supports vision (Gemini does, ChatGPT may need GPT-4 Vision)
- Always read files before modifying - Use Read tool to understand current implementation
- Follow existing patterns - Match import organization, error handling, and type usage
- Maintain type safety - No
anytypes except for Baileys library types - Test incrementally - Test each model addition/change individually
- Update documentation - Keep CLAUDE.md in sync with code changes
- Don't break existing models - Changes should be additive or isolated
- Preserve session management - Don't skip
sessionExists()checks - Handle errors gracefully - Always provide user-friendly error messages
- Respect configuration - Honor enabled/disabled states from config
- Maintain backwards compatibility - Existing .env files should still work
- Single Responsibility - Each model handles one AI provider
- Open/Closed - Easy to add models, hard to break existing ones
- Configuration over Code - Prefer .env changes to code changes
- Fail Gracefully - Disabled models don't break the bot
- Session Isolation - User conversations don't leak between models
- Main branch:
mainormaster - Feature branches:
feature/model-nameorfix/issue-description - Development on assigned branch:
claude/claude-md-*(for AI sessions)
Follow conventional commits:
feat: add Anthropic Claude model support
fix: handle empty prompts in GeminiModel
docs: update CLAUDE.md with new model guide
refactor: extract session management to util
- Format code:
yarn format - Check TypeScript:
tsc --noEmit - Test manually with
yarn dev - Update documentation if needed
- Baileys Documentation
- Config Documentation
- Deployment Guide
- OpenAI API Reference
- Google Gemini Docs
- Stability AI Docs
- Entry point:
src/index.ts - Configuration:
src/whatsapp-ai.config.ts - Environment:
src/baileys/env.ts - Model routing:
src/baileys/handlers/message.ts - Prefix matching:
src/util/Util.ts - Message parsing:
src/baileys/hooks/useMessageParser.ts
- AI Models enum:
src/types/AiModels.d.ts - Config interface:
src/types/Config.d.ts - Metadata types:
src/models/BaseAiModel.ts
- Boolean env vars:
process.env.VAR === 'True' - Model instantiation: Conditional on
ENV.MODEL_ENABLED - Session management: Check existence before use
- Error handling: Always catch and call
handle('', error)
Last Updated: 2025-11-22 Bot Version: 1.0.0 Maintainer: Zain-ul-Din