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