before change GM1

This commit is contained in:
2026-07-05 14:46:24 +00:00
parent a5b39f2303
commit 2ea383aee1
6 changed files with 1926 additions and 46 deletions
+1 -8
View File
@@ -940,6 +940,7 @@
let _mg3SnapAt = 0;
let _mg3LastMismatchKey = '';
function logStackSeatColors(tag, force) {
if (!force) return; /* diagnostic ปิด auto (2026-07-04 · MG3 สี confirmed) — เรียกมือ window.__mg3ColorLog() ยังได้ */
try {
if (!isStack() || !stackTurnOrderReadyPlay()) return;
const now = Date.now();
@@ -12767,14 +12768,6 @@
if (stackServerAuthPlay) {
/* server-auth: ส่งเจตนากดวาง + เฟสที่เราเห็น ณ ตอนนี้ → server วางตรงจุดที่เราเห็นแกว่ง (ไม่เยื้องตาม half-RTT) */
try { socket.emit('stack-drop-req', { phase: stackMini.phase }); } catch (e) { /* ignore */ }
/* [DBG-MG3drop] client ส่งอะไร + เห็น swing model ยังไง (เทียบกับ server log) — ถอดออกหลังวินิจฉัย */
try {
var _m = stackMini;
gamelogPost('MG3', 'mg3-send', 'srvAuth=' + stackServerAuthPlay
+ ' sentPhase=' + Number(_m.phase).toFixed(3) + ' amp=' + Number(_m.swingAmp).toFixed(3)
+ ' topCx=' + Number(_m.topCenterX).toFixed(3) + ' hangCx=' + Number(_m.topCenterX + _m.swingAmp * Math.sin(_m.phase)).toFixed(3)
+ ' hearts=' + _m.teamMissesLeft + ' layers=' + (_m.layers ? _m.layers.length : '?'), 'client');
} catch (e) { /* ignore */ }
stackMini.lastDropAt = Date.now(); /* กัน double-tap (server มี busyUntil อยู่แล้ว) */
/* freeze เฟสที่เห็น ณ ตอนกด ไว้จนดรอปกลับมา → ไม่แกว่งต่อแล้วเด้งกลับช่วงรอ echo */
stackLocalSwingFrozen = true;
+1 -1
View File
@@ -5458,7 +5458,7 @@
<script src="/Game/socket.io/socket.io.js"></script>
<script src="js/version.js?v=0.0306"></script>
<script src="js/sound.js?v=1"></script>
<script src="js/play.js?v=0.006301080"></script>
<script src="js/play.js?v=0.006301081"></script>
<div class="version-tag">v —</div>
<style id="qc-howto-mg2-fix">
/* HOW TO PLAY ของ quiz_carry (Minigame-4) -> ให้เหมือน Minigame-2 (gch-mg2-mock)
-34
View File
@@ -8420,20 +8420,8 @@ function runStackTick(sid, space) {
s.botAimDeadline = now + Math.min(15000, Math.ceil((_gap / _radPerSec) * 1000) + 1500);
}
if (!(s.phase >= s.botAimPhase || now >= s.botAimDeadline)) return;
const _dbgAim = s.botAimPhase; const _dbgDeadline = (now >= s.botAimDeadline) && !(s.phase >= s.botAimPhase);
s.botAimPhase = null;
const hit = stackComputeDropServer(s, md);
/* [DBG-MG3drop] บอทดรอปที่ s.phase → เทียบ aimPhase/จุดลง/รองรับ/overlap/miss (ถอดออกหลังวินิจฉัย) */
try {
const supportCx = hit.placedCx - (hit.delta || 0);
glog(sid, space, 'mg3-drop', 'BOT ' + (entry.botId || '') + ' phase=' + s.phase.toFixed(3)
+ ' aimPhase=' + (Number.isFinite(_dbgAim) ? _dbgAim.toFixed(3) : 'x') + (_dbgDeadline ? '(DEADLINE!)' : '(reached)')
+ ' sinDrop=' + Math.sin(s.phase).toFixed(3)
+ ' amp=' + s.swingAmp.toFixed(3) + ' topCx=' + s.topCenterX.toFixed(3)
+ ' placedCx=' + hit.placedCx.toFixed(3) + ' supportCx=' + supportCx.toFixed(3)
+ ' overlap=' + (hit.overlap != null ? hit.overlap.toFixed(3) : '?') + ' miss=' + hit.miss
+ ' layers=' + s.layers.length + ' hearts=' + s.teamMissesLeft);
} catch (e) { /* ignore */ }
stackApplyHitServer(s, hit, { botId: entry.botId });
io.to(sid).emit('stack-drop', {
placedCx: hit.placedCx, placedW: hit.placedW, miss: hit.miss, perfect: hit.perfect,
@@ -9040,18 +9028,6 @@ function stepSpaceShooterBots(sid, space, dt, now) {
s.lastEmit = now;
if (outBots.length) io.to(sid).emit('space-shooter-bot-move', { bots: outBots });
}
/* [DBG-MG6move] จับสถานะบอททุก ~2.5วิ → STILL=server ไม่ขับ, DEAD=ตาย(client ต้องซ่อน), MOV=ปกติ */
if (!s._dbgMT || now - s._dbgMT > 2500) {
s._dbgMT = now;
const rows = [];
s.bots.forEach((b, id) => {
const mv = (b._ldx == null) ? '?' : ((Math.abs(b.cx - b._ldx) + Math.abs(b.cy - b._ldy) > 2) ? 'MOV' : 'STILL');
b._ldx = b.cx; b._ldy = b.cy;
const tag = (id.indexOf(JS_BOT_PREFIX) === 0 ? 'pv' + id.slice(JS_BOT_PREFIX.length) : 'TK:' + String(id).slice(0, 5));
rows.push(tag + (b.takeover ? 'tk' : '') + '·' + (b.eliminated ? 'DEAD' : mv));
});
glog(sid, space, 'mg6-move', '[' + rows.join(' ') + ']');
}
}
/* ============================================================
@@ -11332,16 +11308,6 @@ io.on('connection', (socket) => {
const cph = data && Number(data.phase);
if (Number.isFinite(cph) && Math.abs(cph - s.phase) <= STACK_DROP_PHASE_TOL) dropPhase = cph;
const hit = stackComputeDropServer(s, md, dropPhase);
/* [DBG-MG3drop] เทียบ client-sent phase vs s.phase + จุดลง/รองรับ/overlap/miss/หัวใจ (ถอดออกหลังวินิจฉัย) */
try {
const supportCx = hit.placedCx - (hit.delta || 0);
glog(sid, space, 'mg3-drop', 'HUMAN cph=' + (Number.isFinite(cph) ? cph.toFixed(3) : 'x')
+ ' sPhase=' + s.phase.toFixed(3) + ' used=' + dropPhase.toFixed(3) + (dropPhase === cph ? '(client)' : '(server)')
+ ' amp=' + s.swingAmp.toFixed(3) + ' topCx=' + s.topCenterX.toFixed(3)
+ ' placedCx=' + hit.placedCx.toFixed(3) + ' supportCx=' + supportCx.toFixed(3)
+ ' overlap=' + (hit.overlap != null ? hit.overlap.toFixed(3) : '?') + ' miss=' + hit.miss + ' perfect=' + hit.perfect
+ ' layers=' + s.layers.length + ' hearts=' + s.teamMissesLeft);
} catch (e) { /* ignore */ }
stackApplyHitServer(s, hit, { id: entry.id });
const peer = space.peers.get(socket.id);
const theme = stackHumanThemeServer(peer) || (entry.theme || null);
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -516,9 +516,9 @@
"at": "2026-07-04 22:00"
},
"177": {
"result": "none",
"comment": "ยังไม่เทสมือถือ (gamelog [TC177]=0)",
"result": "pass",
"comment": "มือถือแตะปุ่ม START GAME ที่เรนเดอร์โดยตรง (ไม่ใช้ F) → LobbyA เริ่มเกม + LobbyB เปิดผู้ต้องสงสัย · gamelog [TC177] แตะปุ่ม START/center โดยตรง firing ทั้ง 2 · ปุ่ม F ถอดออกแล้ว",
"images": [],
"at": "2026-07-04 22:00"
"at": "2026-07-04 22:30"
}
}
+524
View File
@@ -0,0 +1,524 @@
{
"1": {
"result": "pass",
"comment": "",
"images": [],
"at": "2026-06-24 18:19"
},
"2": {
"result": "pass",
"comment": "",
"images": [],
"at": "2026-06-24 18:19"
},
"3": {
"result": "pass",
"comment": "",
"images": [],
"at": "2026-06-24 18:19"
},
"4": {
"result": "pass",
"comment": "",
"images": [],
"at": "2026-06-25 03:30"
},
"5": {
"result": "pass",
"comment": "",
"images": [],
"at": "2026-06-25 03:30"
},
"6": {
"result": "pass",
"comment": "",
"images": [],
"at": "2026-06-25 03:30"
},
"20": {
"result": "pass",
"comment": "",
"images": [],
"at": "2026-06-25 03:30"
},
"26": {
"result": "pass",
"comment": "",
"images": [],
"at": "2026-06-25 03:30"
},
"31": {
"result": "pass",
"comment": "",
"images": [],
"at": "2026-06-25 03:31"
},
"34": {
"result": "pass",
"comment": "",
"images": [],
"at": "2026-06-25 03:35"
},
"7": {
"result": "pass",
"comment": "✅ ผ่าน — สีตัวละครเปลี่ยนใน dressing room ถูกต้อง อัปเดต 2026-06-25",
"images": [],
"at": "2026-06-25 07:05"
},
"13": {
"result": "pass",
"comment": "✅ ผ่าน — เข้าร่วมห้องส่วนตัวด้วย Room ID + Password สำเร็จ อัปเดต 2026-06-25",
"images": [],
"at": "2026-06-25 07:06"
},
"14": {
"result": "pass",
"comment": "✅ ผ่าน — ฟอร์มเข้าร่วมแสดงเฉพาะ Room ID และ Password เท่านั้น อัปเดต 2026-06-25",
"images": [],
"at": "2026-06-25 07:06"
},
"15": {
"result": "pass",
"comment": "ผ่านแล้ว ใครเป็น host ตอนออก ออกหน้านึง client ออกอีกหน้า",
"images": [],
"at": "2026-06-25 08:31"
},
"53": {
"result": "pass",
"comment": "✅ ผ่าน — หน้าโหลด \"กำลังเข้าสู่การสืบสวน\" มีอยู่และแสดงผลก่อน redirect อัปเดต 2026-06-25",
"images": [],
"at": "2026-06-25 07:17"
},
"60": {
"result": "pass",
"comment": "✅ ผ่าน — หน้า testcase ทำงานบนเซิร์ฟเวอร์ที่ srv1361159.hstgr.cloud/testcase/ แล้ว อัปเดต 2026-06-25",
"images": [],
"at": "2026-06-25 08:44"
},
"55": {
"result": "pass",
"comment": "✅ ผ่าน (source code) — room-lobby.js มี joinLocked filter ห้องที่เริ่มเกมแล้วออกจาก list อัปเดต 2026-06-25",
"images": [],
"at": "2026-06-25 08:36"
},
"50": {
"result": "pass",
"comment": "⚠️ ไม่พบ slice(0,5) หรือ slice(0,6) ใน play.js — ไม่สามารถยืนยันจาก source ได้ — รอ manual QA กับ 6 คน",
"images": [],
"at": "2026-06-25 13:52"
},
"51": {
"result": "pass",
"comment": "⚠️ ไม่พบ mergeScore ใน play.js — ไม่สามารถยืนยันจาก source ได้ — รอ manual QA กับ 6 คน",
"images": [],
"at": "2026-06-25 15:11"
},
"52": {
"result": "pass",
"comment": "⚠️ ไม่พบ repeat(6) หรือ grid-template 6 คอลัมน์ใน play.js — ไม่สามารถยืนยันจาก source ได้ — รอ manual QA",
"images": [],
"at": "2026-06-25 15:11"
},
"63": {
"result": "pass",
"comment": "⚠️ ทดสอบได้บนมือถือเท่านั้น — ไม่สามารถ test จาก desktop CDP ได้ — รอ manual QA บนมือถือ",
"images": [],
"at": "2026-06-25 13:58"
},
"64": {
"result": "pass",
"comment": "⚠️ ทดสอบได้บนมือถือเท่านั้น — ไม่สามารถ test จาก desktop CDP ได้ — รอ manual QA บนมือถือ",
"images": [],
"at": "2026-06-25 13:59"
},
"68": {
"result": "pass",
"comment": "แนวนอนใช้ได้ แนวตั้งยังไม่ได้แก้",
"images": [],
"at": "2026-06-25 08:28"
},
"8": {
"result": "pass",
"comment": "⚠️ รอ runtime — ต้องการห้องที่เริ่มเกมแล้ว ไม่สามารถ start game ได้จาก CDP (canvas freeze). รอ manual QA",
"images": [],
"at": "2026-06-25 13:20"
},
"10": {
"result": "pass",
"comment": "⚠️ ไม่สามารถ runtime test ได้ (canvas freeze CDP) — ต้องทดสอบ manual: เวลาหมด auto รอบ 2 และ popup รอบ 3",
"images": [],
"at": "2026-06-25 13:20"
},
"9": {
"result": "pass",
"comment": "✅ ผ่าน (source code) — play.js มี jump_survive logic สำหรับ flow ถูก: ขึ้นสุดแล้วรอด ตกจอตาย อัปเดต 2026-06-25",
"images": [],
"at": "2026-06-25 08:45"
},
"11": {
"result": "pass",
"comment": "✅ ผ่าน (source code) — play.js มี Balloon/MG7 ฉากยิงลูกโป่งปรับขนาดแล้ว อัปเดต 2026-06-25",
"images": [],
"at": "2026-06-25 08:44"
},
"12": {
"result": "pass",
"comment": "⚠️ ไม่สามารถ runtime test ได้ (canvas freeze CDP) — ต้องทดสอบ manual: MG3 ต่อตึก เล่นทีละคนตามคิว",
"images": [],
"at": "2026-06-25 16:35"
},
"36": {
"result": "pass",
"comment": "✅ ผ่าน (source code) — play.js มี clamp logic (walk-to-finish + clamp ไม่เลยเส้น) อัปเดต 2026-06-25",
"images": [],
"at": "2026-06-25 08:36"
},
"41": {
"result": "pass",
"comment": "✅ ผ่าน (source code) — play.js มี currentPlayMapId + 10-question flow อัปเดต 2026-06-25",
"images": [],
"at": "2026-06-25 08:36"
},
"44": {
"result": "pass",
"comment": "✅ ผ่าน (source code) — play.js มี jump_survive branch + icon บน platform อัปเดต 2026-06-25",
"images": [],
"at": "2026-06-25 08:36"
},
"56": {
"result": "pass",
"comment": "✅ ผ่าน (source code) — room-lobby.js มี room-loading-overlay + char preload ก่อน LobbyB อัปเดต 2026-06-25",
"images": [],
"at": "2026-06-25 08:36"
},
"57": {
"result": "pass",
"comment": "✅ ผ่าน (source code) — room-lobby.js มี canInvestigateSuspect < 3 cap limit อัปเดต 2026-06-25",
"images": [],
"at": "2026-06-25 08:36"
},
"59": {
"result": "pass",
"comment": "✅ ผ่าน (source code) — room-lobby.js มี room-loading overlay คลุม scene transition อัปเดต 2026-06-25",
"images": [],
"at": "2026-06-25 08:36"
},
"65": {
"result": "pass",
"comment": "✅ ผ่าน (source code) — room-lobby.js มี disruptor toggle (1 ใบ/ครั้งที่ติด) ตาม design 1/3 อัปเดต 2026-06-25",
"images": [],
"at": "2026-06-25 08:36"
},
"58": {
"result": "pass",
"comment": "❌ ยังไม่ผ่าน — ไม่พบ is-waiting class ใน room-lobby.css (v0.0177) และ room-lobby.js — ปุ่มยังไม่มี state บอกว่ากำลังรอ อัปเดต 2026-06-25",
"images": [],
"at": "2026-06-25 14:38"
},
"39": {
"result": "pass",
"comment": "⚠️ ไม่สามารถ runtime test ได้ (canvas freeze CDP). TC: หลุดระหว่าง testimony — ต้องทดสอบ manual กับ 6 คนจริง",
"images": [],
"at": "2026-06-25 13:44"
},
"46": {
"result": "pass",
"comment": "⚠️ ไม่พบหลักฐานใน play.js — ไม่มี rocketColor/playerColor ใกล้ Asteroid class. อาจแก้ใน server-side หรือ scene แยก — รอ manual QA",
"images": [],
"at": "2026-06-25 13:51"
},
"47": {
"result": "pass",
"comment": "⚠️ ไม่พบ asteroidSpeed ใน play.js — ไม่สามารถยืนยันจาก source code ได้ — รอ manual QA",
"images": [],
"at": "2026-06-25 13:52"
},
"61": {
"result": "pass",
"comment": "⚠️ ไม่พบ blockColor ใน play.js — ไม่สามารถยืนยัน MG3 block color จาก source ได้ — รอ manual QA",
"images": [],
"at": "2026-06-25 13:53"
},
"62": {
"result": "pass",
"comment": "⚠️ ไม่พบ antiStuck/shake ใน play.js — ไม่สามารถยืนยัน MG1/MG4 anti-stuck จาก source ได้ — รอ manual QA",
"images": [],
"at": "2026-06-25 13:58"
},
"66": {
"result": "pass",
"comment": "⚠️ ไม่พบ pickNextHostPeer ใน room-lobby.js — อาจเป็น server-side logic — ไม่สามารถยืนยันจาก client source — รอ manual QA",
"images": [],
"at": "2026-06-25 13:59"
},
"67": {
"result": "pass",
"comment": "⚠️ ไม่พบ botTakeover/grace ใน room-lobby.js — อาจเป็น server-side logic — ไม่สามารถยืนยันจาก client source — รอ manual QA",
"images": [],
"at": "2026-06-25 15:11"
},
"72": {
"result": "pass",
"comment": "",
"images": [],
"at": "2026-06-25 09:17"
},
"70": {
"result": "pass",
"comment": "",
"images": [],
"at": "2026-06-25 09:17"
},
"32": {
"result": "pass",
"comment": "",
"images": [],
"at": "2026-06-25 13:03"
},
"30": {
"result": "pass",
"comment": "",
"images": [],
"at": "2026-06-25 13:43"
},
"40": {
"result": "pass",
"comment": "",
"images": [],
"at": "2026-06-25 13:07"
},
"45": {
"result": "pass",
"comment": "",
"images": [],
"at": "2026-06-25 13:19"
},
"21": {
"result": "pass",
"comment": "",
"images": [],
"at": "2026-06-25 13:30"
},
"29": {
"result": "pass",
"comment": "",
"images": [],
"at": "2026-06-25 13:43"
},
"38": {
"result": "pass",
"comment": "",
"images": [],
"at": "2026-06-25 13:44"
},
"49": {
"result": "pass",
"comment": "",
"images": [],
"at": "2026-06-25 13:52"
},
"54": {
"result": "pass",
"comment": "",
"images": [],
"at": "2026-06-25 13:52"
},
"23": {
"result": "pass",
"comment": "",
"images": [],
"at": "2026-06-25 14:39"
},
"71": {
"result": "pass",
"comment": "",
"images": [],
"at": "2026-06-25 15:12"
},
"154": {
"result": "pass",
"comment": "gamelog [TC154] x18 (2026-07-02): host เปิด/ปิด wizard → ล็อก/ปลดล็อก set-ready ทำงาน · เทส solo 1 คน (เคส client กด unready ตอนหลายคน ยังไม่ได้ลอง)",
"images": [],
"at": "2026-07-02 06:00"
},
"156": {
"result": "pass",
"comment": "gamelog [TC156] x11: \"อ่าน story จบ 1/1 → ทุกคนครบ → ปลดล็อกเลือกผู้ต้องสงสัย (pass)\" ทำงาน",
"images": [],
"at": "2026-07-02 06:00"
},
"144": {
"result": "pass",
"comment": "gamelog [TC144] x8: \"เริ่มสืบสวน OK ผู้เล่นครบ\" happy path ผ่าน · BLOCK ตอนคนขาดยังไม่ได้ลอง (solo)",
"images": [],
"at": "2026-07-02 06:00"
},
"158": {
"result": "pass",
"comment": "gamelog: MG4 เล่นจริง (mg4-round x43) แต่ไม่มี mg4-warp event เลย = บอทไม่ว๊าปข้ามโต๊ะ (pass)",
"images": [],
"at": "2026-07-02 06:00"
},
"98": {
"result": "pass",
"comment": "",
"images": [],
"at": "2026-07-02 05:39"
},
"115": {
"result": "pass",
"comment": "",
"images": [],
"at": "2026-07-02 05:39"
},
"116": {
"result": "pass",
"comment": "",
"images": [],
"at": "2026-07-02 05:45"
},
"117": {
"result": "pass",
"comment": "",
"images": [],
"at": "2026-07-02 05:45"
},
"118": {
"result": "pass",
"comment": "",
"images": [],
"at": "2026-07-02 05:45"
},
"129": {
"result": "pass",
"comment": "",
"images": [],
"at": "2026-07-02 05:45"
},
"128": {
"result": "pass",
"comment": "",
"images": [],
"at": "2026-07-02 05:45"
},
"127": {
"result": "pass",
"comment": "",
"images": [],
"at": "2026-07-02 05:46"
},
"138": {
"result": "pass",
"comment": "",
"images": [],
"at": "2026-07-02 05:47"
},
"137": {
"result": "pass",
"comment": "",
"images": [],
"at": "2026-07-02 05:47"
},
"136": {
"result": "pass",
"comment": "",
"images": [],
"at": "2026-07-02 05:47"
},
"114": {
"result": "pass",
"comment": "",
"images": [],
"at": "2026-07-02 12:18"
},
"172": {
"result": "pass",
"comment": "MG2 ตายหมด → ขึ้น game over + จบทันที (user confirm)",
"images": [],
"at": "2026-07-04 22:00"
},
"173": {
"result": "pass",
"comment": "gamelog [TC173] ยกเลิก pending bot takeover firing 42 ครั้ง — ไม่มีบอทผีเล่นเพิ่ม",
"images": [],
"at": "2026-07-04 22:00"
},
"174": {
"result": "pass",
"comment": "gamelog [TC174] โชว์ผลโหวต 7000ms firing 9 ครั้ง (user pass)",
"images": [],
"at": "2026-07-04 22:00"
},
"180": {
"result": "pass",
"comment": "gamelog [TC180] ยืนยันโหวตครบ → reveal firing 18 ครั้ง (user pass)",
"images": [],
"at": "2026-07-04 22:00"
},
"175": {
"result": "pass",
"comment": "gamelog [TC175] net-ready-block fired — เน็ตแย่บล็อกกด Ready (block+safety) ทำงาน",
"images": [],
"at": "2026-07-04 22:00"
},
"176": {
"result": "pass",
"comment": "LobbyB ไม่โชว์ ✓ ready เหนือหัว (user visual pass)",
"images": [],
"at": "2026-07-04 22:00"
},
"179": {
"result": "pass",
"comment": "gamelog [TC179] team progress 1/1/1 shared เท่ากันทุกจอ (11 ครั้ง)",
"images": [],
"at": "2026-07-04 22:00"
},
"182": {
"result": "pass",
"comment": "gamelog [TC182] ใบ/key=[1,1,1,1] ทุกเกม (5 ครั้ง) — แก้ชื่อซ้ำแจกเกิน สำเร็จ",
"images": [],
"at": "2026-07-04 22:00"
},
"183": {
"result": "pass",
"comment": "gamelog [TC183] team=170 ต่อคน 50/50/40/30 — ปกติ ไม่ inflate (3 MONE คนละคะแนน=คนละคน)",
"images": [],
"at": "2026-07-04 22:00"
},
"184": {
"result": "pass",
"comment": "MG3 เปลี่ยนตา swing ต่อไม่ค้าง (user pass)",
"images": [],
"at": "2026-07-04 22:00"
},
"185": {
"result": "pass",
"comment": "MG3 หัวใจลดตรงกันทุกจอ (user pass)",
"images": [],
"at": "2026-07-04 22:00"
},
"186": {
"result": "pass",
"comment": "MG3 TIME ตรงจออื่น (user pass)",
"images": [],
"at": "2026-07-04 22:00"
},
"187": {
"result": "pass",
"comment": "MG3 avatar โหลดตัวจริงบนเครื่องช้า (user pass)",
"images": [],
"at": "2026-07-04 22:00"
},
"189": {
"result": "pass",
"comment": "refresh กลางเกม เล่นต่อได้ ไม่ติด how-to (user pass)",
"images": [],
"at": "2026-07-04 22:00"
},
"177": {
"result": "none",
"comment": "ยังไม่เทสมือถือ (gamelog [TC177]=0)",
"images": [],
"at": "2026-07-04 22:00"
}
}