PluginProbe
WPBot – AI ChatBot for Live Support, Lead Generation, WordPress Automation, AI Services / 8.8.0
WPBot – AI ChatBot for Live Support, Lead Generation, WordPress Automation, AI Services v8.8.0
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 8.5.6 8.5.5 All 536 releases
chatbot / includes / chat-sessions / wpbot-chat-sessions.php

wpbot-chat-sessions.php in WPBot – AI ChatBot for Live Support, Lead Generation, WordPress Automation, AI Services 8.8.0, at includes/chat-sessions/wpbot-chat-sessions.php

1,349 lines 76.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * WPBot Sessions & Analytics — Built-in Chat Session Module
4 *
5 * Provides full chat session recording, analytics, AI Insight, and
6 * "Questions Not Answered" reporting natively in the free chatbot plugin.
7 *
8 * This module is a port of the Pro plugin's chat-session-addon.
9 * It uses the SAME database table names (wpbot_user, wpbot_conversation,
10 * wpbot_failed_response) so data is shared if the Pro addon is later activated.
11 *
12 * Everything is guarded with function_exists() checks so that when the Pro
13 * addon IS active it takes full precedence and this code is skipped entirely.
14 */
15
16 if ( ! defined( 'ABSPATH' ) ) {
17 exit;
18 }
19
20 // ─── Guard: Do not run if the Pro addon is active ────────────────────────────
21 // The Pro addon defines qcwp_chat_session_menu_fnc(); if that function already
22 // exists we skip everything here to avoid duplicate menus / conflicts.
23 if ( function_exists( 'qcwp_chat_session_menu_fnc' ) ) {
24 return;
25 }
26
27 // ─── Constants ────────────────────────────────────────────────────────────────
28 // Define our own URL / path constants for assets.
29 // Also define the legacy constant names that all copied report partials reference,
30 // so those files work without modification.
31
32 if ( ! defined( 'QCLD_CHATBOT_FREE_SESSION_PLUGIN_URL' ) ) {
33 define( 'QCLD_CHATBOT_FREE_SESSION_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
34 }
35 if ( ! defined( 'QCLD_CHATBOT_FREE_SESSION_DIR_PATH' ) ) {
36 define( 'QCLD_CHATBOT_FREE_SESSION_DIR_PATH', plugin_dir_path( __FILE__ ) );
37 }
38
39 // Legacy alias constants — used by all copied partials / report files.
40 if ( ! defined( 'QCLD_wpCHATBOT_HISTORY_PLUGIN_URL' ) ) {
41 define( 'QCLD_wpCHATBOT_HISTORY_PLUGIN_URL', QCLD_CHATBOT_FREE_SESSION_PLUGIN_URL );
42 }
43 if ( ! defined( 'QCLD_WPCHATBOT_HISTORY_DIR_PATH' ) ) {
44 define( 'QCLD_WPCHATBOT_HISTORY_DIR_PATH', QCLD_CHATBOT_FREE_SESSION_DIR_PATH );
45 }
46
47 // ─── Database Structure ───────────────────────────────────────────────────────
48 require_once QCLD_CHATBOT_FREE_SESSION_DIR_PATH . 'inc/chatsession-db-structure.php';
49
50 // ─── Admin Menu ───────────────────────────────────────────────────────────────
51 add_action( 'admin_menu', 'qcwp_chat_session_menu_fnc_free' );
52
53 function qcwp_chat_session_menu_fnc_free() {
54 // All menu registration is now handled by chatbot (qcld-wpwbot.php)
55 // We no longer register submenus here to keep the sidebar clean.
56 }
57
58 function qc_wpbot_cs_tabbed_wrapper() {
59 $active_tab = isset( $_GET['tab'] ) ? sanitize_text_field( wp_unslash( $_GET['tab'] ) ) : 'sessions';
60
61 $tabs = array(
62 'sessions' => array(
63 'label' => __( 'Chat Sessions', 'wpbot-chat-history' ),
64 'icon' => 'dashicons-format-chat',
65 ),
66 'not-answered' => array(
67 'label' => __( 'Questions Not Answered', 'wpbot-chat-history' ),
68 'icon' => 'dashicons-editor-help',
69 ),
70 'ai-insight' => array(
71 'label' => __( 'AI Insight', 'wpbot-chat-history' ),
72 'icon' => 'dashicons-lightbulb',
73 ),
74 );
75
76 if ( function_exists( 'qcpdcs_is_woowbot_active' ) && qcpdcs_is_woowbot_active() ) {
77 $tabs['woowbot-sessions'] = array(
78 'label' => __( 'ChatBot Sessions', 'wpbot-chat-history' ),
79 'icon' => 'dashicons-cart',
80 );
81 }
82
83 ?>
84 <div class="wrap qcld-main-wrapper qcld-chat-sessions-wrap">
85 <h1 style="display:none"><?php esc_html_e( 'Chat Sessions', 'wpbot-chat-history' ); ?></h1>
86
87 <div class="qcld-wp-chatbot-wrap-header">
88 <div class="qcld-wp-chatbot-wrap-header-logo">
89 <a href="#" class="qcld-wp-chatbot-wrap-site__logo">
90 <img src="<?php echo esc_url( QCLD_wpCHATBOT_IMG_URL . '/chatbot.png' ); ?>" alt="WPBot"> WPBot Control Panel
91 </a>
92 <p><strong>Core Version:</strong> v<?php echo esc_html( QCLD_wpCHATBOT_VERSION ); ?></p>
93 </div>
94 <ul class="qcld-wp-chatbot-wrap-version-wrapper">
95 <li>
96 <a class="wpchatbot-Upgrade" href="https://www.wpbot.pro/" target="_blank"><?php esc_html_e( 'Upgrade To Pro', 'chatbot' ); ?></a>
97 </li>
98 </ul>
99 </div>
100
101 <div class="qcld-wp-chatbot-wrap-header_inn qcld-chat-sessions-header">
102 <div class="qcld-wp-chatbot-wrap-header_inn_heading">
103 <h1 class="wp-heading-inline"><?php esc_html_e( 'Sessions & Analytics', 'wpbot-chat-history' ); ?></h1>
104 </div>
105 <nav class="nav-tab-wrapper qcld-chat-sessions-tabs">
106 <?php foreach ( $tabs as $tab_id => $tab ) : ?>
107 <a href="<?php echo esc_url( admin_url( 'admin.php?page=wbcs-botsessions-page&tab=' . rawurlencode( $tab_id ) ) ); ?>" class="nav-tab <?php echo $active_tab === $tab_id ? 'nav-tab-active' : ''; ?>">
108 <span class="dashicons <?php echo esc_attr( $tab['icon'] ); ?>"></span>
109 <span><?php echo esc_html( $tab['label'] ); ?></span>
110 </a>
111 <?php endforeach; ?>
112 </nav>
113 </div>
114
115 <div class="qcld-chat-sessions-tab-content">
116 <?php
117 switch ( $active_tab ) {
118 case 'not-answered':
119 if ( function_exists( 'qcld_wpbot_not_answered_question' ) ) {
120 qcld_wpbot_not_answered_question();
121 }
122 break;
123 case 'ai-insight':
124 if ( function_exists( 'qcld_wpbot_schedule_session_reporting' ) ) {
125 qcld_wpbot_schedule_session_reporting();
126 }
127 break;
128 case 'woowbot-sessions':
129 if ( function_exists( 'woowbot_cs_menu_page_callback_func' ) ) {
130 woowbot_cs_menu_page_callback_func();
131 }
132 break;
133 case 'sessions':
134 default:
135 if ( function_exists( 'qc_wpbot_cs_menu_page_callback_func' ) ) {
136 qc_wpbot_cs_menu_page_callback_func();
137 }
138 break;
139 }
140 ?>
141 </div>
142 </div>
143 <?php
144 }
145
146 // ─── Admin Scripts & Styles ───────────────────────────────────────────────────
147 add_action( 'admin_enqueue_scripts', 'qcld_wb_chatbot_session_admin_scripts_free' );
148
149 function qcld_wb_chatbot_session_admin_scripts_free( $hook ) {
150 // Only enqueue on our specific sessions page
151 if ( ! isset( $_GET['page'] ) || 'wbcs-botsessions-page' !== $_GET['page'] ) {
152 return;
153 }
154
155 wp_register_style( 'qlcd-wp-bootstrap-cs', QCLD_CHATBOT_FREE_SESSION_PLUGIN_URL . 'css/qlcd-wp-bootstrap.css', array(), QCLD_wpCHATBOT_VERSION, 'screen' );
156 wp_enqueue_style( 'qlcd-wp-bootstrap-cs' );
157
158 wp_register_style( 'qlcd-wp-bootstrap-icons-cs', QCLD_CHATBOT_FREE_SESSION_PLUGIN_URL . 'css/qlcd-wp-bootstrap-icons.css', array(), QCLD_wpCHATBOT_VERSION, 'screen' );
159 wp_enqueue_style( 'qlcd-wp-bootstrap-icons-cs' );
160
161 wp_register_style( 'qlcd-wp-dataTables-cs', QCLD_CHATBOT_FREE_SESSION_PLUGIN_URL . 'css/qlcd-wp-dataTables.css', array(), QCLD_wpCHATBOT_VERSION, 'screen' );
162 wp_enqueue_style( 'qlcd-wp-dataTables-cs' );
163
164 wp_register_style( 'qlcd-wp-session-style-cs', QCLD_CHATBOT_FREE_SESSION_PLUGIN_URL . 'reports/view/assets/style.css', array(), QCLD_wpCHATBOT_VERSION, 'screen' );
165 wp_enqueue_style( 'qlcd-wp-session-style-cs' );
166
167 wp_register_style( 'qcld-wp-chatbot-history-style', QCLD_CHATBOT_FREE_SESSION_PLUGIN_URL . 'css/history-style.css', array( 'qlcd-wp-chatbot-admin-style' ), QCLD_wpCHATBOT_VERSION, 'screen' );
168 wp_enqueue_style( 'qcld-wp-chatbot-history-style' );
169
170 // SweetAlert2 — used by admin.js for Swal.fire() and Swal.showLoading()
171 wp_register_script( 'qcld-wp-chatbot-sweetalrt-cs', QCLD_wpCHATBOT_PLUGIN_URL . 'js/sweetalrt.js', array( 'jquery' ), QCLD_wpCHATBOT_VERSION, true );
172 wp_enqueue_script( 'qcld-wp-chatbot-sweetalrt-cs' );
173
174 // admin.js depends on SweetAlert2 being loaded first
175 wp_register_script( 'qcld-wp-session-admin-cs', QCLD_CHATBOT_FREE_SESSION_PLUGIN_URL . 'js/admin.js', array( 'jquery', 'qcld-wp-chatbot-sweetalrt-cs' ), QCLD_wpCHATBOT_VERSION, true );
176 wp_enqueue_script( 'qcld-wp-session-admin-cs' );
177 wp_localize_script(
178 'qcld-wp-session-admin-cs',
179 'ajax_object',
180 array(
181 'ajax_url' => admin_url( 'admin-ajax.php' ),
182 'ajax_nonce' => wp_create_nonce( 'wpbot_session_ajax_nonce' )
183 )
184 );
185
186 wp_register_script( 'qcld-wp-dataTables-cs', QCLD_CHATBOT_FREE_SESSION_PLUGIN_URL . 'js/qcld-dataTables.min.js', array( 'jquery' ), QCLD_wpCHATBOT_VERSION, true );
187 wp_enqueue_script( 'qcld-wp-dataTables-cs' );
188 }
189
190
191 // ─── AI Insight Page Callback ─────────────────────────────────────────────────
192 function qcld_wpbot_schedule_session_reporting() {
193 wp_register_style( 'qcld-wp-chatbot-history-style', QCLD_CHATBOT_FREE_SESSION_PLUGIN_URL . 'css/history-style.css', array(), QCLD_wpCHATBOT_VERSION, 'screen' );
194 wp_enqueue_style( 'qcld-wp-chatbot-history-style' );
195 ?>
196 <div class="wrap wpbot-ai-insight-page">
197 <div class="wpbot-ai-insight-header">
198 <h2><?php echo esc_html__( 'AI Insight', 'chatbot' ); ?></h2>
199 <p><?php echo esc_html__( 'Scheduled AI summaries of your chat conversations.', 'chatbot' ); ?></p>
200 </div>
201
202 <div class="wpbot-ai-insight-locked">
203 <div class="wpbot-ai-insight-locked__icon" aria-hidden="true">
204 <span class="dashicons dashicons-lock"></span>
205 </div>
206 <h3><?php echo esc_html__( 'Feature Locked', 'chatbot' ); ?></h3>
207 <p class="wpbot-ai-insight-locked__desc">
208 <?php echo esc_html__( 'The AI Insight feature allows you to receive an AI-based summary of all chat conversations emailed directly to you on a schedule.', 'chatbot' ); ?>
209 </p>
210 <p class="wpbot-ai-insight-locked__cta">
211 <?php echo esc_html__( 'Please upgrade to WPBot Pro to unlock this feature!', 'chatbot' ); ?>
212 </p>
213 <a href="https://www.wpbot.pro/" target="_blank" rel="noopener noreferrer" class="wpbot-ai-insight-locked__btn">
214 <?php echo esc_html__( 'Upgrade to Pro', 'chatbot' ); ?>
215 </a>
216 </div>
217 </div>
218 <?php
219 }
220
221
222 // ─── Questions Not Answered Page Callback ─────────────────────────────────────
223 function qcld_wpbot_not_answered_question() {
224 global $wpdb;
225 $wpdb->show_errors = true; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange, PluginCheck.Security.DirectDB.UnescapedDBParameter
226 $table = $wpdb->prefix . 'wpbot_failed_response'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange, PluginCheck.Security.DirectDB.UnescapedDBParameter
227
228 if ( isset( $_GET['msg'] ) && $_GET['msg'] == 'success' ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
229 echo '<div class="notice notice-success"><p>Record has been Deleted Successfully!</p></div>';
230 }
231
232 if ( isset( $_GET['action'] ) && $_GET['action'] == 'deleteall' ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
233 $wpdb->query( "TRUNCATE TABLE `$table`" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
234 echo '<div class="notice notice-success"><p>All Records have been deleted successfully!</p></div>';
235 }
236
237 $sql = "SELECT * FROM $table WHERE 1 ORDER BY `id` DESC"; // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
238 $sql1 = "SELECT count(*) FROM $table WHERE 1 ORDER BY `id` DESC"; // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
239
240 $total = $wpdb->get_var( $sql1 ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter
241 $items_per_page = 30;
242 $page = isset( $_GET['cpage'] ) ? abs( (int) $_GET['cpage'] ) : 1; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
243 $offset = ( $page * $items_per_page ) - $items_per_page;
244 $sql .= " LIMIT {$offset}, {$items_per_page}";
245 $result = $wpdb->get_results( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter
246 $totalPage = ceil( $total / $items_per_page );
247 $customPagHTML = '';
248 if ( $totalPage > 1 ) {
249 $customPagHTML = '<div><span class="wpbot_pagination">Page ' . esc_html( $page ) . ' of ' . esc_html( $totalPage ) . '</span>' . paginate_links(
250 array(
251 'base' => add_query_arg( 'cpage', '%#%' ),
252 'format' => '',
253 'prev_text' => __( '« prev' ), // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
254 'next_text' => __( 'next »' ), // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
255 'total' => esc_html( $totalPage ),
256 'current' => esc_html( $page ),
257 )
258 ) . '</div>';
259 }
260
261 wp_register_style( 'qcld-wp-chatbot-history-style', QCLD_CHATBOT_FREE_SESSION_PLUGIN_URL . 'css/history-style.css', array(), QCLD_wpCHATBOT_VERSION, 'screen' );
262 wp_enqueue_style( 'qcld-wp-chatbot-history-style' );
263 ?>
264
265 <div class="sld_menu_title qcld_session_chat_menu_title">
266 <h2><?php echo esc_html__( 'Questions Not Answered', 'chatbot' ) . ' (' . intval( $total ) . ')'; ?></h2>
267 </div>
268
269 <?php if ( $customPagHTML != '' ) : ?>
270 <div class="sld_menu_title sld_menu_title_align"><?php echo wp_kses_post( $customPagHTML ); ?></div>
271 <?php endif; ?>
272
273 <?php
274 require_once QCLD_CHATBOT_FREE_SESSION_DIR_PATH . 'reports/view/partials/questions-not-answered.php';
275 }
276
277 // ─── Main Chat Sessions Page Callback ────────────────────────────────────────
278 function qc_wpbot_cs_menu_page_callback_func() {
279
280 global $wpdb;
281 $wpdb->show_errors = true; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange, PluginCheck.Security.DirectDB.UnescapedDBParameter
282
283 $tableuser = $wpdb->prefix . 'wpbot_user'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange, PluginCheck.Security.DirectDB.UnescapedDBParameter
284 $tableconversation = $wpdb->prefix . 'wpbot_conversation'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange, PluginCheck.Security.DirectDB.UnescapedDBParameter
285 $mainurl = admin_url( 'admin.php?page=wbcs-botsessions-page' );
286
287 if ( isset( $_GET['min_interaction'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
288 $mainurl .= '&min_interaction=' . intval( $_GET['min_interaction'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
289 }
290 if ( isset( $_GET['wp_user'] ) && $_GET['wp_user'] != '' ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
291 $mainurl .= '&wp_user=' . intval( $_GET['wp_user'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
292 }
293
294 $msg = '';
295
296 if ( isset( $_GET['action'] ) && $_GET['action'] == 'deleteall' ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
297 $wpdb->query( "TRUNCATE TABLE `$tableuser`" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
298 $wpdb->query( "TRUNCATE TABLE `$tableconversation`" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
299 $msg = esc_html( 'All Sessions have been deleted successfully!' );
300 }
301
302 if ( isset( $_GET['msg'] ) && $_GET['msg'] == 'success' ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
303 echo '<div class="notice notice-success"><p>Record has been Deleted Successfully!</p></div>';
304 }
305
306 if ( isset( $_GET['userid'] ) && $_GET['userid'] != '' ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
307 require_once QCLD_CHATBOT_FREE_SESSION_DIR_PATH . 'reports/view/partials/view-single-chat.php';
308 } else {
309
310 wp_register_style( 'qcld-wp-chatbot-history-style', QCLD_CHATBOT_FREE_SESSION_PLUGIN_URL . 'css/history-style.css', array(), QCLD_wpCHATBOT_VERSION, 'screen' );
311 wp_enqueue_style( 'qcld-wp-chatbot-history-style' );
312 wp_register_style( 'qcld-wp-chatbot-jquery-ui', QCLD_CHATBOT_FREE_SESSION_PLUGIN_URL . 'css/jqueryui.css', array(), '', 'screen' );
313 wp_enqueue_style( 'qcld-wp-chatbot-jquery-ui' );
314 wp_register_script( 'qcld-wp-chatsession-admin-js', QCLD_CHATBOT_FREE_SESSION_PLUGIN_URL . 'js/chatsession.js', array( 'jquery' ), QCLD_wpCHATBOT_VERSION, true );
315 wp_enqueue_script( 'qcld-wp-chatsession-admin-js' );
316 wp_localize_script(
317 'qcld-wp-chatsession-admin-js',
318 'ajax_object',
319 array(
320 'ajax_url' => admin_url( 'admin-ajax.php' ),
321 'ajax_nonce' => wp_create_nonce( 'wpbot_session_ajax_nonce' )
322 )
323 );
324 wp_register_script( 'qcld-wp-jqueryui-js', QCLD_CHATBOT_FREE_SESSION_PLUGIN_URL . 'js/jqueryui.js', array( 'jquery' ), QCLD_wpCHATBOT_VERSION, true );
325 wp_enqueue_script( 'qcld-wp-jqueryui-js' );
326
327 $where = '';
328 if ( isset( $_GET['min_interaction'] ) && $_GET['min_interaction'] != 'all' ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
329 if ( isset( $_GET['min_interaction'] ) && $_GET['min_interaction'] > 0 ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
330 $where = ' and `interaction` >= ' . intval( $_GET['min_interaction'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
331 }
332 if ( isset( $_GET['min_interaction'] ) && $_GET['min_interaction'] == 0 ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
333 $where = ' and `interaction` = 0';
334 }
335 }
336
337 $wwhere = '';
338 if ( isset( $_GET['wp_user'] ) && $_GET['wp_user'] != 'all' && $_GET['wp_user'] != 0 && $_GET['wp_user'] != '' ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
339 $wwhere = ' and `user_id` = ' . intval( $_GET['wp_user'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
340 }
341
342 $sql = "SELECT * FROM $tableuser WHERE 1 $where $wwhere ORDER BY `date` DESC"; // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
343 $sql1 = "SELECT count(*) FROM $tableuser WHERE 1 $where $wwhere"; // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
344
345 $dateFilter = '';
346 if ( isset( $_GET['FilterDate'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
347 if ( $_GET['FilterDate'] === 'LastWeek' ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
348 $dateFilter = " WHERE `date` >= CURDATE() - INTERVAL 7 DAY";
349 }
350 if ( $_GET['FilterDate'] === 'LastMonth' ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
351 $dateFilter = " WHERE `date` >= CURDATE() - INTERVAL 30 DAY";
352 }
353 if ( $_GET['FilterDate'] === 'Last3Months' ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
354 $dateFilter = " WHERE `date` >= CURDATE() - INTERVAL 90 DAY";
355 }
356 $sql = "SELECT * FROM $tableuser $dateFilter $wwhere ORDER BY `date` DESC"; // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
357 $sql1 = "SELECT count(*) FROM $tableuser $dateFilter"; // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
358 }
359
360 $total = $wpdb->get_var( $sql1 ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter
361 $items_per_page = 30;
362 $page = isset( $_GET['cpage'] ) ? abs( (int) $_GET['cpage'] ) : 1; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
363 $offset = ( $page * $items_per_page ) - $items_per_page;
364 $sql .= " LIMIT {$offset}, {$items_per_page}";
365 $result = $wpdb->get_results( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter
366 $totalPage = ceil( $total / $items_per_page );
367 $customPagHTML = '';
368 if ( $totalPage > 1 ) {
369 $customPagHTML = '<div class="qcld-session-pagination"><span class="wpbot_pagination">Page ' . esc_html( $page ) . ' of ' . esc_html( $totalPage ) . '</span>' . paginate_links(
370 array(
371 'base' => add_query_arg( 'cpage', '%#%' ),
372 'format' => '',
373 'prev_text' => __( '« prev' ), // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
374 'next_text' => __( 'next »' ), // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
375 'total' => esc_html( $totalPage ),
376 'current' => esc_html( $page ),
377 )
378 ) . '</div>';
379 }
380
381 $deleteurl = admin_url( 'admin.php?page=wbcs-botsessions-page&action=deleteall' );
382 ?>
383
384 <div class="qchero_sliders_list_wrapper qcld-session-history_menu_box">
385 <?php if ( $msg != '' ) : ?>
386 <div class="notice notice-success is-dismissible">
387 <p><?php echo esc_html( $msg ); ?></p>
388 </div>
389 <?php endif; ?>
390
391 <div class="sld_menu_title qcld-session-history_menu_title">
392 <h2><?php echo esc_html__( 'Chat Sessions', 'chatbot' ) . ' (' . intval( $total ) . ')'; ?></h2>
393 </div>
394
395 <div>
396 <?php
397 if ( isset( $_GET['FilterDate'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
398 $filterText = '';
399 if ( $_GET['FilterDate'] === 'LastWeek' ) { $filterText = 'LAST WEEK'; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended
400 if ( $_GET['FilterDate'] === 'LastMonth' ) { $filterText = 'LAST MONTH'; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended
401 if ( $_GET['FilterDate'] === 'Last3Months' ) { $filterText = 'LAST 3 MONTHS'; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended
402 echo '<div class="sld_menu_title"><em>Filtering Records by: <strong>' . esc_html( $filterText ) . '</strong></em></div>';
403 }
404 ?>
405 </div>
406
407
408 <form id="wpcs_form_sessions" action="<?php echo esc_url( $mainurl ); ?>" method="POST">
409 <?php wp_nonce_field( 'wpcs_bulk_action' ); ?>
410 <input type="hidden" name="wpbot_session_remove" />
411
412 <?php if ( ! empty( $result ) ) : ?>
413 <?php require_once QCLD_CHATBOT_FREE_SESSION_DIR_PATH . 'reports/view/partials/chatsession-table.php'; ?>
414 <?php else : ?>
415 <div class="sld_menu_title"><h2>No result found.</h2></div>
416 <?php endif; ?>
417 </form>
418 </div>
419 <?php
420 }
421 }
422
423 // ─── Request Handler (delete, export, redirect) ───────────────────────────────
424 add_action( 'init', 'qc_wp_cs_request_handle_free' );
425
426 function qc_wp_cs_request_handle_free() {
427 if ( ! is_admin() || ! current_user_can( 'manage_options' ) ) {
428 return;
429 }
430
431 global $wpdb;
432 $wpdb->show_errors = true; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange, PluginCheck.Security.DirectDB.UnescapedDBParameter
433
434 $tableuser1 = $wpdb->prefix . 'wpbot_user'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange, PluginCheck.Security.DirectDB.UnescapedDBParameter
435 $tableconversation1 = $wpdb->prefix . 'wpbot_conversation'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange, PluginCheck.Security.DirectDB.UnescapedDBParameter
436 $table = $wpdb->prefix . 'wpbot_failed_response'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange, PluginCheck.Security.DirectDB.UnescapedDBParameter
437
438 // Delete single "not answered" record.
439 if ( isset( $_GET['page'] ) && $_GET['page'] == 'wbcs-botsessions-notansweredpage' && isset( $_GET['act'] ) && $_GET['act'] == 'delete' ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
440 $userid = intval( $_GET['id'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
441 check_admin_referer( 'wpcs_delete_session_' . $userid );
442 $wpdb->delete( $table, array( 'id' => $userid ), array( '%d' ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
443 wp_safe_redirect( admin_url( 'admin.php?page=wbcs-botsessions-notansweredpage&msg=success' ) );
444 exit;
445 }
446
447 // Delete single chat session.
448 if ( isset( $_GET['page'] ) && $_GET['page'] == 'wbcs-botsessions-page' && isset( $_GET['act'] ) && $_GET['act'] == 'delete' ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
449 $userid = intval( $_GET['userid'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
450 check_admin_referer( 'wpcs_delete_session_' . $userid );
451 $wpdb->delete( $tableuser1, array( 'id' => $userid ), array( '%d' ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
452 $wpdb->delete( $tableconversation1, array( 'user_id' => $userid ), array( '%d' ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
453 wp_safe_redirect( admin_url( 'admin.php?page=wbcs-botsessions-page&msg=success' ) );
454 exit;
455 }
456
457 // Export all sessions as CSV.
458 if ( isset( $_POST['wpbot_session_export_all'] ) && isset( $_POST['wpbot_session_remove'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
459 check_admin_referer( 'wpcs_bulk_action' );
460 $users = $wpdb->get_results( "SELECT wu.`id`, wu.`session_id`, wu.`name`, wu.`email`, wu.`date`, wu.`phone`, wu.`interaction`, wc.`conversation` FROM $tableuser1 as wu, $tableconversation1 as wc WHERE 1 AND wu.id = wc.user_id LIMIT 5000" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter
461 $sessions = array();
462 if ( ! empty( $users ) ) {
463 foreach ( $users as $user ) {
464 $sessions[] = wpbot_conversations_export( $user );
465 }
466 }
467 qcld_wpbot_chatsession_download_send_headers( 'wpbot_chatsession_' . gmdate( 'Y-m-d' ) . '.csv' );
468 print wpbot_chatsession_array2csv( $sessions ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- raw CSV download, escaping would corrupt the file.
469 exit;
470 }
471
472 // Export selected sessions or delete selected sessions.
473 if ( isset( $_POST['wpbot_session_remove'] ) && ! empty( $_POST['sessions'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
474 check_admin_referer( 'wpcs_bulk_action' );
475 $userids = array_map( 'intval', $_POST['sessions'] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
476
477 if ( isset( $_POST['wpbot_session_export'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
478 $sessions = array();
479 foreach ( $userids as $userid ) {
480 $user = $wpdb->get_row( $wpdb->prepare( "SELECT wu.`id`, wu.`session_id`, wu.`name`, wu.`email`, wu.`date`, wu.`phone`, wu.`interaction`, wc.`conversation` FROM $tableuser1 as wu, $tableconversation1 as wc WHERE 1 AND wu.id = wc.user_id AND wu.id = %d", $userid ) ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter
481 $sessions[] = wpbot_conversations_export( $user );
482 }
483 qcld_wpbot_chatsession_download_send_headers( 'wpbot_chatsession_' . gmdate( 'Y-m-d' ) . '.csv' );
484 print wpbot_chatsession_array2csv( $sessions ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- raw CSV download, escaping would corrupt the file.
485 exit;
486 }
487
488 if ( isset( $_POST['wpbot_session_delete'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
489 foreach ( $userids as $userid ) {
490 $wpdb->delete( $tableuser1, array( 'id' => $userid ), array( '%d' ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
491 $wpdb->delete( $tableconversation1, array( 'user_id' => $userid ), array( '%d' ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
492 }
493 wp_safe_redirect( admin_url( 'admin.php?page=wbcs-botsessions-page&msg=success' ) );
494 exit;
495 }
496 }
497 }
498
499 // ─── Admin Footer: Email Modal ────────────────────────────────────────────────
500 add_action( 'admin_footer', 'wpcs_admin_footer_content_free' );
501
502 function wpcs_admin_footer_content_free() {
503 if ( isset( $_GET['page'] ) && $_GET['page'] == 'wbcs-botsessions-page' ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
504 ?>
505 <div id="wpcsmyModal" class="wpcsmodal">
506 <div class="wpcsmodal-content">
507 <span class="wpcsclose">&times;</span>
508 <h2><?php echo esc_html( 'Send an Email to' ); ?> <span id="wpcs_show_email"></span></h2>
509 <div class="wpcs_form_container">
510 <form id="wpcs_email_form" action="">
511 <label for="fname"><?php echo esc_html( 'Subject' ); ?></label>
512 <input type="text" class="wpcs_text_field" id="wpcs_email_subject" name="wpcs_email_subject" placeholder="Subject.." required>
513 <label for="lname"><?php echo esc_html( 'Your Message' ); ?></label>
514 <textarea id="wpcs_email_message" class="wpcs_text_field" name="wpcs_email_message" placeholder="" style="height:200px" required></textarea>
515 <input type="hidden" id="wpcs_to_email_address" value="" />
516 <input type="submit" class="wpcs_submit_field" id="wpcs_email_submit" value="Submit">
517 <span id="wpcs_email_loading" style="display:none;"><img style="width:20px;" src="<?php echo esc_url( QCLD_CHATBOT_FREE_SESSION_PLUGIN_URL . 'images/ajax-loader.gif' ); ?>"></span>
518 <span id="wpcs_email_status"></span>
519 </form>
520 </div>
521 </div>
522 </div>
523 <?php
524 }
525 }
526
527 // ─── AJAX: Send Email to User ─────────────────────────────────────────────────
528 add_action( 'wp_ajax_wpcs_send_email', 'wpcs_send_email' );
529
530 function wpcs_send_email() {
531 if ( ! current_user_can( 'manage_options' ) ) {
532 wp_die();
533 }
534 check_ajax_referer( 'wpbot_session_ajax_nonce', 'security' );
535
536 $subject = sanitize_text_field( $_POST['data']['subject'] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
537 $message = sanitize_text_field( $_POST['data']['message'] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
538 $to = sanitize_email( $_POST['data']['to'] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
539
540 global $wpdb;
541 $tableuser = $wpdb->prefix . 'wpbot_user';
542 $table_sql = esc_sql( $tableuser );
543 $cache_key = 'wpbot_user_email_' . md5( $to );
544 $user_exists = wp_cache_get( $cache_key, 'wpbot' );
545 if ( false === $user_exists ) {
546 $user_exists = $wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
547 $wpdb->prepare(
548 'SELECT id FROM `' . $table_sql . '` WHERE email = %s LIMIT 1', // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
549 $to
550 )
551 );
552 wp_cache_set( $cache_key, $user_exists, 'wpbot', 60 );
553 }
554
555 $admin_email = get_option('admin_email');
556 if ( ! $user_exists && $to !== $admin_email ) {
557 wp_send_json( array( 'status' => 'fail', 'message' => 'Invalid recipient address. Email must be a stored session email or admin email.' ) );
558 }
559
560 $url = get_site_url();
561 $url = wp_parse_url( $url );
562 $domain = $url['host'];
563 $fromEmail = 'wordpress@' . $domain;
564 $headers = array(
565 'Content-Type: text/html; charset=UTF-8',
566 'From: ' . esc_html( $domain ) . ' <' . esc_html( $fromEmail ) . '>',
567 );
568
569 $result = wp_mail( $to, $subject, $message, $headers );
570 if ( $result ) {
571 $response = array( 'status' => 'success', 'message' => 'Email has been sent successfully!' );
572 } else {
573 $response = array( 'status' => 'fail', 'message' => 'Unable to send email. Please contact your server administrator.' );
574 }
575 ob_clean();
576 echo wp_json_encode( $response );
577 die();
578 }
579
580 // ─── AJAX: Save Email Notification Preference ─────────────────────────────────
581 add_action( 'wp_ajax_session_email_notification_update', 'session_email_notification_update_free' );
582
583 function session_email_notification_update_free() {
584 if ( ! current_user_can( 'manage_options' ) ) {
585 wp_die();
586 }
587 $email_notification = sanitize_text_field( $_POST['email_notification'] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
588 update_option( 'session_email_notification_update', $email_notification );
589 wp_send_json( array( 'success' => true ) );
590 }
591
592 // ─── AJAX: Conversation Save (Frontend) ──────────────────────────────────────
593 // This is the main conversation-save handler. Guarded with function_exists
594 // so the Pro addon's definition wins if it's active.
595 if ( ! function_exists( 'qcld_wb_chatbot_conversation_save' ) ) {
596
597 function qcld_wb_chatbot_conversation_save() {
598
599 check_ajax_referer( 'qcsecretbotnonceval123qc', 'security' );
600 global $wpdb;
601
602 $tableuser = $wpdb->prefix . 'wpbot_user'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange, PluginCheck.Security.DirectDB.UnescapedDBParameter
603 $tableconversation = $wpdb->prefix . 'wpbot_conversation'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange, PluginCheck.Security.DirectDB.UnescapedDBParameter
604
605 // SECURITY FIX: Pass raw (decoded) input to qcld_wpbot_input_validation(), which now
606 // correctly runs html_entity_decode() BEFORE wp_kses(). Previously, wp_kses() ran first
607 // on entity-encoded input (&lt;img onerror=...&gt;), saw inert text, and passed it through.
608 // html_entity_decode() then revived the executable markup after sanitization had already run.
609 $raw_conversation = isset( $_POST['conversation'] ) ? wp_unslash( $_POST['conversation'] ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing
610 $conversation = qcld_wpbot_input_validation( $raw_conversation ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
611 $email = isset( $_POST['email'] ) ? sanitize_email( $_POST['email'] ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing
612 $phone = isset( $_POST['phone'] ) ? sanitize_text_field( $_POST['phone'] ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing
613 $name = isset( $_POST['name'] ) ? sanitize_text_field( $_POST['name'] ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing
614 $session_id = isset( $_POST['session_id'] ) ? sanitize_text_field( $_POST['session_id'] ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing
615 $wpuser_id = isset( $_POST['user_id'] ) ? sanitize_text_field( $_POST['user_id'] ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing
616 $source_url = isset( $_POST['source_url'] ) && ! empty( $_POST['source_url'] ) ? sanitize_url( wp_unslash( $_POST['source_url'] ) ) : ( isset( $_SERVER['HTTP_REFERER'] ) ? sanitize_url( wp_unslash( $_SERVER['HTTP_REFERER'] ) ) : '' ); // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
617 $user_agent = isset( $_POST['user_agent'] ) && ! empty( $_POST['user_agent'] ) ? sanitize_text_field( wp_unslash( $_POST['user_agent'] ) ) : ( isset( $_SERVER['HTTP_USER_AGENT'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) : '' ); // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
618 $session_mailed = isset( $_POST['session_mailed'] ) ? sanitize_text_field( $_POST['session_mailed'] ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing
619
620 // Prepend source URL to conversation.
621 $conversation = '&#x3C;ul&#x3E;&#x3C;li class=&#x22;session_start_url&#x22;&#x3E;&#x3C;span&#x3E;Source URL: &#x3C;/span&#x3E;&#x3C;a href=&#x22;' . $source_url . '&#x22;&#x3E;' . $source_url . '&#x3C;/a&#x3E;&#x3C;/li&#x3E;&#x3C;/ul&#x3E;' . $conversation;
622
623 $response = array();
624 $response['status'] = 'success';
625
626 $user_exists = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $tableuser WHERE 1 AND session_id = %s", $session_id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter
627 $is_new_insert = false;
628
629 if ( empty( $user_exists ) ) {
630 $lock_key = 'wpcs_lock_' . md5( $session_id );
631 if ( add_option( $lock_key, '1', '', 'no' ) ) {
632 $interaction = (int) substr_count( $conversation, 'wp-chat-user-msg' );
633 if ( $interaction == 0 ) {
634 $interaction = (int) substr_count( $conversation, 'woo-chat-user-msg' );
635 }
636
637 if ( $interaction != 0 ) {
638 $wpdb->insert( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange, PluginCheck.Security.DirectDB.UnescapedDBParameter
639 $tableuser,
640 array(
641 'date' => current_time( 'mysql' ),
642 'name' => $name,
643 'email' => $email,
644 'phone' => $phone,
645 'session_id' => $session_id,
646 'interaction' => $interaction,
647 'user_id' => $wpuser_id,
648 )
649 );
650 $user_id = $wpdb->insert_id; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange, PluginCheck.Security.DirectDB.UnescapedDBParameter
651 $wpdb->insert( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange, PluginCheck.Security.DirectDB.UnescapedDBParameter
652 $tableconversation,
653 array(
654 'user_id' => $user_id,
655 'conversation' => $conversation,
656 'interaction' => $interaction,
657 'environment_info' => $user_agent,
658 )
659 );
660 $is_new_insert = true;
661 }
662 delete_option( $lock_key );
663 } else {
664 $retries = 3;
665 while ( $retries > 0 ) {
666 usleep( 500000 );
667 $user_exists = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $tableuser WHERE 1 AND session_id = %s", $session_id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter
668 if ( ! empty( $user_exists ) ) {
669 break;
670 }
671 $retries--;
672 }
673 }
674 }
675
676 if ( ! $is_new_insert && ! empty( $user_exists ) ) {
677 $interaction = (int) substr_count( $conversation, 'wp-chat-user-msg' );
678 if ( $interaction == 0 ) {
679 $interaction = (int) substr_count( $conversation, 'woo-chat-user-msg' );
680 }
681
682 $user_id = isset( $user_exists->id ) ? $user_exists->id : get_current_user_id();
683 $wpdb->update( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange, PluginCheck.Security.DirectDB.UnescapedDBParameter
684 $tableuser,
685 array(
686 'date' => current_time( 'mysql' ),
687 'name' => $name,
688 'email' => $email,
689 'phone' => $phone,
690 'interaction' => $interaction,
691 'user_id' => $wpuser_id,
692 ),
693 array( 'id' => $user_id ),
694 array( '%s', '%s', '%s', '%s', '%d', '%d' ),
695 array( '%d' )
696 );
697 $wpdb->update( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange, PluginCheck.Security.DirectDB.UnescapedDBParameter
698 $tableconversation,
699 array(
700 'conversation' => $conversation,
701 'interaction' => $interaction,
702 ),
703 array( 'user_id' => $user_id ),
704 array( '%s', '%d' ),
705 array( '%d' )
706 );
707 }
708
709 // Email notification for new session.
710 if ( $is_new_insert && ( get_option( 'session_email_notification_update' ) == 'checked' ) ) {
711 $admin_email = get_option( 'admin_email' );
712 $subject = esc_html__( 'Someone has started a new chat session with ChatBot.', 'chatbot' );
713 $bodyContent = '<p>' . esc_html__( 'Hi,', 'chatbot' ) . '</p>';
714 $bodyContent .= '<p>' . esc_html__( 'Someone has started a new chat session with ChatBot. Please go to ', 'chatbot' ) . '<a href="' . admin_url() . 'admin.php?page=wbcs-botsessions-page&userid='. $user_id .'">' . esc_html__( 'Bot Sessions Dashboard', 'chatbot' ) . '</a>' . esc_html__( ' and find him/her.', 'chatbot' ) . '</p>';
715
716 $bodyContent .= '<ul>';
717 $bodyContent .= '<li>' . esc_html__( 'Session ID:', 'chatbot' ) . ' <strong>' . esc_html( $session_id ) . '</strong></li>';
718 if ( ! empty( $email ) ) {
719 $bodyContent .= '<li>' . esc_html__( 'Email:', 'chatbot' ) . ' <strong>' . esc_html( $email ) . '</strong></li>';
720 }
721 if ( ! empty( $phone ) ) {
722 $bodyContent .= '<li>' . esc_html__( 'Phone:', 'chatbot' ) . ' <strong>' . esc_html( $phone ) . '</strong></li>';
723 }
724 if ( ! empty( $source_url ) ) {
725 $bodyContent .= '<li>' . esc_html__( 'Page Link:', 'chatbot' ) . ' <strong><a href="' . esc_url( $source_url ) . '">' . esc_html( $source_url ) . '</a></strong></li>';
726 }
727 if ( ! empty( $user_agent ) ) {
728 $bodyContent .= '<li>' . esc_html__( 'Browser:', 'chatbot' ) . ' <strong>' . esc_html( $user_agent ) . '</strong></li>';
729 }
730 $bodyContent .= '</ul>';
731
732 $bodyContent .= '<p>' . esc_html__( 'Thanks', 'chatbot' ) . '</p>';
733 $bodyContent .= '<p>' . esc_html__( '(You can disable email notifications from ', 'chatbot' ) . '<a href="' . admin_url() . 'admin.php?page=wbcs-botsessions-page">' . esc_html__( 'Bot - Sessions)', 'chatbot' ) . '</a></p>';
734
735 $to = get_option( 'qlcd_wp_chatbot_admin_email' ) != '' ? get_option( 'qlcd_wp_chatbot_admin_email' ) : $admin_email;
736 $headers = array( 'Content-Type: text/html; charset=UTF-8' );
737 wp_mail( $to, $subject, $bodyContent, $headers );
738 }
739
740 // WPBot Automator Trigger - Debounced by 3 minutes
741 $cron_args = array( $session_id );
742 if ( wp_next_scheduled( 'wpbot_automator_delayed_trigger', $cron_args ) ) {
743 wp_clear_scheduled_hook( 'wpbot_automator_delayed_trigger', $cron_args );
744 }
745 wp_schedule_single_event( time() + 60, 'wpbot_automator_delayed_trigger', $cron_args );
746
747 echo wp_json_encode( $response );
748 die();
749 }
750 }
751 add_action( 'wp_ajax_qcld_wb_chatbot_conversation_save', 'qcld_wb_chatbot_conversation_save' );
752 add_action( 'wp_ajax_nopriv_qcld_wb_chatbot_conversation_save', 'qcld_wb_chatbot_conversation_save' );
753
754 // ─── AJAX: Date Filter ────────────────────────────────────────────────────────
755 add_action( 'wp_ajax_qcld_chatbot_session_date_filter', 'qcld_chatbot_session_date_filter_free' );
756
757 function qcld_chatbot_session_date_filter_free() {
758 if ( ! current_user_can( 'manage_options' ) ) {
759 wp_die();
760 }
761 check_ajax_referer( 'wpbot_session_ajax_nonce', 'security' );
762 global $wpdb;
763 $tableuser = $wpdb->prefix . 'wpbot_user'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange, PluginCheck.Security.DirectDB.UnescapedDBParameter
764 $start_date = sanitize_text_field( $_POST['start_date'] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
765 $end_date = sanitize_text_field( $_POST['end_date'] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
766 $result = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $tableuser WHERE date BETWEEN %s AND %s", $start_date, $end_date ) ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter
767 echo wp_json_encode( $result );
768 wp_die();
769 }
770
771 // ─── AJAX: Email Transcript ───────────────────────────────────────────────────
772 add_action( 'wp_ajax_wpbot_send_email_transcript', 'wpbot_send_email_transcript_free' );
773
774 function wpbot_send_email_transcript_free() {
775 if ( ! current_user_can( 'manage_options' ) ) {
776 wp_send_json( array( 'status' => 'fail', 'message' => 'Unauthorized' ) );
777 }
778 check_ajax_referer( 'wpbot_session_ajax_nonce', 'security' );
779
780 global $wpdb;
781
782 $session = trim( sanitize_text_field( $_POST['session'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
783
784 $url = wp_parse_url( get_site_url() );
785 $domain = $url['host'];
786 $admin_email = get_option( 'admin_email' );
787 $fromEmail = get_option( 'qlcd_wp_chatbot_from_email' ) ? get_option( 'qlcd_wp_chatbot_from_email' ) : 'wordpress@' . $domain;
788 $subject = 'Chat transcript by ' . get_bloginfo( 'name' );
789
790 $tableuser = $wpdb->prefix . 'wpbot_user'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange, PluginCheck.Security.DirectDB.UnescapedDBParameter
791 $tableconversation = $wpdb->prefix . 'wpbot_conversation'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange, PluginCheck.Security.DirectDB.UnescapedDBParameter
792
793 $user = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $tableuser WHERE 1 AND session_id = %s", $session ) ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter
794
795 $response = array( 'status' => 'fail', 'message' => 'Session not found.' );
796
797 if ( ! empty( $user ) ) {
798 $email = sanitize_email( $user->email ); // Use email from user record
799 if ( empty( $email ) ) {
800 wp_send_json( array( 'status' => 'fail', 'message' => 'User has no email.' ) );
801 }
802
803 $result = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $tableconversation WHERE 1 AND user_id = %d", $user->id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter
804 $bodyContent = '';
805 $bodyContent .= '<p><strong>' . esc_html__( 'User Details', 'chatbot' ) . ':</strong></p><hr>';
806 $bodyContent .= '<p>' . esc_html__( 'Name', 'chatbot' ) . ' : ' . esc_html( $user->name ) . '</p>';
807 $bodyContent .= '<p>' . esc_html__( 'Email', 'chatbot' ) . ' : ' . esc_html( $email ) . '</p>';
808 $bodyContent .= '<p><b>Conversations</b></p><p>-----------------------</p>';
809 $messages = qcld_wpch_conversation_extract( htmlspecialchars_decode( $result->conversation ) );
810 foreach ( $messages as $message ) {
811 if ( isset( $message['bot'] ) && trim( $message['bot'] ) != '' ) {
812 $bodyContent .= '<p>Chatbot : ' . esc_html( trim( $message['bot'] ) ) . '</p>';
813 }
814 if ( isset( $message['user'] ) && trim( $message['user'] ) != '' ) {
815 $bodyContent .= '<p>' . esc_html( $user->name ) . ' : ' . esc_html( trim( $message['user'] ) ) . '</p>';
816 }
817 }
818 $bodyContent .= '<p>-----------------------</p>';
819 $bodyContent .= '<p>Mail Generated on: ' . current_time( 'F j, Y, g:i a' ) . '</p>';
820 $headers = array(
821 'Content-Type: text/html; charset=UTF-8',
822 'From: ' . esc_html( $user->name ) . ' <' . esc_html( $fromEmail ) . '>',
823 'Reply-To: ' . esc_html( $user->name ) . ' <' . esc_html( $email ) . '>',
824 );
825 $result_mail = wp_mail( $email, $subject, $bodyContent, $headers );
826 if ( $result_mail ) {
827 $response = array( 'status' => 'success', 'message' => 'Email transcript sent successfully.' );
828 }
829 }
830 echo wp_json_encode( $response );
831 die();
832 }
833
834 // ─── AJAX: Forward Session to Email ──────────────────────────────────────────
835 add_action( 'wp_ajax_forward_session_to_email', 'forward_session_to_email_free' );
836
837 function forward_session_to_email_free() {
838 if ( ! current_user_can( 'manage_options' ) ) {
839 wp_send_json( array( 'success' => false, 'msg' => esc_html__( 'Insufficient permissions', 'chatbot' ) ) );
840 wp_die();
841 }
842 check_ajax_referer( 'wpbot_session_ajax_nonce', 'security' );
843 global $wpdb;
844
845 $session_id = sanitize_text_field( $_POST['session_id'] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
846 $to = sanitize_email( $_POST['email'] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
847 $tableuser = $wpdb->prefix . 'wpbot_user'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange, PluginCheck.Security.DirectDB.UnescapedDBParameter
848 $tableconversation = $wpdb->prefix . 'wpbot_conversation'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange, PluginCheck.Security.DirectDB.UnescapedDBParameter
849
850 $userinfo = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $tableuser WHERE 1 AND session_id = %s", $session_id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter
851 $result = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $tableconversation WHERE 1 AND user_id = %d", $userinfo->id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter
852
853 if ( ! empty( $result ) ) {
854 $raw_html = isset( $result->conversation ) ? (string) $result->conversation : '';
855 $decoded_content = html_entity_decode( $raw_html );
856 $doc = new DOMDocument();
857 libxml_use_internal_errors( true );
858 $doc->loadHTML( '<meta http-equiv="Content-Type" content="text/html; charset=utf-8">' . $decoded_content );
859 libxml_clear_errors();
860
861 $decoded_content = '';
862 $body_nodes = $doc->getElementsByTagName( 'body' );
863 if ( $body_nodes->length > 0 ) {
864 foreach ( $body_nodes->item( 0 )->childNodes as $child_node ) {
865 $decoded_content .= $doc->saveHTML( $child_node );
866 }
867 }
868
869 $email_body = '<!DOCTYPE html><html><head><style>
870 .wp-chatbot-messages-container { list-style: none; padding: 20px; background: #f4f7f6; font-family: sans-serif; }
871 .wp-chatbot-msg { margin-bottom: 15px; display: flex; flex-wrap: wrap; }
872 .wp-chatbot-agent { font-weight: bold; color: #333; display: block; margin-bottom: 4px; }
873 .wp-chatbot-paragraph { background: #ffffff; padding: 10px; border-radius: 8px; border: 1px solid #ddd; flex: 1; }
874 .wp-chat-user-msg { display: flex; flex-wrap: wrap; flex-direction: row-reverse; }
875 .wp-chat-user-msg .wp-chatbot-paragraph { background: #ffffff; padding: 10px; border-radius: 8px; border: 1px solid #ddd; flex: none; }
876 body ul { width:100%; max-width: 640px; list-style: none; padding: 0; margin: 0 auto; }
877 </style></head><body>' . $decoded_content . '</body></html>';
878
879 $subject = isset( $_POST['subject'] ) ? sanitize_text_field( wp_unslash( $_POST['subject'] ) ) : 'Chat Session Transcript'; // phpcs:ignore WordPress.Security.NonceVerification.Missing
880 if ( empty( $subject ) ) { $subject = 'Chat Session Transcript'; }
881 $headers = array( 'Content-Type: text/html; charset=UTF-8' );
882 wp_mail( $to, $subject, $email_body, $headers );
883 wp_send_json( array( 'success' => true, 'msg' => esc_html__( 'Session has been forwarded to email successfully', 'chatbot' ) ) );
884 wp_die();
885 } else {
886 wp_send_json( array( 'success' => false, 'msg' => esc_html__( 'No conversation found for this session', 'chatbot' ) ) );
887 wp_die();
888 }
889 }
890
891 // ─── AJAX: Session Hover Details ─────────────────────────────────────────────
892 add_action( 'wp_ajax_wpbot_session_hover_details', 'wpbot_session_hover_details_free' );
893
894 function wpbot_session_hover_details_free() {
895 if ( ! current_user_can( 'manage_options' ) ) {
896 wp_send_json( array( 'success' => false, 'msg' => esc_html__( 'Insufficient permissions', 'chatbot' ) ) );
897 wp_die();
898 }
899 check_ajax_referer( 'wpbot_session_ajax_nonce', 'security' );
900 global $wpdb;
901 $session_id = sanitize_text_field( $_POST['session_id'] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
902 $tableconversation = $wpdb->prefix . 'wpbot_conversation';
903 $tableuser = $wpdb->prefix . 'wpbot_user';
904 $email_from = get_option( 'qlcd_wp_chatbot_from_email' );
905 $result = $wpdb->get_row( $wpdb->prepare( "SELECT c.*, u.email, u.name, u.session_id as user_session_id FROM $tableconversation AS c LEFT JOIN $tableuser AS u ON c.user_id = u.id WHERE c.user_id = %d", $session_id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter
906 if ( ! empty( $result ) ) {
907 $result->email_from = $email_from;
908 $result->status = 'success';
909
910 // SECURITY FIX: The stored conversation is entity-encoded (htmlspecialchars output).
911 // Decode it and re-sanitize with wp_kses before returning to the admin UI.
912 // This guarantees admin.js always receives clean, safe HTML — no onerror/onclick can survive.
913 if ( isset( $result->conversation ) ) {
914 $decoded = html_entity_decode( (string) $result->conversation, ENT_QUOTES | ENT_HTML5, 'UTF-8' );
915 $result->conversation = wp_kses( $decoded, wpbot_get_safe_conversation_tags() );
916 }
917 }
918 echo wp_json_encode( $result );
919 wp_die();
920 }
921
922 // ─── AJAX: Send Reply Email ───────────────────────────────────────────────────
923 add_action( 'wp_ajax_wpbot_send_reply_email', 'wpbot_send_reply_email_free' );
924
925 function wpbot_send_reply_email_free() {
926 if ( ! current_user_can( 'manage_options' ) ) {
927 wp_send_json( array( 'success' => false, 'msg' => esc_html__( 'Insufficient permissions', 'chatbot' ) ) );
928 wp_die();
929 }
930 check_ajax_referer( 'wpbot_session_ajax_nonce', 'security' );
931
932 $to = isset( $_POST['email'] ) ? sanitize_email( $_POST['email'] ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing
933 $from_raw = isset( $_POST['from_email'] ) ? trim( wp_unslash( $_POST['from_email'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing
934 $from = sanitize_email( $from_raw );
935 $subject = isset( $_POST['subject'] ) ? sanitize_text_field( wp_unslash( $_POST['subject'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing
936 $message = isset( $_POST['message'] ) ? wp_kses_post( wp_unslash( $_POST['message'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing
937
938 if ( empty( $to ) || empty( $message ) ) {
939 wp_send_json( array( 'success' => false, 'msg' => esc_html__( 'Email and Message are required', 'chatbot' ) ) );
940 wp_die();
941 }
942
943 // if ( ! empty( $from_raw ) && ( $from_raw !== $from || ! is_email( $from ) ) ) {
944 // wp_send_json( array( 'success' => false, 'msg' => esc_html__( 'Please enter a valid From email address', 'chatbot' ) ) );
945 // wp_die();
946 // }
947
948 $headers = array( 'Content-Type: text/html; charset=UTF-8' );
949 if ( ! empty( $from ) && is_email( $from ) ) {
950 $headers[] = 'From: ' . $from;
951 }
952
953 // Convert newlines to HTML line breaks
954 $email_body = nl2br( $message );
955
956 $sent = wp_mail( $to, $subject, $email_body, $headers );
957
958 if ( $sent ) {
959 wp_send_json( array( 'success' => true, 'msg' => esc_html__( 'Email replied successfully', 'chatbot' ) ) );
960 } else {
961 wp_send_json( array( 'success' => false, 'msg' => esc_html__( 'Failed to send email', 'chatbot' ) ) );
962 }
963 wp_die();
964 }
965
966 // ─── AJAX: Save Cron Settings ─────────────────────────────────────────────────
967 add_action( 'wp_ajax_wpbot_seesion_corn_save', 'wpbot_seesion_corn_save_free' );
968
969 function wpbot_seesion_corn_save_free() {
970 if ( ! current_user_can( 'manage_options' ) ) {
971 wp_send_json( array( 'success' => false, 'msg' => esc_html__( 'Insufficient permissions', 'chatbot' ) ) );
972 wp_die();
973 }
974
975 $wbsession_ai_enabled = isset( $_POST['ai_enabled'] ) ? sanitize_text_field( $_POST['ai_enabled'] ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing
976 $wbsession_corn_schedule_interval = isset( $_POST['corn_schedule_interval'] ) ? sanitize_text_field( $_POST['corn_schedule_interval'] ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing
977 $qcld_wpsession_corn_promt = isset( $_POST['qcld_wpsession_corn_promt'] ) ? sanitize_text_field( $_POST['qcld_wpsession_corn_promt'] ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing
978 $qcld_wbsession_corn_starttime = isset( $_POST['qcld_wbsession_corn_starttime'] ) ? sanitize_text_field( $_POST['qcld_wbsession_corn_starttime'] ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing
979
980 $openai_enabled = get_option( 'ai_enabled' );
981 $apiKey = get_option( 'open_ai_api_key' );
982
983 if ( $openai_enabled != '1' ) {
984 wp_send_json( array( 'success' => false, 'icon' => 'error', 'response' => esc_html__( 'OpenAI is Not Enabled', 'chatbot' ) ) );
985 wp_die();
986 }
987 if ( $apiKey == '' ) {
988 wp_send_json( array( 'success' => false, 'icon' => 'error', 'response' => esc_html__( 'OpenAI API key is not set', 'chatbot' ) ) );
989 wp_die();
990 }
991
992 update_option( 'qcld_wbsession_ai_enable', $wbsession_ai_enabled );
993 update_option( 'qcld_wbsession_corn_interval', $wbsession_corn_schedule_interval );
994 update_option( 'qcld_wbsession_corn_starttime', $qcld_wbsession_corn_starttime );
995 update_option( 'qcld_wpsession_corn_promt', $qcld_wpsession_corn_promt );
996
997 wp_clear_scheduled_hook( 'qcld_wpsession_mysql_scraper_event' );
998 wp_send_json( array( 'success' => true, 'icon' => 'success', 'response' => esc_html__( 'Settings Saved Successfully', 'chatbot' ) ) );
999 wp_die();
1000 }
1001
1002 // ─── AJAX: Manual AI Scraper ──────────────────────────────────────────────────
1003 add_action( 'wp_ajax_qcld_chatbot_session_mannual_scraper', 'qcld_chatbot_session_mannual_scraper_free' );
1004
1005 if ( ! function_exists( 'qcld_chatbot_session_mannual_scraper_free' ) ) {
1006 function qcld_chatbot_session_mannual_scraper_free() {
1007 if ( ! current_user_can( 'manage_options' ) ) {
1008 wp_send_json_error( array( 'msg' => 'Insufficient permissions.' ) );
1009 wp_die();
1010 }
1011 if ( get_option( 'qcld_wbsession_ai_enable' ) != '1' ) {
1012 wp_send_json( array( 'success' => false, 'icon' => 'error', 'response' => esc_html__( 'AI Insight is not enabled.', 'chatbot' ) ) );
1013 wp_die();
1014 }
1015
1016 global $wpdb;
1017 $num = isset( $_POST['wpchatbot_session_mannual_number'] ) ? intval( $_POST['wpchatbot_session_mannual_number'] ) : 20; // phpcs:ignore WordPress.Security.NonceVerification.Missing
1018 if ( ! is_numeric( $num ) || $num <= 0 ) {
1019 wp_send_json( array( 'success' => false, 'icon' => 'error', 'response' => esc_html__( 'Invalid number of sessions.', 'chatbot' ) ) );
1020 wp_die();
1021 }
1022
1023 $tableuser = $wpdb->prefix . 'wpbot_user';
1024 $tableconversation = $wpdb->prefix . 'wpbot_conversation';
1025 $results = $wpdb->get_results( $wpdb->prepare( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter
1026 "SELECT u.id, c.user_id, u.date, u.session_id, c.id AS conversation_id, c.conversation
1027 FROM $tableuser AS u LEFT JOIN $tableconversation AS c ON u.id = c.user_id
1028 ORDER BY u.date DESC LIMIT %d", $num ) // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
1029 );
1030
1031 $remarkable_session = array();
1032 foreach ( $results as $row ) {
1033 $trimmed = wpsession_message_html_filter_free( htmlspecialchars_decode( $row->conversation ) );
1034 $remarkable_session[] = array( 'id' => $row->session_id, 'conversation' => $trimmed );
1035 }
1036
1037 $keyword = get_option( 'qcld_wpsession_corn_promt' ) ?: 'Below is Chat conversation session data from our users on our website. Each conversation starts with an ID. Can you analyze each conversation and summarize each of them? Note down the total number of conversations you analyzed. Create a condensed summary at the end of your report for all the conversations. Point out the important questions asked by the users below the summary and include the IDs for the important points.';
1038 $gptkeyword = array(
1039 array( 'role' => 'system', 'content' => array( array( 'type' => 'input_text', 'text' => $keyword ) ) ),
1040 array( 'role' => 'user', 'content' => array( array( 'type' => 'input_text', 'text' => wp_json_encode( $remarkable_session ) ) ) ),
1041 );
1042
1043 $api_key = get_option( 'open_ai_api_key' );
1044 $engines = get_option( 'openai_engines' );
1045 $post_fields = array( 'model' => $engines, 'input' => $gptkeyword );
1046
1047 $api_response = wp_remote_post(
1048 'https://api.openai.com/v1/responses',
1049 array(
1050 'headers' => array(
1051 'Content-Type' => 'application/json',
1052 'Authorization' => 'Bearer ' . $api_key,
1053 ),
1054 'body' => wp_json_encode( $post_fields ),
1055 'timeout' => 60,
1056 )
1057 );
1058 $result = is_wp_error( $api_response ) ? '' : wp_remote_retrieve_body( $api_response );
1059
1060 $mess = json_decode( $result );
1061 if ( ! empty( $mess->error ) ) {
1062 wp_send_json( array( 'status' => 'error', 'icon' => 'error', 'msg' => esc_html( $mess->error->code ), 'response' => esc_html( $mess->error->message ) ) );
1063 wp_die();
1064 }
1065
1066 $msg = isset( $mess->output[0]->content ) ? $mess->output[0]->content[0]->text : ( $mess->output[1]->content[0]->text ?? '' );
1067 $msg = preg_replace( "/\r\n|\r|\n/", '<br/>', $msg );
1068
1069 $to = get_option( 'qlcd_wp_chatbot_admin_email' ) ?: get_option( 'admin_email' );
1070 $headers = array(
1071 'Content-Type: text/html; charset=UTF-8',
1072 'From: ' . esc_html( get_bloginfo( 'name' ) ) . ' <wordpress@' . wp_parse_url( get_site_url(), PHP_URL_HOST ) . '>',
1073 );
1074 wp_mail( $to, 'ChatBot Sessions Analysis', $msg, $headers );
1075
1076 wp_send_json( array( 'status' => 'success', 'icon' => 'success', 'response' => 'Please Check Email for Report' ) );
1077 wp_die();
1078 }
1079 }
1080
1081 // ─── Helper: HTML filter for AI scraper ──────────────────────────────────────
1082 if ( ! function_exists( 'wpsession_message_html_filter_free' ) ) {
1083 function wpsession_message_html_filter_free( $html ) {
1084 $dom = new DOMDocument();
1085 libxml_use_internal_errors( true );
1086 $dom->loadHTML( '<?xml encoding="utf-8" ?>' . $html );
1087
1088 $lis = $dom->getElementsByTagName( 'li' );
1089 $full_conversation = array();
1090 foreach ( $lis as $li ) {
1091 $agentDiv = $li->getElementsByTagName( 'div' )->item( 1 );
1092 $paragraphDiv = $li->getElementsByTagName( 'div' )->item( 2 );
1093 if ( $paragraphDiv ) {
1094 $full_conversation[] = array(
1095 'id' => trim( $agentDiv->textContent ),
1096 'conversation' => trim( $paragraphDiv->textContent ),
1097 );
1098 }
1099 }
1100 return wp_json_encode( $full_conversation );
1101 }
1102 }
1103
1104 // ─── Helper: Conversation Extract ────────────────────────────────────────────
1105 if ( ! function_exists( 'qcld_wpch_conversation_extract' ) ) {
1106 function qcld_wpch_conversation_extract( $html ) {
1107 $doc = new DOMDocument();
1108 libxml_use_internal_errors( true );
1109 $doc->loadHTML( '<meta http-equiv="Content-Type" content="text/html; charset=utf-8">' . $html );
1110 $lis = iterator_to_array( $doc->getElementsByTagName( 'li' ) );
1111 $messages = array();
1112 foreach ( $lis as $li ) {
1113 if ( strpos( $li->getAttribute( 'class' ), 'wp-chatbot-msg' ) !== false ) {
1114 $messages[]['bot'] = trim( $li->textContent );
1115 }
1116 if ( strpos( $li->getAttribute( 'class' ), 'wp-chat-user-msg' ) !== false ) {
1117 $messages[]['user'] = trim( $li->textContent );
1118 }
1119 }
1120 $messages = array_filter( $messages, function( $val ) {
1121 if ( isset( $val['bot'] ) && empty( $val['bot'] ) ) { return false; }
1122 return true;
1123 } );
1124 return $messages;
1125 }
1126 }
1127
1128 // ─── CSV Export Helpers ───────────────────────────────────────────────────────
1129 add_action( 'admin_post_wpbot_conversations.csv', 'wpbot_conversations_csv_export_free' );
1130
1131 function wpbot_conversations_csv_export_free() {
1132 if ( ! current_user_can( 'manage_options' ) ) {
1133 wp_die( esc_html__( 'Unauthorized', 'chatbot' ) );
1134 }
1135
1136 if ( ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( sanitize_key( $_GET['_wpnonce'] ), 'wpbot_conversations_csv' ) ) {
1137 wp_die( esc_html__( 'Security check failed.', 'chatbot' ) );
1138 }
1139
1140 global $wpdb;
1141 $tableuser = $wpdb->prefix . 'wpbot_user'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange, PluginCheck.Security.DirectDB.UnescapedDBParameter
1142 $tableconversation = $wpdb->prefix . 'wpbot_conversation'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange, PluginCheck.Security.DirectDB.UnescapedDBParameter
1143 $userid = sanitize_text_field( $_GET['user_id'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1144
1145 $userinfo = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $tableuser WHERE 1 AND id = %d", $userid ) ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter
1146 $result = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $tableconversation WHERE 1 AND user_id = %d", $userid ) ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter
1147 $data = array();
1148
1149 if ( ! empty( $result ) ) {
1150 $data[] = array( 'User Name', $userinfo->name );
1151 $data[] = array( 'User Email', $userinfo->email );
1152 $data[] = array( 'Session ID', $userinfo->session_id );
1153 $data[] = array( 'Date', gmdate( 'M,d,Y h:i:s A', strtotime( $userinfo->date ) ) );
1154 $data[] = array( 'Bot Message', 'User Message' );
1155 $messages = qcld_wpch_conversation_extract( htmlspecialchars_decode( $result->conversation ) );
1156 foreach ( $messages as $message ) {
1157 if ( isset( $message['bot'] ) && trim( $message['bot'] ) != '' ) {
1158 $data[] = array( str_replace( '&nbsp;', ' ', trim( $message['bot'] ) ), '' );
1159 }
1160 if ( isset( $message['user'] ) && trim( $message['user'] ) != '' ) {
1161 $data[] = array( '', str_replace( '&nbsp;', ' ', trim( $message['user'] ) ) );
1162 }
1163 }
1164 }
1165 qcld_wpbot_chatsession_download_send_headers( $userinfo->name . '_wpbot_chatsession_' . gmdate( 'Y-m-d' ) . '.csv' );
1166 print wpbot_chatsession_array2csv( $data ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- raw CSV download, escaping would corrupt the file.
1167 }
1168
1169 if ( ! function_exists( 'wpbot_conversations_export' ) ) {
1170 function wpbot_conversations_export( $user ) {
1171 $user_id = isset( $user->id ) ? $user->id : $user;
1172 $dataArray = array();
1173 if ( ! empty( $user ) ) {
1174 $messages = qcld_wpch_conversation_extract( htmlspecialchars_decode( $user->conversation ) );
1175 $dataArray = array(
1176 'Session ID' => $user->session_id,
1177 'Date' => gmdate( 'M,d,Y h:i:s A', strtotime( $user->date ) ),
1178 'User Name' => $user->name,
1179 'User Email' => $user->email,
1180 );
1181 $conversations = '';
1182 foreach ( $messages as $message ) {
1183 if ( isset( $message['bot'] ) && trim( $message['bot'] ) != '' ) {
1184 $conversations .= 'Bot Message: ' . str_replace( '&nbsp;', ' ', trim( $message['bot'] ) ) . "\n";
1185 }
1186 if ( isset( $message['user'] ) && trim( $message['user'] ) != '' ) {
1187 $conversations .= 'User Message: ' . str_replace( '&nbsp;', ' ', trim( $message['user'] ) ) . "\n";
1188 }
1189 }
1190 $dataArray['Conversations'] = $conversations;
1191 }
1192 $dataArray['Interaction'] = $user->interaction;
1193 return $dataArray;
1194 }
1195 }
1196
1197 if ( ! function_exists( 'qcld_wpbot_chatsession_download_send_headers' ) ) {
1198 function qcld_wpbot_chatsession_download_send_headers( $filename ) {
1199 $now = gmdate( 'D, d M Y H:i:s' );
1200 header( 'Expires: Tue, 03 Jul 2001 06:00:00 GMT' );
1201 header( 'Cache-Control: max-age=0, no-cache, must-revalidate, proxy-revalidate' );
1202 header( "Last-Modified: {$now} GMT" );
1203 header( 'Content-Encoding: UTF-8' );
1204 header( 'Content-type: text/csv; charset=UTF-8' );
1205 header( "Content-Disposition: attachment;filename={$filename}" );
1206 header( 'Content-Transfer-Encoding: binary' );
1207 }
1208 }
1209
1210 if ( ! function_exists( 'wpbot_chatsession_array2csv' ) ) {
1211 function wpbot_chatsession_array2csv( array &$array ) {
1212 if ( count( $array ) == 0 ) { return null; }
1213 ob_start();
1214 // phpcs:disable WordPress.WP.AlternativeFunctions.file_system_operations_fopen, WordPress.WP.AlternativeFunctions.file_system_operations_fputs, WordPress.WP.AlternativeFunctions.file_system_operations_fclose -- php://output memory stream for CSV export.
1215 $df = fopen( 'php://output', 'w' );
1216 fputs( $df, chr( 0xEF ) . chr( 0xBB ) . chr( 0xBF ) ); // UTF-8 BOM
1217 foreach ( $array as $data ) {
1218 fputcsv( $df, array_keys( $data ), ',', '"', '\\' );
1219 break;
1220 }
1221 foreach ( $array as $row ) {
1222 fputcsv( $df, $row, ',', '"', '\\' );
1223 }
1224 fclose( $df );
1225 // phpcs:enable WordPress.WP.AlternativeFunctions.file_system_operations_fopen, WordPress.WP.AlternativeFunctions.file_system_operations_fputs, WordPress.WP.AlternativeFunctions.file_system_operations_fclose
1226 return ob_get_clean();
1227 }
1228 }
1229
1230 // ─── Shortcode: User Session History ─────────────────────────────────────────
1231 if ( ! function_exists( 'qc_current_user_session' ) ) {
1232 function qc_current_user_session() {
1233 $user = wp_get_current_user();
1234 global $wpdb;
1235 $tableuser = $wpdb->prefix . 'wpbot_user';
1236 $conversatios_table = $wpdb->prefix . 'wpbot_conversation';
1237 $result = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $tableuser AS u LEFT JOIN $conversatios_table AS c ON u.user_id = c.user_id WHERE u.user_id = %d", $user->ID ) ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter
1238 if ( ! $user->exists() ) {
1239 return '<p>No user logged in.</p>';
1240 }
1241 ob_start(); ?>
1242 <style>
1243 .cell-content #wp-chatbot-messages-container { height: 200px; overflow: scroll; overflow-x: hidden; }
1244 .session_start_url { display: none; }
1245 .table-striped tr { border: 2px solid #888; }
1246 </style>
1247 <table class="table table-striped align-middle" id="chatsession-table">
1248 <thead>
1249 <tr class="table-primary">
1250 <th class="text-left"><?php echo esc_html__( 'Date', 'chatbot' ); ?></th>
1251 <th class="text-left"><?php echo esc_html__( 'Session ID', 'chatbot' ); ?></th>
1252 <th class="text-left"><?php echo esc_html__( 'Name', 'chatbot' ); ?></th>
1253 <th class="text-left" data-dt-order="disable"><?php echo esc_html__( 'Conversation', 'chatbot' ); ?></th>
1254 </tr>
1255 <?php foreach ( $result as $key => $value ) : ?>
1256 <tr>
1257 <td class="text-left"><?php echo esc_html( $value->date ); ?></td>
1258 <td class="text-left"><?php echo esc_html( $value->session_id ); ?></td>
1259 <td class="text-left"><?php echo esc_html( $value->name ); ?></td>
1260 <td class="text-left"><div class="cell-content"><a class="qcld-modal-content" data-value="<?php echo esc_attr( $value->conversation ); ?>">view data</a></div></td>
1261 </tr>
1262 <?php endforeach; ?>
1263 </thead>
1264 </table>
1265 <?php
1266 return ob_get_clean();
1267 }
1268 }
1269 add_shortcode( 'qcpress_user', 'qc_current_user_session' );
1270
1271 // ─── WP-Cron: AI Insight Scheduled Email ─────────────────────────────────────
1272 add_filter( 'cron_schedules', 'qcld_wpsession_wp_cron_schedule_free' );
1273
1274 if ( ! function_exists( 'qcld_wpsession_wp_cron_schedule_free' ) ) {
1275 function qcld_wpsession_wp_cron_schedule_free( $schedules ) {
1276 $schedules['session_schedules'] = array(
1277 'interval' => ( get_option( 'qcld_wbsession_corn_interval' ) != null ) ? get_option( 'qcld_wbsession_corn_interval' ) : 86400,
1278 'display' => esc_attr__( 'Session min', 'chatbot' ),
1279 );
1280 return $schedules;
1281 }
1282 }
1283
1284 $wpsession_corn_start_times = wp_date( 'Y-m-d' ) . ' ' . get_option( 'qcld_wbsession_corn_starttime' );
1285 $wpsession_corn_start_time = strtotime( $wpsession_corn_start_times );
1286 if ( ! wp_next_scheduled( 'qcld_wpsession_mysql_scraper_event' ) && ( get_option( 'qcld_wbsession_ai_enable' ) == '1' ) ) {
1287 wp_schedule_event( $wpsession_corn_start_time, 'session_schedules', 'qcld_wpsession_mysql_scraper_event' );
1288 }
1289
1290 add_action( 'qcld_wpsession_mysql_scraper_event', 'qcld_wpsession_mysql_scraper_function_free' );
1291
1292 if ( ! function_exists( 'qcld_wpsession_mysql_scraper_function_free' ) ) {
1293 function qcld_wpsession_mysql_scraper_function_free() {
1294 if ( get_option( 'qcld_wbsession_ai_enable' ) != '1' ) { return; }
1295
1296 global $wpdb;
1297 $interval_hours = ( (int) get_option( 'qcld_wbsession_corn_interval' ) ?: 86400 ) / 3600;
1298 $tableuser = $wpdb->prefix . 'wpbot_user';
1299 $tableconversation = $wpdb->prefix . 'wpbot_conversation';
1300 $results = $wpdb->get_results( $wpdb->prepare( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter
1301 "SELECT u.id, c.user_id, u.date, u.session_id, c.id, c.conversation
1302 FROM $tableuser AS u LEFT JOIN $tableconversation AS c ON u.id = c.user_id
1303 WHERE u.date >= (NOW() - INTERVAL %d HOUR) ORDER BY u.date DESC", $interval_hours ) // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
1304 );
1305
1306 $remarkable_session = array();
1307 foreach ( $results as $row ) {
1308 $remarkable_session[] = array( 'id' => $row->session_id, 'conversation' => wpsession_message_html_filter_free( htmlspecialchars_decode( $row->conversation ) ) );
1309 }
1310
1311 $keyword = get_option( 'qcld_wpsession_corn_promt' ) ?: 'Below is Chat conversation session data from our users on our website. Each conversation starts with an ID. Can you analyze each conversation and summarize each of them?';
1312 $gptkeyword = array(
1313 array( 'role' => 'system', 'content' => array( array( 'type' => 'input_text', 'text' => $keyword ) ) ),
1314 array( 'role' => 'user', 'content' => array( array( 'type' => 'input_text', 'text' => wp_json_encode( $remarkable_session ) ) ) ),
1315 );
1316
1317 $api_key = get_option( 'open_ai_api_key' );
1318 $engines = get_option( 'openai_engines' );
1319 $post_fields = array( 'model' => $engines, 'input' => $gptkeyword );
1320
1321 $api_response = wp_remote_post(
1322 'https://api.openai.com/v1/responses',
1323 array(
1324 'headers' => array(
1325 'Content-Type' => 'application/json',
1326 'Authorization' => 'Bearer ' . $api_key,
1327 ),
1328 'body' => wp_json_encode( $post_fields ),
1329 'timeout' => 60,
1330 )
1331 );
1332 $result = is_wp_error( $api_response ) ? '' : wp_remote_retrieve_body( $api_response );
1333
1334 $mess = json_decode( $result );
1335 $msg = isset( $mess->output[0]->content[0]->text ) ? $mess->output[0]->content[0]->text : ( isset( $mess->output[1]->content[0]->text ) ? $mess->output[1]->content[0]->text : 'No response from OpenAI.' );
1336 $msg = preg_replace( "/\r\n|\r|\n/", '<br/>', $msg );
1337
1338 $to = get_option( 'qlcd_wp_chatbot_admin_email' ) ?: get_option( 'admin_email' );
1339 $headers = array(
1340 'Content-Type: text/html; charset=UTF-8',
1341 'From: ' . esc_html( get_bloginfo( 'name' ) ) . ' <wordpress@' . wp_parse_url( get_site_url(), PHP_URL_HOST ) . '>',
1342 );
1343 wp_mail( $to, 'ChatBot Sessions Analysis', $msg, $headers );
1344 }
1345 }
1346
1347 // ─── Reports (Bot - Reports submenu) ─────────────────────────────────────────
1348 require_once QCLD_CHATBOT_FREE_SESSION_DIR_PATH . 'reports/chatbot-history-reporting.php';
1349