Files
forust 0506aaaac8 chore(userbot): apply ruff check --fix and ruff format
- ruff check --fix: 210 auto-fixed errors (import sorting, trailing
  whitespace, unused imports, f-string fixups, deprecated annotations)
- ruff format: 104 files reformatted to consistent style
- 268 non-auto-fixable issues remain (S113 requests timeout, etc.)
2026-06-19 12:19:01 +02:00

191 lines
8.0 KiB
Python

import asyncio
import io
import logging
import os
import time
from concurrent.futures import ThreadPoolExecutor
import aiohttp
from PIL import Image
from pyrogram import Client, enums, filters
from pyrogram.types import Message
from utils.db import db
from utils.misc import modules_help, prefix
from utils.scripts import format_exc
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
async def query_huggingface(payload):
api_key = db.get('custom.hf', 'api_key', None)
model = db.get('custom.hf', 'current_model', None)
if not api_key:
raise ValueError(f'API key not set. Use {prefix}set_hf api <api_key> to set it.')
if not model:
raise ValueError(f'Model not set. Use {prefix}set_hf model <model_name> to set it.')
api_url = f'https://api-inference.huggingface.co/models/{model}'
headers = {'Authorization': f'Bearer {api_key}'}
timeout = aiohttp.ClientTimeout(total=120)
start_time = time.time()
retries = 3
for attempt in range(1, retries + 1):
try:
async with (
aiohttp.ClientSession(timeout=timeout) as session,
session.post(api_url, headers=headers, json=payload) as response,
):
fetch_time = int((time.time() - start_time) * 1000)
if response.status != 200:
error_text = await response.text()
logger.error(f'API Error {response.status}: {error_text}')
return None, fetch_time
return await response.read(), fetch_time
except TimeoutError:
logger.error(f'TimeoutError: Attempt {attempt}/{retries} timed out.')
if attempt == retries:
raise
except asyncio.CancelledError:
logger.error('Request was cancelled. Ensure the task is not being forcefully terminated.')
raise
except aiohttp.ClientError as e:
logger.error(f'Network Error: {e}')
if attempt == retries:
raise
finally:
await asyncio.sleep(2)
async def save_image(image_bytes, path):
loop = asyncio.get_event_loop()
with ThreadPoolExecutor() as pool:
await loop.run_in_executor(pool, lambda: Image.open(io.BytesIO(image_bytes)).save(path))
@Client.on_message(filters.command(['set_hf'], prefix) & filters.me)
async def manage_huggingface(_, message: Message):
"""Manage Hugging Face API key and models."""
subcommand = message.command[1].lower() if len(message.command) > 1 else None
arg = message.command[2] if len(message.command) > 2 else None
if subcommand == 'api':
if arg:
db.set('custom.hf', 'api_key', arg)
return await message.edit_text(f'Hugging Face API key set successfully.\nAPI Key: {arg}')
return await message.edit_text(f'Usage: {prefix}hf api <api_key>')
if subcommand == 'model':
if arg:
models = db.get('custom.hf', 'models', [])
if arg not in models:
models.append(arg)
db.set('custom.hf', 'models', models)
db.set('custom.hf', 'current_model', arg)
return await message.edit_text(f"Model '{arg}' added and set as the current model.")
return await message.edit_text(f'Usage: {prefix}hf model <model_name>')
if subcommand == 'select':
models = db.get('custom.hf', 'models', [])
if arg and arg.lower() == 'all':
db.set('custom.hf', 'current_model', 'all')
model_list = '\n'.join([f'*{i + 1}. {m}' for i, m in enumerate(models)])
return await message.edit_text(
f'All models selected:\n<code>{model_list}</code>\n\nImages will be generated from all models.'
)
if arg:
try:
index = int(arg) - 1
if 0 <= index < len(models):
db.set('custom.hf', 'current_model', models[index])
return await message.edit_text(f"Model set to '{models[index]}'.")
return await message.edit_text('Invalid model number.')
except ValueError:
return await message.edit_text('Invalid model number. Use a valid integer.')
return await message.edit_text(f'Usage: {prefix}hf select <model_number|all>')
if subcommand == 'delete' and arg:
try:
index = int(arg) - 1
models = db.get('custom.hf', 'models', [])
if 0 <= index < len(models):
removed_model = models.pop(index)
db.set('custom.hf', 'models', models)
if db.get('custom.hf', 'current_model') == removed_model:
db.set('custom.hf', 'current_model', models[0] if models else 'None')
return await message.edit_text(f"Model '{removed_model}' deleted.")
return await message.edit_text('Invalid model number.')
except ValueError:
return await message.edit_text('Invalid model number. Use a valid integer.')
api_key = db.get('custom.hf', 'api_key', None)
models = db.get('custom.hf', 'models', [])
current_model = db.get('custom.hf', 'current_model', 'Not set')
model_list = '\n'.join(
[f'{"*" if m == current_model or current_model == "all" else ""}{i + 1}. {m}' for i, m in enumerate(models)]
)
settings = (
f'<b>Hugging Face settings:</b>\n'
f'<b>API Key:</b>\n<code>{api_key if api_key else "Not set"}</code>\n\n'
f'<b>Available Models:</b>\n<code>{model_list}</code>'
)
usage_message = (
f'{settings}\n\n<b>Usage:</b>\n'
f'<code>{prefix}set_hf</code> <code>api</code>, <code>model</code>, <code>select</code>, <code>delete</code>, <code>select all</code>'
)
await message.edit_text(usage_message)
@Client.on_message(filters.command(['hf', 'hface', 'huggingface'], prefix))
async def imgflux_(_, message: Message):
prompt = message.text.split(' ', 1)[1] if len(message.command) > 1 else None
if not prompt:
usage_message = f'<b>Usage:</b> <code>{prefix}{message.command[0]} [custom prompt]</code>'
return await (message.edit_text if message.from_user.is_self else message.reply_text)(usage_message)
processing_message = await (message.edit_text if message.from_user.is_self else message.reply_text)('Processing...')
try:
current_model = db.get('custom.hf', 'current_model', None)
models = db.get('custom.hf', 'models', [])
models_to_use = models if current_model == 'all' else [current_model]
generated_images = []
for model in models_to_use:
db.set('custom.hf', 'current_model', model)
payload = {'inputs': prompt}
image_bytes, fetch_time = await query_huggingface(payload)
if not image_bytes:
logger.warning(f'Failed to fetch image for model: {model}')
continue
image_path = f'hf_flux_gen_{model.replace("/", "_")}.jpg'
await save_image(image_bytes, image_path)
generated_images.append((image_path, model, fetch_time))
if not generated_images:
return await processing_message.edit_text('Failed to generate an image for all models.')
for image_path, model_name, fetch_time in generated_images:
caption = f'**Model:**\n> {model_name}\n**Prompt used:**\n> {prompt}\n\n**Fetching Time:** {fetch_time} ms'
await message.reply_photo(image_path, caption=caption, parse_mode=enums.ParseMode.MARKDOWN)
os.remove(image_path)
except Exception as e:
logger.error(f'Unexpected Error: {e}')
await processing_message.edit_text(format_exc(e))
finally:
await processing_message.delete()
modules_help['huggingface'] = {
'hf [prompt]*': 'Generate an AI image using Hugging Face model(s).',
'set_hf <api>*': 'Set the Hugging Face API key.',
'set_hf model <model_name>*': 'Add and set a Hugging Face model.',
'set_hf select <model_number|all>*': 'Select a specific model or all models for use.',
'set_hf delete <model_number>*': 'Delete a model from the list.',
}