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:
1 parent
81625b2e6f
commit
c6048aeea0
37 files changed
+7037
-225
No files matched your search
@@ -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()
|
||||
Reference in new issue
Block a user