Files
social-post-writer-bot/help_message.py
T

124 lines
8.1 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.
from __future__ import annotations
import html
WELCOME_RICH_HTML = """<h1>Привет! Я — бот для написания постов</h1>
<p>Помогаю писать и редактировать посты для соцсетей. Расскажи, о чём нужен пост, а я верну три варианта: короткий, развёрнутый и с призывом к действию.</p>
<h2>Примеры запросов</h2>
<ul>
<li>«Напиши пост про запуск нового продукта»</li>
<li>«Сделай пост-напоминание о вебинаре в официальном тоне»</li>
<li>«Обнови этот вариант, добавь эмодзи» — реплаем на мой текст</li>
</ul>
<p>Выбери режим в нижнем меню: «Обучение», «Генерация», «Подписка» или «Помощь». Внутри режима кнопка «Главное меню» возвращает сюда. Команда /start тоже открывает главное меню.</p>
<p>В режиме «Обучение» пришли скриншоты своих постов; в режиме «Генерация» попроси идею или пост.</p>
"""
HELP_MENU_TITLE = "Помощь по боту"
HELP_MENU_INTRO = "Выбери раздел — откроется только он. Из любого раздела можно перейти в любой."
HELP_SECTIONS: dict[str, tuple[str, str]] = {
"posts": (
"✍️ Написать пост",
"""<ol>
<li>В режиме «Генерация» напиши сообщение или отправь голосовое с прямой просьбой, например: «Придумай пост для Telegram про запуск нового продукта».</li>
<li>Можешь указать площадку, аудиторию, факты, длину, цель и другие требования. Можно надиктовать подробное ТЗ или просто тему.</li>
<li>Я распознаю речь, проверю, просишь ли ты именно написать пост, и верну три варианта отдельными сообщениями.</li>
</ol>
<p>В сообщениях с вариантами будет только текст поста: без нумерации и вступлений, с абзацами, переносами строк и уместными эмодзи.</p>""",
),
"tone": (
"🎨 Tone of Voice",
"""<p>Пришли скриншот или галерею скриншотов своих опубликованных постов. Я распознаю текст постов и сохраню его как образцы, дополню предыдущие примеры и обновлю только правила Tone of Voice. Сами публикации не переписываю.</p>""",
),
"edit": (
"✏️ Правка поста",
"""<p>Ответь реплаем на сообщение с вариантом и укажи, что изменить. Например: «Сделай короче и добавь призыв». Я учту оригинальный текст и верну три новых варианта отдельными сообщениями.</p>""",
),
"idea": (
"💡 Идея для поста",
"""<p>Напиши «предложи идею для поста», «о чём написать?» или «накидай тему» — верну одну идею. Команда <code>/idea</code> работает так же.</p>
<p>Идеи сохраняются, чтобы не повторяться: каждая новая идея отличается от предыдущих.</p>""",
),
"subscription": (
"💳 Подписка",
"""<p>У тебя есть пробный период — 3 дня бесплатно. Дальше — подписка на 30 дней за 100 звёзд.</p>
<ul>
<li><code>/balance</code> — узнать статус: сколько осталось дней триала или подписки.</li>
<li><code>/subscribe</code> — оплатить подписку на 30 дней.</li>
</ul>
<p>При входе в режим «Подписка» бот сразу показывает активный период и сколько дней осталось. Нажми «Продлить подписку», чтобы сразу открыть счёт в звёздах. Когда период закончится, генерация постов и идей станет недоступна.</p>""",
),
"commands": (
"⌨️ Команды",
"""<p>Команды-переключатели доступны в любом режиме: <code>/start</code> — главное меню; <code>/training</code> — обучение; <code>/generate</code> — генерация; <code>/subscription</code> — подписка; <code>/help</code> — помощь.</p>
<p>Действия доступны только в своём режиме: <code>/style</code> и <code>/clear</code> — обучение; <code>/post</code> и <code>/idea</code> — генерация; <code>/balance</code> и <code>/subscribe</code> — подписка. <code>/clear</code> очищает пользовательские данные, но не меняет подписку и триал.</p>
<p>В режиме «Генерация» можно попросить идею обычными словами: «предложи идею для поста», «о чём написать?», «накидай тему».</p>""",
),
}
HELP_SECTION_ORDER: tuple[str, ...] = tuple(HELP_SECTIONS)
HELP_MENU_BUTTON = "⌂ В меню"
def _menu_buttons(current: str | None = None) -> str:
rows = []
for section in HELP_SECTION_ORDER:
title = HELP_SECTIONS[section][0]
if section == current:
button = f'<tg-button type="disabled">{title}</tg-button>'
else:
button = (
f'<tg-button type="callback_data" data="help:{section}" style="primary">'
f"{title}</tg-button>"
)
rows.append(f'<tg-button-row align="left">{button}</tg-button-row>')
return "".join(rows)
def _menu_button_row() -> str:
return (
f'<tg-button-row align="left">'
f'<tg-button type="callback_data" data="help:menu" style="primary">'
f"{HELP_MENU_BUTTON}</tg-button></tg-button-row>"
)
def help_menu_html(current: str | None = None) -> str:
return (
f"<h1>{HELP_MENU_TITLE}</h1>"
f"<p>{HELP_MENU_INTRO}</p>"
f"<p>Я помогаю писать и редактировать посты для соцсетей. "
f"Генерирую посты только по явной просьбе.</p>"
f"{_menu_buttons(current)}"
)
def help_section_html(section: str) -> str:
title, body = HELP_SECTIONS[section]
return f"<h1>{title}</h1>{body}{_menu_button_row()}{_menu_buttons(section)}"
def help_view(section: str | None = None) -> str:
if section and section in HELP_SECTIONS:
return help_section_html(section)
return help_menu_html(section)
def help_plain_view(section: str | None = None) -> str:
if section and section in HELP_SECTIONS:
title, body = HELP_SECTIONS[section]
return f"{HELP_MENU_TITLE} — {title}\n\n{rich_to_plain(body)}"
lines = [HELP_MENU_TITLE, HELP_MENU_INTRO]
lines += [f"• {HELP_SECTIONS[name][0]}" for name in HELP_SECTION_ORDER]
return "\n".join(lines)
def rich_to_plain(rich_html: str) -> str:
import re
text = re.sub(r"<li(?:\s[^>]*)?>", "• ", rich_html, flags=re.IGNORECASE)
text = re.sub(r"</?(?:h[1-6]|p|ul|ol|li|hr|table|tr|td|th|input)(?:\s[^>]*)?/?>", "\n", text, flags=re.IGNORECASE)
text = re.sub(r"<[^>]+>", "", text)
text = html.unescape(text)
return "\n".join(line.strip() for line in text.splitlines() if line.strip())