event = $event; $this->logger = $logger; } public function bindRoom(string $roomToken): void { $this->roomToken = $roomToken; } public function send(string $message, int|bool $replyTo = false, bool $silent = false): bool { if (trim($message) === '') { return true; // Never queue empty answers - Talk rejects them. } try { // Reply targeting: pass the concrete message id when we have one so // the answer is threaded under it, matching the webhook transport. $replyTarget = (is_int($replyTo) && $replyTo > 0) ? $replyTo : false; $this->event->addAnswer($message, $replyTarget, $silent); return true; } catch (\Throwable $e) { $this->logger->error('Failed to queue native Talk answer', [ 'room_token' => $this->roomToken, 'message_length' => strlen($message), 'exception' => $e, ]); return false; } } public function react(string $emoji): void { try { $this->event->addReaction($emoji); } catch (\Throwable $e) { $this->logger->error('Failed to queue native Talk reaction', [ 'room_token' => $this->roomToken, 'exception' => $e, ]); } } public function supportsStreaming(): bool { // Talk posts answers after the invocation completes, so incremental // streaming is not possible - the pipeline coalesces to final replies. return false; } }