PluginProbe
SupportCandy – AI Customer Support Ticket System & Live Chatbot Agent / 3.5.4
SupportCandy – AI Customer Support Ticket System & Live Chatbot Agent v3.5.4
3.5.4 3.5.3 3.5.2 3.5.1 3.4.9 3.5.0 3.4.8 3.4.7 trunk 2.3.1 3.3.6 3.3.7 3.3.8 3.3.9 3.4.0 3.4.1 3.4.2 3.4.3 3.4.4 3.4.5 3.4.6
supportcandy / asset / js / ai-assistant / script.js

script.js in SupportCandy – AI Customer Support Ticket System & Live Chatbot Agent 3.5.4, at asset/js/ai-assistant/script.js

385 lines 12.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Appends a message to the chat UI.
3 * @param {string} role - The role of the message sender (e.g., 'user', 'assistant').
4 * @param {string} content - The message content to display.
5 */
6 function appendMessageToChat(role, content) {
7 var $model_body = jQuery(".wpsc-modal-body");
8 var $action_buttons = jQuery(".wpsc-ai-action-buttons");
9 var messageClass =
10 role === "user" ? "wpsc-customer-reply-message" : "wpsc-ai-reply-message";
11 var div = jQuery("<div>")
12 .addClass("wpsc-ai-message " + messageClass)
13 .html(content);
14
15 $model_body.append(div);
16
17 if (role === "user") {
18 var loaderDiv = jQuery("<div>").addClass("wpsc-loading-ai-response").html(supportcandy.ai_loader_html);
19 $model_body.append(loaderDiv);
20 $action_buttons.hide();
21 }
22
23 if (role === "assistant") {
24 if ($action_buttons.length) {
25 $action_buttons.appendTo($model_body).show();
26 }
27 }
28
29 // Smooth scroll to bottom
30 $model_body.scrollTop($model_body[0].scrollHeight);
31 }
32
33 /**
34 * Ensures AI conversation history exists and is an array.
35 * @returns {Array}
36 */
37 function ensureAIConversation() {
38 if (!Array.isArray(window.wpsc_ai_conversation)) {
39 window.wpsc_ai_conversation = [];
40 }
41 return window.wpsc_ai_conversation;
42 }
43
44 /**
45 * Calls the AI assistant and gets an improved reply.
46 * @param {HTMLElement} el - The element triggering the request.
47 * @param {string|number} ticket_id - The ticket ID.
48 * @param {string} nonce - The security nonce.
49 */
50 function wpsc_polish_reply_with_ai(el, ticket_id, nonce) {
51
52 window.wpsc_ai_conversation = [
53 {
54 role: "system",
55 content:
56 "You are a customer support assistant. Improve the given reply to make it clear, professional, concise, and helpful. Preserve the original meaning and intent. Do not add new information or assumptions. Fix grammar, tone, and structure. Use a polite and friendly tone. Keep the response easy to read. Return only the improved reply, without any explanations or extra text.",
57 },
58 ];
59
60 var is_tinymce =
61 typeof tinyMCE != "undefined" &&
62 tinyMCE.activeEditor &&
63 !tinyMCE.activeEditor.isHidden();
64 var description =
65 is_tinymce && tinymce.get("description")
66 ? tinyMCE.get("description").getContent()
67 : jQuery("#description").val().trim();
68
69 if (!description) {
70 alert(supportcandy.translations.empty_desc_warning);
71 return;
72 }
73
74 jQuery
75 .post({
76 url: supportcandy.ajax_url,
77 data: {
78 action: "wpsc_refine_ticket_reply_with_ai",
79 ticket_id,
80 _ajax_nonce: nonce,
81 },
82 })
83 .done(function (res) {
84 jQuery(".wpsc-modal-header").html(res.title);
85 jQuery(".wpsc-modal-body").html(res.body);
86 jQuery(".wpsc-modal-footer").html(res.footer);
87
88 wpsc_show_modal();
89 wpsc_show_modal_inner_container();
90 var $model_body = jQuery(".wpsc-modal-body");
91 var loaderDiv = jQuery("<div>").addClass("wpsc-loading-ai-response").html(supportcandy.ai_loader_html);
92 $model_body.append(loaderDiv);
93 jQuery(".wpsc-modal-footer").hide();
94 fetchAndAppendAIReply(ticket_id, description, nonce);
95 });
96 }
97
98 /**
99 * Fetches the AI-generated reply and appends it to the chat UI.
100 * @param {string|number} ticket_id - The ticket ID.
101 * @param {string} description - The ticket description.
102 * @param {string} nonce - The security nonce.
103 */
104 function fetchAndAppendAIReply(ticket_id, description, nonce) {
105 jQuery
106 .post({
107 url: supportcandy.ajax_url,
108 data: {
109 action: "wpsc_generate_ai_reply",
110 ticket_id: ticket_id,
111 is_system_call: true,
112 description: description,
113 _ajax_nonce: nonce,
114 },
115 })
116 .done(function (ai) {
117 jQuery(".wpsc-loading-ai-response").html("");
118 jQuery(".wpsc-modal-footer").show();
119
120 if (!ai.success) {
121 appendMessageToChat("assistant", "Failed to generate AI reply.");
122 return;
123 }
124
125 var $model_body = jQuery(".wpsc-modal-body");
126 var lastMsg = $model_body.find(".wpsc-ai-reply-message").last().text();
127 if (lastMsg !== ai.data.reply) {
128 ensureAIConversation().push({
129 role: "assistant",
130 content: ai.data.reply,
131 });
132 appendMessageToChat("assistant", ai.data.reply);
133 }
134 });
135 }
136
137 /**
138 * Handles user prompt submission and AI response.
139 * @param {string|number} ticket_id - The ticket ID.
140 * @param {string} prompt - The user's prompt.
141 * @param {string} nonce - The security nonce.
142 * @param {function} callback - The callback function to execute after processing.
143 * @param {string} type - The type of AI request.
144 */
145 function wpsc_refine_reply_using_agent_prompt(ticket_id, prompt, nonce, callback, type) {
146 var conversation = ensureAIConversation();
147 conversation.push({ role: "user", content: prompt });
148 jQuery(".wpsc-modal-footer").hide();
149 // Append to UI
150 appendMessageToChat("user", prompt);
151
152 // If type is auto_draft_reply, and callback is wpsc_generate_ai_reply replace and action as wpsc_generate_ai_reply otherwise keep action as callback
153 var action = "wpsc_generate_ai_reply";
154 if (type === "auto_draft_reply" && callback === "wpsc_generate_ai_reply") {
155 action = callback;
156 } else if (callback) {
157 action = callback;
158 }
159
160 // get data from class wpsc-auto-draft-message > span
161 var draftReply = jQuery(".wpsc-auto-draft-message > span").text().trim();
162 if (draftReply) {
163 // Only add draftReply if not already present in conversation
164 var alreadySeeded = conversation.some(function(msg) {
165 return msg.role === "assistant" && msg.content === draftReply;
166 });
167 if (!alreadySeeded) {
168 conversation.push({ role: "assistant", content: draftReply });
169 }
170 }
171
172 // Call AI
173 jQuery
174 .post({
175 url: supportcandy.ajax_url,
176 data: {
177 action: action,
178 ticket_id,
179 is_system_call: false,
180 description: JSON.stringify(conversation),
181 _ajax_nonce: nonce,
182 },
183 })
184 .done(function (ai) {
185 jQuery(".wpsc-loading-ai-response").html("");
186 jQuery(".wpsc-modal-footer").show();
187 if (ai.success) {
188 // Add AI reply to conversation and UI
189 ensureAIConversation().push({
190 role: "assistant",
191 content: ai.data.reply,
192 });
193 appendMessageToChat("assistant", ai.data.reply);
194 } else {
195 appendMessageToChat("assistant", "Failed to generate AI reply.");
196 }
197 });
198 }
199
200 /*
201 * Handles Enter key press in the AI chat textarea to submit user prompt.
202 */
203 jQuery(document).on("keydown", ".wpsc-improve-auto-draft-reply", function (e) {
204 if (e.key === "Enter" && !e.shiftKey) {
205 var conversation = ensureAIConversation();
206
207 e.preventDefault();
208 var prompt = jQuery(this).val().trim();
209 if (prompt) {
210 var pre_content = jQuery(".wpsc-auto-draft-message > span").text().trim();
211
212 if (pre_content) {
213 // Add the draft reply as an assistant message to the conversation history
214 conversation.push({ role: 'assistant', content: pre_content });
215 }
216 var nonce = jQuery(this).data("nonce");
217 var ticket_id = jQuery(this).data("ticket-id");
218 var callback = jQuery(this).data("callback");
219 wpsc_refine_reply_using_agent_prompt(ticket_id, prompt, nonce, callback, "auto_draft_reply");
220 jQuery(this).val(""); // clear input
221 }
222 }
223 });
224
225 /*
226 * Handles Enter key press in the AI chat textarea to submit user prompt.
227 */
228 jQuery(document).on("keydown", ".wpsc-ai-chat-textarea", function (e) {
229 if (e.key === "Enter" && !e.shiftKey) {
230 e.preventDefault();
231 var prompt = jQuery(this).val().trim();
232 if (prompt) {
233 var nonce = jQuery(this).data("nonce");
234 var ticket_id = jQuery(this).data("ticket-id");
235 var callback = jQuery(this).data("callback");
236 wpsc_refine_reply_using_agent_prompt(ticket_id, prompt, nonce, callback, "polish_reply");
237 jQuery(this).val(""); // clear input
238 }
239 }
240 });
241
242 /**
243 * Appends improved reply to the editor when "Append" button is clicked.
244 */
245 jQuery(document).off("click", ".wpsc-ai-append").on("click", ".wpsc-ai-append", function () {
246 // Get the improved reply from the modal (adjust selector as needed)
247 var improvedReply = jQuery(".wpsc-ai-reply-message").last().html() || "";
248
249 // TinyMCE editor
250 var is_tinymce =
251 typeof tinyMCE != "undefined" &&
252 tinyMCE.activeEditor &&
253 !tinyMCE.activeEditor.isHidden();
254 if (is_tinymce) {
255 var currentContent = tinymce.activeEditor.getContent({ format: "html" });
256 tinymce.activeEditor.setContent(currentContent + "<br>" + improvedReply);
257 } else {
258 // Plain textarea: convert <br> to newlines
259 var improvedReplyText = improvedReply.replace(/<br\s*\/?>(\r?\n)?/gi, "\n");
260 var $txt = jQuery(".wpsc_textarea");
261 var textAreaTxt = $txt.val();
262 $txt.val(textAreaTxt + "\n\n" + improvedReplyText);
263 }
264 wpsc_close_modal();
265 });
266
267 /**
268 * Replaces editor content with improved reply when "Replace" button is clicked.
269 */
270 jQuery(document).off("click", ".wpsc-ai-replace").on("click", ".wpsc-ai-replace", function () {
271 // Get the improved reply from the modal (adjust selector as needed)
272 var improvedReply = jQuery(".wpsc-ai-reply-message").last().html() || "";
273
274 // TinyMCE editor
275 var is_tinymce =
276 typeof tinyMCE != "undefined" &&
277 tinyMCE.activeEditor &&
278 !tinyMCE.activeEditor.isHidden();
279 if (is_tinymce) {
280 tinymce.activeEditor.setContent(improvedReply);
281 } else {
282 // Plain textarea: convert <br> to newlines
283 var improvedReplyText = improvedReply.replace(/<br\s*\/?>(\r?\n)?/gi, "\n");
284 jQuery(".wpsc_textarea").val(improvedReplyText);
285 }
286 wpsc_close_modal();
287 });
288
289 /**
290 * Fetches and displays AI-generated ticket summary.
291 * @param {HTMLElement} el - The element triggering the request.
292 * @param {string|number} ticket_id - The ticket ID.
293 * @param {string} nonce - The security nonce.
294 */
295 function wpsc_generate_ticket_summary(el, ticket_id, nonce) {
296 var summary_container = jQuery(".wpsc-ticket-summary-container");
297
298 summary_container.html(supportcandy.loader_html);
299 jQuery.ajax({
300 url: supportcandy.ajax_url,
301 type: "POST",
302 data: {
303 action: "wpsc_get_ticket_summary",
304 ticket_id: ticket_id,
305 _ajax_nonce: nonce,
306 },
307 success: function (response) {
308 if (response.success) {
309 summary_container.html(response.data.summary);
310 } else {
311 summary_container.text("Failed to fetch ticket summary.");
312 }
313 },
314 error: function () {
315 summary_container.text("Error fetching ticket summary.");
316 },
317 });
318 }
319
320 /**
321 * Calls the AI assistant and gets an improved reply.
322 * @param {HTMLElement} el - The element triggering the request.
323 * @param {string|number} id - The ticket ID.
324 * @param {string} nonce - The security nonce.
325 */
326 function wpsc_handle_ai_auto_draft(el, id, nonce) {
327
328 jQuery.post({
329 url: supportcandy.ajax_url,
330 data: {
331 action: "wpsc_auto_draft_ticket_reply_with_ai",
332 id,
333 _ajax_nonce: nonce,
334 },
335 }).done(function (res) {
336
337 jQuery(".wpsc-modal-header").html(res.title);
338 jQuery(".wpsc-modal-body").html(res.body);
339 jQuery(".wpsc-modal-footer").html(res.footer);
340
341 wpsc_show_modal();
342 wpsc_show_modal_inner_container();
343 var $model_body = jQuery(".wpsc-modal-body");
344 var loaderDiv = jQuery("<div>").addClass("wpsc-loading-ai-response").html(supportcandy.ai_loader_html);
345 $model_body.append(loaderDiv);
346 jQuery(".wpsc-modal-footer").hide();
347 fetchAndAppendAIDraft(id, nonce);
348 });
349 }
350
351 /**
352 * Fetches the AI-generated reply and appends it to the chat UI.
353 * @param {string|number} id - The ticket ID.
354 * @param {string} nonce - The security nonce.
355 */
356 function fetchAndAppendAIDraft(id, nonce) {
357 jQuery
358 .post({
359 url: supportcandy.ajax_url,
360 data: {
361 action: "wpsc_generate_ai_auto_draft",
362 id: id,
363 _ajax_nonce: nonce,
364 },
365 })
366 .done(function (ai) {
367 jQuery(".wpsc-loading-ai-response").html("");
368 jQuery(".wpsc-modal-footer").show();
369
370 if (!ai.success) {
371 appendMessageToChat("assistant", "Failed to generate AI reply.");
372 return;
373 }
374
375 var $model_body = jQuery(".wpsc-modal-body");
376 var lastMsg = $model_body.find(".wpsc-ai-reply-message").last().text();
377 if (lastMsg !== ai.data.draft_reply) {
378 ensureAIConversation().push({
379 role: "assistant",
380 content: ai.data.draft_reply,
381 });
382 appendMessageToChat("assistant", ai.data.draft_reply);
383 }
384 });
385 }