feat: web, webhook and telegram channel drivers

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BCrwHHnGCB5XH968Nxokqw
This commit is contained in:
nikita.hohlov
2026-09-04 07:18:37 -03:00
parent 1b03fe0796
commit 1b0a559e3c
11 changed files with 487 additions and 0 deletions

View File

@@ -0,0 +1,70 @@
<?php
namespace App\Channels;
use App\Enums\Presence;
use App\Http\Controllers\Api\EventsController as Presenter;
use App\Models\Delivery;
use App\Models\Event;
use App\Models\User;
use Illuminate\Support\Facades\Http;
use Throwable;
/** Универсальный webhook. В нашем случае — Home Assistant. */
final class WebhookChannel implements ChannelDriver
{
public function __construct(private readonly int $presenceTimeout) {}
public function presence(User $user, array $config): Presence
{
try {
$response = Http::timeout($this->presenceTimeout)->acceptJson()->get($config['presence_url']);
if (! $response->successful()) {
return Presence::Unknown;
}
return $response->json('present') === true ? Presence::Present : Presence::Absent;
} catch (Throwable) {
return Presence::Unknown;
}
}
public function deliver(Event $event, User $user, array $config, Delivery $delivery): DeliveryOutcome
{
try {
$response = Http::timeout(5)->asJson()->post($config['deliver_url'], self::payload($event, $delivery));
if ($response->successful()) {
return DeliveryOutcome::ok();
}
return DeliveryOutcome::failed("HTTP {$response->status()}: ".mb_substr($response->body(), 0, 200));
} catch (Throwable $e) {
return DeliveryOutcome::failed($e->getMessage());
}
}
public function onDone(Event $event, Delivery $delivery): void {}
/** @return array<string,mixed> */
public static function payload(Event $event, Delivery $delivery): array
{
$presented = Presenter::present($event);
return [
'event' => [
'id' => $event->id,
'state' => $presented['state'],
'due_at' => $presented['due_at'],
'source' => $event->source->name,
'title' => $event->payload['title'],
'subtitle' => $event->payload['subtitle'] ?? null,
'deep_link' => $event->payload['deep_link'] ?? null,
'done_label' => $event->payload['done_label'] ?? 'Сделано',
],
'actions' => [
'ack' => route('action.ack', $delivery->action_token),
'done' => route('action.done', $delivery->action_token),
],
];
}
}