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