feat(chat): свой приём сообщений вместо вебхука Jivo
Заменяет JivoBotController+JivoBotClient на ChatController (POST /api/chat/message) и ProcessJivoMessageJob на ProcessChatMessageJob. Реплику клиента теперь пишет контроллер и возвращает её номер (message_id); джоба получает этот номер и берёт историю строго до него, не отправляя ответ во внешний Jivo — он ложится в bot_dialogs, откуда его заберёт своё окошко (Задача 3). Мозг бота не менялся. Спека: docs/superpowers/specs/2026-07-13-own-chat-widget-design.md §5
This commit is contained in:
@@ -2,12 +2,11 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Jobs\Bot\ProcessJivoMessageJob;
|
||||
use App\Jobs\Bot\ProcessChatMessageJob;
|
||||
use App\Mail\BotContactRequestMail;
|
||||
use App\Models\BotDialog;
|
||||
use App\Models\KnowledgeChunk;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
@@ -17,7 +16,6 @@ uses(RefreshDatabase::class);
|
||||
* не сидит в чате круглосуточно), а ПРОСИТ телефон и присылает контакт письмом.
|
||||
*/
|
||||
beforeEach(function () {
|
||||
config()->set('services.jivo_bot.outbound_url', 'https://bot.jivosite.com/webhooks/p/t');
|
||||
config()->set('services.support.email', 'support@liderra.ru');
|
||||
config()->set('services.yandexgpt', [
|
||||
'api_key' => 'k', 'folder_id' => 'f', 'model' => 'yandexgpt-lite/latest',
|
||||
@@ -29,27 +27,41 @@ beforeEach(function () {
|
||||
'topics' => 'создать проект', 'chunk_index' => 0,
|
||||
'content' => 'Проект — это заявка на поток клиентов.',
|
||||
]);
|
||||
Http::fake(['bot.jivosite.com/*' => Http::response(['ok' => true])]);
|
||||
});
|
||||
|
||||
it('эскалация просит телефон, а не зовёт оператора в чат', function () {
|
||||
/**
|
||||
* Мимикрирует то, что делает ChatController: пишет реплику клиента в журнал и
|
||||
* запускает джобу с её номером.
|
||||
*/
|
||||
function chatTurn(string $chatId, string $text): void
|
||||
{
|
||||
$row = BotDialog::create([
|
||||
'chat_id' => $chatId,
|
||||
'direction' => 'in',
|
||||
'message' => $text,
|
||||
'created_at' => now(),
|
||||
]);
|
||||
|
||||
(new ProcessChatMessageJob($chatId, (int) $row->id, $text))->handle();
|
||||
}
|
||||
|
||||
it('эскалация просит телефон', function () {
|
||||
Mail::fake();
|
||||
|
||||
(new ProcessJivoMessageJob('chat-c1', 'client-1', 'какой у меня баланс?'))->handle();
|
||||
chatTurn('chat-c1', 'какой у меня баланс?');
|
||||
|
||||
$out = BotDialog::where('direction', 'out')->firstOrFail();
|
||||
expect($out->escalated)->toBeTrue()
|
||||
->and($out->message)->toContain('номер телефона');
|
||||
|
||||
Http::assertNotSent(fn ($r) => ($r['event'] ?? '') === 'INVITE_AGENT');
|
||||
Mail::assertNothingSent();
|
||||
});
|
||||
|
||||
it('клиент прислал телефон после просьбы → письмо с контактом и вопросом уходит в поддержку', function () {
|
||||
Mail::fake();
|
||||
|
||||
(new ProcessJivoMessageJob('chat-c2', 'client-2', 'какой у меня баланс?'))->handle();
|
||||
(new ProcessJivoMessageJob('chat-c2', 'client-2', 'мой номер +7 916 123-45-67'))->handle();
|
||||
chatTurn('chat-c2', 'какой у меня баланс?');
|
||||
chatTurn('chat-c2', 'мой номер +7 916 123-45-67');
|
||||
|
||||
// Письмо уходит ЧЕРЕЗ ОЧЕРЕДЬ: почта не должна ронять ответ клиенту (прогон диалогами
|
||||
// 12.07.2026 — SMTP не ответил, задача упала, клиент с номером остался без ответа).
|
||||
@@ -69,7 +81,7 @@ it('телефон без просьбы бота — тоже принимае
|
||||
// Номер в чате = заявка на звонок. Не теряем его никогда.
|
||||
Mail::fake();
|
||||
|
||||
(new ProcessJivoMessageJob('chat-c3', 'client-3', 'звоните мне на +79161234567 если что'))->handle();
|
||||
chatTurn('chat-c3', 'звоните мне на +79161234567 если что');
|
||||
|
||||
Mail::assertQueued(BotContactRequestMail::class, fn ($m) => $m->phone === '79161234567');
|
||||
$last = BotDialog::where('direction', 'out')->latest('id')->firstOrFail();
|
||||
@@ -82,8 +94,8 @@ it('номер распознаётся в разных форматах', funct
|
||||
$variants = ['8 (916) 123-45-67', '+7-916-123-45-67', '79161234567'];
|
||||
foreach ($variants as $i => $phone) {
|
||||
$chat = 'chat-fmt-'.$i;
|
||||
(new ProcessJivoMessageJob($chat, 'c', 'какой у меня баланс?'))->handle();
|
||||
(new ProcessJivoMessageJob($chat, 'c', $phone))->handle();
|
||||
chatTurn($chat, 'какой у меня баланс?');
|
||||
chatTurn($chat, $phone);
|
||||
}
|
||||
|
||||
Mail::assertQueued(BotContactRequestMail::class, 3);
|
||||
|
||||
Reference in New Issue
Block a user