A production-ready Telegram bot that acts as an executive assistant for 0G (Zero Gravity) — the Decentralized AI Operating System. Built for event lead capture, prospect engagement, and automated CRM logging.
- Answers questions about 0G using a live-scraped knowledge base from docs.0g.ai (65 pages)
- Conference Mode — automatically onboards new group chat members with a guided questionnaire (name, role, company, X handle) and pushes every answer to HubSpot in real-time
- HubSpot CRM integration — creates contacts, logs every interaction as a note, tracks Telegram metadata (
telegram_id,telegram_username) - Meeting booking — sends your HubSpot scheduling link via inline button
- LLM-powered responses — streams Claude answers token-by-token with typing indicator (falls back to keyword search if no Anthropic key)
- Persona system — users pick from 4 conversational tones (Professional, Friendly, Concise, Enthusiastic)
- Admin tools —
/refreshknowledge base,/statsdashboard,/broadcastto all users - Group-aware — only responds to
@mentionsin groups (except during onboarding)
telegram-ea-bot/
├── bot/
│ ├── __init__.py
│ ├── config.py # Dataclass config — all secrets from env vars
│ ├── main.py # Entry point, handler registration, routing
│ ├── utils.py # Intent detection, @mention gating, markdown escape
│ ├── handlers/
│ │ ├── __init__.py
│ │ ├── conference.py # Conference Mode state machine (onboarding)
│ │ ├── info.py # Info queries — LLM streaming or keyword fallback
│ │ ├── scheduling.py # Meeting link handler
│ │ ├── persona.py # /persona command with inline keyboard
│ │ └── admin.py # /refresh, /stats, /broadcast (admin-only)
│ └── services/
│ ├── __init__.py
│ ├── hubspot.py # HubSpot CRM client (contacts, notes, properties)
│ ├── docs_scraper.py # Async docs site scraper (65 pages, HTML→text)
│ ├── llm.py # Claude streaming + persona system + history
│ ├── google_sheets.py # Google Sheets fallback knowledge source
│ └── cache.py # In-memory TTL cache
├── Dockerfile
├── requirements.txt
├── .env.example # Template for environment variables
├── DEPLOYMENT.md # Step-by-step Railway deployment guide
├── .gitignore
└── .dockerignore
- Startup —
main.pybuilds the TelegramApplication, registers 13 handlers, pre-loads the docs knowledge base - Message routing —
route_message()checks if user is in onboarding → checks@mentionin groups → detects intent (greeting / schedule / info) → dispatches - Conference Mode — When someone joins a group, the bot detects it via
ChatMemberHandler+NEW_CHAT_MEMBERS(dual detection with dedup), starts a 4-step questionnaire, and updates HubSpot after each answer - Info queries — If LLM is enabled, streams Claude's response; otherwise falls back to keyword search across scraped docs
- CRM logging — Every interaction creates/finds a HubSpot contact by
telegram_idand logs a timestamped note
User joins group
│
▼
"What's your full name?" → HubSpot: firstname, lastname
│
▼
"What's your role or title?" → HubSpot: jobtitle
│
▼
"Which company are you with?" → HubSpot: company
│
▼
"X/Twitter handle? (or skip)" → HubSpot: twitterhandle
│
▼
0G overview + stack diagram
+ "Book a Call" button → HubSpot: engagement note
Before deploying, each team member needs:
| Service | What You Need | Where to Get It |
|---|---|---|
| Telegram | Bot Token + your User ID | @BotFather on Telegram |
| HubSpot | Private App Access Token + Meeting Link | HubSpot Settings → Integrations → Private Apps |
| Anthropic (optional) | API Key | console.anthropic.com |
| Railway | Account (Hobby plan, $5/mo) | railway.app |
- Open Telegram and message @BotFather
- Send
/newbotand follow the prompts (pick a name and username) - Copy the bot token (looks like
1234567890:ABCDefGHIjklMNOpqrsTUVwxyz) - To get your admin user ID: message @userinfobot on Telegram — it replies with your numeric ID
- Go to HubSpot Settings → Integrations → Private Apps
- Click Create a private app, name it
Telegram EA Bot - Under Scopes, enable:
crm.objects.contacts.readcrm.objects.contacts.write
- Click Create app and copy the access token
- Create custom contact properties (one-time setup):
- Go to Settings → Properties → Contact Properties
- Create
telegram_id(Single-line text) - Create
telegram_username(Single-line text)
- Get your meeting link: Go to Sales → Meetings → copy the scheduling URL
Skip this step if you want keyword search only — the bot works without it.
- Go to console.anthropic.com
- Create an API key
- Add it as
ANTHROPIC_API_KEYin your environment variables
git clone https://github.com/Jake0G/telegram-ea-bot.git
cd telegram-ea-bot
cp .env.example .env
# Edit .env with your credentials (see Environment Variables table below)python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install -r requirements.txt
python -m bot.mainThe bot will log its feature status on startup:
Feature status — LLM: OFF (keyword search fallback) | Knowledge Base: ON — docs scraper | HubSpot: ON | Conference Mode: ON
Starting bot in POLLING mode.
See DEPLOYMENT.md for the full step-by-step Railway deployment guide.
| Variable | Required | Default | Description |
|---|---|---|---|
TELEGRAM_BOT_TOKEN |
Yes | — | Bot token from @BotFather |
TELEGRAM_ADMIN_USER_ID |
Yes | — | Your Telegram numeric user ID (for admin commands) |
DOCS_BASE_URL |
No | "" |
Docs site to scrape (e.g., https://docs.0g.ai) |
HUBSPOT_ACCESS_TOKEN |
No | "" |
HubSpot Private App token (CRM disabled without it) |
HUBSPOT_MEETING_LINK |
No | "" |
HubSpot scheduling page URL |
ANTHROPIC_API_KEY |
No | "" |
Anthropic API key (keyword search fallback without it) |
ANTHROPIC_MODEL |
No | claude-sonnet-4-20250514 |
Claude model to use |
LLM_MAX_TOKENS |
No | 1024 |
Max tokens for LLM responses |
CACHE_TTL_SECONDS |
No | 900 |
Knowledge base cache duration (seconds) |
CONFERENCE_MODE |
No | true |
Enable/disable group onboarding |
USE_WEBHOOK |
No | false |
Use webhook mode instead of polling |
WEBHOOK_URL |
No | "" |
Public URL for webhook mode |
GOOGLE_SHEET_ID |
No | "" |
Google Sheet ID (fallback knowledge source) |
GOOGLE_CREDENTIALS_FILE |
No | credentials.json |
Path to Google service account JSON |
The bot auto-detects which features to enable based on which env vars are set:
| Feature | Enabled When |
|---|---|
| LLM (Claude) responses | ANTHROPIC_API_KEY is set |
| Docs scraper knowledge base | DOCS_BASE_URL is set |
| Google Sheets knowledge base | GOOGLE_SHEET_ID is set (fallback if no docs URL) |
| HubSpot CRM logging | HUBSPOT_ACCESS_TOKEN is set |
| Meeting booking button | HUBSPOT_MEETING_LINK is set |
| Conference Mode | CONFERENCE_MODE=true (default on) |
| Command | Who | Description |
|---|---|---|
/start |
Everyone | Welcome message with capabilities overview |
/help |
Everyone | Show available commands |
/book |
Everyone | Get the meeting booking link |
/persona |
Everyone | Switch bot personality (requires LLM) |
/clear |
Everyone | Reset conversation memory |
/refresh |
Admin only | Force-reload the knowledge base |
/stats |
Admin only | View interaction statistics |
/broadcast |
Admin only | Send a message to all known users |
- Create a test group on Telegram
- Add your bot as a member (search for its @username)
- Give the bot admin rights (required for reliable join detection):
- Group Settings → Administrators → Add Administrator → select your bot
- Add a new member to the group — the bot will welcome them and start the questionnaire
- Check HubSpot — a new contact should appear with Telegram metadata and engagement notes
Tip: If the bot doesn't have admin rights, it falls back to
NEW_CHAT_MEMBERSservice messages, which work but are less reliable.
- Change
DOCS_BASE_URLto your documentation URL - Edit the
DOCS_PAGESlist inbot/services/docs_scraper.pywith your site's page paths and labels - Redeploy (or run
/refreshif running locally)
The 0G overview and stack diagram sent at the end of onboarding are in bot/handlers/conference.py → _handle_twitter() function.
The state machine is in bot/handlers/conference.py:
- Add a new state to the
OnboardingStateenum (e.g.,ASK_EMAIL) - Create a handler function following the existing pattern (see
_handle_rolefor a simple example) - Update the previous step to advance to your new state
- Add the dispatch in
handle_onboarding_response() - Add the HubSpot property update in your new handler
| Issue | Solution |
|---|---|
KeyError: 'TELEGRAM_BOT_TOKEN' |
Env var not set. Check Railway Variables or your .env file |
| Bot doesn't respond in groups | Bot must be @mentioned in groups. For Conference Mode, bot needs admin rights |
| HubSpot contacts not created | Check HUBSPOT_ACCESS_TOKEN and Private App scopes (contacts read+write) |
telegram_id property error |
Create custom HubSpot properties (see Setup step 2.5) |
| Knowledge base empty | Check DOCS_BASE_URL is correct and accessible. Run /refresh to force reload |
| LLM not responding | Set ANTHROPIC_API_KEY. Without it, keyword search is used |
| Conference Mode not triggering | Ensure CONFERENCE_MODE=true and bot has admin rights in the group |
| Railway deployment crashes | See DEPLOYMENT.md troubleshooting section |
- Python 3.11 + python-telegram-bot 20.7
- Anthropic Claude for LLM responses (streaming)
- HubSpot CRM API v3 for contact management
- httpx for async HTTP
- Docker for containerized deployment
- Railway for cloud hosting