PluginProbe
Fluent Support – Helpdesk & Customer Support Ticket System / 1.10.1
Fluent Support – Helpdesk & Customer Support Ticket System v1.10.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 1.10.1, at app/Services/Integrations/FluentBot/FluentBotAPI.php

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