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