db = $database; $this->telegram = $telegramService; $this->registrationStepsDir = __DIR__.'/../../logs'; // สร้าง directory ถ้ายังไม่มี if (!is_dir($this->registrationStepsDir)) { mkdir($this->registrationStepsDir, 0755, true); } } /** * ตรวจสอบว่าผู้ใช้สามารถใช้บอทได้หรือไม่ */ public function canUserAccessBot($chatUserId) { // ตรวจสอบว่าผู้ใช้ลงทะเบียนแล้วหรือยัง $stmt = $this->db->prepare("SELECT id FROM employees WHERE chat_user_id = ?"); $stmt->execute([$chatUserId]); $employee = $stmt->fetch(); if ($employee) { return true; // ลงทะเบียนแล้ว } // ถ้ายังไม่ลงทะเบียน ตรวจสอบว่าเปิดการลงทะเบียนด้วยตัวเองหรือไม่ return ALLOW_SELF_REGISTRATION; } /** * เริ่มกระบวนการลงทะเบียน */ public function startRegistration($chatId, $userId) { if (!ALLOW_SELF_REGISTRATION) { return $this->telegram->sendMessage($chatId, "❌ ขออภัย การลงทะเบียนด้วยตัวเองถูกปิดใช้งาน\n". "กรุณาติดต่อผู้ดูแลระบบเพื่อเพิ่มข้อมูลของคุณ" ); } // ตรวจสอบว่าผู้ใช้ลงทะเบียนแล้วหรือยัง $stmt = $this->db->prepare("SELECT id FROM employees WHERE chat_user_id = ?"); $stmt->execute([$userId]); if ($stmt->fetch()) { return $this->telegram->sendMessage($chatId, "✅ คุณได้ลงทะเบียนในระบบแล้ว"); } // เริ่มการลงทะเบียน $this->saveRegistrationStep($userId, $chatId, 'first_name', []); return $this->telegram->sendMessage($chatId, "🎯 *การลงทะเบียนในระบบลา*\n\n". "กรุณากรอกข้อมูลตามขั้นตอน:\n\n". "1️⃣ *ชื่อจริง* (ภาษาไทยหรือภาษาอังกฤษ)\n". "กรุณาพิมพ์ชื่อจริงของคุณ:", null, 'Markdown' ); } /** * จัดการข้อมูลที่ผู้ใช้ส่งมา */ public function handleUserInput($chatId, $userId, $messageText) { // ตรวจสอบ rate limit if (!RateLimiter::checkRateLimit($userId)) { SecurityHelper::logSecurityEvent('registration_rate_limit', $userId); $this->telegram->sendMessage($chatId, "⚠️ คุณส่งข้อความบ่อยเกินไป กรุณารอสักครู่แล้วลองใหม่" ); return true; // จัดการแล้ว } $step = $this->getCurrentRegistrationStep($userId, $chatId); if (!$step) { return false; // ไม่อยู่ในกระบวนการลงทะเบียน } // กรองข้อมูล input $messageText = SecurityHelper::sanitizeInput($messageText, 'text'); // ตรวจสอบความยาว if (strlen($messageText) > (defined('MAX_INPUT_LENGTH') ? MAX_INPUT_LENGTH : 500)) { $this->telegram->sendMessage($chatId, "❌ ข้อความยาวเกินไป กรุณากรอกข้อมูลให้สั้นลง" ); return true; } $stepData = $step['data']; switch ($step['currentStep']) { case 'first_name': return $this->handleFirstName($chatId, $userId, $messageText, $stepData); case 'last_name': return $this->handleLastName($chatId, $userId, $messageText, $stepData); case 'email': return $this->handleEmail($chatId, $userId, $messageText, $stepData); case 'confirm': return $this->handleConfirmation($chatId, $userId, $messageText, $stepData); default: $this->clearRegistrationStep($userId, $chatId); return false; } } /** * @param $chatId * @param $userId * @param $firstName * @param $stepData * @return mixed */ private function handleFirstName($chatId, $userId, $firstName, $stepData) { $firstName = SecurityHelper::sanitizeInput($firstName, 'name'); if (!SecurityHelper::validateInput($firstName, 'name', 2, 50)) { SecurityHelper::logSecurityEvent('invalid_first_name', $userId, ['input' => $firstName]); return $this->telegram->sendMessage($chatId, "❌ กรุณากรอกชื่อจริงให้ถูกต้อง (2-50 ตัวอักษร, ใช้ได้เฉพาะตัวอักษรไทย-อังกฤษ):" ); } $stepData['first_name'] = $firstName; $this->saveRegistrationStep($userId, $chatId, 'last_name', $stepData); $safeFirstName = SecurityHelper::escapeMarkdown($firstName); return $this->telegram->sendMessage($chatId, "✅ รับทราบชื่อจริง: *{$safeFirstName}*\n\n". "2️⃣ *นามสกุล* (ภาษาไทยหรือภาษาอังกฤษ)\n". "กรุณาพิมพ์นามสกุลของคุณ:", null, 'Markdown' ); } /** * @param $chatId * @param $userId * @param $lastName * @param $stepData * @return mixed */ private function handleLastName($chatId, $userId, $lastName, $stepData) { $lastName = SecurityHelper::sanitizeInput($lastName, 'name'); if (!SecurityHelper::validateInput($lastName, 'name', 2, 50)) { SecurityHelper::logSecurityEvent('invalid_last_name', $userId, ['input' => $lastName]); return $this->telegram->sendMessage($chatId, "❌ กรุณากรอกนามสกุลให้ถูกต้อง (2-50 ตัวอักษร, ใช้ได้เฉพาะตัวอักษรไทย-อังกฤษ):" ); } $stepData['last_name'] = $lastName; $this->saveRegistrationStep($userId, $chatId, 'email', $stepData); $safeLastName = SecurityHelper::escapeMarkdown($lastName); return $this->telegram->sendMessage($chatId, "✅ รับทราบนามสกุล: *{$safeLastName}*\n\n". "3️⃣ *อีเมล* (สำหรับการติดต่อ)\n". "กรุณาพิมพ์อีเมลของคุณ:", null, 'Markdown' ); } /** * @param $chatId * @param $userId * @param $email * @param $stepData * @return mixed */ private function handleEmail($chatId, $userId, $email, $stepData) { $email = SecurityHelper::sanitizeInput($email, 'email'); $email = strtolower(trim($email)); if (!SecurityHelper::validateInput($email, 'email', 5, 100)) { SecurityHelper::logSecurityEvent('invalid_email', $userId, ['input' => $email]); return $this->telegram->sendMessage($chatId, "❌ รูปแบบอีเมลไม่ถูกต้อง กรุณาลองใหม่:" ); } // ตรวจสอบว่าอีเมลนี้มีคนใช้แล้วหรือยัง $stmt = $this->db->prepare("SELECT id FROM employees WHERE email = ?"); $stmt->execute([$email]); if ($stmt->fetch()) { SecurityHelper::logSecurityEvent('email_already_exists', $userId, ['email' => $email]); return $this->telegram->sendMessage($chatId, "❌ อีเมลนี้มีคนใช้แล้วในระบบ กรุณาใช้อีเมลอื่น:" ); } $stepData['email'] = $email; $this->saveRegistrationStep($userId, $chatId, 'confirm', $stepData); $safeFirstName = SecurityHelper::escapeMarkdown($stepData['first_name']); $safeLastName = SecurityHelper::escapeMarkdown($stepData['last_name']); $safeEmail = SecurityHelper::escapeMarkdown($email); $confirmMessage = "📋 *ยืนยันข้อมูลการลงทะเบียน*\n\n". "ชื่อจริง: *{$safeFirstName}*\n". "นามสกุล: *{$safeLastName}*\n". "อีเมล: *{$safeEmail}*\n\n". "❓ ข้อมูลถูกต้องหรือไม่?\n\n". "💬 พิมพ์ `ถูกต้อง` หรือ `ใช่` เพื่อยืนยัน\n". "💬 พิมพ์ `ยกเลิก` เพื่อยกเลิกการลงทะเบียน"; return $this->telegram->sendMessage($chatId, $confirmMessage, null, 'Markdown'); } /** * @param $chatId * @param $userId * @param $confirmation * @param $stepData * @return mixed */ private function handleConfirmation($chatId, $userId, $confirmation, $stepData) { $confirmation = trim(strtolower($confirmation)); if ($confirmation === 'ยกเลิก' || $confirmation === 'cancel') { $this->clearRegistrationStep($userId, $chatId); return $this->telegram->sendMessage($chatId, "❌ ยกเลิกการลงทะเบียนแล้ว\n\n". "หากต้องการลงทะเบียนใหม่ พิมพ์ /register" ); } if ($confirmation === 'ถูกต้อง' || $confirmation === 'ใช่' || $confirmation === 'yes' || $confirmation === 'confirm') { return $this->createEmployee($chatId, $userId, $stepData); } return $this->telegram->sendMessage($chatId, "❓ กรุณาพิมพ์ `ถูกต้อง` หรือ `ยกเลิก` เท่านั้น:" ); } /** * @param $chatId * @param $userId * @param $stepData */ private function createEmployee($chatId, $userId, $stepData) { try { // สร้างรหัสพนักงานอัตโนมัติ (ถ้าเปิดการตั้งค่า) $employeeCode = null; if (AUTO_GENERATE_EMPLOYEE_CODE) { $employeeCode = $this->generateEmployeeCode(); } // เพิ่มข้อมูลพนักงานใหม่ $stmt = $this->db->prepare(" INSERT INTO employees (employee_code, chat_user_id, first_name, last_name, email, manager_id, is_manager, is_hr, created_at) VALUES (?, ?, ?, ?, ?, ?, 0, 0, NOW()) "); $result = $stmt->execute([ $employeeCode, $userId, $stepData['first_name'], $stepData['last_name'], $stepData['email'], DEFAULT_MANAGER_ID ]); if ($result) { $this->clearRegistrationStep($userId, $chatId); $successMessage = "🎉 *ลงทะเบียนสำเร็จ!*\n\n". "✅ ยินดีต้อนรับสู่ระบบลา\n\n". "ข้อมูลของคุณ:\n". "👤 ชื่อ: {$stepData['first_name']} {$stepData['last_name']}\n". "📧 อีเมล: {$stepData['email']}\n"; if ($employeeCode) { $successMessage .= "🏷️ รหัสพนักงาน: {$employeeCode}\n"; } $successMessage .= "\n💡 พิมพ์ /help เพื่อดูคำสั่งที่ใช้งานได้"; return $this->telegram->sendMessage($chatId, $successMessage, null, 'Markdown'); } else { throw new Exception("ไม่สามารถบันทึกข้อมูลได้"); } } catch (Exception $e) { error_log("Registration error: ".$e->getMessage()); return $this->telegram->sendMessage($chatId, "❌ เกิดข้อผิดพลาดในการลงทะเบียน กรุณาลองใหม่อีกครั้ง\n\n". "พิมพ์ /register เพื่อเริ่มต้นใหม่" ); } } private function generateEmployeeCode() { // สร้างรหัสพนักงานแบบ EMP001, EMP002, ... $stmt = $this->db->prepare(" SELECT employee_code FROM employees WHERE employee_code LIKE ? ORDER BY employee_code DESC LIMIT 1 "); $prefix = EMPLOYEE_CODE_PREFIX.'%'; $stmt->execute([$prefix]); $lastCode = $stmt->fetchColumn(); if ($lastCode) { // แยกหมายเลขออกมา $number = (int) substr($lastCode, strlen(EMPLOYEE_CODE_PREFIX)); $newNumber = $number + 1; } else { $newNumber = 1; } return EMPLOYEE_CODE_PREFIX.str_pad($newNumber, 3, '0', STR_PAD_LEFT); } // Methods สำหรับจัดการ registration steps /** * @param $userId * @param $chatId * @param $step * @param $data */ private function saveRegistrationStep($userId, $chatId, $step, $data) { $secureUserId = SecurityHelper::securePath($userId); $secureChatId = SecurityHelper::securePath($chatId); $filename = $this->registrationStepsDir."/registration_{$secureUserId}_{$secureChatId}.json"; $stepData = [ 'currentStep' => $step, 'data' => $data, 'timestamp' => time(), 'hash' => SecurityHelper::generateHash($userId.$chatId.$step) ]; // เข้ารหัสข้อมูลก่อนบันทึก $encryptedData = base64_encode(json_encode($stepData)); if (!file_put_contents($filename, $encryptedData, LOCK_EX)) { SecurityHelper::logSecurityEvent('registration_save_failed', $userId); error_log("Failed to save registration step for user {$userId}"); } } /** * @param $userId * @param $chatId */ private function getCurrentRegistrationStep($userId, $chatId) { $secureUserId = SecurityHelper::securePath($userId); $secureChatId = SecurityHelper::securePath($chatId); $filename = $this->registrationStepsDir."/registration_{$secureUserId}_{$secureChatId}.json"; if (!file_exists($filename)) { return null; } $content = file_get_contents($filename); if (!$content) { return null; } // ถอดรหัสข้อมูล $decodedContent = base64_decode($content); $stepData = json_decode($decodedContent, true); if (!$stepData || !isset($stepData['timestamp'])) { $this->clearRegistrationStep($userId, $chatId); return null; } // ตรวจสอบ hash เพื่อความปลอดภัย $expectedHash = SecurityHelper::generateHash($userId.$chatId.$stepData['currentStep']); if (!isset($stepData['hash']) || $stepData['hash'] !== $expectedHash) { SecurityHelper::logSecurityEvent('registration_hash_mismatch', $userId); $this->clearRegistrationStep($userId, $chatId); return null; } // ตรวจสอบว่าข้อมูลหมดอายุหรือยัง (30 นาที) if (time() - $stepData['timestamp'] > 1800) { $this->clearRegistrationStep($userId, $chatId); return null; } return $stepData; } /** * @param $userId * @param $chatId */ private function clearRegistrationStep($userId, $chatId) { $filename = $this->registrationStepsDir."/registration_{$userId}_{$chatId}.json"; if (file_exists($filename)) { unlink($filename); } } /** * ยกเลิกการลงทะเบียน */ public function cancelRegistration($chatId, $userId) { $this->clearRegistrationStep($userId, $chatId); return $this->telegram->sendMessage($chatId, "❌ ยกเลิกการลงทะเบียนแล้ว\n\n". "หากต้องการลงทะเบียนใหม่ พิมพ์ /register" ); } }