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