SkyAlone / bot.py
FreshPixels's picture
Update bot.py
0c68972 verified
Raw
History Blame
9.64 kB
import asyncio
import logging
import os
import ssl
import sys
import aiohttp
import certifi
from aiohttp import web
from aiogram import Bot, Dispatcher
from aiogram.enums import ParseMode
from aiogram.client.default import DefaultBotProperties
from aiogram.webhook.aiohttp_server import SimpleRequestHandler, setup_application
from config import config
from database import db
from handlers import commands_router, chat_router
from middlewares import OwnerMiddleware
# ═══════════════════════════════════════════════════════════════════
# BLOCK 0: SSL-ΠΏΠ°Ρ‚Ρ‡ΠΈ для HF Spaces (certifi + proxy)
# ═══════════════════════════════════════════════════════════════════
# HF Spaces Π½Π΅ ΠΈΠΌΠ΅Π΅Ρ‚ систСмных CA-сСртификатов.
# ΠŸΠ°Ρ‚Ρ‡ΠΈ ΠΏΡ€ΠΈΠΌΠ΅Π½ΡΡŽΡ‚ΡΡ ΠΊΠ°ΠΊ side-effect ΠΏΡ€ΠΈ ΠΈΠΌΠΏΠΎΡ€Ρ‚Π΅.
# ═══════════════════════════════════════════════════════════════════
custom_ssl = ssl.create_default_context(cafile=certifi.where())
custom_ssl.check_hostname = True
custom_ssl.verify_mode = ssl.CERT_REQUIRED
_orig_tcp_init = aiohttp.TCPConnector.__init__
def _patched_tcp_init(self, *args, **kwargs):
if kwargs.get("ssl") is not False:
kwargs["ssl"] = custom_ssl
_orig_tcp_init(self, *args, **kwargs)
aiohttp.TCPConnector.__init__ = _patched_tcp_init
_orig_session_init = aiohttp.ClientSession.__init__
def _patched_session_init(self, *args, **kwargs):
kwargs["trust_env"] = True
_orig_session_init(self, *args, **kwargs)
aiohttp.ClientSession.__init__ = _patched_session_init
_orig_request = aiohttp.ClientSession._request
async def _patched_request(self, method, url, *args, **kwargs):
proxy_server = os.getenv("TELEGRAM_API_SERVER")
if proxy_server and "api.telegram.org" in str(url):
proxy_server = proxy_server.strip().rstrip("/")
str_url = str(url).replace("https://api.telegram.org", proxy_server)
logging.info("πŸ”€ ΠŸΠ΅Ρ€Π΅Π°Π΄Ρ€Π΅ΡΠ°Ρ†ΠΈΡ aiogram Ρ‡Π΅Ρ€Π΅Π· прокси ➑️ %s", str_url)
url = str_url
return await _orig_request(self, method, url, *args, **kwargs)
aiohttp.ClientSession._request = _patched_request
# ═══════════════════════════════════════════════════════════════════
# BLOCK 1: Π›ΠΎΠ³ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅
# ═══════════════════════════════════════════════════════════════════
def setup_logging() -> None:
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
stream=sys.stdout,
)
# ═══════════════════════════════════════════════════════════════════
# BLOCK 2: Π˜Π½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΡ Π±ΠΎΡ‚Π° ΠΈ диспСтчСра
# ═══════════════════════════════════════════════════════════════════
bot = Bot(
token=config.BOT_TOKEN,
default=DefaultBotProperties(parse_mode=ParseMode.HTML)
)
dp = Dispatcher()
dp.message.middleware(OwnerMiddleware())
@dp.update.middleware()
async def log_updates(handler, event, data):
logger = logging.getLogger(__name__)
logger.info("β†’ Update received: %s", event.update_id if hasattr(event, 'update_id') else 'N/A')
try:
return await handler(event, data)
except Exception as e:
logger.error("Update failed: %s", e, exc_info=True)
raise
dp.include_router(commands_router)
dp.include_router(chat_router)
# ═══════════════════════════════════════════════════════════════════
# BLOCK 3: HTTP Handlers
# ═══════════════════════════════════════════════════════════════════
@web.middleware
async def hf_logging_middleware(request, handler):
return await handler(request)
async def health_check(request: web.Request) -> web.Response:
"""HF Spaces Health Check β€” ΠΎΠ±ΡΠ·Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎ ΠΎΡ‚Π²Π΅Ρ‡Π°Ρ‚ΡŒ 200 Π½Π° '/'."""
return web.Response(text="πŸš€ GLM Bot Π ΠΠ‘ΠžΠ’ΠΠ•Π’!")
# ═══════════════════════════════════════════════════════════════════
# BLOCK 4: Lifecycle hooks
# ═══════════════════════════════════════════════════════════════════
async def on_startup(app: web.Application) -> None:
logger = logging.getLogger(__name__)
await db.connect()
logger.info("Database connected")
space_host = os.getenv("SPACE_HOST", "")
if space_host:
full_webhook_link = f"https://{space_host.strip()}{config.WEBHOOK_PATH}"
for attempt in range(5):
try:
await bot.set_webhook(
url=full_webhook_link,
drop_pending_updates=True,
request_timeout=30,
)
logger.info("βœ… Webhook установлСн: %s", full_webhook_link)
break
except Exception as e:
logger.warning(
"⚠️ ΠŸΠΎΠΏΡ‹Ρ‚ΠΊΠ° %d/5 установки webhook: %s", attempt + 1, e
)
await asyncio.sleep(5)
else:
logger.warning("⚠️ SPACE_HOST Π½Π΅ Π·Π°Π΄Π°Π½, webhook Π½Π΅ установлСн!")
async def on_shutdown(app: web.Application) -> None:
logger = logging.getLogger(__name__)
logger.info("πŸ›‘ Shutdown Π½Π°Ρ‡Π°Ρ‚...")
try:
await bot.delete_webhook(drop_pending_updates=True)
logger.info("Webhook ΡƒΠ΄Π°Π»Ρ‘Π½")
except Exception as e:
logger.warning("Ошибка ΠΏΡ€ΠΈ ΡƒΠ΄Π°Π»Π΅Π½ΠΈΠΈ webhook: %s", e)
try:
await dp.storage.close()
await bot.session.close()
logger.info("Bot session Π·Π°ΠΊΡ€Ρ‹Ρ‚")
except Exception as e:
logger.warning("Ошибка ΠΏΡ€ΠΈ Π·Π°ΠΊΡ€Ρ‹Ρ‚ΠΈΠΈ сСссии Π±ΠΎΡ‚Π°: %s", e)
try:
await db.disconnect()
logger.info("Database disconnected")
except Exception as e:
logger.warning("Ошибка ΠΏΡ€ΠΈ ΠΎΡ‚ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠΈ Π‘Π”: %s", e)
# ═══════════════════════════════════════════════════════════════════
# BLOCK 5: Main
# ═══════════════════════════════════════════════════════════════════
async def debug_on_startup(app: web.Application) -> None:
logger = logging.getLogger(__name__)
me = await bot.get_me()
logger.info(
"πŸ€– RUNNING BOT: @%s | ID=%s",
me.username,
me.id
)
# Π›ΠΎΠ³ΠΈΡ€ΡƒΠ΅ΠΌ всС env vars (Π±Π΅Π· сСкрСтов)
logger.info("SPACE_HOST=%s", os.getenv("SPACE_HOST", "NOT SET"))
logger.info("PORT=%s", os.getenv("PORT", "NOT SET"))
logger.info("WEBHOOK_PATH=%s", config.WEBHOOK_PATH)
await db.connect()
space_host = os.getenv("SPACE_HOST", "")
if not space_host:
logger.error("CRITICAL: SPACE_HOST is empty! Webhook cannot be set.")
return
full_webhook_link = f"https://{space_host.strip()}{config.WEBHOOK_PATH}"
logger.info("Setting webhook to: %s", full_webhook_link)
for attempt in range(5):
try:
result = await bot.set_webhook(
url=full_webhook_link,
drop_pending_updates=True,
request_timeout=30,
)
logger.info("Webhook set result: %s", result)
break
except Exception as e:
logger.error("Webhook attempt %d failed: %s", attempt + 1, e)
await asyncio.sleep(5)
def main() -> None:
setup_logging()
logger = logging.getLogger(__name__)
logger.info("Starting HF Spaces bot...")
app = web.Application(middlewares=[hf_logging_middleware])
app.router.add_get("/", health_check)
webhook_requests_handler = SimpleRequestHandler(dispatcher=dp, bot=bot)
webhook_requests_handler.register(app, path=config.WEBHOOK_PATH)
setup_application(app, dp, bot=bot)
app.on_startup.append(on_startup)
app.on_shutdown.append(on_shutdown)
# HF Spaces Ρ‚Ρ€Π΅Π±ΡƒΠ΅Ρ‚: host='0.0.0.0', port=7860
port = int(os.environ.get("PORT", 7860))
logger.info("πŸš€ Запуск сСрвСра Π½Π° %s:%d...", "0.0.0.0", port)
web.run_app(app, host="0.0.0.0", port=port)
if __name__ == "__main__":
main()