diff --git a/app/app/Services/Bot/AnswerGuard.php b/app/app/Services/Bot/AnswerGuard.php index 2c260550..8bb6f22e 100644 --- a/app/app/Services/Bot/AnswerGuard.php +++ b/app/app/Services/Bot/AnswerGuard.php @@ -65,9 +65,19 @@ class AnswerGuard // расписания приёма заявок нет (контрольный прогон 12.07.2026, D33). '/ночью[^.!?]{0,30}заяв\w*[^.!?]{0,25}(не\s+бывает|не\s+приход|нет)/iu', '/(поток|заявк\w*)[^.!?]{0,30}только\s+дн[её]м/iu', - // «Не берёт трубку — значит, не заинтересован, гонять смысла нет»: по - // zamena-zayavki.md такую заявку МЕНЯЮТ. Совет бота стоит клиенту денег (D04). - '/(не\s+бер[её]т\s+трубку|не\s+отвечает|автоответчик)[^.!?]{0,60}(не\s+заинтересован|смысла\s+нет|не\s+стоит|заб\w+|бесполезн)/iu', + // Срок ответа поддержки мы не обещаем: она работает 24/7 (podderzhka.md), а «ответят + // в течение рабочего дня / за пару часов» — выдумка и обещание, которое можно нарушить. + '/(отвеч\w+|ответ\w*|разбер\w+)[^.!?]{0,35}(в\s+течение\s+(рабочего\s+дня|часа|дня|суток)|за\s+(пару|несколько)\s+час|в\s+рабочие\s+дни|в\s+течение\s+\d)/iu', + // Тема замены/возврата — тонкая (правило владельца 13.07.2026): бот не обещает + // замену, но и НЕ РУБИТ дверью. «Замена не полагается», «деньги не возвращаются» — + // убираем: остаётся совет по работе с номером и путь к живому специалисту. + '/(замен\w*|возврат\w*|компенсаци\w*)[^.!?]{0,30}(не\s+полагается|не\s+предусмотрен\w*|невозможн\w*|мы\s+не\s+делаем)/iu', + '/деньги\s+(не\s+возвраща\w*|мы\s+не\s+верн\w*)/iu', + '/(возврата|замены)\s+(у\s+нас\s+)?нет/iu', + // «Не берёт трубку — значит, не заинтересован, гонять смысла нет»: пренебрежительно + // и неверно. Недозвон — обычная часть работы (правило владельца 13.07.2026). + // NB: «не стоит опускать руки» — ХОРОШИЙ совет, его резать нельзя. + '/(не\s+бер[её]т\s+трубку|не\s+отвечает|автоответчик)[^.!?]{0,60}(не\s+заинтересован|смысла\s+нет|бесполезн|заб(удьте|ей|ить))/iu', '/(не\s+заинтересован|смысла\s+нет\s+звонить)[^.!?]{0,40}(не\s+бер[её]т|не\s+отвеча)/iu', // «Работаем не со всеми нишами / есть ограничения по нишам» — прямое противоречие // nishi-i-pravila.md («Ограничений по нишам у нас нет») (D22). @@ -106,6 +116,7 @@ class AnswerGuard foreach ($sentences as $sentence) { if ($this->isBanned($sentence) || $this->priceTiedToNiche($sentence) + || $this->promisesReplacement($sentence) || $this->hasInventedPrice($sentence, $allowed)) { $cutSomething = true; @@ -150,6 +161,24 @@ class AnswerGuard return $hasPrice && $hasNiche && ! $isDenial; } + /** + * Обещание ЗАМЕНИТЬ заявку или ВЕРНУТЬ деньги. Правило владельца 13.07.2026: замены + * нет — недозвон браком не считается. Обещать её нельзя ни в какой формулировке: + * за такое обещание клиент придёт с претензией. Спорный случай — только «специалист + * разберёт индивидуально», без гарантий исхода. + * + * Исключение — отрицание («заменить не получится»): такое предложение оставляем, + * решение о тоне принимает статья, а не сторож. + */ + private function promisesReplacement(string $sentence): bool + { + $promise = preg_match('/(замен\w+|поменя\w+|верн[её]м|возврат\w*|компенсир\w+)/iu', $sentence) === 1; + $object = preg_match('/(заявк\w*|номер\w*|лид\w*|сделк\w*|деньг\w*|средств\w*)/iu', $sentence) === 1; + $denial = preg_match('/(не\s+замен|не\s+меня|не\s+верн|не\s+возвращ|замены\s+нет|без\s+замен|не\s+компенсир)/iu', $sentence) === 1; + + return $promise && $object && ! $denial; + } + private function isBanned(string $sentence): bool { foreach (self::BANNED as $pattern) { diff --git a/app/app/Services/Bot/BotAnswerService.php b/app/app/Services/Bot/BotAnswerService.php index f276702b..d220de30 100644 --- a/app/app/Services/Bot/BotAnswerService.php +++ b/app/app/Services/Bot/BotAnswerService.php @@ -228,6 +228,11 @@ class BotAnswerService городом, источником или «категорией» — такой зависимости нет. И не считай за клиента, сколько заявок выйдет на его сумму («на тысячу — от 2 до 20») — назови цену ступени и предложи посмотреть сетку в Биллинге. + — НЕ ОБЕЩАЙ ЗАМЕНУ ЗАЯВКИ И ВОЗВРАТ ДЕНЕГ. Недозвон («не берёт трубку») — обычная + часть работы, а не брак: замены за это нет. Но и грубить нельзя — не говори + «замена не полагается, деньги не вернём». Скажи мягко: это нормально, посоветуй + СМС и повторный звонок позже, а спорный случай предложи описать специалисту + на support@liderra.ru — он разбирает каждый индивидуально (без обещаний исхода). — НИКАКИХ ЗАПРЕТНЫХ НИШ. Ограничений по нишам нет: не выдумывай «направления, с которыми мы не работаем», и не говори «работаем не со всеми». — НЕ ПОДДАКИВАЙ. Клиент часто сам подсказывает ответ в вопросе («деньги diff --git a/app/resources/help/kak-rabotat-s-nomerom.md b/app/resources/help/kak-rabotat-s-nomerom.md index fca64e6a..13e1a06c 100644 --- a/app/resources/help/kak-rabotat-s-nomerom.md +++ b/app/resources/help/kak-rabotat-s-nomerom.md @@ -20,8 +20,11 @@ topics: что говорить когда звоню, скрипт продаж чем именно перебить конкурента (ценой, качеством, сервисом, сроком) — ваше решение и ваше конкурентное преимущество. -**Если номер недоступен или человек не берёт трубку** — не считайте это нормой: -напишите нам на `support@liderra.ru`, такую заявку мы можем заменить. +**Если человек не берёт трубку** — это обычное дело, а не брак: он за рулём, на встрече +или не отвечает на незнакомые номера. Отправьте СМС, перезвоните позже или в другое время +суток, напишите в мессенджер. Один пропущенный звонок — ещё не отказ. Если же номер выглядит +явно нерабочим или заявка вызывает вопросы, напишите на `support@liderra.ru` с номером +сделки — специалист разберёт случай индивидуально. **Скриптов продаж мы не даём** — они у каждого бизнеса свои. Но важное правило одно: чем быстрее вы вышли на связь, тем теплее контакт. diff --git a/app/resources/help/sdelki.md b/app/resources/help/sdelki.md index 377802d3..cdde04b3 100644 --- a/app/resources/help/sdelki.md +++ b/app/resources/help/sdelki.md @@ -42,9 +42,9 @@ topics: сделки, что такое сделка, что значит сде **Удалить сделку нельзя** — и это сделано специально: история заявок и списаний должна сходиться. Если заявка не пригодилась, поставьте ей статус **«Не реализовано»** — она -уйдёт из работы, но останется в истории и в отчётах. Если считаете, что заявка бракованная -(номер недоступен, не берёт трубку, не та тема) — напишите на `support@liderra.ru`, такую -заявку мы можем заменить. +уйдёт из работы, но останется в истории и в отчётах. Недозвон сам по себе браком не считается +(об этом — статья «Человек не берёт трубку»). Если же заявка вызывает вопросы, напишите +на `support@liderra.ru` с номером сделки — специалист разберёт случай индивидуально. Успешную заявку отмечайте статусом **«Сделка»** («клиент купил»). Все пять статусов — в статье «Статусы сделок»; выдуманных вроде «Успешная» или «Отказ» в портале нет. diff --git a/app/resources/help/zamena-zayavki.md b/app/resources/help/zamena-zayavki.md index 8f8a71fd..66b58f8e 100644 --- a/app/resources/help/zamena-zayavki.md +++ b/app/resources/help/zamena-zayavki.md @@ -1,19 +1,31 @@ --- -title: Заявка не дозвонилась или оказалась некачественной — заменим +title: Человек не берёт трубку или заявка кажется неудачной — что делать tour: support -topics: замена заявки, некачественная заявка, нецелевая заявка, номер недоступен, не берёт трубку, брак, плохая заявка, вернут ли деньги, компенсация, спорная заявка, жалоба на заявку +topics: замена заявки, заменят ли заявку, некачественная заявка, нецелевая заявка, номер недоступен, не берёт трубку, не дозвонился, недозвон, не отвечает, автоответчик, брак, плохая заявка, вернут ли деньги, компенсация, спорная заявка, жалоба на заявку --- -Если номер оказался недоступен, человек не берёт трубку или заявка явно не по теме — -**напишите нам на `support@liderra.ru`**. Такую заявку мы можем **заменить**. +**Не дозвонились с первого раза — это обычное дело, а не брак.** Человек за рулём, +на встрече или просто не берёт незнакомый номер. Это нормальная часть работы с тёплыми +контактами: опускать руки после одного гудка не стоит. -Что приложить к письму, чтобы разобрались быстро: +Что реально повышает результат: -- номер сделки (виден в «Сделках» и в выгрузке «Списаний»); -- что именно не так — недоступен, не берёт трубку, не тот интерес. +- **Сначала СМС, через час — звонок.** Человек успевает посмотреть ваше предложение, + и звонок уже не «холодный»: он понимает, кто вы и зачем. +- **Перезвоните позже или на следующий день,** в другое время суток. Один пропущенный + звонок — ещё не отказ. +- **Напишите в мессенджер** — многим так удобнее ответить. +- **Звоните быстро.** Чем меньше времени прошло с момента интереса, тем выше шанс. -Каждый такой случай разбирает **живой специалист** индивидуально — бот здесь не решает. -Замена делается по факту разбора; универсального «автовозврата» нет. +Заявка — это интерес человека, а не гарантированная продажа: результат зависит и от +скорости звонка, и от вашего разговора. Сколько людей в среднем выходят на разговор — +в статье «Средние показатели». + +**Спорная ситуация — напишите живому специалисту.** Если номер выглядит явно нерабочим +или заявка вызывает вопросы, отправьте письмо на `support@liderra.ru`: укажите номер +сделки (виден в «Сделках» и в разделе «Списания») и что именно вас смутило. Каждый такой +случай специалист разбирает **индивидуально** — бот здесь ничего не решает и заранее +ничего не обещает. Отдельно: **повторов не бывает** — номер, который мы уже передавали, не вернётся 30 дней (статья «Повторы номеров»). Если вам кажется, что повтор всё же случился, это тоже повод diff --git a/app/tests/Feature/Bot/DialogRunFixesTest.php b/app/tests/Feature/Bot/DialogRunFixesTest.php index 09483981..402f6904 100644 --- a/app/tests/Feature/Bot/DialogRunFixesTest.php +++ b/app/tests/Feature/Bot/DialogRunFixesTest.php @@ -268,14 +268,64 @@ it('«где посмотреть...» — это про портал, а не }); /** - * 🔴 Хвост прогона (D04): «не берёт трубку — значит, не заинтересован, гонять смысла нет». - * По zamena-zayavki.md такую заявку МЕНЯЮТ. Совет бота стоит клиенту денег. + * 🔴 Правило владельца 13.07.2026: заявку с недозвоном мы НЕ МЕНЯЕМ. Бот не имеет права + * обещать замену или возврат денег — за такое обещание потом придёт злой клиент. + * Но и грубить нельзя: недозвон — обычное дело, а не брак. */ -it('сторож режет совет «не берёт трубку — забей»', function () { +it('сторож не даёт обещать замену заявки и возврат денег', function () { + $guard = app(AnswerGuard::class); + $context = 'Спорный случай разбирает специалист индивидуально.'; + + expect($guard->clean('Такую заявку мы заменим. Напишите на support@liderra.ru.', $context)) + ->toBe('Напишите на support@liderra.ru.'); + + expect($guard->clean('Мы поменяем этот номер бесплатно. Укажите номер сделки.', $context)) + ->toBe('Укажите номер сделки.'); + + expect($guard->clean('Вернём деньги за эту заявку. Укажите номер сделки.', $context)) + ->toBe('Укажите номер сделки.'); +}); + +/** + * Правило владельца 13.07.2026: тема тонкая — сглаживать. Бот не обещает замену, + * но и не рубит «замена не полагается, деньги не возвращаются»: остаётся совет + * и путь к живому специалисту. + */ +it('сторож убирает жёсткий отказ «замена не полагается / деньги не возвращаются»', function () { + $guard = app(AnswerGuard::class); + $context = 'Спорный случай разбирает специалист индивидуально.'; + + expect($guard->clean('Замена за недозвон не полагается. Отправьте СМС и перезвоните позже.', $context)) + ->toBe('Отправьте СМС и перезвоните позже.'); + + expect($guard->clean('Деньги не возвращаются. Напишите на support@liderra.ru.', $context)) + ->toBe('Напишите на support@liderra.ru.'); +}); + +/** + * Поддержка работает 24/7 (podderzhka.md). «Ответят в течение рабочего дня» / «за пару + * часов» — выдуманный срок, которого мы не обещали. + */ +it('сторож режет выдуманный срок ответа поддержки', function () { $guard = app(AnswerGuard::class); - $text = 'Если не берёт трубку — значит, не заинтересован, гонять смысла нет. Напишите в поддержку.'; - expect($guard->clean($text, 'Заявку можно заменить.'))->toBe('Напишите в поддержку.'); + $text = 'Обычно специалист отвечает в течение рабочего дня. Напишите на support@liderra.ru.'; + expect($guard->clean($text, 'Поддержка работает круглосуточно.')) + ->toBe('Напишите на support@liderra.ru.'); +}); + +it('сторож режет пренебрежительный совет «не берёт трубку — забей»', function () { + $guard = app(AnswerGuard::class); + + $text = 'Если не берёт трубку — значит, не заинтересован, гонять смысла нет. Отправьте СМС.'; + expect($guard->clean($text, 'Недозвон — обычное дело.'))->toBe('Отправьте СМС.'); +}); + +it('сторож НЕ режет добрый совет про недозвон', function () { + $guard = app(AnswerGuard::class); + + $text = 'Если человек не берёт трубку, не стоит опускать руки: отправьте СМС и перезвоните позже.'; + expect($guard->clean($text, 'Недозвон — обычное дело, а не брак.'))->toBe($text); }); /** diff --git a/app/tests/Feature/Bot/JudgeFindingsTest.php b/app/tests/Feature/Bot/JudgeFindingsTest.php index 97e9933e..6ca62deb 100644 --- a/app/tests/Feature/Bot/JudgeFindingsTest.php +++ b/app/tests/Feature/Bot/JudgeFindingsTest.php @@ -115,10 +115,18 @@ it('на старте не требует пополнения — подаро } }); -it('«верните деньги за заявку» — статья про замену находится', function () { +/** + * Правило владельца 13.07.2026: замены заявки НЕТ (недозвон браком не считается). + * Прежний текст статьи обещал «заменим» — это была ошибка, и бот её повторял клиентам. + */ +it('«верните деньги за заявку» — находится статья про недозвон, и она НЕ обещает замену', function () { $hits = app(KnowledgeSearch::class)->search('верните деньги за плохую заявку', 3); - expect(collect($hits)->pluck('title')->implode(' | '))->toContain('заменим'); + expect(collect($hits)->pluck('title')->implode(' | '))->toContain('не берёт трубку'); + + $text = collect($hits)->pluck('content')->implode(' '); + expect($text)->not->toContain('заменим') + ->and($text)->not->toContain('можем заменить'); }); it('ночью и после лимита — отвечает по статье, без выдумок', function () { diff --git a/app/tests/Feature/Bot/MassRunFixesTest.php b/app/tests/Feature/Bot/MassRunFixesTest.php index 7211d5f0..b7c5b8d3 100644 --- a/app/tests/Feature/Bot/MassRunFixesTest.php +++ b/app/tests/Feature/Bot/MassRunFixesTest.php @@ -132,14 +132,21 @@ it('знает про повторы номеров: один номер не в } }); -it('знает, что делать, если номер недоступен — заменим по письму в поддержку', function () { +/** + * Правило владельца 13.07.2026: недозвон — обычная часть работы, замены за него НЕТ. + * Раньше статья обещала «заменим» — бот повторял это клиентам, а компания не меняет. + * Отвечаем мягко: совет по работе с номером + спорный случай разбирает специалист. + */ +it('на «не берёт трубку» даёт совет и путь к специалисту, а не обещание замены', function () { $hits = app(KnowledgeSearch::class)->search('а если номер недоступен или не берёт трубку', 3); expect($hits)->not->toBeEmpty(); $text = collect($hits)->pluck('content')->implode(' '); - expect($text)->toContain('замен') - ->and($text)->toContain('support@liderra.ru'); + expect($text)->toContain('support@liderra.ru') + ->and($text)->toContain('обычное дело') + ->and($text)->not->toContain('заменим') + ->and($text)->not->toContain('можем заменить'); }); it('знает про вывод остатка и отсутствие рассрочки', function () {