<?php
// header('Content-Type: application/json');
require_once __DIR__.'/config/telegram_config.php';
require_once __DIR__.'/config/db_config.php';
require_once __DIR__.'/src/Core/Database.php';
require_once __DIR__.'/src/Services/TelegramService.php';
require_once __DIR__.'/src/Models/LeaveType.php';
require_once __DIR__.'/src/Models/Employee.php';
require_once __DIR__.'/src/Models/LeaveRequest.php';
require_once __DIR__.'/src/Models/LeaveBalance.php';
require_once __DIR__.'/src/Handlers/LeaveApplicationHandler.php';
require_once __DIR__.'/src/Handlers/BalanceCheckHandler.php';
require_once __DIR__.'/src/Handlers/CancelRequestHandler.php';
require_once __DIR__.'/src/Handlers/ManagerActionHandler.php';
require_once __DIR__.'/src/Handlers/HrSummaryHandler.php';
require_once __DIR__.'/src/Handlers/SelfRegistrationHandler.php';
use App\Core\Database;
use App\Handlers\BalanceCheckHandler;
use App\Handlers\CancelRequestHandler;
use App\Handlers\HrSummaryHandler;
use App\Handlers\LeaveApplicationHandler;
use App\Handlers\ManagerActionHandler;
use App\Services\TelegramService;
// PHP 7.4 compatibility functions
if (!function_exists('str_starts_with')) {
/**
* @param string $haystack
* @param string $needle
*/
function str_starts_with(string $haystack, string $needle): bool
{
return strpos($haystack, $needle) === 0;
}
}
if (!function_exists('str_ends_with')) {
/**
* @param string $haystack
* @param string $needle
*/
function str_ends_with(string $haystack, string $needle): bool
{
return substr($haystack, -strlen($needle)) === $needle;
}
}
if (!function_exists('str_contains')) {
/**
* @param string $haystack
* @param string $needle
*/
function str_contains(string $haystack, string $needle): bool
{
return strpos($haystack, $needle) !== false;
}
}
if (!function_exists('log_message')) {
/**
* @param string $message
* @return null
*/
function log_message(string $message): void
{
$logFile = __DIR__.'/logs/webhook.log';
if (!file_exists(dirname($logFile))) {
if (!mkdir(dirname($logFile), 0775, true) && !is_dir(dirname($logFile))) {
return;
}
}
$timestamp = date("Y-m-d H:i:s");
file_put_contents($logFile, "[{$timestamp}] {$message}\n", FILE_APPEND);
}
}
$input = file_get_contents('php://input');
// สำหรับการทดสอบใน CLI mode
if (empty($input) && php_sapi_name() === 'cli') {
log_message("CLI mode detected, reading from STDIN");
$input = stream_get_contents(STDIN);
log_message("STDIN input: ".$input);
}
log_message("Total input received: ".strlen($input)." characters");
if (empty($input)) {
log_message("Webhook received empty input.");
http_response_code(400);
echo json_encode(['status' => 'error', 'message' => 'No input received.']);
exit;
}
$update = json_decode($input, true);
if (json_last_error() !== JSON_ERROR_NONE) {
log_message("Webhook received invalid JSON: ".json_last_error_msg()." | Input: ".$input);
http_response_code(400);
echo json_encode(['status' => 'error', 'message' => 'Invalid JSON.']);
exit;
}
if (isset($update['message']['message_id'])) {
log_message("Update ID: {$update['update_id']}, Message ID: {$update['message']['message_id']}, Chat ID: {$update['message']['chat']['id']}, User ID: {$update['message']['from']['id']}, Text: ".($update['message']['text'] ?? "[no text]"));
} elseif (isset($update['callback_query']['id'])) {
log_message("Update ID: {$update['update_id']}, Callback Query ID: {$update['callback_query']['id']}, Chat ID: {$update['callback_query']['message']['chat']['id']}, User ID: {$update['callback_query']['from']['id']}, Data: ".($update['callback_query']['data'] ?? "[no data]"));
}
$telegramService = new TelegramService();
// สร้าง Database connection และ SelfRegistrationHandler
$database = Database::getInstance();
$pdo = $database->getConnection();
$selfRegistrationHandler = new SelfRegistrationHandler($pdo, $telegramService);
log_message("TelegramService and SelfRegistrationHandler initialized successfully");
/**
* @param int $userId
* @param int $chatId
*/
function getUserState(int $userId, int $chatId): ?string
{
$stateFile = __DIR__."/logs/user_{$userId}_chat_{$chatId}_state.txt";
if (file_exists($stateFile)) {
return trim(file_get_contents($stateFile));
}
return null;
}
/**
* @param int $userId
* @param int $chatId
*/
function clearUserStateWebhook(int $userId, int $chatId): void
{
$stateFile = __DIR__."/logs/user_{$userId}_chat_{$chatId}_state.txt";
if (file_exists($stateFile)) {
if (unlink($stateFile)) {
log_message("State file cleared by webhook_handler for user {$userId} chat {$chatId}: {$stateFile}");
} else {
log_message("Failed to clear state file by webhook_handler for user {$userId} chat {$chatId}: {$stateFile}");
}
}
}
if (isset($update['message'])) {
$message = $update['message'];
$chatId = $message['chat']['id'];
$userId = $message['from']['id'];
$firstName = $message['from']['first_name'] ?? 'User';
$text = $message['text'] ?? '';
$lowerText = strtolower($text);
// ตรวจสอบสิทธิ์การเข้าใช้งาน
$canAccess = $selfRegistrationHandler->canUserAccessBot($userId);
log_message("Access check for user {$userId}: ".($canAccess ? "ALLOWED" : "DENIED"));
if (!$canAccess) {
$responseMsg = "❌ ขออภัย คุณยังไม่ได้รับอนุญาตให้ใช้บอทนี้\nการลงทะเบียนด้วยตัวเองถูกปิดใช้งาน กรุณาติดต่อผู้ดูแลระบบ";
$result = $telegramService->sendMessage($chatId, $responseMsg);
log_message("Access denied response sent to user {$userId}. Result: ".json_encode($result));
exit;
}
// ตรวจสอบว่าอยู่ในกระบวนการลงทะเบียนหรือไม่
$registrationHandled = $selfRegistrationHandler->handleUserInput($chatId, $userId, $text);
log_message("Registration handler result for user {$userId}: ".($registrationHandled ? "HANDLED" : "NOT_HANDLED"));
if ($registrationHandled) {
log_message("Registration process handled, exiting for user {$userId}");
exit; // หยุดการประมวลผลเมื่อจัดการกับกระบวนการลงทะเบียน
}
$userState = getUserState($userId, $chatId);
$currentMode = 'leave';
if ($userState) {
if (str_starts_with($userState, 'wfh_')) {
$currentMode = 'wfh';
}
} elseif ($lowerText === '/wfh') {
$currentMode = 'wfh';
}
if ($userState) {
if (str_starts_with($userState, 'cancelReq_confirm_')) {
$telegramService->sendMessage($chatId, "น้องลารอการยืนยันการยกเลิกจากปุ่มค่ะ กรุณากดปุ่มที่แสดงผลไปก่อนหน้านี้นะคะ หรือพิมพ์ /cancel เพื่อยกเลิกทั้งหมด");
} elseif (str_starts_with($userState, 'la_') || str_starts_with($userState, 'wfh_')) {
// ตรวจสอบว่าผู้ใช้ลงทะเบียนแล้ว
$stmt = $pdo->prepare("SELECT id FROM employees WHERE chat_user_id = ?");
$stmt->execute([$userId]);
if (!$stmt->fetch()) {
$telegramService->sendMessage($chatId, "❌ กรุณาลงทะเบียนก่อนใช้งาน พิมพ์ /register");
clearUserStateWebhook($userId, $chatId);
exit;
}
$leaveAppHandler = new LeaveApplicationHandler($telegramService, $chatId, $userId, $currentMode);
if (str_starts_with($userState, 'la_expect_startDate_for_') || str_starts_with($userState, 'wfh_expect_startDate_for_')) {
$leaveAppHandler->handleStartDateInput($userState, $text);
} elseif (str_starts_with($userState, 'la_expect_endDate_for_') || str_starts_with($userState, 'wfh_expect_endDate_for_')) {
$leaveAppHandler->handleEndDateInput($userState, $text);
} elseif (str_starts_with($userState, 'la_expect_reason_for_') || str_starts_with($userState, 'wfh_expect_reason_for_')) {
$leaveAppHandler->handleReasonInput($userState, $text);
} elseif (str_starts_with($userState, 'la_confirm_') || str_starts_with($userState, 'wfh_confirm_')) {
$telegramService->sendMessage($chatId, "น้องลารอการยืนยันการขอลา/WFH จากปุ่มค่ะ กรุณากดปุ่มที่แสดงผลไปก่อนหน้านี้นะคะ หรือพิมพ์ /cancel เพื่อยกเลิกทั้งหมด");
} else {
log_message("Unhandled state '{$userState}' for user {$userId} with text input '{$text}'. No action taken.");
}
} else {
log_message("Unknown state prefix for user {$userId}: '{$userState}'. No action taken.");
}
}
// คำสั่งใหม่เมื่อไม่มี state ที่ active
else if ($lowerText === '/ลา' || $lowerText === '/leave') {
// ตรวจสอบว่าผู้ใช้ลงทะเบียนแล้ว
$stmt = $pdo->prepare("SELECT id FROM employees WHERE chat_user_id = ?");
$stmt->execute([$userId]);
if (!$stmt->fetch()) {
$telegramService->sendMessage($chatId, "❌ กรุณาลงทะเบียนก่อนใช้งาน พิมพ์ /register");
exit;
}
$leaveAppHandler = new LeaveApplicationHandler($telegramService, $chatId, $userId, 'leave');
$leaveAppHandler->initiateRequest();
} elseif ($lowerText === '/wfh') {
// ตรวจสอบว่าผู้ใช้ลงทะเบียนแล้ว
$stmt = $pdo->prepare("SELECT id FROM employees WHERE chat_user_id = ?");
$stmt->execute([$userId]);
if (!$stmt->fetch()) {
$telegramService->sendMessage($chatId, "❌ กรุณาลงทะเบียนก่อนใช้งาน พิมพ์ /register");
exit;
}
$leaveAppHandler = new LeaveApplicationHandler($telegramService, $chatId, $userId, 'wfh');
$leaveAppHandler->initiateRequest();
} elseif ($lowerText === '/เช็คยอด' || $lowerText === '/balance' || $lowerText === '/checkbalance') {
// ตรวจสอบว่าผู้ใช้ลงทะเบียนแล้ว
$stmt = $pdo->prepare("SELECT id FROM employees WHERE chat_user_id = ?");
$stmt->execute([$userId]);
if (!$stmt->fetch()) {
$telegramService->sendMessage($chatId, "❌ กรุณาลงทะเบียนก่อนใช้งาน พิมพ์ /register");
exit;
}
$balanceHandler = new BalanceCheckHandler($telegramService, $chatId, $userId);
$balanceHandler->showBalances();
clearUserStateWebhook($userId, $chatId);
} elseif ($lowerText === '/ยกเลิกคำขอ' || $lowerText === '/cancelrequest') {
// ตรวจสอบว่าผู้ใช้ลงทะเบียนแล้ว
$stmt = $pdo->prepare("SELECT id FROM employees WHERE chat_user_id = ?");
$stmt->execute([$userId]);
if (!$stmt->fetch()) {
$telegramService->sendMessage($chatId, "❌ กรุณาลงทะเบียนก่อนใช้งาน พิมพ์ /register");
exit;
}
$cancelHandler = new CancelRequestHandler($telegramService, $chatId, $userId);
$cancelHandler->startCancellationProcess();
} elseif ($lowerText === '/สรุปวันนี้' || $lowerText === '/whosout') {
// ตรวจสอบว่าผู้ใช้ลงทะเบียนแล้ว
$stmt = $pdo->prepare("SELECT id FROM employees WHERE chat_user_id = ?");
$stmt->execute([$userId]);
if (!$stmt->fetch()) {
$telegramService->sendMessage($chatId, "❌ กรุณาลงทะเบียนก่อนใช้งาน พิมพ์ /register");
exit;
}
$hrHandler = new HrSummaryHandler($telegramService, $chatId, $userId);
$hrHandler->showTodaysAbsences();
clearUserStateWebhook($userId, $chatId);
} elseif ($lowerText === '/start') {
log_message("Processing /start command for user {$userId}");
$stmt = $pdo->prepare("SELECT first_name, last_name FROM employees WHERE chat_user_id = ?");
$stmt->execute([$userId]);
$employee = $stmt->fetch();
if ($employee) {
$employeeName = $employee['first_name'].' '.$employee['last_name'];
$responseText = "สวัสดีครับคุณ {$employeeName}! บอทน้องลา (Leave & WFH Bot) พร้อมให้บริการแล้วครับ\nพิมพ์ /help เพื่อดูคำสั่งที่ใช้ได้";
log_message("Found registered employee: {$employeeName}");
} else {
$responseText = "สวัสดีครับคุณ {$firstName}! บอทน้องลา (Leave & WFH Bot) พร้อมให้บริการแล้วครับ\n\n";
if (ALLOW_SELF_REGISTRATION) {
$responseText .= "🎯 คุณยังไม่ได้ลงทะเบียนในระบบ กรุณาพิมพ์ /register เพื่อเริ่มการลงทะเบียน\n\n";
} else {
$responseText .= "❌ การลงทะเบียนด้วยตัวเองถูกปิดใช้งาน กรุณาติดต่อผู้ดูแลระบบ\n\n";
}
$responseText .= "พิมพ์ /help เพื่อดูคำสั่งที่ใช้ได้";
log_message("New user, self registration: ".(ALLOW_SELF_REGISTRATION ? "ENABLED" : "DISABLED"));
}
$result = $telegramService->sendMessage($chatId, $responseText);
log_message("Start response sent to user {$userId}. Result: ".json_encode($result));
clearUserStateWebhook($userId, $chatId);
} elseif ($lowerText === '/register') {
log_message("Processing /register command for user {$userId}");
$result = $selfRegistrationHandler->startRegistration($chatId, $userId);
log_message("Registration start result for user {$userId}: ".json_encode($result));
clearUserStateWebhook($userId, $chatId);
} elseif ($lowerText === '/ลงทะเบียน') {
$selfRegistrationHandler->startRegistration($chatId, $userId);
clearUserStateWebhook($userId, $chatId);
} elseif ($lowerText === '/สถานะ' || $lowerText === '/status') {
$stmt = $pdo->prepare("SELECT employee_code, first_name, last_name, email FROM employees WHERE chat_user_id = ?");
$stmt->execute([$userId]);
$employee = $stmt->fetch();
if ($employee) {
$responseText = "✅ *สถานะการลงทะเบียน*\n\n".
"👤 ชื่อ: {$employee['first_name']} {$employee['last_name']}\n".
"📧 อีเมล: {$employee['email']}\n";
if ($employee['employee_code']) {
$responseText .= "🏷️ รหัสพนักงาน: {$employee['employee_code']}\n";
}
$responseText .= "\n✅ ลงทะเบียนแล้ว สามารถใช้งานได้";
} else {
if (ALLOW_SELF_REGISTRATION) {
$responseText = "❌ คุณยังไม่ได้ลงทะเบียนในระบบ\nพิมพ์ /register เพื่อเริ่มการลงทะเบียน";
} else {
$responseText = "❌ คุณยังไม่ได้ลงทะเบียนในระบบ\nการลงทะเบียนด้วยตัวเองถูกปิดใช้งาน กรุณาติดต่อผู้ดูแลระบบ";
}
}
$telegramService->sendMessage($chatId, $responseText, null, 'Markdown');
clearUserStateWebhook($userId, $chatId);
} elseif ($lowerText === '/help') {
$stmt = $pdo->prepare("SELECT id FROM employees WHERE chat_user_id = ?");
$stmt->execute([$userId]);
$isRegistered = $stmt->fetch();
if ($isRegistered) {
$responseText = "คำสั่งที่น้องลาเข้าใจตอนนี้:\n".
"/start - เริ่มการสนทนา\n".
"/help - แสดงข้อความช่วยเหลือนี้\n".
"/สถานะ (หรือ /status) - ตรวจสอบสถานะการลงทะเบียน\n".
"/ลา (หรือ /leave) - เริ่มขั้นตอนการขอลา\n".
"/wfh - เริ่มขั้นตอนการขอ Work From Home\n".
"/เช็คยอด (หรือ /balance) - ตรวจสอบยอดวันลาคงเหลือ\n".
"/ยกเลิกคำขอ (หรือ /cancelrequest) - ยกเลิกคำขอที่รออนุมัติ\n".
"/สรุปวันนี้ (หรือ /whosout) - (HR) สรุปผู้ที่ไม่อยู่ในออฟฟิศวันนี้\n".
"/cancel - ยกเลิกการดำเนินการปัจจุบัน";
} else {
$responseText = "คำสั่งที่น้องลาเข้าใจตอนนี้:\n".
"/start - เริ่มการสนทนา\n".
"/help - แสดงข้อความช่วยเหลือนี้\n";
if (ALLOW_SELF_REGISTRATION) {
$responseText .= "/register - ลงทะเบียนเข้าใช้งานระบบ\n";
}
$responseText .= "/สถานะ (หรือ /status) - ตรวจสอบสถานะการลงทะเบียน\n\n";
if (ALLOW_SELF_REGISTRATION) {
$responseText .= "⚠️ คุณต้องลงทะเบียนก่อนเพื่อใช้งานคำสั่งอื่นๆ";
} else {
$responseText .= "⚠️ การลงทะเบียนด้วยตัวเองถูกปิดใช้งาน กรุณาติดต่อผู้ดูแลระบบ";
}
}
$telegramService->sendMessage($chatId, $responseText);
clearUserStateWebhook($userId, $chatId);
} elseif ($lowerText === '/cancel') {
clearUserStateWebhook($userId, $chatId);
$selfRegistrationHandler->cancelRegistration($chatId, $userId);
} else if (str_starts_with($lowerText, '/') && !$userState) {
$responseText = "ขออภัยครับคุณ {$firstName}, น้องลายังไม่เข้าใจคำสั่ง: '{$text}'. ลองพิมพ์ /help ดูนะครับ";
$telegramService->sendMessage($chatId, $responseText);
}
} elseif (isset($update['callback_query'])) {
$callbackQuery = $update['callback_query'];
$chatId = $callbackQuery['message']['chat']['id'];
$userId = $callbackQuery['from']['id'];
$callbackData = $callbackQuery['data'] ?? '';
$callbackQueryId = $callbackQuery['id'];
$originalMessageId = $callbackQuery['message']['message_id'];
$telegramService->answerCallbackQuery($callbackQueryId);
// ตรวจสอบสิทธิ์การเข้าใช้งาน
if (!$selfRegistrationHandler->canUserAccessBot($userId)) {
$telegramService->editMessageText($chatId, $originalMessageId,
"❌ ขออภัย คุณยังไม่ได้รับอนุญาตให้ใช้บอทนี้"
);
exit;
}
// ตรวจสอบว่าผู้ใช้ลงทะเบียนแล้ว
$stmt = $pdo->prepare("SELECT id FROM employees WHERE chat_user_id = ?");
$stmt->execute([$userId]);
if (!$stmt->fetch()) {
$telegramService->editMessageText($chatId, $originalMessageId,
"❌ กรุณาลงทะเบียนก่อนใช้งาน พิมพ์ /register"
);
exit;
}
$userState = getUserState($userId, $chatId);
$currentLeaveAppMode = 'leave';
if ($userState) {
if (str_starts_with($userState, 'wfh_')) {
$currentLeaveAppMode = 'wfh';
}
}
if (str_starts_with($callbackData, 'req_type_')) {
$leaveAppHandler = new LeaveApplicationHandler($telegramService, $chatId, $userId, $currentLeaveAppMode);
$leaveAppHandler->handleTypeSelection($callbackData);
} elseif (str_starts_with($callbackData, 'req_action_')) {
$leaveAppHandler = new LeaveApplicationHandler($telegramService, $chatId, $userId, $currentLeaveAppMode);
if ($userState && (str_starts_with($userState, 'la_confirm_') || str_starts_with($userState, 'wfh_confirm_'))) {
$leaveAppHandler->handleConfirmationAction($userState, $callbackData);
} else {
$telegramService->editMessageText($chatId, $originalMessageId, "การดำเนินการนี้อาจหมดอายุหรือไม่ถูกต้องแล้วค่ะ (ไม่พบ state)");
if ($userState) {
clearUserStateWebhook($userId, $chatId);
}
}
} elseif (str_starts_with($callbackData, 'cancelReq_select_')) {
$cancelHandler = new CancelRequestHandler($telegramService, $chatId, $userId);
$cancelHandler->handleCancellationSelection($callbackData);
} elseif (str_starts_with($callbackData, 'cancelReq_confirm_') || $callbackData === 'cancelReq_abort') {
$cancelHandler = new CancelRequestHandler($telegramService, $chatId, $userId);
if ($userState && str_starts_with($userState, 'cancelReq_confirm_')) {
$cancelHandler->handleCancellationConfirmation($userState, $callbackData);
} else if ($callbackData === 'cancelReq_abort' && $userState && str_starts_with($userState, 'cancelReq_confirm_')) {
// Ensure abort also needs the confirm state
$cancelHandler->handleCancellationConfirmation($userState, $callbackData);
} else {
$telegramService->editMessageText($chatId, $originalMessageId, "การดำเนินการยกเลิกนี้อาจหมดอายุหรือไม่ถูกต้องแล้วค่ะ (ไม่พบ state)");
if ($userState) {
clearUserStateWebhook($userId, $chatId);
}
}
} elseif (str_starts_with($callbackData, 'mgrAction_')) {
$managerHandler = new ManagerActionHandler($telegramService, $chatId, $userId);
$managerHandler->handleApprovalAction($callbackData, $originalMessageId);
} else {
log_message("Unhandled callback_data: {$callbackData} from user {$userId} in chat {$chatId}");
}
} else {
log_message("Received an update type that is not a message or callback_query: ".json_encode(array_keys($update)));
}
http_response_code(200);
exit;