Files
press/ai_core/neural_network.py
T
apuc c6048aeea0 feat: AI Press Secretary - content generation and publishing platform
Features:
- AI-powered post generation from brand profiles
- Image generation with templates
- Content planning
- Telegram publishing
- Social profile analyzer
- Community organization support
- Markdown to HTML conversion for Telegram
- Inline AI chat for post editing

Deploy:
- Add deployment scripts for Linux servers
- Add PowerShell deploy script for Windows
- Add Nginx and SSL setup scripts
- Add backup and restore scripts
2026-08-27 15:24:15 +03:00

992 lines
53 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import random
import os
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, provider_settings=None):
"""Инициализация генератора контента.
provider_settings — опциональный dict с ключами:
provider_type, api_key, base_url, text_model
Если не передан — используются глобальные настройки PiAPI из config."""
print("🔄 Инициализация ContentGenerator...")
if provider_settings and provider_settings.get("api_key"):
# Per-user настройки
self.api_key = provider_settings["api_key"]
self.base_url = provider_settings.get("base_url") or PIAPI_URL.replace("/chat/completions", "")
self.model = provider_settings.get("text_model") or PIAPI_MODEL
self.use_piapi = True
self.chat_url = self.base_url.rstrip("/") + "/chat/completions"
self.provider_type = provider_settings.get("provider_type", "custom")
# Прокси используется для всех провайдеров (если настроен в .env)
# Это нужно для доступа из России к зарубежным API
self.proxies = get_proxies()
print(f"✅ Провайдер: {self.provider_type} | модель: {self.model} | прокси: {'да' if self.proxies else 'нет'}")
elif PIAPI_API_KEY:
# Глобальные настройки из config
self.api_key = PIAPI_API_KEY
self.base_url = PIAPI_URL.replace("/chat/completions", "")
self.model = PIAPI_MODEL
self.use_piapi = True
self.chat_url = PIAPI_URL
self.provider_type = "piapi"
self.proxies = get_proxies()
print("✅ PiAPI подключен (глобальные настройки)!")
else:
print("⚠️ API ключ не найден, использую шаблоны")
self.use_piapi = False
self.api_key = None
self.chat_url = PIAPI_URL
self.model = PIAPI_MODEL
self.provider_type = "piapi"
self.proxies = get_proxies()
# Шаблоны для запасного варианта (если PiAPI не работает)
self.templates = {
'instagram': [
"🔥 {business_name} — это не просто бизнес, это миссия!\n\n"
"Рассказываю, как мы пришли к тому, что делаем сегодня.\n"
"В {years} лет мы прошли путь от идеи до реализации.\n\n"
"А вы знали, что {business_name} помогает {problem_solution}?\n\n"
"👉 Напиши в комментариях, сталкивался ли ты с этой проблемой!\n\n"
"#бизнес #предприниматель #{hashtag} #историяуспеха",
"📊 {business_name} — цифры, которые вдохновляют!\n\n"
"За {years} лет мы:\n"
"✅ Помогли {clients} клиентам\n"
"✅ Увеличили прибыль на {profit}%\n"
"✅ Выросли в {growth} раз\n\n"
"Хочешь так же? Пиши в DM! 💬\n\n"
"#бизнес #предпринимательство #{hashtag}"
],
'linkedin': [
"🚀 Как {business_name} изменил рынок в {years} году?\n\n"
"Когда мы начинали, казалось, что {problem} — это непреодолимая стена.\n"
"Но мы нашли решение: {solution}\n\n"
"Результат: {result} 📈\n\n"
"Какую бизнес-задачу вы решаете сегодня? Делитесь в комментариях! 💬\n\n"
"#бизнес #лидерство #{hashtag} #growth",
"💡 5 уроков за {years} лет в бизнесе\n\n"
"1️⃣ {lesson1}\n"
"2️⃣ {lesson2}\n"
"3️⃣ {lesson3}\n"
"4️⃣ {lesson4}\n"
"5️⃣ {lesson5}\n\n"
"Какой урок оказался для вас самым важным? 👇\n\n"
"#бизнес #урокипредпринимательства #{hashtag}"
],
'telegram': [
"💬 Привет, друзья! 👋\n\n"
"Сегодня хочу поделиться мыслями о {business_name}.\n\n"
"Многие спрашивают: «Как ты пришел к этому?»\n"
"Ответ прост: {short_story}\n\n"
"А какой бизнес вдохновляет тебя? Пиши в комментариях! 👇\n\n"
"#бизнес #предприниматель #{hashtag}",
"📌 Важный пост для предпринимателей!\n\n"
"В {business_name} мы верим, что {belief}\n\n"
"Поэтому сегодня делюсь {tip} советом:\n"
"{advice}\n\n"
"А какой совет ты бы дал себе в начале пути? 🔥\n\n"
"#бизнес #советы #{hashtag}"
],
'twitter': [
"🧵 Нить: Как {business_name} за {years} лет изменил подход к {industry}\n\n"
"1/ Мы начинали с {start_point}\n"
"2/ Главная проблема была: {problem}\n"
"3/ Решение нашли через {solution}\n"
"4/ Результат: {result}\n"
"5/ Вывод: {lesson}\n\n"
"Согласны? Репост, если цените бизнес-мышление! 🔄\n\n"
"#бизнес #{hashtag}",
"💭 Мысль дня от {business_name}:\n\n"
"{quote}\n\n"
"Как думаете, это правда? 👇\n\n"
"#бизнес #мысли #{hashtag}"
],
'ad': [
"🎯 Хочешь получить {benefit}?\n\n"
"В {business_name} мы знаем, как это сделать!\n\n"
"За {years} лет мы помогли {clients} клиентам достичь {result}\n\n"
"Напиши «ХОЧУ» в комментариях или DM, и я расскажу как! 💬\n\n"
"#реклама #бизнес #{hashtag}"
]
}
print("✅ ContentGenerator готов к работе!")
def generate_post(self, business_type, business_description, platform):
"""
Генерирует контент для соцсетей
Args:
business_type (str): Тип бизнеса
business_description (str): Описание бизнеса
platform (str): Платформа (instagram, linkedin, telegram, twitter, ad)
Returns:
str: Сгенерированный пост
"""
print(f"🔄 Генерация поста для {platform}...")
# Пробуем сгенерировать через PiAPI
if self.use_piapi:
try:
post = self._generate_with_piapi(business_type, business_description, platform)
if post and len(post) > 20:
print("✅ Пост сгенерирован через PiAPI!")
return post
except Exception as e:
print(f"⚠️ Ошибка PiAPI: {e}, использую шаблоны")
# Если PiAPI не работает - используем шаблоны
print("🔄 Использую шаблоны...")
return self._generate_with_templates(business_type, business_description, platform)
def _generate_with_piapi(self, business_type, business_description, platform):
"""Генерация через PiAPI (OpenAI-совместимый chat/completions)"""
platform_names = {
'instagram': 'Instagram',
'linkedin': 'LinkedIn',
'telegram': 'Telegram',
'twitter': 'Twitter/X',
'ad': 'рекламный пост'
}
prompt = f"""
Ты — профессиональный SMM-специалист и копирайтер.
Напиши интересный, живой пост для {platform_names.get(platform, 'соцсетей')} о бизнесе.
Тип бизнеса: {business_type}
Описание бизнеса: {business_description}
Требования к посту:
- Длина: 100-200 слов (но не больше 200 слов)
- Живой, разговорный стиль
- Полезный контент для предпринимателей
- Призыв к действию в конце
- Используй 2-3 эмодзи
Напиши только текст поста, без лишних пояснений.
Пост должен быть уникальным и интересным.
"""
response = requests.post(
self.chat_url,
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {self.api_key}"
},
json={
"model": self.model,
"messages": [
{"role": "system", "content": "Ты профессиональный копирайтер. Пиши ярко, полезно и вовлекающе."},
{"role": "user", "content": prompt}
],
"temperature": 0.9,
"max_tokens": 500
},
proxies=self.proxies,
timeout=30
)
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(
self.chat_url,
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {self.api_key}"
},
json={
"model": self.model,
"messages": [
{"role": "system", "content": "Ты профессиональный копирайтер. Точно следуешь стилю и пожеланиям клиента."},
{"role": "user", "content": prompt}
],
"temperature": 0.9,
"max_tokens": 500
},
proxies=self.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_post_from_brand_profile(self, brand_data, platform, style, wishes):
"""Генерирует пост на основе медиа-образа (данных опроса brand_survey).
brand_data — dict с полями опроса."""
print(f"🔄 Генерация поста из медиа-образа для {platform}...")
brand_type = brand_data.get("brand_type", "personal")
is_org = (brand_type == "organization")
is_community = (brand_type == "community")
# Собираем ключевые поля медиа-образа
client_name = brand_data.get("client_name", "")
client_role = brand_data.get("client_role", "")
brand_essence = brand_data.get("brand_essence", "")
positioning_who = brand_data.get("positioning_who", "")
positioning_problem = brand_data.get("positioning_problem", "")
positioning_result = brand_data.get("positioning_result", "")
usp = brand_data.get("usp", "")
tov_description = brand_data.get("tov_description", "")
tov_references = brand_data.get("tov_references", "")
tov_axes = brand_data.get("tov_axes", "")
prompt_context = brand_data.get("prompt_context", "")
prompt_tov = brand_data.get("prompt_tov", "")
prompt_structure = brand_data.get("prompt_structure", "")
prompt_directions = brand_data.get("prompt_directions", "")
prompt_formats = brand_data.get("prompt_formats", "")
prompt_technical = brand_data.get("prompt_technical", "")
# Поля организации
org_name = brand_data.get("org_name", "")
org_industry = brand_data.get("org_industry", "")
org_mission = brand_data.get("org_mission", "")
org_products = brand_data.get("org_products", "")
org_team_size = brand_data.get("org_team_size", "")
positioning_difference = brand_data.get("positioning_difference", "")
community_formats = brand_data.get("community_formats", "")
platform_names = {
'instagram': 'Instagram',
'linkedin': 'LinkedIn',
'telegram': 'Telegram',
'twitter': 'Twitter/X',
'ad': 'рекламный пост'
}
if is_community:
identity_name = org_name
identity_role = org_industry
identity_block = f"""=== ОБЩЕСТВЕННАЯ ОРГАНИЗАЦИЯ ===
Название: {org_name}
Сфера: {org_industry}
Миссия: {org_mission}
Направления деятельности: {org_products}
Участников: {org_team_size}
Формат взаимодействия: {community_formats}
Сущность бренда: {brand_essence}
УТП: {usp}"""
positioning_block = f"""=== ПОЗИЦИОНИРОВАНИЕ ===
Для кого создано: {positioning_who}
Какую проблему решает: {positioning_problem}
Ценность для участника: {positioning_result}
Чем отличаемся от других: {positioning_difference}"""
tone_intro = "Ты — профессиональный копирайтер, создающий контент для общественной организации / бизнес-сообщества."
post_intro = f"Напиши пост для {platform_names.get(platform, 'соцсетей')} от имени организации «{org_name}»."
system_msg = f"Ты профессиональный копирайтер. Пишешь контент для бизнес-сообщества «{org_name}» в соответствии с его миссией, ценностями и Tone of Voice."
elif is_org:
identity_name = org_name
identity_role = org_industry
identity_block = f"""=== ОРГАНИЗАЦИЯ ===
Название: {org_name}
Сфера: {org_industry}
Миссия: {org_mission}
Продукты/услуги: {org_products}
Сотрудников: {org_team_size}
Сущность бренда: {brand_essence}
УТП: {usp}"""
positioning_block = f"""=== ПОЗИЦИОНИРОВАНИЕ ===
Кому помогаем: {positioning_who}
Какую проблему решаем: {positioning_problem}
Какой результат получает клиент: {positioning_result}
Почему выбирают нас: {positioning_difference}"""
tone_intro = "Ты — профессиональный копирайтер, создающий контент для организации."
post_intro = f"Напиши пост для {platform_names.get(platform, 'соцсетей')} от имени организации «{org_name}»."
system_msg = f"Ты профессиональный копирайтер. Пишешь контент для организации «{org_name}» в соответствии с её Tone of Voice и позиционированием."
else:
identity_name = client_name
identity_role = client_role
identity_block = f"""=== ЛИЧНОСТЬ И КОНТЕКСТ ===
Имя: {client_name}
Роль: {client_role}
Сущность бренда: {brand_essence}
УТП: {usp}"""
positioning_block = f"""=== ПОЗИЦИОНИРОВАНИЕ ===
Кому помогаю: {positioning_who}
Какую проблему решаю: {positioning_problem}
Какой результат получает клиент: {positioning_result}"""
tone_intro = "Ты — профессиональный копирайтер-психолог, который глубоко понимает образ и стратегию личного бренда."
post_intro = f"Напиши пост для {platform_names.get(platform, 'соцсетей')} от лица автора медиа-образа."
system_msg = "Ты профессиональный копирайтер-психолог. Пишешь в точном соответствии с образом и Tone of Voice автора."
if self.use_piapi:
try:
prompt = f"""
{tone_intro}
{post_intro}
{identity_block}
{positioning_block}
=== TONE OF VOICE ===
Описание тона: {tov_description}
Референсы: {tov_references}
Оси ToV: {tov_axes}
=== СТРУКТУРА МЫСЛИ ===
{prompt_structure}
=== КОНТЕНТ-НАПРАВЛЕНИЯ ===
{prompt_directions}
=== ФОРМАТЫ И ШАБЛОНЫ ===
{prompt_formats}
=== ТЕХНИЧЕСКИЕ ТРЕБОВАНИЯ ===
{prompt_technical}
=== ПАРАМЕТРЫ ЭТОГО ПОСТА ===
Платформа: {platform_names.get(platform, 'соцсети')}
Стиль: {style}
Пожелания: {wishes}
Напиши только текст поста, без лишних пояснений. Следуй структуре мысли и Tone of Voice.
"""
response = requests.post(
self.chat_url,
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {self.api_key}"
},
json={
"model": self.model,
"messages": [
{"role": "system", "content": system_msg},
{"role": "user", "content": prompt}
],
"temperature": 0.9,
"max_tokens": 4000
},
proxies=self.proxies,
timeout=90
)
response.raise_for_status()
msg = response.json()["choices"][0]["message"]
post = (msg.get("content") or "").strip()
if post and len(post) > 20:
print("✅ Пост из медиа-образа сгенерирован через API!")
return post
# Если content пустой — возможно reasoning-модель не успела завершить
raise RuntimeError("API вернул пустой ответ (content пуст). Увеличьте max_tokens или попробуйте другую модель.")
except requests.exceptions.HTTPError as e:
status = e.response.status_code if e.response is not None else "?"
body = e.response.text[:200] if e.response is not None else ""
raise RuntimeError(f"API вернул ошибку {status}: {body}")
except requests.exceptions.ConnectionError as e:
raise RuntimeError(f"Не удалось подключиться к API: {e}")
except requests.exceptions.Timeout:
raise RuntimeError("Превышен таймаут запроса к API (30 сек)")
except Exception as e:
raise RuntimeError(f"Ошибка API: {e}")
# Fallback — шаблонный пост с данными медиа-образа
print("🔄 Использую шаблонный пост из медиа-образа...")
return self._generate_template_brand_post(brand_data, platform, wishes)
def modify_post(self, original_post: str, instruction: str) -> str:
"""Модифицирует существующий пост по инструкции пользователя.
original_post — текущий текст поста
instruction — что нужно сделать («сократи вдвое», «сделай официальнее» и т.д.)
Возвращает новый текст поста.
"""
print(f"🔄 Модификация поста: «{instruction[:60]}»...")
prompt = f"""Ты — профессиональный копирайтер. Пользователь просит изменить пост.
=== ТЕКУЩИЙ ПОСТ ===
{original_post}
=== ИНСТРУКЦИЯ ===
{instruction}
=== ЗАДАЧА ===
Перепиши пост, выполнив инструкцию. Сохрани общий смысл и стиль. Верни ТОЛЬКО готовый пост без пояснений и markdown."""
if self.use_piapi:
try:
response = requests.post(
self.chat_url,
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {self.api_key}"
},
json={
"model": self.model,
"messages": [
{"role": "system", "content": "Ты профессиональный копирайтер. Ты редактируешь посты строго по инструкции."},
{"role": "user", "content": prompt}
],
"temperature": 0.7,
"max_tokens": 4000
},
proxies=self.proxies,
timeout=90
)
response.raise_for_status()
post = response.json()["choices"][0]["message"]["content"].strip()
if post and len(post) > 20:
print("✅ Пост модифицирован!")
return post
except Exception as e:
print(f"⚠️ Ошибка API при модификации: {e}")
# Если ИИ не сработал — возвращаем оригинал с пометкой
return f"{original_post}\n\n💬 [ИИ не смог изменить пост: {instruction}]"
def _generate_template_brand_post(self, brand_data, platform, wishes):
"""Простой пост из медиа-образа без обращения к ИИ (запасной вариант)."""
client_name = brand_data.get("client_name", "Автор")
brand_essence = brand_data.get("brand_essence", "")
usp = brand_data.get("usp", "")
positioning_result = brand_data.get("positioning_result", "")
platform_emoji = {
"instagram": "📸", "linkedin": "💼", "telegram": "📱",
"twitter": "🐦", "ad": "📢"
}
emoji = platform_emoji.get(platform, "📝")
post = (
f"{emoji} {brand_essence}\n\n"
f"Меня зовут {client_name}.\n"
f"{usp}\n\n"
f"Я помогаю получить: {positioning_result}\n\n"
)
if wishes:
post += f"💭 {wishes}\n\n"
post += "Напишите в комментариях, какая у вас сейчас задача 👇\n\n#личныйбренд #бизнес"
return post
def suggest_image_concepts(self, post_text, brand_data=None):
"""Анализирует текст поста и предлагает 4 варианта концепции изображения.
Возвращает список dict: [{title, description, style, size}, ...]"""
print(f"🔄 Анализ поста для подбора концепции изображения ({len(post_text)} символов)...")
if not self.use_piapi:
# Fallback — базовые концепции без ИИ
return [
{"title": "Тематическая фотография", "description": "Профессиональная фотография, иллюстрирующая ключевую идею поста", "style": "photo", "size": "wide"},
{"title": "Минималистичный баннер", "description": "Чистый баннер с текстом поста на минималистичном фоне", "style": "minimal", "size": "wide"},
{"title": "Абстрактная иллюстрация", "description": "Цветная абстрактная иллюстрация в тон поста", "style": "illustration", "size": "square"},
{"title": "Инфографика", "description": "Визуализация данных и ключевых тезисов поста", "style": "flat", "size": "tall"},
]
brand_context = ""
if brand_data:
brand_context = f"""
Контекст бренда:
- Имя: {brand_data.get('client_name', '')}
- Роль: {brand_data.get('client_role', '')}
- Сущность: {brand_data.get('brand_essence', '')}
- УТП: {brand_data.get('usp', '')}
"""
prompt = f"""Ты — арт-директор и визуальный стратег. Проанализируй текст поста и предложи 4 разных варианта визуального оформления.
=== ТЕКСТ ПОСТА ===
{post_text}
{brand_context}
=== ЗАДАЧА ===
Предложи 4 контрастных варианта изображения. Каждый вариант — это отдельная идея с уникальным настроением и стилем.
Для каждого варианта укажи:
- title: краткое название (2-4 слова на русском)
- description: что изображено (1 предложение на русском)
- style: один из: photo, illustration, minimal, 3d, flat
- size: один из: square, wide, tall
Верни ТОЛЬКО JSON-массив без markdown и пояснений:
[{{"title": "...", "description": "...", "style": "...", "size": "..."}}, ...]"""
try:
response = requests.post(
self.chat_url,
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {self.api_key}"
},
json={
"model": self.model,
"messages": [
{"role": "system", "content": "Ты — арт-директор. Отвечай только валидным JSON без markdown-обёрток."},
{"role": "user", "content": prompt}
],
"temperature": 0.8,
"max_tokens": 1000
},
proxies=self.proxies,
timeout=60
)
response.raise_for_status()
raw = response.json()["choices"][0]["message"]["content"].strip()
# Убираем markdown-обёртку если есть
if raw.startswith("```"):
raw = raw.split("\n", 1)[1]
if raw.endswith("```"):
raw = raw[:-3]
raw = raw.strip()
concepts = json.loads(raw)
if isinstance(concepts, list) and len(concepts) > 0:
print(f"✅ Получено {len(concepts)} концепций от ИИ")
return concepts[:5] # макс 5
except Exception as e:
print(f"⚠️ Ошибка при получении концепций: {e}")
# Fallback
return [
{"title": "Тематическая фотография", "description": "Профессиональная фотография, иллюстрирующая ключевую идею поста", "style": "photo", "size": "wide"},
{"title": "Минималистичный баннер", "description": "Чистый баннер с текстом поста на минималистичном фоне", "style": "minimal", "size": "wide"},
{"title": "Абстрактная иллюстрация", "description": "Цветная абстрактная иллюстрация в тон поста", "style": "illustration", "size": "square"},
{"title": "Инфографика", "description": "Визуализация данных и ключевых тезисов поста", "style": "flat", "size": "tall"},
]
def parse_brand_doc_to_survey(self, doc_text):
"""Разбирает текст .docx документа стратегии личного бренда
и извлекает структурированные поля опроса через PiAPI.
Возвращает dict с полями brand_survey.data или None при ошибке."""
print(f"🔄 Разбор документа медиа-образа ({len(doc_text)} символов)...")
if not self.use_piapi:
print("⚠️ PiAPI недоступен — разбор документа невозможен")
return None
# Ограничиваем длину, чтобы влезть в контекст
text = doc_text[:12000]
prompt = f"""
Ты — аналитик. Извлеки структурированные данные из документа стратегии личного бренда и верни их в виде JSON.
Документ:
---
{text}
---
Извлеки следующие поля (если поле отсутствует в документе — оставь пустую строку ""):
{{
"client_name": "Имя клиента",
"client_role": "Профессия/роль",
"client_location": "Город/регион",
"current_situation": "Описание текущей ситуации",
"goal_audience_number": "число подписчиков",
"goal_audience_platform": "основная платформа",
"goal_audience_description": "описание аудитории",
"goal_expert_field": "область экспертизы",
"goal_expert_speaking": "число выступлений в год",
"goal_commercial_leads": "число заявок в месяц",
"goal_commercial_services": "услуги",
"goal_content_system": "описание контент-системы",
"brand_essence": "сущность бренда одной строкой",
"positioning_who": "кому помогаю",
"positioning_problem": "какую проблему решаю",
"positioning_result": "какой результат получает клиент",
"usp": "УТП",
"ideal_audiences": "идеальные подписчики, по строкам",
"visual_concept": "концепция визуала",
"color_main_name": "название основного цвета",
"color_main_hex": "HEX основного цвета",
"color_accent1_name": "название акцентного цвета 1",
"color_accent1_hex": "HEX акцентного цвета 1",
"color_accent2_name": "название акцентного цвета 2",
"color_accent2_hex": "HEX акцентного цвета 2",
"color_neutral": "нейтральная база",
"typography_headings": "шрифт заголовков",
"typography_body": "шрифт текста",
"typography_accents": "шрифт акцентов",
"tov_description": "описание тона",
"tov_references": "референсы",
"tov_axes": "оси ToV по строкам",
"pillar1_name": "название столпа 1",
"pillar1_goal": "цель столпа 1",
"pillar1_formats": "форматы столпа 1 по строкам",
"pillar1_frequency": "частота столпа 1",
"pillar2_name": "название столпа 2",
"pillar2_goal": "цель столпа 2",
"pillar2_formats": "форматы столпа 2 по строкам",
"pillar2_frequency": "частота столпа 2",
"pillar3_name": "название столпа 3",
"pillar3_goal": "цель столпа 3",
"pillar3_formats": "форматы столпа 3 по строкам",
"pillar3_frequency": "частота столпа 3",
"special_formats": "особые форматы",
"product_lead_magnet": "лид-магнит",
"product_simple": "простая услуга",
"product_converter": "услуга-конвертер",
"product_complex": "комплексные решения",
"product_expert": "экспертные продукты",
"metrics_quantitative": "количественные метрики, формат: Название | Значение | Измерение, по строкам",
"metrics_qualitative": "качественные метрики, тот же формат",
"metrics_commercial": "коммерческие метрики, тот же формат",
"risks": "риски, формат: Риск | Решение, по строкам",
"recommendations": "рекомендации по строкам",
"archetype_key_name": "название ключевого архетипа",
"archetype_key_essence": "суть ключевого архетипа",
"archetype_key_manifestations": "проявления по строкам",
"archetype_supporting": "вспомогательные архетипы, формат: Название | Суть | Проявления, по строкам",
"archetype_synthesis": "синтез архетипов",
"archetype_table": "таблица проявлений, формат: Ситуация | Архетип | Описание, по строкам",
"prompt_context": "контекст и личность для промпта",
"prompt_tov": "Tone of Voice для промпта",
"prompt_structure": "структура мысли",
"prompt_directions": "контент-направления по строкам",
"prompt_formats": "форматы и шаблоны",
"prompt_technical": "технические требования к тексту"
}}
Верни ТОЛЬКО валидный JSON без пояснений и markdown.
"""
try:
response = requests.post(
self.chat_url,
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {self.api_key}"
},
json={
"model": self.model,
"messages": [
{"role": "system", "content": "Ты отвечаешь только валидным JSON, без markdown и пояснений."},
{"role": "user", "content": prompt}
],
"temperature": 0.3,
"max_tokens": 3000,
"response_format": {"type": "json_object"}
},
proxies=self.proxies,
timeout=60
)
response.raise_for_status()
content = response.json()["choices"][0]["message"]["content"].strip()
parsed = json.loads(content)
print("✅ Документ медиа-образа разобран через PiAPI!")
# Гарантируем наличие channels и funnel
if "channels" not in parsed:
parsed["channels"] = []
if "funnel" not in parsed:
parsed["funnel"] = []
return parsed
except Exception as e:
print(f"⚠️ Ошибка разбора документа: {e}")
return None
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(
self.chat_url,
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {self.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=self.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_content_plan_from_brand_profile(self, brand_data, days):
"""Генерирует контент-план на основе медиа-образа."""
print(f"🔄 Генерация контент-плана из медиа-образа на {days} дн...")
brand_type = brand_data.get("brand_type", "personal")
is_org = (brand_type == "organization")
is_community = (brand_type == "community")
client_name = brand_data.get("client_name", "")
org_name = brand_data.get("org_name", "")
brand_essence = brand_data.get("brand_essence", "")
positioning_who = brand_data.get("positioning_who", "")
usp = brand_data.get("usp", "")
tov_description = brand_data.get("tov_description", "")
prompt_directions = brand_data.get("prompt_directions", "")
pillar1_formats = brand_data.get("pillar1_formats", "")
pillar2_formats = brand_data.get("pillar2_formats", "")
pillar3_formats = brand_data.get("pillar3_formats", "")
if is_community:
entity_name = org_name
pillar1_label = "О миссии"
pillar2_label = "Мероприятия"
pillar3_label = "Участники"
entity_type = "Бизнес-сообщество / общественная организация"
elif is_org:
entity_name = org_name
pillar1_label = "О продукте"
pillar2_label = "Экспертиза"
pillar3_label = "Вовлечение"
entity_type = "Организация"
else:
entity_name = client_name
pillar1_label = "Кто я"
pillar2_label = "Решаю боль"
pillar3_label = "Втягиваю"
entity_type = "Автор"
if self.use_piapi:
try:
prompt = f"""
Ты — SMM-стратег. Составь контент-план на {days} дней.
{entity_type}: {entity_name}
Сущность бренда: {brand_essence}
УТП: {usp}
Аудитория: {positioning_who}
Tone of Voice: {tov_description}
Контент-направления:
{prompt_directions}
Три столпа контента:
1. «{pillar1_label}» (форматы): {pillar1_formats}
2. «{pillar2_label}» (форматы): {pillar2_formats}
3. «{pillar3_label}» (форматы): {pillar3_formats}
Для каждого дня укажи платформу (instagram, linkedin, telegram, twitter или ad) и короткую идею поста (1 предложение), учитывая три столпа и Tone of Voice.
Верни ТОЛЬКО валидный JSON без пояснений в формате:
{{"plan": [{{"day": 1, "platform": "instagram", "idea": "..."}}, ...]}}
"""
response = requests.post(
self.chat_url,
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {self.api_key}"
},
json={
"model": self.model,
"messages": [
{"role": "system", "content": "Ты отвечаешь только валидным JSON, без markdown и пояснений."},
{"role": "user", "content": prompt}
],
"temperature": 0.7,
"max_tokens": 1200,
"response_format": {"type": "json_object"}
},
proxies=self.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(brand_essence or client_name or "бизнес", 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):
"""Генерация через шаблоны (запасной вариант)"""
# Если платформы нет в шаблонах - используем instagram
if platform not in self.templates:
platform = 'instagram'
# Подготовка данных для шаблона
data = {
'business_name': business_type,
'industry': business_description[:30] if business_description else "твоей нише",
'years': random.randint(2, 15),
'clients': random.randint(50, 5000),
'profit': random.randint(20, 300),
'growth': random.randint(2, 10),
'problem': random.choice([
"не хватало знаний", "было сложно найти клиентов",
"конкуренты давили", "не было системы"
]),
'solution': random.choice([
"инновационный подход", "автоматизация процессов",
"клиентоориентированность", "стратегическое планирование"
]),
'result': random.choice([
"выручка выросла в 3 раза", "число клиентов удвоилось",
"бренд узнали в 5 городах", "создали команду из 30 человек"
]),
'problem_solution': random.choice([
"решает главную проблему предпринимателей",
"помогает бизнесу расти быстрее",
"упрощает сложные процессы",
"делает мир лучше"
]),
'lesson1': "Не бойся пробовать новое",
'lesson2': "Клиент всегда прав (почти всегда)",
'lesson3': "Команда — это всё",
'lesson4': "Без маркетинга никуда",
'lesson5': "Верь в своё дело",
'short_story': random.choice([
"я просто начал и не остановился",
"увидел проблему и решил её",
"поверил в идею, несмотря на сомнения",
"просто делал то, что люблю"
]),
'belief': random.choice([
"успех приходит к тем, кто не боится ошибаться",
"лучший способ предсказать будущее — создать его",
"качество всегда побеждает количество"
]),
'tip': random.choice(["простой", "важный", "ключевой"]),
'advice': random.choice([
"не распыляйся на всё подряд — фокусируйся на главном",
"слушай своих клиентов — они лучшие советчики",
"инвестируй в своё развитие — это окупается",
"не бойся делегировать задачи"
]),
'start_point': random.choice([
"идеи и ноутбука", "небольшого стартапа",
"мечты и энтузиазма", "первых 5 клиентов"
]),
'quote': random.choice([
"Бизнес — это не про деньги, а про ценность, которую ты создаёшь",
"Каждый отказ приближает тебя к успеху",
"Лучший момент начать — прямо сейчас",
"Сложно — значит ты на правильном пути"
]),
'benefit': random.choice([
"прибыль без головной боли",
"стабильный доход",
"финансовую свободу",
"масштабирование бизнеса"
]),
'hashtag': random.choice([
"бизнесрост", "предприниматель", "успех",
"бизнесидеи", "мойбизнес", "развитие"
])
}
# Выбираем случайный шаблон для платформы
template = random.choice(self.templates[platform])
# Заполняем шаблон данными
try:
post = template.format(**data)
except KeyError as e:
# Если какие-то ключи отсутствуют
post = f"📝 Пост для {platform.upper()}\n\n" \
f"Бизнес: {business_type}\n" \
f"Описание: {business_description or 'Не указано'}\n\n" \
f"Расскажи о своём бизнесе в комментариях! 👇\n\n#бизнес #предприниматель"
return post