PluginProbe
Fluent Support – Helpdesk & Customer Support Ticket System / 2.1.1
Fluent Support – Helpdesk & Customer Support Ticket System v2.1.1
2.4.0 2.3.2 2.3.1 2.3.0 2.2.1 2.2.0 trunk 1.10.0 1.10.1 1.10.2 1.10.3 1.10.4 1.10.5 1.4.0 1.4.1 1.4.2 1.4.5 1.4.6 1.4.7 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 All 68 releases
fluent-support / app / Services / Integrations / FluentBot / FluentBotAPI.php

FluentBotAPI.php in Fluent Support – Helpdesk & Customer Support Ticket System 2.1.1, at app/Services/Integrations/FluentBot/FluentBotAPI.php

183 lines 6.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentSupport\App\Services\Integrations\FluentBot;
4
5 use WP_Error;
6
7 class FluentBotAPI
8 {
9 protected $apiUrl;
10
11 public function __construct(string $apiUrl)
12 {
13 $this->apiUrl = $apiUrl;
14 }
15
16 public function makeRequest(int $ticketId, $prompt, array $args = [])
17 {
18 $response = $this->sendRequest($args);
19
20 if (is_wp_error($response)) {
21 $message = $response->get_error_message();
22 $code = $response->get_error_code();
23 return new \WP_Error($code, $message);
24 }
25
26 $responseBody = json_decode(wp_remote_retrieve_body($response), true) ?? [];
27
28 if (!$responseBody || !is_array($responseBody)) {
29 return new \WP_Error('fluent_bot_error', __('Invalid or empty response from API', 'fluent-support'));
30 }
31
32 if (!empty($responseBody['error'])) {
33 $message = $responseBody['error']['message'] ?? __('Unknown error occurred', 'fluent-support');
34 return new \WP_Error('fluent_bot_error', $message);
35 }
36
37 $statusCode = wp_remote_retrieve_response_code($response);
38
39 if ($statusCode !== 200) {
40 $error = $responseBody['message'] ?? __('Something went wrong.', 'fluent-support');
41 return new \WP_Error($statusCode, $error);
42 }
43
44 $content = $responseBody['response'] ?? '';
45
46 if (empty($content)) {
47 return new \WP_Error('fluent_bot_error', __('No AI response found in the API response.', 'fluent-support'));
48 }
49
50 $totalTokens = $responseBody['token_usage']['total_tokens'] ?? $responseBody['totalTokens'] ?? 0;
51 do_action('fluent_support/ai_response_success', $ticketId, $prompt, $totalTokens, "Fluent Bot");
52
53 // Return both content and chat_id if available
54 return [
55 'content' => $content,
56 'chat_id' => $responseBody['chat_id'] ?? null
57 ];
58 }
59
60 public function makeStreamRequest(int $ticketId, $prompt, array $args = [])
61 {
62 $timeout = apply_filters('fs_ai_request_timeout', 120);
63
64 // Use cURL for streaming
65 // Note: Using cURL here because WordPress HTTP API doesn't support streaming SSE responses
66 // phpcs:disable WordPress.WP.AlternativeFunctions.curl_curl_init
67 // phpcs:disable WordPress.WP.AlternativeFunctions.curl_curl_setopt
68 // phpcs:disable WordPress.WP.AlternativeFunctions.curl_curl_exec
69 // phpcs:disable WordPress.WP.AlternativeFunctions.curl_curl_close
70 // phpcs:disable WordPress.WP.AlternativeFunctions.curl_curl_getinfo
71 // phpcs:disable WordPress.WP.AlternativeFunctions.curl_curl_error
72 // PluginCheck:ignoreFile
73 $ch = curl_init();
74 curl_setopt($ch, CURLOPT_URL, $this->apiUrl);
75 curl_setopt($ch, CURLOPT_POST, true);
76 curl_setopt($ch, CURLOPT_POSTFIELDS, wp_json_encode($args));
77 curl_setopt($ch, CURLOPT_HTTPHEADER, [
78 'Content-Type: application/json',
79 ]);
80
81 $buffer = '';
82 $conversationId = null;
83
84 curl_setopt($ch, CURLOPT_WRITEFUNCTION, function($ch, $data) use (&$buffer, &$conversationId) {
85 $buffer .= $data;
86
87 // Process complete SSE events from the AI API
88 $events = explode("\n\n", $buffer);
89 $buffer = array_pop($events); // Keep incomplete event in buffer
90
91 foreach ($events as $event) {
92 if (trim($event)) {
93 $lines = explode("\n", $event);
94 $eventType = '';
95 $eventId = '';
96 $eventDataLines = [];
97
98 foreach ($lines as $line) {
99 if (strpos($line, 'event: ') === 0) {
100 $eventType = trim((string)substr($line, 7));
101 } elseif (strpos($line, 'id: ') === 0) {
102 $eventId = trim((string)substr($line, 4));
103 } elseif (strpos($line, 'data: ') === 0) {
104 $eventDataLines[] = (string)substr($line, 6);
105 }
106 }
107
108 // Forward the event to the browser with proper formatting
109 if ($eventType) {
110 echo "event: ".esc_html($eventType)."\n";
111
112 // Include ID if present
113 if ($eventId !== '') {
114 echo "id: ".esc_html($eventId)."\n";
115 }
116
117 // Handle multiple data lines properly
118 if (!empty($eventDataLines)) {
119 foreach ($eventDataLines as $dataLine) {
120 echo "data: ".esc_html($dataLine)."\n";
121 }
122 } else {
123 echo "data: \n";
124 }
125
126 echo "\n";
127
128 // Store chat_id for later use
129 if ($eventType === 'chat_id' && !empty($eventDataLines)) {
130 $conversationId = $eventDataLines[0];
131 }
132
133 flush();
134 }
135 }
136 }
137
138 return strlen($data);
139 });
140
141 curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
142 curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
143 curl_setopt($ch, CURLOPT_BUFFERSIZE, 128); // Smaller buffer for faster streaming
144
145 $result = curl_exec($ch);
146 $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
147
148 if (curl_error($ch)) {
149 echo "event: error\n";
150 echo "data: " . json_encode(['error' => curl_error($ch)]) . "\n\n";
151 flush();
152 }
153
154 curl_close($ch);
155
156 // phpcs:enable WordPress.WP.AlternativeFunctions.curl_curl_init
157 // phpcs:enable WordPress.WP.AlternativeFunctions.curl_curl_setopt
158 // phpcs:enable WordPress.WP.AlternativeFunctions.curl_curl_exec
159 // phpcs:enable WordPress.WP.AlternativeFunctions.curl_curl_close
160 // phpcs:enable WordPress.WP.AlternativeFunctions.curl_curl_getinfo
161 // phpcs:enable WordPress.WP.AlternativeFunctions.curl_curl_error
162
163 if ($httpCode === 200) {
164 do_action('fluent_support/ai_response_success', $ticketId, $prompt, 0, "Fluent Bot");
165 }
166 }
167
168 protected function sendRequest(array $payload)
169 {
170 $headers = [
171 'Content-Type' => 'application/json',
172 ];
173
174 $timeout = apply_filters('fs_ai_request_timeout', 60);
175
176 return wp_remote_post($this->apiUrl, [
177 'headers' => $headers,
178 'body' => wp_json_encode($payload),
179 'timeout' => $timeout,
180 ]);
181 }
182 }
183