logging_bot / telegram_preview.py
AstraOS's picture
Update telegram_preview.py
64931bf verified
"""
Reusable Telegram‑bot preview router.
Exposes:
β€’ GET / – HTML preview card
β€’ GET /avatar.jpg – bot avatar (streamed, in‑memory)
To use: `from telegram_preview import router` and include it in your FastAPI app.
"""
import io
import os
import requests
from pathlib import Path
from typing import Optional
from fastapi import APIRouter, Response, Request
from fastapi.responses import HTMLResponse, StreamingResponse
from fastapi.templating import Jinja2Templates
from fastapi.staticfiles import StaticFiles # NEW
# ─── Locate templates folder absolute path ────────────────────────────────────
BASE_DIR = Path(__file__).resolve().parent
templates = Jinja2Templates(directory=str(BASE_DIR / "templates"))
def include_in_app(app):
"""
Helper so the main application can pull in both router *and* static mount.
"""
app.include_router(router)
app.mount(
"/static",
StaticFiles(directory=str(BASE_DIR / "static")),
name="static",
)
# ─── Environment ───────────────────────────────────────────────────────────────
TOKEN = os.getenv("BOT_TOKEN")
FALLBACK_IMG = "https://telegram.org/img/t_logo.png"
if not TOKEN:
raise RuntimeError("BOT_TOKEN environment variable not set")
API_ROOT = f"https://api.telegram.org/bot{TOKEN}"
router = APIRouter()
# ─── Helpers ───────────────────────────────────────────────────────────────────
def _get_description() -> str:
"""Try short β†’ full β†’ empty string."""
try:
r = requests.get(f"{API_ROOT}/getMyShortDescription", timeout=5).json()
if (desc := r["result"].get("short_description")):
return desc
except Exception:
pass
try:
r = requests.get(f"{API_ROOT}/getMyDescription", timeout=5).json()
return r["result"].get("description", "")
except Exception:
return ""
def _fetch_avatar_bytes() -> Optional[bytes]:
"""Return the newest avatar as bytes, or None if missing/error."""
try:
me = requests.get(f"{API_ROOT}/getMe", timeout=5).json()
user_id = me["result"]["id"]
photos = requests.get(
f"{API_ROOT}/getUserProfilePhotos",
params={"user_id": user_id, "limit": 1},
timeout=5,
).json()
if photos["result"]["total_count"] == 0:
return None
file_id = photos["result"]["photos"][0][-1]["file_id"]
file_obj = requests.get(
f"{API_ROOT}/getFile", params={"file_id": file_id}, timeout=5
).json()
file_path = file_obj["result"]["file_path"]
resp = requests.get(
f"https://api.telegram.org/file/bot{TOKEN}/{file_path}", timeout=10
)
if resp.status_code == 200:
return resp.content
except Exception as exc:
print("Avatar fetch error:", exc)
return None
def _get_bot_identity() -> dict:
"""Return {'first_name': ..., 'username': ...} from getMe"""
try:
r = requests.get(f"{API_ROOT}/getMe", timeout=5).json()
return {
"first_name": r["result"].get("first_name", "Telegram Bot"),
"username": r["result"].get("username", "unknown_bot"),
}
except Exception:
return {"first_name": "Telegram Bot", "username": "unknown_bot"}
# ─── Routes ────────────────────────────────────────────────────────────────────
@router.get("/", include_in_schema=False, response_class=HTMLResponse)
def bot_preview(request: Request):
description = _get_description()
identity = _get_bot_identity()
return templates.TemplateResponse(
"preview.html",
{
"request": request,
"title": f"@{identity['username']}",
"username": identity["username"],
"first_name": identity["first_name"],
"description": description,
},
)
@router.get("/avatar.jpg", include_in_schema=False)
def serve_avatar():
"""Streams avatar JPEG or Telegram logo (PNG) as fallback."""
data = _fetch_avatar_bytes()
if data:
return StreamingResponse(io.BytesIO(data), media_type="image/jpeg")
fallback = requests.get(FALLBACK_IMG, timeout=10)
return Response(content=fallback.content, media_type="image/png")