Files
justice/www/html/Admin/api/quiz-battle-modes.php
2026-07-31 03:36:58 +00:00

228 lines
9.0 KiB
PHP

<?php
declare(strict_types=1);
/**
* เปิด/ปิด 10 โหมด (ห้อง) ของ Quiz Battle + กำหนดวัน-เวลาที่เล่นได้ถึง [2026-07-22]
*
* อ่าน/เขียน Game/data/quiz-battle-modes.json
* เวลาอ้างอิง = **เวลาไทย (Asia/Bangkok, UTC+7)** เก็บเป็นสตริง 'YYYY-MM-DD HH:MM' ตรงๆ
* (ไม่เก็บ UTC เพื่อให้ค่าที่แอดมินกรอกกับค่าที่เก็บตรงกันเป๊ะ อ่านไฟล์แล้วเข้าใจทันที)
*
* openUntil = '' → ไม่มีวันหมดเขต (เปิดตลอด)
*/
require __DIR__ . '/_common.php';
require_login();
require_tab('quiz-battle');
define('QB_MODES_FILE', dirname(__DIR__, 2) . '/Game/data/quiz-battle-modes.json');
define('QB_ROOM_COUNT', 10);
define('QB_TZ', 'Asia/Bangkok');
define('QB_MAX_PLAYERS_CAP', 50); /* เพดานแข็ง — จำนวนคนต่อห้องปรับได้ไม่เกินนี้ */
/** จำกัดจำนวนคนต่อห้อง 1..50 (ค่าว่าง/เพี้ยน → 50) */
function qb_clamp_max($raw): int
{
$n = (int)$raw;
if ($n < 1) {
return QB_MAX_PLAYERS_CAP;
}
return min(QB_MAX_PLAYERS_CAP, $n);
}
/** ชื่อหัวข้อของแต่ละห้อง (ให้หน้า admin โชว์ — ตรงกับรูป Room-NN.png) */
function qb_default_titles(): array
{
return [
1 => 'กฎหมายใกล้ตัว',
2 => 'กฎหมายสิทธิพื้นฐาน',
3 => 'กฎหมายจราจร',
4 => 'คดีเกี่ยวกับทรัพย์',
5 => 'คดีหมิ่นประมาท',
6 => 'คดีทางเพศ',
7 => 'คดีอาชญากรรม',
8 => 'อาชญากรรมออนไลน์',
9 => 'กระบวนการยุติธรรม',
10 => 'งานบริการกระทรวงยุติธรรม',
];
}
function qb_default_modes(): array
{
$titles = qb_default_titles();
$modes = [];
for ($i = 1; $i <= QB_ROOM_COUNT; $i++) {
$modes[(string)$i] = [
'title' => $titles[$i] ?? ('ห้อง ' . $i),
'enabled' => true,
'openUntil' => '',
'maxPlayers' => QB_MAX_PLAYERS_CAP,
];
}
return $modes;
}
/** ชื่อหัวข้อห้อง — แก้ได้ [2026-07-23] · ตัดช่องว่าง/อักขระควบคุม จำกัด 60 ตัว · ว่าง → คืน default */
function qb_clean_title($raw, string $default): string
{
if (!is_string($raw)) {
return $default;
}
$s = preg_replace('/[\x00-\x1F\x7F]+/u', ' ', $raw); // ตัดอักขระควบคุม/ขึ้นบรรทัด
$s = trim(preg_replace('/\s+/u', ' ', $s ?? ''));
if ($s === '') {
return $default;
}
if (function_exists('mb_substr')) {
$s = mb_substr($s, 0, 60, 'UTF-8');
} else {
$s = substr($s, 0, 180);
}
return $s;
}
/** 'YYYY-MM-DD HH:MM' เท่านั้น (เวลาไทย) — ค่าอื่นถือว่าไม่ได้ตั้ง */
function qb_clean_until($raw): string
{
if (!is_string($raw)) {
return '';
}
$s = trim($raw);
if ($s === '') {
return '';
}
$s = str_replace('T', ' ', $s);
if (strlen($s) === 16 && preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$/', $s) === 1) {
[$d, $t] = explode(' ', $s, 2);
[$y, $m, $dd] = array_map('intval', explode('-', $d));
if (checkdate($m, $dd, $y)) {
[$hh, $mm] = array_map('intval', explode(':', $t));
if ($hh >= 0 && $hh <= 23 && $mm >= 0 && $mm <= 59) {
return $s;
}
}
}
return '';
}
function qb_read_modes(): array
{
$out = ['version' => 1, 'updatedAt' => '', 'modes' => qb_default_modes()];
$raw = @file_get_contents(QB_MODES_FILE);
if ($raw === false || $raw === '') {
return $out;
}
$j = json_decode($raw, true);
if (!is_array($j) || !isset($j['modes']) || !is_array($j['modes'])) {
return $out;
}
$titles = qb_default_titles();
foreach ($out['modes'] as $k => $def) {
$src = $j['modes'][$k] ?? null;
if (!is_array($src)) {
continue;
}
$out['modes'][$k] = [
/* [2026-07-23] หัวข้อแก้ได้แล้ว — ใช้ค่าที่เก็บไว้ ถ้าว่าง fallback เป็น default ของห้องนั้น */
'title' => qb_clean_title($src['title'] ?? '', $titles[(int)$k] ?? $def['title']),
'enabled' => !isset($src['enabled']) || (bool)$src['enabled'],
'openUntil' => qb_clean_until($src['openUntil'] ?? ''),
'maxPlayers' => qb_clamp_max($src['maxPlayers'] ?? QB_MAX_PLAYERS_CAP),
];
}
$out['updatedAt'] = is_string($j['updatedAt'] ?? null) ? $j['updatedAt'] : '';
return $out;
}
function qb_write_modes(array $data): bool
{
$dir = dirname(QB_MODES_FILE);
if (!is_dir($dir) && !@mkdir($dir, 0775, true) && !is_dir($dir)) {
return false;
}
$tmp = QB_MODES_FILE . '.tmp';
$json = json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
if ($json === false || @file_put_contents($tmp, $json, LOCK_EX) === false) {
return false;
}
/* เขียน tmp แล้ว rename — กันไฟล์พังครึ่งทางถ้า Node อ่านพอดีจังหวะ */
if (!@rename($tmp, QB_MODES_FILE)) {
@unlink($tmp);
return false;
}
@chmod(QB_MODES_FILE, 0664);
return true;
}
/** สถานะที่คำนวณแล้ว ณ เวลาไทยตอนนี้ — ให้หน้า admin โชว์ว่าห้องไหน "ปิดไปแล้ว" */
function qb_state(array $mode): array
{
$tz = new DateTimeZone(QB_TZ);
$now = new DateTimeImmutable('now', $tz);
$until = $mode['openUntil'] ?? '';
$expired = false;
if ($until !== '') {
$end = DateTimeImmutable::createFromFormat('Y-m-d H:i', $until, $tz);
$expired = ($end !== false) && ($now > $end);
}
return [
'open' => (bool)($mode['enabled'] ?? true) && !$expired,
'expired' => $expired,
];
}
$method = $_SERVER['REQUEST_METHOD'];
if ($method === 'GET') {
$data = qb_read_modes();
$tz = new DateTimeZone(QB_TZ);
$now = new DateTimeImmutable('now', $tz);
foreach ($data['modes'] as $k => $m) {
$data['modes'][$k] = array_merge($m, qb_state($m));
}
$data['nowThai'] = $now->format('Y-m-d H:i');
$data['tz'] = QB_TZ;
json_response(['ok' => true] + $data);
}
if ($method === 'PUT' || $method === 'POST') {
$body = require_json_body();
$src = $body['modes'] ?? null;
if (!is_array($src)) {
json_response(['ok' => false, 'error' => 'ต้องส่ง modes มาด้วย'], 400);
}
$cur = qb_read_modes();
$next = $cur['modes'];
foreach ($next as $k => $def) {
$m = $src[$k] ?? null;
if (!is_array($m)) {
continue;
}
$next[$k] = [
/* [2026-07-23] หัวข้อแก้ได้ — sanitize + ถ้าไม่ส่ง/ว่าง คงค่าเดิม (ไม่ reset เป็น default) */
'title' => qb_clean_title($m['title'] ?? ($def['title'] ?? ''), $def['title'] ?? ('ห้อง ' . $k)),
'enabled' => !isset($m['enabled']) || (bool)$m['enabled'],
'openUntil' => qb_clean_until($m['openUntil'] ?? ''),
/* [2026-07-23] จำนวนคนต่อห้อง — ถ้าไม่ส่งมา คงค่าเดิมไว้ (ไม่ reset เป็น 50) */
'maxPlayers' => qb_clamp_max($m['maxPlayers'] ?? ($def['maxPlayers'] ?? QB_MAX_PLAYERS_CAP)),
];
}
$me = current_admin();
$payload = [
'version' => 1,
'updatedAt' => (new DateTimeImmutable('now', new DateTimeZone(QB_TZ)))->format('Y-m-d H:i'),
'updatedBy' => $me['username'] ?? '',
'tz' => QB_TZ,
'modes' => $next,
];
if (!qb_write_modes($payload)) {
json_response(['ok' => false, 'error' => 'บันทึกไม่สำเร็จ (สิทธิ์เขียนไฟล์?)'], 500);
}
foreach ($payload['modes'] as $k => $m) {
$payload['modes'][$k] = array_merge($m, qb_state($m));
}
json_response(['ok' => true, 'message' => 'บันทึกแล้ว'] + $payload);
}
json_response(['ok' => false, 'error' => 'Use GET / PUT'], 405);