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