PluginProbe ʕ •ᴥ•ʔ
AI Engine – The Chatbot, AI Framework & MCP for WordPress / 1.9.99
AI Engine – The Chatbot, AI Framework & MCP for WordPress v1.9.99
3.5.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
assistants.php 3 years ago chatbot.php 2 years ago chatbot_legacy.php 2 years ago discussions.php 2 years ago files.php 2 years ago security.php 3 years ago
chatbot.php
520 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',
7 'textInputPlaceholder', 'textInputMaxLength', 'textCompliance', 'startSentence', 'localMemory',
8 'themeId', 'window', 'icon', 'iconText', 'iconAlt', 'iconPosition', 'fullscreen', 'copyButton'
9 ] );
10 define( 'MWAI_CHATBOT_SERVER_PARAMS', [ 'id', 'envId', 'env', 'mode', 'contentAware', 'context',
11 'embeddingsEnvId', 'embeddingsIndex', 'embeddingsNamespace',
12 'casuallyFineTuned', 'promptEnding', 'completionEnding', 'model', 'temperature', 'maxTokens',
13 'maxResults', 'apiKey', 'service'
14 ] );
15
16 // Params for the discussions (front and server)
17
18 define( 'MWAI_DISCUSSIONS_FRONT_PARAMS', [ 'themeId', 'textNewChat' ] );
19 define( 'MWAI_DISCUSSIONS_SERVER_PARAMS', [ 'customId' ] );
20
21 class Meow_MWAI_Modules_Chatbot {
22 private $core = null;
23 private $namespace = 'mwai-ui/v1';
24 private $siteWideChatId = null;
25
26 public function __construct() {
27 global $mwai_core;
28 $this->core = $mwai_core;
29 add_shortcode( 'mwai_chatbot', array( $this, 'chat_shortcode' ) );
30 add_shortcode( 'mwai_chatbot_v2', array( $this, 'chat_shortcode' ) );
31 add_action( 'rest_api_init', array( $this, 'rest_api_init' ) );
32 $this->siteWideChatId = $this->core->get_option( 'botId' );
33 add_action( 'wp_enqueue_scripts', array( $this, 'register_scripts' ) );
34
35 if ( $this->core->get_option( 'shortcode_chat_discussions' ) ) {
36 add_shortcode( 'mwai_discussions', [ $this, 'shortcode_chat_discussions' ] );
37 }
38 }
39
40 public function register_scripts() {
41 $physical_file = trailingslashit( MWAI_PATH ) . 'app/chatbot.js';
42 $cache_buster = file_exists( $physical_file ) ? filemtime( $physical_file ) : MWAI_VERSION;
43 wp_register_script( 'mwai_chatbot', trailingslashit( MWAI_URL ) . 'app/chatbot.js',
44 [ 'wp-element' ], $cache_buster, false );
45 if ( !empty( $this->siteWideChatId ) && $this->siteWideChatId !== 'none' ) {
46 $this->enqueue_scripts();
47 add_action( 'wp_footer', array( $this, 'inject_chat' ) );
48 }
49 }
50
51 public function enqueue_scripts() {
52 wp_enqueue_script( "mwai_chatbot" );
53 if ( $this->core->get_option( 'shortcode_chat_syntax_highlighting' ) ) {
54 wp_enqueue_script( "mwai_highlight" );
55 }
56 }
57
58 public function rest_api_init() {
59 register_rest_route( $this->namespace, '/chats/submit', array(
60 'methods' => 'POST',
61 'callback' => array( $this, 'rest_chat' ),
62 'permission_callback' => '__return_true'
63 ) );
64 }
65
66 public function basics_security_check( $botId, $customId, $newMessage ) {
67 if ( empty( $newMessage ) ) {
68 error_log("AI Engine: The query was rejected - message was empty.");
69 return false;
70 }
71 if ( !$botId && !$customId ) {
72 error_log("AI Engine: The query was rejected - no botId nor id was specified.");
73 return false;
74 }
75
76 $length = strlen( $newMessage );
77 if ( $length < 1 || $length > ( 4096 * 16 ) ) {
78 error_log("AI Engine: The query was rejected - message was too short or too long.");
79 return false;
80 }
81 return true;
82 }
83
84 public function rest_chat( $request ) {
85 $params = $request->get_json_params();
86 $botId = $params['botId'] ?? null;
87 $customId = $params['customId'] ?? null;
88 $stream = $params['stream'] ?? false;
89 $newMessage = trim( $params['newMessage'] ?? '' );
90 $newImageId = $params['newImageId'] ?? null;
91
92 if ( !$this->basics_security_check( $botId, $customId, $newMessage )) {
93 return new WP_REST_Response( [
94 'success' => false,
95 'message' => apply_filters( 'mwai_ai_exception', 'Sorry, your query has been rejected.' )
96 ], 403 );
97 }
98
99 try {
100 $data = $this->chat_submit( $botId, $newMessage, $newImageId, $params, $stream );
101 return new WP_REST_Response( [
102 'success' => true,
103 'reply' => $data['reply'],
104 'images' => $data['images'],
105 'usage' => $data['usage']
106 ], 200 );
107 }
108 catch ( Exception $e ) {
109 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
110 return new WP_REST_Response( [
111 'success' => false,
112 'message' => $message
113 ], 500 );
114 }
115 }
116
117 public function chat_submit( $botId, $newMessage, $newImageId, $params = [], $stream = false ) {
118 try {
119 $chatbot = null;
120 $customId = $params['customId'] ?? null;
121
122 // Custom Chatbot
123 if ( $customId ) {
124 $chatbot = get_transient( 'mwai_custom_chatbot_' . $customId );
125 }
126 // Registered Chatbot
127 if ( !$chatbot && $botId ) {
128 $chatbot = $this->core->getChatbot( $botId );
129 }
130
131 if ( !$chatbot ) {
132 error_log("AI Engine: No chatbot was found for this query.");
133 throw new Exception( 'Sorry, your query has been rejected.' );
134 }
135
136 // Create QueryText
137 $context = null;
138 $mode = $chatbot['mode'] ?? 'chat';
139
140 if ( $mode === 'images' ) {
141 $query = new Meow_MWAI_Query_Image( $newMessage );
142
143 // Handle Params
144 $newParams = [];
145 foreach ( $chatbot as $key => $value ) {
146 $newParams[$key] = $value;
147 }
148 foreach ( $params as $key => $value ) {
149 $newParams[$key] = $value;
150 }
151 $params = apply_filters( 'mwai_chatbot_params', $newParams );
152 $params['env'] = empty( $params['env'] ) ? 'chatbot' : $params['env'];
153 $query->injectParams( $params );
154 }
155 else {
156 $query = new Meow_MWAI_Query_Text( $newMessage, 1024 );
157 $streamCallback = null;
158
159 // Handle Params
160 $newParams = [];
161 foreach ( $chatbot as $key => $value ) {
162 $newParams[$key] = $value;
163 }
164 foreach ( $params as $key => $value ) {
165 $newParams[$key] = $value;
166 }
167 $params = apply_filters( 'mwai_chatbot_params', $newParams );
168 $params['env'] = empty( $params['env'] ) ? 'chatbot' : $params['env'];
169 $query->injectParams( $params );
170
171 // Support for Uploaded Image
172 if ( !empty( $newImageId ) ) {
173 $remote_upload = $this->core->get_option( 'image_remote_upload' );
174 if ( $remote_upload === 'data' ) {
175 $data = $this->core->files->get_base64_data( $newImageId );
176 $query->setNewImageData( $data );
177 }
178 else {
179 $url = $this->core->files->get_url( $newImageId );
180 $query->setNewImage( $url );
181 }
182 }
183
184 // Takeover
185 $takeoverAnswer = apply_filters( 'mwai_chatbot_takeover', null, $query, $params );
186 if ( !empty( $takeoverAnswer ) ) {
187 return [
188 'reply' => $takeoverAnswer,
189 'images' => null,
190 'usage' => null
191 ];
192 }
193
194 // Moderation
195 if ( $this->core->get_option( 'shortcode_chat_moderation' ) ) {
196 global $mwai;
197 $isFlagged = $mwai->moderationCheck( $query->prompt );
198 if ( $isFlagged ) {
199 throw new Exception( 'Sorry, your message has been rejected by moderation.' );
200 }
201 }
202
203 // Awareness & Embeddings
204 // TODO: This is same in Chatbot Legacy and Forms, maybe we should move it to the core?
205 $embeddingsEnvId = $params['embeddingsEnvId'] ?? null;
206 $embeddingsIndex = $params['embeddingsIndex'] ?? null;
207 $embeddingsNamespace = $params['embeddingsNamespace'] ?? null;
208 if ( $query->mode === 'chat' ) {
209 $context = apply_filters( 'mwai_context_search', $context, $query, [
210 'embeddingsEnvId' => $embeddingsEnvId,
211 'embeddingsIndex' => $embeddingsIndex,
212 'embeddingsNamespace' => $embeddingsNamespace
213 ] );
214 if ( !empty( $context ) ) {
215 if ( isset( $context['content'] ) ) {
216 $content = $this->core->cleanSentences( $context['content'] );
217 $query->injectContext( $content );
218 }
219 else {
220 error_log( "AI Engine: A context without content was returned." );
221 }
222 }
223 }
224 }
225
226 // Process Query
227 if ( $stream ) {
228 $streamCallback = function( $reply ) {
229 //$raw = _wp_specialchars( $reply, ENT_NOQUOTES, 'UTF-8', true );
230 $raw = $reply;
231 $this->stream_push( [ 'type' => 'live', 'data' => $raw ] );
232 if ( ob_get_level() > 0 ) {
233 ob_flush();
234 }
235 flush();
236 };
237 header( 'Cache-Control: no-cache' );
238 header( 'Content-Type: text/event-stream' );
239 header( 'X-Accel-Buffering: no' ); // This is useful to disable buffering in nginx through headers.
240 ob_implicit_flush( true );
241 ob_end_flush();
242 }
243
244 $reply = $this->core->ai->run( $query, $streamCallback );
245 $rawText = $reply->result;
246 $extra = [];
247 if ( $context ) {
248 $extra = [ 'embeddings' => $context['embeddings'] ];
249 }
250
251 $rawText = apply_filters( 'mwai_chatbot_reply', $rawText, $query, $params, $extra );
252 // TODO: There is no need for the shortcode_chat_formatting sice Markdown is handled on the client side.
253 // if ( $this->core->get_option( 'shortcode_chat_formatting' ) ) {
254 // $html = $this->core->markdown_to_html( $rawText );
255 // }
256
257 $restRes = [
258 'reply' => $rawText,
259 'images' => $reply->getType() === 'images' ? $reply->results : null,
260 'usage' => $reply->usage
261 ];
262
263 // Process Reply
264 if ( $stream ) {
265 $this->stream_push( [
266 'type' => 'end',
267 'data' => json_encode([
268 'success' => true,
269 'reply' => $restRes['reply'],
270 'images' => $restRes['images'],
271 'usage' => $restRes['usage']
272 ])
273 ] );
274 die();
275 }
276 else {
277 return $restRes;
278 }
279
280 }
281 catch ( Exception $e ) {
282 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
283 if ( $stream ) {
284 $this->stream_push( [ 'type' => 'error', 'data' => $message ] );
285 die();
286 }
287 else {
288 throw $e;
289 }
290 }
291 }
292
293 public function stream_push( $data ) {
294 $out = "data: " . json_encode( $data );
295 echo $out;
296 echo "\n\n";
297 if (ob_get_level() > 0) {
298 ob_end_flush();
299 }
300 flush();
301 }
302
303 public function inject_chat() {
304 $params = $this->core->getChatbot( $this->siteWideChatId );
305 $cleanParams = [];
306 if ( !empty( $params ) ) {
307 $cleanParams['window'] = true;
308 $cleanParams['id'] = $this->siteWideChatId;
309 echo $this->chat_shortcode( $cleanParams );
310 }
311 return null;
312 }
313
314 public function build_front_params( $botId, $customId ) {
315 $frontSystem = [
316 'botId' => $customId ? null : $botId,
317 'customId' => $customId,
318 'userData' => $this->core->getUserData(),
319 'sessionId' => $this->core->get_session_id(),
320 'restNonce' => $this->core->get_nonce(),
321 'contextId' => get_the_ID(),
322 'pluginUrl' => MWAI_URL,
323 'restUrl' => untrailingslashit( rest_url() ),
324 'debugMode' => $this->core->get_option( 'debug_mode' ),
325 'typewriter' => $this->core->get_option( 'shortcode_chat_typewriter' ),
326 'speech_recognition' => $this->core->get_option( 'speech_recognition' ),
327 'speech_synthesis' => $this->core->get_option( 'speech_synthesis' ),
328 'stream' => $this->core->get_option( 'shortcode_chat_stream' ),
329 ];
330 return $frontSystem;
331 }
332
333 public function resolveBotInfo( &$atts )
334 {
335 $chatbot = null;
336 $botId = $atts['id'] ?? null;
337 $customId = $atts['custom_id'] ?? null;
338 if (!$botId && !$customId) {
339 $botId = "default";
340 }
341 if ( $botId ) {
342 $chatbot = $this->core->getChatbot( $botId );
343 if (!$chatbot) {
344 $botId = $botId ?: 'N/A';
345 return [
346 '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'.",
347 ];
348 }
349 }
350 $chatbot = $chatbot ?: $this->core->getChatbot( 'default' );
351 if ( !empty( $customId ) ) {
352 $botId = null;
353 }
354 unset( $atts['id'] );
355 return [
356 'chatbot' => $chatbot,
357 'botId' => $botId,
358 'customId' => $customId,
359 ];
360 }
361
362 public function chat_shortcode( $atts ) {
363 $atts = empty( $atts ) ? [] : $atts;
364
365 // Let the user override the chatbot params
366 $atts = apply_filters( 'mwai_chatbot_params', $atts );
367
368 // Resolve the bot info
369 $resolvedBot = $this->resolveBotInfo( $atts, 'chatbot' );
370 if ( isset( $resolvedBot['error'] ) ) {
371 return $resolvedBot['error'];
372 }
373 $chatbot = $resolvedBot['chatbot'];
374 $botId = $resolvedBot['botId'];
375 $customId = $resolvedBot['customId'];
376
377 // Rename the keys of the atts into camelCase to match the internal params system.
378 $atts = array_map( function( $key, $value ) {
379 $key = str_replace( '_', ' ', $key );
380 $key = ucwords( $key );
381 $key = str_replace( ' ', '', $key );
382 $key = lcfirst( $key );
383 return [ $key => $value ];
384 }, array_keys( $atts ), $atts );
385 $atts = array_merge( ...$atts );
386
387 $frontParams = [];
388 foreach ( MWAI_CHATBOT_FRONT_PARAMS as $param ) {
389 if ( isset( $atts[$param] ) ) {
390 if ( $param === 'localMemory' ) {
391 $frontParams[$param] = $atts[$param] === 'true';
392 }
393 else {
394 $frontParams[$param] = $atts[$param];
395 }
396 }
397 else if ( isset( $chatbot[$param] ) ) {
398 $frontParams[$param] = $chatbot[$param];
399 }
400 }
401
402 // Server Params
403 // NOTE: We don't need the server params for the chatbot if there are no overrides, it means
404 // we are using the default or a specific chatbot.
405 $hasServerOverrides = count( array_intersect( array_keys( $atts ), MWAI_CHATBOT_SERVER_PARAMS ) ) > 0;
406 $serverParams = [];
407 if ( $hasServerOverrides ) {
408 foreach ( MWAI_CHATBOT_SERVER_PARAMS as $param ) {
409 if ( isset( $atts[$param] ) ) {
410 $serverParams[$param] = $atts[$param];
411 }
412 else {
413 $serverParams[$param] = $chatbot[$param] ?? null;
414 }
415 }
416 }
417
418 // Front Params
419 $frontSystem = $this->build_front_params( $botId, $customId );
420
421 // Clean Params
422 $frontParams = $this->cleanParams( $frontParams );
423 $frontSystem = $this->cleanParams( $frontSystem );
424 $serverParams = $this->cleanParams( $serverParams );
425
426 // Server-side: Keep the System Params
427 if ( $hasServerOverrides ) {
428 if ( empty( $customId ) ) {
429 $customId = md5( json_encode( $serverParams ) );
430 $frontSystem['customId'] = $customId;
431 }
432 set_transient( 'mwai_custom_chatbot_' . $customId, $serverParams, 60 * 60 * 24 );
433 }
434
435 // Client-side: Prepare JSON for Front Params and System Params
436 $theme = isset( $frontParams['themeId'] ) ? $this->core->getTheme( $frontParams['themeId'] ) : null;
437 $jsonFrontParams = htmlspecialchars( json_encode( $frontParams ), ENT_QUOTES, 'UTF-8' );
438 $jsonFrontSystem = htmlspecialchars( json_encode( $frontSystem ), ENT_QUOTES, 'UTF-8' );
439 $jsonFrontTheme = htmlspecialchars( json_encode( $theme ), ENT_QUOTES, 'UTF-8' );
440 //$jsonAttributes = htmlspecialchars(json_encode($atts), ENT_QUOTES, 'UTF-8');
441
442 $this->enqueue_scripts();
443 return "<div class='mwai-chatbot-container' data-params='{$jsonFrontParams}' data-system='{$jsonFrontSystem}' data-theme='{$jsonFrontTheme}'></div>";
444 }
445
446 function shortcode_chat_discussions( $atts ) {
447 $atts = empty($atts) ? [] : $atts;
448
449 // Resolve the bot info
450 $resolvedBot = $this->resolveBotInfo( $atts );
451 if ( isset( $resolvedBot['error'] ) ) {
452 return $resolvedBot['error'];
453 }
454 $chatbot = $resolvedBot['chatbot'];
455 $botId = $resolvedBot['botId'];
456 $customId = $resolvedBot['customId'];
457
458 // Rename the keys of the atts into camelCase to match the internal params system.
459 $atts = array_map( function( $key, $value ) {
460 $key = str_replace( '_', ' ', $key );
461 $key = ucwords( $key );
462 $key = str_replace( ' ', '', $key );
463 $key = lcfirst( $key );
464 return [ $key => $value ];
465 }, array_keys( $atts ), $atts );
466 $atts = array_merge( ...$atts );
467
468 // Front Params
469 $frontParams = [];
470 foreach ( MWAI_DISCUSSIONS_FRONT_PARAMS as $param ) {
471 if ( isset( $atts[$param] ) ) {
472 $frontParams[$param] = $atts[$param];
473 }
474 else if ( isset( $chatbot[$param] ) ) {
475 $frontParams[$param] = $chatbot[$param];
476 }
477 }
478
479 // Server Params
480 $serverParams = [];
481 foreach ( MWAI_DISCUSSIONS_SERVER_PARAMS as $param ) {
482 if ( isset( $atts[$param] ) ) {
483 $serverParams[$param] = $atts[$param];
484 }
485 }
486
487 // Front System
488 $frontSystem = $this->build_front_params( $botId, $customId );
489
490 // Clean Params
491 $frontParams = $this->cleanParams( $frontParams );
492 $frontSystem = $this->cleanParams( $frontSystem );
493 $serverParams = $this->cleanParams( $serverParams );
494
495 $theme = isset( $frontParams['themeId'] ) ? $this->core->getTheme( $frontParams['themeId'] ) : null;
496 $jsonFrontParams = htmlspecialchars( json_encode( $frontParams ), ENT_QUOTES, 'UTF-8' );
497 $jsonFrontSystem = htmlspecialchars( json_encode( $frontSystem ), ENT_QUOTES, 'UTF-8' );
498 $jsonFrontTheme = htmlspecialchars( json_encode( $theme ), ENT_QUOTES, 'UTF-8' );
499
500 return "<div class='mwai-discussions-container' data-params='{$jsonFrontParams}' data-system='{$jsonFrontSystem}' data-theme='{$jsonFrontTheme}'></div>";
501 }
502
503 function cleanParams( &$params ) {
504 foreach ( $params as $param => $value ) {
505 if ( empty( $value ) || is_array( $value ) ) {
506 continue;
507 }
508 $lowerCaseValue = strtolower( $value );
509 if ( $lowerCaseValue === 'true' || $lowerCaseValue === 'false' || is_bool( $value ) ) {
510 $params[$param] = filter_var( $value, FILTER_VALIDATE_BOOLEAN );
511 }
512 else if ( is_numeric( $value ) ) {
513 $params[$param] = filter_var( $value, FILTER_VALIDATE_FLOAT );
514 }
515 }
516 return $params;
517 }
518
519 }
520