PluginProbe ʕ •ᴥ•ʔ
AI Engine – The Chatbot, AI Framework & MCP for WordPress / 1.9.83
AI Engine – The Chatbot, AI Framework & MCP for WordPress v1.9.83
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 / modules / chatbot_legacy.php
ai-engine / classes / modules Last commit date
assistants.php 3 years ago chatbot.php 2 years ago chatbot_legacy.php 2 years ago discussions.php 2 years ago security.php 3 years ago
chatbot_legacy.php
849 lines
1 <?php
2
3 class Meow_MWAI_Modules_Chatbot_Legacy {
4 private $core = null;
5 private $namespace = 'ai-chatbot/v1';
6 private $usingChat = false;
7
8 public function __construct() {
9 global $mwai_core;
10 $this->core = $mwai_core;
11 if ( is_admin() ) { return; }
12 add_shortcode( 'mwai_chat', array( $this, 'chat' ) );
13 add_shortcode( 'mwai_chatbot', array( $this, 'chat' ) );
14 add_shortcode( 'mwai_imagesbot', array( $this, 'imageschat' ) );
15 add_action( 'rest_api_init', array( $this, 'rest_api_init' ) );
16 add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
17
18 if ( $this->core->get_option( 'shortcode_chat_inject' ) ) {
19 add_action( 'wp_footer', array( $this, 'inject_chat' ) );
20 }
21
22 if ( $this->core->get_option( 'shortcode_chat_styles' ) ) {
23 add_filter( 'mwai_chatbot_style', [ $this, 'apply_chat_styles' ], 10, 2 );
24 }
25 }
26
27 public function enqueue_scripts() {
28 // if ( $this->core->get_option( 'shortcode_chat_syntax_highlighting' ) ) {
29 // wp_enqueue_script( 'mwai_chatbot', MWAI_URL . 'vendor/highlightjs/highlight.min.js', [], '11.7', false );
30 // wp_enqueue_style( 'mwai_chatbot', MWAI_URL . '/vendor/highlightjs/stackoverflow-dark.min.css', [], '11.7' );
31 // }
32 if ( $this->core->get_option( 'shortcode_chat_typewriter' ) ) {
33 wp_enqueue_script( 'mwai_chatbot_typewriter', MWAI_URL . 'vendor/typewriterjs/typewriter.min.js', [], '2.0', true );
34 }
35 }
36
37 public function rest_api_init() {
38 register_rest_route( $this->namespace, '/chat', array(
39 'methods' => 'POST',
40 'callback' => array( $this, 'rest_chat' ),
41 'permission_callback' => '__return_true'
42 ) );
43 register_rest_route( $this->namespace, '/imagesbot', array(
44 'methods' => 'POST',
45 'callback' => array( $this, 'rest_imagesbot' ),
46 'permission_callback' => '__return_true'
47 ) );
48 }
49
50 public function chatgpt_style( $id ) {
51 $css = file_get_contents( MWAI_PATH . '/themes/ChatGPT.module.css' );
52 $css = str_replace( '#mwai-chat-id', "#mwai-chat-{$id}", $css );
53 return '<style>' . $css . '</style>';
54 }
55
56 public function basics_security_check( $params ) {
57 if ( empty( $params['newMessage'] ) ) {
58 return false;
59 }
60 $length = strlen( trim( $params['newMessage'] ) );
61 if ( $length < 1 || $length > ( 4096 - 512 ) ) {
62 return false;
63 }
64 if ( empty( $params['prompt'] ) ) {
65 return false;
66 }
67 return true;
68 }
69
70 public function rest_chat( $request ) {
71 try {
72 $params = $request->get_json_params();
73 $context = null;
74 if ( !$this->basics_security_check( $params )) {
75 return new WP_REST_Response( [
76 'success' => false,
77 'message' => apply_filters( 'mwai_ai_exception', 'Sorry, your query has been rejected.' )
78 ], 403 );
79 }
80
81 $query = new Meow_MWAI_Query_Text( $params['newMessage'], 1024 );
82 $query->injectParams( $params );
83
84 $takeoverAnswer = apply_filters( 'mwai_chatbot_takeover', null, $query, $params );
85 if ( !empty( $takeoverAnswer ) ) {
86 return new WP_REST_Response( [ 'success' => true, 'reply' => $takeoverAnswer,
87 'html' => $takeoverAnswer, 'usage' => null ], 200 );
88 }
89
90 // Moderation
91 if ( $this->core->get_option( 'shortcode_chat_moderation' ) ) {
92 global $mwai;
93 $isFlagged = $mwai->moderationCheck( $query->prompt );
94 if ( $isFlagged ) {
95 return new WP_REST_Response( [
96 'success' => false,
97 'message' => 'Sorry, your message has been rejected by moderation.' ], 403
98 );
99 }
100 }
101
102 // Awareness & Embeddings
103 // TODO: This is same in Chatbot Legacy and Forms, maybe we should move it to the core?
104 $embeddingsIndex = $params['embeddingsIndex'] ?? null;
105 if ( $query->mode === 'chat' ) {
106 $context = apply_filters( 'mwai_context_search', $context, $query, [ 'embeddingsIndex' => $embeddingsIndex ] );
107 if ( !empty( $context ) ) {
108 if ( isset( $context['content'] ) ) {
109 $content = $this->core->cleanSentences( $context['content'] );
110 $query->injectContext( $content );
111 }
112 else {
113 error_log("AI Engine: A context without content was returned.");
114 }
115 }
116 }
117
118 $reply = $this->core->ai->run( $query );
119 $rawText = $reply->result;
120 $extra = [];
121 if ( $context ) {
122 $extra = [ 'embeddings' => $context['embeddings'] ];
123 }
124 $html = apply_filters( 'mwai_chatbot_reply', $rawText, $query, $params, $extra );
125 if ( $this->core->get_option( 'shortcode_chat_formatting' ) ) {
126 $html = $this->core->markdown_to_html( $html );
127 }
128 return new WP_REST_Response( [ 'success' => true, 'reply' => $rawText,
129 'html' => $html, 'usage' => $reply->usage ], 200 );
130 }
131 catch ( Exception $e ) {
132 return new WP_REST_Response( [ 'success' => false, 'message' => $e->getMessage() ], 500 );
133 }
134 }
135
136 public function rest_imagesbot( $request ) {
137 try {
138 $params = $request->get_json_params();
139 $query = new Meow_MWAI_Query_Image( $params['prompt'] );
140 $query->injectParams( $params );
141 $reply = $this->core->ai->run( $query );
142 return new WP_REST_Response( [ 'success' => true, 'images' => $reply->results, 'usage' => $reply->usage ], 200 );
143 }
144 catch ( Exception $e ) {
145 return new WP_REST_Response( [ 'success' => false, 'message' => $e->getMessage() ], 500 );
146 }
147 }
148
149 public function apply_chat_styles( $css, $chatbotId ) {
150 $chatStyles = $this->core->get_option( 'shortcode_chat_styles' );
151 return preg_replace_callback( '/--mwai-(\w+):\s*([^;]+);/', function ( $matches ) use ( $chatStyles ) {
152 if ( isset( $chatStyles[$matches[1]] ) ) {
153 return '--mwai-' . $matches[1] . ': ' . $chatStyles[$matches[1]] . ';';
154 }
155 return $matches[0];
156 }, $css );
157 }
158
159 public function inject_chat() {
160 $params = $this->core->get_option( 'shortcode_chat_params' );
161 echo $this->chat( $params );
162 }
163
164 public function imageschat( $atts ) {
165 $atts['mode'] = 'images';
166 return $this->chat( $atts );
167 }
168
169 public function getCurrentUser() {
170 if ( is_user_logged_in() ) {
171 return wp_get_current_user();
172 }
173 return null;
174 }
175
176 public function handlePlaceholders( $data, $guestName = 'Guest: ' ) {
177 if ( strpos( $data, '{' ) === false ) {
178 return $data;
179 }
180 $placeholders_meta = [ '{FIRST_NAME}', '{LAST_NAME}' ];
181 $placeholders_data = [ '{USER_LOGIN}', '{DISPLAY_NAME}' ];
182 $user = $this->getCurrentUser();
183 if ( $user ) {
184 foreach ( $placeholders_meta as $placeholder ) {
185 if ( strpos( $data, $placeholder ) === false ) { continue; }
186 $lcPlaceholder = substr( strtolower( $placeholder ), 1, -1 );
187 $value = get_user_meta( $user->ID, $lcPlaceholder, true );
188 $data = str_replace( $placeholder, $value, $data );
189 }
190 foreach ( $placeholders_data as $placeholder ) {
191 if ( strpos( $data, $placeholder ) === false ) { continue; }
192 $lcPlaceholder = substr( strtolower( $placeholder ), 1, -1 );
193 $value = $user->data->$lcPlaceholder;
194 $data = str_replace( $placeholder, $value, $data );
195 }
196 if ( !empty( $data ) ) {
197 return $data;
198 }
199 }
200 return $guestName;
201 }
202
203 public function formatUserName( $userName, $guestName = 'Guest: ' ) {
204 // Default avatar
205 if ( empty( $userName ) ) {
206 $user = $this->getCurrentUser();
207 if ( $user ) {
208 // Gravatar
209 $userName = '<div class="mwai-avatar"><img src="' . get_avatar_url( $user->user_email ) . '" /></div>';
210 }
211 else {
212 // Default avatar
213 $userName = '<div class="mwai-avatar mwai-svg"><img src="' . MWAI_URL . '/images/avatar-user.svg" /></div>';
214 }
215 }
216 // Custom avatar
217 else if ( $this->core->isUrl( $userName ) ) {
218 $userName = '<div class="mwai-avatar"><img src="' . $userName . '" /></div>';
219 }
220 // Placeholders
221 else {
222 $userName = $this->handlePlaceholders( $userName, $guestName );
223 $userName = '<div class="mwai-name-text">' . $userName . '</div>';
224 }
225 return $userName;
226 }
227
228 public function formatAiName( $aiName ) {
229 // Default avatar
230 if ( empty( $aiName ) ) {
231 $aiName = '<div class="mwai-avatar mwai-svg"><img src="' . MWAI_URL . '/images/avatar-ai.svg" /></div>';
232 }
233 // Custom avatar
234 else if ( $this->core->isUrl( $aiName ) ) {
235 $aiName = '<div class="mwai-avatar"><img src="' . $aiName . '" /></div>';
236 }
237 else {
238 $aiName = '<div class="mwai-name-text">' . $aiName . '</div>';
239 }
240 return $aiName;
241 }
242
243 public function formatRawName( $aiName ) {
244 return 'AI: ';
245 }
246
247 public function formatRawUserName( $userName, $guestName ) {
248 return 'User: ';
249 }
250
251 public function chat( $atts ) {
252 $this->usingChat = true;
253
254 // Use the core default parameters, or the user default parameters
255 $override = $this->core->get_option( 'shortcode_chat_params_override' );
256 $defaults_params = $override ? $this->core->get_option( 'shortcode_chat_params' ) :
257 $this->core->get_option( 'shortcode_chat_default_params' );
258
259 // Give a chance to modify the default parameters one last time
260 $defaults = apply_filters( 'mwai_chatbot_params_defaults', $defaults_params );
261
262 // Make sure all the mandatory params are set
263 foreach ( $this->core->defaultChatbotParams as $key => $value ) {
264 if ( !isset( $defaults[$key] ) ) {
265 $defaults[$key] = $value;
266 }
267 }
268
269 // Override with the shortcode, and before/after filters
270 //$atts = apply_filters( 'mwai_chatbot_params_before', $atts );
271 $atts = shortcode_atts( $defaults, $atts );
272 //$atts = apply_filters( 'mwai_chatbot_params', $atts );
273
274 // UI Parameters
275 $aiName = addslashes( trim( $atts['ai_name'] ) );
276 $userName = addslashes( trim( $atts['user_name'] ) );
277 $guestName = addslashes( trim( $atts['guest_name'] ) );
278 $sysName = addslashes( trim( $atts['sys_name'] ) );
279 $context = addslashes( $atts['context'] );
280 $context = preg_replace( '/\n/', "\\n", $context );
281 $textSend = addslashes( trim( $atts['text_send'] ) );
282 $textClear = addslashes( trim( $atts['text_clear'] ) );
283 $textInputMaxLength = intval( $atts['text_input_maxlength'] );
284 $textInputPlaceholder = addslashes( trim( $atts['text_input_placeholder'] ) );
285 $textCompliance = ( trim( $atts['text_compliance'] ) );
286 $startSentence = addslashes( trim( $atts['start_sentence'] ) );
287 $window = filter_var( $atts['window'], FILTER_VALIDATE_BOOLEAN );
288 $copyButton = filter_var( $atts['copy_button'], FILTER_VALIDATE_BOOLEAN );
289 $fullscreen = filter_var( $atts['fullscreen'], FILTER_VALIDATE_BOOLEAN );
290 $icon = isset( $atts['icon'] ) ? addslashes( trim( $atts['icon'] ) ) : '';
291 $iconText = trim( $atts['icon_text'] );
292 $iconAlt = addslashes( trim( $atts['icon_alt'] ) );
293 $iconPosition = addslashes( trim( $atts['icon_position'] ) );
294 $style = $atts['style'];
295
296 // Validade & Enhance UI Parameters
297 $aiName = $this->formatAiName( $aiName );
298 $userName = $this->formatUserName( $userName, $guestName );
299 $rawAiName = $this->formatRawName( $aiName );
300 $rawUserName = $this->formatRawUserName( $userName, $guestName );
301
302 // Chatbot System Parameters
303 $id = empty( $atts['id'] ) ? uniqid() : $atts['id'];
304 $typewriter = $this->core->get_option( 'shortcode_chat_typewriter' );
305 $memorizeChat = !empty( $atts['id'] );
306 $id = preg_replace( '/[^a-zA-Z0-9]/', '', $id );
307 $env = $atts['env'];
308 $mode = $atts['mode'];
309 $maxResults = $mode === 'chat' ? 1 : $atts['max_results'];
310 $maxSentences = !empty( $atts['max_messages'] ) ? intval( $atts['max_messages'] ) : 1;
311 $sessionId = $this->core->get_session_id();
312 $rest_nonce = wp_create_nonce( 'wp_rest' );
313 $casuallyFineTuned = boolval( $atts['casually_fine_tuned'] );
314 $embeddingsIndex = $atts['embeddings_index'];
315 $promptEnding = addslashes( trim( $atts['prompt_ending'] ) );
316 $completionEnding = addslashes( trim( $atts['completion_ending'] ) );
317 if ( $casuallyFineTuned ) {
318 $promptEnding = "\\n\\n###\\n\\n";
319 $completionEnding = "\\n\\n";
320 }
321 $debugMode = $this->core->get_option( 'debug_mode' );
322
323 // OpenAI Parameters
324 $model = $atts['model'];
325 $temperature = $atts['temperature'];
326 $maxTokens = $atts['max_tokens'];
327 $service = $atts['service'];
328 $apiKey = $atts['api_key'];
329
330 // Variables
331 $apiUrl = get_rest_url( null, $mode === 'images' ? 'ai-chatbot/v1/imagesbot' : 'ai-chatbot/v1/chat' );
332 $baseClasses = 'mwai-chat';
333 $baseClasses .= ( $window ? ' mwai-window' : '' );
334 $baseClasses .= ( !$window && $fullscreen ? ' mwai-fullscreen' : '' );
335 $baseClasses .= ( $style === 'chatgpt' ? ' mwai-chatgpt' : '' );
336 $baseClasses .= ( $window && !empty( $iconPosition ) ? ( ' mwai-' . $iconPosition ) : '' );
337
338 // Output CSS
339 ob_start();
340 $style_content = '';
341 if ( $style === 'chatgpt' ) {
342 $style_content = $this->chatgpt_style( $id, $style );
343 }
344 $style_content = apply_filters( 'mwai_chatbot_style', $style_content, $id );
345 echo wp_kses( $style_content, array( 'style' => array() ) );
346
347 // Output HTML & CSS
348 $chatStyles = $this->core->get_option( 'shortcode_chat_styles' );
349 $iconUrl = MWAI_URL . '/images/chat-green.svg';
350 if ( !empty( $icon ) ) {
351 $iconUrl = $icon;
352 }
353 else if ( !empty( $chatStyles ) && isset( $chatStyles['icon'] ) ) {
354 $url = $chatStyles['icon'];
355 $iconUrl = $this->core->isUrl( $url ) ? $url : ( MWAI_URL . 'images/' . $chatStyles['icon'] );
356 }
357 ?>
358 <div id="mwai-chat-<?php echo esc_attr( $id ); ?>" class="<?php echo esc_attr( $baseClasses ); ?>">
359 <?php if ( $window ) : ?>
360 <div class="mwai-open-button">
361 <?php if ( !empty( $iconText ) ) : ?>
362 <div class="mwai-icon-text"><?php echo esc_html( $iconText ); ?></div>
363 <?php endif; ?>
364 <img width="64" height="64" alt="<?php echo esc_attr( $iconAlt ); ?>" src="<?php echo esc_url( $iconUrl ); ?>" />
365 </div>
366 <div class="mwai-header">
367 <div class="mwai-buttons">
368 <?php if ( $fullscreen ) : ?>
369 <div class="mwai-resize-button"></div>
370 <?php endif; ?>
371 <div class="mwai-close-button"></div>
372 </div>
373 </div>
374 <?php endif; ?>
375 <div class="mwai-content">
376 <div class="mwai-conversation">
377 </div>
378 <div class="mwai-input">
379 <textarea rows="1" maxlength="<?php echo (int)$textInputMaxLength; ?>" placeholder="<?php echo esc_attr( $textInputPlaceholder ); ?>"></textarea>
380 <button><span><?php echo esc_html( $textSend ); ?></span></button>
381 </div>
382 <?php if ( !empty( $textCompliance ) ) : ?>
383 <div class="mwai-compliance">
384 ⚠️ <?php echo wp_kses_post( $textCompliance ); ?>
385 </div>
386 <?php endif; ?>
387 </div>
388 </div>
389
390 <script>
391 (function () {
392 let isMobile = window.matchMedia( "only screen and (max-width: 760px)" ).matches;
393 let isWindow = <?php echo $window ? 'true' : 'false' ?>;
394 let isDebugMode = <?php echo $debugMode ? 'true' : 'false' ?>;
395 let isFullscreen = <?php echo $fullscreen ? 'true' : 'false' ?>;
396 let restNonce = '<?php echo esc_attr( $rest_nonce ) ?>';
397 let apiURL = '<?php echo esc_url( $apiUrl ) ?>';
398 let isCasuallyFineTuned = <?php echo $casuallyFineTuned ? 'true' : 'false' ?>;
399 let rawUserName = '<?php echo esc_attr( $rawUserName ) ?>';
400 let rawAiName = '<?php echo esc_attr( $rawAiName ) ?>';
401 let userName = '<?php echo wp_kses_post( $userName ) ?>';
402 let aiName = '<?php echo wp_kses_post( $aiName ) ?>';
403 let sysName = '<?php echo wp_kses_post( $sysName ) ?>';
404 let env = '<?php echo esc_attr( $env ) ?>';
405 let apiKey = '<?php echo esc_attr( $apiKey ) ?>';
406 let service = '<?php echo esc_attr( $service ) ?>';
407 let session = '<?php echo esc_attr( $sessionId ) ?>';
408 let mode = '<?php echo esc_attr( $mode ) ?>';
409 let model = '<?php echo esc_attr( $model ) ?>';
410 let context = isCasuallyFineTuned ? null : '<?php echo esc_attr( $context ) ?>';
411 let embeddingsIndex = '<?php echo esc_attr( $embeddingsIndex ) ?>';
412 let promptEnding = '<?php echo esc_attr( $promptEnding ) ?>';
413 let stop = '<?php echo esc_attr( $completionEnding ) ?>';
414 let startSentence = '<?php echo esc_attr( $startSentence ) ?>';
415 let maxSentences = <?php echo (int)$maxSentences ?>;
416 let memorizeChat = <?php echo $memorizeChat ? 'true' : 'false' ?>;
417 let maxTokens = <?php echo (int)$maxTokens ?>;
418 let maxResults = <?php echo (int)$maxResults ?>;
419 let temperature = <?php echo str_replace(',', '.', (float)$temperature) ?>;
420 let typewriter = <?php echo $typewriter ? 'true' : 'false' ?>;
421 let copyButton = <?php echo $copyButton ? 'true' : 'false' ?>;
422 let chatId = randomStr();
423 let memorizedChat = { chatId, messages: [] };
424
425 if (isDebugMode) {
426 window.mwai_<?php echo esc_attr( $id ) ?> = {
427 memorizedChat: memorizedChat,
428 parameters: { mode: mode, model, temperature, maxTokens, context: context, startSentence,
429 isMobile, isWindow, isFullscreen, isCasuallyFineTuned, memorizeChat, maxSentences,
430 rawUserName, rawAiName, embeddingsIndex, typewriter, maxResults, userName, aiName, env, apiKey, service, session
431 }
432 };
433 }
434
435 function randomStr() {
436 return Math.random().toString(36).substring(2);
437 }
438
439 // Set button text
440 function setButtonText() {
441 let input = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-input textarea');
442 let button = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-input button');
443 let buttonSpan = button.querySelector('span');
444 if (memorizedChat.messages.length < 2) {
445 buttonSpan.innerHTML = '<?php echo esc_html( $textSend ); ?>';
446 }
447 else if (!input.value.length) {
448 button.classList.add('mwai-clear');
449 buttonSpan.innerHTML = '<?php echo esc_html( $textClear ); ?>';
450 }
451 else {
452 button.classList.remove('mwai-clear');
453 buttonSpan.innerHTML = '<?php echo esc_html( $textSend ); ?>';
454 }
455 }
456
457 // Inject timer
458 function injectTimer(element) {
459 let intervalId;
460 let startTime = new Date();
461 let timerElement = null;
462
463 function updateTimer() {
464 let now = new Date();
465 let timer = Math.floor((now - startTime) / 1000);
466 if (!timerElement) {
467 if (timer > 0.5) {
468 timerElement = document.createElement('div');
469 timerElement.classList.add('mwai-timer');
470 element.appendChild(timerElement);
471 }
472 }
473 if (timerElement) {
474 let minutes = Math.floor(timer / 60);
475 let seconds = timer - (minutes * 60);
476 seconds = seconds < 10 ? '0' + seconds : seconds;
477 let display = minutes + ':' + seconds;
478 timerElement.innerHTML = display;
479 }
480 }
481
482 intervalId = setInterval(updateTimer, 500);
483
484 return function stopTimer() {
485 clearInterval(intervalId);
486 if (timerElement) {
487 timerElement.remove();
488 }
489 };
490 }
491
492 // Push the reply in the conversation
493 function addReply(text, role = 'user', replay = false) {
494 var conversation = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-conversation');
495
496 if (memorizeChat) {
497 localStorage.setItem('mwai-chat-<?php echo esc_attr( $id ) ?>', JSON.stringify(memorizedChat));
498 }
499
500 // If text is array, then it's image URLs. Let's create a simple gallery in HTML in $text.
501 if (Array.isArray(text)) {
502 var newText = '<div class="mwai-gallery">';
503 for (var i = 0; i < text.length; i++) {
504 newText += '<a href="' + text[i] + '" target="_blank"><img src="' + text[i] + '" />';
505 }
506 text = newText + '</div>';
507 }
508
509 var mwaiClasses = ['mwai-reply'];
510 if (role === 'assistant') {
511 mwaiClasses.push('mwai-ai');
512 }
513 else if (role === 'system') {
514 mwaiClasses.push('mwai-system');
515 }
516 else {
517 mwaiClasses.push('mwai-user');
518 }
519 var div = document.createElement('div');
520 div.classList.add(...mwaiClasses);
521 var nameSpan = document.createElement('span');
522 nameSpan.classList.add('mwai-name');
523 if (role === 'assistant') {
524 nameSpan.innerHTML = aiName;
525 }
526 else if (role === 'system') {
527 nameSpan.innerHTML = sysName;
528 }
529 else {
530 nameSpan.innerHTML = userName;
531 }
532 var textSpan = document.createElement('span');
533 textSpan.classList.add('mwai-text');
534 textSpan.innerHTML = text;
535 div.appendChild(nameSpan);
536 div.appendChild(textSpan);
537
538 // Copy Button
539 if (copyButton && role === 'assistant') {
540 var button = document.createElement('div');
541 button.classList.add('mwai-copy-button');
542 var firstElement = document.createElement('div');
543 firstElement.classList.add('mwai-copy-button-one');
544 var secondElement = document.createElement('div');
545 secondElement.classList.add('mwai-copy-button-two');
546 button.appendChild(firstElement);
547 button.appendChild(secondElement);
548 div.appendChild(button);
549 button.addEventListener('click', function () {
550 try {
551 var content = textSpan.textContent;
552 navigator.clipboard.writeText(content);
553 button.classList.add('mwai-animate');
554 setTimeout(function () {
555 button.classList.remove('mwai-animate');
556 }, 1000);
557 }
558 catch (err) {
559 console.warn('Not allowed to copy to clipboard. Make sure your website uses HTTPS.');
560 }
561 });
562 }
563
564 conversation.appendChild(div);
565
566 if (typewriter) {
567 if (role === 'assistant' && text !== startSentence && !replay) {
568 let typewriter = new Typewriter(textSpan, {
569 deleteSpeed: 50, delay: 25, loop: false, cursor: '', autoStart: true,
570 wrapperClassName: 'mwai-typewriter',
571 });
572 typewriter.typeString(text).start().callFunction((state) => {
573 state.elements.cursor.setAttribute('hidden', 'hidden');
574 typewriter.stop();
575 });
576 }
577 }
578
579 conversation.scrollTop = conversation.scrollHeight;
580 setButtonText();
581
582 // Syntax coloring
583 if (typeof hljs !== 'undefined') {
584 document.querySelectorAll('pre code').forEach((el) => {
585 hljs.highlightElement(el);
586 });
587 }
588 }
589
590 function buildPrompt(last = 15) {
591 let prompt = context ? (context + '\n\n') : '';
592 memorizedChat.messages = memorizedChat.messages.slice(-last);
593
594 // Casually fine tuned, let's use the last question
595 if (isCasuallyFineTuned) {
596 let lastLine = memorizedChat.messages[memorizedChat.messages.length - 1];
597 prompt = lastLine.content + promptEnding;
598 return prompt;
599 }
600
601 // Otherwise let's compile the latest conversation
602 let conversation = memorizedChat.messages.map(x => x.who + x.content);
603 prompt += conversation.join('\n');
604 prompt += '\n' + rawAiName;
605 return prompt;
606 }
607
608 // Function to request the completion
609 function onSendClick() {
610 let input = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-input textarea');
611 let inputText = input.value.trim();
612
613 // Reset the conversation if empty
614 if (inputText === '') {
615 chatId = randomStr();
616 document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-conversation').innerHTML = '';
617 localStorage.removeItem('mwai-chat-<?php echo esc_attr( $id ) ?>')
618 memorizedChat = { chatId: chatId, messages: [] };
619 memorizedChat.messages.push({
620 id: randomStr(),
621 role: 'assistant',
622 content: startSentence,
623 who: rawAiName,
624 html: startSentence
625 });
626 addReply(startSentence, 'assistant');
627 return;
628 }
629
630 // Disable the button
631 var button = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-input button');
632 button.disabled = true;
633
634 // Add the user reply
635 memorizedChat.messages.push({
636 id: randomStr(),
637 role: 'user',
638 content: inputText,
639 who: rawUserName,
640 html: inputText
641 });
642 addReply(inputText, 'user');
643 input.value = '';
644 input.setAttribute('rows', 1);
645 input.disabled = true;
646
647 let prompt = buildPrompt(maxSentences);
648
649 const data = mode === 'images' ? {
650 env, session: session,
651 prompt: inputText,
652 newMessage: inputText,
653 model: model,
654 maxResults,
655 apiKey: apiKey,
656 service: service,
657 chatId: chatId,
658 } : {
659 env, session: session,
660 prompt: prompt,
661 context: context,
662 messages: memorizedChat.messages,
663 newMessage: inputText,
664 userName: userName,
665 aiName: aiName,
666 model: model,
667 temperature: temperature,
668 maxTokens: maxTokens,
669 maxResults: 1,
670 apiKey: apiKey,
671 service: service,
672 embeddingsIndex: embeddingsIndex,
673 stop: stop,
674 chatId: chatId,
675 };
676
677 // Start the timer
678 const stopTimer = injectTimer(button);
679
680 // Send the request
681 if (isDebugMode) {
682 console.log('[BOT] Sent: ', data);
683 }
684 fetch(apiURL, { method: 'POST', headers: {
685 'Content-Type': 'application/json',
686 'X-WP-Nonce': restNonce,
687 },
688 body: JSON.stringify(data)
689 })
690 .then(response => response.json())
691 .then(data => {
692 if (isDebugMode) {
693 console.log('[BOT] Recv: ', data);
694 }
695 if (!data.success) {
696 addReply(data.message, 'system');
697 }
698 else {
699 let html = data.images ? data.images : data.html;
700 memorizedChat.messages.push({
701 id: randomStr(),
702 role: 'assistant',
703 content: data.reply,
704 who: rawAiName,
705 html: html
706 });
707 addReply(html, 'assistant');
708 }
709 button.disabled = false;
710 input.disabled = false;
711 stopTimer();
712
713 // Only focus only on desktop (to avoid the mobile keyboard to kick-in)
714 if (!isMobile) {
715 input.focus();
716 }
717 })
718 .catch(error => {
719 console.error(error);
720 button.disabled = false;
721 input.disabled = false;
722 stopTimer();
723 });
724 }
725
726 // Keep the textarea height in sync with the content
727 function resizeTextArea(ev) {
728 ev.target.style.height = 'auto';
729 ev.target.style.height = ev.target.scrollHeight + 'px';
730 }
731
732 // Keep the textarea height in sync with the content
733 function delayedResizeTextArea(ev) {
734 window.setTimeout(resizeTextArea, 0, event);
735 }
736
737 // Init the chatbot
738 function initMeowChatbot() {
739 var input = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-input textarea');
740 var button = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-input button');
741
742 input.addEventListener('keypress', (event) => {
743 let text = event.target.value;
744 if (event.keyCode === 13 && !text.length && !event.shiftKey) {
745 event.preventDefault();
746 return;
747 }
748 if (event.keyCode === 13 && text.length && !event.shiftKey) {
749 onSendClick();
750 }
751 });
752 input.addEventListener('keydown', (event) => {
753 var rows = input.getAttribute('rows');
754 if (event.keyCode === 13 && event.shiftKey) {
755 var lines = input.value.split('\n').length + 1;
756 //mwaiSetTextAreaHeight(input, lines);
757 }
758 });
759 input.addEventListener('keyup', (event) => {
760 var rows = input.getAttribute('rows');
761 var lines = input.value.split('\n').length ;
762 //mwaiSetTextAreaHeight(input, lines);
763 setButtonText();
764 });
765
766 input.addEventListener('change', resizeTextArea, false);
767 input.addEventListener('cut', delayedResizeTextArea, false);
768 input.addEventListener('paste', delayedResizeTextArea, false);
769 input.addEventListener('drop', delayedResizeTextArea, false);
770 input.addEventListener('keydown', delayedResizeTextArea, false);
771
772 button.addEventListener('click', (event) => {
773 onSendClick();
774 });
775
776 // If window, add event listener to mwai-open-button and mwai-close-button
777 if ( isWindow ) {
778 var openButton = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-open-button');
779 openButton.addEventListener('click', (event) => {
780 var chat = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?>');
781 chat.classList.add('mwai-open');
782 // Only focus only on desktop (to avoid the mobile keyboard to kick-in)
783 if (!isMobile) {
784 input.focus();
785 }
786 });
787 var closeButton = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-close-button');
788 closeButton.addEventListener('click', (event) => {
789 var chat = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?>');
790 chat.classList.remove('mwai-open');
791 });
792 if (isFullscreen) {
793 var resizeButton = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-resize-button');
794 resizeButton.addEventListener('click', (event) => {
795 var chat = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?>');
796 chat.classList.toggle('mwai-fullscreen');
797 });
798 }
799 }
800
801 // Get back the previous chat if any for the same ID
802 var chatHistory = [];
803 if (memorizeChat) {
804 chatHistory = localStorage.getItem('mwai-chat-<?php echo esc_attr( $id ) ?>');
805 if (chatHistory) {
806 memorizedChat = JSON.parse(chatHistory);
807 if (memorizedChat && memorizedChat.chatId && memorizedChat.messages) {
808 chatId = memorizedChat.chatId;
809 memorizedChat.messages = memorizedChat.messages.filter(x => x && x.html && x.role);
810 memorizedChat.messages.forEach(x => {
811 addReply(x.html, x.role, true);
812 });
813 }
814 else {
815 memorizedChat = null;
816 }
817 }
818 if (!memorizedChat) {
819 memorizedChat = {
820 chatId: chatId,
821 messages: []
822 };
823 }
824 }
825 if (memorizedChat.messages.length === 0) {
826 memorizedChat.messages.push({
827 id: randomStr(),
828 role: 'assistant',
829 content: startSentence,
830 who: rawAiName,
831 html: startSentence
832 });
833 addReply(startSentence, 'assistant');
834 }
835 }
836
837 // Let's go totally meoooow on this!
838 initMeowChatbot();
839 })();
840 </script>
841
842 <?php
843 $output = ob_get_contents();
844 ob_end_clean();
845 $output = apply_filters( 'mwai_chatbot', $output, $atts );
846 return $output;
847 }
848 }
849