| @@ -282,8 +282,55 @@ | ||
| 282 | 282 | 'product_id' => $productMeta ? (int) $productMeta->value : null |
| 283 | 283 | ]; |
| 284 | 284 | } |
| 285 | 285 | |
| 286 | + /** | |
| 287 | + * Reconnect to the ticket's in-flight FluentBot turn and stream its buffered SSE | |
| 288 | + * to the browser. Used after a refresh or a conversation switch so a turn that | |
| 289 | + * is still running upstream renders live instead of appearing as an empty panel. | |
| 290 | + * | |
| 291 | + * Emits `idle` (via the upstream) when nothing is buffered, so the client can | |
| 292 | + * stop without special-casing. | |
| 293 | + */ | |
| 294 | + public function resumeChatStream(Request $request, $id) | |
| 295 | + { | |
| 296 | + $ticketId = intval($id); | |
| 297 | + $this->authorizeTicketAccess($ticketId); | |
| 298 | + | |
| 299 | + $meta = $this->getTicketMeta($ticketId, '_fluent_bot_chat_id'); | |
| 300 | + $chatId = $meta ? (string) $meta->value : ''; | |
| 301 | + | |
| 302 | + // Only ever resume a chat this ticket owns, and only a well-formed id — | |
| 303 | + // the id is interpolated into the upstream URL. | |
| 304 | + if (!$chatId || !preg_match(self::CHAT_ID_PATTERN, $chatId)) { | |
| 305 | + return $this->sendError(['message' => __('No conversation to resume.', 'fluent-support')], 404); | |
| 306 | + } | |
| 307 | + | |
| 308 | + $productMeta = $this->getTicketMeta($ticketId, '_fluent_bot_chat_product'); | |
| 309 | + $productId = $productMeta ? (int) $productMeta->value : $request->getSafe('product_id', 'intval'); | |
| 310 | + | |
| 311 | + // Prevent WordPress and PHP from flushing/compressing buffers on shutdown | |
| 312 | + remove_action('shutdown', 'wp_ob_end_flush_all', 1); | |
| 313 | + @ini_set('zlib.output_compression', 'Off'); | |
| 314 | + @ini_set('output_buffering', 'Off'); | |
| 315 | + @ini_set('output_handler', ''); | |
| 316 | + | |
| 317 | + // Clear all output buffers for raw SSE streaming | |
| 318 | + $maxLevels = 10; | |
| 319 | + while (ob_get_level() && $maxLevels-- > 0) { | |
| 320 | + @ob_end_clean(); | |
| 321 | + } | |
| 322 | + | |
| 323 | + header('Content-Type: text/event-stream'); | |
| 324 | + header('Cache-Control: no-cache'); | |
| 325 | + header('Connection: keep-alive'); | |
| 326 | + header('X-Accel-Buffering: no'); | |
| 327 | + | |
| 328 | + (new FluentBotService())->resumeChatStream($chatId, $productId); | |
| 329 | + | |
| 330 | + exit; | |
| 331 | + } | |
| 332 | + | |
| 286 | 333 | public function getChatMessages(Request $request, $id) |
| 287 | 334 | { |
| 288 | 335 | $ticketId = intval($id); |
| 289 | 336 | $this->authorizeTicketAccess($ticketId); |