init backend presensi

This commit is contained in:
mwpn
2026-03-05 14:37:36 +07:00
commit b4fda6b9c9
319 changed files with 27261 additions and 0 deletions

View File

@@ -0,0 +1,146 @@
<?php
namespace App\Modules\Notification\Services;
use App\Modules\Academic\Models\StudentModel;
use App\Modules\Notification\Models\StudentParentModel;
use App\Modules\Notification\Models\TelegramAccountModel;
/**
* Parent Link Service
*
* Handles parent linking logic for students via Telegram.
*/
class ParentLinkService
{
protected StudentModel $studentModel;
protected TelegramAccountModel $telegramAccountModel;
protected StudentParentModel $studentParentModel;
/**
* Link code length
*/
protected int $linkCodeLength = 16;
public function __construct()
{
$this->studentModel = new StudentModel();
$this->telegramAccountModel = new TelegramAccountModel();
$this->studentParentModel = new StudentParentModel();
}
/**
* Generate unique link code for student
*
* @param int $studentId Student ID
* @return string Generated link code
*/
public function generateLinkCode(int $studentId): string
{
// Check if student exists
$student = $this->studentModel->find($studentId);
if (!$student) {
throw new \InvalidArgumentException("Student with ID {$studentId} not found");
}
// Generate unique link code
do {
$linkCode = $this->generateRandomCode($this->linkCodeLength);
$existing = $this->studentModel->where('parent_link_code', $linkCode)->first();
} while ($existing !== null);
// Update student with link code
$this->studentModel->update($studentId, [
'parent_link_code' => $linkCode,
]);
return $linkCode;
}
/**
* Link Telegram account to student using link code
*
* @param string $linkCode Link code from student
* @param int $telegramUserId Telegram user ID
* @param array $profileData Telegram profile data (username, first_name, last_name)
* @return array Result with student_id, parent_id, telegram_account_id
*/
public function linkTelegramToStudent(string $linkCode, int $telegramUserId, array $profileData): array
{
// Find student by link code
$student = $this->studentModel->where('parent_link_code', $linkCode)->first();
if (!$student) {
throw new \InvalidArgumentException("Invalid link code");
}
// Check if Telegram account already exists
$telegramAccount = $this->telegramAccountModel->findByTelegramUserId($telegramUserId);
// Create parent first so we can set parent_id on telegram account
$parentModel = new \App\Modules\Notification\Models\ParentModel();
$parentId = $parentModel->insert([
'name' => trim(($profileData['first_name'] ?? '') . ' ' . ($profileData['last_name'] ?? '')),
'phone_number' => null, // Can be updated later
]);
if (!$telegramAccount) {
// Create new Telegram account linked to parent
$telegramAccountId = $this->telegramAccountModel->insert([
'telegram_user_id' => $telegramUserId,
'username' => $profileData['username'] ?? null,
'first_name' => $profileData['first_name'] ?? null,
'last_name' => $profileData['last_name'] ?? null,
'is_verified' => 1,
'parent_id' => $parentId,
]);
} else {
$telegramAccountId = $telegramAccount->id;
// Update profile data and link to parent
$this->telegramAccountModel->update($telegramAccountId, [
'username' => $profileData['username'] ?? $telegramAccount->username,
'first_name' => $profileData['first_name'] ?? $telegramAccount->first_name,
'last_name' => $profileData['last_name'] ?? $telegramAccount->last_name,
'is_verified' => 1,
'parent_id' => $parentId,
]);
}
// Create student-parent relationship
$studentParentId = $this->studentParentModel->insert([
'student_id' => $student->id,
'parent_id' => $parentId,
'relationship' => 'WALI',
]);
// Clear link code after successful linking
$this->studentModel->update($student->id, [
'parent_link_code' => null,
]);
return [
'student_id' => $student->id,
'parent_id' => $parentId,
'telegram_account_id' => $telegramAccountId,
'student_parent_id' => $studentParentId,
];
}
/**
* Generate random alphanumeric code
*
* @param int $length Code length
* @return string Generated code
*/
protected function generateRandomCode(int $length): string
{
$characters = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$code = '';
for ($i = 0; $i < $length; $i++) {
$code .= $characters[random_int(0, strlen($characters) - 1)];
}
return $code;
}
}

View File

@@ -0,0 +1,76 @@
<?php
namespace App\Modules\Notification\Services;
/**
* Telegram Bot Service
*
* Sends messages via Telegram Bot API.
*/
class TelegramBotService
{
protected string $apiBase = 'https://api.telegram.org/bot';
protected ?string $token;
public function __construct()
{
$this->token = env('telegram.bot_token') ?: env('telegram_bot_token');
}
/**
* Send a text message to a Telegram user
*
* @param int $telegramUserId Telegram user ID (chat_id)
* @param string $message Text message to send (HTML supported)
* @return bool True on success, false on failure
*/
public function sendMessage(int $telegramUserId, string $message): bool
{
if (empty($this->token)) {
log_message('error', 'TelegramBotService: telegram.bot_token is not set in .env');
return false;
}
$url = $this->apiBase . $this->token . '/sendMessage';
$payload = [
'chat_id' => $telegramUserId,
'text' => $message,
'parse_mode' => 'HTML',
'disable_web_page_preview' => true,
];
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($payload),
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlError = curl_error($ch);
curl_close($ch);
if ($curlError) {
log_message('error', 'TelegramBotService sendMessage curl error: ' . $curlError . ' for chat_id=' . $telegramUserId);
return false;
}
if ($httpCode !== 200) {
log_message('error', 'TelegramBotService sendMessage HTTP ' . $httpCode . ' for chat_id=' . $telegramUserId . ' response=' . $response);
return false;
}
$data = json_decode($response, true);
if (isset($data['ok']) && $data['ok'] === true) {
return true;
}
log_message('error', 'TelegramBotService sendMessage API error: ' . ($response ?: 'empty'));
return false;
}
}