PluginProbe ʕ •ᴥ•ʔ
AI Engine – The Chatbot, AI Framework & MCP for WordPress / 2.2.3
AI Engine – The Chatbot, AI Framework & MCP for WordPress v2.2.3
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
536 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, $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 $query->set_file( $data, 'data', 'vision' );
206 }
207 else {
208 $url = $this->core->files->get_url( $newFileId );
209 $query->set_file( $url, 'url', 'vision' );
210 }
211 $fileId = $this->core->files->get_id_from_refId( $newFileId );
212 $this->core->files->update_envId( $fileId, $query->envId );
213 $this->core->files->add_metadata( $fileId, 'query_envId', $query->envId );
214 $this->core->files->add_metadata( $fileId, 'query_session', $query->session );
215 }
216 }
217
218 // Takeover
219 $takeoverAnswer = apply_filters( 'mwai_chatbot_takeover', null, $query, $params );
220 if ( !empty( $takeoverAnswer ) ) {
221 return [
222 'reply' => $takeoverAnswer,
223 'images' => null,
224 'usage' => null
225 ];
226 }
227
228 // Moderation
229 if ( $this->core->get_option( 'shortcode_chat_moderation' ) ) {
230 global $mwai;
231 $isFlagged = $mwai->moderationCheck( $query->get_message() );
232 if ( $isFlagged ) {
233 throw new Exception( 'Sorry, your message has been rejected by moderation.' );
234 }
235 }
236
237 // Awareness & Embeddings
238 // TODO: We probably don't need to limit the context...
239 //if ( $query->mode === 'chat' || $query->mode === 'assistant' ) {
240 $context = $this->core->retrieve_context( $params, $query );
241 if ( !empty( $context ) ) {
242 $query->set_context( $context['content'] );
243 }
244 //}
245 }
246
247 // Process Query
248 if ( $stream ) {
249 $streamCallback = function( $reply ) {
250 $raw = $reply;
251 $this->stream_push( [ 'type' => 'live', 'data' => $raw ] );
252 if ( ob_get_level() > 0 ) {
253 ob_flush();
254 }
255 flush();
256 };
257 header( 'Cache-Control: no-cache' );
258 header( 'Content-Type: text/event-stream' );
259 // This is useful to disable buffering in nginx through headers.
260 header( 'X-Accel-Buffering: no' );
261 ob_implicit_flush( true );
262 ob_end_flush();
263 }
264
265 $reply = $this->core->run_query( $query, $streamCallback, true );
266 $rawText = $reply->result;
267 $extra = [];
268 if ( $context ) {
269 $extra = [ 'embeddings' => $context['embeddings'] ];
270 }
271 $rawText = apply_filters( 'mwai_chatbot_reply', $rawText, $query, $params, $extra );
272
273 $restRes = [
274 'reply' => $rawText,
275 'images' => $reply->get_type() === 'images' ? $reply->results : null,
276 'usage' => $reply->usage
277 ];
278
279 // Process Reply
280 if ( $stream ) {
281 $this->stream_push( [
282 'type' => 'end',
283 'data' => json_encode([
284 'success' => true,
285 'reply' => $restRes['reply'],
286 'images' => $restRes['images'],
287 'usage' => $restRes['usage']
288 ])
289 ] );
290 die();
291 }
292 else {
293 return $restRes;
294 }
295
296 }
297 catch ( Exception $e ) {
298 $message = apply_filters( 'mwai_ai_exception', $e->getMessage() );
299 if ( $stream ) {
300 $this->stream_push( [ 'type' => 'error', 'data' => $message ] );
301 die();
302 }
303 else {
304 throw $e;
305 }
306 }
307 }
308
309 public function stream_push( $data ) {
310 $out = "data: " . json_encode( $data );
311 echo $out;
312 echo "\n\n";
313 if (ob_get_level() > 0) {
314 ob_end_flush();
315 }
316 flush();
317 }
318
319 public function inject_chat() {
320 $params = $this->core->get_chatbot( $this->siteWideChatId );
321 $clean_params = [];
322 if ( !empty( $params ) ) {
323 $clean_params['window'] = true;
324 $clean_params['id'] = $this->siteWideChatId;
325 echo $this->chat_shortcode( $clean_params );
326 }
327 return null;
328 }
329
330 public function build_front_params( $botId, $customId ) {
331 $frontSystem = [
332 'botId' => $customId ? null : $botId,
333 'customId' => $customId,
334 'userData' => $this->core->get_user_data(),
335 'sessionId' => $this->core->get_session_id(),
336 'restNonce' => $this->core->get_nonce(),
337 'contextId' => get_the_ID(),
338 'pluginUrl' => MWAI_URL,
339 'restUrl' => untrailingslashit( get_rest_url() ),
340 'debugMode' => $this->core->get_option( 'debug_mode' ),
341 'typewriter' => $this->core->get_option( 'shortcode_chat_typewriter' ),
342 'speech_recognition' => $this->core->get_option( 'speech_recognition' ),
343 'speech_synthesis' => $this->core->get_option( 'speech_synthesis' ),
344 'stream' => $this->core->get_option( 'shortcode_chat_stream' ),
345 ];
346 return $frontSystem;
347 }
348
349 public function resolveBotInfo( &$atts )
350 {
351 $chatbot = null;
352 $botId = $atts['id'] ?? null;
353 $customId = $atts['custom_id'] ?? null;
354 if (!$botId && !$customId) {
355 $botId = "default";
356 }
357 if ( $botId ) {
358 $chatbot = $this->core->get_chatbot( $botId );
359 if (!$chatbot) {
360 $botId = $botId ?: 'N/A';
361 return [
362 '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'.",
363 ];
364 }
365 }
366 $chatbot = $chatbot ?: $this->core->get_chatbot( 'default' );
367 if ( !empty( $customId ) ) {
368 $botId = null;
369 }
370 unset( $atts['id'] );
371 return [
372 'chatbot' => $chatbot,
373 'botId' => $botId,
374 'customId' => $customId,
375 ];
376 }
377
378 public function chat_shortcode( $atts ) {
379 $atts = empty( $atts ) ? [] : $atts;
380
381 // Let the user override the chatbot params
382 $atts = apply_filters( 'mwai_chatbot_params', $atts );
383
384 // Resolve the bot info
385 $resolvedBot = $this->resolveBotInfo( $atts, 'chatbot' );
386 if ( isset( $resolvedBot['error'] ) ) {
387 return $resolvedBot['error'];
388 }
389 $chatbot = $resolvedBot['chatbot'];
390 $botId = $resolvedBot['botId'];
391 $customId = $resolvedBot['customId'];
392
393 // Rename the keys of the atts into camelCase to match the internal params system.
394 $atts = array_map( function( $key, $value ) {
395 $key = str_replace( '_', ' ', $key );
396 $key = ucwords( $key );
397 $key = str_replace( ' ', '', $key );
398 $key = lcfirst( $key );
399 return [ $key => $value ];
400 }, array_keys( $atts ), $atts );
401 $atts = array_merge( ...$atts );
402
403 $frontParams = [];
404 foreach ( MWAI_CHATBOT_FRONT_PARAMS as $param ) {
405 if ( isset( $atts[$param] ) ) {
406 if ( $param === 'localMemory' ) {
407 $frontParams[$param] = $atts[$param] === 'true';
408 }
409 else {
410 $frontParams[$param] = $atts[$param];
411 }
412 }
413 else if ( isset( $chatbot[$param] ) ) {
414 $frontParams[$param] = $chatbot[$param];
415 }
416 }
417
418 // Server Params
419 // NOTE: We don't need the server params for the chatbot if there are no overrides, it means
420 // we are using the default or a specific chatbot.
421 $hasServerOverrides = count( array_intersect( array_keys( $atts ), MWAI_CHATBOT_SERVER_PARAMS ) ) > 0;
422 $serverParams = [];
423 if ( $hasServerOverrides ) {
424 foreach ( MWAI_CHATBOT_SERVER_PARAMS as $param ) {
425 if ( isset( $atts[$param] ) ) {
426 $serverParams[$param] = $atts[$param];
427 }
428 else {
429 $serverParams[$param] = $chatbot[$param] ?? null;
430 }
431 }
432 }
433
434 // Front Params
435 $frontSystem = $this->build_front_params( $botId, $customId );
436
437 // Clean Params
438 $frontParams = $this->clean_params( $frontParams );
439 $frontSystem = $this->clean_params( $frontSystem );
440 $serverParams = $this->clean_params( $serverParams );
441
442 // Server-side: Keep the System Params
443 if ( $hasServerOverrides ) {
444 if ( empty( $customId ) ) {
445 $customId = md5( json_encode( $serverParams ) );
446 $frontSystem['customId'] = $customId;
447 }
448 set_transient( 'mwai_custom_chatbot_' . $customId, $serverParams, 60 * 60 * 24 );
449 }
450
451 // Client-side: Prepare JSON for Front Params and System Params
452 $theme = isset( $frontParams['themeId'] ) ? $this->core->get_theme( $frontParams['themeId'] ) : null;
453 $jsonFrontParams = htmlspecialchars( json_encode( $frontParams ), ENT_QUOTES, 'UTF-8' );
454 $jsonFrontSystem = htmlspecialchars( json_encode( $frontSystem ), ENT_QUOTES, 'UTF-8' );
455 $jsonFrontTheme = htmlspecialchars( json_encode( $theme ), ENT_QUOTES, 'UTF-8' );
456 //$jsonAttributes = htmlspecialchars(json_encode($atts), ENT_QUOTES, 'UTF-8');
457
458 $this->enqueue_scripts();
459 return "<div class='mwai-chatbot-container' data-params='{$jsonFrontParams}' data-system='{$jsonFrontSystem}' data-theme='{$jsonFrontTheme}'></div>";
460 }
461
462 function shortcode_chat_discussions( $atts ) {
463 $atts = empty($atts) ? [] : $atts;
464
465 // Resolve the bot info
466 $resolvedBot = $this->resolveBotInfo( $atts );
467 if ( isset( $resolvedBot['error'] ) ) {
468 return $resolvedBot['error'];
469 }
470 $chatbot = $resolvedBot['chatbot'];
471 $botId = $resolvedBot['botId'];
472 $customId = $resolvedBot['customId'];
473
474 // Rename the keys of the atts into camelCase to match the internal params system.
475 $atts = array_map( function( $key, $value ) {
476 $key = str_replace( '_', ' ', $key );
477 $key = ucwords( $key );
478 $key = str_replace( ' ', '', $key );
479 $key = lcfirst( $key );
480 return [ $key => $value ];
481 }, array_keys( $atts ), $atts );
482 $atts = array_merge( ...$atts );
483
484 // Front Params
485 $frontParams = [];
486 foreach ( MWAI_DISCUSSIONS_FRONT_PARAMS as $param ) {
487 if ( isset( $atts[$param] ) ) {
488 $frontParams[$param] = $atts[$param];
489 }
490 else if ( isset( $chatbot[$param] ) ) {
491 $frontParams[$param] = $chatbot[$param];
492 }
493 }
494
495 // Server Params
496 $serverParams = [];
497 foreach ( MWAI_DISCUSSIONS_SERVER_PARAMS as $param ) {
498 if ( isset( $atts[$param] ) ) {
499 $serverParams[$param] = $atts[$param];
500 }
501 }
502
503 // Front System
504 $frontSystem = $this->build_front_params( $botId, $customId );
505
506 // Clean Params
507 $frontParams = $this->clean_params( $frontParams );
508 $frontSystem = $this->clean_params( $frontSystem );
509 $serverParams = $this->clean_params( $serverParams );
510
511 $theme = isset( $frontParams['themeId'] ) ? $this->core->get_theme( $frontParams['themeId'] ) : null;
512 $jsonFrontParams = htmlspecialchars( json_encode( $frontParams ), ENT_QUOTES, 'UTF-8' );
513 $jsonFrontSystem = htmlspecialchars( json_encode( $frontSystem ), ENT_QUOTES, 'UTF-8' );
514 $jsonFrontTheme = htmlspecialchars( json_encode( $theme ), ENT_QUOTES, 'UTF-8' );
515
516 return "<div class='mwai-discussions-container' data-params='{$jsonFrontParams}' data-system='{$jsonFrontSystem}' data-theme='{$jsonFrontTheme}'></div>";
517 }
518
519 function clean_params( &$params ) {
520 foreach ( $params as $param => $value ) {
521 if ( empty( $value ) || is_array( $value ) ) {
522 continue;
523 }
524 $lowerCaseValue = strtolower( $value );
525 if ( $lowerCaseValue === 'true' || $lowerCaseValue === 'false' || is_bool( $value ) ) {
526 $params[$param] = filter_var( $value, FILTER_VALIDATE_BOOLEAN );
527 }
528 else if ( is_numeric( $value ) ) {
529 $params[$param] = filter_var( $value, FILTER_VALIDATE_FLOAT );
530 }
531 }
532 return $params;
533 }
534
535 }
536