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