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