Карта функциональных этапов
На этой странице
- Получить событие Telegram
- Разобрать команду или ответ
- Проверить участника, копию и версию
- Принять операцию атомарно
- Показать принятие запроса
- Определить окно поиска
- Найти кандидатов
- Проверить URL и загрузить статью
- Извлечь текст и дату
- Оценить и отобрать темы
- Сохранить подборку и отправить карточки
- Принять выбор новости
- Подготовить подтверждённые факты
- Составить текст и надписи обложки
- Собрать подпись и проверить длину
- Проверить смысл трёх полей
- Создать или переиспользовать иллюстрацию
- Импортировать PNG
- Собрать фирменную обложку
- Получить обложку и доставить превью
- Сохранить готовность к согласованию
- Запросить и применить доработку
- Опубликовать одобренную версию
- Отклонить черновик
- Продолжить сохранённую попытку
- Сохранить ошибку и сообщить о ней
Что делает каждый этап, какие данные ему нужны и почему он стоит здесь.
01Получить событие Telegram
Единственный действующий Telegram Trigger передаёт событие в BFL-content. Данные о пользователе, чате и сообщении сохраняют связь с исходным действием.
- Вход
- Сообщение, reply или callback_query.
- Выход
- Событие для проверки и маршрутизации.
- При сбое
- Не создавать второй обработчик того же бота ради тестирования.
- Почему здесь
- Все действия должны проходить одинаковые проверки доступа и повторов.
Узлы экспорта: Telegram — участники проекта; Проверить участника Telegram
02Разобрать команду или ответ
Определить /start, /post, /news, /retry, /resume, кнопку либо ожидаемый reply. /start отделяет регистрацию от выдачи прав; /post и /news — самостоятельные ветки.
- Вход
- Текст сообщения, callback_data, reply_to_message.
- Выход
- Тип действия и его аргументы, ссылка на исходное сообщение.
- При сбое
- Неизвестный ввод вне ожидаемого reply может остаться без ответа. /help и /cancel не реализованы.
- Почему здесь
- Один и тот же текст в новой команде и в ответе на доработку означает разные операции.
Узлы экспорта: Распознать команду или решение; Маршрут Telegram-события; Ответить на безопасный /start
03Проверить участника, копию и версию
Сверить numeric ID, active, private chat=from.id и is_bot=false; для кнопки — собственную доставленную копию и последнюю версию. Перед внешними запросами допуск проверяется повторно.
- Вход
- Настоящий отправитель, личный чат, действующая роль и запись доставленной копии.
- Выход
- Разрешённое действие либо отказ.
- При сбое
- Чужой пользователь, неверное сообщение, устаревшая версия или закрытое состояние останавливают действие.
- Почему здесь
- Подписанный контекст и наличие кнопки не заменяют текущую роль и общий захват состояния.
Узлы экспорта: Проверить участника Telegram; Проверить владельца версию и статус; Проверить актуальность замечаний; Проверить новостную кнопку; Допуск → OpenAI — написать текст
04Принять операцию атомарно
Уникальные индексы и условные операции БД резервируют событие, версию и выбор статьи. Пустой результат захвата не разрешает внешний запрос.
- Вход
- Проверенное событие и сохранённое состояние.
- Выход
- Одна принятая операция с сохранённым владельцем блокировки.
- При сбое
- Повтор или уже выполняемая операция получает отказ/статус ожидания; неизвестный внешний исход не считается свободной попыткой.
- Почему здесь
- Чистая функция guard проверяет данные, но не заменяет атомарную операцию в БД.
Узлы экспорта: Зарезервировать версию; Принять статью атомарно; Принять поиск атомарно
05Показать принятие запроса
Отправить или обновить статус инициатору до долгой подготовки. Контекст связывает дочерний execution с участником. Callback отдельно получает всплывающее подтверждение.
- Вход
- Успешно принятая операция.
- Выход
- status_message_id и видимое «Взято в работу».
- При сбое
- Сбой статуса фиксируется отдельно и не повторяет генерацию.
- Почему здесь
- Инициатор видит принятие запроса. Измерение 2,208 с относится к восстановлению 349, а не гарантии будущих запросов.
Узлы экспорта: Подтвердить приём задания; Выбрать актуальный статус; Выбрать актуальный статус
06Определить окно поиска
Вычислить календарный диапазон по Europe/Moscow, включая сегодня. Допустимы 7 и 30 дней; тема поиска ограничена 200 единицами UTF-16.
- Вход
- /news с необязательными периодом и темой; доверенное серверное время.
- Выход
- from, to, days, timezone, topic.
- При сбое
- Неподдерживаемый период отклоняется; дата не назначается моделью.
- Почему здесь
- Свежесть материала должна определяться одинаково во всех стадиях.
Узлы экспорта: Подготовить подборку
07Найти кандидатов
Вызвать Responses с web_search, проверить и сохранить фактические URL. Текущий код допускает до 20 кандидатов из семи разрешённых издателей.
- Вход
- Период, тема, список разрешённых источников и поисковый prompt.
- Выход
- Ссылки кандидатов, поисковые источники, response_id.
- При сбое
- Незавершённый ответ, отказ, невалидный JSON или отсутствие реального поиска — отдельные ошибки.
- Почему здесь
- Модель не должна добавлять выдуманную ссылку лишь потому, что она выглядит правдоподобно.
Узлы экспорта: Подготовить web search; OpenAI — поиск по официальным сайтам; Проверить результат поиска
08Проверить URL и загрузить статью
Канонизировать URL, проверить точный хост и путь, загрузить HTML, проверяя каждый переход. Credentials OpenAI и Telegram в запрос к изданию не передаются.
- Вход
- Кандидат из фактического поиска.
- Выход
- HTTP-код, конечный адрес, тип данных, время и тело ответа.
- При сбое
- Недоступность, неподдерживаемый адрес, недопустимый редирект или формат отмечаются как причина пропуска.
- Почему здесь
- Поисковый фрагмент не равен прочитанной статье.
Узлы экспорта: Начать чтение; HTTP статья 0; Проверить HTTP 0
09Извлечь текст и дату
Выделить тело статьи и публикационную дату, сохранив контекст её происхождения. Удалить меню, стили и служебные блоки с сохранением границ слов.
- Вход
- Настоящий HTML-ответ.
- Выход
- read_id, article_text, published_date, publication_date_context, fetched_at.
- При сбое
- Неоднозначная дата или испорченное извлечение не заменяются догадкой модели.
- Почему здесь
- В прошлой версии склейки слов влияли на проверку цитат и отбор.
Узлы экспорта: Извлечь статью и дату; Проверить извлечённую статью
10Оценить и отобрать темы
Оценить прямую или смежную связь с долгами граждан. Удалить дубли и выбрать до пяти тем. Разнообразие источников — предпочтение; дословные цитаты не обязательны для показа идеи.
- Вход
- Прочитанные статьи, дата, редакционная оценка и ранее использованные URL.
- Выход
- Название, идея поста, категория, дата, источник и URL каждой темы.
- При сбое
- Не смешивать отсутствие подходящих тем с неполным чтением источников.
- Почему здесь
- Подробная проверка выбранного материала не должна уничтожать полезную подборку на раннем этапе.
Узлы экспорта: OpenAI — классифицировать темы; Выбрать темы без дублей
11Сохранить подборку и отправить карточки
Сохранить карточки общей подборки и доставить их обоим активным участникам. Журнал хранит отдельное подтверждение каждой копии; вторая доставка не запускает новый поиск.
- Вход
- Отобранные темы.
- Выход
- Карточки с кнопкой «Подготовить пост», записи о доставке.
- При сбое
- Повтор события не должен переслать подборку заново. Частичная доставка требует учёта уже отправленных карточек.
- Почему здесь
- Выбор должен относиться к сохранённой статье и исходному сообщению.
Узлы экспорта: Подготовить записи карточек; Отправить карточку; Сохранить доставку карточки
12Принять выбор новости
Проверить active, личный чат, доставленное message_id, срок карточки и состояние ready/offered. Общий атомарный claim по статье допускает один материал для обоих участников.
- Вход
- Новостная кнопка в зарегистрированной копии участника и действующая карточка.
- Выход
- Новостной черновик, связанный с карточкой и URL.
- При сбое
- Устаревшая подборка или уже выбранная карточка не запускает вторую обработку.
- Почему здесь
- Выбор темы начинает подготовку, но не даёт разрешения отправлять её в канал.
Узлы экспорта: Проверить новостную кнопку; Принять статью атомарно; Подготовить пост по сохранённой новости
13Подготовить подтверждённые факты
BFLNewsSelected041 формирует и проверяет 1–6 фактов по выбранному источнику: каждый до 700 единиц, с 1–3 существующими source_ids и совпадающим document_key.
- Вход
- Текст выбранной статьи, document_key и блоки источника.
- Выход
- Проверенный fact_pack или конкретная причина отказа.
- При сбое
- Сохранённый источник не разрешает додумывать факты. Для неподтверждённого вывода о действующей норме возвращается selected_primary_document_required.
- Почему здесь
- Отдельное судебное дело, мнение эксперта и действующая правовая норма требуют разных формулировок.
Узлы экспорта: Подготовить факты; OpenAI — факты выбранной темы; Итог фактов
14Составить текст и надписи обложки
Получить тело поста, cover_title, cover_subtitle и описание сцены. Проверить завершённость ответа и соответствие полей контракту.
- Вход
- Тема пользователя либо выбранные факты, редакционные правила, доступный бюджет.
- Выход
- Структурированный кандидат для проверки.
- При сбое
- Невалидный JSON, пустое поле и ответ модели не должны маскироваться общим успехом.
- Почему здесь
- Подпись, надписи и иллюстрация должны описывать один материал.
Узлы экспорта: Собрать текстовый запрос 0; OpenAI — написать текст; Извлечь готовый текст
15Собрать подпись и проверить длину
Составить body и суффикс со ссылкой. Полная подпись ограничена 1024 единицами UTF-16. Предпочтение 700–900 не является минимумом. Допускаются две попытки сокращения с повторной смысловой проверкой новости.
- Вход
- Тело текста и сохранённый canonical URL.
- Выход
- Нормализованный caption в допустимом бюджете.
- При сбое
- Редакционные заметки не являются hard_errors. После смысловой правки длину считать повторно.
- Почему здесь
- Ссылка занимает часть места; допустимый текст до правки может перестать помещаться после неё.
Узлы экспорта: Текст соответствует лимитам?; Сократить текст 1; Сократить текст 2
16Проверить смысл трёх полей
Сопоставить body, cover_title и cover_subtitle с фактами: числа, субъектов, отрицания, условия и атрибуцию. Допустимы до десяти issues, один цикл точечных правок и повторная проверка. Длинная смысловая правка может вернуть поток к сокращению.
- Вход
- caption_body, cover_title, cover_subtitle, факты и фрагменты источника.
- Выход
- Подтверждённый комплект текста либо конкретные issues.
- При сбое
- Сбой API и отсутствие ответа не равны pass=true. Не исправлять подпись, забыв старый заголовок на обложке.
- Почему здесь
- Смысл проверяется до затрат на иллюстрацию и до согласования.
Узлы экспорта: OpenAI — сверить подпись; Точечно исправить смысл; OpenAI — повторная смысловая проверка
17Создать или переиспользовать иллюстрацию
Сохранить исходную иллюстрацию, если нет распознанной явной просьбы заменить её. Иначе после проверок текста выполнить один запрос Images: PNG 1536×1024, quality=medium.
- Вход
- Проверенный текст, image_prompt и наличие сохранённой сцены.
- Выход
- PNG или ссылка на подтверждённый сохранённый результат операции.
- При сбое
- Неизвестный исход платного вызова блокирует слепой повтор.
- Почему здесь
- Правка одного предложения не должна автоматически создавать новую платную картинку.
Узлы экспорта: Есть сохранённый исходник?; Зарезервировать генерацию изображения; OpenAI — создать иллюстрацию
18Импортировать PNG
Передать raw bytes в POST /v1/images. Renderer проверяет тип, размеры и структуру, вычисляет SHA-256 и сохраняет объект.
- Вход
- Бинарный PNG из генератора.
- Выход
- image_id, sha256, размеры, asset_path и reused.
- При сбое
- Невалидный или слишком большой PNG отклоняется; идентификатор не выдумывается.
- Почему здесь
- Исходная сцена должна пережить перезапуск и использоваться в следующих версиях.
Узлы экспорта: Восстановить бинарный PNG; Сохранить PNG в renderer; Проверить импорт PNG
19Собрать фирменную обложку
Renderer 0.3.0 собирает обложку 1080×1080 по шаблону ff-square-0.1: логотип, шрифты и графика накладываются на сохранённый исходник. Название каталога v0.2 не определяет версию сервиса.
- Вход
- draft_id/version, image_id, заголовок, подзаголовок и версия шаблона.
- Выход
- PNG 1080 × 1080 и метаданные шаблона/объекта.
- При сбое
- Другое содержимое с тем же номером версии возвращает конфликт; нужен новый номер, а не перезапись.
- Почему здесь
- Изменение надписей не требует поручать генератору воспроизводить фирменный стиль.
Узлы экспорта: Подготовить рендер версии; Создать обложку версии; Проверить контракт обложки
20Получить обложку и доставить превью
Получить PNG, сверить сохранённый хеш и доставить одно общее фото с полной подписью и тремя кнопками каждому активному участнику. У каждого получателя отдельный результат в журнале.
- Вход
- Сохранённый asset_id, caption и кнопки текущей версии.
- Выход
- Подтверждённые message_id копий и общий photo file_id; первичная успешная копия сохраняется также в прежних полях.
- При сбое
- Неизвестная доставка требует выяснения; сообщение «Готово» не должно опережать подтверждённую отправку.
- Почему здесь
- Каждая кнопка связана с версией и сообщением, которое действительно получил этот участник.
Узлы экспорта: Загрузить PNG для превью; Прислать пост одним фото; Прислать исправленный пост прежним фото
21Сохранить готовность к согласованию
Проверить подтверждённые доставки, сохранить file_id и каноническое approval_message_id. Все копии учтены в журнале; awaiting_approval разрешает одно общее решение.
- Вход
- Подтверждённая доставка превью.
- Выход
- Активный комплект, связанный со своими кнопками.
- При сбое
- Сбой записи после отправки — отдельная ситуация; нельзя автоматически отправлять фото ещё раз.
- Почему здесь
- Публикация и защита старых кнопок используют сохранённые идентификаторы.
Узлы экспорта: Проверить фото с подписью; Сохранить согласование фото
22Запросить и применить доработку
Закрепить ForceReply за actor/chat/prompt участника, начавшего правку. Ответ другого участника не подходит. Применить точную замену или обычную редакцию; новая версия доставляется обоим, старая перестаёт разрешать действия.
- Вход
- Кнопка «Доработать», затем reply с замечаниями.
- Выход
- Новая версия и новое превью.
- При сбое
- Неоднозначная замена, старый reply или потеря смысла останавливают изменение без удаления прежнего комплекта.
- Почему здесь
- Пользователь одобряет каждую изменённую версию отдельно.
Узлы экспорта: Запросить замечания ответом; Подготовить новую версию; Применить точную замену
23Опубликовать одобренную версию
После настоящего нажатия любого активного участника атомарно захватить общую публикацию, отправить сохранённые caption и file_id и записать подтверждение Telegram. Повторная генерация не требуется.
- Вход
- Разрешённое нажатие «Опубликовать» под текущим превью.
- Выход
- Одна подтверждённая публикация и уведомление обоим участникам; старые клавиатуры снимаются.
- При сбое
- Старая версия, повторный клик или неизвестный исход отправки не должны порождать новый пост.
- Почему здесь
- Одобрение не разрешает переписывать текст или подменять изображение после проверки.
Узлы экспорта: Заблокировать повторную публикацию; Опубликовать фото с полным текстом; Проверить публикацию одного фото; Завершить публикацию с подписью; Общее решение → Завершить публикацию с подписью
24Отклонить черновик
Записать status=rejected и сохранить историю. Это не удаление черновика и не отмена выполняющейся генерации. Claim статьи автоматически не освобождается.
- Вход
- Кнопка «Отклонить» под разрешённой версией.
- Выход
- Общее состояние rejected и уведомления участникам; старые кнопки недействительны.
- При сбое
- Повторное решение или старая версия не должны менять актуальный комплект.
- Почему здесь
- Отказ от подготовки не равен удалению уже опубликованного сообщения.
Узлы экспорта: Сохранить отклонение; Подтвердить отклонение
25Продолжить сохранённую попытку
/retry для подходящего text_failed создаёт новую версию с сохранённым контекстом. /resume продолжает импорт, рендер или превью той же последней версии со статусом image_upload_failed, render_failed или preview_failed. Служебный BFLEditorPostResume339 отдельно продолжил прежнюю v1 по серверному доказательству остановки до запроса. Его одноразовое разрешение уже использовано.
- Вход
- /retry DRAFT_ID V [замечания] либо /resume DRAFT_ID V.
- Выход
- Продолжение до превью либо конкретный отказ/блокер.
- При сбое
- Unknown outcome, опубликованная или заменённая версия не допускают повтор. /retry первоначального обычного поста без пригодного родителя не является универсальным восстановлением.
- Почему здесь
- Отдельная исправленная служебная ветка не должна расходиться с обычным маршрутом.
Узлы экспорта: Подготовить восстановленную правку; Проверить безопасное восстановление; Продолжить сохранённый текст; Продолжить существующий BFL-content
26Сохранить ошибку и сообщить о ней
Сохранить известный бизнес-отказ в версии. Необработанное исключение передать в Error Workflow: audit связывает execution с участником, сервис пытается уведомить инициатора и владельца. Генерация автоматически не повторяется.
- Вход
- Исходное исключение, стадия, состояние внешнего запроса и сохранённые результаты.
- Выход
- Диагностируемая остановка; команда продолжения только при допустимом состоянии.
- При сбое
- Неизвестный внешний исход блокирует повтор. Отказ Telegram/сервиса или аварийное завершение процесса не гарантируют доставку сообщения об ошибке.
- Почему здесь
- Для восстановления нужно знать не только ошибку, но и то, что уже было выполнено.
Узлы экспорта: Сохранить ошибку текста; Сохранить ошибку рендера; Сохранить неопределённую публикацию; Обновить статус ошибки; Ошибка выполнения; Сообщить о техническом отказе