A Node.js WhatsApp assistant built on Baileys with a multi-provider free-tier LLM chain, per-user rate limiting and a daily request budget, prompt-injection and leak guardrails, TTL-based MongoDB memory, natural-language reminders, and an admin dashboard — designed to run at zero cost.
Misa is a WhatsApp chatbot I run as a personal assistant. It is a long-running Node.js service that stays connected to WhatsApp through the Baileys library, decides which messages deserve a reply, calls a language model, post-processes the answer, and keeps a small, privacy-conscious memory in MongoDB.
The engineering goal was a bot that costs nothing to run and degrades gracefully: it uses only free-tier model endpoints, switches providers automatically when one is rate-limited, caps its own daily usage, and never exposes its own setup. The repository is private, so this page describes the design rather than linking to code.
What It Does
Message gating: drops system events, replays, duplicates and its own messages; in groups it only continues when tagged or replied to
Provider fallback chain: several free-tier LLMLarge language model — a neural network trained on large amounts of text to predict the next token, used for generation, summarisation, classification and more. providers tried in order, with automatic pausing on rate limits and bad keys
Rate limiting and budget: per-sender cooldown, burst muting, concurrency limits and a daily request budget with a reserve for the owner
guardrailA safety layer around an LLM that checks inputs and/or outputs against policies, then blocks, rewrites or flags violations.: forged-header sanitising, secret/internal-question detection before the model is called, and an output filter that blocks leaks
Reply planning: length classes, a hint mode for long problem statements, a repeat-avoidance tracker and response cleanup
Reminders: natural-language reminders ("tomorrow 9am …") parsed to a time and text with chrono-node
Scheduled messages: a small scheduler plus an external cron endpoint, with the decision logic split into pure, testable functions
Admin dashboard: usage meters, a kill switch, feature flags, provider status and a live provider test
How It Works
A message arrives from WhatsApp and is filtered, sanitised and classified.
Slash commands and reminder phrases are handled without the model.
Guardrails check whether the question is about the bot's internals; if so it gets a canned deflection and never reaches the LLM.
Limits and the daily budget are checked.
A reply is planned, the provider chain is called, and the answer is cleaned, validated and shaped.
Memory is marked dirty and saved with a short debounce, and also flushed on shutdown.
Data: MongoDB via Mongoose (auth state, bot state, TTL-expiring recap lines and contacts, reminders)
AI:OpenAI-compatibleAn API that accepts the same request and response format as OpenAI's, so existing clients and SDKs work with it by changing only the base URL. chat endpoints from multiple free-tier providers, with streaming for slow reasoning models
Hosting: Render, with a self keep-alive ping and graceful shutdown handling
Notes
The bot is intentionally small in scope and I treat it as an ongoing experiment in running LLM features reliably on free infrastructure, not as a finished product.