A Telegram bot that turns complaints into action by recommending NGOs and projects.
- User sends a complaint/rant to the bot
- LLM extracts the underlying problems and solution concepts
- Vector similarity search finds matching organizations and projects
- Bot replies with a styled, structured message (validation → guidance → encouragement)
| Pipeline | Trigger | What it does |
|---|---|---|
process_message |
Any complaint/rant | Extracts problems → finds orgs → generates styled reply |
show_orgs |
/orgs or asking for orgs |
Direct org search by category |
change_style |
/style or style-related message |
Updates user's tone preference |
about_me |
/about |
Bot description |
start |
/start |
Welcome message |
polite · funny · sarcastic · normal · rude
Style priority: user preference > chat preference > default (normal)
- Python 3.11+
- PostgreSQL with pgvector
- Telegram Bot Token (from @BotFather)
- OpenAI API Key
cp .env.example .env
# Fill in TELEGRAM_BOT_TOKEN and OPENAI_API_KEY in .env
docker-compose up -d db
sleep 5
docker-compose run bot python init_db.py
docker-compose up bot# 1. Install dependencies
pip install -r requirements.txt
# 2. Set up environment
cp .env.example .env
# Edit .env with your credentials
# 3. Initialize database (creates tables, seeds data, generates embeddings)
python init_db.py
# 4. Run the bot
python bot/main.pyhate2action/
├── bot/
│ └── main.py # Telegram bot handlers
├── db/
│ ├── queries.py # All database operations
│ ├── schema.sql # Table definitions
│ └── seed.sql # Initial organizations, projects, problems, solutions
├── pipelines/
│ ├── message_orchestrator.py # Intent routing + start/about pipelines
│ ├── problem_solution.py # Main complaint-to-action pipeline
│ ├── show_organizations.py # Organization search pipeline
│ └── change_style.py # Style configuration pipeline
├── utils/
│ └── llm.py # OpenAI API helpers (embeddings, LLM calls)
├── tests/
│ └── test_pipelines.py # Unit tests (no DB/API required)
├── init_db.py # One-time DB setup + embedding generation
├── docker-compose.yml
├── Dockerfile
└── requirements.txt
users → user preferences (style)
chats → chat preferences
messages_history → full conversation log
organizations → NGOs (manually verified)
projects → specific NGO projects (manually verified)
problems → extracted problem concepts (AI generated)
solutions → solution concepts (AI generated)
*_vec → embedding tables for each entity
problems_solutions → problem ↔ solution similarity
projects_solutions → project ↔ solution similarity
organizations_solutions → org ↔ solution similarity
python -m pytest tests/ -v
# or
python tests/test_pipelines.pyTests mock all DB and LLM calls — no credentials needed.
Manually insert into the organizations and projects tables, then re-run embedding:
from utils.llm import get_embedding
from db.queries import db_cursor
with db_cursor() as cur:
cur.execute("INSERT INTO organizations (name, description, website) VALUES (%s, %s, %s) RETURNING organization_id",
("New Org", "Description", "https://example.org"))
org_id = cur.fetchone()["organization_id"]
emb = get_embedding("New Org: Description")
emb_str = "[" + ",".join(str(v) for v in emb) + "]"
cur.execute("INSERT INTO organizations_vec (organization_id, text_to_embed, embedding) VALUES (%s, %s, %s::vector)",
(org_id, "New Org: Description", emb_str))- Database schema with all tables
- Seed data (organizations, projects, problems, solutions)
- Embedding generation + storage
- Similarity computation (problems↔solutions↔orgs/projects)
-
process_messagepipeline with deduplication -
show_orgspipeline -
change_stylepipeline -
startandabout_mepipelines - Conversation memory (messages_history)
- Style priority resolution (user > chat > default)
- Group chat support (mention-triggered)
- Inline keyboard buttons in DM
- Unit tests for all evaluation factors