175 lines
7.2 KiB
Python
175 lines
7.2 KiB
Python
from __future__ import annotations
|
||
import shlex
|
||
from enum import Enum
|
||
from typing import Dict, Optional
|
||
|
||
|
||
class MessageType(Enum):
|
||
TEXT = "text"
|
||
PHOTO = "photo"
|
||
VIDEO = "video"
|
||
ANIMATION = "animation"
|
||
|
||
|
||
class Messages:
|
||
# Команды
|
||
WELCOME_MESSAGE = (
|
||
"👋 Привет! Я редактор постов для каналов.\n\n"
|
||
"🤖 Сначала добавьте бота через /add_bot\n"
|
||
"📢 Затем добавьте каналы через /add_channel\n"
|
||
"📝 Потом можно:\n"
|
||
"- Создать пост: /newpost\n"
|
||
"- Управлять шаблонами: /tpl_new, /tpl_list\n"
|
||
"- Посмотреть список ботов: /bots\n"
|
||
"- Посмотреть список каналов: /channels\n\n"
|
||
"❓ Справка: /help"
|
||
)
|
||
|
||
HELP_MESSAGE = (
|
||
"📖 Справка по командам:\n\n"
|
||
"Управление ботами и каналами:\n"
|
||
"/add_bot - Добавить нового бота\n"
|
||
"/bots - Список ваших ботов\n"
|
||
"/add_channel - Добавить канал\n"
|
||
"/channels - Список ваших каналов\n\n"
|
||
"Управление постами:\n"
|
||
"/newpost - Создать новый пост\n\n"
|
||
"Управление шаблонами:\n"
|
||
"/tpl_new - Создать шаблон\n"
|
||
"/tpl_list - Список ваших шаблонов"
|
||
)
|
||
|
||
START = ("👋 Привет! Я редактор постов. Доступные команды:\n"
|
||
"/newpost — создать новый пост\n"
|
||
"/tpl_new — создать шаблон\n"
|
||
"/tpl_list — список шаблонов")
|
||
|
||
# Ошибки
|
||
ERROR_SESSION_EXPIRED = "❌ Сессия истекла. Начните заново с /newpost"
|
||
ERROR_INVALID_FORMAT = "❌ Неверный формат. Попробуйте еще раз"
|
||
ERROR_NO_CHANNELS = "❌ У вас нет каналов. Добавьте канал через админку"
|
||
ERROR_TEMPLATE_NOT_FOUND = "❌ Шаблон не найден"
|
||
ERROR_TEMPLATE_CREATE = "❌ Ошибка при создании шаблона: {error}"
|
||
ERROR_INVALID_KEYBOARD = "❌ Неверный формат клавиатуры"
|
||
|
||
# Создание поста
|
||
SELECT_CHANNEL = "📢 Выберите канал для публикации:"
|
||
SELECT_TYPE = "📝 Выберите тип поста:"
|
||
SELECT_FORMAT = "🔤 Выберите формат текста:"
|
||
ENTER_TEXT = "✏️ Введите текст сообщения\nИли используйте #имя_шаблона для применения шаблона"
|
||
ENTER_MEDIA = "📎 Отправьте {media_type}"
|
||
ENTER_KEYBOARD = "⌨️ Введите клавиатуру в формате:\nтекст|url\nтекст2|url2\n\nИли отправьте 'skip' для пропуска"
|
||
|
||
# Шаблоны
|
||
TEMPLATE_LIST = "📜 Список шаблонов (стр. {page}/{total_pages}):"
|
||
TEMPLATE_CREATE_NAME = "📝 Введите имя для нового шаблона:"
|
||
TEMPLATE_CREATE_TYPE = "📌 Выберите тип шаблона:"
|
||
TEMPLATE_CREATE_FORMAT = "🔤 Выберите формат текста:"
|
||
TEMPLATE_CREATE_CONTENT = "✏️ Введите содержимое шаблона:"
|
||
TEMPLATE_CREATE_KEYBOARD = "⌨️ Введите клавиатуру или отправьте 'skip':"
|
||
TEMPLATE_CREATED = "✅ Шаблон успешно создан"
|
||
TEMPLATE_DELETED = "🗑 Шаблон удален"
|
||
|
||
# Отправка
|
||
CONFIRM_SEND = "📤 Как отправить пост?"
|
||
ENTER_SCHEDULE = "📅 Введите дату и время для публикации в формате YYYY-MM-DD HH:MM"
|
||
POST_SCHEDULED = "✅ Пост запланирован на {datetime}"
|
||
POST_SENT = "✅ Пост отправлен"
|
||
|
||
|
||
class MessageParsers:
|
||
@staticmethod
|
||
def parse_template_invocation(s: str) -> tuple[str, Dict[str, str]]:
|
||
"""
|
||
Парсит вызов шаблона вида: "#promo title='Hi' url=https://x.y"
|
||
Возвращает: ("promo", {"title":"Hi", "url":"https://x.y"})
|
||
"""
|
||
s = (s or "").strip()
|
||
if not s.startswith("#"):
|
||
raise ValueError("not a template invocation")
|
||
|
||
try:
|
||
parts = shlex.split(s)
|
||
name = parts[0][1:] # убираем #
|
||
args: Dict[str, str] = {}
|
||
|
||
for tok in parts[1:]:
|
||
if "=" in tok:
|
||
k, v = tok.split("=", 1)
|
||
args[k.strip()] = v.strip().strip('"\'')
|
||
return name, args
|
||
|
||
except ValueError as e:
|
||
raise ValueError(f"Ошибка парсинга шаблона: {e}")
|
||
|
||
@staticmethod
|
||
def parse_key_value_lines(text: str) -> Dict[str, str]:
|
||
"""
|
||
Парсит переменные в форматах:
|
||
- построчно:
|
||
key=value
|
||
key2="quoted value"
|
||
- одной строкой:
|
||
key=value key2="quoted value"
|
||
"""
|
||
text = (text or "").strip()
|
||
if not text:
|
||
return {}
|
||
|
||
try:
|
||
if "\n" in text:
|
||
# Построчный формат
|
||
out: Dict[str, str] = {}
|
||
for line in text.splitlines():
|
||
line = line.strip()
|
||
if "=" in line:
|
||
k, v = line.split("=", 1)
|
||
out[k.strip()] = v.strip().strip('"\'')
|
||
return out
|
||
else:
|
||
# Однострочный формат
|
||
out: Dict[str, str] = {}
|
||
for tok in shlex.split(text):
|
||
if "=" in tok:
|
||
k, v = tok.split("=", 1)
|
||
out[k.strip()] = v.strip()
|
||
return out
|
||
|
||
except ValueError as e:
|
||
raise ValueError(f"Ошибка парсинга переменных: {e}")
|
||
|
||
@staticmethod
|
||
def parse_keyboard(text: str) -> Optional[Dict]:
|
||
"""
|
||
Парсит клавиатуру в формате:
|
||
текст1|url1
|
||
текст2|url2
|
||
|
||
Возвращает:
|
||
{
|
||
"rows": [
|
||
[{"text": "текст1", "url": "url1"}],
|
||
[{"text": "текст2", "url": "url2"}]
|
||
]
|
||
}
|
||
"""
|
||
text = (text or "").strip()
|
||
if not text or text.lower() == "skip":
|
||
return None
|
||
|
||
try:
|
||
rows = []
|
||
for line in text.splitlines():
|
||
line = line.strip()
|
||
if "|" in line:
|
||
text, url = line.split("|", 1)
|
||
rows.append([{
|
||
"text": text.strip(),
|
||
"url": url.strip()
|
||
}])
|
||
return {"rows": rows} if rows else None
|
||
|
||
except Exception as e:
|
||
raise ValueError(f"Ошибка парсинга клавиатуры: {e}")
|
||
return out
|