feat: Integrate Telegram bot with polling for owner commands
Co-authored-by: aider (gemini/gemini-2.5-pro) <aider@aider.chat>
This commit is contained in:
63
app/app.py
63
app/app.py
@@ -23,6 +23,7 @@ from datetime import datetime
|
|||||||
from typing import Dict, List, Optional
|
from typing import Dict, List, Optional
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import httpx
|
||||||
from fastapi import FastAPI, UploadFile, WebSocket, WebSocketDisconnect, Request, Form
|
from fastapi import FastAPI, UploadFile, WebSocket, WebSocketDisconnect, Request, Form
|
||||||
from fastapi.responses import HTMLResponse, JSONResponse, FileResponse, RedirectResponse, Response
|
from fastapi.responses import HTMLResponse, JSONResponse, FileResponse, RedirectResponse, Response
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
@@ -170,6 +171,68 @@ class SessionHub:
|
|||||||
|
|
||||||
hub = SessionHub()
|
hub = SessionHub()
|
||||||
|
|
||||||
|
# ---------- Telegram Bot ----------
|
||||||
|
|
||||||
|
TELEGRAM_API_URL = f"https://api.telegram.org/bot{SETTINGS.telegram_bot_api_key}"
|
||||||
|
TELEGRAM_OWNER_ID = SETTINGS.telegram_bot_owner_id
|
||||||
|
|
||||||
|
async def send_telegram_message(chat_id: str, text: str):
|
||||||
|
"""Send a message via Telegram bot."""
|
||||||
|
if not SETTINGS.telegram_bot_api_key:
|
||||||
|
return
|
||||||
|
async with httpx.AsyncClient() as client:
|
||||||
|
try:
|
||||||
|
await client.post(f"{TELEGRAM_API_URL}/sendMessage", json={"chat_id": chat_id, "text": text})
|
||||||
|
logger.info("Sent Telegram message to %s", chat_id)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error("Failed to send Telegram message: %s", e)
|
||||||
|
|
||||||
|
async def handle_telegram_update(update: dict):
|
||||||
|
"""Process a single Telegram update."""
|
||||||
|
if "message" not in update:
|
||||||
|
return
|
||||||
|
message = update["message"]
|
||||||
|
chat_id = message.get("chat", {}).get("id")
|
||||||
|
from_id = message.get("from", {}).get("id")
|
||||||
|
text = message.get("text", "")
|
||||||
|
|
||||||
|
if str(from_id) != TELEGRAM_OWNER_ID:
|
||||||
|
logger.warning("Ignoring Telegram message from non-owner: %s", from_id)
|
||||||
|
return
|
||||||
|
|
||||||
|
if text == "/start":
|
||||||
|
await send_telegram_message(str(chat_id), "File Drop Bot active.")
|
||||||
|
|
||||||
|
async def poll_telegram_updates():
|
||||||
|
"""Poll for Telegram updates and process them."""
|
||||||
|
if not SETTINGS.telegram_bot_api_key or not TELEGRAM_OWNER_ID:
|
||||||
|
logger.info("Telegram bot not configured, skipping polling.")
|
||||||
|
return
|
||||||
|
|
||||||
|
update_offset = 0
|
||||||
|
async with httpx.AsyncClient(timeout=35) as client:
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
response = await client.get(
|
||||||
|
f"{TELEGRAM_API_URL}/getUpdates",
|
||||||
|
params={"offset": update_offset, "timeout": 30}
|
||||||
|
)
|
||||||
|
updates = response.json().get("result", [])
|
||||||
|
for update in updates:
|
||||||
|
await handle_telegram_update(update)
|
||||||
|
update_offset = update["update_id"] + 1
|
||||||
|
except Exception as e:
|
||||||
|
logger.error("Error polling Telegram updates: %s", e)
|
||||||
|
await asyncio.sleep(10) # wait before retrying on error
|
||||||
|
|
||||||
|
@app.on_event("startup")
|
||||||
|
async def startup_event():
|
||||||
|
"""On app startup, send boot message and start polling."""
|
||||||
|
if SETTINGS.telegram_bot_api_key and TELEGRAM_OWNER_ID:
|
||||||
|
await send_telegram_message(TELEGRAM_OWNER_ID, "File Drop Bot booted up.")
|
||||||
|
asyncio.create_task(poll_telegram_updates())
|
||||||
|
|
||||||
|
|
||||||
# ---------- Helpers ----------
|
# ---------- Helpers ----------
|
||||||
|
|
||||||
def sha1_hex(file_bytes: bytes) -> str:
|
def sha1_hex(file_bytes: bytes) -> str:
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ class Settings:
|
|||||||
chunked_uploads_enabled: bool = False
|
chunked_uploads_enabled: bool = False
|
||||||
chunk_size_mb: int = 95
|
chunk_size_mb: int = 95
|
||||||
timezone: str = "UTC"
|
timezone: str = "UTC"
|
||||||
|
telegram_bot_api_key: str = ""
|
||||||
|
telegram_bot_owner_id: str = ""
|
||||||
|
|
||||||
def _hash_password(pw: str) -> str:
|
def _hash_password(pw: str) -> str:
|
||||||
"""Return PBKDF2-SHA256 hash of a password."""
|
"""Return PBKDF2-SHA256 hash of a password."""
|
||||||
@@ -74,6 +76,8 @@ def load_settings() -> Settings:
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
chunk_size_mb = 95
|
chunk_size_mb = 95
|
||||||
timezone = os.getenv("TIMEZONE", "UTC")
|
timezone = os.getenv("TIMEZONE", "UTC")
|
||||||
|
telegram_bot_api_key = os.getenv("TELEGRAM_BOT_API_KEY", "")
|
||||||
|
telegram_bot_owner_id = os.getenv("TELEGRAM_BOT_OWNER_ID", "")
|
||||||
return Settings(
|
return Settings(
|
||||||
admin_password=admin_password,
|
admin_password=admin_password,
|
||||||
max_concurrent=maxc,
|
max_concurrent=maxc,
|
||||||
@@ -85,4 +89,6 @@ def load_settings() -> Settings:
|
|||||||
chunked_uploads_enabled=chunked_uploads_enabled,
|
chunked_uploads_enabled=chunked_uploads_enabled,
|
||||||
chunk_size_mb=chunk_size_mb,
|
chunk_size_mb=chunk_size_mb,
|
||||||
timezone=timezone,
|
timezone=timezone,
|
||||||
|
telegram_bot_api_key=telegram_bot_api_key,
|
||||||
|
telegram_bot_owner_id=telegram_bot_owner_id,
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user