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