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