PluginProbe ʕ •ᴥ•ʔ
AI Engine – The Chatbot, AI Framework & MCP for WordPress / 1.3.93
AI Engine – The Chatbot, AI Framework & MCP for WordPress v1.3.93
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.php 3 years ago chatbot_logs.php 3 years ago discussions.php 3 years ago
chatbot.php
809 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_Discussions();
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 . '/themes/ChatGPT.module.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 $context = null;
89 $embeddingsIndex = $params['embeddingsIndex'];
90 if ( $query->mode === 'chat' && !empty( $embeddingsIndex ) ) {
91 $context = apply_filters( 'mwai_context_search', $query, $embeddingsIndex );
92 if ( !empty( $context ) ) {
93 $query->injectContext( $context['content'] );
94 }
95 }
96
97 $answer = $this->core->ai->run( $query );
98 $rawText = $answer->result;
99 $extra = [ 'embeddings' => $context['embeddings'] ];
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 $service = $atts['service'];
304 $apiKey = $atts['api_key'];
305
306 // Variables
307 $apiUrl = get_rest_url( null, $mode === 'images' ? 'ai-chatbot/v1/imagesbot' : 'ai-chatbot/v1/chat' );
308 $baseClasses = 'mwai-chat';
309 $baseClasses .= ( $window ? ' mwai-window' : '' );
310 $baseClasses .= ( !$window && $fullscreen ? ' mwai-fullscreen' : '' );
311 $baseClasses .= ( $style === 'chatgpt' ? ' mwai-chatgpt' : '' );
312 $baseClasses .= ( $window && !empty( $iconPosition ) ? ( ' mwai-' . $iconPosition ) : '' );
313
314 // Output CSS
315 ob_start();
316 $style_content = '';
317 if ( $style === 'chatgpt' ) {
318 $style_content = $this->chatgpt_style( $id, $style );
319 }
320 $style_content = apply_filters( 'mwai_chatbot_style', $style_content, $id );
321 echo wp_kses( $style_content, array( 'style' => array() ) );
322
323 // Output HTML & CSS
324 $chatStyles = $this->core->get_option( 'shortcode_chat_styles' );
325 $iconUrl = MWAI_URL . '/images/chat-green.svg';
326 if ( !empty( $icon ) ) {
327 $iconUrl = $icon;
328 }
329 else if ( !empty( $chatStyles ) && isset( $chatStyles['icon'] ) ) {
330 $url = $chatStyles['icon'];
331 $iconUrl = $this->core->isUrl( $url ) ? $url : ( MWAI_URL . 'images/' . $chatStyles['icon'] );
332 }
333 ?>
334 <div id="mwai-chat-<?php echo esc_attr( $id ); ?>" class="<?php echo esc_attr( $baseClasses ); ?>">
335 <?php if ( $window ) : ?>
336 <div class="mwai-open-button">
337 <?php if ( !empty( $iconText ) ) : ?>
338 <div class="mwai-icon-text"><?php echo esc_html( $iconText ); ?></div>
339 <?php endif; ?>
340 <img width="64" height="64" alt="<?php echo esc_attr( $iconAlt ); ?>" src="<?php echo esc_url( $iconUrl ); ?>" />
341 </div>
342 <div class="mwai-header">
343 <div class="mwai-buttons">
344 <?php if ( $fullscreen ) : ?>
345 <div class="mwai-resize-button"></div>
346 <?php endif; ?>
347 <div class="mwai-close-button"></div>
348 </div>
349 </div>
350 <?php endif; ?>
351 <div class="mwai-content">
352 <div class="mwai-conversation">
353 </div>
354 <div class="mwai-input">
355 <textarea rows="1" maxlength="<?php echo (int)$textInputMaxLength; ?>" placeholder="<?php echo esc_attr( $textInputPlaceholder ); ?>"></textarea>
356 <button><span><?php echo esc_html( $textSend ); ?></span></button>
357 </div>
358 <?php if ( !empty( $textCompliance ) ) : ?>
359 <div class="mwai-compliance">
360 ⚠️ <?php echo wp_kses_post( $textCompliance ); ?>
361 </div>
362 <?php endif; ?>
363 </div>
364 </div>
365
366 <script>
367 (function () {
368 let isMobile = window.matchMedia( "only screen and (max-width: 760px)" ).matches;
369 let isWindow = <?php echo $window ? 'true' : 'false' ?>;
370 let isDebugMode = <?php echo $debugMode ? 'true' : 'false' ?>;
371 let isFullscreen = <?php echo $fullscreen ? 'true' : 'false' ?>;
372 let restNonce = '<?php echo esc_attr( $rest_nonce ) ?>';
373 let apiURL = '<?php echo esc_url( $apiUrl ) ?>';
374 let isCasuallyFineTuned = <?php echo $casuallyFineTuned ? 'true' : 'false' ?>;
375 let rawUserName = '<?php echo esc_attr( $rawUserName ) ?>';
376 let rawAiName = '<?php echo esc_attr( $rawAiName ) ?>';
377 let userName = '<?php echo wp_kses_post( $userName ) ?>';
378 let aiName = '<?php echo wp_kses_post( $aiName ) ?>';
379 let sysName = '<?php echo wp_kses_post( $sysName ) ?>';
380 let env = '<?php echo esc_attr( $env ) ?>';
381 let apiKey = '<?php echo esc_attr( $apiKey ) ?>';
382 let service = '<?php echo esc_attr( $service ) ?>';
383 let session = '<?php echo esc_attr( $sessionId ) ?>';
384 let mode = '<?php echo esc_attr( $mode ) ?>';
385 let model = '<?php echo esc_attr( $model ) ?>';
386 let context = isCasuallyFineTuned ? null : '<?php echo esc_attr( $context ) ?>';
387 let embeddingsIndex = '<?php echo esc_attr( $embeddingsIndex ) ?>';
388 let promptEnding = '<?php echo esc_attr( $promptEnding ) ?>';
389 let stop = '<?php echo esc_attr( $completionEnding ) ?>';
390 let startSentence = '<?php echo esc_attr( $startSentence ) ?>';
391 let maxSentences = <?php echo (int)$maxSentences ?>;
392 let memorizeChat = <?php echo $memorizeChat ? 'true' : 'false' ?>;
393 let maxTokens = <?php echo (int)$maxTokens ?>;
394 let maxResults = <?php echo (int)$maxResults ?>;
395 let temperature = <?php echo str_replace(',', '.', (float)$temperature) ?>;
396 let typewriter = <?php echo $typewriter ? 'true' : 'false' ?>;
397 let copyButton = <?php echo $copyButton ? 'true' : 'false' ?>;
398 let clientId = randomStr();
399 let memorizedChat = { clientId, messages: [] };
400
401 if (isDebugMode) {
402 window.mwai_<?php echo esc_attr( $id ) ?> = {
403 memorizedChat: memorizedChat,
404 parameters: { mode: mode, model, temperature, maxTokens, context: context, startSentence,
405 isMobile, isWindow, isFullscreen, isCasuallyFineTuned, memorizeChat, maxSentences,
406 rawUserName, rawAiName, embeddingsIndex, typewriter, maxResults, userName, aiName, env, apiKey, service, session
407 }
408 };
409 }
410
411 function randomStr() {
412 return Math.random().toString(36).substring(2);
413 }
414
415 // Set button text
416 function setButtonText() {
417 let input = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-input textarea');
418 let button = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-input button');
419 let buttonSpan = button.querySelector('span');
420 if (memorizedChat.messages.length < 2) {
421 buttonSpan.innerHTML = '<?php echo esc_html( $textSend ); ?>';
422 }
423 else if (!input.value.length) {
424 button.classList.add('mwai-clear');
425 buttonSpan.innerHTML = '<?php echo esc_html( $textClear ); ?>';
426 }
427 else {
428 button.classList.remove('mwai-clear');
429 buttonSpan.innerHTML = '<?php echo esc_html( $textSend ); ?>';
430 }
431 }
432
433 // Inject timer
434 function injectTimer(element) {
435 let intervalId;
436 let startTime = new Date();
437 let timerElement = null;
438
439 function updateTimer() {
440 let now = new Date();
441 let timer = Math.floor((now - startTime) / 1000);
442 if (!timerElement) {
443 if (timer > 0.5) {
444 timerElement = document.createElement('div');
445 timerElement.classList.add('mwai-timer');
446 element.appendChild(timerElement);
447 }
448 }
449 if (timerElement) {
450 let minutes = Math.floor(timer / 60);
451 let seconds = timer - (minutes * 60);
452 seconds = seconds < 10 ? '0' + seconds : seconds;
453 let display = minutes + ':' + seconds;
454 timerElement.innerHTML = display;
455 }
456 }
457
458 intervalId = setInterval(updateTimer, 500);
459
460 return function stopTimer() {
461 clearInterval(intervalId);
462 if (timerElement) {
463 timerElement.remove();
464 }
465 };
466 }
467
468 // Push the reply in the conversation
469 function addReply(text, role = 'user', replay = false) {
470 var conversation = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-conversation');
471
472 if (memorizeChat) {
473 localStorage.setItem('mwai-chat-<?php echo esc_attr( $id ) ?>', JSON.stringify(memorizedChat));
474 }
475
476 // If text is array, then it's image URLs. Let's create a simple gallery in HTML in $text.
477 if (Array.isArray(text)) {
478 var newText = '<div class="mwai-gallery">';
479 for (var i = 0; i < text.length; i++) {
480 newText += '<a href="' + text[i] + '" target="_blank"><img src="' + text[i] + '" />';
481 }
482 text = newText + '</div>';
483 }
484
485 var mwaiClasses = ['mwai-reply'];
486 if (role === 'assistant') {
487 mwaiClasses.push('mwai-ai');
488 }
489 else if (role === 'system') {
490 mwaiClasses.push('mwai-system');
491 }
492 else {
493 mwaiClasses.push('mwai-user');
494 }
495 var div = document.createElement('div');
496 div.classList.add(...mwaiClasses);
497 var nameSpan = document.createElement('span');
498 nameSpan.classList.add('mwai-name');
499 if (role === 'assistant') {
500 nameSpan.innerHTML = aiName;
501 }
502 else if (role === 'system') {
503 nameSpan.innerHTML = sysName;
504 }
505 else {
506 nameSpan.innerHTML = userName;
507 }
508 var textSpan = document.createElement('span');
509 textSpan.classList.add('mwai-text');
510 textSpan.innerHTML = text;
511 div.appendChild(nameSpan);
512 div.appendChild(textSpan);
513
514 // Copy Button
515 if (copyButton && role === 'assistant') {
516 var button = document.createElement('div');
517 button.classList.add('mwai-copy-button');
518 var firstElement = document.createElement('div');
519 firstElement.classList.add('mwai-copy-button-one');
520 var secondElement = document.createElement('div');
521 secondElement.classList.add('mwai-copy-button-two');
522 button.appendChild(firstElement);
523 button.appendChild(secondElement);
524 div.appendChild(button);
525 button.addEventListener('click', function () {
526 try {
527 var content = textSpan.textContent;
528 navigator.clipboard.writeText(content);
529 button.classList.add('mwai-animate');
530 setTimeout(function () {
531 button.classList.remove('mwai-animate');
532 }, 1000);
533 }
534 catch (err) {
535 console.warn('Not allowed to copy to clipboard. Make sure your website uses HTTPS.');
536 }
537 });
538 }
539
540 conversation.appendChild(div);
541
542 if (typewriter) {
543 if (role === 'assistant' && text !== startSentence && !replay) {
544 let typewriter = new Typewriter(textSpan, {
545 deleteSpeed: 50, delay: 25, loop: false, cursor: '', autoStart: true,
546 wrapperClassName: 'mwai-typewriter',
547 });
548 typewriter.typeString(text).start().callFunction((state) => {
549 state.elements.cursor.setAttribute('hidden', 'hidden');
550 typewriter.stop();
551 });
552 }
553 }
554
555 conversation.scrollTop = conversation.scrollHeight;
556 setButtonText();
557
558 // Syntax coloring
559 if (typeof hljs !== 'undefined') {
560 document.querySelectorAll('pre code').forEach((el) => {
561 hljs.highlightElement(el);
562 });
563 }
564 }
565
566 function buildPrompt(last = 15) {
567 let prompt = context ? (context + '\n\n') : '';
568 memorizedChat.messages = memorizedChat.messages.slice(-last);
569
570 // Casually fine tuned, let's use the last question
571 if (isCasuallyFineTuned) {
572 let lastLine = memorizedChat.messages[memorizedChat.messages.length - 1];
573 prompt = lastLine.content + promptEnding;
574 return prompt;
575 }
576
577 // Otherwise let's compile the latest conversation
578 let conversation = memorizedChat.messages.map(x => x.who + x.content);
579 prompt += conversation.join('\n');
580 prompt += '\n' + rawAiName;
581 return prompt;
582 }
583
584 // Function to request the completion
585 function onSendClick() {
586 let input = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-input textarea');
587 let inputText = input.value.trim();
588
589 // Reset the conversation if empty
590 if (inputText === '') {
591 clientId = randomStr();
592 document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-conversation').innerHTML = '';
593 localStorage.removeItem('mwai-chat-<?php echo esc_attr( $id ) ?>')
594 memorizedChat = { clientId: clientId, messages: [] };
595 memorizedChat.messages.push({
596 id: randomStr(),
597 role: 'assistant',
598 content: startSentence,
599 who: rawAiName,
600 html: startSentence
601 });
602 addReply(startSentence, 'assistant');
603 return;
604 }
605
606 // Disable the button
607 var button = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-input button');
608 button.disabled = true;
609
610 // Add the user reply
611 memorizedChat.messages.push({
612 id: randomStr(),
613 role: 'user',
614 content: inputText,
615 who: rawUserName,
616 html: inputText
617 });
618 addReply(inputText, 'user');
619 input.value = '';
620 input.setAttribute('rows', 1);
621 input.disabled = true;
622
623 let prompt = buildPrompt(maxSentences);
624
625 const data = mode === 'images' ? {
626 env, session: session,
627 prompt: inputText, rawInput: inputText,
628 model: model, maxResults, apiKey: apiKey, service: service, clientId: clientId,
629 } : {
630 env, session: session,
631 prompt: prompt, context: context,
632 messages: memorizedChat.messages, rawInput: inputText,
633 userName: userName, aiName: aiName,
634 model: model, temperature: temperature, maxTokens: maxTokens, maxResults: 1, apiKey: apiKey, service: service, embeddingsIndex: embeddingsIndex, stop: stop, clientId: clientId,
635 };
636
637 // Start the timer
638 const stopTimer = injectTimer(button);
639
640 // Send the request
641 if (isDebugMode) {
642 console.log('[BOT] Sent: ', data);
643 }
644 fetch(apiURL, { method: 'POST', headers: {
645 'Content-Type': 'application/json',
646 'X-WP-Nonce': restNonce,
647 },
648 body: JSON.stringify(data)
649 })
650 .then(response => response.json())
651 .then(data => {
652 if (isDebugMode) {
653 console.log('[BOT] Recv: ', data);
654 }
655 if (!data.success) {
656 addReply(data.message, 'system');
657 }
658 else {
659 let html = data.images ? data.images : data.html;
660 memorizedChat.messages.push({
661 id: randomStr(),
662 role: 'assistant',
663 content: data.answer,
664 who: rawAiName,
665 html: html
666 });
667 addReply(html, 'assistant');
668 }
669 button.disabled = false;
670 input.disabled = false;
671 stopTimer();
672
673 // Only focus only on desktop (to avoid the mobile keyboard to kick-in)
674 if (!isMobile) {
675 input.focus();
676 }
677 })
678 .catch(error => {
679 console.error(error);
680 button.disabled = false;
681 input.disabled = false;
682 stopTimer();
683 });
684 }
685
686 // Keep the textarea height in sync with the content
687 function resizeTextArea(ev) {
688 ev.target.style.height = 'auto';
689 ev.target.style.height = ev.target.scrollHeight + 'px';
690 }
691
692 // Keep the textarea height in sync with the content
693 function delayedResizeTextArea(ev) {
694 window.setTimeout(resizeTextArea, 0, event);
695 }
696
697 // Init the chatbot
698 function initMeowChatbot() {
699 var input = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-input textarea');
700 var button = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-input button');
701
702 input.addEventListener('keypress', (event) => {
703 let text = event.target.value;
704 if (event.keyCode === 13 && !text.length && !event.shiftKey) {
705 event.preventDefault();
706 return;
707 }
708 if (event.keyCode === 13 && text.length && !event.shiftKey) {
709 onSendClick();
710 }
711 });
712 input.addEventListener('keydown', (event) => {
713 var rows = input.getAttribute('rows');
714 if (event.keyCode === 13 && event.shiftKey) {
715 var lines = input.value.split('\n').length + 1;
716 //mwaiSetTextAreaHeight(input, lines);
717 }
718 });
719 input.addEventListener('keyup', (event) => {
720 var rows = input.getAttribute('rows');
721 var lines = input.value.split('\n').length ;
722 //mwaiSetTextAreaHeight(input, lines);
723 setButtonText();
724 });
725
726 input.addEventListener('change', resizeTextArea, false);
727 input.addEventListener('cut', delayedResizeTextArea, false);
728 input.addEventListener('paste', delayedResizeTextArea, false);
729 input.addEventListener('drop', delayedResizeTextArea, false);
730 input.addEventListener('keydown', delayedResizeTextArea, false);
731
732 button.addEventListener('click', (event) => {
733 onSendClick();
734 });
735
736 // If window, add event listener to mwai-open-button and mwai-close-button
737 if ( isWindow ) {
738 var openButton = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-open-button');
739 openButton.addEventListener('click', (event) => {
740 var chat = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?>');
741 chat.classList.add('mwai-open');
742 // Only focus only on desktop (to avoid the mobile keyboard to kick-in)
743 if (!isMobile) {
744 input.focus();
745 }
746 });
747 var closeButton = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-close-button');
748 closeButton.addEventListener('click', (event) => {
749 var chat = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?>');
750 chat.classList.remove('mwai-open');
751 });
752 if (isFullscreen) {
753 var resizeButton = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?> .mwai-resize-button');
754 resizeButton.addEventListener('click', (event) => {
755 var chat = document.querySelector('#mwai-chat-<?php echo esc_attr( $id ) ?>');
756 chat.classList.toggle('mwai-fullscreen');
757 });
758 }
759 }
760
761 // Get back the previous chat if any for the same ID
762 var chatHistory = [];
763 if (memorizeChat) {
764 chatHistory = localStorage.getItem('mwai-chat-<?php echo esc_attr( $id ) ?>');
765 if (chatHistory) {
766 memorizedChat = JSON.parse(chatHistory);
767 if (memorizedChat && memorizedChat.clientId && memorizedChat.messages) {
768 clientId = memorizedChat.clientId;
769 memorizedChat.messages = memorizedChat.messages.filter(x => x && x.html && x.role);
770 memorizedChat.messages.forEach(x => {
771 addReply(x.html, x.role, true);
772 });
773 }
774 else {
775 memorizedChat = null;
776 }
777 }
778 if (!memorizedChat) {
779 memorizedChat = {
780 clientId: clientId,
781 messages: []
782 };
783 }
784 }
785 if (memorizedChat.messages.length === 0) {
786 memorizedChat.messages.push({
787 id: randomStr(),
788 role: 'assistant',
789 content: startSentence,
790 who: rawAiName,
791 html: startSentence
792 });
793 addReply(startSentence, 'assistant');
794 }
795 }
796
797 // Let's go totally meoooow on this!
798 initMeowChatbot();
799 })();
800 </script>
801
802 <?php
803 $output = ob_get_contents();
804 ob_end_clean();
805 $output = apply_filters( 'mwai_chatbot', $output, $atts );
806 return $output;
807 }
808 }
809