PluginProbe ʕ •ᴥ•ʔ
AI Engine – The Chatbot, AI Framework & MCP for WordPress / 2.3.8
AI Engine – The Chatbot, AI Framework & MCP for WordPress v2.3.8
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 / chatbot.php
ai-engine / classes / modules Last commit date
advisor.php 2 years ago chatbot.php 2 years ago discussions.php 2 years ago files.php 2 years ago security.php 2 years ago tasks.php 2 years ago utilities.php 2 years ago wand.php 2 years ago
chatbot.php
574 lines
1 <?php
2
3 // Params for the chatbot (front and server)
4
5 define( 'MWAI_CHATBOT_FRONT_PARAMS', [ 'id', 'customId', 'aiName', 'userName', 'guestName',
6 'textSend', 'textClear', 'imageUpload', 'fileSearch',
7 'textInputPlaceholder', 'textInputMaxLength', 'textCompliance', 'startSentence', 'localMemory',
8 'themeId', 'window', 'icon', 'iconText', 'iconAlt', 'iconPosition', 'fullscreen', 'copyButton'
9 ] );
10 define( 'MWAI_CHATBOT_SERVER_PARAMS', [ 'id', 'envId', 'scope', 'mode', 'contentAware', 'context',
11 'embeddingsEnvId', 'embeddingsIndex', 'embeddingsNamespace', 'assistantId', 'instructions',
12 'model', 'temperature', 'maxTokens', 'contextMaxLength', 'maxResults', 'apiKey', 'functions'
13 ] );
14
15 // Params for the discussions (front and server)
16
17 define( 'MWAI_DISCUSSIONS_FRONT_PARAMS', [ 'themeId', 'textNewChat' ] );
18 define( 'MWAI_DISCUSSIONS_SERVER_PARAMS', [ 'customId' ] );
19
20 class Meow_MWAI_Modules_Chatbot {
21 private $core = null;
22 private $namespace = 'mwai-ui/v1';
23 private $siteWideChatId = null;
24
25 public function __construct() {
26 global $mwai_core;
27 $this->core = $mwai_core;
28 add_shortcode( 'mwai_chatbot', array( $this, 'chat_shortcode' ) );
29 add_shortcode( 'mwai_chatbot_v2', array( $this, 'chat_shortcode' ) );
30 add_action( 'rest_api_init', array( $this, 'rest_api_init' ) );
31 $this->siteWideChatId = $this->core->get_option( 'botId' );
32 add_action( 'wp_enqueue_scripts', array( $this, 'register_scripts' ) );
33
34 if ( $this->core->get_option( 'shortcode_chat_discussions' ) ) {
35 add_shortcode( 'mwai_discussions', [ $this, 'shortcode_chat_discussions' ] );
36 }
37 }
38
39 public function register_scripts() {
40 $physical_file = trailingslashit( MWAI_PATH ) . 'app/chatbot.js';
41 $cache_buster = file_exists( $physical_file ) ? filemtime( $physical_file ) : MWAI_VERSION;
42 wp_register_script( 'mwai_chatbot', trailingslashit( MWAI_URL ) . 'app/chatbot.js',
43 [ 'wp-element' ], $cache_buster, false );
44 if ( !empty( $this->siteWideChatId ) && $this->siteWideChatId !== 'none' ) {
45 $this->enqueue_scripts();
46 add_action( 'wp_footer', array( $this, 'inject_chat' ) );
47 }
48 }
49
50 public function enqueue_scripts() {
51 wp_enqueue_script( "mwai_chatbot" );
52 if ( $this->core->get_option( 'shortcode_chat_syntax_highlighting' ) ) {
53 wp_enqueue_script( "mwai_highlight" );
54 }
55 }
56
57 public function rest_api_init() {
58 register_rest_route( $this->namespace, '/chats/submit', array(
59 'methods' => 'POST',
60 'callback' => [ $this, 'rest_chat' ],
61 'permission_callback' => array( $this->core, 'check_rest_nonce' )
62 ) );
63 }
64
65 public function basics_security_check( $botId, $customId, $newMessage, $newFileId ) {
66 if ( !$botId && !$customId ) {
67 $this->core->log( "⚠️ The query was rejected - no botId nor id was specified.");
68 return false;
69 }
70
71 if ( $newFileId ) {
72 return true;
73 }
74
75 $length = strlen( empty( $newMessage ) ? "" : $newMessage );
76 if ( $length < 1 || $length > ( 4096 * 16 ) ) {
77 $this->core->log( "⚠️ The query was rejected - message was too short or too long.");
78 return false;
79 }
80 return true;
81 }
82
83 public function rest_chat( $request ) {
84 $params = $request->get_json_params();
85 $botId = $params['botId'] ?? null;
86 $customId = $params['customId'] ?? null;
87 $stream = $params['stream'] ?? false;
88 $newMessage = trim( $params['newMessage'] ?? '' );
89 $newFileId = $params['newFileId'] ?? null;
90
91 if ( !$this->basics_security_check( $botId, $customId, $newMessage, $newFileId )) {
92 return new WP_REST_Response( [
93 'success' => false,
94 'message' => apply_filters( 'mwai_ai_exception', 'Sorry, your query has been rejected.' )
95 ], 403 );
96 }
97
98 try {
99 $data = $this->chat_submit( $botId, $newMessage, $newFileId, $params, $stream );
100 return new WP_REST_Response( [
101 'success' => true,
102 'reply' => $data['reply'],
103 'images' => $data['images'],
104 'usage' => $data['usage']
105 ], 200 );
106 }
107 catch ( Exception $e ) {
108 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
109 return new WP_REST_Response( [
110 'success' => false,
111 'message' => $message
112 ], 500 );
113 }
114 }
115
116 public function chat_submit( $botId, $newMessage, $newFileId = null, $params = [], $stream = false ) {
117 try {
118 $chatbot = null;
119 $customId = $params['customId'] ?? null;
120
121 // Custom Chatbot
122 if ( $customId ) {
123 $chatbot = get_transient( 'mwai_custom_chatbot_' . $customId );
124 }
125 // Registered Chatbot
126 if ( !$chatbot && $botId ) {
127 $chatbot = $this->core->get_chatbot( $botId );
128 }
129
130 if ( !$chatbot ) {
131 $this->core->log( "⚠️ No chatbot was found for this query.");
132 throw new Exception( 'Sorry, your query has been rejected.' );
133 }
134
135 $textInputMaxLength = $chatbot['textInputMaxLength'] ?? null;
136 if ( $textInputMaxLength && strlen( $newMessage ) > (int)$textInputMaxLength ) {
137 throw new Exception( 'Sorry, your query has been rejected.' );
138 }
139
140 // Create QueryText
141 $context = null;
142 $mode = $chatbot['mode'] ?? 'chat';
143
144 if ( $mode === 'images' ) {
145 $query = new Meow_MWAI_Query_Image( $newMessage );
146
147 // Handle Params
148 $newParams = [];
149 foreach ( $chatbot as $key => $value ) {
150 $newParams[$key] = $value;
151 }
152 foreach ( $params as $key => $value ) {
153 $newParams[$key] = $value;
154 }
155 $params = apply_filters( 'mwai_chatbot_params', $newParams );
156 $params['scope'] = empty( $params['scope'] ) ? 'chatbot' : $params['scope'];
157 $query->inject_params( $params );
158 }
159 else {
160 $query = $mode === 'assistant' ? new Meow_MWAI_Query_Assistant( $newMessage ) :
161 new Meow_MWAI_Query_Text( $newMessage, 1024 );
162 $streamCallback = null;
163
164 // Handle Params
165 $newParams = [];
166 foreach ( $chatbot as $key => $value ) {
167 $newParams[$key] = $value;
168 }
169 foreach ( $params as $key => $value ) {
170 $newParams[$key] = $value;
171 }
172 $params = apply_filters( 'mwai_chatbot_params', $newParams );
173 $params['scope'] = empty( $params['scope'] ) ? 'chatbot' : $params['scope'];
174 $query->inject_params( $params );
175
176 $storeId = null;
177 if ( $mode === 'assistant' ) {
178 $chatId = $params['chatId'] ?? null;
179 if ( !empty( $chatId ) ) {
180 $discussion = $this->core->discussions->get_discussion( $query->botId, $chatId );
181 if ( isset( $discussion['storeId'] ) ) {
182 $storeId = $discussion['storeId'];
183 $query->setStoreId( $storeId );
184 }
185 }
186 }
187
188 // Support for Uploaded Image
189 if ( !empty( $newFileId ) ) {
190
191 // Get extension and mime type
192 $isImage = $this->core->files->is_image( $newFileId );
193
194 if ( $mode === 'assistant' && !$isImage ) {
195 $url = $this->core->files->get_path( $newFileId );
196 $data = $this->core->files->get_data( $newFileId );
197 $openai = Meow_MWAI_Engines_Factory::get_openai( $this->core, $query->envId );
198 $filename = basename( $url );
199
200 // Upload the file
201 $file = $openai->upload_file( $filename, $data, 'assistants' );
202
203 // Create a store
204 if ( empty( $storeId ) ) {
205 $chatbotName = 'mwai_' . strtolower( !empty( $chatbot['name'] ) ? $chatbot['name'] : 'default' );
206 if ( !empty( $query->chatId ) ) {
207 $chatbotName .= "_" . $query->chatId;
208 }
209 $expiry = $this->core->get_option( 'image_expires' );
210 $metadata = [];
211 if ( !empty( $chatbot['assistantId'] ) ) {
212 $metadata['assistantId'] = $chatbot['assistantId'];
213 }
214 if ( !empty( $query->chatId ) ) {
215 $metadata['chatId'] = $query->chatId;
216 }
217 $storeId = $openai->create_vector_store( $chatbotName, $expiry, $metadata );
218 $query->setStoreId( $storeId );
219 }
220
221 // Add the file to the store
222 $storeFileId = $openai->add_vector_store_file( $storeId, $file['id'] );
223
224 // Update the local file with the OpenAI RefId, StoreId and StoreFileId
225 $openAiRefId = $file['id'];
226 $internalFileId = $this->core->files->get_id_from_refId( $newFileId );
227 $this->core->files->update_refId( $internalFileId, $openAiRefId );
228 $this->core->files->update_envId( $internalFileId, $query->envId );
229 $this->core->files->update_purpose( $internalFileId, 'assistant-in' );
230 $this->core->files->add_metadata( $internalFileId, 'assistant_storeId', $storeId );
231 $this->core->files->add_metadata( $internalFileId, 'assistant_storeFileId', $storeFileId );
232 $newFileId = $openAiRefId;
233 $scope = $params['fileSearch'];
234 if ( $scope === 'discussion' || $scope === 'user' || $scope === 'assistant' ) {
235 $id = $this->core->files->get_id_from_refId( $newFileId );
236 $this->core->files->add_metadata( $id, 'assistant_scope', $scope );
237 }
238 }
239 else {
240 $url = $this->core->files->get_url( $newFileId );
241 $mimeType = $this->core->files->get_mime_type( $newFileId );
242 $query->set_file( Meow_MWAI_Query_DroppedFile::from_url( $url, 'vision', $mimeType ) );
243 $fileId = $this->core->files->get_id_from_refId( $newFileId );
244 $this->core->files->update_envId( $fileId, $query->envId );
245 $this->core->files->update_purpose( $fileId, 'vision' );
246 $this->core->files->add_metadata( $fileId, 'query_envId', $query->envId );
247 $this->core->files->add_metadata( $fileId, 'query_session', $query->session );
248 }
249 }
250
251 // Takeover
252 $takeoverAnswer = apply_filters( 'mwai_chatbot_takeover', null, $query, $params );
253 if ( !empty( $takeoverAnswer ) ) {
254 return [
255 'reply' => $takeoverAnswer,
256 'images' => null,
257 'usage' => null
258 ];
259 }
260
261 // Moderation
262 $moderationEnabled = $this->core->get_option( 'module_moderation' ) &&
263 $this->core->get_option( 'shortcode_chat_moderation' );
264 if ( $moderationEnabled ) {
265 global $mwai;
266 $isFlagged = $mwai->moderationCheck( $query->get_message() );
267 if ( $isFlagged ) {
268 throw new Exception( 'Sorry, your message has been rejected by moderation.' );
269 }
270 }
271
272 // Awareness & Embeddings
273 $context = $this->core->retrieve_context( $params, $query );
274 if ( !empty( $context ) ) {
275 $query->set_context( $context['content'] );
276 }
277
278 // Function Aware
279 $query = apply_filters( 'mwai_chatbot_query', $query, $params );
280 }
281
282 // Process Query
283 if ( $stream ) {
284 $streamCallback = function( $reply ) {
285 $raw = $reply;
286 $this->stream_push( [ 'type' => 'live', 'data' => $raw ] );
287 if ( ob_get_level() > 0 ) {
288 ob_flush();
289 }
290 flush();
291 };
292 header( 'Cache-Control: no-cache' );
293 header( 'Content-Type: text/event-stream' );
294 // This is useful to disable buffering in nginx through headers.
295 header( 'X-Accel-Buffering: no' );
296 ob_implicit_flush( true );
297 ob_end_flush();
298 }
299
300 $reply = $this->core->run_query( $query, $streamCallback, true );
301 $rawText = $reply->result;
302 $extra = [];
303 if ( $context ) {
304 $extra = [ 'embeddings' => $context['embeddings'] ];
305 }
306 $rawText = apply_filters( 'mwai_chatbot_reply', $rawText, $query, $params, $extra );
307
308 $restRes = [
309 'reply' => $rawText,
310 'images' => $reply->get_type() === 'images' ? $reply->results : null,
311 'usage' => $reply->usage
312 ];
313
314 // Process Reply
315 if ( $stream ) {
316 $this->stream_push( [
317 'type' => 'end',
318 'data' => json_encode([
319 'success' => true,
320 'reply' => $restRes['reply'],
321 'images' => $restRes['images'],
322 'usage' => $restRes['usage']
323 ])
324 ] );
325 die();
326 }
327 else {
328 return $restRes;
329 }
330
331 }
332 catch ( Exception $e ) {
333 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
334 if ( $stream ) {
335 $this->stream_push( [ 'type' => 'error', 'data' => $message ] );
336 die();
337 }
338 else {
339 throw $e;
340 }
341 }
342 }
343
344 public function stream_push( $data ) {
345 $out = "data: " . json_encode( $data );
346 echo $out;
347 echo "\n\n";
348 if (ob_get_level() > 0) {
349 ob_end_flush();
350 }
351 flush();
352 }
353
354 public function inject_chat() {
355 $params = $this->core->get_chatbot( $this->siteWideChatId );
356 $clean_params = [];
357 if ( !empty( $params ) ) {
358 $clean_params['window'] = true;
359 $clean_params['id'] = $this->siteWideChatId;
360 echo $this->chat_shortcode( $clean_params );
361 }
362 return null;
363 }
364
365 public function build_front_params( $botId, $customId ) {
366 $frontSystem = [
367 'botId' => $customId ? null : $botId,
368 'customId' => $customId,
369 'userData' => $this->core->get_user_data(),
370 'sessionId' => $this->core->get_session_id(),
371 'restNonce' => $this->core->get_nonce(),
372 'contextId' => get_the_ID(),
373 'pluginUrl' => MWAI_URL,
374 'restUrl' => untrailingslashit( get_rest_url() ),
375 'debugMode' => $this->core->get_option( 'debug_mode' ),
376 'typewriter' => $this->core->get_option( 'shortcode_chat_typewriter' ),
377 'speech_recognition' => $this->core->get_option( 'speech_recognition' ),
378 'speech_synthesis' => $this->core->get_option( 'speech_synthesis' ),
379 'stream' => $this->core->get_option( 'shortcode_chat_stream' ),
380 ];
381 return $frontSystem;
382 }
383
384 public function resolveBotInfo( &$atts )
385 {
386 $chatbot = null;
387 $botId = $atts['id'] ?? null;
388 $customId = $atts['custom_id'] ?? null;
389 if (!$botId && !$customId) {
390 $botId = "default";
391 }
392 if ( $botId ) {
393 $chatbot = $this->core->get_chatbot( $botId );
394 if (!$chatbot) {
395 $botId = $botId ?: 'N/A';
396 return [
397 'error' => "AI Engine: Chatbot '{$botId}' not found. If you meant to set an ID for your custom chatbot, please use 'custom_id' instead of 'id'.",
398 ];
399 }
400 }
401 $chatbot = $chatbot ?: $this->core->get_chatbot( 'default' );
402 if ( !empty( $customId ) ) {
403 $botId = null;
404 }
405 unset( $atts['id'] );
406 return [
407 'chatbot' => $chatbot,
408 'botId' => $botId,
409 'customId' => $customId,
410 ];
411 }
412
413 public function chat_shortcode( $atts ) {
414 $atts = empty( $atts ) ? [] : $atts;
415
416 // Let the user override the chatbot params
417 $atts = apply_filters( 'mwai_chatbot_params', $atts );
418
419 // Resolve the bot info
420 $resolvedBot = $this->resolveBotInfo( $atts, 'chatbot' );
421 if ( isset( $resolvedBot['error'] ) ) {
422 return $resolvedBot['error'];
423 }
424 $chatbot = $resolvedBot['chatbot'];
425 $botId = $resolvedBot['botId'];
426 $customId = $resolvedBot['customId'];
427
428 // Rename the keys of the atts into camelCase to match the internal params system.
429 $atts = array_map( function( $key, $value ) {
430 $key = str_replace( '_', ' ', $key );
431 $key = ucwords( $key );
432 $key = str_replace( ' ', '', $key );
433 $key = lcfirst( $key );
434 return [ $key => $value ];
435 }, array_keys( $atts ), $atts );
436 $atts = array_merge( ...$atts );
437
438 $frontParams = [];
439 foreach ( MWAI_CHATBOT_FRONT_PARAMS as $param ) {
440 if ( isset( $atts[$param] ) ) {
441 if ( $param === 'localMemory' ) {
442 $frontParams[$param] = $atts[$param] === 'true';
443 }
444 else {
445 $frontParams[$param] = $atts[$param];
446 }
447 }
448 else if ( isset( $chatbot[$param] ) ) {
449 $frontParams[$param] = $chatbot[$param];
450 }
451 }
452
453 // Server Params
454 // NOTE: We don't need the server params for the chatbot if there are no overrides, it means
455 // we are using the default or a specific chatbot.
456 $hasServerOverrides = count( array_intersect( array_keys( $atts ), MWAI_CHATBOT_SERVER_PARAMS ) ) > 0;
457 $serverParams = [];
458 if ( $hasServerOverrides ) {
459 foreach ( MWAI_CHATBOT_SERVER_PARAMS as $param ) {
460 if ( isset( $atts[$param] ) ) {
461 $serverParams[$param] = $atts[$param];
462 }
463 else {
464 $serverParams[$param] = $chatbot[$param] ?? null;
465 }
466 }
467 }
468
469 // Front Params
470 $frontSystem = $this->build_front_params( $botId, $customId );
471
472 // Clean Params
473 $frontParams = $this->clean_params( $frontParams );
474 $frontSystem = $this->clean_params( $frontSystem );
475 $serverParams = $this->clean_params( $serverParams );
476
477 // Server-side: Keep the System Params
478 if ( $hasServerOverrides ) {
479 if ( empty( $customId ) ) {
480 $customId = md5( json_encode( $serverParams ) );
481 $frontSystem['customId'] = $customId;
482 }
483 set_transient( 'mwai_custom_chatbot_' . $customId, $serverParams, 60 * 60 * 24 );
484 }
485
486 // Client-side: Prepare JSON for Front Params and System Params
487 $theme = isset( $frontParams['themeId'] ) ? $this->core->get_theme( $frontParams['themeId'] ) : null;
488 $jsonFrontParams = htmlspecialchars( json_encode( $frontParams ), ENT_QUOTES, 'UTF-8' );
489 $jsonFrontSystem = htmlspecialchars( json_encode( $frontSystem ), ENT_QUOTES, 'UTF-8' );
490 $jsonFrontTheme = htmlspecialchars( json_encode( $theme ), ENT_QUOTES, 'UTF-8' );
491 //$jsonAttributes = htmlspecialchars(json_encode($atts), ENT_QUOTES, 'UTF-8');
492
493 $this->enqueue_scripts();
494 return "<div class='mwai-chatbot-container' data-params='{$jsonFrontParams}' data-system='{$jsonFrontSystem}' data-theme='{$jsonFrontTheme}'></div>";
495 }
496
497 function shortcode_chat_discussions( $atts ) {
498 $atts = empty($atts) ? [] : $atts;
499
500 // Resolve the bot info
501 $resolvedBot = $this->resolveBotInfo( $atts );
502 if ( isset( $resolvedBot['error'] ) ) {
503 return $resolvedBot['error'];
504 }
505 $chatbot = $resolvedBot['chatbot'];
506 $botId = $resolvedBot['botId'];
507 $customId = $resolvedBot['customId'];
508
509 // Rename the keys of the atts into camelCase to match the internal params system.
510 $atts = array_map( function( $key, $value ) {
511 $key = str_replace( '_', ' ', $key );
512 $key = ucwords( $key );
513 $key = str_replace( ' ', '', $key );
514 $key = lcfirst( $key );
515 return [ $key => $value ];
516 }, array_keys( $atts ), $atts );
517 $atts = array_merge( ...$atts );
518
519 // Front Params
520 $frontParams = [];
521 foreach ( MWAI_DISCUSSIONS_FRONT_PARAMS as $param ) {
522 if ( isset( $atts[$param] ) ) {
523 $frontParams[$param] = $atts[$param];
524 }
525 else if ( isset( $chatbot[$param] ) ) {
526 $frontParams[$param] = $chatbot[$param];
527 }
528 }
529
530 // Server Params
531 $serverParams = [];
532 foreach ( MWAI_DISCUSSIONS_SERVER_PARAMS as $param ) {
533 if ( isset( $atts[$param] ) ) {
534 $serverParams[$param] = $atts[$param];
535 }
536 }
537
538 // Front System
539 $frontSystem = $this->build_front_params( $botId, $customId );
540
541 // Clean Params
542 $frontParams = $this->clean_params( $frontParams );
543 $frontSystem = $this->clean_params( $frontSystem );
544 $serverParams = $this->clean_params( $serverParams );
545
546 $theme = isset( $frontParams['themeId'] ) ? $this->core->get_theme( $frontParams['themeId'] ) : null;
547 $jsonFrontParams = htmlspecialchars( json_encode( $frontParams ), ENT_QUOTES, 'UTF-8' );
548 $jsonFrontSystem = htmlspecialchars( json_encode( $frontSystem ), ENT_QUOTES, 'UTF-8' );
549 $jsonFrontTheme = htmlspecialchars( json_encode( $theme ), ENT_QUOTES, 'UTF-8' );
550
551 return "<div class='mwai-discussions-container' data-params='{$jsonFrontParams}' data-system='{$jsonFrontSystem}' data-theme='{$jsonFrontTheme}'></div>";
552 }
553
554 function clean_params( &$params ) {
555 foreach ( $params as $param => $value ) {
556 if ( $param === 'restNonce' ) {
557 continue;
558 }
559 if ( empty( $value ) || is_array( $value ) ) {
560 continue;
561 }
562 $lowerCaseValue = strtolower( $value );
563 if ( $lowerCaseValue === 'true' || $lowerCaseValue === 'false' || is_bool( $value ) ) {
564 $params[$param] = filter_var( $value, FILTER_VALIDATE_BOOLEAN );
565 }
566 else if ( is_numeric( $value ) ) {
567 $params[$param] = filter_var( $value, FILTER_VALIDATE_FLOAT );
568 }
569 }
570 return $params;
571 }
572
573 }
574