ตัวอย่างการใช้งาน API นักข่าว
ดึงเฉพาะ สถานะเซิร์ฟเวอร์ BIT CITIES (ER:LC) ไปแสดงบนเว็บหรือโปรเจกต์ของคุณได้ฟรี ผ่าน JSON API หรือวิดเจ็ตสำเร็จรูป (มีกราฟผู้เล่นย้อนหลัง + 3 ธีม) โดยต้องแสดงเครดิตว่าข้อมูลมาจาก API นักข่าว BIT NEWS ทุกครั้ง
1 วิดเจ็ตสำเร็จรูป (ตัวอย่างสด)
วางแท็ก script ครั้งเดียว วิดเจ็ตจะดึงสถานะ + กราฟผู้เล่นย้อนหลัง และรีเฟรชอัตโนมัติ พร้อมแสดงเครดิตกลับมาที่ BIT NEWS ให้ในตัว รองรับ 3 ธีม: นักข่าว, มืด และ ขาว
<script src="https://bitnewsth.qd.je/widget.js" data-theme="news" data-lang="th" data-chart="true" data-chart-range="6h" data-interval="30" async></script>
ตัวเลือก: data-theme="news|dark|light" · data-lang="th|en" ·
data-chart="true|false" (เปิด/ปิดกราฟ) · data-chart-range="6h|24h|7d|30d" ·
data-interval="10-300" (วินาที) · data-target="#selector" (กำหนดตำแหน่งที่แสดง)
2 JSON API
เรียกด้วย GET ได้เลย ไม่ต้องมีคีย์ · จำกัด 120 ครั้ง/นาที ต่อ IP · แคช 15–60 วินาที · ทุก endpoint เรียกได้ทั้ง /api/public/* และ /api/public/v1/*
https://bitnewsth.qd.je/api/public/server-status
https://bitnewsth.qd.je/api/public/server-status/history?range=6h
https://bitnewsth.qd.je/api/public/server-status/summary?range=24h
https://bitnewsth.qd.je/api/public/server-status/stream
กำลังโหลดข้อมูล...
fetch('https://bitnewsth.qd.je/api/public/server-status')
.then(res => res.json())
.then(data => {
console.log(data.server.status); // online | offline | maintenance
console.log(data.server.playerCount); // จำนวนผู้เล่น
console.log(data.server.maxPlayers); // ความจุเซิร์ฟเวอร์
console.log(data.roster.departments); // รายชื่อผู้เล่นแยกตามหน่วยงาน (v1.1.0)
console.log(data.joinUrl); // ลิงก์เข้าเซิร์ฟเวอร์
// ต้องแสดงเครดิต "ข้อมูลจาก API นักข่าว BIT NEWS" คู่กับสถานะที่นำไปแสดง
})
.catch(err => console.error('โหลดสถานะไม่สำเร็จ', err));
ฟิลด์ที่ให้ใช้: server.status, server.statusText, server.statusLabel, server.playerCount,
server.maxPlayers, server.queueCount, server.staffCount,
server.capacityPercent, session, roster, movement, notice,
joinUrl, updatedAt — ข้อมูลอื่นของเว็บไม่เปิดผ่าน API นี้
ส่วน history จะคืน points[] (at, players, queue, staff, maxPlayers, status)
รองรับ range=6h|24h|7d|30d สำหรับเอาไปวาดกราฟเอง — เลือกเฉพาะฟิลด์ที่อยากได้ด้วย ?fields=players,queue
ทั้ง summary (พีค/เฉลี่ย/คิว/uptime %) และ stream (SSE แจ้งเตือนเมื่อสถานะเปลี่ยน) ดูรายละเอียดต่อจากหัวข้อ 5
ตัวอย่างผลลัพธ์ของ /server-status (เพิ่มใน v1.1.0)
{
"ok": true,
"api": "BIT NEWS Journalist API",
"apiVersion": "1.1.0",
"server": {
"status": "online",
"statusText": "เปิดให้บริการ",
"statusLabel": "เปิดให้บริการขณะนี้",
"playerCount": 6,
"maxPlayers": 50,
"queueCount": 0,
"staffCount": 2,
"capacityPercent": 12
},
"session": {
"openedAt": "2026-10-12T06:27:05.000Z",
"openedForMs": 1980000,
"closedAt": "2026-10-12T07:00:47.000Z",
"closedForMs": 2940000
},
"roster": {
"listedPlayerCount": 6,
"departments": [
{ "key": "1556545073297100871", "name": "ประชาชน", "emojiUrl": "https://cdn.discordapp.com/emojis/1556545073297100871.png", "count": 2,
"players": [ { "name": "NEW4024v", "badge": null }, { "name": "popkark27", "badge": null } ] },
{ "key": "1550999320587927702", "name": "Bit Cities Police Department", "emojiUrl": "https://cdn.discordapp.com/emojis/1550999320587927702.png", "count": 1,
"players": [ { "name": "BoqR_64", "badge": "PD-01" } ] }
]
},
"movement": {
"lastJoin": { "name": "popkark27", "badge": null, "at": "2026-10-12T07:48:00.000Z", "atLabel": "12 ตุลาคม 2569" },
"lastLeave": { "name": "NEW4024v", "badge": null, "at": "2026-10-12T07:45:00.000Z", "atLabel": "12 ตุลาคม 2569" }
},
"notice": "เซิร์ฟเวอร์ไม่ได้เปิดให้บริการตลอด 24 ชั่วโมง เวลาเปิดให้บริการอาจเปลี่ยนแปลงตามจำนวนผู้เล่นและผู้ดูแล",
"joinUrl": "https://erlc.gg/join/BIT",
"updatedAt": "2026-10-12T07:50:12.000Z",
"credit": {
"required": true,
"text": "ข้อมูลจาก API นักข่าว BIT NEWS",
"provider": "BIT NEWS",
"url": "https://bitnewsth.qd.je/server-status",
"terms": "https://bitnewsth.qd.je/terms"
}
}
session คืนเวลาเปิด/ปิดรอบล่าสุดเป็น ISO 8601 พร้อมความยาวรอบเป็นมิลลิวินาที ·
roster.departments[].emojiUrl คือ URL รูปโมโดจิหน่วยงานจาก Discord CDN (อาจเป็น null ถ้าบอทไม่ได้แนบรูปมา) ·
roster.departments[].players[].badge คือรหัสประจำตัวในเซิร์ฟเวอร์ คืน null ได้ ·
ทุกฟิลด์ชุดใหม่นี้เป็นข้อมูลที่บอทสถานะรายงานจริง ถ้าบอทยังไม่ได้ส่งมา จะได้ค่า null หรืออาร์เรย์ว่าง ไม่ใช่ค่าที่ระบบเดาขึ้นมา
3 คู่มือ API กราฟ (History API) ฉบับละเอียด
API นี้คืนจุดข้อมูลย้อนหลังแบบย่อ สำหรับนำไปวาดกราฟจำนวนผู้เล่นและสถานะเซิร์ฟเวอร์เอง เหมาะกับทำแดชบอร์ด สถิติ หรือกราฟฝังเว็บ โดยไม่เปิดเผยข้อมูลอื่นของเว็บ
1. Endpoint
https://bitnewsth.qd.je/api/public/server-status/history
2. Query parameters
| พารามิเตอร์ | ประเภท | ค่าเริ่มต้น | คำอธิบาย |
|---|---|---|---|
range |
string | 6h |
ช่วงเวลาย้อนหลัง รองรับ 6h (6 ชั่วโมง), 24h (24 ชั่วโมง), 7d (7 วัน), 30d (30 วัน) — หากส่งค่าอื่น ระบบจะใช้ 6h แทน |
fields |
string | ทั้งหมด | เลือกฟิลด์ของจุดกราฟที่อยากได้ คั่นด้วยจุลภาค เช่น ?fields=players,queue — at และ status คงไว้เสมอ (ใช้ลดขนาดไฟล์เวลาเอาไปวาดกราฟเล็กๆ) |
3. ตัวอย่างผลลัพธ์ (200 OK)
{
"ok": true,
"api": "BIT NEWS Journalist API",
"apiVersion": "1.1.0",
"range": "24h",
"rangeLabel": "24 ชั่วโมง",
"rangeMs": 86400000,
"sampleCount": 288,
"fields": ["at", "players", "queue", "staff", "maxPlayers", "status"],
"points": [
{ "at": 1790982600000, "players": 12, "queue": 0, "staff": 1, "maxPlayers": 50, "status": "online" },
{ "at": 1790983200000, "players": 27, "queue": 3, "staff": 2, "maxPlayers": 50, "status": "online" },
{ "at": 1790983800000, "players": 0, "queue": 0, "staff": 0, "maxPlayers": 50, "status": "offline" }
],
"updatedAt": "2026-10-03T05:10:00.000Z",
"credit": {
"required": true,
"text": "ข้อมูลจาก API นักข่าว BIT NEWS",
"provider": "BIT NEWS",
"url": "https://bitnewsth.qd.je/server-status",
"terms": "https://bitnewsth.qd.je/terms"
}
}
4. คำอธิบายฟิลด์ทั้งหมด
| ฟิลด์ | ประเภท | คำอธิบาย |
|---|---|---|
ok | boolean | true เมื่อโหลดข้อมูลสำเร็จ |
api / apiVersion | string | ชื่อ API และเวอร์ชันที่ตอบกลับมารอบนี้ |
range | string | ช่วงเวลาที่ยังผลจริงหลังตรวจสอบค่าแล้ว |
rangeLabel | string | ชื่อช่วงเวลาแบบอ่านง่ายเช่น 24 ชั่วโมง |
rangeMs | number | ความยาวของช่วงเวลา หน่วยเป็นมิลลิวินาที (6h = 21600000, 24h = 86400000, 7d = 604800000, 30d = 2592000000) |
sampleCount | number | จำนวนจุดทั้งหมดของช่วงนั้น ก่อนระบบย่อข้อมูล |
fields | array | ฟิลด์ที่จุดกราฟมีจริงในรอบนี้ (เปลี่ยนตาม ?fields=) |
points | array | จุดกราฟ เรียงจากเก่าไปใหม่ (ถูกย่อให้ไม่เกิน ~121 จุด เพื่อให้ไฟล์เล็ก) |
points[].at | number | เวลาของจุดนั้น เป็น Unix timestamp หน่วยมิลลิวินาที (UTC) |
points[].players | number | null | จำนวนผู้เล่น ณ เวลานั้น — อาจเป็น null กรณีไม่มีข้อมูล ควรกันค่าไว้ก่อนวาดกราฟ |
points[].queue | number | null | จำนวนคนรอคิวเฉลี่ยของช่วงนั้น (สานกราฟคิวได้เลย ไม่ต้องยิงเพิ่ม) |
points[].staff | number | null | จำนวนผู้ดูแลในเซิร์ฟเวอร์เฉลี่ยของช่วงนั้น |
points[].maxPlayers | number | null | ความจุเซิร์ฟเวอร์ที่ระบบรู้ถึง ณ ช่วงนั้น |
points[].status | string | สถานะเซิร์ฟเวอร์ ณ จุดนั้น: online / offline / maintenance |
updatedAt | string | null | เวลาของจุดล่าสุด รูปแบบ ISO 8601 |
credit | object | ข้อมูลเครดิตที่ต้องแสดง: required, text, provider, url, terms |
5. การทำงานของระบบ & ข้อควรทราบ
- แคช 60 วินาที (มี stale-while-revalidate 120 วินาที) — เรียกถี่กว่านี้จะได้ข้อมูลเดิม แต่มี ETag ตรวจให้ ถ้าไม่เปลี่ยนจะคืน
304ไม่ส่ง body ซ้ำ - Rate limit 120 ครั้ง/นาที/IP — เกินจะได้ HTTP
429พร้อม headerRetry-After,X-RateLimit-Limit,X-RateLimit-Remaining,X-RateLimit-Reset - CORS เปิด
Access-Control-Allow-Origin: *เรียกจากเว็บไซต์ใดก็ได้ ไม่ต้องมีคีย์ - ระบบบันทึกสถานะอัตโนมัติเป็นช่วงสั้น ๆ แล้วนำมาสรุปเป็นจุดกราฟ — จุดล่าสุดอาจช้ากว่าเวลาจริงเล็กน้อย
- หาก
sampleCountมากกว่าจำนวนจุดที่ได้รับ แปลว่าระบบย่อจุดแล้ว (จุดแรกและจุดสุดท้ายถูกเก็บไว้เสมอ) - เวลาเป็น UTC (Unix ms) — แปลงเป็นเวลาไทยได้ด้วย
toLocaleString('th-TH', { timeZone: 'Asia/Bangkok' }) - ต้องแสดงเครดิต “ข้อมูลจาก API นักข่าว BIT NEWS” ควบคู่กับกราฟที่นำไปแสดงเสมอ
6. ตัวอย่างโค้ดดึงข้อมูลไปวาดกราฟ
async function loadChartPoints(range = '24h') {
const res = await fetch(
'https://bitnewsth.qd.je/api/public/server-status/history?range=' + range,
{ headers: { Accept: 'application/json' } }
);
if (!res.ok) throw new Error('โหลดไม่สำเร็จ: HTTP ' + res.status);
const data = await res.json();
// เวลาสิ้นสุด = ปัจจุบัน, เริ่มต้น = ย้อนหลังตาม rangeMs
const endAt = Date.now();
const startAt = endAt - data.rangeMs;
// แปลงจุดเป็นพิกัด: x ตามเวลา, y ตามจำนวนผู้เล่น
const xOf = at => ((at - startAt) / data.rangeMs) * chartWidth;
const maxPlayers = Math.max(...data.points.map(p => p.players || 0), 1);
const yOf = v => chartHeight - (v / maxPlayers) * chartHeight;
// วาดเส้น (ข้ามจุดที่ players = null ได้)
const path = data.points
.filter(p => p.players != null)
.map((p, i) => (i ? 'L' : 'M') + xOf(p.at) + ' ' + yOf(p.players))
.join(' ');
// อย่าลืมแสดงเครดิต "ข้อมูลจาก API นักข่าว BIT NEWS" ใต้กราฟ
return { points: data.points, path, credit: data.credit };
}
7. กรณีผิดพลาด
| HTTP | ตัวอย่างผลลัพธ์ | เกิดเมื่อ |
|---|---|---|
429 | { "error": "ส่งคำขอเร็วเกินไป กรุณาลองใหม่ภายหลัง" } | เรียกเกิน 120 ครั้ง/นาที จาก IP เดียวกัน (ดู header Retry-After) |
500 | { "ok": false, "error": "ไม่สามารถโหลดประวัติสถานะได้" } | ระบบฐานข้อมูลขัดข้องชั่วคราว |
503 | { "error": "ระบบกำลังบำรุงรักษา กรุณาลองใหม่ภายหลัง" } | เว็บอยู่ในโหมดบำรุงรักษา |
4 ตัวอย่างกราฟสดจาก API
กราฟด้านล่างดึงข้อมูลจริงจาก /api/public/server-status/history โดยตรง สลับช่วงเวลาได้ — เป็นตัวอย่างการนำ points[] ไปวาดกราฟ (ชี้เมาส์บนกราฟเพื่อดูรายละเอียด)
เส้นชมพู = ค่าสูงสุดในช่วงที่เลือก · แถบใต้กราฟ: เขียว = เปิด, เหลือง = ปรับปรุง, แดง = ปิด · ข้อมูลจาก API นักข่าว BIT NEWS
5 มาตรฐาน HTTP · เวอร์ชัน v1 · กรณีข้อผิดพลาด
API ทุกตัวตอบกลับในรูปแบบเดียวกัน (JSON + header ครบ) — เรียกได้ทั้ง /api/public/* (เดิม) และ /api/public/v1/* (เวอร์ชันปัจจุบัน เหมือนกันทุก field) แนะนำให้ยึด v1 ไว้นานเพราะมันคือ path ที่ดึงไปใช้ได้นาน ๆ โดยไม่หลุดของที่เคยเขียนไว้
1. Header ที่สำคัญ
| Header | ตัวอย่าง | ใช้ทำอะไร |
|---|---|---|
ETag + If-None-Match | "a1b2c3" | เก็บ ETag จากรอบก่อนแล้วยิงใหม่พร้อม If-None-Match — ถ้าไม่เปลี่ยนจะได้ 304 Not Modified ประหยัด bandwidth |
Cache-Control | public, max-age=60, stale-while-revalidate=120 | บอก CDN/browser ว่าจะแคชได้นานเท่าไรต่อ endpoint |
X-RateLimit-Limit | 120 | โควต้าสูงสุดต่อนาที |
X-RateLimit-Remaining | 97 | โควต้าที่เหลือในนาทีนี้ — เหลือต่ำควรผ่อนการเรียก |
X-RateLimit-Reset | 1790982000 | เวลาที่โคว toutes รีเซ็ต (Unix seconds) |
Retry-After | 23 | เมื่อถูก 429 — รอกี่วิก่อนยิงใหม่ |
X-API-Version | 1.0.0 | เวอร์ชันของข้อมูลที่ตอบกลับ |
X-Data-Credit | BIT NEWS Journalist API | แหล่งที่มาข้อมูล (ใช้กับ log/ตรวจสอบได้) |
2. ยกตัวอย่าง v1
let etag = null;
async function fetchStatus() {
const headers = { Accept: 'application/json' };
if (etag) headers['If-None-Match'] = etag; // ส่ง ETag รอบเก่ากลับไป
const res = await fetch('https://bitnewsth.qd.je/api/public/v1/server-status', { headers });
if (res.status === 304) return null; // ไม่มีอะไรเปลี่ยน — ไม่ต้องอัปเดตจอ
etag = res.headers.get('ETag'); // เก็บไว้ใช้รอบหน้า
const limit = res.headers.get('X-RateLimit-Remaining');
if (Number(limit) < 5) console.warn('ใกล้เกินโควต้าแล้ว เหลือ ' + limit);
const data = await res.json();
// อย่าลืมแสดงเครดิต "ข้อมูลจาก API นักข่าว BIT NEWS"
return data.server;
}
3. แบบแผนข้อผิดพลาด (เหมือนกันทุก endpoint)
| HTTP | code | ตัวอย่างผลลัพธ์ | เกิดเมื่อ |
|---|---|---|---|
404 | NOT_FOUND |
{ "ok": false, "error": "ไม่พบ endpoint นี้", "code": "NOT_FOUND", "docs": ".../api-demo" } |
path ไม่มีในระบบ — ดูรายการทั้งหมดที่ /api/public |
429 | RATE_LIMITED |
{ "ok": false, "error": "ส่งคำขอเร็วเกินไป กรุณาลองใหม่ภายหลัง", "code": "RATE_LIMITED" } |
เกิน 120 ครั้ง/นาที/IP (ดู Retry-After) |
429 | TOO_MANY_CONNECTIONS |
{ "ok": false, "error": "การเชื่อมต่อสดจาก IP นี้ครบจำนวนแล้ว", "code": "TOO_MANY_CONNECTIONS" } |
เปิด SSE เกิน 3 การเชื่อมต่อจาก IP เดียว |
500 | HISTORY_UNAVAILABLE / SUMMARY_UNAVAILABLE |
{ "ok": false, "error": "ไม่สามารถโหลดประวัติสถานะได้", "code": "HISTORY_UNAVAILABLE" } |
ฐานข้อมูลกราฟขัดข้องชั่วคราว — ลองใหม่ภายหลัง |
ทุก error มี ok: false, error (ข้อความไทย), code (ตอนเขียนโปรแกรม), และ docs (ลิงก์คู่มือ) —
แนะนำให้ ternary ตาม code ไม่ใช่ตามข้อความ เพราะข้อความอาจปรับรูปได้
6 สถิติสรุป (Summary) & OpenAPI
Summary — สรุปช่วงที่เลือกเป็นตัวเลขเดียวจบ ไม่ต้องเอาจุดกราฟมาคำนวนเอง เหมาะกับทำ “สถิติ 30 วัน” หรือการ์ด pequenoบนหน้าแรก ส่วน OpenAPI — สเปกมาตรฐานนำไป import ใน Postman/Insomnia หรือ generate client ได้ทันที
https://bitnewsth.qd.je/api/public/server-status/summary?range=24h
https://bitnewsth.qd.je/api/public/openapi.json
{
"ok": true,
"api": "BIT NEWS Journalist API",
"apiVersion": "1.1.0",
"range": "24h",
"rangeLabel": "24 ชั่วโมง",
"rangeMs": 86400000,
"summary": {
"sampleCount": 17280,
"peakPlayers": 43,
"peakAt": "2026-10-08T13:20:00.000Z",
"minPlayers": 0,
"avgPlayers": 24,
"peakQueue": 6,
"avgQueue": 1,
"uptimePercent": 31,
"openHours": 7.4,
"maintenanceHours": 0,
"offlineHours": 16.6,
"hasData": true
},
"updatedAt": "2026-10-08T18:45:00.000Z",
"credit": { "required": true, "text": "ข้อมูลจาก API นักข่าว BIT NEWS" }
}
range ไม่ส่งมาหรือส่งผิด = 24h ( summary เริ่มกลางวัน ส่วน history ยังคง 6h เหมือนเดิม) — uptimePercent เป็น null เมื่อช่วงนั้นยังไม่มีข้อมูลเลย
7 เรียลไทม์กับ SSE (ไม่ต้องวน fetch เอง)
เปิดสตรีมหนึ่งครั้ง แล้วรอรับเหตุการณ์มาวาดต่อ — ระบบ poll ให้อยู่ในตัว ส่งต่อเฉพาะตอนค่ามีเปลี่ยนแปลงจริง (ผู้เล่น/คิว/สถานะ) พร้อม ping ค้างการเชื่อมต่อทุก 15 วิ เหมาะกับหน้าจอที่เปิดทิ้งไว้ทั้งวัน
กด “เชื่อมต่อสตรีม” เพื่อทดสอบ — เหตุการณ์ที่เข้ามาจะโชว์ที่นี่
const source = new EventSource('https://bitnewsth.qd.je/api/public/server-status/stream');
source.addEventListener('snapshot', (ev) => {
const data = JSON.parse(ev.data); // มาครบชุดหนึ่งรอบ ตอนเพิ่งเชื่อมต่อ
render(data.server);
});
source.addEventListener('status', (ev) => {
const data = JSON.parse(ev.data); // ส่งต่อก็ต่อเมื่อค่าเปลี่ยนจริง
render(data.server);
});
source.onerror = () => {
// browser จะพยายาม reconnect เอง หากเน็ตหลุด
console.warn('สตรีมหลุด กำลังลองใหม่...');
};
// อย่าลืมแสดงเครดิต "ข้อมูลจาก API นักข่าว BIT NEWS" ใต้สถานะที่นำไปแสดง
เหตุการณ์: snapshot (ตอนเชื่อมต่อ), status (เมื่อค่าเปลี่ยน), heart beat : ping ทุก 15 วิ ·
จำกัด 3 การเชื่อมต่อต่อ IP · ปิดแล้วคำนวณแบนด์วิดท์แค่ไม่กี่ KB/ชม. · ไม่ต้องมีคีย์เหมือน endpoint อื่น
ทุกการนำข้อมูลไปใช้ (ทั้ง JSON API และวิดเจ็ต) ต้องแสดงเครดิตว่า “ข้อมูลจาก API นักข่าว BIT NEWS” อย่างชัดเจน โดยลิงก์กลับมาที่หน้าสถานะเซิร์ฟเวอร์ และห้ามดัดแปลงหรือลบเครดิตในวิดเจ็ต
📰 ข้อมูลจาก API นักข่าว BIT NEWS