9ef688fa8d27bd65efaefd1b9d528f9b400a54be
6 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
32df332901 |
fix(телеграм-реклама): заголовок объявления для рекламы сайта — кампания больше не встаёт
Приёмка глазами вскрыла: кабинет МТС требует «Заголовок объявления» (до 40 знаков), когда в объявлении ссылка на САЙТ, а не на телеграм-канал. Робот про это поле не знал, «Продолжить» молча не срабатывало, кампания вставала на шаге «Объявление» — в бою уже ПОСЛЕ списания денег. Проверено живьём: 2234454 (сайт — встала) против 2234462 (канал — дошла до подтверждения) и 2234490 (сайт с заголовком — дошла). Портал спрашивает заголовок заранее, на создании черновика: обязателен только для не-телеграмной ссылки (App\Support\TelegramLink), колонка ad_headline varchar(40), поле на экране появляется по той же развилке. Робот заполняет его в кабинете. Три ловушки, добытые живыми прогонами (описаны в коде): - поле дорисовывается в ОТВЕТ на ссылку, с задержкой — надо ждать, а не спрашивать; - под описание подходит несколько элементов — нужен .first(); - серая надпись внутри поля НЕ placeholder, а нарисованная подпись: поиск по атрибуту давал ноль совпадений при видимом на снимке поле. Опознаём по видимой надписи. Тесты: робот 130/130, ClientTg 250/250, экран 23/23. Полный прогон бэкенда — те же 13 падающих классов до и после правки (ни одного в телеграм-части). В baseline статанализа добавлен известный ложный класс Pest для нового файла тестов. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
6f469fb13d |
feat(телеграм-реклама): деньги на общем балансе + чистка СМС-фантазий + медиа объявления
Денежная модель (куски 1-2): реклама в Телеграме (МТС Маркетолог, робот-в-браузере)
оплачивается с ОБЩЕГО баланса тенанта (как СМС), наценка 40%. Списание при запуске,
возврат при отказе модерации/сбое/отмене (сальдо-идемпотентность tg_ad_charge/tg_ad_refund).
Старый рекламный кошелёк-заморозка убран из потока кампаний (таблицы ad_wallets НЕ удалены).
Новый TelegramCampaignChargeService (зеркало SmsChargeService); F5 money-safety сохранён
(сбой с mts_campaign_id → needs_review без возврата). Уборщик добивает зависшие queued с возвратом.
Чистка СМС-наследия (аудит модуля построчно 5 проверяющими): модуль ресейлит ПОКАЗЫ рекламы,
а не рассылку сообщений — вырезаны выдуманные сущности, механически скопированные из СМС:
- «имя/бренд отправителя» + помесячная абонплата (в телеге МТС имени отправителя нет):
ChargeTgNameFeeJob, TelegramSenderService, SenderController, модели Sender/Setting,
фронт-панель TelegramSenderPanel, админ-карточка, updateTgSettings, тип tg_name_fee.
Таблицы client_tg_senders/_settings дропнуты.
- мёртвые таблицы client_tg_messages (доставка по номеру) и client_tg_templates (шаблоны
сообщений) — без модели/использования, дропнуты (миграция 000017, create-миграции удалены).
- client_tg_optouts переосмыслен как ручной список «не показывать этим номерам» (не «отписки»).
- лексика рассылки → рекламы: «Авторассылка»→«Авто-реклама», «кошелёк/заморожено»→«общий баланс».
Медиа объявления (реальный пробел показов): новый POST /api/telegram/campaigns/{id}/media
(картинка/видео к черновику, png/jpg/gif/mp4 ≤50 МБ, замена удаляет старый файл) + поле
загрузки в форме кабинета. Робот уже принимал media_path (RunTelegramCampaignJob → mediaFile
→ cabinet.js fillAdMedia) — не хватало только приёма файла от клиента.
Схема: db/CHANGELOG_schema.md v8.94 (CHECK += tg_ad_charge/tg_ad_refund) + v8.95 (дроп
СМС-таблиц, tg_name_fee убран). Тесты зелёные поштучно (квирк партиций — гонять по одному):
Schema/Models/AdminTg/CampaignCharge/CampaignApi/Audience/Media; фронт advertising-telegram-view
19 + admin-tg/auto-rule 24. pint/deptrac(0) чисто; larastan исключён (Pest-$this шум). Робот
bots/mts-telegram-ads НЕ трогали.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
||
|
|
aa4b482d45 |
feat(телеграм-реклама): Этап 5 — «Своё имя» + «Авторассылка» из кабинета + предохранители трат
Закрывает Этап 5 плана docs/superpowers/plans/2026-07-27-telegram-module-hardening.md.
Клиент управляет именем отправителя и авторассылкой сам из кабинета; авто не тратит
без денег и сверх дневного лимита. TDD, весь бэкенд+фронт зелёный.
## 5.1 — предохранители авторассылки
Накопитель в БОЮ перед постановкой пачки проверяет: смета умещается И в свободный
остаток кошелька (balance−frozen), И в дневной лимит правила за вычетом трат за
сегодня. Не прошла — держим черновиком + Log::info('client_tg.auto_skipped', reason).
Дефолт daily_limit_rub=0 → авто выключено (само денег не потратит). В песочнице гейта
нет (деньги не трогаются, как ручной launch). Новые колонки client_tg_auto_rule:
daily_limit_rub, spent_today_rub, spent_date (счётчик за день, сброс при смене даты).
## 5.2 — клиентское API имени (SenderController)
GET /sender (статус + остаток грейса), POST /sender (завести), /sender/disable,
/sender/enable (suspended→active, идемпотентно по периоду — без двойной оплаты в месяц;
проверка средств ДО списания). Логика в TelegramSenderService (enableSender+snapshot),
контроллер тонкий. Всё скоуп тенантом.
## 5.3 — экран имени (TelegramSenderPanel.vue)
Самодостаточная панель: статус имени человеческими словами, дата оплаты, остаток грейса
при долге; suspended → «Отключено за долг» + «Включить»; нет имени → форма «Завести имя».
telegram.ts: fetchSender/createSender/disableSender/enableSender.
## 5.4 — экран авторассылки + API (AutoRuleController)
GET/PUT /api/telegram/auto-rule (вкл/выкл, объявление, порог, бюджет, дневной лимит).
Порог клиентский — новая колонка client_tg_auto_rule.batch_threshold (NULL → дефолт
конфига 367; ниже 367 API не даёт — минимум МТС). TelegramAutoRulePanel.vue: тумблер +
поля порога/бюджета/лимита. В списке кампаний авто-кампании (created_by=null) помечены
чипом «авто».
Обе панели встроены в AdvertisingTelegramView.
## Схема
Две аддитивные миграции на существующую таблицу client_tg_auto_rule (000014 daily_limit
+ счётчик за день; 000015 batch_threshold). RLS/GRANT не тронуты (табличный GRANT
покрывает новые колонки). Записи db/CHANGELOG_schema.md v8.91/v8.92; обе прогнаны через
rls-reviewer — CLEAN.
## Приёмка
TDD. Бэкенд ClientTg 177/177; phpstan/pint/deptrac чисто. Фронт полный набор
212 файлов/1538 тестов зелёные; vue-tsc+ESLint по нашим файлам чисто. Проверено вживую
в браузере (клиент demo, песочница): обе панели читают и пишут — правило сохраняется
точь-в-точь (порог/бюджет/лимит), имя заводится (pending, деньги не тронуты).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
||
|
|
9b9055753b |
feat(телеграм-реклама): Этап 4 — клиентский опыт + причёсывание экрана под домашний стиль
Закрывает фронтенд Этапа 4 плана docs/superpowers/plans/2026-07-27-telegram-module-hardening.md (бэкенд 4.3 — уведомление об одобрении — сделан в Этапе 3). Чистый Vue+Vitest, TDD. ## 4.1 — смета и охват ДО запуска Разделены create/launch в UI: кнопка «Рассчитать» создаёт черновик (createTelegram) и показывает прогноз стоимости + охват; «Запустить» доступна только после расчёта. Любая правка формы сбрасывает расчёт (watch), чтобы не запустить по устаревшим данным. ## 4.2 — автообновление статуса Пока есть «живые» кампании (queued/running/moderating) — экран сам зовёт fetchTelegram по интервалу (15с), setInterval со снятием в onUnmounted; вне живых статусов не опрашивает. ## 4.4 — экран отказа: пересдача + пустая причина У rejected-кампании кнопка «Исправить и пересдать» → инлайн-форма правки (текст/ссылка/ОРД + опц. документ модератору) → новый resubmitTelegram в telegram.ts (multipart POST .../resubmit). При пустой status_reason — единая заглушка REASON_PLACEHOLDER (действенная, без «круга»). В интерфейс TelegramCampaign добавлено moderator_file_path. ## 4.5 — подсказки У moderating — «проверка ~4 часа»; пока песочница — метка «песочница» на каждой кампании. ## Причёсывание под домашний стиль кабинета (осмотр вживую 28.07) Экран приведён к виду соседних клиентских экранов (эталон DashboardView): обёртка v-container fluid pa-6 + scoped max-width:1100px (были прижаты к краям во всю ширину); крупный заголовок + строка-описание; поля density=compact variant=outlined (была «рыхлая» форма); контент собран в карточки «Новая кампания» / «Мои кампании» (была «полосатая» зона). Палитра Forest не тронута. STATUS_LABELS дополнен moderating/needs_review/cancelled. Починена мелочь-логика: «Рассчитать»/«Запустить» больше не активны с пустым списком номеров в режиме «Свой список». Миграция не нужна (moderator_file_path добавлена в Этапе 3; новые ярлыки статусов — без БД). TDD. Приёмка (моя область): фронт-тесты Телеграма 22/22; полный фронт-набор 210 файлов / 1529 зелёные; vue-tsc + ESLint по изменённым файлам чисто. Проверено вживую в браузере (клиент demo): двухшаговые кнопки, скрытие/показ полей по аудитории, гейт по номерам. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
cc985e07de |
feat(телеграм-реклама): экран «реклама отклонена» + закалка робота против глюков МТС
Клиентский модуль «Реклама в Телеграме по своей базе» (робот-в-браузере, МТС Маркетолог). Задача 5.2 — обратная связь по отказу: - миграция client_tg_campaigns.status_reason (varchar 500, nullable; RLS без изменений — колонка на существующей таблице), запись v8.88 в CHANGELOG_schema - Campaign: status_reason в fillable - NotificationService::notifyTelegramCampaignRejected — in-app уведомление всем активным пользователям тенанта без pref-гейта (важное операционное сообщение доходит всегда) - RunTelegramCampaignJob: при провале робота сохраняет причину в status_reason и шлёт уведомление (Tenant грузится внутри tenantTx для RLS, notify — после транзакции) - фронт: telegram.ts (+status_reason), AdvertisingTelegramView показывает причину отказа/ошибки - тесты: RejectNotifyTest (4 Pest) + 2 Vitest на экран Закалка робота против частых глюков кабинета МТС: - browser.js: gotoStable() — терпеливая загрузка с перезагрузкой (кабинет виснет на пустой крутилке) - session.js: isLoggedIn через gotoStable — больше нет ложного «вход слетел» - cabinet.js: знакомство пропускается если его нет (только для новичков); оферта по #isOfferAccepted; осознан шаг /payment (денежная развилка «оплатить/без оплаты» — Сессия 6) Живая разведка кабинета зафиксирована в FLOW-FINDINGS.md (селекторы, шаг /payment, поле файла модератору). Проверки: Pest ClientTg 93/93, Vitest экрана 7/7, node-тесты робота 20/20. Робот в тестах замокан, тесты на liderra_testing. Реальных ПДн нет (номера фейковые 7999…). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
0c9642f3d0 |
feat(телеграм-модуль): клиентский экран + ручной запуск в песочнице — API, telegram.ts, экран, сквозной сценарий
Сессия 3 плана docs/superpowers/plans/2026-07-27-client-telegram-ads-module.md. Клиент сам собирает объявление + аудиторию и жмёт «Запустить»; робот кабинета МТС (в тестах замокан) доводит до черновика. Всё в песочнице: деньги/живой запуск выключены. - Клиентский API (Api\ClientTg\CampaignController + маршруты /api/telegram/*): создание (store) и запуск (launch) РАЗДЕЛЕНЫ. store создаёт ЧЕРНОВИК со сметой и числом кандидатов (деньги/робот не трогаются); launch draft→queued, в бою бронирует лимит на объявление (budget_cap_rub) и ставит RunTelegramCampaignJob. Нехватка денег → 409 (кампания остаётся черновиком); повторный запуск не-черновика → 422; изоляция тенантов (чужой show → 404). Валидация текст/ссылка/аудитория/бюджет; deals без срока → 422. - Фронт-API telegram.ts (зеркало client-sms.ts, уже): fetch/create/get/launch, cookie-сессия. - Экран AdvertisingTelegramView.vue (/advertising/telegram): форма (объявление, ссылка, 3 способа аудитории, лимит), кнопка «Запустить» (create→launch), баннер песочницы, список кампаний с человеческим ярлыком статуса, сообщение при нехватке денег. - Канал «Реклама Телеграм» АКТИВИРОВАН: advertisingChannels.ts route, провод в AppSidebar и AppMoreDrawer (route → реальный экран, остальные каналы — прежняя заглушка), маршрут в router. Тесты каналов обновлены (телеграм больше не заглушка). - Ручной сквозной сценарий (песочница): создать → запустить → джоб (робот замокан, draft) → draft_ready + matched виден в API. Отличие от СМС-близнеца: отдельного эндпоинта «предпросчёт до создания» нет — смету и кандидатов клиент видит в самом черновике (создание черновик денег не тратит). Проверки: 60/60 Pest ClientTg зелёные (+10 новых), 1512 Vitest зелёные (0 регрессий), phpstan 0 по своему коду (level 5), pint чисто, deptrac 0 нарушений. Робот в тестах замокан (кабинет МТС не тронут). Синтетические номера 7999… — реальных нет. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |