An AI auto-responder for your personal Telegram account.
When someone messages you, a bot connected via Telegram's Business (Secretary) Mode replies as you — in your voice, your language, and within your rules — powered by Google Gemini 2.5 Flash.
Secretary Bot uses Telegram's Business Mode (also called Chat Automation / Secretary Mode) to handle incoming messages on your behalf. Unlike a generic chatbot, it's built to sound like you: it reads from a small set of your real example messages, mirrors your tone and language, and respects boundaries you define — so replies feel personal, not robotic.
- 🧠 Speaks in your voice — few-shot examples from your own messages teach it your tone, length, and style.
- 👥 Per-contact awareness — set tone, gender, and rules per sender (casual with friends, "never quote a price" to clients).
- 🤝 Learns relationships — paste a real chat and it captures how you talk to that specific person (tone + example exchanges), so replies match each relationship.
- 🗣️ Configure by chat — DM the bot "treat @ali as my client, he's a he" or "add a fact: I don't work weekends" and it parses, confirms, and saves contacts and facts — no file editing, no restart.
- 📇 Personal knowledge base — answers from a facts/FAQ file, and refuses to invent personal details it doesn't know.
- ⌨️ Human pacing — bursty, bimodal timing: usually replies in seconds, but occasionally goes quiet for a minute or two like someone who stepped away, then a short "typing…" burst right before sending. Momentum-aware (rapid back-and-forth stays snappy).
- 🎙️ Voice notes — transcribes incoming voice messages (Uzbek/Russian/English, kept verbatim) and replies to them like any text.
- 🌍 Language-matching — replies in whatever language the sender writes in.
- 💬 Per-chat memory — keeps the last 20 message pairs of context for each conversation.
- ⏰ Time-aware — grounded with your real local date/time, so it never fabricates "what time is it?"
- 🔇 No leaked reasoning — Gemini's internal thinking is disabled and sanitized so only the final message is sent.
- 🛡️ Safe by design — never replies to your own messages, respects Telegram's 24-hour window, and notifies you instead of sending a broken reply on failure.
Someone messages YOU ──▶ Telegram ──(business_message)──▶ Secretary Bot ──▶ Gemini 2.5 Flash
│
Their chat ◀──(sendMessage with business_connection_id)───────────┘
The bot subscribes to Telegram's business_connection and business_message updates. For each incoming message it composes a prompt (persona + your voice examples + per-message context + recent history), calls Gemini, and sends the reply back into the chat as you via the business_connection_id parameter.
- Node.js ≥ 18
- A Telegram bot token — from @BotFather
- Business (Secretary) Mode enabled on that bot in BotFather — see step 1. Without it, Telegram won't deliver the
business_messageupdates the bot depends on, and nothing will work. - A Google Gemini API key — from Google AI Studio
git clone https://github.com/dostonsulaymon/secretary-bot.git
cd secretary-bot
npm install
cp .env.example .env # then fill in your values
npm run dev # run from sourceIn @BotFather: /mybots → select your bot → Bot Settings → Business Mode → Enable.
In the Telegram app: Settings → Telegram Business → Chatbots (or Chat Automation on some clients) → enter @yourbotusername, and grant the "Reply to messages" permission.
You'll receive a confirmation DM: ✅ Secretary Mode active.
Three optional, gitignored files shape how the bot behaves. Each ships with a committed *.example.json template — copy and edit:
cp voice.example.json voice.json # how you talk
cp contacts.example.json contacts.json # who you talk to
cp facts.example.json facts.json # what you knowvoice.json— paste 15–20 of your real Telegram replies plusstylehints. Examples teach tone far better than instructions; this is what makes replies sound like you.contacts.json— per-sendertone,relationship, andnotes/rules, keyed bychat_idor@username. Adefaultentry covers everyone else.facts.json—factsthe bot may rely on andfaqguidance. It answers from these and refuses to invent personal details beyond them.
Restart the bot after editing — these load once at startup.
Tip: You don't have to edit
contacts.jsonorfacts.jsonby hand. Just DM the bot (from your owner account) in plain language — it confirms before saving, and changes apply with no restart:
- Contacts: "treat @ali as my client, formal, he's a he, never quote prices" · "who is @ali" · "list my contacts" · "forget @ali"
- Teach a relationship: "this is my chat with @bekzod:" then paste a real conversation → it learns the tone and saves up to 10 example exchanges for that person
- Facts: "add a fact: I don't work weekends" · "when someone asks for my email, tell them to message me here" · "what do you know about me" · "forget the fact about weekends"
- Clear (asks to confirm): "clear all facts" · "clear all contacts" · "reset everything"
To tag someone without a public
@username, use their numericchat_id(the bot logs each sender's id when they message you).
All configuration lives in .env (see .env.example):
| Variable | Required | Description |
|---|---|---|
TELEGRAM_BOT_TOKEN |
✅ | Bot token from BotFather |
GEMINI_API_KEY |
✅ | Google Gemini API key |
OWNER_USER_ID |
✅ | Your numeric Telegram ID (@userinfobot) — used to notify you and to ignore your own messages |
SYSTEM_PROMPT |
✅ | Base persona / instructions for the model |
OWNER_TIMEZONE |
— | IANA timezone for date/time grounding (default Asia/Tashkent) |
NATURAL_TYPING |
— | Show "typing…" and delay replies to feel human; false to reply instantly (default true) |
Build and run under pm2:
npm run build
pm2 start ecosystem.config.js
pm2 save && pm2 startup # survive reboots
pm2 logs secretary-bot| Command | Description |
|---|---|
npm run dev |
Run from source via ts-node |
npm run typecheck |
Type-check without emitting (tsc --noEmit) |
npm run build |
Compile src/ → dist/ |
npm start |
Run the compiled build |
src/
├── index.ts # entry point, bot setup, allowed_updates
├── handlers/
│ ├── business.ts # business_connection + business_message (the core auto-responder)
│ ├── direct.ts # routes owner DMs to admin; canned reply for everyone else
│ └── admin.ts # owner control panel — manage contacts by chatting with the bot
├── ai/
│ └── gemini.ts # Gemini 2.5 Flash wrapper
├── profile/
│ ├── voice.ts # persona + style + voice examples → system prompt
│ ├── contacts.ts # per-sender tone & rules lookup (by chat_id / @username)
│ └── facts.ts # facts/FAQ knowledge base + anti-hallucination guard
└── store/
└── sessions.ts # in-memory per-chat conversation memory
- Memory is in-process — conversation history is wiped on restart. Swap
src/store/sessions.tsfor Redis/SQLite to persist. - Text & voice — text messages and voice notes (transcribed via Gemini) are handled; other media (photos, stickers, video notes) are currently ignored.
- Single instance — long polling does not support multiple instances on the same token.
- SDK — uses the now-deprecated
@google/generative-ai; migration to@google/genaiis planned.
- Voice personalization via few-shot examples
- Per-contact awareness (tone & rules per sender)
- A personal facts / FAQ knowledge base
- Interactive owner control — manage contacts by DMing the bot
- Draft-approval mode for important contacts
Contributions are welcome! Please read CONTRIBUTING.md and our Code of Conduct before opening a PR. Found a security issue? See SECURITY.md — report it privately, not as a public issue.
MIT © Doston Sulaymon