PluginProbe ʕ •ᴥ•ʔ
AI Engine – The Chatbot, AI Framework & MCP for WordPress / 1.3.39
AI Engine – The Chatbot, AI Framework & MCP for WordPress v1.3.39
3.5.7 3.5.6 3.5.5 3.5.4 3.5.3 3.5.2 3.5.1 3.5.0 3.4.9 3.4.8 3.4.7 0.2.1 1.6.91 0.2.2 1.6.92 0.2.3 1.6.93 0.2.4 1.6.94 0.2.5 1.6.95 0.2.6 1.6.96 0.2.7 1.6.97 0.2.8 1.6.98 0.2.9 1.6.99 0.3.0 1.7.0 0.3.1 1.7.1 0.3.2 1.7.2 0.3.3 1.7.3 0.3.4 1.7.4 0.3.5 1.7.5 0.3.6 1.7.6 0.4.0 1.7.7 0.4.1 1.7.8 0.4.2 1.7.9 0.4.3 1.8.0 0.4.4 1.8.1 0.4.5 1.8.2 0.4.6 1.8.3 0.4.7 1.8.4 0.4.8 1.8.5 0.4.9 1.8.6 0.5.0 1.8.7 0.5.1 1.8.8 0.5.2 1.8.9 0.5.3 1.9.0 0.5.4 1.9.1 0.5.5 1.9.2 0.5.6 1.9.3 0.5.7 1.9.4 0.5.8 1.9.5 0.5.9 1.9.6 0.6.0 1.9.7 0.6.1 1.9.8 0.6.2 1.9.81 0.6.3 1.9.82 0.6.4 1.9.83 0.6.5 1.9.84 0.6.6 1.9.85 0.6.7 1.9.86 0.6.8 1.9.87 0.6.9 1.9.88 0.7.0 1.9.89 0.7.1 1.9.90 0.7.2 1.9.91 0.7.3 1.9.92 0.7.4 1.9.93 0.7.5 1.9.94 0.7.6 1.9.95 0.7.7 1.9.96 0.7.8 1.9.97 0.7.9 1.9.98 0.8.0 1.9.99 0.8.1 2.0.0 0.8.2 2.0.1 0.8.3 2.0.2 0.8.4 2.0.3 0.8.5 2.0.4 0.8.6 2.0.5 0.8.7 2.0.6 0.8.8 2.0.7 0.8.9 2.0.8 0.9.0 2.0.9 0.9.2 2.1.0 0.9.3 2.1.1 0.9.4 2.1.2 0.9.5 2.1.3 0.9.6 2.1.4 0.9.7 2.1.5 0.9.8 2.1.6 0.9.81 2.1.7 0.9.82 2.1.8 0.9.83 2.1.9 0.9.84 2.2.0 0.9.85 2.2.1 0.9.86 2.2.2 0.9.87 2.2.3 0.9.88 2.2.4 0.9.89 2.2.5 0.9.9 2.2.51 0.9.91 2.2.52 0.9.92 2.2.53 0.9.93 2.2.54 0.9.94 2.2.56 0.9.95 2.2.57 0.9.96 2.2.6 0.9.97 2.2.60 0.9.98 2.2.61 0.9.99 2.2.62 1.0.0 2.2.63 1.0.01 2.2.70 1.0.1 2.2.80 1.0.2 2.2.81 1.0.3 2.2.90 1.0.4 2.2.91 1.0.5 2.2.92 1.0.6 2.2.93 1.0.7 2.2.94 1.0.8 2.2.95 1.0.9 2.3.0 1.1.0 2.3.1 1.1.1 2.3.2 1.1.2 2.3.3 1.1.3 2.3.4 1.1.4 2.3.5 1.1.5 2.3.6 1.1.6 2.3.7 1.1.7 2.3.8 1.1.8 2.3.9 1.1.9 2.4.0 1.2.0 2.4.1 1.2.1 2.4.2 1.2.2 2.4.3 1.2.21 2.4.4 1.2.3 2.4.5 1.2.30 2.4.6 1.3.0 2.4.7 1.3.1 2.4.8 1.3.2 2.4.9 1.3.3 2.5.0 1.3.31 2.5.1 1.3.32 2.5.2 1.3.33 2.5.3 1.3.34 2.5.4 1.3.35 2.5.5 1.3.36 2.5.6 1.3.37 2.5.7 1.3.38 2.5.8 1.3.39 2.5.9 1.3.40 2.6.0 1.3.41 2.6.1 1.3.42 2.6.2 1.3.43 2.6.3 1.3.44 2.6.5 1.3.45 2.6.6 1.3.46 2.6.7 1.3.47 2.6.8 1.3.48 2.6.9 1.3.49 2.7.0 1.3.50 2.7.1 1.3.51 2.7.2 1.3.52 2.7.3 1.3.53 2.7.4 1.3.54 2.7.5 1.3.56 2.7.6 1.3.57 2.7.7 1.3.58 2.7.8 1.3.59 2.7.9 1.3.60 2.8.0 1.3.61 2.8.1 1.3.62 2.8.2 1.3.63 2.8.3 1.3.64 2.8.4 1.3.65 2.8.5 1.3.66 2.8.6 1.3.67 2.8.7 1.3.68 2.8.8 1.3.69 2.8.9 1.3.70 2.9.0 1.3.71 2.9.1 1.3.72 2.9.2 1.3.73 2.9.3 1.3.74 2.9.4 1.3.75 2.9.5 1.3.76 2.9.6 1.3.77 2.9.7 1.3.78 2.9.8 1.3.79 2.9.9 1.3.80 3.0.0 1.3.81 3.0.1 1.3.82 3.0.2 1.3.83 3.0.3 1.3.84 3.0.4 1.3.85 3.0.5 1.3.86 3.0.6 1.3.87 3.0.7 1.3.88 3.0.8 1.3.89 3.0.9 1.3.90 3.1.0 1.3.91 3.1.1 1.3.92 3.1.2 1.3.93 3.1.3 1.3.94 3.1.4 1.3.95 3.1.5 1.3.96 3.1.6 1.3.97 3.1.7 1.3.98 3.1.8 1.3.99 3.1.9 1.4.0 3.2.0 1.4.1 3.2.1 1.4.2 3.2.2 1.4.3 3.2.3 1.4.4 3.2.4 1.4.5 3.2.5 1.4.6 3.2.6 1.4.7 3.2.7 1.4.8 3.2.8 1.4.9 3.2.9 1.5.0 3.3.0 1.5.1 3.3.1 1.5.2 3.3.2 1.5.3 3.3.3 1.5.4 3.3.4 1.5.5 3.3.5 1.5.6 3.3.6 1.5.7 3.3.7 1.5.8 3.3.8 1.5.9 3.3.9 1.6.0 3.4.0 1.6.1 3.4.1 1.6.2 3.4.2 1.6.3 3.4.3 1.6.5 3.4.4 1.6.51 3.4.5 1.6.52 3.4.6 1.6.53 1.6.54 1.6.55 1.6.56 1.6.57 1.6.58 1.6.59 1.6.60 1.6.61 1.6.62 1.6.63 1.6.64 1.6.65 1.6.66 1.6.67 1.6.68 trunk 1.6.69 0.0.1 1.6.70 0.0.2 1.6.71 0.0.3 1.6.72 0.0.4 1.6.73 0.0.5 1.6.74 0.0.6 1.6.75 0.0.7 1.6.76 0.0.8 1.6.77 0.0.9 1.6.78 0.1.0 1.6.79 0.1.1 1.6.81 0.1.2 1.6.82 0.1.3 1.6.83 0.1.4 1.6.84 0.1.5 1.6.85 0.1.6 1.6.86 0.1.7 1.6.87 0.1.8 1.6.88 0.1.9 1.6.89 0.2.0 1.6.90
ai-engine / classes / modules / chatbot.php
ai-engine / classes / modules Last commit date
assistants.php 3 years ago chatbot-chatgpt.css 3 years ago chatbot-chatgpt.scss 3 years ago chatbot.php 3 years ago chatbot_logs.php 3 years ago
chatbot.php
754 lines
1 <?php
2
3 class Meow_MWAI_Modules_Chatbot {
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
19 if ( $this->core->get_option( 'shortcode_chat_inject' ) ) {
20 add_action( 'wp_footer', array( $this, 'inject_chat' ) );
21 }
22
23 $logs = $this->core->get_option( 'shortcode_chat_logs' );
24 if ( $logs && strpos( $logs, 'file' ) !== false ) {
25 new Meow_MWAI_Modules_Chatbot_Logs();
26 }
27
28 if ( $this->core->get_option( 'shortcode_chat_styles' ) ) {
29 add_filter( 'mwai_chatbot_style', [ $this, 'apply_chat_styles' ], 10, 2 );
30 }
31
32 }
33
34 public function enqueue_scripts() {
35 if ( $this->core->get_option( 'shortcode_chat_syntax_highlighting' ) ) {
36 wp_enqueue_script( 'mwai_chatbot', MWAI_URL . 'vendor/highlightjs/highlight.min.js', [], '11.7', false );
37 wp_enqueue_style( 'mwai_chatbot', MWAI_URL . '/vendor/highlightjs/stackoverflow-dark.min.css', [], '11.7' );
38 }
39 if ( $this->core->get_option( 'shortcode_chat_typewriter' ) ) {
40 wp_enqueue_script( 'mwai_chatbot_typewriter', MWAI_URL . 'vendor/typewriterjs/typewriter.min.js', [], '2.0', true );
41 }
42 }
43
44 public function rest_api_init() {
45 register_rest_route( $this->namespace, '/chat', array(
46 'methods' => 'POST',
47 'callback' => array( $this, 'rest_chat' ),
48 'permission_callback' => '__return_true'
49 ) );
50 register_rest_route( $this->namespace, '/imagesbot', array(
51 'methods' => 'POST',
52 'callback' => array( $this, 'rest_imagesbot' ),
53 'permission_callback' => '__return_true'
54 ) );
55 }
56
57 public function chatgpt_style( $id ) {
58 $css = file_get_contents( MWAI_PATH . '/classes/modules/chatbot-chatgpt.css' );
59 $css = str_replace( '#mwai-chat-id', "#mwai-chat-{$id}", $css );
60 return '<style>' . $css . '</style>';
61 }
62
63 public function rest_chat( $request ) {
64 try {
65 $params = $request->get_json_params();
66 $embeddingsIndex = $params['embeddingsIndex'];
67 $query = new Meow_MWAI_QueryText( $params['prompt'], 1024 );
68 $query->injectParams( $params );
69 $takeoverAnswer = apply_filters( 'mwai_chatbot_takeover', null, $query, $params );
70 if ( !empty( $takeoverAnswer ) ) {
71 return new WP_REST_Response( [ 'success' => true, 'answer' => $takeoverAnswer,
72 'html' => $takeoverAnswer, 'usage' => null ], 200 );
73 }
74
75 // Awareness & Embeddings
76 if ( $query->mode === 'chat' && !empty( $embeddingsIndex ) ) {
77 $context = apply_filters( 'mwai_context_search', $query, $embeddingsIndex );
78 if ( !empty( $context ) ) {
79 $query->injectContext( $context );
80 }
81 }
82
83 $answer = $this->core->ai->run( $query );
84 $rawText = $answer->result;
85 $html = apply_filters( 'mwai_chatbot_reply', $rawText, $query, $params );
86 if ( $this->core->get_option( 'shortcode_chat_formatting' ) ) {
87 $html = $this->core->markdown_to_html( $html );
88 }
89 return new WP_REST_Response( [ 'success' => true, 'answer' => $rawText,
90 'html' => $html, 'usage' => $answer->usage ], 200 );
91 }
92 catch ( Exception $e ) {
93 return new WP_REST_Response( [ 'success' => false, 'message' => $e->getMessage() ], 500 );
94 }
95 }
96
97 public function rest_imagesbot( $request ) {
98 try {
99 $params = $request->get_json_params();
100 $query = new Meow_MWAI_QueryImage( $params['prompt'] );
101 $query->injectParams( $params );
102 $answer = $this->core->ai->run( $query );
103 return new WP_REST_Response( [ 'success' => true, 'images' => $answer->results, 'usage' => $answer->usage ], 200 );
104 }
105 catch ( Exception $e ) {
106 return new WP_REST_Response( [ 'success' => false, 'message' => $e->getMessage() ], 500 );
107 }
108 }
109
110 public function apply_chat_styles( $css, $chatbotId ) {
111 $chatStyles = $this->core->get_option( 'shortcode_chat_styles' );
112 return preg_replace_callback( '/--mwai-(\w+):\s*([^;]+);/', function ( $matches ) use ( $chatStyles ) {
113 if ( isset( $chatStyles[$matches[1]] ) ) {
114 return '--mwai-' . $matches[1] . ': ' . $chatStyles[$matches[1]] . ';';
115 }
116 return $matches[0];
117 }, $css );
118 }
119
120 public function inject_chat() {
121 $params = $this->core->get_option( 'shortcode_chat_params' );
122 echo $this->chat( $params );
123 }
124
125 public function imageschat( $atts ) {
126 $atts['mode'] = 'images';
127 return $this->chat( $atts );
128 }
129
130 public function getCurrentUser() {
131 if ( is_user_logged_in() ) {
132 return wp_get_current_user();
133 }
134 return null;
135 }
136
137 public function handlePlaceholders( $data, $guestName = 'Guest: ' ) {
138 if ( strpos( $data, '{' ) === false ) {
139 return $data;
140 }
141 $placeholders_meta = [ '{FIRST_NAME}', '{LAST_NAME}' ];
142 $placeholders_data = [ '{USER_LOGIN}', '{DISPLAY_NAME}' ];
143 $user = $this->getCurrentUser();
144 if ( $user ) {
145 foreach ( $placeholders_meta as $placeholder ) {
146 if ( strpos( $data, $placeholder ) === false ) { continue; }
147 $lcPlaceholder = substr( strtolower( $placeholder ), 1, -1 );
148 $value = get_user_meta( $user->ID, $lcPlaceholder, true );
149 $data = str_replace( $placeholder, $value, $data );
150 }
151 foreach ( $placeholders_data as $placeholder ) {
152 if ( strpos( $data, $placeholder ) === false ) { continue; }
153 $lcPlaceholder = substr( strtolower( $placeholder ), 1, -1 );
154 $value = $user->data->$lcPlaceholder;
155 $data = str_replace( $placeholder, $value, $data );
156 }
157 if ( !empty( $data ) ) {
158 return $data;
159 }
160 }
161 return $guestName;
162 }
163
164 public function formatUserName( $userName, $guestName = 'Guest: ' ) {
165 // Default avatar
166 if ( empty( $userName ) ) {
167 $user = $this->getCurrentUser();
168 if ( $user ) {
169 // Gravatar
170 $userName = '<div class="mwai-avatar"><img src="' . get_avatar_url( $user->user_email ) . '" /></div>';
171 }
172 else {
173 // Default avatar
174 $userName = '<div class="mwai-avatar mwai-svg"><img src="' . MWAI_URL . '/images/avatar-user.svg" /></div>';
175 }
176 }
177 // Custom avatar
178 else if ( $this->core->isUrl( $userName ) ) {
179 $userName = '<div class="mwai-avatar"><img src="' . $userName . '" /></div>';
180 }
181 // Placeholders
182 else {
183 $userName = $this->handlePlaceholders( $userName, $guestName );
184 $userName = '<div class="mwai-name-text">' . $userName . '</div>';
185 }
186 return $userName;
187 }
188
189 public function formatAiName( $aiName ) {
190 // Default avatar
191 if ( empty( $aiName ) ) {
192 $aiName = '<div class="mwai-avatar mwai-svg"><img src="' . MWAI_URL . '/images/avatar-ai.svg" /></div>';
193 }
194 // Custom avatar
195 else if ( $this->core->isUrl( $aiName ) ) {
196 $aiName = '<div class="mwai-avatar"><img src="' . $aiName . '" /></div>';
197 }
198 else {
199 $aiName = '<div class="mwai-name-text">' . $aiName . '</div>';
200 }
201 return $aiName;
202 }
203
204 public function formatRawName( $aiName ) {
205 return 'AI: ';
206 }
207
208 public function formatRawUserName( $userName, $guestName ) {
209 return 'User: ';
210 }
211
212 public function chat( $atts ) {
213 $this->usingChat = true;
214
215 // Use the core default parameters, or the user default parameters
216 $override = $this->core->get_option( 'shortcode_chat_params_override' );
217 $defaults_params = $override ? $this->core->get_option( 'shortcode_chat_params' ) :
218 $this->core->get_option( 'shortcode_chat_default_params' );
219
220 // Give a chance to modify the default parameters one last time
221 $defaults = apply_filters( 'mwai_chatbot_params_defaults', $defaults_params );
222
223 // Make sure all the mandatory params are set
224 foreach ( $this->core->defaultChatbotParams as $key => $value ) {
225 if ( !isset( $defaults[$key] ) ) {
226 $defaults[$key] = $value;
227 }
228 }
229
230 // Override with the shortcode, and before/after filters
231 $atts = apply_filters( 'mwai_chatbot_params_before', $atts );
232 $atts = shortcode_atts( $defaults, $atts );
233 $atts = apply_filters( 'mwai_chatbot_params', $atts );
234
235 // UI Parameters
236 $aiName = addslashes( trim( $atts['ai_name'] ) );
237 $userName = addslashes( trim( $atts['user_name'] ) );
238 $guestName = addslashes( trim( $atts['guest_name'] ) );
239 $sysName = addslashes( trim( $atts['sys_name'] ) );
240 $context = addslashes( $atts['context'] );
241 $context = preg_replace( '/\n/', "\\n", $context );
242 $textSend = addslashes( trim( $atts['text_send'] ) );
243 $textClear = addslashes( trim( $atts['text_clear'] ) );
244 $textInputMaxLength = intval( $atts['text_input_maxlength'] );
245 $textInputPlaceholder = addslashes( trim( $atts['text_input_placeholder'] ) );
246 $textCompliance = ( trim( $atts['text_compliance'] ) );
247 $startSentence = addslashes( trim( $atts['start_sentence'] ) );
248 $window = filter_var( $atts['window'], FILTER_VALIDATE_BOOLEAN );
249 $copyButton = filter_var( $atts['copy_button'], FILTER_VALIDATE_BOOLEAN );
250 $fullscreen = filter_var( $atts['fullscreen'], FILTER_VALIDATE_BOOLEAN );
251 $icon = isset( $atts['icon'] ) ? addslashes( trim( $atts['icon'] ) ) : '';
252 $iconText = trim( $atts['icon_text'] );
253 $iconAlt = addslashes( trim( $atts['icon_alt'] ) );
254 $iconPosition = addslashes( trim( $atts['icon_position'] ) );
255 $style = $atts['style'];
256
257 // Validade & Enhance UI Parameters
258 $aiName = $this->formatAiName( $aiName );
259 $userName = $this->formatUserName( $userName, $guestName );
260 $rawAiName = $this->formatRawName( $aiName );
261 $rawUserName = $this->formatRawUserName( $userName, $guestName );
262
263 // Chatbot System Parameters
264 $id = empty( $atts['id'] ) ? uniqid() : $atts['id'];
265 $typewriter = $this->core->get_option( 'shortcode_chat_typewriter' );
266 $memorizeChat = !empty( $atts['id'] );
267 $id = preg_replace( '/[^a-zA-Z0-9]/', '', $id );
268 $env = $atts['env'];
269 $mode = $atts['mode'];
270 $maxResults = $mode === 'chat' ? 1 : $atts['max_results'];
271 $maxSentences = !empty( $atts['max_sentences'] ) ? intval( $atts['max_sentences'] ) : 1;
272 $sessionId = $this->core->get_session_id();
273 $rest_nonce = wp_create_nonce( 'wp_rest' );
274 $casuallyFineTuned = boolval( $atts['casually_fine_tuned'] );
275 $embeddingsIndex = $atts['embeddings_index'];
276 $promptEnding = addslashes( trim( $atts['prompt_ending'] ) );
277 $completionEnding = addslashes( trim( $atts['completion_ending'] ) );
278 if ( $casuallyFineTuned ) {
279 $promptEnding = "\\n\\n###\\n\\n";
280 $completionEnding = "\\n\\n";
281 }
282 $debugMode = $this->core->get_option( 'debug_mode' );
283
284 // OpenAI Parameters
285 $model = $atts['model'];
286 $temperature = $atts['temperature'];
287 $maxTokens = $atts['max_tokens'];
288 $apiKey = $atts['api_key'];
289
290 // Variables
291 $apiUrl = get_rest_url( null, $mode === 'images' ? 'ai-chatbot/v1/imagesbot' : 'ai-chatbot/v1/chat' );
292 $baseClasses = 'mwai-chat';
293 $baseClasses .= ( $window ? ' mwai-window' : '' );
294 $baseClasses .= ( !$window && $fullscreen ? ' mwai-fullscreen' : '' );
295 $baseClasses .= ( $style === 'chatgpt' ? ' mwai-chatgpt' : '' );
296 $baseClasses .= ( $window && !empty( $iconPosition ) ? ( ' mwai-' . $iconPosition ) : '' );
297
298 // Output CSS
299 ob_start();
300 $style_content = '';
301 if ( $style === 'chatgpt' ) {
302 $style_content = $this->chatgpt_style( $id, $style );
303 }
304 $style_content = apply_filters( 'mwai_chatbot_style', $style_content, $id );
305 echo wp_kses( $style_content, array( 'style' => array() ) );
306
307 // Output HTML & CSS
308 $chatStyles = $this->core->get_option( 'shortcode_chat_styles' );
309 $iconUrl = MWAI_URL . '/images/chat-green.svg';
310 if ( !empty( $icon ) ) {
311 $iconUrl = $icon;
312 }
313 else if ( !empty( $chatStyles ) && isset( $chatStyles['icon'] ) ) {
314 $url = $chatStyles['icon'];
315 $iconUrl = $this->core->isUrl( $url ) ? $url : ( MWAI_URL . 'images/' . $chatStyles['icon'] );
316 }
317 ?>
318 <div id="mwai-chat-<?php echo esc_attr( $id ); ?>" class="<?php echo esc_attr( $baseClasses ); ?>">
319 <?php if ( $window ) : ?>
320 <div class="mwai-open-button">
321 <?php if ( !empty( $iconText ) ) : ?>
322 <div class="mwai-icon-text"><?php echo esc_html( $iconText ); ?></div>
323 <?php endif; ?>
324 <img width="64" height="64" alt="<?php echo esc_attr( $iconAlt ); ?>" src="<?php echo esc_url( $iconUrl ); ?>" />
325 </div>
326 <div class="mwai-header">
327 <div class="mwai-buttons">
328 <?php if ( $fullscreen ) : ?>
329 <div class="mwai-resize-button"></div>
330 <?php endif; ?>
331 <div class="mwai-close-button"></div>
332 </div>
333 </div>
334 <?php endif; ?>
335 <div class="mwai-content">
336 <div class="mwai-conversation">
337 </div>
338 <div class="mwai-input">
339 <textarea rows="1" maxlength="<?php echo (int)$textInputMaxLength; ?>" placeholder="<?php echo esc_attr( $textInputPlaceholder ); ?>"></textarea>
340 <button><span><?php echo esc_html( $textSend ); ?></span></button>
341 </div>
342 <?php if ( !empty( $textCompliance ) ) : ?>
343 <div class="mwai-compliance">
344 ⚠️ <?php echo wp_kses_post( $textCompliance ); ?>
345 </div>
346 <?php endif; ?>
347 </div>
348 </div>
349
350 <script>
351 (function () {
352 let isMobile = window.matchMedia( "only screen and (max-width: 760px)" ).matches;
353 let isWindow = <?php echo $window ? 'true' : 'false' ?>;
354 let isDebugMode = <?php echo $debugMode ? 'true' : 'false' ?>;
355 let isFullscreen = <?php echo $fullscreen ? 'true' : 'false' ?>;
356 let restNonce = '<?php echo esc_attr( $rest_nonce ) ?>';
357 let apiURL = '<?php echo esc_url( $apiUrl ) ?>';
358 let isCasuallyFineTuned = <?php echo $casuallyFineTuned ? 'true' : 'false' ?>;
359 let rawUserName = '<?php echo esc_attr( $rawUserName ) ?>';
360 let rawAiName = '<?php echo esc_attr( $rawAiName ) ?>';
361 let userName = '<?php echo wp_kses_post( $userName ) ?>';
362 let aiName = '<?php echo wp_kses_post( $aiName ) ?>';
363 let sysName = '<?php echo wp_kses_post( $sysName ) ?>';
364 let env = '<?php echo esc_attr( $env ) ?>';
365 let apiKey = '<?php echo esc_attr( $apiKey ) ?>';
366 let session = '<?php echo esc_attr( $sessionId ) ?>';
367 let mode = '<?php echo esc_attr( $mode ) ?>';
368 let model = '<?php echo esc_attr( $model ) ?>';
369 let context = isCasuallyFineTuned ? null : '<?php echo esc_attr( $context ) ?>';
370 let embeddingsIndex = '<?php echo esc_attr( $embeddingsIndex ) ?>';
371 let promptEnding = '<?php echo esc_attr( $promptEnding ) ?>';
372 let stop = '<?php echo esc_attr( $completionEnding ) ?>';
373 let startSentence = '<?php echo esc_attr( $startSentence ) ?>';
374 let maxSentences = <?php echo (int)$maxSentences ?>;
375 let memorizeChat = <?php echo $memorizeChat ? 'true' : 'false' ?>;
376 let maxTokens = <?php echo (int)$maxTokens ?>;
377 let maxResults = <?php echo (int)$maxResults ?>;
378 let temperature = <?php echo (int)$temperature ?>;
379 let memorizedChat = [];
380 let typewriter = <?php echo $typewriter ? 'true' : 'false' ?>;
381 let copyButton = <?php echo $copyButton ? 'true' : 'false' ?>;
382
383 if (isDebugMode) {
384 window.mwai_<?php echo esc_attr( $id ) ?> = {
385 memorizedChat: memorizedChat,
386 parameters: { mode: mode, model, temperature, maxTokens, context: context, startSentence,
387 isMobile, isWindow, isFullscreen, isCasuallyFineTuned, memorizeChat, maxSentences,
388 rawUserName, rawAiName, embeddingsIndex, typewriter, maxResults, userName, aiName, env, apiKey, session
389 }
390 };
391 }
392
393 // Set button text
394 function setButtonText() {
395 let input = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-input textarea');
396 let button = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-input button');
397 let buttonSpan = button.querySelector('span');
398 if (memorizedChat.length < 2) {
399 buttonSpan.innerHTML = '<?php echo esc_html( $textSend ); ?>';
400 }
401 else if (!input.value.length) {
402 button.classList.add('mwai-clear');
403 buttonSpan.innerHTML = '<?php echo esc_html( $textClear ); ?>';
404 }
405 else {
406 button.classList.remove('mwai-clear');
407 buttonSpan.innerHTML = '<?php echo esc_html( $textSend ); ?>';
408 }
409 }
410
411 // Inject timer
412 function injectTimer(element) {
413 let intervalId;
414 let startTime = new Date();
415 let timerElement = null;
416
417 function updateTimer() {
418 let now = new Date();
419 let timer = Math.floor((now - startTime) / 1000);
420 if (!timerElement) {
421 if (timer > 0.5) {
422 timerElement = document.createElement('div');
423 timerElement.classList.add('mwai-timer');
424 element.appendChild(timerElement);
425 }
426 }
427 if (timerElement) {
428 let minutes = Math.floor(timer / 60);
429 let seconds = timer - (minutes * 60);
430 seconds = seconds < 10 ? '0' + seconds : seconds;
431 let display = minutes + ':' + seconds;
432 timerElement.innerHTML = display;
433 }
434 }
435
436 intervalId = setInterval(updateTimer, 500);
437
438 return function stopTimer() {
439 clearInterval(intervalId);
440 if (timerElement) {
441 timerElement.remove();
442 }
443 };
444 }
445
446 // Push the reply in the conversation
447 function addReply(text, role = 'user', replay = false) {
448 var conversation = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-conversation');
449
450 if (memorizeChat) {
451 localStorage.setItem('mwai-chat-<?php echo esc_attr( $id ) ?>', JSON.stringify(memorizedChat));
452 }
453
454 // If text is array, then it's image URLs. Let's create a simple gallery in HTML in $text.
455 if (Array.isArray(text)) {
456 var newText = '<div class="mwai-gallery">';
457 for (var i = 0; i < text.length; i++) {
458 newText += '<a href="' + text[i] + '" target="_blank"><img src="' + text[i] + '" />';
459 }
460 text = newText + '</div>';
461 }
462
463 var mwaiClasses = ['mwai-reply'];
464 if (role === 'assistant') {
465 mwaiClasses.push('mwai-ai');
466 }
467 else if (role === 'system') {
468 mwaiClasses.push('mwai-system');
469 }
470 else {
471 mwaiClasses.push('mwai-user');
472 }
473 var div = document.createElement('div');
474 div.classList.add(...mwaiClasses);
475 var nameSpan = document.createElement('span');
476 nameSpan.classList.add('mwai-name');
477 if (role === 'assistant') {
478 nameSpan.innerHTML = aiName;
479 }
480 else if (role === 'system') {
481 nameSpan.innerHTML = sysName;
482 }
483 else {
484 nameSpan.innerHTML = userName;
485 }
486 var textSpan = document.createElement('span');
487 textSpan.classList.add('mwai-text');
488 textSpan.innerHTML = text;
489 div.appendChild(nameSpan);
490 div.appendChild(textSpan);
491
492 // Copy Button
493 if (copyButton && role === 'assistant') {
494 var button = document.createElement('div');
495 button.classList.add('mwai-copy-button');
496 var firstElement = document.createElement('div');
497 firstElement.classList.add('mwai-copy-button-one');
498 var secondElement = document.createElement('div');
499 secondElement.classList.add('mwai-copy-button-two');
500 button.appendChild(firstElement);
501 button.appendChild(secondElement);
502 div.appendChild(button);
503 button.addEventListener('click', function () {
504 try {
505 var content = textSpan.textContent;
506 navigator.clipboard.writeText(content);
507 button.classList.add('mwai-animate');
508 setTimeout(function () {
509 button.classList.remove('mwai-animate');
510 }, 1000);
511 }
512 catch (err) {
513 console.warn('Not allowed to copy to clipboard. Make sure your website uses HTTPS.');
514 }
515 });
516 }
517
518 conversation.appendChild(div);
519
520 if (typewriter) {
521 if (role === 'assistant' && text !== startSentence && !replay) {
522 let typewriter = new Typewriter(textSpan, {
523 deleteSpeed: 50, delay: 25, loop: false, cursor: '', autoStart: true,
524 wrapperClassName: 'mwai-typewriter',
525 });
526 typewriter.typeString(text).start().callFunction((state) => {
527 state.elements.cursor.setAttribute('hidden', 'hidden');
528 typewriter.stop();
529 });
530 }
531 }
532
533 conversation.scrollTop = conversation.scrollHeight;
534 setButtonText();
535
536 // Syntax coloring
537 if (typeof hljs !== 'undefined') {
538 document.querySelectorAll('pre code').forEach((el) => {
539 hljs.highlightElement(el);
540 });
541 }
542 }
543
544 function buildPrompt(last = 15) {
545 let prompt = context ? (context + '\n\n') : '';
546 memorizedChat = memorizedChat.slice(-last);
547
548 // Casually fine tuned, let's use the last question
549 if (isCasuallyFineTuned) {
550 let lastLine = memorizedChat[memorizedChat.length - 1];
551 prompt = lastLine.content + promptEnding;
552 return prompt;
553 }
554
555 // Otherwise let's compile the latest conversation
556 let conversation = memorizedChat.map(x => x.who + x.content);
557 prompt += conversation.join('\n');
558 prompt += '\n' + rawAiName;
559 return prompt;
560 }
561
562 // Function to request the completion
563 function onSendClick() {
564 let input = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-input textarea');
565 let inputText = input.value.trim();
566
567 // Reset the conversation if empty
568 if (inputText === '') {
569 document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-conversation').innerHTML = '';
570 localStorage.removeItem('mwai-chat-<?php echo esc_attr( $id ) ?>');
571 memorizedChat = [];
572 memorizedChat.push({ role: 'assistant', content: startSentence, who: rawAiName, html: startSentence });
573 addReply(startSentence, 'assistant');
574 return;
575 }
576
577 // Disable the button
578 var button = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-input button');
579 button.disabled = true;
580
581 // Add the user reply
582 memorizedChat.push({ role: 'user', content: inputText, who: rawUserName, html: inputText });
583 addReply(inputText, 'user');
584 input.value = '';
585 input.setAttribute('rows', 1);
586 input.disabled = true;
587
588 let prompt = buildPrompt(maxSentences);
589
590 const data = mode === 'images' ? {
591 env, session: session,
592 prompt: inputText, rawInput: inputText,
593 model: model, maxResults, apiKey: apiKey,
594 } : {
595 env, session: session,
596 prompt: prompt, context: context,
597 messages: memorizedChat, rawInput: inputText,
598 userName: userName, aiName: aiName,
599 model: model, temperature: temperature, maxTokens: maxTokens, maxResults: 1, apiKey: apiKey,
600 embeddingsIndex: embeddingsIndex, stop: stop,
601 };
602
603 // Start the timer
604 const stopTimer = injectTimer(button);
605
606 // Send the request
607 if (isDebugMode) {
608 console.log('[BOT] Sent: ', data);
609 }
610 fetch(apiURL, { method: 'POST', headers: {
611 'Content-Type': 'application/json',
612 'X-WP-Nonce': restNonce,
613 },
614 body: JSON.stringify(data)
615 })
616 .then(response => response.json())
617 .then(data => {
618 if (isDebugMode) {
619 console.log('[BOT] Recv: ', data);
620 }
621 if (!data.success) {
622 addReply(data.message, 'system');
623 }
624 else {
625 let html = data.images ? data.images : data.html;
626 memorizedChat.push({ role: 'assistant', content: data.answer, who: rawAiName, html: html });
627 addReply(html, 'assistant');
628 }
629 button.disabled = false;
630 input.disabled = false;
631 stopTimer();
632
633 // Only focus only on desktop (to avoid the mobile keyboard to kick-in)
634 if (!isMobile) {
635 input.focus();
636 }
637 })
638 .catch(error => {
639 console.error(error);
640 button.disabled = false;
641 input.disabled = false;
642 stopTimer();
643 });
644 }
645
646 // Keep the textarea height in sync with the content
647 function resizeTextArea(ev) {
648 ev.target.style.height = 'auto';
649 ev.target.style.height = ev.target.scrollHeight + 'px';
650 }
651
652 // Keep the textarea height in sync with the content
653 function delayedResizeTextArea(ev) {
654 window.setTimeout(resizeTextArea, 0, event);
655 }
656
657 // Init the chatbot
658 function initMeowChatbot() {
659 var input = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-input textarea');
660 var button = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-input button');
661
662 input.addEventListener('keypress', (event) => {
663 let text = event.target.value;
664 if (event.keyCode === 13 && !text.length && !event.shiftKey) {
665 event.preventDefault();
666 return;
667 }
668 if (event.keyCode === 13 && text.length && !event.shiftKey) {
669 onSendClick();
670 }
671 });
672 input.addEventListener('keydown', (event) => {
673 var rows = input.getAttribute('rows');
674 if (event.keyCode === 13 && event.shiftKey) {
675 var lines = input.value.split('\n').length + 1;
676 //mwaiSetTextAreaHeight(input, lines);
677 }
678 });
679 input.addEventListener('keyup', (event) => {
680 var rows = input.getAttribute('rows');
681 var lines = input.value.split('\n').length ;
682 //mwaiSetTextAreaHeight(input, lines);
683 setButtonText();
684 });
685
686 input.addEventListener('change', resizeTextArea, false);
687 input.addEventListener('cut', delayedResizeTextArea, false);
688 input.addEventListener('paste', delayedResizeTextArea, false);
689 input.addEventListener('drop', delayedResizeTextArea, false);
690 input.addEventListener('keydown', delayedResizeTextArea, false);
691
692 button.addEventListener('click', (event) => {
693 onSendClick();
694 });
695
696 // If window, add event listener to mwai-open-button and mwai-close-button
697 if ( isWindow ) {
698 var openButton = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-open-button');
699 openButton.addEventListener('click', (event) => {
700 var chat = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?>');
701 chat.classList.add('mwai-open');
702 // Only focus only on desktop (to avoid the mobile keyboard to kick-in)
703 if (!isMobile) {
704 input.focus();
705 }
706 });
707 var closeButton = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-close-button');
708 closeButton.addEventListener('click', (event) => {
709 var chat = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?>');
710 chat.classList.remove('mwai-open');
711 });
712 if (isFullscreen) {
713 var resizeButton = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-resize-button');
714 resizeButton.addEventListener('click', (event) => {
715 var chat = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?>');
716 chat.classList.toggle('mwai-fullscreen');
717 });
718 }
719 }
720
721 // Get back the previous chat if any for the same ID
722 var chatHistory = [];
723 if (memorizeChat) {
724 chatHistory = localStorage.getItem('mwai-chat-<?php echo esc_attr( $id ) ?>');
725 if (chatHistory) {
726 memorizedChat = JSON.parse(chatHistory);
727 memorizedChat = memorizedChat.filter(x => x && x.html && x.role);
728 memorizedChat.forEach(x => {
729 addReply(x.html, x.role, true);
730 });
731 }
732 else {
733 memorizedChat = [];
734 }
735 }
736 if (memorizedChat.length === 0) {
737 memorizedChat.push({ role: 'assistant', content: startSentence, who: rawAiName, html: startSentence });
738 addReply(startSentence, 'assistant');
739 }
740 }
741
742 // Let's go totally meoooow on this!
743 initMeowChatbot();
744 })();
745 </script>
746
747 <?php
748 $output = ob_get_contents();
749 ob_end_clean();
750 $output = apply_filters( 'mwai_chatbot', $output, $atts );
751 return $output;
752 }
753 }
754