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