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