PluginProbe ʕ •ᴥ•ʔ
AI Engine – The Chatbot, AI Framework & MCP for WordPress / 1.9.93
AI Engine – The Chatbot, AI Framework & MCP for WordPress v1.9.93
3.5.9 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 security.php 3 years ago
chatbot.php
507 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',
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
67 public function basics_security_check( $botId, $customId, $newMessage ) {
68 if ( empty( $newMessage ) ) {
69 error_log("AI Engine: The query was rejected - message was empty.");
70 return false;
71 }
72 if ( !$botId && !$customId ) {
73 error_log("AI Engine: The query was rejected - no botId nor id was specified.");
74 return false;
75 }
76
77 $length = strlen( $newMessage );
78 if ( $length < 1 || $length > ( 4096 * 16 ) ) {
79 error_log("AI Engine: The query was rejected - message was too short or too long.");
80 return false;
81 }
82 return true;
83 }
84
85 public function rest_chat( $request ) {
86 $params = $request->get_json_params();
87 $botId = $params['botId'] ?? null;
88 $customId = $params['customId'] ?? null;
89 $stream = $params['stream'] ?? false;
90 $newMessage = trim( $params['newMessage'] ?? '' );
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, $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, $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 // Takeover
172 $takeoverAnswer = apply_filters( 'mwai_chatbot_takeover', null, $query, $params );
173 if ( !empty( $takeoverAnswer ) ) {
174 return [
175 'reply' => $takeoverAnswer,
176 'images' => null,
177 'usage' => null
178 ];
179 }
180
181 // Moderation
182 if ( $this->core->get_option( 'shortcode_chat_moderation' ) ) {
183 global $mwai;
184 $isFlagged = $mwai->moderationCheck( $query->prompt );
185 if ( $isFlagged ) {
186 throw new Exception( 'Sorry, your message has been rejected by moderation.' );
187 }
188 }
189
190 // Awareness & Embeddings
191 // TODO: This is same in Chatbot Legacy and Forms, maybe we should move it to the core?
192 $embeddingsEnvId = $params['embeddingsEnvId'] ?? null;
193 $embeddingsIndex = $params['embeddingsIndex'] ?? null;
194 $embeddingsNamespace = $params['embeddingsNamespace'] ?? null;
195 if ( $query->mode === 'chat' ) {
196 $context = apply_filters( 'mwai_context_search', $context, $query, [
197 'embeddingsEnvId' => $embeddingsEnvId,
198 'embeddingsIndex' => $embeddingsIndex,
199 'embeddingsNamespace' => $embeddingsNamespace
200 ] );
201 if ( !empty( $context ) ) {
202 if ( isset( $context['content'] ) ) {
203 $content = $this->core->cleanSentences( $context['content'] );
204 $query->injectContext( $content );
205 }
206 else {
207 error_log( "AI Engine: A context without content was returned." );
208 }
209 }
210 }
211 }
212
213 // Process Query
214 if ( $stream ) {
215 $streamCallback = function( $reply ) {
216 //$raw = _wp_specialchars( $reply, ENT_NOQUOTES, 'UTF-8', true );
217 $raw = $reply;
218 $this->stream_push( [ 'type' => 'live', 'data' => $raw ] );
219 if ( ob_get_level() > 0 ) {
220 ob_flush();
221 }
222 flush();
223 };
224 header( 'Cache-Control: no-cache' );
225 header( 'Content-Type: text/event-stream' );
226 header( 'X-Accel-Buffering: no' ); // This is useful to disable buffering in nginx through headers.
227 ob_implicit_flush( true );
228 ob_end_flush();
229 }
230
231 $reply = $this->core->ai->run( $query, $streamCallback );
232 $rawText = $reply->result;
233 $extra = [];
234 if ( $context ) {
235 $extra = [ 'embeddings' => $context['embeddings'] ];
236 }
237
238 $rawText = apply_filters( 'mwai_chatbot_reply', $rawText, $query, $params, $extra );
239 // TODO: There is no need for the shortcode_chat_formatting sice Markdown is handled on the client side.
240 // if ( $this->core->get_option( 'shortcode_chat_formatting' ) ) {
241 // $html = $this->core->markdown_to_html( $rawText );
242 // }
243
244 $restRes = [
245 'reply' => $rawText,
246 'images' => $reply->getType() === 'images' ? $reply->results : null,
247 'usage' => $reply->usage
248 ];
249
250 // Process Reply
251 if ( $stream ) {
252 $this->stream_push( [
253 'type' => 'end',
254 'data' => json_encode([
255 'success' => true,
256 'reply' => $restRes['reply'],
257 'images' => $restRes['images'],
258 'usage' => $restRes['usage']
259 ])
260 ] );
261 die();
262 }
263 else {
264 return $restRes;
265 }
266
267 }
268 catch ( Exception $e ) {
269 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
270 if ( $stream ) {
271 $this->stream_push( [ 'type' => 'error', 'data' => $message ] );
272 die();
273 }
274 else {
275 throw $e;
276 }
277 }
278 }
279
280 public function stream_push( $data ) {
281 $out = "data: " . json_encode( $data );
282 echo $out;
283 echo "\n\n";
284 if (ob_get_level() > 0) {
285 ob_end_flush();
286 }
287 flush();
288 }
289
290 public function inject_chat() {
291 $params = $this->core->getChatbot( $this->siteWideChatId );
292 $cleanParams = [];
293 if ( !empty( $params ) ) {
294 $cleanParams['window'] = true;
295 $cleanParams['id'] = $this->siteWideChatId;
296 echo $this->chat_shortcode( $cleanParams );
297 }
298 return null;
299 }
300
301 public function build_front_params( $botId, $customId ) {
302 $frontSystem = [
303 'botId' => $customId ? null : $botId,
304 'customId' => $customId,
305 'userData' => $this->core->getUserData(),
306 'sessionId' => $this->core->get_session_id(),
307 'restNonce' => $this->core->get_nonce(),
308 'contextId' => get_the_ID(),
309 'pluginUrl' => MWAI_URL,
310 'restUrl' => untrailingslashit( rest_url() ),
311 'debugMode' => $this->core->get_option( 'debug_mode' ),
312 'typewriter' => $this->core->get_option( 'shortcode_chat_typewriter' ),
313 'speech_recognition' => $this->core->get_option( 'speech_recognition' ),
314 'speech_synthesis' => $this->core->get_option( 'speech_synthesis' ),
315 'stream' => $this->core->get_option( 'shortcode_chat_stream' ),
316 ];
317 return $frontSystem;
318 }
319
320 public function resolveBotInfo( &$atts )
321 {
322 $chatbot = null;
323 $botId = $atts['id'] ?? null;
324 $customId = $atts['custom_id'] ?? null;
325 if (!$botId && !$customId) {
326 $botId = "default";
327 }
328 if ( $botId ) {
329 $chatbot = $this->core->getChatbot( $botId );
330 if (!$chatbot) {
331 $botId = $botId ?: 'N/A';
332 return [
333 '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'.",
334 ];
335 }
336 }
337 $chatbot = $chatbot ?: $this->core->getChatbot( 'default' );
338 if ( !empty( $customId ) ) {
339 $botId = null;
340 }
341 unset( $atts['id'] );
342 return [
343 'chatbot' => $chatbot,
344 'botId' => $botId,
345 'customId' => $customId,
346 ];
347 }
348
349 public function chat_shortcode( $atts ) {
350 $atts = empty( $atts ) ? [] : $atts;
351
352 // Let the user override the chatbot params
353 $atts = apply_filters( 'mwai_chatbot_params', $atts );
354
355 // Resolve the bot info
356 $resolvedBot = $this->resolveBotInfo( $atts, 'chatbot' );
357 if ( isset( $resolvedBot['error'] ) ) {
358 return $resolvedBot['error'];
359 }
360 $chatbot = $resolvedBot['chatbot'];
361 $botId = $resolvedBot['botId'];
362 $customId = $resolvedBot['customId'];
363
364 // Rename the keys of the atts into camelCase to match the internal params system.
365 $atts = array_map( function( $key, $value ) {
366 $key = str_replace( '_', ' ', $key );
367 $key = ucwords( $key );
368 $key = str_replace( ' ', '', $key );
369 $key = lcfirst( $key );
370 return [ $key => $value ];
371 }, array_keys( $atts ), $atts );
372 $atts = array_merge( ...$atts );
373
374 $frontParams = [];
375 foreach ( MWAI_CHATBOT_FRONT_PARAMS as $param ) {
376 if ( isset( $atts[$param] ) ) {
377 if ( $param === 'localMemory' ) {
378 $frontParams[$param] = $atts[$param] === 'true';
379 }
380 else {
381 $frontParams[$param] = $atts[$param];
382 }
383 }
384 else if ( isset( $chatbot[$param] ) ) {
385 $frontParams[$param] = $chatbot[$param];
386 }
387 }
388
389 // Server Params
390 // NOTE: We don't need the server params for the chatbot if there are no overrides, it means
391 // we are using the default or a specific chatbot.
392 $hasServerOverrides = count( array_intersect( array_keys( $atts ), MWAI_CHATBOT_SERVER_PARAMS ) ) > 0;
393 $serverParams = [];
394 if ( $hasServerOverrides ) {
395 foreach ( MWAI_CHATBOT_SERVER_PARAMS as $param ) {
396 if ( isset( $atts[$param] ) ) {
397 $serverParams[$param] = $atts[$param];
398 }
399 else {
400 $serverParams[$param] = $chatbot[$param] ?? null;
401 }
402 }
403 }
404
405 // Front Params
406 $frontSystem = $this->build_front_params( $botId, $customId );
407
408 // Clean Params
409 $frontParams = $this->cleanParams( $frontParams );
410 $frontSystem = $this->cleanParams( $frontSystem );
411 $serverParams = $this->cleanParams( $serverParams );
412
413 // Server-side: Keep the System Params
414 if ( $hasServerOverrides ) {
415 if ( empty( $customId ) ) {
416 $customId = md5( json_encode( $serverParams ) );
417 $frontSystem['customId'] = $customId;
418 }
419 set_transient( 'mwai_custom_chatbot_' . $customId, $serverParams, 60 * 60 * 24 );
420 }
421
422 // Client-side: Prepare JSON for Front Params and System Params
423 $theme = isset( $frontParams['themeId'] ) ? $this->core->getTheme( $frontParams['themeId'] ) : null;
424 $jsonFrontParams = htmlspecialchars( json_encode( $frontParams ), ENT_QUOTES, 'UTF-8' );
425 $jsonFrontSystem = htmlspecialchars( json_encode( $frontSystem ), ENT_QUOTES, 'UTF-8' );
426 $jsonFrontTheme = htmlspecialchars( json_encode( $theme ), ENT_QUOTES, 'UTF-8' );
427 //$jsonAttributes = htmlspecialchars(json_encode($atts), ENT_QUOTES, 'UTF-8');
428
429 $this->enqueue_scripts();
430 return "<div class='mwai-chatbot-container' data-params='{$jsonFrontParams}' data-system='{$jsonFrontSystem}' data-theme='{$jsonFrontTheme}'></div>";
431 }
432
433 function shortcode_chat_discussions( $atts ) {
434 $atts = empty($atts) ? [] : $atts;
435
436 // Resolve the bot info
437 $resolvedBot = $this->resolveBotInfo( $atts );
438 if ( isset( $resolvedBot['error'] ) ) {
439 return $resolvedBot['error'];
440 }
441 $chatbot = $resolvedBot['chatbot'];
442 $botId = $resolvedBot['botId'];
443 $customId = $resolvedBot['customId'];
444
445 // Rename the keys of the atts into camelCase to match the internal params system.
446 $atts = array_map( function( $key, $value ) {
447 $key = str_replace( '_', ' ', $key );
448 $key = ucwords( $key );
449 $key = str_replace( ' ', '', $key );
450 $key = lcfirst( $key );
451 return [ $key => $value ];
452 }, array_keys( $atts ), $atts );
453 $atts = array_merge( ...$atts );
454
455 // Front Params
456 $frontParams = [];
457 foreach ( MWAI_DISCUSSIONS_FRONT_PARAMS as $param ) {
458 if ( isset( $atts[$param] ) ) {
459 $frontParams[$param] = $atts[$param];
460 }
461 else if ( isset( $chatbot[$param] ) ) {
462 $frontParams[$param] = $chatbot[$param];
463 }
464 }
465
466 // Server Params
467 $serverParams = [];
468 foreach ( MWAI_DISCUSSIONS_SERVER_PARAMS as $param ) {
469 if ( isset( $atts[$param] ) ) {
470 $serverParams[$param] = $atts[$param];
471 }
472 }
473
474 // Front System
475 $frontSystem = $this->build_front_params( $botId, $customId );
476
477 // Clean Params
478 $frontParams = $this->cleanParams( $frontParams );
479 $frontSystem = $this->cleanParams( $frontSystem );
480 $serverParams = $this->cleanParams( $serverParams );
481
482 $theme = isset( $frontParams['themeId'] ) ? $this->core->getTheme( $frontParams['themeId'] ) : null;
483 $jsonFrontParams = htmlspecialchars( json_encode( $frontParams ), ENT_QUOTES, 'UTF-8' );
484 $jsonFrontSystem = htmlspecialchars( json_encode( $frontSystem ), ENT_QUOTES, 'UTF-8' );
485 $jsonFrontTheme = htmlspecialchars( json_encode( $theme ), ENT_QUOTES, 'UTF-8' );
486
487 return "<div class='mwai-discussions-container' data-params='{$jsonFrontParams}' data-system='{$jsonFrontSystem}' data-theme='{$jsonFrontTheme}'></div>";
488 }
489
490 function cleanParams( &$params ) {
491 foreach ( $params as $param => $value ) {
492 if ( empty( $value ) || is_array( $value ) ) {
493 continue;
494 }
495 $lowerCaseValue = strtolower( $value );
496 if ( $lowerCaseValue === 'true' || $lowerCaseValue === 'false' || is_bool( $value ) ) {
497 $params[$param] = filter_var( $value, FILTER_VALIDATE_BOOLEAN );
498 }
499 else if ( is_numeric( $value ) ) {
500 $params[$param] = filter_var( $value, FILTER_VALIDATE_FLOAT );
501 }
502 }
503 return $params;
504 }
505
506 }
507