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 / FluentBotHelper.php

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

279 lines 10.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace FluentSupport\App\Services\Integrations\FluentBot;
3
4 use FluentSupport\App\Models\Meta;
5 use FluentSupport\App\Services\Integrations\FluentBot\FluentBotAPI;
6 use FluentSupport\Framework\Support\Arr;
7 use WP_Error;
8 class FluentBotHelper
9 {
10 const BASE_URL = 'https://beta.fluentbot.ai/ai';
11
12 const ENDPOINTS = [
13 'default' => '/responses',
14 'ticket_reply' => '/chat/fs-completion',
15 ];
16
17 public function generateStreamResponse($prompt, $ticket, $productId, $conversationId = null)
18 {
19 $prompt = apply_filters('fluent_support/generate_response', $prompt, $ticket);
20 $payload = [
21 'ticket_conversation' => $this->getTicketMessages($ticket),
22 'source' => 'fluent_support',
23 'prompt' => $prompt,
24 'stream' => true,
25 'conversation_id' => $conversationId ?? null,
26 ];
27 return $this->makeStreamAPICall($payload, $prompt, $ticket->id, 'ticket_reply', $productId);
28 }
29
30 public function modifyResponse($prompt, $selectedText, $ticketId)
31 {
32 $prompt = apply_filters('fluent_support/modify_selected_text', $prompt);
33 $payload = [
34 'message' => "Instruction: {$prompt} Now apply this to the given text: {$selectedText}",
35 ];
36
37 return $this->makeAPICall($payload, $prompt, $ticketId);
38 }
39
40 public function generateTicketSummary($ticket)
41 {
42 $prompt = 'Provide a summary of the ticket from the customer\'s perspective. Each step should start with "-". Break it down into concise steps, with a maximum of 6 steps. Each step should be within 6 words per line. Use full stops for separation.';
43 $prompt = apply_filters('fluent_support/generate_ticket_summary', $prompt);
44
45 $messages = $this->getSimpleTicketMessages($ticket);
46 $payload = [
47 'message' => "Instruction: {$prompt} Ticket Data: " . json_encode($messages),
48 ];
49
50 return $this->makeAPICall($payload, $prompt, $ticket->id);
51 }
52
53 public function generateTicketTone($ticket)
54 {
55 $prompt = 'What is the tone of this ticket? Is it positive, negative, or neutral? Provide a response with a single word.';
56 $prompt = apply_filters('fluent_support/find_customer_sentiment', $prompt);
57
58 $messages = $this->getSimpleTicketMessages($ticket);
59 $payload = [
60 'message' => "Instruction: {$prompt} Ticket Data: " . json_encode($messages),
61 ];
62
63 return $this->makeAPICall($payload, $prompt, $ticket->id);
64 }
65
66 public function getPresetPrompts(string $type): array
67 {
68 if ($type === 'modifyResponse') {
69 return $this->getModifyResponsePresets();
70 }
71
72 if ($type === 'createResponse') {
73 return $this->getCreateResponsePresets();
74 }
75
76 return [];
77 }
78
79 private function makeAPICall(array $payload, string $prompt, int $ticketId, string $type = 'default', $productId = null )
80 {
81 $apiUrl = static::BASE_URL . static::ENDPOINTS[$type];
82
83 $credentials = $this->resolveApiCredentials($productId);
84
85 if (is_wp_error($credentials)) {
86 return $credentials;
87 }
88
89 // Use bot_id instead of botId for the new API
90 $payload['bot_id'] = $credentials['botId'];
91
92 $api = new FluentBotAPI($credentials['apiKey'], $apiUrl);
93 $result = $api->makeRequest($ticketId, $prompt, $payload);
94
95 // For ticket_reply endpoint, return the full result with conversation_id
96 // For other endpoints, return just the content for backward compatibility
97 if ($type === 'ticket_reply' && is_array($result) && isset($result['content'])) {
98 return $result;
99 } elseif (is_array($result) && isset($result['content'])) {
100 return $result['content'];
101 }
102
103 return $result;
104 }
105
106 private function makeStreamAPICall(array $payload, string $prompt, int $ticketId, string $type = 'default', $productId = null)
107 {
108 $apiUrl = static::BASE_URL . static::ENDPOINTS[$type];
109
110 $credentials = $this->resolveApiCredentials($productId);
111
112 if (is_wp_error($credentials)) {
113 echo "data: " . json_encode(['error' => $credentials->get_error_message()]) . "\n\n";
114 return;
115 }
116
117 // Use bot_id instead of botId for the new API
118 $payload['bot_id'] = $credentials['botId'];
119
120 $api = new FluentBotAPI($credentials['apiKey'], $apiUrl);
121 $api->makeStreamRequest($ticketId, $prompt, $payload);
122 }
123
124 private function resolveApiCredentials($productId)
125 {
126 $meta = Meta::where([
127 'object_type' => 'fluent_bot_settings',
128 'object_id' => 1,
129 'key' => '_fs_fluent_bot_config'
130 ])->first();
131
132 $config = $meta ? unserialize($meta->value) : [];
133
134 $apiKey = $config['generalApiKey'] ?? '';
135 $botId = $config['generalBotId'] ?? '';
136
137 if ($productId && !empty($config['productMappings']) && is_array($config['productMappings'])) {
138 foreach ($config['productMappings'] as $mapping) {
139 if ((int)$mapping['productId'] === (int)$productId) {
140 $apiKey = $mapping['apiKey'] ?? $apiKey;
141 $botId = $mapping['botId'] ?? $botId;
142 break;
143 }
144 }
145 }
146
147 if (!$botId) {
148 return new \WP_Error(
149 'missing_bot_credentials',
150 __('Bot ID is not set for this product.', 'fluent-support')
151 );
152 }
153
154 return [
155 'apiKey' => $apiKey,
156 'botId' => $botId
157 ];
158 }
159
160 private function getTicketMessages($ticket): array
161 {
162 $messages = [];
163 $ticketArray = $ticket->toArray();
164
165 if (!empty($ticketArray['content'])) {
166 $messages[] = [
167 'role' => 'customer',
168 'message' => $this->cleanText($ticketArray['content']),
169 ];
170 }
171
172 foreach (Arr::get($ticketArray, 'responses', []) as $response) {
173 $role = Arr::get($response, 'person.person_type') === 'customer' ? 'customer' : 'support_agent';
174 $messages[] = [
175 'role' => $role,
176 'message' => $this->cleanText(Arr::get($response, 'content', '')),
177 ];
178 }
179
180 return $messages;
181 }
182
183 private function getSimpleTicketMessages($ticket): array
184 {
185 $messages = [];
186 $ticketArray = $ticket->toArray();
187
188 if (!empty($ticketArray['content'])) {
189 $messages[] = [
190 'role' => 'human',
191 'message' => $this->cleanText($ticketArray['content']),
192 ];
193 }
194
195 foreach (Arr::get($ticketArray, 'responses', []) as $response) {
196 $role = Arr::get($response, 'person.person_type') === 'customer' ? 'human' : 'ai';
197 $messages[] = [
198 'role' => $role,
199 'message' => $this->cleanText(Arr::get($response, 'content', '')),
200 ];
201 }
202
203 return $messages;
204 }
205
206
207
208 private function cleanText(string $text): string
209 {
210 return trim(strip_tags($text));
211 }
212
213 private function getModifyResponsePresets(): array
214 {
215 $presets = [
216 [
217 'label' => 'Improve Writing',
218 'text' => 'shorten',
219 'description' => 'Use AI to refine the text by removing unnecessary words and making it more concise while retaining the original meaning and key information.'
220 ],
221 [
222 'label' => 'Fix Spelling & Grammar',
223 'text' => 'lengthen',
224 'description' => 'Apply AI to correct any spelling and grammatical errors in the text, ensuring it is free of mistakes and reads professionally.'
225 ],
226 [
227 'label' => 'Make Shorter',
228 'text' => 'friendly',
229 'description' => 'AI will modify the text to make it shorter and more casual, making it suitable for informal or friendly communication.'
230 ],
231 [
232 'label' => 'Make Longer',
233 'text' => 'professional',
234 'description' => 'Enhance the text by adding more details and using refined language to make it more formal and detailed, appropriate for professional settings.'
235 ],
236 [
237 'label' => 'Simplify Language',
238 'text' => 'simplify',
239 'description' => 'Utilize AI to simplify complex phrases and terminology, making the text easier to read and understand for a general audience.'
240 ]
241 ];
242
243 return apply_filters('fluent_support/get_modify_response_preset_prompts', $presets);
244 }
245
246 private function getCreateResponsePresets(): array
247 {
248 $presets = [
249 [
250 'label' => 'Request More Information',
251 'text' => 'requestInfo',
252 'description' => 'Ask the customer to provide additional details or clarification about the issue they reported. This helps in gathering more information to resolve the issue effectively.'
253 ],
254 [
255 'label' => 'Acknowledge Issue',
256 'text' => 'acknowledgeIssue',
257 'description' => 'Confirm receipt of the customer\'s issue and reassure them that it is being investigated. This demonstrates that their concern is being taken seriously.'
258 ],
259 [
260 'label' => 'Provide Solution',
261 'text' => 'provideSolution',
262 'description' => 'Offer a comprehensive solution or resolution to the problem described by the customer. This should address their concerns and provide actionable steps to resolve the issue.'
263 ],
264 [
265 'label' => 'Follow Up',
266 'text' => 'followUp',
267 'description' => 'Reach out to the customer after a solution has been provided to ensure that their issue has been resolved to their satisfaction. This helps in confirming the resolution and maintaining good customer relations.'
268 ],
269 [
270 'label' => 'Close Ticket',
271 'text' => 'closeTicket',
272 'description' => 'Notify the customer that their ticket will be closed as the issue has been resolved. Ensure that all their concerns are addressed before closing the ticket.'
273 ]
274 ];
275
276 return apply_filters('fluent_support/get_create_response_preset_prompts', $presets);
277 }
278 }
279