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