commit 9138512e2f4c1f729d48b4fc2d9fa7bb6d9adb6c Author: Noloquideus Date: Fri Apr 24 20:38:31 2026 +0300 init diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..2fb8793 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,5 @@ +.git +.venv +__pycache__ +*.pyc +.env diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..50d561f --- /dev/null +++ b/.env.example @@ -0,0 +1,3 @@ +TELEGRAM_BOT_TOKEN= +TELEGRAM_CHAT_ID= +TELEGRAM_MESSAGE_THREAD_ID= diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e049cf3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +.venv/ +__pycache__/ +*.py[cod] +*$py.class +.Python +*.egg-info/ +.eggs/ +dist/ +build/ +.env +.idea/ +.vscode/ +*.swp +*.swo diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..031010b --- /dev/null +++ b/Dockerfile @@ -0,0 +1,12 @@ +FROM python:3.12-slim-bookworm + +WORKDIR /app + +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +COPY main.py settings.py . + +EXPOSE 8000 + +CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..80ce210 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,8 @@ +services: + app: + build: . + ports: + - '8000:8000' + env_file: + - .env + restart: unless-stopped diff --git a/main.py b/main.py new file mode 100644 index 0000000..3f835e1 --- /dev/null +++ b/main.py @@ -0,0 +1,49 @@ +from contextlib import asynccontextmanager + +from fastapi import FastAPI, HTTPException +from httpx import AsyncClient +from pydantic import BaseModel, EmailStr + +from settings import settings + + +class NotifyBody(BaseModel): + name: str + email: EmailStr + + +@asynccontextmanager +async def lifespan(app: FastAPI): + app.state.http = AsyncClient(timeout=30.0) + yield + await app.state.http.aclose() + + + +app = FastAPI(lifespan=lifespan) + + +async def send_telegram_message(client: AsyncClient, text: str) -> None: + url = f'https://api.telegram.org/bot{settings.telegram_bot_token}/sendMessage' + payload = { + 'chat_id': settings.telegram_chat_id, + 'message_thread_id': settings.telegram_message_thread_id, + 'text': text, + } + resp = await client.post(url, json=payload) + data = resp.json() + if resp.status_code != 200 or not data.get('ok'): + detail = data.get('description', resp.text) + raise HTTPException(status_code=502, detail=detail) + + +@app.post('/notify') +async def notify(body: NotifyBody): + text = f'Новая заявка\nИмя: {body.name}\nПочта: {body.email}' + try: + await send_telegram_message(app.state.http, text) + except HTTPException: + raise + except Exception as exc: + raise HTTPException(status_code=502, detail=str(exc)) from exc + return {'ok': True} diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..ab00e9c --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +fastapi>=0.115.0 +uvicorn[standard]>=0.32.0 +pydantic-settings>=2.6.0 +httpx>=0.27.0 +email-validator>=2.2.0 diff --git a/settings.py b/settings.py new file mode 100644 index 0000000..d1d72b8 --- /dev/null +++ b/settings.py @@ -0,0 +1,12 @@ +from pydantic_settings import BaseSettings, SettingsConfigDict + + +class Settings(BaseSettings): + model_config = SettingsConfigDict(env_file='.env', env_file_encoding='utf-8') + + telegram_bot_token: str + telegram_chat_id: str + telegram_message_thread_id: int + + +settings = Settings()