PluginProbe ʕ •ᴥ•ʔ
AI Engine – The Chatbot, AI Framework & MCP for WordPress / 2.7.1
AI Engine – The Chatbot, AI Framework & MCP for WordPress v2.7.1
3.5.7 3.5.6 3.5.5 3.5.4 3.5.3 3.5.2 3.5.1 3.5.0 3.4.9 3.4.8 3.4.7 0.2.1 1.6.91 0.2.2 1.6.92 0.2.3 1.6.93 0.2.4 1.6.94 0.2.5 1.6.95 0.2.6 1.6.96 0.2.7 1.6.97 0.2.8 1.6.98 0.2.9 1.6.99 0.3.0 1.7.0 0.3.1 1.7.1 0.3.2 1.7.2 0.3.3 1.7.3 0.3.4 1.7.4 0.3.5 1.7.5 0.3.6 1.7.6 0.4.0 1.7.7 0.4.1 1.7.8 0.4.2 1.7.9 0.4.3 1.8.0 0.4.4 1.8.1 0.4.5 1.8.2 0.4.6 1.8.3 0.4.7 1.8.4 0.4.8 1.8.5 0.4.9 1.8.6 0.5.0 1.8.7 0.5.1 1.8.8 0.5.2 1.8.9 0.5.3 1.9.0 0.5.4 1.9.1 0.5.5 1.9.2 0.5.6 1.9.3 0.5.7 1.9.4 0.5.8 1.9.5 0.5.9 1.9.6 0.6.0 1.9.7 0.6.1 1.9.8 0.6.2 1.9.81 0.6.3 1.9.82 0.6.4 1.9.83 0.6.5 1.9.84 0.6.6 1.9.85 0.6.7 1.9.86 0.6.8 1.9.87 0.6.9 1.9.88 0.7.0 1.9.89 0.7.1 1.9.90 0.7.2 1.9.91 0.7.3 1.9.92 0.7.4 1.9.93 0.7.5 1.9.94 0.7.6 1.9.95 0.7.7 1.9.96 0.7.8 1.9.97 0.7.9 1.9.98 0.8.0 1.9.99 0.8.1 2.0.0 0.8.2 2.0.1 0.8.3 2.0.2 0.8.4 2.0.3 0.8.5 2.0.4 0.8.6 2.0.5 0.8.7 2.0.6 0.8.8 2.0.7 0.8.9 2.0.8 0.9.0 2.0.9 0.9.2 2.1.0 0.9.3 2.1.1 0.9.4 2.1.2 0.9.5 2.1.3 0.9.6 2.1.4 0.9.7 2.1.5 0.9.8 2.1.6 0.9.81 2.1.7 0.9.82 2.1.8 0.9.83 2.1.9 0.9.84 2.2.0 0.9.85 2.2.1 0.9.86 2.2.2 0.9.87 2.2.3 0.9.88 2.2.4 0.9.89 2.2.5 0.9.9 2.2.51 0.9.91 2.2.52 0.9.92 2.2.53 0.9.93 2.2.54 0.9.94 2.2.56 0.9.95 2.2.57 0.9.96 2.2.6 0.9.97 2.2.60 0.9.98 2.2.61 0.9.99 2.2.62 1.0.0 2.2.63 1.0.01 2.2.70 1.0.1 2.2.80 1.0.2 2.2.81 1.0.3 2.2.90 1.0.4 2.2.91 1.0.5 2.2.92 1.0.6 2.2.93 1.0.7 2.2.94 1.0.8 2.2.95 1.0.9 2.3.0 1.1.0 2.3.1 1.1.1 2.3.2 1.1.2 2.3.3 1.1.3 2.3.4 1.1.4 2.3.5 1.1.5 2.3.6 1.1.6 2.3.7 1.1.7 2.3.8 1.1.8 2.3.9 1.1.9 2.4.0 1.2.0 2.4.1 1.2.1 2.4.2 1.2.2 2.4.3 1.2.21 2.4.4 1.2.3 2.4.5 1.2.30 2.4.6 1.3.0 2.4.7 1.3.1 2.4.8 1.3.2 2.4.9 1.3.3 2.5.0 1.3.31 2.5.1 1.3.32 2.5.2 1.3.33 2.5.3 1.3.34 2.5.4 1.3.35 2.5.5 1.3.36 2.5.6 1.3.37 2.5.7 1.3.38 2.5.8 1.3.39 2.5.9 1.3.40 2.6.0 1.3.41 2.6.1 1.3.42 2.6.2 1.3.43 2.6.3 1.3.44 2.6.5 1.3.45 2.6.6 1.3.46 2.6.7 1.3.47 2.6.8 1.3.48 2.6.9 1.3.49 2.7.0 1.3.50 2.7.1 1.3.51 2.7.2 1.3.52 2.7.3 1.3.53 2.7.4 1.3.54 2.7.5 1.3.56 2.7.6 1.3.57 2.7.7 1.3.58 2.7.8 1.3.59 2.7.9 1.3.60 2.8.0 1.3.61 2.8.1 1.3.62 2.8.2 1.3.63 2.8.3 1.3.64 2.8.4 1.3.65 2.8.5 1.3.66 2.8.6 1.3.67 2.8.7 1.3.68 2.8.8 1.3.69 2.8.9 1.3.70 2.9.0 1.3.71 2.9.1 1.3.72 2.9.2 1.3.73 2.9.3 1.3.74 2.9.4 1.3.75 2.9.5 1.3.76 2.9.6 1.3.77 2.9.7 1.3.78 2.9.8 1.3.79 2.9.9 1.3.80 3.0.0 1.3.81 3.0.1 1.3.82 3.0.2 1.3.83 3.0.3 1.3.84 3.0.4 1.3.85 3.0.5 1.3.86 3.0.6 1.3.87 3.0.7 1.3.88 3.0.8 1.3.89 3.0.9 1.3.90 3.1.0 1.3.91 3.1.1 1.3.92 3.1.2 1.3.93 3.1.3 1.3.94 3.1.4 1.3.95 3.1.5 1.3.96 3.1.6 1.3.97 3.1.7 1.3.98 3.1.8 1.3.99 3.1.9 1.4.0 3.2.0 1.4.1 3.2.1 1.4.2 3.2.2 1.4.3 3.2.3 1.4.4 3.2.4 1.4.5 3.2.5 1.4.6 3.2.6 1.4.7 3.2.7 1.4.8 3.2.8 1.4.9 3.2.9 1.5.0 3.3.0 1.5.1 3.3.1 1.5.2 3.3.2 1.5.3 3.3.3 1.5.4 3.3.4 1.5.5 3.3.5 1.5.6 3.3.6 1.5.7 3.3.7 1.5.8 3.3.8 1.5.9 3.3.9 1.6.0 3.4.0 1.6.1 3.4.1 1.6.2 3.4.2 1.6.3 3.4.3 1.6.5 3.4.4 1.6.51 3.4.5 1.6.52 3.4.6 1.6.53 1.6.54 1.6.55 1.6.56 1.6.57 1.6.58 1.6.59 1.6.60 1.6.61 1.6.62 1.6.63 1.6.64 1.6.65 1.6.66 1.6.67 1.6.68 trunk 1.6.69 0.0.1 1.6.70 0.0.2 1.6.71 0.0.3 1.6.72 0.0.4 1.6.73 0.0.5 1.6.74 0.0.6 1.6.75 0.0.7 1.6.76 0.0.8 1.6.77 0.0.9 1.6.78 0.1.0 1.6.79 0.1.1 1.6.81 0.1.2 1.6.82 0.1.3 1.6.83 0.1.4 1.6.84 0.1.5 1.6.85 0.1.6 1.6.86 0.1.7 1.6.87 0.1.8 1.6.88 0.1.9 1.6.89 0.2.0 1.6.90
ai-engine / classes / modules / discussions.php
ai-engine / classes / modules Last commit date
advisor.php 2 years ago chatbot.php 1 year ago discussions.php 1 year ago files.php 1 year ago gdpr.php 1 year ago security.php 1 year ago tasks.php 2 years ago wand.php 1 year ago
discussions.php
658 lines
1 <?php
2
3 class Meow_MWAI_Modules_Discussions {
4 private $wpdb = null;
5 private $core = null;
6 public $table_chats = null;
7 private $db_check = false;
8 private $namespace_admin = 'mwai/v1';
9 private $namespace_ui = 'mwai-ui/v1';
10
11 public function __construct() {
12 global $wpdb;
13 $this->wpdb = $wpdb;
14 global $mwai_core;
15 $this->core = $mwai_core;
16 $this->table_chats = $wpdb->prefix . 'mwai_chats';
17
18 if ( $this->core->get_option( 'chatbot_discussions' ) ) {
19 add_filter( 'mwai_chatbot_reply', [ $this, 'chatbot_reply' ], 10, 4 );
20 add_action( 'rest_api_init', [ $this, 'rest_api_init' ] );
21
22 if ( ! wp_next_scheduled( 'mwai_discussions' ) ) {
23 wp_schedule_event( time(), 'hourly', 'mwai_discussions' );
24 }
25 add_action( 'mwai_discussions', [ $this, 'cron_discussions' ] );
26 }
27 }
28
29 public function rest_api_init() {
30
31 // Admin
32 register_rest_route( $this->namespace_admin, '/discussions/list', [
33 'methods' => 'POST',
34 'callback' => [ $this, 'rest_discussions_list' ],
35 'permission_callback' => [ $this->core, 'can_access_settings' ],
36 ] );
37 register_rest_route( $this->namespace_admin, '/discussions/delete', [
38 'methods' => 'POST',
39 'callback' => [ $this, 'rest_discussions_delete_admin' ],
40 'permission_callback' => [ $this->core, 'can_access_settings' ],
41 ] );
42
43 // UI
44 register_rest_route( $this->namespace_ui, '/discussions/list', [
45 'methods' => 'POST',
46 'callback' => [ $this, 'rest_discussions_ui_list' ],
47 'permission_callback' => '__return_true'
48 ] );
49 register_rest_route( $this->namespace_ui, '/discussions/edit', [
50 'methods' => 'POST',
51 'callback' => [ $this, 'rest_discussions_ui_edit' ],
52 'permission_callback' => '__return_true'
53 ] );
54 register_rest_route( $this->namespace_ui, '/discussions/delete', [
55 'methods' => 'POST',
56 'callback' => [ $this, 'rest_discussions_delete' ],
57 'permission_callback' => [ $this, 'can_delete_discussion' ],
58 ] );
59 }
60
61 function can_delete_discussion( $request ) {
62 $params = $request->get_json_params();
63 $chatIds = isset( $params['chatIds'] ) ? $params['chatIds'] : null;
64 $userId = get_current_user_id();
65 if ( ! $userId ) {
66 return false;
67 }
68 foreach ( $chatIds as $chatId ) {
69 $chat = $this->wpdb->get_row(
70 $this->wpdb->prepare(
71 "SELECT *
72 FROM $this->table_chats
73 WHERE chatId = %s",
74 $chatId
75 )
76 );
77 if ( ! $chat || (int) $chat->userId !== (int) $userId ) {
78 return false;
79 }
80 }
81 return true;
82 }
83
84 /**
85 * Generate or update the title for a specific discussion
86 * by calling the AI (if it meets the requirements).
87 *
88 * @param stdClass $discussion A row from the DB (object form).
89 * @return void
90 */
91 private function generate_title_for_discussion( $discussion ) {
92 // Check if there's already a title
93 if ( ! empty( $discussion->title ) ) {
94 return; // Nothing to do if title is already set.
95 }
96
97 // Ensure it's not older than 10 days, or whatever logic you prefer
98 $ten_days_ago = strtotime( '-10 days' );
99 if ( strtotime( $discussion->updated ) < $ten_days_ago ) {
100 return; // Skip if older than 10 days
101 }
102
103 // We expect JSON in the messages
104 $messages = json_decode( $discussion->messages, true );
105 if ( ! is_array( $messages ) ) {
106 return;
107 }
108
109 // Check for at least one user and one assistant message
110 $has_user_message = false;
111 $has_assistant_message = false;
112 foreach ( $messages as $message ) {
113 if ( isset( $message['role'] ) ) {
114 if ( $message['role'] === 'user' ) {
115 $has_user_message = true;
116 }
117 if ( $message['role'] === 'assistant' ) {
118 $has_assistant_message = true;
119 }
120 }
121 if ( $has_user_message && $has_assistant_message ) {
122 break;
123 }
124 }
125
126 if ( ! ( $has_user_message && $has_assistant_message ) ) {
127 return; // If doesn't have both, skip
128 }
129
130 // Prepare the conversation text for the prompt
131 $conversation_text = '';
132 foreach ( $messages as $message ) {
133 if ( isset( $message['role'] ) && isset( $message['content'] ) ) {
134 $role = ucfirst( $message['role'] );
135 $content = $message['content'];
136 $conversation_text .= "$role: $content\n";
137 }
138 }
139
140 $base_prompt = "Based on the following conversation, generate a concise and specific title for the discussion, strictly less than 64 characters. Focus on the main topic, avoiding unnecessary words such as articles, pronouns, or adjectives. Do not include any punctuation at the end. Do not include anything else than the title itself, only one sentence, no line breaks, just the title.\n\nConversation:\n$conversation_text\n";
141 $prompt = apply_filters( 'mwai_discussions_title_prompt', $base_prompt, $conversation_text, $discussion );
142
143 // Run the AI query
144 global $mwai;
145 $answer = $mwai->simpleTextQuery( $prompt, [ "scope" => 'discussions' ] );
146
147 // Clean up the answer
148 $title = trim( $answer );
149 $title = rtrim( $title, ".!?:;,—–-–" ); // Remove trailing punctuation
150 $title = substr( $title, 0, 64 ); // Ensure less than 64 characters
151 if ( empty( $title ) ) {
152 $title = 'Untitled';
153 }
154
155 // Update the discussion with the title
156 $updated = $this->wpdb->update(
157 $this->table_chats,
158 [ 'title' => $title ],
159 [ 'id' => $discussion->id ]
160 );
161
162 if ( $updated === false ) {
163 error_log( "Failed to update the title for discussion ID {$discussion->id}" );
164 }
165 }
166
167 /**
168 * Admin route for listing discussions. No forced logic here.
169 */
170 public function rest_discussions_list( $request ) {
171 try {
172 $params = $request->get_json_params();
173 $offset = $params['offset'];
174 $limit = $params['limit'];
175 $filters = $params['filters'];
176 $sort = $params['sort'];
177
178 // Retrieve the chats
179 $chats = $this->chats_query( [], $offset, $limit, $filters, $sort );
180
181 return new WP_REST_Response([
182 'success' => true,
183 'total' => $chats['total'],
184 'chats' => $chats['rows']
185 ], 200 );
186 }
187 catch ( Exception $e ) {
188 return new WP_REST_Response([
189 'success' => false,
190 'message' => $e->getMessage()
191 ], 500 );
192 }
193 }
194
195 public function rest_discussions_ui_edit( $request ) {
196 try {
197 $params = $request->get_json_params();
198 $chatId = isset( $params['chatId'] ) ? sanitize_text_field( $params['chatId'] ) : null;
199 $title = isset( $params['title'] ) ? sanitize_text_field( $params['title'] ) : null;
200
201 if ( is_null( $chatId ) || is_null( $title ) ) {
202 return new WP_REST_Response( [
203 'success' => false,
204 'message' => 'chatId and title are required.'
205 ], 400 );
206 }
207
208 $userId = get_current_user_id();
209 if ( ! $userId ) {
210 return new WP_REST_Response( [
211 'success' => false,
212 'message' => 'You need to be logged in.'
213 ], 401 );
214 }
215
216 // Update the discussion title for the current user
217 $updated = $this->wpdb->update(
218 $this->table_chats,
219 [ 'title' => $title ],
220 [ 'chatId' => $chatId, 'userId' => $userId ]
221 );
222
223 if ( $updated === false ) {
224 return new WP_REST_Response( [
225 'success' => false,
226 'message' => 'Failed to update the discussion.'
227 ], 500 );
228 }
229
230 return new WP_REST_Response( [ 'success' => true ], 200 );
231 } catch ( Exception $e ) {
232 return new WP_REST_Response( [
233 'success' => false,
234 'message' => $e->getMessage()
235 ], 500 );
236 }
237 }
238
239 public function cron_discussions() {
240 $this->check_db();
241
242 // NEW CHECK: Only run if auto-titling is enabled
243 if ( ! $this->core->get_option( 'chatbot_discussions_titling' ) ) {
244 return;
245 }
246 // END NEW CHECK
247
248 $now = date( 'Y-m-d H:i:s' );
249 $ten_days_ago = date( 'Y-m-d H:i:s', strtotime( '-10 days' ) );
250
251 // Get 5 latest discussions, not older than 10 days, which have no 'title' yet
252 $query = $this->wpdb->prepare(
253 "SELECT * FROM {$this->table_chats}
254 WHERE title IS NULL AND updated >= %s
255 ORDER BY updated DESC LIMIT 5",
256 $ten_days_ago
257 );
258 $discussions = $this->wpdb->get_results( $query );
259
260 if ( empty( $discussions ) ) {
261 return;
262 }
263
264 foreach ( $discussions as $discussion ) {
265 $this->generate_title_for_discussion( $discussion );
266 }
267 }
268
269 /**
270 * UI route for listing discussions.
271 * Here we add the "forced cron" logic for up to 5 discussions,
272 * but only if auto-titling is enabled.
273 */
274 public function rest_discussions_ui_list( $request ) {
275 try {
276 $params = $request->get_json_params();
277 $offset = isset( $params['offset'] ) ? $params['offset'] : 0;
278 $limit = isset( $params['limit'] ) ? $params['limit'] : 10;
279 $botId = isset( $params['botId'] ) ? $params['botId'] : null;
280 $customId = isset( $params['customId'])? $params['customId']: null;
281
282 if ( ! is_null( $customId ) ) {
283 $botId = $customId;
284 }
285
286 if ( is_null( $botId ) ) {
287 return new WP_REST_Response([
288 'success' => false,
289 'message' => "Bot ID is required."
290 ], 200 );
291 }
292
293 $userId = get_current_user_id();
294 if ( ! $userId ) {
295 return new WP_REST_Response([
296 'success' => false,
297 'message' => "You need to be connected."
298 ], 200 );
299 }
300
301 $filters = [
302 [ 'accessor' => 'user', 'value' => $userId ],
303 [ 'accessor' => 'botId', 'value' => $botId ],
304 ];
305
306 // Retrieve the chats
307 $chats = $this->chats_query( [], $offset, $limit, $filters );
308
309 // NEW CHECK: only do forced titling if it's enabled
310 if ( $this->core->get_option( 'chatbot_discussions_titling' ) ) {
311 // "Forced cron" logic: we look at the returned discussions,
312 // find a few (up to 5) that have no title, meet the date/message requirements,
313 // and generate titles for them.
314 $counter = 0;
315 foreach ( $chats['rows'] as &$chatRow ) {
316 if ( $counter >= 5 ) {
317 break; // only up to 5
318 }
319 if ( empty( $chatRow['title'] ) && strtotime( $chatRow['updated'] ) >= strtotime( '-10 days' ) ) {
320 // Convert array row to an object so we can reuse generate_title_for_discussion
321 $discussionObj = (object) $chatRow;
322 $this->generate_title_for_discussion( $discussionObj );
323 $counter++;
324 }
325 }
326
327 // If you want the newly-updated titles to show up *immediately*:
328 $chats = $this->chats_query( [], $offset, $limit, $filters );
329 }
330 // END NEW CHECK
331
332 return new WP_REST_Response([
333 'success' => true,
334 'total' => $chats['total'],
335 'chats' => $chats['rows']
336 ], 200 );
337 }
338 catch ( Exception $e ) {
339 return new WP_REST_Response([
340 'success' => false,
341 'message' => $e->getMessage()
342 ], 500 );
343 }
344 }
345
346 public function rest_discussions_delete_admin( $request ) {
347 try {
348 $params = $request->get_json_params();
349 $chatsIds = $params['chatIds'];
350 if ( is_array( $chatsIds ) ) {
351 if ( count( $chatsIds ) === 0 ) {
352 $this->wpdb->query( "TRUNCATE TABLE $this->table_chats" );
353 }
354 foreach( $chatsIds as $chatId ) {
355 $this->wpdb->delete(
356 $this->table_chats,
357 [ 'chatId' => $chatId ]
358 );
359 }
360 }
361 return new WP_REST_Response([ 'success' => true ], 200 );
362 }
363 catch ( Exception $e ) {
364 return new WP_REST_Response([
365 'success' => false,
366 'message' => $e->getMessage()
367 ], 500 );
368 }
369 }
370
371 public function rest_discussions_delete( $request ) {
372 try {
373 $params = $request->get_json_params();
374 $chatIds = isset( $params['chatIds'] ) ? $params['chatIds'] : null;
375
376 if ( ! is_array( $chatIds ) || empty( $chatIds ) ) {
377 return new WP_REST_Response([
378 'success' => false,
379 'message' => 'chatIds is required.'
380 ], 400 );
381 }
382
383 $userId = get_current_user_id();
384 if ( ! $userId ) {
385 return new WP_REST_Response([
386 'success' => false,
387 'message' => 'You need to be logged in.'
388 ], 401 );
389 }
390
391 foreach ( $chatIds as $chatId ) {
392 $this->wpdb->delete(
393 $this->table_chats,
394 [ 'chatId' => $chatId, 'userId' => $userId ]
395 );
396 }
397
398 return new WP_REST_Response([ 'success' => true ], 200 );
399 } catch ( Exception $e ) {
400 return new WP_REST_Response([
401 'success' => false,
402 'message' => $e->getMessage()
403 ], 500 );
404 }
405 }
406
407 // Get latest discussion for the given parameter
408 function get_discussion( $botId, $chatId ) {
409 $this->check_db();
410 $chat = $this->wpdb->get_row(
411 $this->wpdb->prepare(
412 "SELECT *
413 FROM $this->table_chats
414 WHERE chatId = %s AND botId = %s",
415 $chatId,
416 $botId
417 ),
418 ARRAY_A
419 );
420 if ( $chat ) {
421 $chat['messages'] = json_decode( $chat['messages'] );
422 return $chat;
423 }
424 return null;
425 }
426
427 function chats_query( $chats = [], $offset = 0, $limit = null, $filters = null, $sort = null ) {
428 $this->check_db();
429 $offset = ! empty( $offset ) ? intval( $offset ) : 0;
430 $limit = ! empty( $limit ) ? intval( $limit ) : 5;
431 $filters = ! empty( $filters ) ? $filters : [];
432 $this->core->sanitize_sort( $sort, 'updated', 'DESC' );
433
434 $where_clauses = [];
435 $where_values = [];
436
437 if ( is_array( $filters ) ) {
438 foreach ( $filters as $filter ) {
439 $value = $filter['value'];
440 if ( is_null( $value ) || $value === '' ) {
441 continue;
442 }
443
444 switch ( $filter['accessor'] ) {
445 case 'user':
446 $isIP = filter_var( $value, FILTER_VALIDATE_IP );
447 if ( $isIP ) {
448 $where_clauses[] = 'ip = %s';
449 $where_values[] = $value;
450 } else {
451 $where_clauses[] = 'userId = %d';
452 $where_values[] = intval( $value );
453 }
454 break;
455
456 case 'botId':
457 $where_clauses[] = 'botId = %s';
458 $where_values[] = $value;
459 break;
460
461 case 'preview':
462 $like = '%' . $this->wpdb->esc_like( $value ) . '%';
463 $where_clauses[] = 'messages LIKE %s';
464 $where_values[] = $like;
465 break;
466
467 // Add other cases as needed
468 }
469 }
470 }
471
472 $where_sql = '';
473 if ( ! empty( $where_clauses ) ) {
474 $where_sql = 'WHERE ' . implode( ' AND ', $where_clauses );
475 }
476
477 $order_by = 'ORDER BY ' . esc_sql( $sort['accessor'] ) . ' ' . esc_sql( $sort['by'] );
478
479 $limit_sql = '';
480 if ( $limit > 0 ) {
481 $limit_sql = $this->wpdb->prepare( 'LIMIT %d, %d', $offset, $limit );
482 }
483
484 $query = "SELECT * FROM {$this->table_chats} {$where_sql} {$order_by} {$limit_sql}";
485
486 // Execute the prepared statement
487 $chats['rows'] = $this->wpdb->get_results( $this->wpdb->prepare( $query, $where_values ), ARRAY_A );
488
489 // Get the total count
490 $count_query = "SELECT COUNT(*) FROM {$this->table_chats} {$where_sql}";
491 $chats['total'] = $this->wpdb->get_var( $this->wpdb->prepare( $count_query, $where_values ) );
492
493 return $chats;
494 }
495
496 public function chatbot_reply( $rawText, $query, $params, $extra ) {
497 global $mwai_core;
498 $userIp = $mwai_core->get_ip_address();
499 $userId = $mwai_core->get_user_id();
500 $botId = isset( $params['botId'] ) ? $params['botId'] : null;
501 $chatId = $this->core->fix_chat_id( $query, $params );
502 $customId = isset( $params['customId'] ) ? $params['customId'] : null;
503 $threadId = $query instanceof Meow_MWAI_Query_Assistant ? $query->threadId : null;
504 $storeId = $query instanceof Meow_MWAI_Query_Assistant ? $query->storeId : null;
505 $now = date( 'Y-m-d H:i:s' );
506
507 if ( ! empty( $customId ) ) {
508 $botId = $customId;
509 }
510 $newMessage = isset( $params['newMessage'] ) ? $params['newMessage'] : $query->get_message();
511
512 // If there is a file for "Vision", add it to the message
513 if ( isset( $query->filePurpose ) && $query->filePurpose === 'vision' && isset( $query->file ) ) {
514 $newMessage = "![Uploaded Image]({$query->file})\n" . $newMessage;
515 }
516
517 $this->check_db();
518 $chat = $this->wpdb->get_row(
519 $this->wpdb->prepare(
520 "SELECT *
521 FROM $this->table_chats
522 WHERE chatId = %s",
523 $chatId
524 )
525 );
526 $messageExtra = [
527 'embeddings' => isset( $extra['embeddings'] ) ? $extra['embeddings'] : null
528 ];
529 $chatExtra = [
530 'session' => $query->session,
531 'model' => $query->model,
532 ];
533 if ( !empty( $query->temperature ) ) {
534 $chatExtra['temperature'] = $query->temperature;
535 }
536 if ( !empty( $query->context ) ) {
537 $chatExtra['context'] = $query->context;
538 }
539 if ( !empty( $params['parentBotId'] ) ) {
540 $chatExtra['parentBotId'] = $params['parentBotId'];
541 }
542 if ( $query instanceof Meow_MWAI_Query_Assistant ) {
543 $chatExtra['assistantId'] = $query->assistantId;
544 $chatExtra['threadId'] = $query->threadId;
545 $chatExtra['storeId'] = $query->storeId;
546 }
547 if ( $chat ) {
548 $chat->messages = json_decode( $chat->messages );
549 $chat->messages[] = [ 'role' => 'user', 'content' => $newMessage ];
550 $chat->messages[] = [ 'role' => 'assistant', 'content' => $rawText, 'extra' => $messageExtra ];
551 $chat->messages = json_encode( $chat->messages );
552 $this->wpdb->update(
553 $this->table_chats,
554 [
555 'userId' => $userId,
556 'messages' => $chat->messages,
557 'updated' => $now
558 ],
559 [ 'id' => $chat->id ]
560 );
561 }
562 else {
563 $startSentence = isset( $params['startSentence'] ) ? $params['startSentence'] : null;
564 $messages = [];
565 if ( ! empty( $startSentence ) ) {
566 $messages[] = [ 'role' => 'assistant', 'content' => $startSentence ];
567 }
568 $messages[] = [ 'role' => 'user', 'content' => $newMessage ];
569 $messages[] = [ 'role' => 'assistant', 'content' => $rawText, 'extra' => $messageExtra ];
570 $chat = [
571 'userId' => $userId,
572 'ip' => $userIp,
573 'messages' => json_encode( $messages ),
574 'extra' => json_encode( $chatExtra ),
575 'botId' => $botId,
576 'chatId' => $chatId,
577 'threadId' => $threadId,
578 'storeId' => $storeId,
579 'created' => $now,
580 'updated' => $now
581 ];
582 $this->wpdb->insert( $this->table_chats, $chat );
583 }
584 return $rawText;
585 }
586
587 function format_messages( $json, $format = 'html' ) {
588 $html = '';
589 if ( $format === 'html' ) {
590 try {
591 $conversation = json_decode( $json, true );
592 if ( json_last_error() !== JSON_ERROR_NONE ) {
593 return 'Invalid JSON format';
594 }
595 foreach ( $conversation as $message ) {
596 $role = ucfirst( $message['role'] );
597 $html .= '<p><strong>' . htmlspecialchars( $role ) . ':</strong> ' . htmlspecialchars( $message['content'] ) . '</p>';
598 }
599 }
600 catch ( Exception $e ) {
601 error_log( $e->getMessage() );
602 return 'Error while formatting the message';
603 }
604 }
605 $html = apply_filters( 'mwai_discussion_format_messages', $html, $json, $format );
606 return $html;
607 }
608
609 function check_db() {
610 if ( $this->db_check ) {
611 return true;
612 }
613 $this->db_check = !(
614 strtolower( $this->wpdb->get_var( "SHOW TABLES LIKE '$this->table_chats'" ) )
615 != strtolower( $this->table_chats )
616 );
617 if ( ! $this->db_check ) {
618 $this->create_db();
619 $this->db_check = !(
620 strtolower( $this->wpdb->get_var( "SHOW TABLES LIKE '$this->table_chats'" ) )
621 != strtolower( $this->table_chats )
622 );
623 }
624
625 // LATER: REMOVE THIS AFTER MARCH 2025
626 $this->db_check = $this->db_check && $this->wpdb->get_var( "SHOW COLUMNS FROM $this->table_chats LIKE 'title'" );
627 if ( ! $this->db_check ) {
628 $this->wpdb->query( "ALTER TABLE $this->table_chats ADD COLUMN title VARCHAR(64) NULL" );
629 $this->db_check = true;
630 }
631
632 return $this->db_check;
633 }
634
635 function create_db() {
636 $charset_collate = $this->wpdb->get_charset_collate();
637 $sqlLogs = "CREATE TABLE $this->table_chats (
638 id BIGINT(20) NOT NULL AUTO_INCREMENT,
639 userId BIGINT(20) NULL,
640 ip VARCHAR(64) NULL,
641 title VARCHAR(64) NULL,
642 messages TEXT NOT NULL NULL,
643 extra LONGTEXT NOT NULL NULL,
644 botId VARCHAR(64) NULL,
645 chatId VARCHAR(64) NOT NULL,
646 threadId VARCHAR(64) NULL,
647 storeId VARCHAR(64) NULL,
648 created DATETIME NOT NULL,
649 updated DATETIME NOT NULL,
650 PRIMARY KEY (id),
651 INDEX chatId (chatId)
652 ) $charset_collate;";
653 require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
654 dbDelta( $sqlLogs );
655 }
656
657 }
658