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