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