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:
9
.dockerignore
Normal file
9
.dockerignore
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
venv/
|
||||||
|
.env
|
||||||
|
.git/
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
*.pyo
|
||||||
|
*.md
|
||||||
|
.cursor/
|
||||||
|
*.plan.md
|
||||||
1
.env.example
Normal file
1
.env.example
Normal file
@@ -0,0 +1 @@
|
|||||||
|
BOT_TOKEN=your_bot_token_here
|
||||||
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
.env
|
||||||
|
__pycache__/
|
||||||
|
venv/
|
||||||
|
*.pyc
|
||||||
|
*.pyo
|
||||||
18
Dockerfile
Normal file
18
Dockerfile
Normal 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
67
README.md
Normal 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 orchestrator’s 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
10
config.py
Normal 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
9
docker-compose.dev.yml
Normal 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
12
docker-compose.prod.yml
Normal 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
10
handlers/__init__.py
Normal 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
21
handlers/commands.py
Normal 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
13
handlers/errors.py
Normal 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
24
main.py
Normal 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
2
requirements.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
python-telegram-bot[job-queue]>=22.0,<23.0
|
||||||
|
python-dotenv>=1.0,<2.0
|
||||||
Reference in New Issue
Block a user