PluginProbe
WPBot – AI ChatBot for Live Support, Lead Generation, WordPress Automation, AI Services / 8.7.4
WPBot – AI ChatBot for Live Support, Lead Generation, WordPress Automation, AI Services v8.7.4
8.7.7 8.7.6 8.7.5 8.7.4 8.7.3 8.7.2 8.7.1 8.7.0 8.6.9 8.6.8 8.6.7 8.6.6 8.6.5 8.6.4 8.6.2 8.6.1 8.6.0 8.5.9 8.5.8 8.5.7 8.5.6 8.5.5 8.5.4 8.5.3 8.5.2 All 533 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.7.4, at includes/chat-sessions/wpbot-chat-sessions.php

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