PluginProbe ʕ •ᴥ•ʔ
AI Engine – The Chatbot, AI Framework & MCP for WordPress / 1.6.99
AI Engine – The Chatbot, AI Framework & MCP for WordPress v1.6.99
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 / core.php
ai-engine / classes Last commit date
engines 3 years ago modules 3 years ago queries 3 years ago admin.php 3 years ago api.php 3 years ago core.php 3 years ago init.php 3 years ago reply.php 3 years ago rest.php 3 years ago
core.php
566 lines
1 <?php
2
3 require_once( MWAI_PATH . '/vendor/autoload.php' );
4 require_once( MWAI_PATH . '/constants/init.php' );
5
6 use Rahul900day\Gpt3Encoder\Encoder;
7
8 define( 'MWAI_IMG_WAND', MWAI_URL . '/images/wand.png' );
9 define( 'MWAI_IMG_WAND_HTML', "<img style='height: 22px; margin-bottom: -5px; margin-right: 8px;'
10 src='" . MWAI_IMG_WAND . "' alt='AI Wand' />" );
11 define( 'MWAI_IMG_WAND_HTML_XS', "<img style='height: 16px; margin-bottom: -2px;'
12 src='" . MWAI_IMG_WAND . "' alt='AI Wand' />" );
13 class Meow_MWAI_Core
14 {
15 public $admin = null;
16 public $is_rest = false;
17 public $is_cli = false;
18 public $site_url = null;
19 public $ai = null;
20 private $option_name = 'mwai_options';
21 private $themes_option_name = 'mwai_themes';
22 private $chatbots_option_name = 'mwai_chatbots';
23 private $nonce = null;
24 public $defaultChatbotParams = MWAI_CHATBOT_PARAMS;
25
26 public function __construct() {
27 $this->site_url = get_site_url();
28 $this->is_rest = MeowCommon_Helpers::is_rest();
29 $this->is_cli = defined( 'WP_CLI' );
30 $this->ai = new Meow_MWAI_Engines_Core( $this );
31 add_action( 'plugins_loaded', array( $this, 'init' ) );
32 }
33
34 function init() {
35 global $mwai;
36 $mwai = new Meow_MWAI_API();
37 new Meow_MWAI_Modules_Security( $this );
38 if ( $this->is_rest ) {
39 new Meow_MWAI_Rest( $this );
40 }
41 if ( is_admin() ) {
42 new Meow_MWAI_Admin( $this );
43 new Meow_MWAI_Modules_Assistants( $this );
44 }
45 if ( $this->get_option( 'shortcode_chat' ) ) {
46 new Meow_MWAI_Modules_Chatbot();
47 new Meow_MWAI_Modules_Chatbot_Legacy();
48 new Meow_MWAI_Modules_Discussions();
49 }
50
51 // Advanced core
52 if ( class_exists( 'MeowPro_MWAI_Core' ) ) {
53 new MeowPro_MWAI_Core( $this );
54 }
55
56 // Dynamic max tokens
57 if ( $this->get_option( 'dynamic_max_tokens' ) ) {
58 add_filter( 'mwai_estimate_tokens', array( $this, 'dynamic_max_tokens' ), 10, 2 );
59 }
60 }
61
62 #region Roles & Capabilities
63
64 function can_access_settings() {
65 return apply_filters( 'mwai_allow_setup', current_user_can( 'manage_options' ) );
66 }
67
68 function can_access_features() {
69 $editor_or_admin = current_user_can( 'editor' ) || current_user_can( 'administrator' );
70 return apply_filters( 'mwai_allow_usage', $editor_or_admin );
71 }
72
73 #endregion
74
75 #region Text-Related Helpers
76
77 // Clean the text perfectly, resolve shortcodes, etc, etc.
78 function cleanText( $rawText = "" ) {
79 $text = html_entity_decode( $rawText );
80 $text = wp_strip_all_tags( $text );
81 $text = preg_replace( '/[\r\n]+/', "\n", $text );
82 return $text . " ";
83
84 // Before simplification:
85 // $text = strip_tags( $rawText );
86 // $text = strip_shortcodes( $text );
87 // $text = html_entity_decode( $text );
88 // $text = preg_replace( '/[\r\n]+/', "\n", $text );
89 // $sentences = preg_split( '/(?<=[.?!])(?=[a-zA-Z ])/', $text );
90 // foreach ( $sentences as $key => $sentence ) {
91 // $sentences[$key] = trim( $sentence );
92 // }
93 // $text = implode( " ", $sentences );
94 // $text = preg_replace( '/^[\pZ\pC]+|[\pZ\pC]+$/u', '', $text );
95 // return $text . " ";
96 }
97
98 // Make sure there are no duplicate sentences, and keep the length under a maximum length.
99 function cleanSentences( $text, $maxTokens = null ) {
100 //$sentences = preg_split( '/(?<=[.?!])(?=[a-zA-Z ])/', $text );
101 $maxTokens = $maxTokens ? $maxTokens : $this->get_option( 'context_max_tokens', 1024 );
102 $sentences = preg_split('/(?<=[.?!。.!?])+/u', $text);
103 $hashes = array();
104 $uniqueSentences = array();
105 $length = 0;
106 foreach ( $sentences as $sentence ) {
107 $sentence = preg_replace( '/^[\pZ\pC]+|[\pZ\pC]+$/u', '', $sentence );
108 $hash = md5( $sentence );
109 if ( !in_array( $hash, $hashes ) ) {
110 $tokensCount = apply_filters( 'mwai_estimate_tokens', 0, $sentence );
111 if ( $length + $tokensCount > $maxTokens ) {
112 continue;
113 }
114 $hashes[] = $hash;
115 $uniqueSentences[] = $sentence;
116 $length += $tokensCount;
117 }
118 }
119 $freshText = implode( " ", $uniqueSentences );
120 $freshText = preg_replace( '/^[\pZ\pC]+|[\pZ\pC]+$/u', '', $freshText );
121 return $freshText;
122 }
123
124 function getCleanPostContent( $postId ) {
125 $post = get_post( $postId );
126 if ( !$post ) {
127 return false;
128 }
129 $text = $post->post_content;
130 $pattern = '/\[mwai_.*?\]/';
131 $text = preg_replace( $pattern, '', $text );
132 if ( $this->get_option( 'resolve_shortcodes' ) ) {
133 $text = apply_filters( 'the_content', $text );
134 }
135 $text = $this->cleanText( $text );
136 $text = $this->cleanSentences( $text );
137 return $text;
138 }
139
140 function markdown_to_html( $content ) {
141 $Parsedown = new Parsedown();
142 $content = $Parsedown->text( $content );
143 return $content;
144 }
145
146 function get_post_language( $postId ) {
147 $locale = get_locale();
148 $code = strtolower( substr( $locale, 0, 2 ) );
149 $humanLanguage = strtr( $code, MWAI_ALL_LANGUAGES );
150 $lang = apply_filters( 'wpml_post_language_details', null, $postId );
151 if ( !empty( $lang ) ) {
152 $locale = $lang['locale'];
153 $humanLanguage = $lang['display_name'];
154 }
155 return strtolower( "$locale ($humanLanguage)" );
156 }
157 #endregion
158
159 #region Users/Sessions Helpers
160
161 function get_nonce() {
162 if ( !is_user_logged_in() ) {
163 return null;
164 }
165 if ( isset( $this->nonce ) ) {
166 return $this->nonce;
167 }
168 $this->nonce = wp_create_nonce( 'wp_rest' );
169 return $this->nonce;
170 }
171
172 function get_session_id() {
173 if ( isset( $_COOKIE['mwai_session_id'] ) ) {
174 return $_COOKIE['mwai_session_id'];
175 }
176 return "N/A";
177 }
178
179 // Get the UserID from the data, or from the current user
180 function get_user_id( $data = null ) {
181 if ( isset( $data ) && isset( $data['userId'] ) ) {
182 return (int)$data['userId'];
183 }
184 if ( is_user_logged_in() ) {
185 $current_user = wp_get_current_user();
186 if ( $current_user->ID > 0 ) {
187 return $current_user->ID;
188 }
189 }
190 return null;
191 }
192
193 function getUserData() {
194 $user = wp_get_current_user();
195 if ( empty( $user ) || empty( $user->ID ) ) {
196 return null;
197 }
198 $placeholders = array(
199 'FIRST_NAME' => get_user_meta( $user->ID, 'first_name', true ),
200 'LAST_NAME' => get_user_meta( $user->ID, 'last_name', true ),
201 'USER_LOGIN' => isset( $user ) && isset($user->data) && isset( $user->data->user_login ) ?
202 $user->data->user_login : null,
203 'DISPLAY_NAME' => isset( $user ) && isset( $user->data ) && isset( $user->data->display_name ) ?
204 $user->data->display_name : null,
205 'AVATAR_URL' => get_avatar_url( get_current_user_id() ),
206 );
207 return $placeholders;
208 }
209
210 function get_ip_address( $data = null ) {
211 if ( isset( $data ) && isset( $data['ip'] ) ) {
212 $data['ip'] = (string)$data['ip'];
213 }
214 else {
215 if ( isset( $_SERVER['REMOTE_ADDR'] ) ) {
216 $data['ip'] = sanitize_text_field( $_SERVER['REMOTE_ADDR'] );
217 }
218 else if ( isset( $_SERVER['HTTP_CLIENT_IP'] ) ) {
219 $data['ip'] = sanitize_text_field( $_SERVER['HTTP_CLIENT_IP'] );
220 }
221 else if ( isset( $_SERVER['HTTP_X_FORWARDED_ FOR'] ) ) {
222 $data['ip'] = sanitize_text_field( $_SERVER['HTTP_X_FORWARDED_FOR'] );
223 }
224 }
225 $ip = apply_filters( 'mwai_get_ip_address', $data['ip'] );
226 return $ip;
227 }
228
229 #endregion
230
231 #region Other Helpers
232
233 function isUrl( $url ) {
234 return strpos( $url, 'http' ) === 0 ? true : false;
235 }
236
237 function getPostTypes() {
238 $excluded = array( 'attachment', 'revision', 'nav_menu_item' );
239 $post_types = array();
240 $types = get_post_types( array( 'public' => true ), 'objects' );
241 foreach ( $types as $type ) {
242 if ( in_array( $type->name, $excluded ) ) {
243 continue;
244 }
245 $post_types[] = array(
246 'name' => $type->labels->name,
247 'type' => $type->name,
248 );
249 }
250 return $post_types;
251 }
252
253 function getCleanPost( $post ) {
254 if ( is_object( $post ) ) {
255 $post = (array)$post;
256 }
257 $language = $this->get_post_language( $post['ID'] );
258 $content = apply_filters( 'mwai_pre_post_content', $post['post_content'], $post['ID'] );
259 $content = $this->cleanText( $content );
260 $content = apply_filters( 'mwai_post_content', $content, $post['ID'] );
261 $title = $post['post_title'];
262 $excerpt = $post['post_excerpt'];
263 $url = get_permalink( $post['ID'] );
264 $checksum = wp_hash( $content . $title . $url );
265 return [
266 'postId' => $post['ID'],
267 'title' => $title,
268 'content' => $content,
269 'excerpt' => $excerpt,
270 'url' => $url,
271 'language' => $language,
272 'checksum' => $checksum,
273 ];
274 }
275
276 #endregion
277
278 #region Usage & Costs
279
280 public function dynamic_max_tokens( $tokens, $text ) {
281 // Approximation (fast, no lib)
282 $asciiCount = 0;
283 $nonAsciiCount = 0;
284 for ( $i = 0; $i < mb_strlen( $text ); $i++ ) {
285 $char = mb_substr( $text, $i, 1 );
286 if ( ord( $char ) < 128 ) {
287 $asciiCount++;
288 }
289 else {
290 $nonAsciiCount++;
291 }
292 }
293 $asciiTokens = $asciiCount / 3.5;
294 $nonAsciiTokens = $nonAsciiCount * 2.5;
295 $tokens = $asciiTokens + $nonAsciiTokens;
296
297 // More exact (slower, and lib)
298 if ( PHP_VERSION_ID >= 70400 && function_exists( 'mb_convert_encoding' ) ) {
299 try {
300 $token_array = Encoder::encode( $text );
301 if ( !empty( $token_array ) ) {
302 $tokens = count( $token_array );
303 }
304 }
305 catch ( Exception $e ) {
306 error_log( $e->getMessage() );
307 }
308 }
309
310 $tokens = $tokens;
311 return (int)$tokens;
312 }
313
314 public function recordTokensUsage( $model, $prompt_tokens, $completion_tokens = 0 ) {
315 if ( !is_numeric( $prompt_tokens ) ) {
316 throw new Exception( 'Record usage: prompt_tokens is not a number.' );
317 }
318 if ( !is_numeric( $completion_tokens ) ) {
319 $completion_tokens = 0;
320 }
321 if ( !$model ) {
322 throw new Exception( 'Record usage: model is missing.' );
323 }
324 $usage = $this->get_option( 'openai_usage' );
325 $month = date( 'Y-m' );
326 if ( !isset( $usage[$month] ) ) {
327 $usage[$month] = array();
328 }
329 if ( !isset( $usage[$month][$model] ) ) {
330 $usage[$month][$model] = array(
331 'prompt_tokens' => 0,
332 'completion_tokens' => 0,
333 'total_tokens' => 0
334 );
335 }
336 $usage[$month][$model]['prompt_tokens'] += $prompt_tokens;
337 $usage[$month][$model]['completion_tokens'] += $completion_tokens;
338 $usage[$month][$model]['total_tokens'] += $prompt_tokens + $completion_tokens;
339 $this->update_option( 'openai_usage', $usage );
340 return [
341 'prompt_tokens' => $prompt_tokens,
342 'completion_tokens' => $completion_tokens,
343 'total_tokens' => $prompt_tokens + $completion_tokens
344 ];
345 }
346
347 public function recordAudioUsage( $model, $seconds ) {
348 if ( !is_numeric( $seconds ) ) {
349 throw new Exception( 'Record usage: seconds is not a number.' );
350 }
351 if ( !$model ) {
352 throw new Exception( 'Record usage: model is missing.' );
353 }
354 $usage = $this->get_option( 'openai_usage' );
355 $month = date( 'Y-m' );
356 if ( !isset( $usage[$month] ) ) {
357 $usage[$month] = array();
358 }
359 if ( !isset( $usage[$month][$model] ) ) {
360 $usage[$month][$model] = array(
361 'seconds' => 0
362 );
363 }
364 $usage[$month][$model]['seconds'] += $seconds;
365 $this->update_option( 'openai_usage', $usage );
366 return [
367 'seconds' => $seconds
368 ];
369 }
370
371 public function recordImagesUsage( $model, $resolution, $images ) {
372 if ( !$model || !$resolution || !$images ) {
373 throw new Exception( 'Missing parameters for record_image_usage.' );
374 }
375 $usage = $this->get_option( 'openai_usage' );
376 $month = date( 'Y-m' );
377 if ( !isset( $usage[$month] ) ) {
378 $usage[$month] = array();
379 }
380 if ( !isset( $usage[$month][$model] ) ) {
381 $usage[$month][$model] = array(
382 'resolution' => array(),
383 'images' => 0
384 );
385 }
386 if ( !isset( $usage[$month][$model]['resolution'][$resolution] ) ) {
387 $usage[$month][$model]['resolution'][$resolution] = 0;
388 }
389 $usage[$month][$model]['resolution'][$resolution] += $images;
390 $usage[$month][$model]['images'] += $images;
391 $this->update_option( 'openai_usage', $usage );
392 return [
393 'resolution' => $resolution,
394 'images' => $images
395 ];
396 }
397
398 #endregion
399
400 #region Options
401 function getThemes()
402 {
403 $themes = get_option( $this->themes_option_name, [] );
404 $themes = empty( $themes ) ? [] : $themes;
405
406 $internalThemes = [
407 'chatgpt' => [
408 'type' => 'internal', 'name' => 'ChatGPT', 'themeId' => 'chatgpt',
409 'settings' => [], 'style' => ""
410 ],
411 'messages' => [
412 'type' => 'internal', 'name' => 'Messages', 'themeId' => 'messages',
413 'settings' => [], 'style' => ""
414 ],
415 ];
416 $customThemes = [];
417 foreach ( $themes as $theme ) {
418 if ( isset( $internalThemes[$theme['themeId']] ) ) {
419 $internalThemes[$theme['themeId']] = $theme;
420 continue;
421 }
422 $customThemes[] = $theme;
423 }
424 return array_merge(array_values($internalThemes), $customThemes);
425 }
426
427 function updateThemes( $themes ) {
428 update_option( $this->themes_option_name, $themes );
429 return $themes;
430 }
431
432 function getChatbots() {
433 $chatbots = get_option( $this->chatbots_option_name, [] );
434 $hasChanges = false;
435 if ( empty( $chatbots ) ) {
436 $chatbots = [ array_merge( MWAI_CHATBOT_DEFAULT_PARAMS, ['name' => 'Default', 'botId' => 'default' ] ) ];
437 }
438 foreach ( $chatbots as &$chatbot ) {
439 foreach ( MWAI_CHATBOT_DEFAULT_PARAMS as $key => $value ) {
440 // Use default value if not set.
441 if ( !isset( $chatbot[$key] ) ) {
442 $chatbot[$key] = $value;
443 }
444 }
445 // After September 2023, let's remove this if statement.
446 if ( isset( $chatbot['chatId'] ) ) {
447 $chatbot['botId'] = $chatbot['chatId'];
448 unset( $chatbot['chatId'] );
449 $hasChanges = true;
450 }
451 // After September 2023, let's remove this if statement.
452 if ( empty( $chatbot['botId'] ) && $chatbot['name'] === 'default' ) {
453 $chatbot['botId'] = sanitize_title( $chatbot['name'] );
454 $hasChanges = true;
455 }
456 }
457 if ( $hasChanges ) {
458 update_option( $this->chatbots_option_name, $chatbots );
459 }
460 return $chatbots;
461 }
462
463 function getChatbot( $botId ) {
464 $chatbots = $this->getChatbots();
465 foreach ( $chatbots as $chatbot ) {
466 if ( $chatbot['botId'] === (string)$botId ) {
467 // Somehow, the default was set to "openai" when creating a new chatbot, but that overrided
468 // the default value in the Settings. It should be always empty here (except if we add this
469 // into the Settings of the chatbot).
470 $chatbot['service'] = null;
471 return $chatbot;
472 }
473 }
474 return null;
475 }
476
477 function getTheme( $themeId ) {
478 $themes = $this->getThemes();
479 foreach ( $themes as $theme ) {
480 if ( $theme['themeId'] === $themeId ) {
481 return $theme;
482 }
483 }
484 return null;
485 }
486
487 function updateChatbots( $chatbots ) {
488 $htmlFields = [ 'textCompliance', 'aiName', 'userName', 'startSentence' ];
489 $whiteSpacedFields = [ 'context' ];
490 foreach ( $chatbots as &$chatbot ) {
491 foreach ( $chatbot as $key => &$value ) {
492 if ( in_array( $key, $htmlFields ) ) {
493 $value = wp_kses_post( $value );
494 }
495 else if ( in_array( $key, $whiteSpacedFields ) ) {
496 $value = sanitize_textarea_field( $value );
497 }
498 else {
499 $value = sanitize_text_field( $value );
500 }
501 }
502 }
503
504 update_option( $this->chatbots_option_name, $chatbots );
505 return $chatbots;
506 }
507
508 function get_all_options() {
509 $options = get_option( $this->option_name, [] );
510 foreach ( MWAI_OPTIONS as $key => $value ) {
511 if ( !isset( $options[$key] ) ) {
512 $options[$key] = $value;
513 }
514 if ( $key === 'languages' ) {
515 // NOTE: If we decide to make a set of options for languages, we can keep it in the settings
516 $options[$key] = MWAI_LANGUAGES;
517 $options[$key] = apply_filters( 'mwai_languages', $options[$key] );
518 }
519 }
520 $options['shortcode_chat_default_params'] = MWAI_CHATBOT_PARAMS;
521 $options['chatbot_defaults'] = MWAI_CHATBOT_DEFAULT_PARAMS;
522 $options['default_limits'] = MWAI_LIMITS;
523 $options['openai_models'] = MWAI_OPENAI_MODELS;
524 return $options;
525 }
526
527 // Validate and keep the options clean and logical.
528 function sanitize_options() {
529 $options = $this->get_all_options();
530 $needs_update = false;
531
532 // We can sanitize our future options here, let's always remember it.
533 // Now, it is empty...
534
535 if ( $needs_update ) {
536 update_option( $this->option_name, $options, false );
537 }
538 return $options;
539 }
540
541 function update_options( $options ) {
542 if ( !update_option( $this->option_name, $options, false ) ) {
543 return false;
544 }
545 $options = $this->sanitize_options();
546 return $options;
547 }
548
549 function update_option( $option, $value ) {
550 $options = $this->get_all_options();
551 $options[$option] = $value;
552 return $this->update_options( $options );
553 }
554
555 function get_option( $option, $default = null ) {
556 $options = $this->get_all_options();
557 return $options[$option] ?? $default;
558 }
559
560 function reset_options() {
561 return $this->update_options( MWAI_OPTIONS );
562 }
563 #endregion
564 }
565
566 ?>