_setTbl('chatlogs'); } public function getChatbotShowRules() { if (is_null($this->_chatbotShowRules)) { $option = get_option($this->_optionChatbotShow); $this->_chatbotShowRules = $option && is_array($option) ? $option : false; } return $this->_chatbotShowRules; } public function setChatbotShowRules( $rules ) { update_option($this->_optionChatbotShow, empty($rules) ? '' : $rules); } public function isDeleteByCancel() { return false; } public function canUnpublish() { return true; } public function unpublishEtaps( $taskId ) { WaicFrame::_()->getModule('workspace')->getModel('tasks')->updateTask($taskId, array('status' => 6)); return false; } public function publishResults( $taskId, $publish ) { WaicFrame::_()->getModule('workspace')->getModel('tasks')->updateTask($taskId, array('status' => 4)); return false; } public function getColorSchemes( $default = false ) { $schemes = array('#2D3E70', '#6366f1', '#10b981', '#f59e0b', '#ef4444', '#8b5cf6', '#ec4899', '#06b6d4'); if ($default) { $schemes[] = '#2D3E50'; } return $schemes; } public function getDisplayPages() { $pages = array( 'home' => __('Home', 'ai-copilot-content-generator'), 'account' => __('Account', 'ai-copilot-content-generator'), 'blog' => __('Blog page', 'ai-copilot-content-generator'), 'blog_post' => __('Blog posts', 'ai-copilot-content-generator'), 'blog_сat' => __('Blog categories', 'ai-copilot-content-generator'), 'blog_tag' => __('Blog tags', 'ai-copilot-content-generator'), ); if (WaicUtils::isWooCommercePluginActivated()) { $pages['shop'] = __('Shop', 'ai-copilot-content-generator'); $pages['product'] = __('Product Pages', 'ai-copilot-content-generator'); $pages['product_cat'] = __('Product categories', 'ai-copilot-content-generator'); $pages['product_tag'] = __('Product tags', 'ai-copilot-content-generator'); $pages['cart'] = __('Cart', 'ai-copilot-content-generator'); $pages['checkout'] = __('Checkout', 'ai-copilot-content-generator'); } return $pages; } public function clearEtaps( $taskId, $ids = false, $withContent = true ) { WaicFastIndexer::deleteTask($taskId); if ($withContent) { WaicFrame::_()->getModule('workspace')->getModel('history')->deleteHistory($taskId); $query = 'DELETE t FROM @__chatlogs t WHERE NOT EXISTS(SELECT 1 FROM @__history h WHERE h.id=t.his_id)'; WaicDb::query($query); } return true; } public function setChatbotParams( $taskId, $oldTask ) { $task = WaicFrame::_()->getModule('workspace')->getModel('tasks')->getTask($taskId); if (!$task || empty($task)) { return ; } $params = WaicUtils::getArrayValue($task, 'params', array(), 2); $general = WaicUtils::getArrayValue($params, 'general', array(), 2); $rules = $this->getChatbotShowRules(); $rule = array(); $display = WaicUtils::getArrayValue($general, 'display_on'); $pages = array(); if ('all' == $display) { $pages = array('all'); } else if ('specific' == $display) { $pages = WaicUtils::getArrayValue($general, 'display_pages', array(), 2); $ids = WaicUtils::getArrayValue($general, 'display_ids'); if (!empty($ids)) { $pages = array_merge($pages, WaicUtils::controlNumericValues(explode(',', $ids))); } } if (!empty($pages)) { $rule['display'] = $pages; } $hide = WaicUtils::getArrayValue($general, 'hide_on'); $pages = array(); if ('specific' == $hide) { $pages = WaicUtils::getArrayValue($general, 'hide_pages', array(), 2); $ids = WaicUtils::getArrayValue($general, 'hide_ids'); if (!empty($ids)) { $pages = array_merge($pages, WaicUtils::controlNumericValues(explode(',', $ids))); } } if (!empty($pages)) { $rule['hide'] = $pages; } if (empty($rule)) { unset($rules[$taskId]); } else { $rules[$taskId] = $rule; } $this->setChatbotShowRules($rules); $oldParams = WaicUtils::getArrayValue($oldTask, 'params', array()); if (is_string($oldParams)) { $oldParams = WaicUtils::jsonDecode($oldParams); } $oldFast = is_array($oldParams) ? WaicUtils::getArrayValue($oldParams, 'fast_path', array(), 2) : array(); $newFast = WaicUtils::getArrayValue($params, 'fast_path', array(), 2); WaicFastIndexer::syncTask($taskId, $newFast, $oldFast); return WaicDispatcher::applyFilters('setChatbotParams', false, $oldTask, $task); } public function isActiveChat( $taskId = 0, $userId = 0, $ip = '', $mode = 0, $lifetime = 0 ) { $query = 'SELECT 1 FROM @__history WHERE task_id=%d AND mode=%d AND user_id=%d'; $args = array((int) $taskId, (int) $mode, (int) $userId); if (empty($userId)) { $query .= ' AND ip=%s'; $args[] = (string) $ip; } if (!empty($lifetime)) { $query .= ' AND created>DATE_SUB(%s, INTERVAL %d MINUTE)'; $args[] = WaicUtils::getTimestampDB(); $args[] = (int) $lifetime; } $query .= ' LIMIT 1'; return ( WaicDb::get($query, 'one', ARRAY_A, $args) == 1 ); } public function getUserChatLog( $taskId = 0, $userId = 0, $ip = '', $mode = 0, $cnt = 0, $status = 0, $dd = false ) { $forDate = !empty($dd) && WaicUtils::checkDateTime($dd, 'Y-m-d'); $query = 'SELECT h.id as his_id, h.created, l.question, l.answer, h.status, l.file' . ' FROM @__history as h' . ' INNER JOIN @__chatlogs l ON (l.his_id=h.id)' . ' WHERE h.task_id=%d'; $args = array((int) $taskId); if (false !== $status) { $query .= ' AND h.status=%d'; $args[] = (int) $status; } $query .= ' AND h.mode=%d AND h.user_id=%d'; $args[] = (int) $mode; $args[] = (int) $userId; if (false !== $status) { $query .= ' AND l.status=0'; } if (empty($userId) || $forDate) { $query .= ' AND h.ip=%s'; $args[] = (string) $ip; } if ($forDate) { $query .= ' AND h.created BETWEEN %s AND %s'; $args[] = $dd . ' 00:00:00'; $args[] = $dd . ' 23:59:59'; } $query .= ' ORDER BY h.id' . ( empty($cnt) ? '' : ' DESC LIMIT ' . ( (int) $cnt ) ); $log = WaicDb::get($query, 'all', ARRAY_A, $args); if ($log && !empty($log)) { if (!empty($cnt)) { $log = array_reverse($log); } } else { $log = array(); } return $log; } public function deleteUserChatLog( $taskId = 0, $userId = 0, $ip = '', $mode = 0 ) { $query = 'DELETE l FROM @__chatlogs l' . ' INNER JOIN @__history h ON (l.his_id=h.id)' . ' WHERE h.status=%d AND h.task_id=%d AND h.mode=%d'; $args = array(0, (int) $taskId, (int) $mode); if (empty($userId)) { $query .= ' AND h.ip=%s'; $args[] = (string) $ip; } else { $query .= ' AND h.user_id=%d'; $args[] = (int) $userId; } WaicDb::queryPrepared($query, $args); return true; } public function resetUserChatLog( $taskId = 0, $userId = 0, $ip = '', $mode = 0 ) { $query = 'UPDATE @__chatlogs l' . ' INNER JOIN @__history h ON (l.his_id=h.id)' . ' SET l.status=%d' . ' WHERE h.status=%d AND h.task_id=%d AND h.mode=%d'; $args = array(9, 0, (int) $taskId, (int) $mode); if (empty($userId)) { $query .= ' AND h.ip=%s'; $args[] = (string) $ip; } else { $query .= ' AND h.user_id=%d'; $args[] = (int) $userId; } WaicDb::queryPrepared($query, $args); return true; } public function humanRequest( $taskId ) { $workspace = WaicFrame::_()->getModule('workspace'); $task = $workspace->getModel('tasks')->getTask($taskId); $params = WaicUtils::getArrayValue($task, 'params', array(), 2); $context = WaicUtils::getArrayValue($params, 'context', array(), 2); $newLog = array( 'answer' => WaicUtils::getArrayValue($context, 'human_request_message', __('Please provide your email address so we can assist you further. Our team will get back to you within 24 hours. Thank you!', 'ai-copilot-content-generator')), 'error' => 0, 'need_email' => 1, 'request' => 'human', 'plh_email' => WaicUtils::getArrayValue($context, 'plh_email'), ); return $newLog; } public function sendEmail( $email, $taskId, $mode, $request = '' ) { $workspace = WaicFrame::_()->getModule('workspace'); $task = $workspace->getModel('tasks')->getTask($taskId); if (!$task || empty($task)) { return array('question' => __('Unexpected error.', 'ai-copilot-content-generator'), 'error' => 1); } $params = WaicUtils::getArrayValue($task, 'params', array(), 2); $context = WaicUtils::getArrayValue($params, 'context', array(), 2); $isError = ( 'error' == $request ); $newLog = array( 'answer' => __('Email is not correct', 'ai-copilot-content-generator'), 'error' => $isError ? 1 : 0, 'need_email' => 1, ); if (!is_email($email)) { $newLog['answer'] = WaicUtils::getArrayValue($context, 'error_email', $newLog['answer']); return $newLog; } $user = wp_get_current_user(); $userId = $user ? $user->ID : 0; $isGuest = empty($userId); $ip = WaicUtils::getRealUserIp(); $history = array( 'task_id' => $taskId, 'user_id' => $userId, 'ip' => $ip, 'mode' => $mode, 'status' => 3, 'tokens' => 0, ); $newLog = array( 'his_id' => $workspace->getModel('history')->saveHistory($history), 'question' => $email, ); $this->insert($newLog); if (WaicUtils::getArrayValue($context, 'e_' . $request . '_request', 0, 1) != 1) { return $newLog; } $adminEmail = WaicUtils::getArrayValue($context, $request . '_admin_email'); if (empty($adminEmail)) { return $newLog; } $headers = array( 'Content-type: text/html; charset=utf-8', 'Content-Transfer-Encoding: 8bit', 'From: ' . get_option( 'woocommerce_email_from_name' ) . ' <' . get_option( 'woocommerce_email_from_address' ) . '>', ); $subject = $isError ? 'Chatbot Error Request' : 'Chatbot Human Request'; $message = 'A user has requested human assistance through the chatbot. Below are the details:' . PHP_EOL . 'User Login: ' . ( $isGuest ? 'guest' : $user->user_login ) . PHP_EOL . 'User Name: ' . ( $isGuest ? '' : $user->user_firstname . ' ' . $user->user_lastname ) . PHP_EOL . 'User Email: ' . $email . PHP_EOL . 'User IP: ' . $ip . PHP_EOL . 'Date & Time: ' . WaicUtils::getTimestampDB() . PHP_EOL . 'Chatbot Name: ' . $task['title'] . PHP_EOL . PHP_EOL . 'Conversation Log: ' . PHP_EOL ; $log = $this->getUserChatLog($taskId, $userId, $ip, 0, 0, false); foreach ($log as $log) { if (!empty($log['file'])) { $message .= 'User (' . $log['created'] . '): File uploaded' . PHP_EOL; } if (!empty($log['question'])) { $message .= 'User (' . $log['created'] . '): ' . ( 3 == $log['status'] ? 'EMAIL - ' : '' ) . $log['question'] . PHP_EOL; } if (!empty($log['answer'])) { $message .= 'Bot (' . $log['created'] . '): ' . ( empty($log['status']) ? '' : 'ERROR - ' ) . $log['answer'] . PHP_EOL; } } $message .= PHP_EOL . 'Please follow up with the user as soon as possible.' . PHP_EOL . PHP_EOL . 'Best regards,' . PHP_EOL . 'AIWU plugin'; if (!wp_mail($adminEmail, $subject, $message, $headers)) { WaicFrame::_()->pushError(__('Error by sending email', 'ai-copilot-content-generator')); } $newLog['answer'] = WaicUtils::getArrayValue($context, 'error_thank', __('Thank you', 'ai-copilot-content-generator')); $newLog['need_email'] = 0; $newLog['disable'] = $isError ? 1 : 0; return $newLog; } /*public function sendFile( $message, $files, $taskId, $mode ) { error_log('$files='.json_encode($files)); if (empty($files['img'])) { return array('question' => __('Empty File', 'ai-copilot-content-generator'), 'error' => 1); } $file = $files['img']; $error = $this->controlUploatedFile($file); if (!empty($error)) { return array('question' => $error, 'error' => 1); } $workspace = WaicFrame::_()->getModule('workspace'); $hisModel = $workspace->getModel('history'); $task = $workspace->getModel('tasks')->getTask($taskId); if (!$task) { return array('answer' => __('Unexpected error', 'ai-copilot-content-generator'), 'error' => 1); } $user = wp_get_current_user(); $userId = $user ? $user->ID : 0; $isGuest = empty($userId); $ip = WaicUtils::getRealUserIp(); $params = WaicUtils::getArrayValue($task, 'params', array(), 2); $openAI = $workspace->getModel('openai')->init($taskId, $userId, $ip, $mode); if (!$openAI) { return false; } //f"data:image/png;base64,{base64_image}"; $extension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION)); $f = base64_encode(file_get_contents($file['tmp_name'])); $fStr = 'data:image/' . $extension . ";base64,{$f}"; //if (empty($message)) //$message = 'Что это?'; $log = $this->sendMessage($message, $taskId, $mode, '', $fStr); return $log; $boundary = wp_generate_uuid4(); $options = WaicUtils::getArrayValue($params, 'api', array(), 2); $options['contentType'] = 'multipart/form-data; boundary=' . $boundary; error_log('ffffffffffff'); $openAI->setApiOptions($options); $opts = array( 'boundary' => $boundary, 'file_name' => sanitize_file_name($file['name']), 'file_data' => file_get_contents($file['tmp_name']), ); error_log(sanitize_file_name($file['name'])); $result = $openAI->sendFile($opts); $newLog = array( 'his_id' => $result['his_id'], 'question' => empty($result['file_id']) ? __('File not Uploaded', 'ai-copilot-content-generator') : __('File Uploaded', 'ai-copilot-content-generator'), 'answer' => $result['error'] ? $this->controlText($result['msg']) : '', 'typ' => 1, ); if (!empty($result['his_id'])) { $this->insert($newLog); } if (empty($result['file_id'])) { //$context = WaicUtils::getArrayValue($params, 'context', array(), 2); $newLog['tt'] = WaicUtils::getFormatedDateTime(WaicUtils::getTimestamp(), 'H:i'); //$msg = WaicUtils::getArrayValue($context, 'error_message'); $newLog['answer'] = ''; //if (!empty($msg)) { // $newLog['answer'] = $msg; //} return $newLog; } $message = 'Analyze the attached file with ID: ' . $result['file_id']; $log = $this->sendMessage( $message, $taskId, $mode, '', 2); if (!empty($log['error'])) { $log['question'] = $newLog['question']; } return $log; }*/ public function getFileString( $files, $maxSize ) { $result = array( 'file' => '', 'error' => '', ); if (empty($files['img'])) { $result['error'] = __('Empty File', 'ai-copilot-content-generator'); return $result; } $file = $files['img']; $tempPath = WaicUtils::getArrayValue($file, 'tmp_name'); try { $extensions = array('png', 'gif', 'jpeg', 'jpg'); $error = WaicUtils::controlUploatedFile($file, $extensions); if (!empty($error)) { $result['error'] = $error; return $result; } $size = (int) WaicUtils::getArrayValue($file, 'size', 0, 1); if ($size > $maxSize) { $result['error'] = __('File is too big!', 'ai-copilot-content-generator'); return $result; } $extension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION)); $content = file_get_contents($tempPath); if (false === $content) { $result['error'] = __('Unable to read file.', 'ai-copilot-content-generator'); return $result; } $result['file'] = 'data:image/' . $extension . ';base64,' . base64_encode($content); return $result; } finally { if (is_string($tempPath) && is_file($tempPath)) { wp_delete_file($tempPath); } } } public function sendMessage( $message, $taskId, $mode, $cAware = '', $files = false, $options = array() ) { $workspace = WaicFrame::_()->getModule('workspace'); $task = $workspace->getModel('tasks')->getTask($taskId); if (!$task || empty($task)) { return array('question' => __('Unexpected error.', 'ai-copilot-content-generator'), 'error' => 1); } $params = WaicUtils::getArrayValue($task, 'params', array(), 2); $file = ''; if ($files) { $api = WaicUtils::getArrayValue($params, 'api', array(), 2); $files = $this->getFileString($files, WaicUtils::getArrayValue($api, 'max_file_size', 5, 1) * 1048576); if (!empty($files['error'])) { return array('question' => $files['error'], 'error' => 1); } $file = $files['file']; } if (isset($options['user_id'])) { $user = empty($options['user_id']) ? false : get_user_by('id', (int) $options['user_id']); } else { $user = wp_get_current_user(); } $userId = $user ? $user->ID : 0; $isGuest = empty($userId); $ip = isset($options['ip']) ? $options['ip'] : WaicUtils::getRealUserIp(); $chatId = sanitize_key((string) WaicUtils::getArrayValue($options, 'chat_id')); $sessionId = empty($chatId) ? 'chat-' . (int) $taskId . '-' . substr(md5($userId . '|' . $ip . '|' . (int) $mode), 0, 16) : 'chat-' . (int) $taskId . '-' . $chatId; $hisModel = $workspace->getModel('history'); $general = WaicUtils::getArrayValue($params, 'general', array(), 2); $context = WaicUtils::getArrayValue($params, 'context', array(), 2); $tools = WaicUtils::getArrayValue($params, 'tools', array(), 2); $callTools = array(); $fastResult = false; $maxCntMessages = WaicUtils::getArrayValue($general, 'max_messages', 10, 1); $log = array(); $isError = false; $allLimit = WaicUtils::getArrayValue($general, 'alltime_limit', 0, 1); if (!empty($allLimit)) { $where = array( 'task_id' => $taskId, 'mode' => 0, ); $cntTokens = $hisModel->getCountTokens($where); if ($cntTokens >= $allLimit) { $isError = true; $errMsg = 'Exceeds value: All-Time Limit = ' . $allLimit; $task = $workspace->getModel('tasks')->updateTask($taskId, array('status' => 6)); } } if (!$isError) { $monLimit = WaicUtils::getArrayValue($general, 'monthly_limit', 0, 1); $whereMonth = "created BETWEEN'" . WaicUtils::getConvertedDate(false, 'Y-m-01 00:00:00') . "' AND '" . WaicUtils::getConvertedDate(false, 'Y-m-t 23:59:59') . "'"; if (!empty($monLimit)) { $where = array( 'task_id' => $taskId, 'mode' => 0, 'additionalCondition' => $whereMonth, ); $cntTokens = $hisModel->getCountTokens($where); if ($cntTokens >= $monLimit) { $isError = true; $errMsg = 'Exceeds value: Monthly Limit = ' . $monLimit; $task = $workspace->getModel('tasks')->updateTask($taskId, array('status' => 8)); } } } if (!$isError) { if (empty($mode) && WaicUtils::getArrayValue($general, 'e_limit_roles', 0, 1)) { $rLimits = WaicUtils::getArrayValue($general, 'limit_roles', array(), 2); $uRoles = $isGuest ? array('') : $user->roles; $userLimit = ''; if (!empty($rLimits)) { foreach ($rLimits as $limit) { $role = WaicUtils::getArrayValue($limit, 'role'); if (in_array($role, $uRoles)) { $userLimit = WaicUtils::getArrayValue($limit, 'max', '', 1, false, true, true); break; } } } if ('' !== $userLimit) { if (!empty($userLimit)) { $where = array( 'task_id' => $taskId, 'mode' => 0, 'additionalCondition' => $whereMonth, ); if ($isGuest) { $where['ip'] = $ip; } $where['user_id'] = $userId; $cntTokens = $hisModel->getCountTokens($where); } else { $cntTokens = 1; } if ($cntTokens >= $userLimit) { $isError = true; $errMsg = 'Exceeds value: Role-Based Limit = ' . $userLimit; } } } } if (!$isError) { if (!empty($message)) { $maxInput = WaicUtils::getArrayValue($general, 'max_input', 0, 1); if (!empty($maxInput)) { $message = WaicUtils::mbsubstr($message, 0, $maxInput); } } $isActive = true; if (empty($mode)) { $lifetime = WaicUtils::getArrayValue($general, 'lifetime', 0, 1); $isActive = $this->isActiveChat($taskId, $userId, $ip, $mode, $lifetime); } if (isset($options['use_log'])) { $isActive = ( true == $options['use_log'] ); } $log = $isActive ? $this->getUserChatLog($taskId, $userId, $ip, $mode, $maxCntMessages) : array(); if (empty($file)) { $fastRequest = array( 'message' => $message, 'task' => $task, 'task_id' => $taskId, 'params' => $params, 'general' => $general, 'context' => $context, 'tools' => $tools, 'mode' => $mode, 'user_id' => $userId, 'ip' => $ip, 'session_id' => $sessionId, 'chat_id' => $chatId, 'history' => $log, 'aware' => $cAware, 'has_file' => false, 'identity' => WaicFastPath::requestIdentity($userId), ); $fastResult = WaicFastPath::maybeHandle($message, $taskId, $params, $fastRequest); $fastResult = WaicDispatcher::applyFilters('chatbotFastPath', $fastResult, $fastRequest); $fastResult = is_array($fastResult) && !empty($fastResult['handled']) ? $fastResult : false; } $log[] = array( 'question' => $message, 'answer' => '', 'file' => $file, ); if (!$fastResult) { $maxContext = WaicUtils::getArrayValue($general, 'max_context', 0, 1); $needContolLen = !empty($maxContext); $prompt = array(); $apiOptions = WaicUtils::getArrayValue($params, 'api', array(), 2); $instructions = "You are a chatbot. You strictly follow the given instructions and, while maintaining context, respond with exactly one message to the user's last message, without any additional text or unnecessary information." . PHP_EOL . PHP_EOL; $apiModel = WaicUtils::getArrayValue($apiOptions, 'model'); if (strpos($apiModel, 'ft:') === 0) { $instructions .= 'You are a fine-tuned AI assistant trained on a proprietary knowledge base. ' . PHP_EOL . 'You must always attempt to extract answers directly from this internal knowledge base first. ' . PHP_EOL . 'Only if no relevant information exists in the training data, you may infer an answer — but stay within the logical boundaries of the question. ' . PHP_EOL . 'Never invent facts that contradict your training. ' . PHP_EOL . 'If the question is vague, incomplete, or misleading — try to clarify or rephrase it before answering.' . PHP_EOL; } $instructions .= '- Tone of voice: ' . WaicUtils::getArrayValue($apiOptions, 'tone') . PHP_EOL . '- Instructions: ' . WaicUtils::getArrayValue($context, 'instructions') . PHP_EOL . 'Output format (must-follow): ' . PHP_EOL . '- Respond only in valid HTML. ' . PHP_EOL . '- Use only content-level HTML tags such as

,