Добавлен веб-личный кабинет: посты, контент-план, медиа-библиотека и генерация картинок

This commit is contained in:
apuc committed 2026-08-07 09:35:44 +03:00
1 parent d8c086d3b4
commit 81625b2e6f
35 files changed
+2220 -64

No files matched your search

Binary file not shown.
+89
View File
@@ -0,0 +1,89 @@
import base64
import struct
import requests
from config import PIAPI_API_KEY, PIAPI_IMAGE_MODEL, get_proxies
PIAPI_IMAGE_URL = "https://api.piapi.ai/v1/images/generations"
# Доступные размеры (gpt-image-1)
IMAGE_SIZES = {
"square": ("1024x1024", "Квадрат 1024x1024"),
"wide": ("1536x1024", "Горизонтальный 1536x1024"),
"tall": ("1024x1536", "Вертикальный 1024x1536"),
}
# Пресеты стилей - дописываются к промпту
STYLE_PRESETS = {
"none": ("Без стиля", ""),
"photo": ("Фотореализм", ", ultra realistic photo, professional photography, natural lighting"),
"illustration": ("Иллюстрация", ", digital illustration, vibrant colors, detailed"),
"minimal": ("Минимализм", ", minimalist style, clean background, simple composition"),
"3d": ("3D-рендер", ", 3D render, soft shadows, modern design"),
"flat": ("Плоская графика", ", flat vector illustration, bold shapes, social media style"),
}
def detect_ext(data: bytes) -> str:
"""Определяет расширение файла по magic-байтам."""
if data[:8] == b"\x89PNG\r\n\x1a\n":
return ".png"
if data[:2] == b"\xff\xd8":
return ".jpg"
if data[:4] == b"RIFF" and data[8:12] == b"WEBP":
return ".webp"
if data[:4] == b"GIF8":
return ".gif"
return ".png"
class ImageGenerator:
"""Генерация изображений через PiAPI (OpenAI-совместимый /images/generations)."""
def __init__(self, model: str | None = None):
self.model = model or PIAPI_IMAGE_MODEL
self.proxies = get_proxies()
def generate(self, prompt: str, size: str = "square") -> bytes:
"""Генерирует картинку и возвращает её байты. Поднимает исключение при ошибке."""
if size not in IMAGE_SIZES:
size = "square"
size_value = IMAGE_SIZES[size][0]
response = requests.post(
PIAPI_IMAGE_URL,
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {PIAPI_API_KEY}",
},
json={
"model": self.model,
"prompt": prompt,
"n": 1,
"size": size_value,
},
proxies=self.proxies,
timeout=240,
)
response.raise_for_status()
data = response.json().get("data") or []
if not data:
raise RuntimeError("Пустой ответ от API картинок")
item = data[0]
if item.get("b64_json"):
return base64.b64decode(item["b64_json"])
if item.get("url"):
img_response = requests.get(item["url"], proxies=self.proxies, timeout=120)
img_response.raise_for_status()
return img_response.content
raise RuntimeError("Неожиданный формат ответа от API картинок")
def build_prompt(self, prompt: str, style: str = "none") -> str:
"""Дополняет промпт пресетом стиля."""
if style in STYLE_PRESETS:
return prompt.strip() + STYLE_PRESETS[style][1]
return prompt.strip()
+180 -37
View File
@@ -1,28 +1,26 @@
import random
import os
from groq import Groq
from config import GROQ_API_KEY
import json
import requests
from config import PIAPI_API_KEY, PIAPI_MODEL, get_proxies
PIAPI_URL = "https://api.piapi.ai/v1/chat/completions"
class ContentGenerator:
def __init__(self):
"""Инициализация генератора контента"""
print("🔄 Инициализация ContentGenerator...")
# Проверяем, есть ли ключ Groq
if GROQ_API_KEY:
try:
self.client = Groq(api_key=GROQ_API_KEY)
self.model = "llama-3.3-70b-versatile"
self.use_groq = True
print("✅ Groq API подключен!")
except Exception as e:
print(f"⚠️ Ошибка подключения Groq: {e}")
self.use_groq = False
# Проверяем, есть ли ключ PiAPI
if PIAPI_API_KEY:
self.model = PIAPI_MODEL
self.use_piapi = True
print("✅ PiAPI подключен!")
else:
print("⚠️ Groq API ключ не найден, использую шаблоны")
self.use_groq = False
print("⚠️ PiAPI ключ не найден, использую шаблоны")
self.use_piapi = False
# Шаблоны для запасного варианта (если Groq не работает)
# Шаблоны для запасного варианта (если PiAPI не работает)
self.templates = {
'instagram': [
"🔥 {business_name} — это не просто бизнес, это миссия!\n\n"
@@ -116,22 +114,22 @@ class ContentGenerator:
"""
print(f"🔄 Генерация поста для {platform}...")
# Пробуем сгенерировать через Groq
if self.use_groq:
# Пробуем сгенерировать через PiAPI
if self.use_piapi:
try:
post = self._generate_with_groq(business_type, business_description, platform)
post = self._generate_with_piapi(business_type, business_description, platform)
if post and len(post) > 20:
print("✅ Пост сгенерирован через Groq!")
print("✅ Пост сгенерирован через PiAPI!")
return post
except Exception as e:
print(f"⚠️ Ошибка Groq: {e}, использую шаблоны")
# Если Groq не работает - используем шаблоны
print(f"⚠️ Ошибка PiAPI: {e}, использую шаблоны")
# Если PiAPI не работает - используем шаблоны
print("🔄 Использую шаблоны...")
return self._generate_with_templates(business_type, business_description, platform)
def _generate_with_groq(self, business_type, business_description, platform):
"""Генерация через Groq API"""
def _generate_with_piapi(self, business_type, business_description, platform):
"""Генерация через PiAPI (OpenAI-совместимый chat/completions)"""
platform_names = {
'instagram': 'Instagram',
@@ -160,18 +158,163 @@ class ContentGenerator:
Пост должен быть уникальным и интересным.
"""
response = self.client.chat.completions.create(
model=self.model,
messages=[
{"role": "system", "content": "Ты профессиональный копирайтер. Пиши ярко, полезно и вовлекающе."},
{"role": "user", "content": prompt}
],
temperature=0.9,
max_tokens=500
response = requests.post(
PIAPI_URL,
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {PIAPI_API_KEY}"
},
json={
"model": self.model,
"messages": [
{"role": "system", "content": "Ты профессиональный копирайтер. Пиши ярко, полезно и вовлекающе."},
{"role": "user", "content": prompt}
],
"temperature": 0.9,
"max_tokens": 500
},
proxies=get_proxies(),
timeout=30
)
return response.choices[0].message.content.strip()
response.raise_for_status()
data = response.json()
return data["choices"][0]["message"]["content"].strip()
def generate_post_with_style(self, business_type, business_description, platform, style, wishes):
"""Генерирует пост с учётом стиля и пожеланий пользователя"""
print(f"🔄 Генерация поста в своём стиле для {platform}...")
if self.use_piapi:
try:
platform_names = {
'instagram': 'Instagram',
'linkedin': 'LinkedIn',
'telegram': 'Telegram',
'twitter': 'Twitter/X',
'ad': 'рекламный пост'
}
prompt = f"""
Ты — профессиональный SMM-специалист и копирайтер.
Напиши пост для {platform_names.get(platform, 'соцсетей')} о бизнесе.
Тип бизнеса: {business_type}
Описание бизнеса: {business_description}
Стиль поста: {style}
Пожелания к посту: {wishes}
Требования к посту:
- Длина: 100-200 слов
- Строго придерживайся указанного стиля и пожеланий
- Призыв к действию в конце
- Используй 2-3 эмодзи
Напиши только текст поста, без лишних пояснений.
"""
response = requests.post(
PIAPI_URL,
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {PIAPI_API_KEY}"
},
json={
"model": self.model,
"messages": [
{"role": "system", "content": "Ты профессиональный копирайтер. Точно следуешь стилю и пожеланиям клиента."},
{"role": "user", "content": prompt}
],
"temperature": 0.9,
"max_tokens": 500
},
proxies=get_proxies(),
timeout=30
)
response.raise_for_status()
post = response.json()["choices"][0]["message"]["content"].strip()
if post and len(post) > 20:
print("✅ Пост в стиле сгенерирован через PiAPI!")
return post
except Exception as e:
print(f"⚠️ Ошибка PiAPI: {e}, использую шаблоны")
print("🔄 Использую шаблоны...")
post = self._generate_with_templates(business_type, business_description, platform)
return f"{post}\n\n💭 Пожелания учтены частично (без ИИ): {wishes}"
def generate_content_plan(self, business_type, business_description, days):
"""Генерирует контент-план на несколько дней вперёд"""
print(f"🔄 Генерация контент-плана на {days} дн...")
if self.use_piapi:
try:
prompt = f"""
Ты — SMM-стратег. Составь контент-план на {days} дней для бизнеса.
Тип бизнеса: {business_type}
Описание бизнеса: {business_description}
Для каждого дня укажи платформу (instagram, linkedin, telegram, twitter или ad) и короткую идею поста (1 предложение).
Верни ТОЛЬКО валидный JSON без пояснений в формате:
{{"plan": [{{"day": 1, "platform": "instagram", "idea": "..."}}, ...]}}
"""
response = requests.post(
PIAPI_URL,
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {PIAPI_API_KEY}"
},
json={
"model": self.model,
"messages": [
{"role": "system", "content": "Ты отвечаешь только валидным JSON, без markdown и пояснений."},
{"role": "user", "content": prompt}
],
"temperature": 0.7,
"max_tokens": 1000,
"response_format": {"type": "json_object"}
},
proxies=get_proxies(),
timeout=30
)
response.raise_for_status()
content = response.json()["choices"][0]["message"]["content"].strip()
parsed = json.loads(content)
plan = parsed.get("plan")
if plan:
print("✅ Контент-план сгенерирован через PiAPI!")
return plan
except Exception as e:
print(f"⚠️ Ошибка PiAPI: {e}, использую шаблонный план")
print("🔄 Использую шаблонный план...")
return self._generate_template_plan(business_type, days)
def _generate_template_plan(self, business_type, days):
"""Простой контент-план без обращения к ИИ (запасной вариант)"""
platforms = ['instagram', 'linkedin', 'telegram', 'twitter', 'ad']
ideas = [
f"Расскажи историю успеха {business_type}",
f"Покажи закулисье работы {business_type}",
"Поделись отзывом клиента",
"Развенчай миф из твоей ниши",
"Дай практический совет подписчикам",
"Проведи опрос среди аудитории",
"Расскажи о новинке или акции"
]
plan = []
for day in range(1, days + 1):
plan.append({
'day': day,
'platform': platforms[(day - 1) % len(platforms)],
'idea': ideas[(day - 1) % len(ideas)]
})
return plan
def _generate_with_templates(self, business_type, business_description, platform):
"""Генерация через шаблоны (запасной вариант)"""