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