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