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/openrouter/qcld-bot-openrouter.php +494 -446 8.6.4 → 8.8.1 View file →
@@ -1,447 +1,495 @@
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 - wp_send_json( array( 'status' => 'error', 'msg' => esc_html__( 'API request failed: ' . $result->get_error_message(), 'chatbot' ) ) );
200 - } else {
201 - $http_code = wp_remote_retrieve_response_code($result);
202 - $response_body = wp_remote_retrieve_body($result);
203 - $msg = json_decode($response_body, true);
204 - do_action('qcld_openai_user_rate_cal', 1);
205 - if ($http_code === 200 && isset($msg['choices'][0]['message']['content'])) {
206 - wp_send_json( array( 'status' => 'success', 'msg' => esc_html__( $msg['choices'][0]['message']['content'], 'chatbot' ) ) );
207 - } else {
208 - $error_message = isset($msg['error']['message']) ? $msg['error']['message'] : 'Invalid API setup or API request failed.';
209 - wp_send_json( array( 'status' => 'error', 'msg' => esc_html__( $error_message, 'chatbot' ) ) );
210 - }
211 - }
212 - wp_die();
213 - }
214 - // echo wp_json_encode($openrouter_enabled);
215 - wp_die();
216 - }
217 - public function openrouter_response_callback(){
218 - if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'wp_chatbot' ) ) {
219 - wp_send_json_error([
220 - 'status' => 'error',
221 - 'message' => esc_html__( 'Security check failed. Unauthorized request.', 'chatbot' )
222 - ]);
223 - wp_die();
224 - }
225 -
226 - $openrouter_model = get_option('qcld_openrouter_model');
227 - $openrouter_api_key = get_option('qcld_openrouter_api_key');
228 - $keyword = isset( $_POST['keyword'] ) ? sanitize_text_field( wp_unslash( $_POST['keyword'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing
229 - $relevant_pagelink = Qcld_WPBot_Common_Functions::qcpd_relevant_pagelink($keyword);
230 - $relevant_pagelink = array_slice($relevant_pagelink, 0, 5, true);
231 -
232 - // Build context-aware messages with system instructions
233 - $messages = array();
234 -
235 - // Add system message with context if enabled
236 - if ( get_option('opnrouter_context_awareness_enabled') == '1' ) {
237 - $site_name = get_bloginfo('name');
238 - $site_desc = get_bloginfo('description');
239 -
240 - // Get current page URL and title more reliably
241 - $current_url = '';
242 - $page_title = '';
243 - $page_summary = '';
244 -
245 - // Try to get from referrer first
246 - $ref = wp_get_referer();
247 - if ( ! $ref && isset($_SERVER['HTTP_REFERER']) ) {
248 - $ref = esc_url_raw( $_SERVER['HTTP_REFERER'] );
249 - }
250 -
251 - if ( $ref ) {
252 - $current_url = $ref;
253 -
254 - // Try to get post/page by URL
255 - $post_id = url_to_postid( $ref );
256 - if ( $post_id ) {
257 - $page_title = get_the_title( $post_id );
258 - $raw_content = get_post_field( 'post_content', $post_id );
259 - $text_content = wp_strip_all_tags( $raw_content );
260 - $page_summary = wp_trim_words( $text_content, 120, '…' );
261 - } else {
262 - // If not a post/page, try to extract title from URL or use current page
263 - $parsed_url = wp_parse_url( $ref );
264 - if ( isset($parsed_url['path']) ) {
265 - $path = trim($parsed_url['path'], '/');
266 - if ( ! empty($path) ) {
267 - // Try to get title from current page if we're on it
268 - if ( is_singular() ) {
269 - $page_title = get_the_title();
270 - $raw_content = get_the_content();
271 - $text_content = wp_strip_all_tags( $raw_content );
272 - $page_summary = wp_trim_words( $text_content, 120, '…' );
273 - } elseif ( is_archive() ) {
274 - $page_title = get_the_archive_title();
275 - } elseif ( is_search() ) {
276 - $page_title = 'Search Results';
277 - } elseif ( is_404() ) {
278 - $page_title = 'Page Not Found';
279 - }
280 - }
281 - }
282 - }
283 - } else {
284 - // Fallback to current page info
285 - $scheme = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http");
286 - $host = isset($_SERVER['HTTP_HOST']) ? sanitize_text_field($_SERVER['HTTP_HOST']) : '';
287 - $request_uri = isset($_SERVER['REQUEST_URI']) ? sanitize_text_field($_SERVER['REQUEST_URI']) : '';
288 - $current_url = esc_url_raw($scheme . '://' . $host . $request_uri);
289 -
290 - if ( is_singular() ) {
291 - $page_title = get_the_title();
292 - $raw_content = get_the_content();
293 - $text_content = wp_strip_all_tags( $raw_content );
294 - $page_summary = wp_trim_words( $text_content, 120, '…' );
295 - } elseif ( is_archive() ) {
296 - $page_title = get_the_archive_title();
297 - } elseif ( is_search() ) {
298 - $page_title = 'Search Results';
299 - } elseif ( is_404() ) {
300 - $page_title = 'Page Not Found';
301 - }
302 - }
303 -
304 - $context_bits = array();
305 - if ( $site_name ) { $context_bits[] = 'Site: ' . $site_name; }
306 - if ( $site_desc ) { $context_bits[] = 'Tagline: ' . $site_desc; }
307 - if ( $page_title ) { $context_bits[] = 'Page title: ' . $page_title; }
308 - if ( $current_url ) { $context_bits[] = 'URL: ' . $current_url; }
309 - if ( $page_summary ) { $context_bits[] = 'Page summary: ' . $page_summary; }
310 -
311 - if ( ! empty( $context_bits ) ) {
312 - $context_info = 'Context Information: ' . implode( '. ', $context_bits ) . '. Please use this context to provide more relevant and accurate responses.';
313 -
314 - // Add system message with context
315 - $messages[] = array(
316 - 'role' => 'system',
317 - 'content' => $context_info
318 - );
319 - }
320 - }
321 -
322 - // RAG Integration
323 - if (get_option('is_page_rag_enabled') == '1') {
324 - $rag_context_text = Qcld_Bot_Rag::instance()->run_rag_search($keyword);
325 - if (!empty($rag_context_text) && $rag_context_text != "No knowledge base found.") {
326 - $rag_context = "Relevant Knowledge Base Information:\n";
327 - $rag_context .= $rag_context_text;
328 - $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.";
329 -
330 - $messages[] = array(
331 - 'role' => 'system',
332 - 'content' => $rag_context
333 - );
334 - }
335 - }
336 -
337 - // Add user message
338 - $messages[] = array(
339 - 'role' => 'user',
340 - 'content' => $keyword
341 - );
342 -
343 - if( (get_option('page_suggestion_enabled') == '1') && count($relevant_pagelink) > 0 ){
344 -
345 - $relevant_post_link = maybe_unserialize(get_option('qlcd_wp_chatbot_relevant_post_link_openai'));
346 -
347 - // Avoid call to undefined function get_wpbot_locale()
348 - $locale = ( function_exists('get_locale') ) ? get_locale() : 'en_US';
349 - if ( isset($relevant_post_link[$locale]) && is_array($relevant_post_link[$locale]) ) {
350 - $relevant_pagelinks = '<br><br><p><em>' . implode('', $relevant_post_link[$locale]) . '</em><p>' . implode("</br>", $relevant_pagelink);
351 - } elseif ( isset($relevant_post_link[$locale]) ) {
352 - $relevant_pagelinks = '<br><br><p><em>' . $relevant_post_link[$locale] . '</em><p>' . implode("</br>", $relevant_pagelink);
353 - } else {
354 - $relevant_pagelinks = '<br><br><p><em></em><p>' . implode("</br>", $relevant_pagelink);
355 - }
356 -
357 -
358 - }else{
359 - $relevant_pagelinks = '';
360 - }
361 - $Qcld_Parsedown = new Qcld_Parsedown();
362 - $data = wp_json_encode(array(
363 - 'model' => $openrouter_model,
364 - 'messages' => $messages
365 - ));
366 -
367 - $api_url = 'https://openrouter.ai/api/v1/chat/completions';
368 -
369 - // Use WordPress wp_remote_post for better error handling and consistency
370 - $args = array(
371 - 'body' => $data,
372 - 'headers' => array(
373 - 'Content-Type' => 'application/json',
374 - 'Authorization' => 'Bearer ' . $openrouter_api_key
375 - ),
376 - 'timeout' => 60,
377 - 'redirection' => 5,
378 - 'blocking' => true,
379 - 'httpversion' => '1.0',
380 - 'sslverify' => true,
381 - );
382 -
383 - $result = wp_remote_post($api_url, $args);
384 -
385 - // Check for WordPress errors
386 - if (is_wp_error($result)) {
387 - $response['status'] = 'error';
388 - $response['message'] = 'API request failed: ' . $result->get_error_message();
389 - } else {
390 - $http_code = wp_remote_retrieve_response_code($result);
391 - $response_body = wp_remote_retrieve_body($result);
392 -
393 - if ($http_code === 200) {
394 - $msg = json_decode($response_body);
395 - if(isset($msg->choices[0]->message->content)) {
396 - $response['status'] = 'success';
397 - $response['message'] = $Qcld_Parsedown->text($msg->choices[0]->message->content) . $relevant_pagelinks;
398 - } else {
399 - $response['status'] = 'error';
400 - $response['message'] = 'Sorry, I encountered an error processing your AI request. Please check api key and try again later.';
401 - }
402 - } else {
403 - $response['status'] = 'error';
404 - $response['message'] = 'API request failed with HTTP code: ' . $http_code;
405 - }
406 - }
407 - wp_send_json( $response );
408 - }
409 - public function qcld_update_settings_option_callback(){
410 - // Verify nonce for CSRF protection
411 - $nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : '';
412 - if (!wp_verify_nonce($nonce, 'wp_chatbot')) {
413 - wp_send_json_error(array('message' => esc_html__('Security check failed', 'chatbot')));
414 - wp_die();
415 - }
416 -
417 - // Check user capability - only administrators can modify settings
418 - if (!current_user_can('manage_options')) {
419 - wp_send_json_error(array('message' => esc_html__('Unauthorized access', 'chatbot')));
420 - wp_die();
421 - }
422 -
423 - // Proceed with option updates
424 - update_option('disable_wp_chatbot_site_search', 1);
425 - update_option('enable_wp_chatbot_post_content', '');
426 -
427 - // Send success response
428 - wp_send_json_success(array('message' => esc_html__('Settings updated successfully', 'chatbot')));
429 - wp_die();
430 -}
431 - }
432 -
433 - /**
434 - * @return qcld_wpopenai_addon
435 - */
436 - if(!function_exists('qcld_wpopenrouter_addons')){
437 - function qcld_wpopenrouter_addons() {
438 - $qcld_wpopenrouter_addon = new qcld_wpopenrouter_addons();
439 - return $qcld_wpopenrouter_addon->instance();
440 -
441 - }
442 - }
443 -
444 - //fire off the plugin
445 - qcld_wpopenrouter_addons();
446 -
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, 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";
360 + $forms_instruction .= "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.";
361 + $messages[] = array(
362 + 'role' => 'system',
363 + 'content' => $forms_instruction
364 + );
365 + }
366 +
367 + if (!empty($_POST['ai_history'])) {
368 + $parsed_history = json_decode(wp_unslash($_POST['ai_history']), true);
369 + if (is_array($parsed_history)) {
370 + foreach ($parsed_history as $h) {
371 + if (isset($h['role']) && isset($h['content'])) {
372 + $role = ($h['role'] === 'assistant') ? 'assistant' : 'user';
373 + $messages[] = array(
374 + 'role' => $role,
375 + 'content' => sanitize_text_field($h['content'])
376 + );
377 + }
378 + }
379 + }
380 + }
381 +
382 + $last_msg = end($messages);
383 + if (!$last_msg || $last_msg['role'] !== 'user' || $last_msg['content'] !== $keyword) {
384 + $messages[] = array(
385 + 'role' => 'user',
386 + 'content' => $keyword
387 + );
388 + }
389 +
390 + if( (get_option('page_suggestion_enabled') == '1') && count($relevant_pagelink) > 0 ){
391 + $relevant_post_link = maybe_unserialize(get_option('qlcd_wp_chatbot_relevant_post_link_openai'));
392 + $locale = ( function_exists('get_locale') ) ? get_locale() : 'en_US';
393 + if ( isset($relevant_post_link[$locale]) && is_array($relevant_post_link[$locale]) ) {
394 + $relevant_pagelinks = '<br><br><p><em>' . implode('', $relevant_post_link[$locale]) . '</em><p>' . implode("</br>", $relevant_pagelink);
395 + } elseif ( isset($relevant_post_link[$locale]) ) {
396 + $relevant_pagelinks = '<br><br><p><em>' . $relevant_post_link[$locale] . '</em><p>' . implode("</br>", $relevant_pagelink);
397 + } else {
398 + $relevant_pagelinks = '<br><br><p><em></em><p>' . implode("</br>", $relevant_pagelink);
399 + }
400 + } else {
401 + $relevant_pagelinks = '';
402 + }
403 + $Qcld_Parsedown = new Qcld_Parsedown();
404 + $data = wp_json_encode(array(
405 + 'model' => $openrouter_model,
406 + 'messages' => $messages
407 + ));
408 +
409 + $api_url = 'https://openrouter.ai/api/v1/chat/completions';
410 +
411 + // Use WordPress wp_remote_post for better error handling and consistency
412 + $args = array(
413 + 'body' => $data,
414 + 'headers' => array(
415 + 'Content-Type' => 'application/json',
416 + 'Authorization' => 'Bearer ' . $openrouter_api_key
417 + ),
418 + 'timeout' => 60,
419 + 'redirection' => 5,
420 + 'blocking' => true,
421 + 'httpversion' => '1.0',
422 + 'sslverify' => true,
423 + );
424 +
425 + $result = wp_remote_post($api_url, $args);
426 +
427 + // Check for WordPress errors
428 + if (is_wp_error($result)) {
429 + $response['status'] = 'error';
430 + $response['message'] = 'API request failed: ' . $result->get_error_message();
431 + } else {
432 + $http_code = wp_remote_retrieve_response_code($result);
433 + $response_body = wp_remote_retrieve_body($result);
434 +
435 + if ($http_code === 200) {
436 + $msg = json_decode($response_body);
437 + if(isset($msg->choices[0]->message->content)) {
438 + $response['status'] = 'success';
439 + $reply_text = $Qcld_Parsedown->text($msg->choices[0]->message->content);
440 + if (strpos($reply_text, 'AI_FORM_DATA') !== false) {
441 + $reply_text = Qcld_WPBot_Common_Functions::format_and_save_ai_form_response($reply_text);
442 + $response['message'] = $reply_text;
443 + } else {
444 + $response['message'] = $reply_text . $relevant_pagelinks;
445 + }
446 + } else {
447 + $response['status'] = 'error';
448 + $response['message'] = 'Sorry, I encountered an error processing your AI request. Please check api key and try again later.';
449 + }
450 + } else {
451 + $response['status'] = 'error';
452 + $response['message'] = 'API request failed with HTTP code: ' . $http_code;
453 + }
454 + }
455 + wp_send_json( $response );
456 + }
457 + public function qcld_update_settings_option_callback(){
458 + // Verify nonce for CSRF protection
459 + $nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : '';
460 + if (!wp_verify_nonce($nonce, 'wp_chatbot')) {
461 + wp_send_json_error(array('message' => esc_html__('Security check failed', 'chatbot')));
462 + wp_die();
463 + }
464 +
465 + // Check user capability - only administrators can modify settings
466 + if (!current_user_can('manage_options')) {
467 + wp_send_json_error(array('message' => esc_html__('Unauthorized access', 'chatbot')));
468 + wp_die();
469 + }
470 +
471 + // Proceed with option updates
472 + update_option('disable_wp_chatbot_site_search', 1);
473 + update_option('enable_wp_chatbot_post_content', '');
474 +
475 + // Send success response
476 + wp_send_json_success(array('message' => esc_html__('Settings updated successfully', 'chatbot')));
477 + wp_die();
478 +}
479 + }
480 +
481 + /**
482 + * @return qcld_wpopenai_addon
483 + */
484 + if(!function_exists('qcld_wpopenrouter_addons')){
485 + function qcld_wpopenrouter_addons() {
486 + $qcld_wpopenrouter_addon = new qcld_wpopenrouter_addons();
487 + return $qcld_wpopenrouter_addon->instance();
488 +
489 + }
490 + }
491 +
492 + //fire off the plugin
493 + qcld_wpopenrouter_addons();
494 +
447 495 }