PluginProbe
WPBot – AI ChatBot for Live Support, Lead Generation, WordPress Automation, AI Services / 8.8.1
WPBot – AI ChatBot for Live Support, Lead Generation, WordPress Automation, AI Services v8.8.1
8.8.2 8.8.1 8.8.0 8.7.9 8.7.8 8.7.7 8.7.6 8.7.5 8.7.4 8.7.3 8.7.2 8.7.1 8.7.0 8.6.9 8.6.8 8.6.7 8.6.6 8.6.5 8.6.4 8.6.2 8.6.1 8.6.0 8.5.9 8.5.8 8.5.7 All 538 releases
← All changes | includes/integration/grok/qcld-bot-grok.php +669 -667 8.7.3 → 8.8.1 View file →
@@ -1,668 +1,670 @@
1 -<?php
2 -/**
3 - * grok AI
4 - *
5 - * @package Botmaster
6 - * @since 1.0.0
7 - */
8 -
9 -if ( ! defined( 'ABSPATH' ) ) {
10 - exit; // Exit if accessed directly.
11 -}
12 -
13 -if ( ! class_exists( 'qcld_wpgrok_addons' ) ) {
14 -
15 -
16 - /**
17 - * Main Class.
18 - */
19 - final class qcld_wpgrok_addons {
20 -
21 - private $id = 'Open AI';
22 -
23 - /**
24 - * WPBot Pro version.
25 - *
26 - * @var string
27 - */
28 - public $version = '1.0.6';
29 -
30 - /**
31 - * WPBot Pro helper.
32 - *
33 - * @var object
34 - */
35 - public $helper;
36 -
37 - /**
38 - * The single instance of the class.
39 - *
40 - * @var qcld_wb_Chatbot
41 - * @since 1.0.0
42 - */
43 - protected static $_instance = null;
44 -
45 - /**
46 - * Main wpbot Instance.
47 - *
48 - * Ensures only one instance of wpbot is loaded or can be loaded.
49 - *
50 - * @return qcld_wb_Chatbot - Main instance.
51 - * @since 1.0.0
52 - * @static
53 - */
54 - public static function instance() {
55 -
56 - if ( is_null( self::$_instance ) ) {
57 - self::$_instance = new self();
58 - }
59 -
60 - return self::$_instance;
61 - }
62 -
63 - public $response_list;
64 -
65 - /**
66 - * Constructor
67 - */
68 - public function __construct() {
69 - $this->includes();
70 - add_action( 'wp_ajax_qcld_grok_response', array( $this, 'grok_response_callback' ) );
71 - add_action( 'wp_ajax_nopriv_qcld_grok_response', array( $this, 'grok_response_callback' ) );
72 - add_action('wp_ajax_qcld_stream_grokai', array($this, 'qcld_stream_grok_callback'));
73 - add_action( 'wp_ajax_qcld_grok_add_collection', array( $this, 'qcld_grok_add_collection_callback' ) );
74 - add_action('wp_ajax_qcld_grok_collectionlist', array($this, 'qcld_grok_collectionlist_callback'));
75 - add_action('wp_ajax_nopriv_qcld_stream_grokai', array($this, 'qcld_stream_grok_callback'));
76 - add_action('wp_ajax_qcld_grok_remove_collection', array($this, 'qcld_grok_remove_collection_callback'));
77 - add_action( 'wp_ajax_qcld_grok_settings_option', array( $this, 'qcld_grok_settings_option_callback' ) );
78 - add_action( 'wp_ajax_qcld_grok_file_upload', array( $this, 'qcld_grok_file_upload_callback' ) );
79 - if ( is_admin() && ! empty( $_GET['page'] ) && ( ( $_GET['page'] == 'openai-panel_dashboard' ) || ( $_GET['page'] == 'openai-panel_file' ) || ( $_GET['page'] == 'wpbot_openAi' ) || ( $_GET['page'] == 'openai-panel_help' ) ) ) {// phpcs:ignore WordPress.Security.NonceVerification.Missing
80 - add_action( 'admin_enqueue_scripts', array( $this, 'qcld_wb_chatbot_grok_admin_scripts' ) );
81 - }
82 - // add_action('wp_enqueue_scripts', array($this, 'qcld_wb_chatbot_grok_scripts'));
83 -
84 - }
85 -
86 - public function qcld_wb_chatbot_grok_admin_scripts() {
87 - wp_register_script(
88 - 'qcld-wp-chatbot-grok-admin',
89 - QCLD_wpCHATBOT_PLUGIN_URL . 'includes/integration/grok/js/qcld-wp-grok-admin.js',
90 - array( 'jquery' ),
91 - QCLD_wpCHATBOT_VERSION,
92 - true
93 - );
94 -
95 - // Localize the script with necessary data.
96 - wp_localize_script(
97 - 'qcld-wp-chatbot-grok-admin',
98 - 'ajax_object',
99 - array(
100 - 'ajax_url' => admin_url( 'admin-ajax.php' ),
101 - 'ajax_nonce' => wp_create_nonce( 'wp_chatbot' ),
102 - 'grok_api_key' => get_option( 'qcld_grok_api_key' ),
103 - 'grok_model' => get_option( 'qcld_grok_model' ),
104 - 'grok_enabled' => get_option( 'qcld_grok_enabled' ),
105 - 'qcld_grok_rag_enabled' => get_option( 'qcld_grok_rag_enabled' ),
106 - 'qcld_grok_append_content' => get_option( 'qcld_grok_append_content' ),
107 - 'qcld_grok_prepend_content' => get_option( 'qcld_grok_prepend_content' ),
108 - )
109 - );
110 -
111 - wp_enqueue_script( 'qcld-wp-chatbot-grok-admin' );
112 - }
113 -
114 - /**
115 - * Define wpbot Constants.
116 - *
117 - * @return void
118 - * @since 1.0.0
119 - */
120 - public function includes() {
121 - require_once QCLD_wpCHATBOT_PLUGIN_DIR_PATH . 'includes/Parsedown.php';
122 - require_once QCLD_wpCHATBOT_PLUGIN_DIR_PATH . 'includes/class-common-function.php';
123 - }
124 -
125 - public function qcld_grok_settings_option_callback() {
126 - $nonce = sanitize_text_field(wp_unslash($_POST['nonce']));
127 - if ( ! wp_verify_nonce( $nonce, 'wp_chatbot' ) || ! current_user_can( 'manage_options' ) ) {
128 - wp_send_json(
129 - array(
130 - 'success' => false,
131 - 'msg' => esc_html__( 'Failed in Security check', 'chatbot'),
132 - )
133 - );
134 - wp_die();
135 - } else {
136 - $grok_api_key = sanitize_text_field( wp_unslash($_POST['grok_api_key']) ?? '' );
137 - $grok_model = sanitize_text_field( wp_unslash($_POST['grok_model']) ?? '' );
138 - $grok_enabled = sanitize_text_field(wp_unslash($_POST['grok_enabled']));
139 - $qcld_grok_page_suggestion_enabled = sanitize_text_field(wp_unslash($_POST['qcld_grok_page_suggestion_enabled']));
140 - $qcld_grok_append_content = sanitize_text_field(wp_unslash($_POST['qcld_grok_append_content'])) ?? '';
141 - $qcld_grok_prepend_content = sanitize_text_field(wp_unslash($_POST['qcld_grok_prepend_content'])) ?? '';
142 - $grok_rag_enabled = sanitize_text_field(wp_unslash($_POST['grok_rag_enabled'])) ?? '';
143 - $qcld_grok_system_content = sanitize_text_field(wp_unslash($_POST['qcld_grok_system_content'])) ?? '';
144 - $grok_stream_enabled = sanitize_text_field(wp_unslash($_POST['grok_stream_enabled'])) ?? '';
145 - $grok_management_api_key = sanitize_text_field( wp_unslash($_POST['grok_management_api_key']) ?? '' );
146 - $grok_collection_id = sanitize_text_field( wp_unslash($_POST['grok_collection_id']) ?? '' );
147 -
148 - if ( $grok_management_api_key != '' ) {
149 - update_option( 'qcld_grok_management_api_key', $grok_management_api_key );
150 - }
151 - if ( $grok_rag_enabled != '' ) {
152 - update_option( 'qcld_grok_rag_enabled', $grok_rag_enabled );
153 - }
154 - if ( $grok_stream_enabled != '' ) {
155 - update_option( 'qcld_grok_stream_enabled', $grok_stream_enabled );
156 - }
157 - if ( $grok_api_key != '' ) {
158 - update_option( 'qcld_grok_api_key', $grok_api_key );
159 - }
160 - if ( $grok_model != '' ) {
161 - update_option( 'qcld_grok_model', $grok_model );
162 - }
163 - if ( $grok_enabled != '' ) {
164 - update_option( 'qcld_grok_enabled', $grok_enabled );
165 - }
166 - if ( $grok_collection_id != '' ) {
167 - update_option( 'qcld_grok_collection_id', $grok_collection_id );
168 - }
169 - if ( $grok_enabled == '1' ) {
170 - update_option( 'ai_enabled', 0 );
171 - update_option( 'qcld_ollama_enabled', 0 );
172 - update_option('qcld_gemini_enabled', 0);
173 - update_option( 'qcld_openrouter_enabled', 0 );
174 - update_option('disable_wp_chatbot_site_search',1);
175 - }
176 - if ( $qcld_grok_system_content != '' ) {
177 - update_option( 'qcld_grok_system_content', $qcld_grok_system_content );
178 - }
179 - update_option( 'qcld_grok_page_suggestion_enabled', $qcld_grok_page_suggestion_enabled );
180 - if ( isset( $_POST['openai_post_type'] ) ) {
181 - $openai_post_types = is_array( $_POST['openai_post_type'] )
182 - ? array_map( 'sanitize_text_field', wp_unslash( $_POST['openai_post_type'] ) )
183 - : sanitize_text_field( wp_unslash( $_POST['openai_post_type'] ) );
184 - update_option( 'qcld_openai_relevant_post', $openai_post_types );
185 - }
186 -
187 - update_option( 'qcld_grok_append_content', $qcld_grok_append_content );
188 - update_option( 'qcld_grok_prepend_content', $qcld_grok_prepend_content );
189 - }
190 - wp_send_json( $grok_enabled );
191 - }
192 -
193 - public function grok_response_callback() {
194 - if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'wp_chatbot' ) ) {
195 - wp_send_json_error([
196 - 'status' => 'error',
197 - 'message' => esc_html__( 'Security check failed. Unauthorized request.', 'chatbot' )
198 - ]);
199 - wp_die();
200 - }
201 -
202 - if (get_option('is_rate_limiting_enabled') == '1') {
203 - do_action('rate_limit_checker');
204 - }
205 - $grok_api_key = get_option( 'qcld_grok_api_key' );
206 - $keyword = isset($_POST['keyword']) ? sanitize_text_field( wp_unslash($_POST['keyword']) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing
207 - $grok_system_content = get_option( 'qcld_grok_system_content' );
208 -
209 - // AI Interactive Form
210 - if (get_option('enable_ai_interactive_form') == '1') {
211 - $saved_ai_forms = get_option('wpbot_ai_forms', array());
212 - if (!empty($saved_ai_forms) && is_array($saved_ai_forms)) {
213 - $grok_system_content .= "\n\nYou must handle the following interactive forms when the user asks for them:\n";
214 - foreach ($saved_ai_forms as $form) {
215 - $grok_system_content .= "\nForm Title: " . $form['title'] . "\nInstructions: " . $form['prompt'] . "\n";
216 - }
217 - $grok_system_content .= "\n\nCRITICAL INSTRUCTIONS FOR INTERACTIVE FORMS:\n";
218 - $grok_system_content .= "When a user triggers an interactive form, you must act as a step-by-step data collection agent.\n";
219 - $grok_system_content .= "1. DO NOT ask all questions at once. Ask exactly ONE question at a time.\n";
220 - $grok_system_content .= "2. Wait for the user's response before asking the next question.\n";
221 - $grok_system_content .= "3. Once all necessary information is collected for the form, you MUST output a final JSON block summarizing the collected data. The keys inside the \"data\" object MUST be dynamically named based on the specific questions you asked during the form collection (e.g., \"Full Name\", \"Company Size\", \"Email\", etc.). The final JSON block must be wrapped EXACTLY in these delimiters:\n";
222 - $grok_system_content .= "__AI_FORM_DATA__{ \"form_title\": \"<Form Title>\", \"data\": { \"Question 1\": \"Answer 1\", \"Question 2\": \"Answer 2\" } }__AI_FORM_DATA_END__\n";
223 - $grok_system_content .= "Do not include any other text after this JSON block once the form is complete.\n";
224 - $grok_system_content .= "4. If the user provides an invalid, irrelevant, or nonsensical answer to your question, DO NOT apologize or state that you lack information. Instead, respond with 'Invalid answer found' and ask the exact same question again.";
225 - }
226 - }
227 -
228 - $action_prompt = !empty($_POST['action_prompt']) ? wp_unslash($_POST['action_prompt']) : '';
229 - if (!empty($action_prompt)) {
230 - $grok_system_content .= "\n\nCRITICAL ACTIVE ACTION INSTRUCTION:\n" . $action_prompt;
231 - }
232 -
233 - $rag_context = "";
234 - if (get_option('qcld_grok_rag_enabled') == '1') {
235 - $rag_context = Qcld_Bot_Rag()->run_rag_search($keyword);
236 - if (!empty($rag_context) && $rag_context != "No knowledge base found.") {
237 - $grok_system_content .= "\n\nRelevant Knowledge Base Information:\n" . $rag_context;
238 - }
239 - }
240 - $relevant_pagelink = Qcld_WPBot_Common_Functions::qcpd_relevant_pagelink( $keyword );
241 - $relevant_pagelink = array_slice( $relevant_pagelink, 0, 5, true );
242 - if ( ( get_option( 'page_suggestion_enabled' ) == '1' ) && count( $relevant_pagelink ) > 0 ) {
243 - $relevant_post_link = maybe_unserialize( get_option( 'qlcd_wp_chatbot_relevant_post_link_openai' ) );
244 - if ( is_array( $relevant_post_link[ get_wpbot_locale() ] ) ) {
245 - $relevant_pagelinks = '<br><br><p><em>' . implode( '', $relevant_post_link[ get_wpbot_locale() ] ) . '</em><p>' . implode( '</br>', $relevant_pagelink );
246 - } else {
247 - $relevant_pagelinks = '<br><br><p><em>' . $relevant_post_link[ get_wpbot_locale() ] . '</em><p>' . implode( '</br>', $relevant_pagelink );
248 - }
249 - } else {
250 - $relevant_pagelinks = '';
251 - }
252 - $collection_id = get_option( 'qcld_grok_collection_id' ) ?? '';
253 - if( $collection_id != '' ) {
254 - $result = $this->direct_search_collection( $keyword );
255 - $response['status'] = 'success';
256 - $response['message'] = $result ;
257 - wp_send_json( $response );
258 - } else {
259 - $messages = [
260 - ['role' => 'system', 'content' => $grok_system_content ?? 'You are a helpful and intelligent assistant for the website "' . site_url() . '". Use live website data and the provided context to respond accurately and briefly. Stay relevant and do not introduce additional topics.' ]
261 - ];
262 -
263 - if (!empty($_POST['ai_history'])) {
264 - $parsed_history = json_decode(wp_unslash($_POST['ai_history']), true);
265 - if (is_array($parsed_history)) {
266 - foreach ($parsed_history as $h) {
267 - if (isset($h['role']) && isset($h['content'])) {
268 - $role = ($h['role'] === 'assistant') ? 'assistant' : 'user';
269 - $messages[] = [
270 - 'role' => $role,
271 - 'content' => sanitize_text_field($h['content'])
272 - ];
273 - }
274 - }
275 - }
276 - }
277 -
278 - $last_msg = end($messages);
279 - if (!$last_msg || $last_msg['role'] !== 'user' || $last_msg['content'] !== $keyword) {
280 - $messages[] = ['role' => 'user', 'content' => $keyword];
281 - }
282 -
283 - $response_mess = wp_remote_post('https://api.x.ai/v1/chat/completions', [
284 - 'headers' => [
285 - 'Authorization' => 'Bearer ' . $grok_api_key,
286 - 'Content-Type' => 'application/json',
287 - ],
288 - 'body' => wp_json_encode([
289 - 'messages' => $messages,
290 - 'model' => 'grok-3-latest',
291 - 'temperature' => 0,
292 - 'stream' => false
293 - ]),
294 - 'timeout' => 60,
295 - ]);
296 - $response['message'] = '';
297 - $status_code = wp_remote_retrieve_response_code($response_mess);
298 - $data = wp_remote_retrieve_body($response_mess);
299 - $data = json_decode($data, true);
300 - do_action('qcld_openai_user_rate_cal', 1);
301 - $response['status'] = 'success';
302 - $reply_raw = $data['choices'][0]['message']['content'] ?? 'No content received';
303 - $Parsedown = new Parsedown();
304 - $reply_text = $Parsedown->text($reply_raw);
305 - if (strpos($reply_text, 'AI_FORM_DATA') !== false) {
306 - $reply_text = Qcld_WPBot_Common_Functions::format_and_save_ai_form_response($reply_text);
307 - $response['message'] = $reply_text;
308 - } else {
309 - $response['message'] = $reply_text . $relevant_pagelinks;
310 - }
311 - wp_send_json( $response );
312 - }
313 - }
314 - public function qcld_stream_grok_callback()
315 - {
316 -
317 - if (!defined('ABSPATH')) {
318 - // Optional: protect direct access or require wp-load.php
319 - // For security → better to use admin-ajax or custom rewrite rule in production
320 - http_response_code(403);
321 - exit;
322 - }
323 -
324 - if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'wp_chatbot' ) ) {
325 - echo "data: [ERROR] Security check failed.\n\n";
326 - flush();
327 - wp_die();
328 - }
329 -
330 - if (get_option('is_rate_limiting_enabled') == '1') {
331 - do_action('rate_limit_checker');
332 - }
333 - $api_key = get_option('qcld_grok_api_key'); // ← Use get_option() or env in real code!
334 -
335 - // Disable compression (critical for streaming)
336 - if (function_exists('apache_setenv')) {
337 - @apache_setenv('no-gzip', 1);
338 - }
339 - @ini_set('zlib.output_compression', 0);
340 - while (ob_get_level() > 0) { ob_end_clean(); }
341 -
342 - set_time_limit(180); // Allow long-running requests
343 -
344 - $headers = [
345 - 'Content-Type: application/json',
346 - 'Authorization: Bearer ' . $api_key,
347 - ];
348 - $system_content = get_option( 'qcld_grok_system_content' );
349 - if (empty($system_content)) {
350 - $system_content = 'You are a helpful and intelligent assistant for the website "' . site_url() . '". Use live website data and the provided context to respond accurately and briefly. Stay relevant and do not introduce additional topics.';
351 - }
352 - // AI Interactive Form
353 - if (get_option('enable_ai_interactive_form') == '1') {
354 - $saved_ai_forms = get_option('wpbot_ai_forms', array());
355 - if (!empty($saved_ai_forms) && is_array($saved_ai_forms)) {
356 - $system_content .= "\n\nYou must handle the following interactive forms when the user asks for them:\n";
357 - foreach ($saved_ai_forms as $form) {
358 - $system_content .= "\nForm Title: " . $form['title'] . "\nInstructions: " . $form['prompt'] . "\n";
359 - }
360 - $system_content .= "\n\nCRITICAL INSTRUCTIONS FOR INTERACTIVE FORMS:\n";
361 - $system_content .= "When a user triggers an interactive form, you must act as a step-by-step data collection agent.\n";
362 - $system_content .= "1. DO NOT ask all questions at once. Ask exactly ONE question at a time.\n";
363 - $system_content .= "2. Wait for the user's response before asking the next question.\n";
364 - $system_content .= "3. Once all necessary information is collected for the form, you MUST output a final JSON block summarizing the collected data. The keys inside the \"data\" object MUST be dynamically named based on the specific questions you asked during the form collection (e.g., \"Full Name\", \"Company Size\", \"Email\", etc.). The final JSON block must be wrapped EXACTLY in these delimiters:\n";
365 - $system_content .= "__AI_FORM_DATA__{ \"form_title\": \"<Form Title>\", \"data\": { \"Question 1\": \"Answer 1\", \"Question 2\": \"Answer 2\" } }__AI_FORM_DATA_END__\n";
366 - $system_content .= "Do not include any other text after this JSON block once the form is complete.\n";
367 - $system_content .= "4. If the user provides an invalid, irrelevant, or nonsensical answer to your question, DO NOT apologize or state that you lack information. Instead, respond with 'Invalid answer found' and ask the exact same question again.";
368 - }
369 - }
370 - $messages = [];
371 - // Load previous conversation from cookie if continuity is enabled
372 - if ($conversation_continuity == 1 && !empty($_COOKIE['last_five_prompt'])) {
373 - $decoded_cookie = json_decode(stripslashes($_COOKIE['last_five_prompt']), true);
374 - if (is_array($decoded_cookie)) {
375 - $messages = $decoded_cookie;
376 - }
377 - }
378 - // Ensure system message is the first message.
379 - if (empty($messages) || (isset($messages[0]) && $messages[0]['role'] !== 'system')) {
380 - array_unshift($messages, ['role' => 'system', 'content' => $system_content]);
381 - } elseif (isset($messages[0]) && $messages[0]['role'] === 'system' && $messages[0]['content'] !== $system_content) {
382 - $messages[0]['content'] = $system_content;
383 - }
384 -
385 - array_push($messages, ['role' => 'user', 'content' => sanitize_text_field( wp_unslash( $_POST['message'] ) )]);
386 - $postData = json_encode([
387 - 'model' => 'grok-3-latest',
388 - 'messages' => $messages,
389 - 'stream' => true
390 - ]);
391 -
392 - // phpcs:ignore WordPress.WP.AlternativeFunctions -- streaming requires native cURL
393 - $ch = curl_init('https://api.x.ai/v1/chat/completions');
394 - curl_setopt($ch, CURLOPT_POST, true); // phpcs:ignore WordPress.WP.AlternativeFunctions
395 - curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); // phpcs:ignore WordPress.WP.AlternativeFunctions
396 - curl_setopt($ch, CURLOPT_POSTFIELDS, $postData); // phpcs:ignore WordPress.WP.AlternativeFunctions
397 - curl_setopt($ch, CURLOPT_WRITEFUNCTION, function ($ch, $chunk) use (&$full_assistant_response) { // phpcs:ignore WordPress.WP.AlternativeFunctions
398 - echo $chunk; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- SSE streaming raw output
399 - echo str_repeat(' ', 1024); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- SSE streaming padding
400 - flush();
401 - ob_flush();
402 -
403 - // Parse the chunk to extract assistant's content for continuity.
404 - $lines = explode("\n", $chunk);
405 - foreach ($lines as $line) {
406 - if (strpos($line, 'data: ') === 0) {
407 - $json_data = substr($line, 6);
408 - if ($json_data === '[DONE]') {
409 - continue;
410 - }
411 - $data = json_decode($json_data, true);
412 - if (isset($data['choices'][0]['delta']['content'])) {
413 - $full_assistant_response .= $data['choices'][0]['delta']['content'];
414 - }
415 - }
416 - }
417 - return strlen($chunk);
418 - });
419 - curl_exec($ch); // phpcs:ignore WordPress.WP.AlternativeFunctions
420 - curl_close($ch); // phpcs:ignore WordPress.WP.AlternativeFunctions
421 - // phpcs:ignore WordPress.WP.AlternativeFunctions -- streaming requires native cURL
422 - $ch = curl_init('https://api.x.ai/v1/chat/completions');
423 -
424 - // phpcs:ignore WordPress.WP.AlternativeFunctions
425 - curl_setopt_array($ch, [
426 - CURLOPT_POST => true,
427 - CURLOPT_HTTPHEADER => $headers,
428 - CURLOPT_POSTFIELDS => $postData,
429 - CURLOPT_RETURNTRANSFER => false, // We echo ourselves
430 - CURLOPT_WRITEFUNCTION => function ($curl, $chunk) {
431 - echo $chunk; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- SSE streaming raw output
432 - flush(); // Push to browser immediately
433 - return strlen($chunk);
434 - },
435 - CURLOPT_TIMEOUT => 150,
436 - CURLOPT_CONNECTTIMEOUT => 30,
437 - CURLOPT_SSL_VERIFYPEER => true,
438 - CURLOPT_FOLLOWLOCATION => true,
439 - CURLOPT_ENCODING => '', // Let server decide
440 - ]);
441 -
442 - $success = curl_exec($ch); // phpcs:ignore WordPress.WP.AlternativeFunctions
443 - do_action('qcld_openai_user_rate_cal', 1);
444 - if (curl_errno($ch)) { // phpcs:ignore WordPress.WP.AlternativeFunctions
445 - $err = curl_error($ch); // phpcs:ignore WordPress.WP.AlternativeFunctions
446 - echo "data: " . wp_json_encode( array( 'error' => 'cURL error: ' . $err ) ) . "\n\n";
447 - flush();
448 - }
449 -
450 - curl_close($ch); // phpcs:ignore WordPress.WP.AlternativeFunctions
451 -
452 - // Final done signal (optional but nice)
453 - echo "data: [DONE]\n\n";
454 - flush();
455 - }
456 - public function qcld_grok_collectionlist_callback() {
457 - $nonce = sanitize_text_field( wp_unslash($_POST['nonce']) ?? '' );
458 - if ( ! wp_verify_nonce( $nonce, 'wp_chatbot' ) || ! current_user_can( 'manage_options' ) ) {
459 - wp_send_json(
460 - array(
461 - 'success' => false,
462 - 'msg' => esc_html__( 'Failed in Security check', 'chatbot'),
463 - )
464 - );
465 - wp_die();
466 - }
467 - $grok_api_key = get_option( 'qcld_grok_api_key' );
468 - $response_raw = wp_remote_get(
469 - 'https://api.x.ai/v1/collections',
470 - array(
471 - 'headers' => array(
472 - 'Authorization' => 'Bearer ' . $grok_api_key,
473 - 'Content-Type' => 'application/json',
474 - ),
475 - 'timeout' => 60,
476 - )
477 - );
478 - $response = json_decode( wp_remote_retrieve_body( $response_raw ), true );
479 - wp_send_json( $response );
480 - }
481 - public function qcld_grok_file_upload_callback()
482 - {
483 -
484 - $nonce = sanitize_text_field(wp_unslash($_POST['nonce']));
485 - if (! wp_verify_nonce($nonce, 'wp_chatbot') || ! current_user_can( 'manage_options' ) ) {
486 - wp_send_json(
487 - array(
488 - 'success' => false,
489 - 'msg' => esc_html__('Failed in Security check', 'chatbot'),
490 - )
491 - );
492 - wp_die();
493 - } else {
494 - // $uploadedfile = $_FILES['file'];
495 - // $collection_id = sanitize_text_field(wp_unslash($_POST['collection_id']));
496 - // $api_key = get_option( 'qcld_grok_api_key' );
497 -
498 - // $ch = curl_init();
499 -
500 - // $file = new CURLFile(
501 - // $uploadedfile['tmp_name'],
502 - // $uploadedfile['type'] ?: 'application/octet-stream',
503 - // $uploadedfile['name']
504 - // );
505 -
506 - // $post_data = ['file' => $file];
507 -
508 - // curl_setopt_array($ch, [
509 - // CURLOPT_URL => 'https://api.x.ai/v1/files',
510 - // CURLOPT_POST => true,
511 - // CURLOPT_POSTFIELDS => $post_data,
512 - // CURLOPT_RETURNTRANSFER => true,
513 - // CURLOPT_HTTPHEADER => [
514 - // 'Authorization: Bearer ' . $api_key,
515 - // // Do NOT add Content-Type here!!
516 - // ],
517 - // CURLOPT_TIMEOUT => 180,
518 - // CURLOPT_CONNECTTIMEOUT => 30,
519 - // ]);
520 -
521 - // $result = json_decode(curl_exec($ch));
522 - // $error = curl_error($ch);
523 - // curl_close($ch);
524 - // $file_id = $result->id
525 - $collection_id = "collection_22aa88f1-80e8-4e1a-8e13-c119afb0481c";
526 - $file_id = "file_06208083-0e27-44ff-af29-5faa211f4892";
527 - $this->add_file_to_collection($collection_id, $file_id, []);
528 - if (! empty($res->error)) {
529 - $response['status'] = 'error';
530 - $response['message'] = $res->error->message;
531 - }
532 -
533 - if (! empty($res->status)) {
534 - $response['status'] = 'success';
535 - $response['message'] = 'Successfully Created file' . $res->id;
536 - }
537 - // echo wp_send_json(array($response));
538 - // wp_die();
539 - }
540 - }
541 - public function add_file_to_collection($collection_id, $file_id, $metadata = []) {
542 - $url = "https://management-api.x.ai/v1/collections/{$collection_id}/documents/{$file_id}";
543 - $grok_management_api_key = get_option( 'qcld_grok_management_api_key' );
544 -
545 - $args = [
546 - 'method' => 'POST',
547 - 'timeout' => 30,
548 - 'redirection' => 5,
549 - 'httpversion' => '1.1',
550 - 'blocking' => true,
551 - 'headers' => [
552 - 'Authorization' => 'Bearer ' . $grok_management_api_key, // Management API key required
553 - 'Content-Type' => 'application/json',
554 - ],
555 - // 'body' => json_encode([
556 - // 'fields' => $metadata // Optional: matches your field_definitions
557 - // ]),
558 - ];
559 - // return ['success' => false, 'error' => $response->get_error_message()];
560 - $response = wp_remote_post( $url, $args );
561 - $body = wp_remote_retrieve_body( $response );
562 - $status_code = wp_remote_retrieve_response_code( $response );
563 - if ($status_code >= 200 && $status_code < 300) {
564 - return ['success' => true, 'message' => 'File added to collection', 'body' => $body];
565 - } else {
566 - return ['success' => false, 'status' => $status_code, 'error' => $body];
567 - }
568 - }
569 - public function qcld_grok_remove_collection_callback() {
570 - $nonce = sanitize_text_field( wp_unslash($_POST['nonce']) ?? '' );
571 - if ( ! wp_verify_nonce( $nonce, 'wp_chatbot' ) || ! current_user_can( 'manage_options' ) ) {
572 - wp_send_json(
573 - array(
574 - 'success' => false,
575 - 'msg' => esc_html__( 'Failed in Security check', 'chatbot'),
576 - )
577 - );
578 - wp_die();
579 - }
580 - $grok_api_key = get_option( 'qcld_grok_api_key' );
581 - $collection_id = sanitize_text_field( wp_unslash($_POST['collection_id']) ?? '' ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
582 - $url = 'https://api.x.ai/v1/collections/' . $collection_id; // Check exact endpoint in docs.x.ai
583 - $args = [
584 - 'method' => 'DELETE',
585 - 'headers' => [
586 - 'Authorization' => 'Bearer ' . $grok_api_key,
587 - 'Content-Type' => 'application/json'
588 - ],
589 - 'timeout' => 60,
590 - ];
591 - $response = wp_remote_request( $url, $args );
592 - $response_data = json_decode( wp_remote_retrieve_body( $response ), true );
593 - wp_send_json( $response_data );
594 - }
595 - public function qcld_grok_add_collection_callback() {
596 - $nonce = sanitize_text_field( wp_unslash($_POST['nonce']) ?? '' );
597 - if ( ! wp_verify_nonce( $nonce, 'wp_chatbot' ) || ! current_user_can( 'manage_options' ) ) {
598 - wp_send_json(
599 - array(
600 - 'success' => false,
601 - 'msg' => esc_html__( 'Failed in Security check', 'chatbot'),
602 - )
603 - );
604 - wp_die();
605 - }
606 - $grok_api_key = get_option( 'qcld_grok_api_key' );
607 - $collection_name = isset( $_POST['collection_name'] ) ? sanitize_text_field( wp_unslash( $_POST['collection_name'] ) ) : ( site_url() . wp_rand( 1, 999 ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
608 - $response_raw = wp_remote_post(
609 - 'https://api.x.ai/v1/collections',
610 - array(
611 - 'headers' => array(
612 - 'Authorization' => 'Bearer ' . $grok_api_key,
613 - 'Content-Type' => 'application/json',
614 - ),
615 - 'body' => wp_json_encode( array( 'collection_name' => $collection_name, 'field_definitions' => array() ) ),
616 - 'timeout' => 60,
617 - )
618 - );
619 - $response = json_decode( wp_remote_retrieve_body( $response_raw ), true );
620 - wp_send_json( $response );
621 - }
622 - public function direct_search_collection( $query ) {
623 -
624 - $collection_id = get_option( 'qcld_grok_collection_id' );
625 - $grok_api_key = get_option( 'qcld_grok_api_key' );
626 - $url = 'https://api.x.ai/v1/documents/search';
627 - $payload = [
628 - 'query' => $query,
629 - 'source' => ['collection_ids' => [$collection_id]],
630 - 'retrieval_mode' => ['type' => 'hybrid'] // or 'semantic', 'keyword'
631 - ];
632 - $args = [
633 - 'method' => 'POST',
634 - 'headers' => [
635 - 'Authorization' => 'Bearer ' . $grok_api_key,
636 - 'Content-Type' => 'application/json',
637 - ],
638 - 'body' => wp_json_encode($payload),
639 - 'timeout' => 30,
640 - ];
641 - $response = wp_remote_post($url, $args);
642 - $result = json_decode(wp_remote_retrieve_body($response), true);
643 - if (is_wp_error($response)) {
644 - return ['error' => $response->get_error_message()];
645 - }
646 - foreach ($result['matches'] as $key => $match) {
647 - if ($key >= 1) break;
648 - $responses = $match ["chunk_content"];
649 - }
650 - return $responses;
651 - }
652 - }
653 -
654 - /**
655 - * @return qcld_wpopenai_addon
656 - */
657 - if ( ! function_exists( 'qcld_wpgrok_addons' ) ) {
658 -
659 - function qcld_wpgrok_addons() {
660 - $qcld_wpgrok_addon = new qcld_wpgrok_addons();
661 - return $qcld_wpgrok_addon->instance();
662 - }
663 - }
664 -
665 - // fire off the plugin.
666 - qcld_wpgrok_addons();
667 -
1 +<?php
2 +/**
3 + * grok AI
4 + *
5 + * @package Botmaster
6 + * @since 1.0.0
7 + */
8 +
9 +if ( ! defined( 'ABSPATH' ) ) {
10 + exit; // Exit if accessed directly.
11 +}
12 +
13 +if ( ! class_exists( 'qcld_wpgrok_addons' ) ) {
14 +
15 +
16 + /**
17 + * Main Class.
18 + */
19 + final class qcld_wpgrok_addons {
20 +
21 + private $id = 'Open AI';
22 +
23 + /**
24 + * WPBot Pro version.
25 + *
26 + * @var string
27 + */
28 + public $version = '1.0.6';
29 +
30 + /**
31 + * WPBot Pro helper.
32 + *
33 + * @var object
34 + */
35 + public $helper;
36 +
37 + /**
38 + * The single instance of the class.
39 + *
40 + * @var qcld_wb_Chatbot
41 + * @since 1.0.0
42 + */
43 + protected static $_instance = null;
44 +
45 + /**
46 + * Main wpbot Instance.
47 + *
48 + * Ensures only one instance of wpbot is loaded or can be loaded.
49 + *
50 + * @return qcld_wb_Chatbot - Main instance.
51 + * @since 1.0.0
52 + * @static
53 + */
54 + public static function instance() {
55 +
56 + if ( is_null( self::$_instance ) ) {
57 + self::$_instance = new self();
58 + }
59 +
60 + return self::$_instance;
61 + }
62 +
63 + public $response_list;
64 +
65 + /**
66 + * Constructor
67 + */
68 + public function __construct() {
69 + $this->includes();
70 + add_action( 'wp_ajax_qcld_grok_response', array( $this, 'grok_response_callback' ) );
71 + add_action( 'wp_ajax_nopriv_qcld_grok_response', array( $this, 'grok_response_callback' ) );
72 + add_action('wp_ajax_qcld_stream_grokai', array($this, 'qcld_stream_grok_callback'));
73 + add_action( 'wp_ajax_qcld_grok_add_collection', array( $this, 'qcld_grok_add_collection_callback' ) );
74 + add_action('wp_ajax_qcld_grok_collectionlist', array($this, 'qcld_grok_collectionlist_callback'));
75 + add_action('wp_ajax_nopriv_qcld_stream_grokai', array($this, 'qcld_stream_grok_callback'));
76 + add_action('wp_ajax_qcld_grok_remove_collection', array($this, 'qcld_grok_remove_collection_callback'));
77 + add_action( 'wp_ajax_qcld_grok_settings_option', array( $this, 'qcld_grok_settings_option_callback' ) );
78 + add_action( 'wp_ajax_qcld_grok_file_upload', array( $this, 'qcld_grok_file_upload_callback' ) );
79 + if ( is_admin() && ! empty( $_GET['page'] ) && ( ( $_GET['page'] == 'openai-panel_dashboard' ) || ( $_GET['page'] == 'openai-panel_file' ) || ( $_GET['page'] == 'wpbot_openAi' ) || ( $_GET['page'] == 'openai-panel_help' ) ) ) {// phpcs:ignore WordPress.Security.NonceVerification.Missing
80 + add_action( 'admin_enqueue_scripts', array( $this, 'qcld_wb_chatbot_grok_admin_scripts' ) );
81 + }
82 + // add_action('wp_enqueue_scripts', array($this, 'qcld_wb_chatbot_grok_scripts'));
83 +
84 + }
85 +
86 + public function qcld_wb_chatbot_grok_admin_scripts() {
87 + wp_register_script(
88 + 'qcld-wp-chatbot-grok-admin',
89 + QCLD_wpCHATBOT_PLUGIN_URL . 'includes/integration/grok/js/qcld-wp-grok-admin.js',
90 + array( 'jquery' ),
91 + QCLD_wpCHATBOT_VERSION,
92 + true
93 + );
94 +
95 + // Localize the script with necessary data.
96 + wp_localize_script(
97 + 'qcld-wp-chatbot-grok-admin',
98 + 'ajax_object',
99 + array(
100 + 'ajax_url' => admin_url( 'admin-ajax.php' ),
101 + 'ajax_nonce' => wp_create_nonce( 'wp_chatbot' ),
102 + 'grok_api_key' => get_option( 'qcld_grok_api_key' ),
103 + 'grok_model' => get_option( 'qcld_grok_model' ),
104 + 'grok_enabled' => get_option( 'qcld_grok_enabled' ),
105 + 'qcld_grok_rag_enabled' => get_option( 'qcld_grok_rag_enabled' ),
106 + 'qcld_grok_append_content' => get_option( 'qcld_grok_append_content' ),
107 + 'qcld_grok_prepend_content' => get_option( 'qcld_grok_prepend_content' ),
108 + )
109 + );
110 +
111 + wp_enqueue_script( 'qcld-wp-chatbot-grok-admin' );
112 + }
113 +
114 + /**
115 + * Define wpbot Constants.
116 + *
117 + * @return void
118 + * @since 1.0.0
119 + */
120 + public function includes() {
121 + require_once QCLD_wpCHATBOT_PLUGIN_DIR_PATH . 'includes/Parsedown.php';
122 + require_once QCLD_wpCHATBOT_PLUGIN_DIR_PATH . 'includes/class-common-function.php';
123 + }
124 +
125 + public function qcld_grok_settings_option_callback() {
126 + $nonce = sanitize_text_field(wp_unslash($_POST['nonce']));
127 + if ( ! wp_verify_nonce( $nonce, 'wp_chatbot' ) || ! current_user_can( 'manage_options' ) ) {
128 + wp_send_json(
129 + array(
130 + 'success' => false,
131 + 'msg' => esc_html__( 'Failed in Security check', 'chatbot'),
132 + )
133 + );
134 + wp_die();
135 + } else {
136 + $grok_api_key = sanitize_text_field( wp_unslash($_POST['grok_api_key']) ?? '' );
137 + $grok_model = sanitize_text_field( wp_unslash($_POST['grok_model']) ?? '' );
138 + $grok_enabled = sanitize_text_field(wp_unslash($_POST['grok_enabled']));
139 + $qcld_grok_page_suggestion_enabled = sanitize_text_field(wp_unslash($_POST['qcld_grok_page_suggestion_enabled']));
140 + $qcld_grok_append_content = sanitize_text_field(wp_unslash($_POST['qcld_grok_append_content'])) ?? '';
141 + $qcld_grok_prepend_content = sanitize_text_field(wp_unslash($_POST['qcld_grok_prepend_content'])) ?? '';
142 + $grok_rag_enabled = sanitize_text_field(wp_unslash($_POST['grok_rag_enabled'])) ?? '';
143 + $qcld_grok_system_content = sanitize_text_field(wp_unslash($_POST['qcld_grok_system_content'])) ?? '';
144 + $grok_stream_enabled = sanitize_text_field(wp_unslash($_POST['grok_stream_enabled'])) ?? '';
145 + $grok_management_api_key = sanitize_text_field( wp_unslash($_POST['grok_management_api_key']) ?? '' );
146 + $grok_collection_id = sanitize_text_field( wp_unslash($_POST['grok_collection_id']) ?? '' );
147 +
148 + if ( $grok_management_api_key != '' ) {
149 + update_option( 'qcld_grok_management_api_key', $grok_management_api_key );
150 + }
151 + if ( $grok_rag_enabled != '' ) {
152 + update_option( 'qcld_grok_rag_enabled', $grok_rag_enabled );
153 + }
154 + if ( $grok_stream_enabled != '' ) {
155 + update_option( 'qcld_grok_stream_enabled', $grok_stream_enabled );
156 + }
157 + if ( $grok_api_key != '' ) {
158 + update_option( 'qcld_grok_api_key', $grok_api_key );
159 + }
160 + if ( $grok_model != '' ) {
161 + update_option( 'qcld_grok_model', $grok_model );
162 + }
163 + if ( $grok_enabled != '' ) {
164 + update_option( 'qcld_grok_enabled', $grok_enabled );
165 + }
166 + if ( $grok_collection_id != '' ) {
167 + update_option( 'qcld_grok_collection_id', $grok_collection_id );
168 + }
169 + if ( $grok_enabled == '1' ) {
170 + update_option( 'ai_enabled', 0 );
171 + update_option( 'qcld_ollama_enabled', 0 );
172 + update_option('qcld_gemini_enabled', 0);
173 + update_option( 'qcld_openrouter_enabled', 0 );
174 + update_option('disable_wp_chatbot_site_search',1);
175 + }
176 + if ( $qcld_grok_system_content != '' ) {
177 + update_option( 'qcld_grok_system_content', $qcld_grok_system_content );
178 + }
179 + update_option( 'qcld_grok_page_suggestion_enabled', $qcld_grok_page_suggestion_enabled );
180 + if ( isset( $_POST['openai_post_type'] ) ) {
181 + $openai_post_types = is_array( $_POST['openai_post_type'] )
182 + ? array_map( 'sanitize_text_field', wp_unslash( $_POST['openai_post_type'] ) )
183 + : sanitize_text_field( wp_unslash( $_POST['openai_post_type'] ) );
184 + update_option( 'qcld_openai_relevant_post', $openai_post_types );
185 + }
186 +
187 + update_option( 'qcld_grok_append_content', $qcld_grok_append_content );
188 + update_option( 'qcld_grok_prepend_content', $qcld_grok_prepend_content );
189 + }
190 + wp_send_json( $grok_enabled );
191 + }
192 +
193 + public function grok_response_callback() {
194 + if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'wp_chatbot' ) ) {
195 + wp_send_json_error([
196 + 'status' => 'error',
197 + 'message' => esc_html__( 'Security check failed. Unauthorized request.', 'chatbot' )
198 + ]);
199 + wp_die();
200 + }
201 +
202 + if (get_option('is_rate_limiting_enabled') == '1') {
203 + do_action('rate_limit_checker');
204 + }
205 + $grok_api_key = get_option( 'qcld_grok_api_key' );
206 + $keyword = isset($_POST['keyword']) ? sanitize_text_field( wp_unslash($_POST['keyword']) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing
207 + $grok_system_content = get_option( 'qcld_grok_system_content' );
208 +
209 + // AI Interactive Form
210 + if (get_option('enable_ai_interactive_form') == '1') {
211 + $saved_ai_forms = get_option('wpbot_ai_forms', array());
212 + if (!empty($saved_ai_forms) && is_array($saved_ai_forms)) {
213 + $grok_system_content .= "\n\nYou must handle the following interactive forms when the user asks for them:\n";
214 + foreach ($saved_ai_forms as $form) {
215 + $grok_system_content .= "\nForm Title: " . $form['title'] . "\nInstructions: " . $form['prompt'] . "\n";
216 + }
217 + $grok_system_content .= "\n\nCRITICAL INSTRUCTIONS FOR INTERACTIVE FORMS:\n";
218 + $grok_system_content .= "When a user triggers an interactive form, you must act as a step-by-step data collection agent.\n";
219 + $grok_system_content .= "1. DO NOT ask all questions at once. Ask exactly ONE question at a time.\n";
220 + $grok_system_content .= "2. Wait for the user's response before asking the next question.\n";
221 + $grok_system_content .= "3. Once all necessary information is collected for the form, you MUST output a final JSON block summarizing the collected data. The keys inside the \"data\" object MUST be dynamically named based on the specific questions you asked during the form collection (e.g., \"Full Name\", \"Company Size\", \"Email\", etc.). The final JSON block must be wrapped EXACTLY in these delimiters:\n";
222 + $grok_system_content .= "__AI_FORM_DATA__{ \"form_title\": \"<Form Title>\", \"data\": { \"Question 1\": \"Answer 1\", \"Question 2\": \"Answer 2\" } }__AI_FORM_DATA_END__\n";
223 + $grok_system_content .= "Do not include any other text after this JSON block once the form is complete.\n";
224 + $grok_system_content .= "4. If the user provides an invalid, irrelevant, or nonsensical answer to your question, DO NOT apologize or state that you lack information. Instead, politely inform the user in the language of the conversation (the language the user is speaking in, e.g. German if communicating in German) that their answer is invalid or cannot be assigned, and ask the exact same question again in the user's language. NEVER output the English phrase 'Invalid answer found' unless the user is communicating in English.\n";
225 + $grok_system_content .= "5. MULTI-LANGUAGE SUPPORT: Always communicate, ask questions, validate answers, and reply strictly in the user's language (matching the language used by the user, e.g. German, French, Spanish, etc.). Understand and accept valid answers in the user's language.";
226 + }
227 + }
228 +
229 + $action_prompt = !empty($_POST['action_prompt']) ? wp_unslash($_POST['action_prompt']) : '';
230 + if (!empty($action_prompt)) {
231 + $grok_system_content .= "\n\nCRITICAL ACTIVE ACTION INSTRUCTION:\n" . $action_prompt;
232 + }
233 +
234 + $rag_context = "";
235 + if (get_option('qcld_grok_rag_enabled') == '1') {
236 + $rag_context = Qcld_Bot_Rag()->run_rag_search($keyword);
237 + if (!empty($rag_context) && $rag_context != "No knowledge base found.") {
238 + $grok_system_content .= "\n\nRelevant Knowledge Base Information:\n" . $rag_context;
239 + }
240 + }
241 + $relevant_pagelink = Qcld_WPBot_Common_Functions::qcpd_relevant_pagelink( $keyword );
242 + $relevant_pagelink = array_slice( $relevant_pagelink, 0, 5, true );
243 + if ( ( get_option( 'page_suggestion_enabled' ) == '1' ) && count( $relevant_pagelink ) > 0 ) {
244 + $relevant_post_link = maybe_unserialize( get_option( 'qlcd_wp_chatbot_relevant_post_link_openai' ) );
245 + if ( is_array( $relevant_post_link[ get_wpbot_locale() ] ) ) {
246 + $relevant_pagelinks = '<br><br><p><em>' . implode( '', $relevant_post_link[ get_wpbot_locale() ] ) . '</em><p>' . implode( '</br>', $relevant_pagelink );
247 + } else {
248 + $relevant_pagelinks = '<br><br><p><em>' . $relevant_post_link[ get_wpbot_locale() ] . '</em><p>' . implode( '</br>', $relevant_pagelink );
249 + }
250 + } else {
251 + $relevant_pagelinks = '';
252 + }
253 + $collection_id = get_option( 'qcld_grok_collection_id' ) ?? '';
254 + if( $collection_id != '' ) {
255 + $result = $this->direct_search_collection( $keyword );
256 + $response['status'] = 'success';
257 + $response['message'] = $result ;
258 + wp_send_json( $response );
259 + } else {
260 + $messages = [
261 + ['role' => 'system', 'content' => $grok_system_content ?? 'You are a helpful and intelligent assistant for the website "' . site_url() . '". Use live website data and the provided context to respond accurately and briefly. Stay relevant and do not introduce additional topics.' ]
262 + ];
263 +
264 + if (!empty($_POST['ai_history'])) {
265 + $parsed_history = json_decode(wp_unslash($_POST['ai_history']), true);
266 + if (is_array($parsed_history)) {
267 + foreach ($parsed_history as $h) {
268 + if (isset($h['role']) && isset($h['content'])) {
269 + $role = ($h['role'] === 'assistant') ? 'assistant' : 'user';
270 + $messages[] = [
271 + 'role' => $role,
272 + 'content' => sanitize_text_field($h['content'])
273 + ];
274 + }
275 + }
276 + }
277 + }
278 +
279 + $last_msg = end($messages);
280 + if (!$last_msg || $last_msg['role'] !== 'user' || $last_msg['content'] !== $keyword) {
281 + $messages[] = ['role' => 'user', 'content' => $keyword];
282 + }
283 +
284 + $response_mess = wp_remote_post('https://api.x.ai/v1/chat/completions', [
285 + 'headers' => [
286 + 'Authorization' => 'Bearer ' . $grok_api_key,
287 + 'Content-Type' => 'application/json',
288 + ],
289 + 'body' => wp_json_encode([
290 + 'messages' => $messages,
291 + 'model' => 'grok-3-latest',
292 + 'temperature' => 0,
293 + 'stream' => false
294 + ]),
295 + 'timeout' => 60,
296 + ]);
297 + $response['message'] = '';
298 + $status_code = wp_remote_retrieve_response_code($response_mess);
299 + $data = wp_remote_retrieve_body($response_mess);
300 + $data = json_decode($data, true);
301 + do_action('qcld_openai_user_rate_cal', 1);
302 + $response['status'] = 'success';
303 + $reply_raw = $data['choices'][0]['message']['content'] ?? 'No content received';
304 + $Parsedown = new Parsedown();
305 + $reply_text = $Parsedown->text($reply_raw);
306 + if (strpos($reply_text, 'AI_FORM_DATA') !== false) {
307 + $reply_text = Qcld_WPBot_Common_Functions::format_and_save_ai_form_response($reply_text);
308 + $response['message'] = $reply_text;
309 + } else {
310 + $response['message'] = $reply_text . $relevant_pagelinks;
311 + }
312 + wp_send_json( $response );
313 + }
314 + }
315 + public function qcld_stream_grok_callback()
316 + {
317 +
318 + if (!defined('ABSPATH')) {
319 + // Optional: protect direct access or require wp-load.php
320 + // For security → better to use admin-ajax or custom rewrite rule in production
321 + http_response_code(403);
322 + exit;
323 + }
324 +
325 + if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'wp_chatbot' ) ) {
326 + echo "data: [ERROR] Security check failed.\n\n";
327 + flush();
328 + wp_die();
329 + }
330 +
331 + if (get_option('is_rate_limiting_enabled') == '1') {
332 + do_action('rate_limit_checker');
333 + }
334 + $api_key = get_option('qcld_grok_api_key'); // ← Use get_option() or env in real code!
335 +
336 + // Disable compression (critical for streaming)
337 + if (function_exists('apache_setenv')) {
338 + @apache_setenv('no-gzip', 1);
339 + }
340 + @ini_set('zlib.output_compression', 0);
341 + while (ob_get_level() > 0) { ob_end_clean(); }
342 +
343 + set_time_limit(180); // Allow long-running requests
344 +
345 + $headers = [
346 + 'Content-Type: application/json',
347 + 'Authorization: Bearer ' . $api_key,
348 + ];
349 + $system_content = get_option( 'qcld_grok_system_content' );
350 + if (empty($system_content)) {
351 + $system_content = 'You are a helpful and intelligent assistant for the website "' . site_url() . '". Use live website data and the provided context to respond accurately and briefly. Stay relevant and do not introduce additional topics.';
352 + }
353 + // AI Interactive Form
354 + if (get_option('enable_ai_interactive_form') == '1') {
355 + $saved_ai_forms = get_option('wpbot_ai_forms', array());
356 + if (!empty($saved_ai_forms) && is_array($saved_ai_forms)) {
357 + $system_content .= "\n\nYou must handle the following interactive forms when the user asks for them:\n";
358 + foreach ($saved_ai_forms as $form) {
359 + $system_content .= "\nForm Title: " . $form['title'] . "\nInstructions: " . $form['prompt'] . "\n";
360 + }
361 + $system_content .= "\n\nCRITICAL INSTRUCTIONS FOR INTERACTIVE FORMS:\n";
362 + $system_content .= "When a user triggers an interactive form, you must act as a step-by-step data collection agent.\n";
363 + $system_content .= "1. DO NOT ask all questions at once. Ask exactly ONE question at a time.\n";
364 + $system_content .= "2. Wait for the user's response before asking the next question.\n";
365 + $system_content .= "3. Once all necessary information is collected for the form, you MUST output a final JSON block summarizing the collected data. The keys inside the \"data\" object MUST be dynamically named based on the specific questions you asked during the form collection (e.g., \"Full Name\", \"Company Size\", \"Email\", etc.). The final JSON block must be wrapped EXACTLY in these delimiters:\n";
366 + $system_content .= "__AI_FORM_DATA__{ \"form_title\": \"<Form Title>\", \"data\": { \"Question 1\": \"Answer 1\", \"Question 2\": \"Answer 2\" } }__AI_FORM_DATA_END__\n";
367 + $system_content .= "Do not include any other text after this JSON block once the form is complete.\n";
368 + $system_content .= "4. If the user provides an invalid, irrelevant, or nonsensical answer to your question, DO NOT apologize or state that you lack information. Instead, politely inform the user in the language of the conversation (the language the user is speaking in, e.g. German if communicating in German) that their answer is invalid or cannot be assigned, and ask the exact same question again in the user's language. NEVER output the English phrase 'Invalid answer found' unless the user is communicating in English.\n";
369 + $system_content .= "5. MULTI-LANGUAGE SUPPORT: Always communicate, ask questions, validate answers, and reply strictly in the user's language (matching the language used by the user, e.g. German, French, Spanish, etc.). Understand and accept valid answers in the user's language.";
370 + }
371 + }
372 + $messages = [];
373 + // Load previous conversation from cookie if continuity is enabled
374 + if ($conversation_continuity == 1 && !empty($_COOKIE['last_five_prompt'])) {
375 + $decoded_cookie = json_decode(stripslashes($_COOKIE['last_five_prompt']), true);
376 + if (is_array($decoded_cookie)) {
377 + $messages = $decoded_cookie;
378 + }
379 + }
380 + // Ensure system message is the first message.
381 + if (empty($messages) || (isset($messages[0]) && $messages[0]['role'] !== 'system')) {
382 + array_unshift($messages, ['role' => 'system', 'content' => $system_content]);
383 + } elseif (isset($messages[0]) && $messages[0]['role'] === 'system' && $messages[0]['content'] !== $system_content) {
384 + $messages[0]['content'] = $system_content;
385 + }
386 +
387 + array_push($messages, ['role' => 'user', 'content' => sanitize_text_field( wp_unslash( $_POST['message'] ) )]);
388 + $postData = json_encode([
389 + 'model' => 'grok-3-latest',
390 + 'messages' => $messages,
391 + 'stream' => true
392 + ]);
393 +
394 + // phpcs:ignore WordPress.WP.AlternativeFunctions -- streaming requires native cURL
395 + $ch = curl_init('https://api.x.ai/v1/chat/completions');
396 + curl_setopt($ch, CURLOPT_POST, true); // phpcs:ignore WordPress.WP.AlternativeFunctions
397 + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); // phpcs:ignore WordPress.WP.AlternativeFunctions
398 + curl_setopt($ch, CURLOPT_POSTFIELDS, $postData); // phpcs:ignore WordPress.WP.AlternativeFunctions
399 + curl_setopt($ch, CURLOPT_WRITEFUNCTION, function ($ch, $chunk) use (&$full_assistant_response) { // phpcs:ignore WordPress.WP.AlternativeFunctions
400 + echo $chunk; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- SSE streaming raw output
401 + echo str_repeat(' ', 1024); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- SSE streaming padding
402 + flush();
403 + ob_flush();
404 +
405 + // Parse the chunk to extract assistant's content for continuity.
406 + $lines = explode("\n", $chunk);
407 + foreach ($lines as $line) {
408 + if (strpos($line, 'data: ') === 0) {
409 + $json_data = substr($line, 6);
410 + if ($json_data === '[DONE]') {
411 + continue;
412 + }
413 + $data = json_decode($json_data, true);
414 + if (isset($data['choices'][0]['delta']['content'])) {
415 + $full_assistant_response .= $data['choices'][0]['delta']['content'];
416 + }
417 + }
418 + }
419 + return strlen($chunk);
420 + });
421 + curl_exec($ch); // phpcs:ignore WordPress.WP.AlternativeFunctions
422 + curl_close($ch); // phpcs:ignore WordPress.WP.AlternativeFunctions
423 + // phpcs:ignore WordPress.WP.AlternativeFunctions -- streaming requires native cURL
424 + $ch = curl_init('https://api.x.ai/v1/chat/completions');
425 +
426 + // phpcs:ignore WordPress.WP.AlternativeFunctions
427 + curl_setopt_array($ch, [
428 + CURLOPT_POST => true,
429 + CURLOPT_HTTPHEADER => $headers,
430 + CURLOPT_POSTFIELDS => $postData,
431 + CURLOPT_RETURNTRANSFER => false, // We echo ourselves
432 + CURLOPT_WRITEFUNCTION => function ($curl, $chunk) {
433 + echo $chunk; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- SSE streaming raw output
434 + flush(); // Push to browser immediately
435 + return strlen($chunk);
436 + },
437 + CURLOPT_TIMEOUT => 150,
438 + CURLOPT_CONNECTTIMEOUT => 30,
439 + CURLOPT_SSL_VERIFYPEER => true,
440 + CURLOPT_FOLLOWLOCATION => true,
441 + CURLOPT_ENCODING => '', // Let server decide
442 + ]);
443 +
444 + $success = curl_exec($ch); // phpcs:ignore WordPress.WP.AlternativeFunctions
445 + do_action('qcld_openai_user_rate_cal', 1);
446 + if (curl_errno($ch)) { // phpcs:ignore WordPress.WP.AlternativeFunctions
447 + $err = curl_error($ch); // phpcs:ignore WordPress.WP.AlternativeFunctions
448 + echo "data: " . wp_json_encode( array( 'error' => 'cURL error: ' . $err ) ) . "\n\n";
449 + flush();
450 + }
451 +
452 + curl_close($ch); // phpcs:ignore WordPress.WP.AlternativeFunctions
453 +
454 + // Final done signal (optional but nice)
455 + echo "data: [DONE]\n\n";
456 + flush();
457 + }
458 + public function qcld_grok_collectionlist_callback() {
459 + $nonce = sanitize_text_field( wp_unslash($_POST['nonce']) ?? '' );
460 + if ( ! wp_verify_nonce( $nonce, 'wp_chatbot' ) || ! current_user_can( 'manage_options' ) ) {
461 + wp_send_json(
462 + array(
463 + 'success' => false,
464 + 'msg' => esc_html__( 'Failed in Security check', 'chatbot'),
465 + )
466 + );
467 + wp_die();
468 + }
469 + $grok_api_key = get_option( 'qcld_grok_api_key' );
470 + $response_raw = wp_remote_get(
471 + 'https://api.x.ai/v1/collections',
472 + array(
473 + 'headers' => array(
474 + 'Authorization' => 'Bearer ' . $grok_api_key,
475 + 'Content-Type' => 'application/json',
476 + ),
477 + 'timeout' => 60,
478 + )
479 + );
480 + $response = json_decode( wp_remote_retrieve_body( $response_raw ), true );
481 + wp_send_json( $response );
482 + }
483 + public function qcld_grok_file_upload_callback()
484 + {
485 +
486 + $nonce = sanitize_text_field(wp_unslash($_POST['nonce']));
487 + if (! wp_verify_nonce($nonce, 'wp_chatbot') || ! current_user_can( 'manage_options' ) ) {
488 + wp_send_json(
489 + array(
490 + 'success' => false,
491 + 'msg' => esc_html__('Failed in Security check', 'chatbot'),
492 + )
493 + );
494 + wp_die();
495 + } else {
496 + // $uploadedfile = $_FILES['file'];
497 + // $collection_id = sanitize_text_field(wp_unslash($_POST['collection_id']));
498 + // $api_key = get_option( 'qcld_grok_api_key' );
499 +
500 + // $ch = curl_init();
501 +
502 + // $file = new CURLFile(
503 + // $uploadedfile['tmp_name'],
504 + // $uploadedfile['type'] ?: 'application/octet-stream',
505 + // $uploadedfile['name']
506 + // );
507 +
508 + // $post_data = ['file' => $file];
509 +
510 + // curl_setopt_array($ch, [
511 + // CURLOPT_URL => 'https://api.x.ai/v1/files',
512 + // CURLOPT_POST => true,
513 + // CURLOPT_POSTFIELDS => $post_data,
514 + // CURLOPT_RETURNTRANSFER => true,
515 + // CURLOPT_HTTPHEADER => [
516 + // 'Authorization: Bearer ' . $api_key,
517 + // // Do NOT add Content-Type here!!
518 + // ],
519 + // CURLOPT_TIMEOUT => 180,
520 + // CURLOPT_CONNECTTIMEOUT => 30,
521 + // ]);
522 +
523 + // $result = json_decode(curl_exec($ch));
524 + // $error = curl_error($ch);
525 + // curl_close($ch);
526 + // $file_id = $result->id
527 + $collection_id = "collection_22aa88f1-80e8-4e1a-8e13-c119afb0481c";
528 + $file_id = "file_06208083-0e27-44ff-af29-5faa211f4892";
529 + $this->add_file_to_collection($collection_id, $file_id, []);
530 + if (! empty($res->error)) {
531 + $response['status'] = 'error';
532 + $response['message'] = $res->error->message;
533 + }
534 +
535 + if (! empty($res->status)) {
536 + $response['status'] = 'success';
537 + $response['message'] = 'Successfully Created file' . $res->id;
538 + }
539 + // echo wp_send_json(array($response));
540 + // wp_die();
541 + }
542 + }
543 + public function add_file_to_collection($collection_id, $file_id, $metadata = []) {
544 + $url = "https://management-api.x.ai/v1/collections/{$collection_id}/documents/{$file_id}";
545 + $grok_management_api_key = get_option( 'qcld_grok_management_api_key' );
546 +
547 + $args = [
548 + 'method' => 'POST',
549 + 'timeout' => 30,
550 + 'redirection' => 5,
551 + 'httpversion' => '1.1',
552 + 'blocking' => true,
553 + 'headers' => [
554 + 'Authorization' => 'Bearer ' . $grok_management_api_key, // Management API key required
555 + 'Content-Type' => 'application/json',
556 + ],
557 + // 'body' => json_encode([
558 + // 'fields' => $metadata // Optional: matches your field_definitions
559 + // ]),
560 + ];
561 + // return ['success' => false, 'error' => $response->get_error_message()];
562 + $response = wp_remote_post( $url, $args );
563 + $body = wp_remote_retrieve_body( $response );
564 + $status_code = wp_remote_retrieve_response_code( $response );
565 + if ($status_code >= 200 && $status_code < 300) {
566 + return ['success' => true, 'message' => 'File added to collection', 'body' => $body];
567 + } else {
568 + return ['success' => false, 'status' => $status_code, 'error' => $body];
569 + }
570 + }
571 + public function qcld_grok_remove_collection_callback() {
572 + $nonce = sanitize_text_field( wp_unslash($_POST['nonce']) ?? '' );
573 + if ( ! wp_verify_nonce( $nonce, 'wp_chatbot' ) || ! current_user_can( 'manage_options' ) ) {
574 + wp_send_json(
575 + array(
576 + 'success' => false,
577 + 'msg' => esc_html__( 'Failed in Security check', 'chatbot'),
578 + )
579 + );
580 + wp_die();
581 + }
582 + $grok_api_key = get_option( 'qcld_grok_api_key' );
583 + $collection_id = sanitize_text_field( wp_unslash($_POST['collection_id']) ?? '' ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
584 + $url = 'https://api.x.ai/v1/collections/' . $collection_id; // Check exact endpoint in docs.x.ai
585 + $args = [
586 + 'method' => 'DELETE',
587 + 'headers' => [
588 + 'Authorization' => 'Bearer ' . $grok_api_key,
589 + 'Content-Type' => 'application/json'
590 + ],
591 + 'timeout' => 60,
592 + ];
593 + $response = wp_remote_request( $url, $args );
594 + $response_data = json_decode( wp_remote_retrieve_body( $response ), true );
595 + wp_send_json( $response_data );
596 + }
597 + public function qcld_grok_add_collection_callback() {
598 + $nonce = sanitize_text_field( wp_unslash($_POST['nonce']) ?? '' );
599 + if ( ! wp_verify_nonce( $nonce, 'wp_chatbot' ) || ! current_user_can( 'manage_options' ) ) {
600 + wp_send_json(
601 + array(
602 + 'success' => false,
603 + 'msg' => esc_html__( 'Failed in Security check', 'chatbot'),
604 + )
605 + );
606 + wp_die();
607 + }
608 + $grok_api_key = get_option( 'qcld_grok_api_key' );
609 + $collection_name = isset( $_POST['collection_name'] ) ? sanitize_text_field( wp_unslash( $_POST['collection_name'] ) ) : ( site_url() . wp_rand( 1, 999 ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
610 + $response_raw = wp_remote_post(
611 + 'https://api.x.ai/v1/collections',
612 + array(
613 + 'headers' => array(
614 + 'Authorization' => 'Bearer ' . $grok_api_key,
615 + 'Content-Type' => 'application/json',
616 + ),
617 + 'body' => wp_json_encode( array( 'collection_name' => $collection_name, 'field_definitions' => array() ) ),
618 + 'timeout' => 60,
619 + )
620 + );
621 + $response = json_decode( wp_remote_retrieve_body( $response_raw ), true );
622 + wp_send_json( $response );
623 + }
624 + public function direct_search_collection( $query ) {
625 +
626 + $collection_id = get_option( 'qcld_grok_collection_id' );
627 + $grok_api_key = get_option( 'qcld_grok_api_key' );
628 + $url = 'https://api.x.ai/v1/documents/search';
629 + $payload = [
630 + 'query' => $query,
631 + 'source' => ['collection_ids' => [$collection_id]],
632 + 'retrieval_mode' => ['type' => 'hybrid'] // or 'semantic', 'keyword'
633 + ];
634 + $args = [
635 + 'method' => 'POST',
636 + 'headers' => [
637 + 'Authorization' => 'Bearer ' . $grok_api_key,
638 + 'Content-Type' => 'application/json',
639 + ],
640 + 'body' => wp_json_encode($payload),
641 + 'timeout' => 30,
642 + ];
643 + $response = wp_remote_post($url, $args);
644 + $result = json_decode(wp_remote_retrieve_body($response), true);
645 + if (is_wp_error($response)) {
646 + return ['error' => $response->get_error_message()];
647 + }
648 + foreach ($result['matches'] as $key => $match) {
649 + if ($key >= 1) break;
650 + $responses = $match ["chunk_content"];
651 + }
652 + return $responses;
653 + }
654 + }
655 +
656 + /**
657 + * @return qcld_wpopenai_addon
658 + */
659 + if ( ! function_exists( 'qcld_wpgrok_addons' ) ) {
660 +
661 + function qcld_wpgrok_addons() {
662 + $qcld_wpgrok_addon = new qcld_wpgrok_addons();
663 + return $qcld_wpgrok_addon->instance();
664 + }
665 + }
666 +
667 + // fire off the plugin.
668 + qcld_wpgrok_addons();
669 +
668 670 }