Spaces:
Running
Running
Upload 2 files
Browse files- bot.py +53 -5
- requirements.txt +1 -0
bot.py
CHANGED
|
@@ -7,6 +7,7 @@ Corre como una tarea en segundo plano dentro del mismo servidor FastAPI
|
|
| 7 |
"""
|
| 8 |
import os
|
| 9 |
import io
|
|
|
|
| 10 |
from PIL import Image
|
| 11 |
import json
|
| 12 |
import uuid
|
|
@@ -16,6 +17,7 @@ from datetime import date, datetime, timezone
|
|
| 16 |
import httpx
|
| 17 |
import firebase_admin
|
| 18 |
from firebase_admin import credentials, firestore
|
|
|
|
| 19 |
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup, LabeledPrice
|
| 20 |
from telegram.request import HTTPXRequest
|
| 21 |
from telegram.ext import (
|
|
@@ -24,11 +26,12 @@ from telegram.ext import (
|
|
| 24 |
)
|
| 25 |
|
| 26 |
BOT_TOKEN = os.environ.get("TELEGRAM_BOT_TOKEN", "")
|
|
|
|
| 27 |
|
| 28 |
REMOVE_BG_URL = "https://zgrafic-remove-bg-api.hf.space/remove-bg"
|
| 29 |
ENHANCE_URL = "https://zgrafic-remove-bg-api.hf.space/enhance"
|
| 30 |
VECTORIZE_URL = "https://zgrafic-remove-bg-api.hf.space/vectorize"
|
| 31 |
-
|
| 32 |
|
| 33 |
FREE_USES_PER_DAY = 10
|
| 34 |
STARS_PER_IMAGE = 10
|
|
@@ -330,6 +333,45 @@ async def on_successful_payment(update: Update, context: ContextTypes.DEFAULT_TY
|
|
| 330 |
await process_and_send(context, job["chat_id"], job["file_id"], job["mode"])
|
| 331 |
|
| 332 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 333 |
async def post_with_wakeup(context: ContextTypes.DEFAULT_TYPE, chat_id: int, url: str, files: dict, data: dict, mode: str):
|
| 334 |
"""Manda el pedido al servidor, reintentando con paciencia si está
|
| 335 |
'despertando' (Hugging Face devuelve 429 mientras el contenedor
|
|
@@ -407,18 +449,23 @@ async def process_and_send(context: ContextTypes.DEFAULT_TYPE, chat_id: int, fil
|
|
| 407 |
resp = await post_with_wakeup(context, chat_id, VECTORIZE_URL, files, {}, "vectorize")
|
| 408 |
if resp is None:
|
| 409 |
return False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 410 |
|
| 411 |
else:
|
| 412 |
if mode == "remove":
|
| 413 |
url = REMOVE_BG_URL
|
| 414 |
-
elif mode == "vectorize_ai":
|
| 415 |
-
url = VECTORIZE_AI_URL
|
| 416 |
else:
|
| 417 |
url = ENHANCE_URL
|
| 418 |
|
| 419 |
files = {"file": ("imagen.jpg", image_bytes, "image/jpeg")}
|
| 420 |
|
| 421 |
-
if mode
|
| 422 |
data = {}
|
| 423 |
elif mode == "enhance_imagen":
|
| 424 |
data = {"type": "imagen", "scale": "4"}
|
|
@@ -428,11 +475,12 @@ async def process_and_send(context: ContextTypes.DEFAULT_TYPE, chat_id: int, fil
|
|
| 428 |
resp = await post_with_wakeup(context, chat_id, url, files, data, mode)
|
| 429 |
if resp is None:
|
| 430 |
return False
|
|
|
|
| 431 |
|
| 432 |
extension = "svg" if mode in ("vectorize", "vectorize_ai") else "png"
|
| 433 |
await context.bot.send_document(
|
| 434 |
chat_id,
|
| 435 |
-
document=io.BytesIO(
|
| 436 |
filename=f"resultado.{extension}",
|
| 437 |
)
|
| 438 |
return True
|
|
|
|
| 7 |
"""
|
| 8 |
import os
|
| 9 |
import io
|
| 10 |
+
import tempfile
|
| 11 |
from PIL import Image
|
| 12 |
import json
|
| 13 |
import uuid
|
|
|
|
| 17 |
import httpx
|
| 18 |
import firebase_admin
|
| 19 |
from firebase_admin import credentials, firestore
|
| 20 |
+
from gradio_client import Client, handle_file
|
| 21 |
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup, LabeledPrice
|
| 22 |
from telegram.request import HTTPXRequest
|
| 23 |
from telegram.ext import (
|
|
|
|
| 26 |
)
|
| 27 |
|
| 28 |
BOT_TOKEN = os.environ.get("TELEGRAM_BOT_TOKEN", "")
|
| 29 |
+
HF_TOKEN = os.environ.get("HF_TOKEN", "")
|
| 30 |
|
| 31 |
REMOVE_BG_URL = "https://zgrafic-remove-bg-api.hf.space/remove-bg"
|
| 32 |
ENHANCE_URL = "https://zgrafic-remove-bg-api.hf.space/enhance"
|
| 33 |
VECTORIZE_URL = "https://zgrafic-remove-bg-api.hf.space/vectorize"
|
| 34 |
+
ZEROGPU_SPACE = "zgrafic/ia" # Vectorizar con IA (HiVG-3B-Base + ZeroGPU)
|
| 35 |
|
| 36 |
FREE_USES_PER_DAY = 10
|
| 37 |
STARS_PER_IMAGE = 10
|
|
|
|
| 333 |
await process_and_send(context, job["chat_id"], job["file_id"], job["mode"])
|
| 334 |
|
| 335 |
|
| 336 |
+
_gradio_client = None
|
| 337 |
+
|
| 338 |
+
|
| 339 |
+
def _get_gradio_client():
|
| 340 |
+
global _gradio_client
|
| 341 |
+
if _gradio_client is None:
|
| 342 |
+
_gradio_client = Client(ZEROGPU_SPACE, hf_token=HF_TOKEN or None)
|
| 343 |
+
return _gradio_client
|
| 344 |
+
|
| 345 |
+
|
| 346 |
+
def _call_vectorize_ai_sync(image_bytes: bytes) -> str:
|
| 347 |
+
"""Llama al Space de ZeroGPU (Gradio) — es una librería con llamadas
|
| 348 |
+
que bloquean, por eso se corre en un hilo aparte desde afuera."""
|
| 349 |
+
with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp:
|
| 350 |
+
tmp.write(image_bytes)
|
| 351 |
+
tmp_path = tmp.name
|
| 352 |
+
|
| 353 |
+
client = _get_gradio_client()
|
| 354 |
+
result_path = client.predict(handle_file(tmp_path), api_name="/predict")
|
| 355 |
+
|
| 356 |
+
with open(result_path, "r") as f:
|
| 357 |
+
return f.read()
|
| 358 |
+
|
| 359 |
+
|
| 360 |
+
async def call_vectorize_ai(context: ContextTypes.DEFAULT_TYPE, chat_id: int, image_bytes: bytes):
|
| 361 |
+
"""Vectoriza con HiVG-3B-Base, corriendo en el Space de ZeroGPU
|
| 362 |
+
(GPU real, no el servidor de CPU) — devuelve el SVG como texto, o
|
| 363 |
+
None si falló (ya le avisó el motivo a la persona en el chat)."""
|
| 364 |
+
try:
|
| 365 |
+
svg_text = await asyncio.to_thread(_call_vectorize_ai_sync, image_bytes)
|
| 366 |
+
return svg_text
|
| 367 |
+
except Exception as e:
|
| 368 |
+
await context.bot.send_message(
|
| 369 |
+
chat_id,
|
| 370 |
+
f"No se pudo vectorizar con IA ({type(e).__name__}): {e or 'sin detalle'}"
|
| 371 |
+
)
|
| 372 |
+
return None
|
| 373 |
+
|
| 374 |
+
|
| 375 |
async def post_with_wakeup(context: ContextTypes.DEFAULT_TYPE, chat_id: int, url: str, files: dict, data: dict, mode: str):
|
| 376 |
"""Manda el pedido al servidor, reintentando con paciencia si está
|
| 377 |
'despertando' (Hugging Face devuelve 429 mientras el contenedor
|
|
|
|
| 449 |
resp = await post_with_wakeup(context, chat_id, VECTORIZE_URL, files, {}, "vectorize")
|
| 450 |
if resp is None:
|
| 451 |
return False
|
| 452 |
+
result_bytes = resp.content
|
| 453 |
+
|
| 454 |
+
elif mode == "vectorize_ai":
|
| 455 |
+
svg_text = await call_vectorize_ai(context, chat_id, image_bytes)
|
| 456 |
+
if svg_text is None:
|
| 457 |
+
return False
|
| 458 |
+
result_bytes = svg_text.encode("utf-8")
|
| 459 |
|
| 460 |
else:
|
| 461 |
if mode == "remove":
|
| 462 |
url = REMOVE_BG_URL
|
|
|
|
|
|
|
| 463 |
else:
|
| 464 |
url = ENHANCE_URL
|
| 465 |
|
| 466 |
files = {"file": ("imagen.jpg", image_bytes, "image/jpeg")}
|
| 467 |
|
| 468 |
+
if mode == "remove":
|
| 469 |
data = {}
|
| 470 |
elif mode == "enhance_imagen":
|
| 471 |
data = {"type": "imagen", "scale": "4"}
|
|
|
|
| 475 |
resp = await post_with_wakeup(context, chat_id, url, files, data, mode)
|
| 476 |
if resp is None:
|
| 477 |
return False
|
| 478 |
+
result_bytes = resp.content
|
| 479 |
|
| 480 |
extension = "svg" if mode in ("vectorize", "vectorize_ai") else "png"
|
| 481 |
await context.bot.send_document(
|
| 482 |
chat_id,
|
| 483 |
+
document=io.BytesIO(result_bytes),
|
| 484 |
filename=f"resultado.{extension}",
|
| 485 |
)
|
| 486 |
return True
|
requirements.txt
CHANGED
|
@@ -18,3 +18,4 @@ realesrgan==0.3.0
|
|
| 18 |
gfpgan==1.3.8
|
| 19 |
firebase-admin==6.5.0
|
| 20 |
vtracer==0.6.15
|
|
|
|
|
|
| 18 |
gfpgan==1.3.8
|
| 19 |
firebase-admin==6.5.0
|
| 20 |
vtracer==0.6.15
|
| 21 |
+
gradio_client
|