PluginProbe ʕ •ᴥ•ʔ
AI Engine – The Chatbot, AI Framework & MCP for WordPress / 1.7.6
AI Engine – The Chatbot, AI Framework & MCP for WordPress v1.7.6
3.6.2 3.6.1 3.6.0 3.5.9 3.5.8 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
577 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 #endregion
276
277 #region Usage & Costs
278
279 public function dynamic_max_tokens( $tokens, $text ) {
280 // Approximation (fast, no lib)
281 $asciiCount = 0;
282 $nonAsciiCount = 0;
283 for ( $i = 0; $i < mb_strlen( $text ); $i++ ) {
284 $char = mb_substr( $text, $i, 1 );
285 if ( ord( $char ) < 128 ) {
286 $asciiCount++;
287 }
288 else {
289 $nonAsciiCount++;
290 }
291 }
292 $asciiTokens = $asciiCount / 3.5;
293 $nonAsciiTokens = $nonAsciiCount * 2.5;
294 $tokens = $asciiTokens + $nonAsciiTokens;
295
296 // More exact (slower, and lib)
297 if ( PHP_VERSION_ID >= 70400 && function_exists( 'mb_convert_encoding' ) ) {
298 try {
299 $token_array = Encoder::encode( $text );
300 if ( !empty( $token_array ) ) {
301 $tokens = count( $token_array );
302 }
303 }
304 catch ( Exception $e ) {
305 error_log( $e->getMessage() );
306 }
307 }
308
309 $tokens = $tokens;
310 return (int)$tokens;
311 }
312
313 public function recordTokensUsage( $model, $prompt_tokens, $completion_tokens = 0 ) {
314 if ( !is_numeric( $prompt_tokens ) ) {
315 throw new Exception( 'Record usage: prompt_tokens is not a number.' );
316 }
317 if ( !is_numeric( $completion_tokens ) ) {
318 $completion_tokens = 0;
319 }
320 if ( !$model ) {
321 throw new Exception( 'Record usage: model is missing.' );
322 }
323 $usage = $this->get_option( 'openai_usage' );
324 $month = date( 'Y-m' );
325 if ( !isset( $usage[$month] ) ) {
326 $usage[$month] = array();
327 }
328 if ( !isset( $usage[$month][$model] ) ) {
329 $usage[$month][$model] = array(
330 'prompt_tokens' => 0,
331 'completion_tokens' => 0,
332 'total_tokens' => 0
333 );
334 }
335 $usage[$month][$model]['prompt_tokens'] += $prompt_tokens;
336 $usage[$month][$model]['completion_tokens'] += $completion_tokens;
337 $usage[$month][$model]['total_tokens'] += $prompt_tokens + $completion_tokens;
338 $this->update_option( 'openai_usage', $usage );
339 return [
340 'prompt_tokens' => $prompt_tokens,
341 'completion_tokens' => $completion_tokens,
342 'total_tokens' => $prompt_tokens + $completion_tokens
343 ];
344 }
345
346 public function recordAudioUsage( $model, $seconds ) {
347 if ( !is_numeric( $seconds ) ) {
348 throw new Exception( 'Record usage: seconds is not a number.' );
349 }
350 if ( !$model ) {
351 throw new Exception( 'Record usage: model is missing.' );
352 }
353 $usage = $this->get_option( 'openai_usage' );
354 $month = date( 'Y-m' );
355 if ( !isset( $usage[$month] ) ) {
356 $usage[$month] = array();
357 }
358 if ( !isset( $usage[$month][$model] ) ) {
359 $usage[$month][$model] = array(
360 'seconds' => 0
361 );
362 }
363 $usage[$month][$model]['seconds'] += $seconds;
364 $this->update_option( 'openai_usage', $usage );
365 return [
366 'seconds' => $seconds
367 ];
368 }
369
370 public function recordImagesUsage( $model, $resolution, $images ) {
371 if ( !$model || !$resolution || !$images ) {
372 throw new Exception( 'Missing parameters for record_image_usage.' );
373 }
374 $usage = $this->get_option( 'openai_usage' );
375 $month = date( 'Y-m' );
376 if ( !isset( $usage[$month] ) ) {
377 $usage[$month] = array();
378 }
379 if ( !isset( $usage[$month][$model] ) ) {
380 $usage[$month][$model] = array(
381 'resolution' => array(),
382 'images' => 0
383 );
384 }
385 if ( !isset( $usage[$month][$model]['resolution'][$resolution] ) ) {
386 $usage[$month][$model]['resolution'][$resolution] = 0;
387 }
388 $usage[$month][$model]['resolution'][$resolution] += $images;
389 $usage[$month][$model]['images'] += $images;
390 $this->update_option( 'openai_usage', $usage );
391 return [
392 'resolution' => $resolution,
393 'images' => $images
394 ];
395 }
396
397 #endregion
398
399 #region Streaming
400 public function stream_push( $data ) {
401 $out = "data: " . json_encode( $data );
402 echo $out;
403 echo "\n\n";
404 if ( ob_get_level() > 0 ) {
405 ob_end_flush();
406 }
407 flush();
408 }
409 #endregion
410
411 #region Options
412 function getThemes()
413 {
414 $themes = get_option( $this->themes_option_name, [] );
415 $themes = empty( $themes ) ? [] : $themes;
416
417 $internalThemes = [
418 'chatgpt' => [
419 'type' => 'internal', 'name' => 'ChatGPT', 'themeId' => 'chatgpt',
420 'settings' => [], 'style' => ""
421 ],
422 'messages' => [
423 'type' => 'internal', 'name' => 'Messages', 'themeId' => 'messages',
424 'settings' => [], 'style' => ""
425 ],
426 ];
427 $customThemes = [];
428 foreach ( $themes as $theme ) {
429 if ( isset( $internalThemes[$theme['themeId']] ) ) {
430 $internalThemes[$theme['themeId']] = $theme;
431 continue;
432 }
433 $customThemes[] = $theme;
434 }
435 return array_merge(array_values($internalThemes), $customThemes);
436 }
437
438 function updateThemes( $themes ) {
439 update_option( $this->themes_option_name, $themes );
440 return $themes;
441 }
442
443 function getChatbots() {
444 $chatbots = get_option( $this->chatbots_option_name, [] );
445 $hasChanges = false;
446 if ( empty( $chatbots ) ) {
447 $chatbots = [ array_merge( MWAI_CHATBOT_DEFAULT_PARAMS, ['name' => 'Default', 'botId' => 'default' ] ) ];
448 }
449 foreach ( $chatbots as &$chatbot ) {
450 foreach ( MWAI_CHATBOT_DEFAULT_PARAMS as $key => $value ) {
451 // Use default value if not set.
452 if ( !isset( $chatbot[$key] ) ) {
453 $chatbot[$key] = $value;
454 }
455 }
456 // After September 2023, let's remove this if statement.
457 if ( isset( $chatbot['chatId'] ) ) {
458 $chatbot['botId'] = $chatbot['chatId'];
459 unset( $chatbot['chatId'] );
460 $hasChanges = true;
461 }
462 // After September 2023, let's remove this if statement.
463 if ( empty( $chatbot['botId'] ) && $chatbot['name'] === 'default' ) {
464 $chatbot['botId'] = sanitize_title( $chatbot['name'] );
465 $hasChanges = true;
466 }
467 }
468 if ( $hasChanges ) {
469 update_option( $this->chatbots_option_name, $chatbots );
470 }
471 return $chatbots;
472 }
473
474 function getChatbot( $botId ) {
475 $chatbots = $this->getChatbots();
476 foreach ( $chatbots as $chatbot ) {
477 if ( $chatbot['botId'] === (string)$botId ) {
478 // Somehow, the default was set to "openai" when creating a new chatbot, but that overrided
479 // the default value in the Settings. It should be always empty here (except if we add this
480 // into the Settings of the chatbot).
481 $chatbot['service'] = null;
482 return $chatbot;
483 }
484 }
485 return null;
486 }
487
488 function getTheme( $themeId ) {
489 $themes = $this->getThemes();
490 foreach ( $themes as $theme ) {
491 if ( $theme['themeId'] === $themeId ) {
492 return $theme;
493 }
494 }
495 return null;
496 }
497
498 function updateChatbots( $chatbots ) {
499 $htmlFields = [ 'textCompliance', 'aiName', 'userName', 'startSentence' ];
500 $whiteSpacedFields = [ 'context' ];
501 foreach ( $chatbots as &$chatbot ) {
502 foreach ( $chatbot as $key => &$value ) {
503 if ( in_array( $key, $htmlFields ) ) {
504 $value = wp_kses_post( $value );
505 }
506 else if ( in_array( $key, $whiteSpacedFields ) ) {
507 $value = sanitize_textarea_field( $value );
508 }
509 else {
510 $value = sanitize_text_field( $value );
511 }
512 }
513 }
514
515 update_option( $this->chatbots_option_name, $chatbots );
516 return $chatbots;
517 }
518
519 function get_all_options() {
520 $options = get_option( $this->option_name, [] );
521 foreach ( MWAI_OPTIONS as $key => $value ) {
522 if ( !isset( $options[$key] ) ) {
523 $options[$key] = $value;
524 }
525 if ( $key === 'languages' ) {
526 // NOTE: If we decide to make a set of options for languages, we can keep it in the settings
527 $options[$key] = MWAI_LANGUAGES;
528 $options[$key] = apply_filters( 'mwai_languages', $options[$key] );
529 }
530 }
531 $options['shortcode_chat_default_params'] = MWAI_CHATBOT_PARAMS;
532 $options['chatbot_defaults'] = MWAI_CHATBOT_DEFAULT_PARAMS;
533 $options['default_limits'] = MWAI_LIMITS;
534 $options['openai_models'] = MWAI_OPENAI_MODELS;
535 return $options;
536 }
537
538 // Validate and keep the options clean and logical.
539 function sanitize_options() {
540 $options = $this->get_all_options();
541 $needs_update = false;
542
543 // We can sanitize our future options here, let's always remember it.
544 // Now, it is empty...
545
546 if ( $needs_update ) {
547 update_option( $this->option_name, $options, false );
548 }
549 return $options;
550 }
551
552 function update_options( $options ) {
553 if ( !update_option( $this->option_name, $options, false ) ) {
554 return false;
555 }
556 $options = $this->sanitize_options();
557 return $options;
558 }
559
560 function update_option( $option, $value ) {
561 $options = $this->get_all_options();
562 $options[$option] = $value;
563 return $this->update_options( $options );
564 }
565
566 function get_option( $option, $default = null ) {
567 $options = $this->get_all_options();
568 return $options[$option] ?? $default;
569 }
570
571 function reset_options() {
572 return $this->update_options( MWAI_OPTIONS );
573 }
574 #endregion
575 }
576
577 ?>