feat(bot): JivoBotClient — BOT_MESSAGE/INVITE_AGENT, dev-режим без URL

This commit is contained in:
Дмитрий
2026-07-02 20:58:24 +03:00
parent edbfd3e993
commit e2dfd22471
2 changed files with 100 additions and 0 deletions
+56
View File
@@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
namespace App\Services\Bot;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
/**
* Исходящие события в Jivo Bot API. outbound_url выдаёт Jivo письмом при
* подключении бота (протокол, О-2); пустой URL = dev/CI, событие только в лог.
* Формат событий Jivo Bot API: BOT_MESSAGE (текст клиенту), INVITE_AGENT
* (позвать живого оператора).
*/
class JivoBotClient
{
public function sendMessage(string $chatId, string $clientId, string $text): void
{
$this->post([
'event' => 'BOT_MESSAGE',
'id' => (string) Str::uuid(),
'chat_id' => $chatId,
'client_id' => $clientId,
'message' => ['type' => 'TEXT', 'text' => $text, 'timestamp' => now()->getTimestamp()],
]);
}
public function inviteAgent(string $chatId, string $clientId): void
{
$this->post([
'event' => 'INVITE_AGENT',
'id' => (string) Str::uuid(),
'chat_id' => $chatId,
'client_id' => $clientId,
]);
}
/** @param array<string, mixed> $payload */
private function post(array $payload): void
{
$url = (string) config('services.jivo_bot.outbound_url');
if ($url === '') {
Log::info('JivoBot outbound skipped (no outbound_url)', ['event' => $payload['event']]);
return;
}
try {
Http::timeout(5)->post($url, $payload)->throw();
} catch (\Throwable $e) {
Log::warning('JivoBot outbound failure', ['event' => $payload['event'], 'error' => $e->getMessage()]);
}
}
}
+44
View File
@@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
use App\Services\Bot\JivoBotClient;
use Illuminate\Support\Facades\Http;
uses(Tests\TestCase::class);
beforeEach(function () {
config()->set('services.jivo_bot.outbound_url', 'https://bot.jivosite.com/webhooks/prov-1/tok-1');
});
it('BOT_MESSAGE уходит с chat_id/client_id и текстом', function () {
Http::fake(['bot.jivosite.com/*' => Http::response(['ok' => true])]);
app(JivoBotClient::class)->sendMessage('chat-1', 'client-1', 'Проект — это…');
Http::assertSent(function ($request) {
return $request->url() === 'https://bot.jivosite.com/webhooks/prov-1/tok-1'
&& $request['event'] === 'BOT_MESSAGE'
&& $request['chat_id'] === 'chat-1'
&& $request['client_id'] === 'client-1'
&& $request['message']['type'] === 'TEXT'
&& $request['message']['text'] === 'Проект — это…';
});
});
it('INVITE_AGENT уходит без message', function () {
Http::fake(['bot.jivosite.com/*' => Http::response(['ok' => true])]);
app(JivoBotClient::class)->inviteAgent('chat-1', 'client-1');
Http::assertSent(fn ($r) => $r['event'] === 'INVITE_AGENT' && $r['chat_id'] === 'chat-1');
});
it('пустой outbound_url (dev/CI) → ничего не шлёт и не падает', function () {
config()->set('services.jivo_bot.outbound_url', '');
Http::fake();
app(JivoBotClient::class)->sendMessage('chat-1', 'client-1', 'x');
Http::assertNothingSent();
});