PluginProbe
WPBot – AI ChatBot for Live Support, Lead Generation, WordPress Automation, AI Services / 8.7.8
WPBot – AI ChatBot for Live Support, Lead Generation, WordPress Automation, AI Services v8.7.8
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 8.5.6 8.5.5 8.5.4 8.5.3 All 534 releases
chatbot / includes / integration / openrouter / qcld-bot-openrouter.php

qcld-bot-openrouter.php in WPBot – AI ChatBot for Live Support, Lead Generation, WordPress Automation, AI Services 8.7.8, at includes/integration/openrouter/qcld-bot-openrouter.php

494 lines 24.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if (!defined('ABSPATH')) exit; // Exit if accessed directly
3
4 if(!class_exists('qcld_wpopenrouter_addons')){
5
6
7 /**
8 * Main Class.
9 */
10 final class qcld_wpopenrouter_addons
11 {
12 private $id = 'Open AI';
13
14 /**
15 * WPBot Pro version.
16 *
17 * @var string
18 */
19 public $version = '1.0.6';
20
21 /**
22 * WPBot Pro helper.
23 *
24 * @var object
25 */
26 public $helper;
27
28 /**
29 * The single instance of the class.
30 *
31 * @var qcld_wb_Chatbot
32 * @since 1.0.0
33 */
34 protected static $_instance = null;
35
36 /**
37 * Main wpbot Instance.
38 *
39 * Ensures only one instance of wpbot is loaded or can be loaded.
40 *
41 * @return qcld_wb_Chatbot - Main instance.
42 * @since 1.0.0
43 * @static
44 */
45 public static function instance() {
46 if ( is_null( self::$_instance ) ) {
47 self::$_instance = new self();
48 }
49
50 return self::$_instance;
51 }
52
53 public $response_list;
54
55 /**
56 * Constructor
57 */
58 public function __construct()
59 {
60
61 $this->includes();
62 add_action('wp_ajax_openai_save_assistant', [$this, 'save_assistant_callback']);
63 add_action('wp_ajax_openrouter_response',[$this,'openrouter_response_callback']);
64 add_action('wp_ajax_nopriv_openrouter_response', [$this, 'openrouter_response_callback']);
65 add_action('wp_ajax_qcld_openrouter_settings_option',[$this,'qcld_openrouter_settings_option_callback']);
66
67 add_action('wp_ajax_update_settings_option', [$this, 'qcld_update_settings_option_callback']);
68
69 if (is_admin() && !empty($_GET["page"]) && (($_GET["page"] == "openai-panel_dashboard") || ($_GET["page"] == "openai-panel_file") || ($_GET["page"] == "openai-panel_help"))) {
70 add_action('admin_enqueue_scripts', array($this, 'qcld_wb_chatbot_admin_scripts'));
71 }
72 //add_action('wp_enqueue_scripts', array($this, 'qcld_wb_chatbot_openrouter_scripts'));
73 add_action('admin_enqueue_scripts', array($this, 'qcld_wb_chatbot_openrouter_admin_scripts'));
74 }
75
76
77
78 public function qcld_wb_chatbot_openrouter_admin_scripts() {
79 if ( ! current_user_can( 'manage_options' ) ) {
80 return ;
81 }
82 wp_register_script(
83 'qcld-wp-chatbot-openrouter-admin-js',
84 QCLD_wpCHATBOT_PLUGIN_URL . 'includes/integration/openrouter/assets/js/qcld-wp-openrouter-admin.js',
85 array('jquery'),
86 QCLD_wpCHATBOT_VERSION,
87 true
88 );
89
90 // Localize the script with necessary data
91 wp_localize_script('qcld-wp-chatbot-openrouter-admin-js', 'qcld_gemini_admin_data', array(
92 'ajax_url' => admin_url('admin-ajax.php'),
93 'ajax_nonce' => wp_create_nonce('wp_chatbot'),
94 'openrouter_api_key' => get_option('qcld_openrouter_api_key'),
95 'openrouter_model' => get_option('qcld_openrouter_model'),
96 'openrouter_enabled' => get_option('qcld_openrouter_enabled'),
97 'qcld_openrouter_append_content' => get_option('qcld_openrouter_append_content'),
98 'qcld_openrouter_prepend_content' => get_option('qcld_openrouter_prepend_content')
99 ));
100
101 wp_enqueue_script('qcld-wp-chatbot-openrouter-admin-js');
102 }
103
104 /**
105 * Define wpbot Constants.
106 *
107 * @return void
108 * @since 1.0.0
109 */
110 public function includes() {
111 require_once( QCLD_wpCHATBOT_PLUGIN_DIR_PATH . "includes/Parsedown.php" );
112 require_once( QCLD_wpCHATBOT_PLUGIN_DIR_PATH . "includes/class-common-function.php" );
113 }
114 public function qcld_openrouter_settings_option_callback() {
115 $nonce = sanitize_text_field(wp_unslash($_POST['nonce']));
116 if (!wp_verify_nonce($nonce, 'wp_chatbot')) {
117 wp_send_json(array('success' => false, 'msg' => esc_html__('Failed in Security check', 'chatbot')));
118 wp_die();
119 } elseif ( ! current_user_can( 'manage_options' ) ) {
120 wp_send_json( array( 'success' => false, 'msg' => esc_html__( 'Unauthorized user', 'chatbot' ) ) );
121 wp_die();
122 } else {
123 if (get_option('is_rate_limiting_enabled') == '1') {
124 do_action('rate_limit_checker');
125 }
126 $openrouter_api_key = sanitize_text_field(wp_unslash($_POST['openrouter_api_key']));
127 $openrouter_model = sanitize_text_field(wp_unslash($_POST['openrouter_model']));
128 $openrouter_enabled = sanitize_text_field(wp_unslash($_POST['openrouter_enabled']));
129 $qcld_openrouter_page_suggestion_enabled = sanitize_text_field(wp_unslash($_POST['qcld_openrouter_page_suggestion_enabled']));
130 $opnrouter_is_context_awareness_enabled = sanitize_text_field(wp_unslash($_POST['opnrouter_is_context_awareness_enabled']));
131 $qcld_openrouter_append_content = sanitize_text_field(wp_unslash($_POST['qcld_openrouter_append_content'])) ?? '';
132 $qcld_openrouter_prepend_content = sanitize_text_field(wp_unslash($_POST['qcld_openrouter_prepend_content'])) ?? '';
133 if($openrouter_api_key != '') {
134 update_option('qcld_openrouter_api_key', $openrouter_api_key);
135 }
136 if($openrouter_model != '') {
137 update_option('qcld_openrouter_model', $openrouter_model);
138 }
139 if($openrouter_enabled != '') {
140 update_option('qcld_openrouter_enabled', $openrouter_enabled);
141 }
142 if($openrouter_enabled == '1') {
143 update_option('ai_enabled', 0);
144 update_option('qcld_gemini_enabled', 0);
145 update_option('qcld_grok_enabled', 0);
146 update_option('disable_wp_chatbot_site_search',1);
147 }
148 update_option('qcld_openrouter_page_suggestion_enabled', $qcld_openrouter_page_suggestion_enabled);
149 update_option('opnrouter_context_awareness_enabled', $opnrouter_is_context_awareness_enabled);
150 $openai_post_types = array();
151 if (isset($_POST['openai_post_type'])) {
152 $raw_post_types = wp_unslash($_POST['openai_post_type']);
153 if (is_array($raw_post_types)) {
154 $openai_post_types = array_map('sanitize_text_field', $raw_post_types);
155 } else {
156 $openai_post_types = sanitize_text_field($raw_post_types);
157 }
158 }
159 $is_page_rag_enabled = sanitize_text_field(wp_unslash($_POST['is_page_rag_enabled']));
160 update_option('qcld_openai_relevant_post', $openai_post_types);
161 update_option('is_page_rag_enabled', $is_page_rag_enabled);
162
163 update_option('qcld_openrouter_append_content', $qcld_openrouter_append_content);
164 update_option('qcld_openrouter_prepend_content', $qcld_openrouter_prepend_content);
165
166 // Add the check query
167 $openrouter_api_key = get_option('qcld_openrouter_api_key');
168 $openrouter_model = get_option('qcld_openrouter_model') ? get_option('qcld_openrouter_model') : 'openai/gpt-3.5-turbo';
169
170 $messages = array();
171 $messages[] = array(
172 'role' => 'user',
173 'content' => 'give a confirmation that you are an AI (in a small response)?'
174 );
175
176 $data = wp_json_encode(array(
177 'model' => $openrouter_model,
178 'messages' => $messages
179 ));
180
181 $api_url = 'https://openrouter.ai/api/v1/chat/completions';
182
183 $args = array(
184 'body' => $data,
185 'headers' => array(
186 'Content-Type' => 'application/json',
187 'Authorization' => 'Bearer ' . $openrouter_api_key
188 ),
189 'timeout' => 60,
190 'redirection' => 5,
191 'blocking' => true,
192 'httpversion' => '1.0',
193 'sslverify' => true,
194 );
195
196 $result = wp_remote_post($api_url, $args);
197
198 if (is_wp_error($result)) {
199 /* translators: %s: API error message. */
200 wp_send_json( array( 'status' => 'error', 'msg' => esc_html( sprintf( __( 'API request failed: %s', 'chatbot' ), $result->get_error_message() ) ) ) );
201 } else {
202 $http_code = wp_remote_retrieve_response_code($result);
203 $response_body = wp_remote_retrieve_body($result);
204 $msg = json_decode($response_body, true);
205 do_action('qcld_openai_user_rate_cal', 1);
206 if ($http_code === 200 && isset($msg['choices'][0]['message']['content'])) {
207 wp_send_json( array( 'status' => 'success', 'msg' => esc_html( $msg['choices'][0]['message']['content'] ) ) );
208 } else {
209 $error_message = isset($msg['error']['message']) ? $msg['error']['message'] : 'Invalid API setup or API request failed.';
210 wp_send_json( array( 'status' => 'error', 'msg' => esc_html( $error_message ) ) );
211 }
212 }
213 wp_die();
214 }
215 // echo wp_json_encode($openrouter_enabled);
216 wp_die();
217 }
218 public function openrouter_response_callback(){
219 if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'wp_chatbot' ) ) {
220 wp_send_json_error([
221 'status' => 'error',
222 'message' => esc_html__( 'Security check failed. Unauthorized request.', 'chatbot' )
223 ]);
224 wp_die();
225 }
226
227 $openrouter_model = get_option('qcld_openrouter_model');
228 $openrouter_api_key = get_option('qcld_openrouter_api_key');
229 $keyword = isset( $_POST['keyword'] ) ? sanitize_text_field( wp_unslash( $_POST['keyword'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing
230 $relevant_pagelink = Qcld_WPBot_Common_Functions::qcpd_relevant_pagelink($keyword);
231 $relevant_pagelink = array_slice($relevant_pagelink, 0, 5, true);
232
233 // Build context-aware messages with system instructions
234 $messages = array();
235
236 // Add system message with context if enabled
237 if ( get_option('opnrouter_context_awareness_enabled') == '1' ) {
238 $site_name = get_bloginfo('name');
239 $site_desc = get_bloginfo('description');
240
241 // Get current page URL and title more reliably
242 $current_url = '';
243 $page_title = '';
244 $page_summary = '';
245
246 // Try to get from referrer first
247 $ref = wp_get_referer();
248 if ( ! $ref && isset($_SERVER['HTTP_REFERER']) ) {
249 $ref = esc_url_raw( $_SERVER['HTTP_REFERER'] );
250 }
251
252 if ( $ref ) {
253 $current_url = $ref;
254
255 // Try to get post/page by URL
256 $post_id = url_to_postid( $ref );
257 if ( $post_id ) {
258 $page_title = get_the_title( $post_id );
259 $raw_content = get_post_field( 'post_content', $post_id );
260 $text_content = wp_strip_all_tags( $raw_content );
261 $page_summary = wp_trim_words( $text_content, 120, '' );
262 } else {
263 // If not a post/page, try to extract title from URL or use current page
264 $parsed_url = wp_parse_url( $ref );
265 if ( isset($parsed_url['path']) ) {
266 $path = trim($parsed_url['path'], '/');
267 if ( ! empty($path) ) {
268 // Try to get title from current page if we're on it
269 if ( is_singular() ) {
270 $page_title = get_the_title();
271 $raw_content = get_the_content();
272 $text_content = wp_strip_all_tags( $raw_content );
273 $page_summary = wp_trim_words( $text_content, 120, '' );
274 } elseif ( is_archive() ) {
275 $page_title = get_the_archive_title();
276 } elseif ( is_search() ) {
277 $page_title = 'Search Results';
278 } elseif ( is_404() ) {
279 $page_title = 'Page Not Found';
280 }
281 }
282 }
283 }
284 } else {
285 // Fallback to current page info
286 $scheme = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http");
287 $host = isset($_SERVER['HTTP_HOST']) ? sanitize_text_field($_SERVER['HTTP_HOST']) : '';
288 $request_uri = isset($_SERVER['REQUEST_URI']) ? sanitize_text_field($_SERVER['REQUEST_URI']) : '';
289 $current_url = esc_url_raw($scheme . '://' . $host . $request_uri);
290
291 if ( is_singular() ) {
292 $page_title = get_the_title();
293 $raw_content = get_the_content();
294 $text_content = wp_strip_all_tags( $raw_content );
295 $page_summary = wp_trim_words( $text_content, 120, '' );
296 } elseif ( is_archive() ) {
297 $page_title = get_the_archive_title();
298 } elseif ( is_search() ) {
299 $page_title = 'Search Results';
300 } elseif ( is_404() ) {
301 $page_title = 'Page Not Found';
302 }
303 }
304
305 $context_bits = array();
306 if ( $site_name ) { $context_bits[] = 'Site: ' . $site_name; }
307 if ( $site_desc ) { $context_bits[] = 'Tagline: ' . $site_desc; }
308 if ( $page_title ) { $context_bits[] = 'Page title: ' . $page_title; }
309 if ( $current_url ) { $context_bits[] = 'URL: ' . $current_url; }
310 if ( $page_summary ) { $context_bits[] = 'Page summary: ' . $page_summary; }
311
312 if ( ! empty( $context_bits ) ) {
313 $context_info = 'Context Information: ' . implode( '. ', $context_bits ) . '. Please use this context to provide more relevant and accurate responses.';
314
315 // Add system message with context
316 $messages[] = array(
317 'role' => 'system',
318 'content' => $context_info
319 );
320 }
321 }
322
323 // RAG Integration
324 if (get_option('is_page_rag_enabled') == '1') {
325 $rag_context_text = Qcld_Bot_Rag::instance()->run_rag_search($keyword);
326 if (!empty($rag_context_text) && $rag_context_text != "No knowledge base found.") {
327 $rag_context = "Relevant Knowledge Base Information:\n";
328 $rag_context .= $rag_context_text;
329 $rag_context .= "\n\nUse the above information to answer the user's question. If the answer is not in the Knowledge Base, rely on your general knowledge but mention that this information is not in the local knowledge base.";
330
331 $messages[] = array(
332 'role' => 'system',
333 'content' => $rag_context
334 );
335 }
336 }
337
338 $action_prompt = !empty($_POST['action_prompt']) ? wp_unslash($_POST['action_prompt']) : '';
339 if (!empty($action_prompt)) {
340 $messages[] = array(
341 'role' => 'system',
342 'content' => "CRITICAL ACTIVE ACTION INSTRUCTION:\n" . $action_prompt
343 );
344 }
345
346 $saved_ai_forms = get_option('wpbot_ai_forms', array());
347 if (!empty($saved_ai_forms) && is_array($saved_ai_forms)) {
348 $forms_instruction = "You must handle the following interactive forms when the user asks for them:\n";
349 foreach ($saved_ai_forms as $form) {
350 $forms_instruction .= "\nForm Title: " . $form['title'] . "\nInstructions: " . $form['prompt'] . "\n";
351 }
352 $forms_instruction .= "\n\nCRITICAL INSTRUCTIONS FOR INTERACTIVE FORMS:\n";
353 $forms_instruction .= "When a user triggers an interactive form, you must act as a step-by-step data collection agent.\n";
354 $forms_instruction .= "1. DO NOT ask all questions at once. Ask exactly ONE question at a time.\n";
355 $forms_instruction .= "2. Wait for the user's response before asking the next question.\n";
356 $forms_instruction .= "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";
357 $forms_instruction .= "__AI_FORM_DATA__{ \"form_title\": \"<Form Title>\", \"data\": { \"Question 1\": \"Answer 1\", \"Question 2\": \"Answer 2\" } }__AI_FORM_DATA_END__\n";
358 $forms_instruction .= "Do not include any other text after this JSON block once the form is complete.\n";
359 $forms_instruction .= "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.";
360 $messages[] = array(
361 'role' => 'system',
362 'content' => $forms_instruction
363 );
364 }
365
366 if (!empty($_POST['ai_history'])) {
367 $parsed_history = json_decode(wp_unslash($_POST['ai_history']), true);
368 if (is_array($parsed_history)) {
369 foreach ($parsed_history as $h) {
370 if (isset($h['role']) && isset($h['content'])) {
371 $role = ($h['role'] === 'assistant') ? 'assistant' : 'user';
372 $messages[] = array(
373 'role' => $role,
374 'content' => sanitize_text_field($h['content'])
375 );
376 }
377 }
378 }
379 }
380
381 $last_msg = end($messages);
382 if (!$last_msg || $last_msg['role'] !== 'user' || $last_msg['content'] !== $keyword) {
383 $messages[] = array(
384 'role' => 'user',
385 'content' => $keyword
386 );
387 }
388
389 if( (get_option('page_suggestion_enabled') == '1') && count($relevant_pagelink) > 0 ){
390 $relevant_post_link = maybe_unserialize(get_option('qlcd_wp_chatbot_relevant_post_link_openai'));
391 $locale = ( function_exists('get_locale') ) ? get_locale() : 'en_US';
392 if ( isset($relevant_post_link[$locale]) && is_array($relevant_post_link[$locale]) ) {
393 $relevant_pagelinks = '<br><br><p><em>' . implode('', $relevant_post_link[$locale]) . '</em><p>' . implode("</br>", $relevant_pagelink);
394 } elseif ( isset($relevant_post_link[$locale]) ) {
395 $relevant_pagelinks = '<br><br><p><em>' . $relevant_post_link[$locale] . '</em><p>' . implode("</br>", $relevant_pagelink);
396 } else {
397 $relevant_pagelinks = '<br><br><p><em></em><p>' . implode("</br>", $relevant_pagelink);
398 }
399 } else {
400 $relevant_pagelinks = '';
401 }
402 $Qcld_Parsedown = new Qcld_Parsedown();
403 $data = wp_json_encode(array(
404 'model' => $openrouter_model,
405 'messages' => $messages
406 ));
407
408 $api_url = 'https://openrouter.ai/api/v1/chat/completions';
409
410 // Use WordPress wp_remote_post for better error handling and consistency
411 $args = array(
412 'body' => $data,
413 'headers' => array(
414 'Content-Type' => 'application/json',
415 'Authorization' => 'Bearer ' . $openrouter_api_key
416 ),
417 'timeout' => 60,
418 'redirection' => 5,
419 'blocking' => true,
420 'httpversion' => '1.0',
421 'sslverify' => true,
422 );
423
424 $result = wp_remote_post($api_url, $args);
425
426 // Check for WordPress errors
427 if (is_wp_error($result)) {
428 $response['status'] = 'error';
429 $response['message'] = 'API request failed: ' . $result->get_error_message();
430 } else {
431 $http_code = wp_remote_retrieve_response_code($result);
432 $response_body = wp_remote_retrieve_body($result);
433
434 if ($http_code === 200) {
435 $msg = json_decode($response_body);
436 if(isset($msg->choices[0]->message->content)) {
437 $response['status'] = 'success';
438 $reply_text = $Qcld_Parsedown->text($msg->choices[0]->message->content);
439 if (strpos($reply_text, 'AI_FORM_DATA') !== false) {
440 $reply_text = Qcld_WPBot_Common_Functions::format_and_save_ai_form_response($reply_text);
441 $response['message'] = $reply_text;
442 } else {
443 $response['message'] = $reply_text . $relevant_pagelinks;
444 }
445 } else {
446 $response['status'] = 'error';
447 $response['message'] = 'Sorry, I encountered an error processing your AI request. Please check api key and try again later.';
448 }
449 } else {
450 $response['status'] = 'error';
451 $response['message'] = 'API request failed with HTTP code: ' . $http_code;
452 }
453 }
454 wp_send_json( $response );
455 }
456 public function qcld_update_settings_option_callback(){
457 // Verify nonce for CSRF protection
458 $nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : '';
459 if (!wp_verify_nonce($nonce, 'wp_chatbot')) {
460 wp_send_json_error(array('message' => esc_html__('Security check failed', 'chatbot')));
461 wp_die();
462 }
463
464 // Check user capability - only administrators can modify settings
465 if (!current_user_can('manage_options')) {
466 wp_send_json_error(array('message' => esc_html__('Unauthorized access', 'chatbot')));
467 wp_die();
468 }
469
470 // Proceed with option updates
471 update_option('disable_wp_chatbot_site_search', 1);
472 update_option('enable_wp_chatbot_post_content', '');
473
474 // Send success response
475 wp_send_json_success(array('message' => esc_html__('Settings updated successfully', 'chatbot')));
476 wp_die();
477 }
478 }
479
480 /**
481 * @return qcld_wpopenai_addon
482 */
483 if(!function_exists('qcld_wpopenrouter_addons')){
484 function qcld_wpopenrouter_addons() {
485 $qcld_wpopenrouter_addon = new qcld_wpopenrouter_addons();
486 return $qcld_wpopenrouter_addon->instance();
487
488 }
489 }
490
491 //fire off the plugin
492 qcld_wpopenrouter_addons();
493
494 }