| """ |
| Hugging Face Spaces uchun Webhook rejimidagi Telegram Bot |
| aiodns + custom DNS resolver (8.8.8.8) ishlatiladi |
| """ |
|
|
| import logging |
| import sys |
| import os |
|
|
| from fastapi import FastAPI, Request |
| from fastapi.responses import Response, RedirectResponse, HTMLResponse |
| from aiogram import Bot, Dispatcher |
| from aiogram.fsm.storage.memory import MemoryStorage |
| from aiogram.client.default import DefaultBotProperties |
| from aiogram.client.session.aiohttp import AiohttpSession |
| from aiogram.enums import ParseMode |
| from aiogram.types import Update |
|
|
| import aiohttp |
| from aiohttp.resolver import AsyncResolver |
|
|
| from config import BOT_TOKEN |
| from services.db import init_db |
|
|
| |
| from handlers import start, generate, read_qr, settings, batch, history, inline, admin, dynamic |
|
|
| |
| logging.basicConfig( |
| level=logging.INFO, |
| format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", |
| stream=sys.stdout, |
| ) |
| logger = logging.getLogger("app") |
|
|
| |
| session = AiohttpSession() |
| resolver = AsyncResolver(nameservers=["8.8.8.8", "8.8.4.4"]) |
| |
| session._connector_init["resolver"] = resolver |
|
|
| bot = Bot( |
| token=BOT_TOKEN, |
| default=DefaultBotProperties(parse_mode=ParseMode.HTML), |
| session=session, |
| ) |
| dp = Dispatcher(storage=MemoryStorage()) |
|
|
| dp.include_router(admin.router) |
| dp.include_router(start.router) |
| dp.include_router(generate.router) |
| dp.include_router(settings.router) |
| dp.include_router(batch.router) |
| dp.include_router(history.router) |
| dp.include_router(inline.router) |
| dp.include_router(read_qr.router) |
| dp.include_router(dynamic.router) |
|
|
| |
| app = FastAPI(title="QR Bot Webhook Service") |
|
|
| SPACE_HOST = os.environ.get("SPACE_HOST", "ibrohm-qrkodbot.hf.space") |
| WEBHOOK_PATH = f"/webhook/{BOT_TOKEN}" |
| WEBHOOK_URL = f"https://{SPACE_HOST}{WEBHOOK_PATH}" |
|
|
|
|
| @app.on_event("startup") |
| async def on_startup(): |
| await init_db() |
| logger.info("[OK] Database tayyor") |
|
|
| try: |
| await bot.set_webhook( |
| url=WEBHOOK_URL, |
| drop_pending_updates=True, |
| allowed_updates=["message", "callback_query", "inline_query"] |
| ) |
| logger.info(f"[OK] Webhook o'rnatildi: {WEBHOOK_URL}") |
| except Exception as e: |
| logger.warning(f"[!] Webhook xatolik: {e}") |
| logger.info("[INFO] Webhook kompyuterdan o'rnating: python set_webhook.py") |
|
|
|
|
| @app.on_event("shutdown") |
| async def on_shutdown(): |
| try: |
| await bot.session.close() |
| except Exception: |
| pass |
|
|
|
|
| @app.post(WEBHOOK_PATH) |
| async def webhook_handler(request: Request): |
| try: |
| data = await request.json() |
| update = Update.model_validate(data, context={"bot": bot}) |
| await dp.feed_update(bot=bot, update=update) |
| except Exception as e: |
| logger.error(f"Webhook xatosi: {e}") |
| return {"ok": True} |
|
|
|
|
| @app.get("/") |
| async def root(): |
| return {"status": "running", "mode": "webhook"} |
|
|
|
|
| @app.get("/r/{code}") |
| async def redirect_dynamic_qr(code: str, request: Request): |
| """Dinamik QR kod skanerlaganda — skanerni yozib, maqsad URL ga yo'naltirish""" |
| from services.db import get_link_by_code, log_scan |
| link = await get_link_by_code(code) |
| if not link: |
| return HTMLResponse( |
| "<h2>QR kod topilmadi yoki muddati tugagan</h2><p>Bu havola mavjud emas.</p>", |
| status_code=404 |
| ) |
| |
| ip = request.client.host if request.client else "" |
| ua = request.headers.get("user-agent", "") |
| await log_scan(code, ip=ip, user_agent=ua) |
| |
| return RedirectResponse(url=link["target_url"], status_code=302) |
|
|
|
|
| @app.get("/api/qr") |
| async def api_qr(data: str = ""): |
| """Inline rejim uchun QR kod rasmini qaytaruvchi endpoint""" |
| if not data: |
| return Response(content="No data", status_code=400) |
| from services.qr_generator import QRGenerator |
| generator = QRGenerator() |
| img_buf = await generator.generate_async(data) |
| return Response( |
| content=img_buf.read(), |
| media_type="image/png", |
| headers={"Cache-Control": "public, max-age=86400"} |
| ) |
|
|
|
|
| if __name__ == "__main__": |
| import uvicorn |
| uvicorn.run("app:app", host="0.0.0.0", port=7860) |
|
|