Add initial project structure for Telegram bot

- Created Docker configuration files for development and production.
- Added Dockerfile for building the bot image.
- Implemented configuration loading from environment variables.
- Developed main application logic and command handlers.
- Included README with setup instructions and usage details.
- Added .gitignore and .dockerignore files to exclude unnecessary files.
- Provided example environment file (.env.example) for bot token configuration.
- Established basic error handling for the bot.
This commit is contained in:
2026-02-17 12:16:47 +03:00
commit d90d3d1177
13 changed files with 201 additions and 0 deletions

9
.dockerignore Normal file
View File

@@ -0,0 +1,9 @@
venv/
.env
.git/
__pycache__/
*.pyc
*.pyo
*.md
.cursor/
*.plan.md

1
.env.example Normal file
View File

@@ -0,0 +1 @@
BOT_TOKEN=your_bot_token_here

5
.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
.env
__pycache__/
venv/
*.pyc
*.pyo

18
Dockerfile Normal file
View File

@@ -0,0 +1,18 @@
# Single image for both dev and prod; Compose files differentiate behavior.
FROM python:3.12-slim
WORKDIR /app
# Install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Application code
COPY config.py main.py ./
COPY handlers/ ./handlers/
# Run as non-root
RUN adduser --disabled-password --gecos "" botuser && chown -R botuser:botuser /app
USER botuser
CMD ["python", "main.py"]

67
README.md Normal file
View File

@@ -0,0 +1,67 @@
# Duty Teller (Telegram Bot)
A minimal Telegram bot boilerplate using [python-telegram-bot](https://github.com/python-telegram-bot/python-telegram-bot) v22 with the `Application` API.
## Get a bot token
1. Open Telegram and search for [@BotFather](https://t.me/BotFather).
2. Send `/newbot` and follow the prompts to create a bot.
3. Copy the token BotFather gives you.
## Setup
1. **Clone and enter the project**
```bash
cd duty-teller
```
2. **Create a virtual environment (recommended)**
```bash
python -m venv venv
source venv/bin/activate # Linux/macOS
# or: venv\Scripts\activate # Windows
```
3. **Install dependencies**
```bash
pip install -r requirements.txt
```
4. **Configure the bot**
```bash
cp .env.example .env
```
Edit `.env` and set `BOT_TOKEN` to the token from BotFather.
## Run
```bash
python main.py
```
The bot runs in polling mode. Send `/start` or `/help` to your bot in Telegram to test.
## Run with Docker
Ensure `.env` exists (e.g. `cp .env.example .env`) and contains `BOT_TOKEN`.
- **Dev** (volume mount; code changes apply without rebuild):
```bash
docker compose -f docker-compose.dev.yml up --build
```
Stop with `Ctrl+C` or `docker compose -f docker-compose.dev.yml down`.
- **Prod** (no volume; runs the built image; restarts on failure):
```bash
docker compose -f docker-compose.prod.yml up -d --build
```
For production deployments you may use Docker secrets or your orchestrators env instead of a `.env` file.
## Project layout
- `main.py` Builds the `Application`, registers handlers, runs polling.
- `config.py` Loads `BOT_TOKEN` from env; exits if missing.
- `handlers/` Command and error handlers; add new handlers here.
- `requirements.txt` Pinned dependencies (PTB with job-queue, python-dotenv).
To add commands, define async handlers in `handlers/commands.py` (or a new module) and register them in `handlers/__init__.py`.

10
config.py Normal file
View File

@@ -0,0 +1,10 @@
"""Load configuration from environment. Fail fast if BOT_TOKEN is missing."""
import os
from dotenv import load_dotenv
load_dotenv()
BOT_TOKEN = os.getenv("BOT_TOKEN")
if not BOT_TOKEN:
raise SystemExit("BOT_TOKEN is not set. Copy .env.example to .env and set your token from @BotFather.")

9
docker-compose.dev.yml Normal file
View File

@@ -0,0 +1,9 @@
services:
bot:
build:
context: .
dockerfile: Dockerfile
env_file: .env
volumes:
- .:/app
restart: "no"

12
docker-compose.prod.yml Normal file
View File

@@ -0,0 +1,12 @@
services:
bot:
build:
context: .
dockerfile: Dockerfile
env_file: .env
restart: always
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"

10
handlers/__init__.py Normal file
View File

@@ -0,0 +1,10 @@
"""Expose a single register_handlers(app) that registers all handlers."""
from telegram.ext import Application
from . import commands, errors
def register_handlers(app: Application) -> None:
app.add_handler(commands.start_handler)
app.add_handler(commands.help_handler)
app.add_error_handler(errors.error_handler)

21
handlers/commands.py Normal file
View File

@@ -0,0 +1,21 @@
"""Command handlers: /start, /help (and placeholder for more)."""
from telegram import Update
from telegram.ext import CommandHandler, ContextTypes
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
if update.message:
await update.message.reply_text("Hello! I'm your bot. Use /help to see available commands.")
async def help_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
if update.message:
await update.message.reply_text(
"Available commands:\n"
"/start - Start the bot\n"
"/help - Show this help"
)
start_handler = CommandHandler("start", start)
help_handler = CommandHandler("help", help_cmd)

13
handlers/errors.py Normal file
View File

@@ -0,0 +1,13 @@
"""Global error handler: log exception and notify user."""
import logging
from telegram import Update
from telegram.ext import ContextTypes
logger = logging.getLogger(__name__)
async def error_handler(update: object, context: ContextTypes.DEFAULT_TYPE) -> None:
logger.exception("Exception while handling an update")
if isinstance(update, Update) and update.effective_message:
await update.effective_message.reply_text("Something went wrong. Please try again later.")

24
main.py Normal file
View File

@@ -0,0 +1,24 @@
"""Single entry point: build Application, register handlers, run polling."""
import logging
import config
from telegram.ext import ApplicationBuilder
from handlers import register_handlers
logging.basicConfig(
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
level=logging.INFO,
)
logger = logging.getLogger(__name__)
def main() -> None:
app = ApplicationBuilder().token(config.BOT_TOKEN).build()
register_handlers(app)
logger.info("Bot starting (polling)...")
app.run_polling(allowed_updates=["message"])
if __name__ == "__main__":
main()

2
requirements.txt Normal file
View File

@@ -0,0 +1,2 @@
python-telegram-bot[job-queue]>=22.0,<23.0
python-dotenv>=1.0,<2.0