This commit is contained in:
2026-05-03 23:33:47 +03:00
parent 2a89917c44
commit 015b22264c
4 changed files with 536 additions and 231 deletions
+112 -227
View File
@@ -14,9 +14,6 @@ from telegram.ext import (
filters,
ContextTypes
)
from telegram.request import HTTPXRequest
import httpx
from httpx_socks import AsyncProxyTransport
from config import TELEGRAM_BOT_TOKEN, PIApi_API_KEY, PIApi_BASE_URL, PROXY_URL, PROXY_TYPE
from database import init_db, save_user, save_image_record, update_image_record, get_user_images
@@ -39,97 +36,6 @@ os.makedirs(UPLOAD_DIR, exist_ok=True)
os.makedirs(PROCESSED_DIR, exist_ok=True)
def get_telegram_request():
"""Настройка SOCKS5 прокси для Telegram с использованием httpx_socks"""
if not PROXY_URL:
return None
logger.info(f"🔌 Настройка SOCKS5 прокси для Telegram")
try:
# Парсим прокси URL
import re
match = re.search(r'socks5://([^:]+):([^@]+)@([^:]+):(\d+)', PROXY_URL)
if match:
username = match.group(1)
password = match.group(2)
host = match.group(3)
port = int(match.group(4))
logger.info(f"🔌 Прокси: {host}:{port}")
# Создаем транспорт с SOCKS5 прокси
transport = AsyncProxyTransport.from_url(
f"socks5://{username}:{password}@{host}:{port}"
)
request = HTTPXRequest(transport=transport)
logger.info("✅ SOCKS5 прокси настроен успешно")
return request
else:
# Пробуем без авторизации
from urllib.parse import urlparse
parsed = urlparse(PROXY_URL)
host = parsed.hostname
port = parsed.port
transport = AsyncProxyTransport.from_url(f"socks5://{host}:{port}")
request = HTTPXRequest(transport=transport)
logger.info("✅ SOCKS5 прокси настроен (без авторизации)")
return request
except Exception as e:
logger.error(f"Ошибка настройки: {e}")
return None
async def test_telegram_connection():
"""Тест подключения к Telegram через прокси"""
if not PROXY_URL:
logger.error("Прокси не настроен")
return False
logger.info("Тестируем подключение к Telegram через SOCKS5 прокси...")
try:
# Используем aiohttp с socks прокси
from aiohttp_socks import ProxyConnector
# Парсим прокси
import re
match = re.search(r'socks5://([^:]+):([^@]+)@([^:]+):(\d+)', PROXY_URL)
if match:
username = match.group(1)
password = match.group(2)
host = match.group(3)
port = int(match.group(4))
connector = ProxyConnector.from_url(f"socks5://{username}:{password}@{host}:{port}")
else:
from urllib.parse import urlparse
parsed = urlparse(PROXY_URL)
connector = ProxyConnector.from_url(f"socks5://{parsed.hostname}:{parsed.port}")
async with aiohttp.ClientSession(connector=connector) as session:
url = f"https://api.telegram.org/bot{TELEGRAM_BOT_TOKEN}/getMe"
async with session.get(url, timeout=15) as response:
if response.status == 200:
data = await response.json()
if data.get('ok'):
logger.info(f"✅ Подключение работает! Бот: @{data['result']['username']}")
return True
else:
logger.error(f"Ошибка API: {data}")
return False
else:
logger.error(f"HTTP {response.status}")
return False
except Exception as e:
logger.error(f"Ошибка: {e}")
return False
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Обработчик /start"""
user = update.effective_user
@@ -164,7 +70,7 @@ async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
await update.message.reply_text(
"📖 Инструкция:\n\n"
"1. Отправь фото\n"
"2. Напиши промпт (например: 'сделай фон пляжем')\n"
"2. Напиши промпт\n"
"3. Получи результат!\n\n"
"Команды: /start, /test, /history"
)
@@ -205,6 +111,7 @@ async def handle_photo(update: Update, context: ContextTypes.DEFAULT_TYPE):
async def cancel(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Отмена"""
logger.info(f"Cancel от {update.effective_user.id}")
if 'image_path' in context.user_data:
path = context.user_data['image_path']
if os.path.exists(path):
@@ -215,6 +122,98 @@ async def cancel(update: Update, context: ContextTypes.DEFAULT_TYPE):
return ConversationHandler.END
async def call_piapi_api(image_path: str, prompt: str) -> str:
"""Вызов PiAPI через SOCKS5 прокси"""
if not PIApi_API_KEY:
raise Exception("PIApi_API_KEY не настроен")
with open(image_path, "rb") as f:
image_base64 = base64.b64encode(f.read()).decode('utf-8')
headers = {
"x-api-key": PIApi_API_KEY,
"Content-Type": "application/json"
}
payload = {
"model": "black-forest-labs/FLUX.1-dev",
"task_type": "image-to-image",
"input": {
"image": f"data:image/jpeg;base64,{image_base64}",
"prompt": prompt,
"num_inference_steps": 28,
"guidance_scale": 7.5,
"strength": 0.8
}
}
# Используем aiohttp-socks для запросов
from aiohttp_socks import ProxyConnector
import re
# Парсим прокси
match = re.search(r'socks5://([^:]+):([^@]+)@([^:]+):(\d+)', PROXY_URL)
if match:
username = match.group(1)
password = match.group(2)
host = match.group(3)
port = int(match.group(4))
connector = ProxyConnector.from_url(f"socks5://{username}:{password}@{host}:{port}")
else:
from urllib.parse import urlparse
parsed = urlparse(PROXY_URL)
connector = ProxyConnector.from_url(f"socks5://{parsed.hostname}:{parsed.port}")
async with aiohttp.ClientSession(connector=connector) as session:
async with session.post(PIApi_BASE_URL, headers=headers, json=payload, timeout=30) as response:
if response.status != 200:
text = await response.text()
raise Exception(f"HTTP {response.status}: {text[:100]}")
data = await response.json()
if data.get('code') != 200:
raise Exception(f"API Error: {data.get('message')}")
task_id = data['data']['task_id']
logger.info(f"Создана задача: {task_id}")
return await poll_task(task_id, connector)
async def poll_task(task_id: str, connector, max_attempts: int = 60) -> str:
"""Ожидание завершения задачи"""
get_url = f"https://api.piapi.ai/api/v1/task/{task_id}"
headers = {"x-api-key": PIApi_API_KEY}
async with aiohttp.ClientSession(connector=connector) as session:
for attempt in range(max_attempts):
try:
async with session.get(get_url, headers=headers) as response:
if response.status != 200:
await asyncio.sleep(2)
continue
data = await response.json()
status = data.get('data', {}).get('status')
logger.info(f"Задача {task_id}: {status} (попытка {attempt + 1})")
if status == 'completed':
output = data.get('data', {}).get('output', {})
result_url = output.get('url') or output.get('image')
if result_url:
logger.info(f"Задача {task_id} выполнена")
return result_url
elif status == 'failed':
raise Exception("Задача не выполнена")
await asyncio.sleep(2)
except Exception as e:
logger.error(f"Ошибка опроса: {e}")
await asyncio.sleep(2)
raise Exception("Превышено время ожидания")
async def process_image(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Обработка через PiAPI"""
prompt = update.message.text
@@ -239,7 +238,7 @@ async def process_image(update: Update, context: ContextTypes.DEFAULT_TYPE):
await db.execute("UPDATE images SET prompt = ? WHERE id = ?", (prompt, image_id))
await db.commit()
result_url = await call_piapi_with_proxy(image_path, prompt)
result_url = await call_piapi_api(image_path, prompt)
if result_url:
result_path = os.path.join(
@@ -247,7 +246,7 @@ async def process_image(update: Update, context: ContextTypes.DEFAULT_TYPE):
f"user_{user.id}_{datetime.now().strftime('%Y%m%d_%H%M%S')}_result.jpg"
)
# Скачиваем результат через SOCKS5
# Скачиваем результат
from aiohttp_socks import ProxyConnector
import re
@@ -276,10 +275,9 @@ async def process_image(update: Update, context: ContextTypes.DEFAULT_TYPE):
caption=f"✅ Готово!\n\n{prompt}"
)
msg = await update.message.reply_photo(photo=open(result_path, "rb"))
await update_image_record(
image_id=image_id,
processed_file_id=msg.photo[-1].file_id,
processed_file_id=result_path,
processed_url=result_path,
status='completed'
)
@@ -295,6 +293,7 @@ async def process_image(update: Update, context: ContextTypes.DEFAULT_TYPE):
await status_msg.edit_text(f"❌ Ошибка: {str(e)}\n\nПопробуйте еще раз.")
await update_image_record(image_id=image_id, status='failed')
# Очистка
if os.path.exists(image_path):
os.remove(image_path)
@@ -302,123 +301,13 @@ async def process_image(update: Update, context: ContextTypes.DEFAULT_TYPE):
return ConversationHandler.END
async def call_piapi_with_proxy(image_path: str, prompt: str) -> str:
"""Вызов PiAPI через SOCKS5 прокси"""
if not PIApi_API_KEY:
raise Exception("PIApi_API_KEY не настроен")
with open(image_path, "rb") as f:
image_base64 = base64.b64encode(f.read()).decode('utf-8')
headers = {
"x-api-key": PIApi_API_KEY,
"Content-Type": "application/json"
}
payload = {
"model": "black-forest-labs/FLUX.1-dev",
"task_type": "image-to-image",
"input": {
"image": f"data:image/jpeg;base64,{image_base64}",
"prompt": prompt,
"num_inference_steps": 28,
"guidance_scale": 7.5,
"strength": 0.8
}
}
# Создаем SOCKS5 коннектор
from aiohttp_socks import ProxyConnector
import re
match = re.search(r'socks5://([^:]+):([^@]+)@([^:]+):(\d+)', PROXY_URL)
if match:
username = match.group(1)
password = match.group(2)
host = match.group(3)
port = int(match.group(4))
connector = ProxyConnector.from_url(f"socks5://{username}:{password}@{host}:{port}")
else:
from urllib.parse import urlparse
parsed = urlparse(PROXY_URL)
connector = ProxyConnector.from_url(f"socks5://{parsed.hostname}:{parsed.port}")
async with aiohttp.ClientSession(connector=connector) as session:
async with session.post(
PIApi_BASE_URL,
headers=headers,
json=payload,
timeout=30
) as response:
if response.status != 200:
text = await response.text()
raise Exception(f"HTTP {response.status}: {text[:100]}")
data = await response.json()
if data.get('code') != 200:
raise Exception(f"API Error: {data.get('message')}")
task_id = data['data']['task_id']
logger.info(f"Создана задача: {task_id}")
return await poll_task_with_proxy(task_id)
async def poll_task_with_proxy(task_id: str, max_attempts: int = 60) -> str:
"""Ожидание завершения задачи через SOCKS5 прокси"""
get_url = f"https://api.piapi.ai/api/v1/task/{task_id}"
headers = {"x-api-key": PIApi_API_KEY}
from aiohttp_socks import ProxyConnector
import re
match = re.search(r'socks5://([^:]+):([^@]+)@([^:]+):(\d+)', PROXY_URL)
if match:
username = match.group(1)
password = match.group(2)
host = match.group(3)
port = int(match.group(4))
connector = ProxyConnector.from_url(f"socks5://{username}:{password}@{host}:{port}")
else:
from urllib.parse import urlparse
parsed = urlparse(PROXY_URL)
connector = ProxyConnector.from_url(f"socks5://{parsed.hostname}:{parsed.port}")
async with aiohttp.ClientSession(connector=connector) as session:
for attempt in range(max_attempts):
try:
async with session.get(get_url, headers=headers) as response:
if response.status != 200:
await asyncio.sleep(2)
continue
data = await response.json()
status = data.get('data', {}).get('status')
if status == 'completed':
output = data.get('data', {}).get('output', {})
result_url = output.get('url') or output.get('image')
if result_url:
logger.info(f"Задача {task_id} выполнена")
return result_url
elif status == 'failed':
raise Exception("Задача не выполнена")
await asyncio.sleep(2)
except Exception as e:
logger.error(f"Ошибка опроса: {e}")
await asyncio.sleep(2)
raise Exception("Превышено время ожидания")
async def history_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""История обработок"""
user = update.effective_user
images = await get_user_images(user.id, limit=5)
if not images:
await update.message.reply_text("📭 Нет обработанных изображений\n\nОтправьте фото для начала работы!")
await update.message.reply_text("📭 Нет обработанных изображений")
return
text = "🖼 **История обработок:**\n\n"
@@ -442,32 +331,28 @@ async def error_handler(update: Update, context: ContextTypes.DEFAULT_TYPE):
async def main():
"""Запуск бота"""
print("\n" + "=" * 50)
print("🤖 ЗАПУСК TELEGRAM БОТА ЧЕРЕЗ SOCKS5 ПРОКСИ")
print("🤖 ЗАПУСК TELEGRAM БОТА")
print("=" * 50)
if not PROXY_URL:
print("❌ Прокси не настроен в .env файле!")
print("❌ Прокси не настроен!")
return
print(f"🔌 Тип: {PROXY_TYPE.upper()}")
print(f"🔌 Прокси: {PROXY_URL.split('@')[-1] if '@' in PROXY_URL else PROXY_URL}")
print("=" * 50)
# Проверяем подключение
if not await test_telegram_connection():
print("\n❌ НЕТ ПОДКЛЮЧЕНИЯ К TELEGRAM ЧЕРЕЗ ПРОКСИ!")
print("Проверьте настройки прокси в файле .env")
return
# Инициализация
# Инициализация БД
await init_db()
# Создаем приложение
request = get_telegram_request()
if request is None:
print("\n❌ Не удалось настроить прокси для Telegram!")
return
# Создаем приложение с поддержкой SOCKS5
# python-telegram-bot[socks] автоматически поддерживает SOCKS5
application = Application.builder().token(TELEGRAM_BOT_TOKEN).build()
application = Application.builder().token(TELEGRAM_BOT_TOKEN).request(request).build()
# Для исходящих запросов бота используем прокси
# Устанавливаем прокси через переменные окружения
os.environ['ALL_PROXY'] = PROXY_URL
os.environ['HTTP_PROXY'] = PROXY_URL
os.environ['HTTPS_PROXY'] = PROXY_URL
# Регистрируем обработчики
application.add_handler(CommandHandler("start", start))
@@ -491,7 +376,7 @@ async def main():
await application.updater.start_polling()
print("\n" + "=" * 50)
print("🤖 БОТ УСПЕШНО ЗАПУЩЕН ЧЕРЕЗ SOCKS5 ПРОКСИ!")
print("🤖 БОТ УСПЕШНО ЗАПУЩЕН!")
print("=" * 50)
print("\n📱 Отправь команду /test в Telegram\n")