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
This commit is contained in:
apuc committed 2026-08-27 15:24:15 +03:00
1 parent 81625b2e6f
commit c6048aeea0
37 files changed
+7037 -225

No files matched your search

+312
View File
@@ -0,0 +1,312 @@
"""Генерация .docx документа стратегии личного бренда из данных опроса."""
import io
from datetime import datetime
from docx import Document
from docx.shared import Pt, RGBColor, Inches
from docx.enum.text import WD_ALIGN_PARAGRAPH
def _split_lines(text):
"""Разбивает текст на строки, удаляя пустые."""
if not text:
return []
return [line.strip() for line in text.strip().split("\n") if line.strip()]
def _split_pipe(text):
"""Разбивает строки по символу '|' на колонки."""
rows = []
for line in _split_lines(text):
cols = [c.strip() for c in line.split("|")]
rows.append(cols)
return rows
def _add_table(doc, headers, rows):
"""Добавляет таблицу с заголовками и строками."""
table = doc.add_table(rows=1, cols=len(headers))
table.style = "Table Grid"
hdr = table.rows[0].cells
for i, h in enumerate(headers):
hdr[i].text = h
for row_data in rows:
cells = table.add_row().cells
for i, val in enumerate(row_data):
cells[i].text = val if i < len(row_data) else ""
doc.add_paragraph()
def generate_brand_docx(data: dict) -> bytes:
"""Генерирует .docx документ стратегии личного бренда или организации.
data — dict с полями опроса.
Возвращает байты документа."""
doc = Document()
brand_type = data.get("brand_type", "personal")
is_org = (brand_type == "organization")
is_community = (brand_type == "community")
# === ТИТУЛ ===
if is_community:
entity_name = data.get("org_name", "")
title = doc.add_heading(f"Стратегия развития бизнес-сообщества\n{entity_name}", 0)
elif is_org:
entity_name = data.get("org_name", "")
title = doc.add_heading(f"Стратегия бренд-коммуникаций\n{entity_name}", 0)
else:
entity_name = data.get("client_name", "")
title = doc.add_heading(f"Стратегия развития личного бренда\n{entity_name}", 0)
title.alignment = WD_ALIGN_PARAGRAPH.CENTER
version = data.get("version", "1.0")
date_str = data.get("date_str") or datetime.now().strftime("%B %Y")
doc.add_paragraph(f"Версия {version} {date_str}")
doc.add_paragraph()
# === 1. РЕЗЮМЕ И ЦЕЛИ ===
doc.add_heading("1. Резюме и цели", level=1)
if is_community:
doc.add_heading("Общественная организация:", level=3)
doc.add_paragraph(f"Название: {data.get('org_name', '—')}")
doc.add_paragraph(f"Сфера деятельности: {data.get('org_industry', '—')}")
doc.add_paragraph(f"Миссия: {data.get('org_mission', '—')}")
doc.add_paragraph(f"Направления деятельности: {data.get('org_products', '—')}")
doc.add_paragraph(f"Участников: {data.get('org_team_size', '—')}")
doc.add_paragraph(f"Год основания: {data.get('org_founded', '—')}")
doc.add_paragraph(f"Формат участия: {data.get('org_format', '—')}")
elif is_org:
doc.add_heading("Организация:", level=3)
doc.add_paragraph(f"Название: {data.get('org_name', '—')}")
doc.add_paragraph(f"Сфера деятельности: {data.get('org_industry', '—')}")
doc.add_paragraph(f"Миссия: {data.get('org_mission', '—')}")
doc.add_paragraph(f"Продукты/услуги: {data.get('org_products', '—')}")
doc.add_paragraph(f"Сотрудников: {data.get('org_team_size', '—')}")
doc.add_paragraph(f"Год основания: {data.get('org_founded', '—')}")
else:
doc.add_heading("Текущая ситуация:", level=3)
doc.add_paragraph(data.get("current_situation", "—"))
doc.add_heading("Ключевые цели на 12 месяцев:", level=3)
doc.add_paragraph(
f"Качественный рост аудитории: Набрать {data.get('goal_audience_number', '—')} "
f"подписчиков в основном {data.get('goal_audience_platform', '—')} "
f"(активная аудитория: {data.get('goal_audience_description', '—')})."
)
doc.add_paragraph(
f"Укрепление экспертного статуса: Стать признанным федеральным экспертом "
f"в сфере {data.get('goal_expert_field', '—')} "
f"(не менее {data.get('goal_expert_speaking', '—')} приглашений выступить "
f"на федеральных площадках в год)."
)
doc.add_paragraph(
f"Коммерциализация личного бренда: Обеспечить поток из не менее "
f"{data.get('goal_commercial_leads', '—')} заявок в месяц на платные услуги "
f"({data.get('goal_commercial_services', '—')}) через личный бренд."
)
doc.add_paragraph(
f"Системный контент: {data.get('goal_content_system', '—')}"
)
# === 2. ПОЗИЦИОНИРОВАНИЕ И УТП ===
doc.add_heading("2. Позиционирование и УТП", level=1)
doc.add_heading("Сущность бренда:", level=3)
doc.add_paragraph(data.get("brand_essence", "—"))
doc.add_heading("Позиционирование (Ответы на ключевые вопросы):", level=3)
if is_community:
doc.add_paragraph(f"Для кого создано: {data.get('positioning_who', '—')}")
doc.add_paragraph(f"Какую проблему решает: {data.get('positioning_problem', '—')}")
doc.add_paragraph(f"Ценность для участника: {data.get('positioning_result', '—')}")
doc.add_paragraph(f"Чем отличаемся от других: {data.get('positioning_difference', '—')}")
doc.add_paragraph(f"Форматы взаимодействия: {data.get('community_formats', '—')}")
elif is_org:
doc.add_paragraph(f"Кому помогаем: {data.get('positioning_who', '—')}")
doc.add_paragraph(f"Какую проблему решаем: {data.get('positioning_problem', '—')}")
doc.add_paragraph(f"Какой результат получает клиент: {data.get('positioning_result', '—')}")
doc.add_paragraph(f"Почему выбирают нас: {data.get('positioning_difference', '—')}")
else:
doc.add_paragraph(f"Кому я помогаю? {data.get('positioning_who', '—')}")
doc.add_paragraph(f"Какую проблему решаю? {data.get('positioning_problem', '—')}")
doc.add_paragraph(f"Какой результат получает клиент? {data.get('positioning_result', '—')}")
doc.add_heading("Уникальное торговое предложение (УТП):", level=3)
doc.add_paragraph(data.get("usp", "—"))
# === 3. АУДИТОРИИ И КАНАЛЫ ===
doc.add_heading("3. Аудитории и каналы коммуникации", level=1)
doc.add_heading("Идеальные подписчики/клиенты:", level=3)
for line in _split_lines(data.get("ideal_audiences", "")):
doc.add_paragraph(line, style="List Bullet")
doc.add_heading("Карта каналов (по приоритету):", level=3)
channels = data.get("channels", [])
if channels:
_add_table(doc, ["Канал", "Тип", "Частота", "Описание"], [
[ch.get("platform", ""), ch.get("type", ""), ch.get("frequency", ""), ch.get("description", "")]
for ch in channels
])
else:
doc.add_paragraph("—")
# === 4. ВИЗУАЛЬНАЯ И ВЕРБАЛЬНАЯ ИДЕНТИЧНОСТЬ ===
doc.add_heading("4. Визуальная и вербальная идентичность", level=1)
doc.add_heading("А. Визуальная идентичность", level=3)
doc.add_paragraph(f"Концепция: {data.get('visual_concept', '—')}")
doc.add_paragraph("Цветовая палитра:")
_add_table(doc, ["Роль", "Название", "HEX"], [
["Основной", data.get("color_main_name", ""), data.get("color_main_hex", "")],
["Акцент 1", data.get("color_accent1_name", ""), data.get("color_accent1_hex", "")],
["Акцент 2", data.get("color_accent2_name", ""), data.get("color_accent2_hex", "")],
["Нейтральная база", data.get("color_neutral", ""), ""],
])
doc.add_paragraph("Типографика:")
doc.add_paragraph(f"Заголовки: {data.get('typography_headings', '—')}", style="List Bullet")
doc.add_paragraph(f"Текст: {data.get('typography_body', '—')}", style="List Bullet")
doc.add_paragraph(f"Акценты: {data.get('typography_accents', '—')}", style="List Bullet")
doc.add_heading("Б. Tone of Voice (Тон коммуникации)", level=3)
doc.add_paragraph(data.get("tov_description", "—"))
doc.add_paragraph(f"Референсы: {data.get('tov_references', '—')}")
tov_axes = _split_lines(data.get("tov_axes", ""))
if tov_axes:
doc.add_paragraph("Оси ToV:")
for axis in tov_axes:
doc.add_paragraph(axis, style="List Number")
# === 5. КОНТЕНТ-СТРАТЕГИЯ ===
doc.add_heading("5. Контент-стратегия: Три столпа", level=1)
doc.add_paragraph(
"Контент строится на балансе трех направлений, работающих на разные этапы воронки доверия."
)
pillars = [
("1", data.get("pillar1_name", "Кто я"), data.get("pillar1_goal", ""),
data.get("pillar1_formats", ""), data.get("pillar1_frequency", "30%")),
("2", data.get("pillar2_name", "Решаю боль"), data.get("pillar2_goal", ""),
data.get("pillar2_formats", ""), data.get("pillar2_frequency", "50%")),
("3", data.get("pillar3_name", "Втягиваю"), data.get("pillar3_goal", ""),
data.get("pillar3_formats", ""), data.get("pillar3_frequency", "20%")),
]
_add_table(doc, ["Столп", "Цель", "Ключевые форматы", "Частота"], [
[f"{p[1]}", p[2], p[3], p[4]]
for p in pillars
])
special = data.get("special_formats", "")
if special:
doc.add_paragraph(f"Особые форматы: {special}")
# === 6. ВОРОНКА И ПРОДУКТЫ ===
doc.add_heading("6. Воронка вовлечения и продуктовый ряд", level=1)
doc.add_heading("Воронка (путь от незнакомца к клиенту):", level=3)
funnel_stages = data.get("funnel", [])
if funnel_stages:
_add_table(doc, ["Этап", "Контент", "CTA"], [
[s.get("name", ""), s.get("content", ""), s.get("cta", "")]
for s in funnel_stages
])
else:
doc.add_paragraph("—")
doc.add_heading("Продуктовый ряд (от простого к сложному):", level=3)
doc.add_paragraph(f"Входная точка (Lead Magnet): {data.get('product_lead_magnet', '—')}")
doc.add_paragraph(f"Самая простая услуга: {data.get('product_simple', '—')}")
doc.add_paragraph(f"Услуга-конвертер: {data.get('product_converter', '—')}")
doc.add_paragraph(f"Комплексные решения: {data.get('product_complex', '—')}")
doc.add_paragraph(f"Экспертные продукты: {data.get('product_expert', '—')}")
# === 7. МЕТРИКИ ===
doc.add_heading("7. Метрики", level=1)
quant_rows = _split_pipe(data.get("metrics_quantitative", ""))
if quant_rows:
doc.add_heading("Количественные:", level=3)
_add_table(doc, ["Метрика", "Целевое значение", "Измерение"], quant_rows)
qual_rows = _split_pipe(data.get("metrics_qualitative", ""))
if qual_rows:
doc.add_heading("Качественные:", level=3)
_add_table(doc, ["Метрика", "Целевое значение", "Измерение"], qual_rows)
comm_rows = _split_pipe(data.get("metrics_commercial", ""))
if comm_rows:
doc.add_heading("Коммерческие:", level=3)
_add_table(doc, ["Метрика", "Целевое значение", "Измерение"], comm_rows)
# === 8. РИСКИ И РЕКОМЕНДАЦИИ ===
doc.add_heading("8. Потенциальные риски и рекомендации", level=1)
risk_rows = _split_pipe(data.get("risks", ""))
if risk_rows:
doc.add_heading("Потенциальные риски:", level=3)
_add_table(doc, ["Риск", "Решение"], risk_rows)
recs = _split_lines(data.get("recommendations", ""))
if recs:
doc.add_heading("Рекомендации:", level=3)
for rec in recs:
doc.add_paragraph(rec, style="List Bullet")
# === 9. АРХЕТИПИЧЕСКИЙ ОБРАЗ ===
doc.add_heading("9. Архетипический образ", level=1)
doc.add_heading(f"Ключевой архетип: {data.get('archetype_key_name', '—')}", level=3)
doc.add_paragraph(f"Суть: {data.get('archetype_key_essence', '—')}")
manifestations = _split_lines(data.get("archetype_key_manifestations", ""))
if manifestations:
doc.add_paragraph("Проявления:")
for m in manifestations:
doc.add_paragraph(m, style="List Bullet")
supporting = _split_pipe(data.get("archetype_supporting", ""))
if supporting:
doc.add_heading("Вспомогательные архетипы:", level=3)
_add_table(doc, ["Архетип", "Суть", "Проявления"], supporting)
doc.add_heading("Синтез архетипов:", level=3)
doc.add_paragraph(data.get("archetype_synthesis", "—"))
table_rows = _split_pipe(data.get("archetype_table", ""))
if table_rows:
doc.add_heading("Таблица проявлений:", level=3)
_add_table(doc, ["Ситуация / Контент-формат", "Доминирующий архетип", "Как это выглядит и звучит"], table_rows)
# === 10. ПРОМПТ ДЛЯ ГЕНЕРАЦИИ КОНТЕНТА ===
doc.add_heading("10. Промпт для генерации контента", level=1)
doc.add_heading("Контекст и личность:", level=3)
doc.add_paragraph(data.get("prompt_context", "—"))
doc.add_heading("Tone of Voice:", level=3)
doc.add_paragraph(data.get("prompt_tov", "—"))
doc.add_heading("Структура мысли:", level=3)
doc.add_paragraph(data.get("prompt_structure", "—"))
doc.add_heading("Контент-направления:", level=3)
for line in _split_lines(data.get("prompt_directions", "")):
doc.add_paragraph(line, style="List Bullet")
doc.add_heading("Форматы и шаблоны:", level=3)
doc.add_paragraph(data.get("prompt_formats", "—"))
doc.add_heading("Технические требования к тексту:", level=3)
doc.add_paragraph(data.get("prompt_technical", "—"))
# === Сериализация в байты ===
buf = io.BytesIO()
doc.save(buf)
buf.seek(0)
return buf.read()