diff --git a/bots/mts-telegram-ads/src/stop-plan.js b/bots/mts-telegram-ads/src/stop-plan.js new file mode 100644 index 00000000..2282b013 --- /dev/null +++ b/bots/mts-telegram-ads/src/stop-plan.js @@ -0,0 +1,41 @@ +// Решения остановки, которые можно проверить без браузера. Отдельным файлом нарочно: сам +// поход в кабинет тестом не поймать, а «что жать» и «нажалось ли» — обычная логика, +// и именно в ней живут опасные случаи. +// +// 🔴 Нажатие необратимо и делается в БОЕВОМ кабинете за живые деньги: у завершённой +// кампании кнопок запуска больше нет (замер 08.08.2026, кампания 2237821). + +/** + * Подпись кнопки завершения в ряду кампании. Полное совпадение, без «похожих»: + * «Завершить кампанию досрочно» — это уже другая кнопка, и жать её вслепую нельзя. + */ +export const NADPIS_ZAVERSHIT = /^завершить$/i; + +/** Белый список кнопок окна подтверждения. Рядом в кабинете живут кнопки удаления. */ +const PODTVERZHDENIE = [/^завершить$/i, /^да$/i, /^подтвердить$/i, /^ок$/i]; + +/** + * Какую кнопку окна подтверждения жать. null — окно незнакомое, не трогаем ничего: + * лучше отчитаться отказом, чем удалить кампанию клиента. + */ +export function vybratPodtverzhdenie(nadpisi) { + if (!Array.isArray(nadpisi)) return null; + + for (const obrazec of PODTVERZHDENIE) { + const nayden = nadpisi.find((t) => obrazec.test(String(t ?? '').trim())); + if (nayden !== undefined) return nayden; + } + + return null; +} + +/** + * Кабинет подтвердил остановку? Доказательство — только смена слова в ряду: + * «Останавливается» (идёт остановка) или «Завершена» (уже закончилась). + * + * 🔴 «Наверное получилось» не считается. Отчитаться успехом, не проверив, значит + * сказать клиенту «остановлено» о рекламе, которая продолжает крутиться за его деньги. + */ +export function ostanovkaPodtverzhdena(status) { + return status === 'stopping' || status === 'completed'; +} diff --git a/bots/mts-telegram-ads/test/stop-plan.test.mjs b/bots/mts-telegram-ads/test/stop-plan.test.mjs new file mode 100644 index 00000000..ccd179d3 --- /dev/null +++ b/bots/mts-telegram-ads/test/stop-plan.test.mjs @@ -0,0 +1,31 @@ +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import { vybratPodtverzhdenie, ostanovkaPodtverzhdena, NADPIS_ZAVERSHIT } from '../src/stop-plan.js'; + +// 🔴 Нажатие необратимо и делается в БОЕВОМ кабинете за живые деньги. Рядом в кабинете +// живут кнопки удаления — ничего «похожего» вслепую не жмём. + +test('в окне подтверждения жмём только кнопку из белого списка', () => { + assert.equal(vybratPodtverzhdenie(['Отмена', 'Завершить']), 'Завершить'); + assert.equal(vybratPodtverzhdenie(['Нет', 'Да']), 'Да'); +}); + +test('🔴 похожей кнопки мало — незнакомое окно не трогаем', () => { + assert.equal(vybratPodtverzhdenie(['Удалить кампанию', 'Отмена']), null); + assert.equal(vybratPodtverzhdenie([]), null); + assert.equal(vybratPodtverzhdenie(null), null); +}); + +test('подпись кнопки завершения — полное слово, а не «похожее»', () => { + assert.match('Завершить', NADPIS_ZAVERSHIT); + assert.equal(NADPIS_ZAVERSHIT.test('Завершить кампанию досрочно'), false); + assert.equal(NADPIS_ZAVERSHIT.test('Посмотреть настройки'), false); +}); + +test('🔴 доказательством остановки считаются ровно два слова кабинета', () => { + assert.equal(ostanovkaPodtverzhdena('stopping'), true); + assert.equal(ostanovkaPodtverzhdena('completed'), true); + // Слово не сменилось — значит не нажалось. «Наверное получилось» не считается. + assert.equal(ostanovkaPodtverzhdena('approved'), false); + assert.equal(ostanovkaPodtverzhdena(null), false); +});