VMS
BPP-VMS ULTIMATE MASTER GUIDE ตชด.215

คู่มือปฏิบัติการระบบความมั่นคงและกล้องวงจรปิดอัจฉริยะ (ฉบับรวมไฟล์เดียวบริบูรณ์ 100%)

🛡️ BPP-VMS (ตชด.215) • n8n Complete All-in-One Masterclass Guide

คู่มือการสอนฉบับสมบูรณ์: ติดตั้ง n8n, ออกแบบบอททุกประเภท & เจาะลึกการเขียนสคริปต์

รวบรวมครบจบในที่เดียว: ติดตั้ง Local ➔ นำเข้า Docker ➔ สถาปัตยกรรม 7 โหนด ➔ กฎ 4 เลเยอร์สร้างบอท ➔ แคตตาล็อกโหนด ➔ ชำแหละสคริปต์แจ้งเตือนบรรทัดต่อบรรทัด

หลักสูตร All-in-One ครบ 6 บทเรียน ● ไฟล์เดียวแชร์ได้ทันที
1

บทที่ 1: ขั้นตอนการติดตั้ง n8n บนเครื่อง Local (Windows) ตั้งแต่ 0

สำหรับผู้เริ่มต้นที่ยังไม่มีโปรแกรมอะไรในเครื่องเลย ทดลองต่อโหนดบนเครื่องตัวเองก่อนนำขึ้นระบบจริง

Local Host Mode
1

ติดตั้ง Node.js (เครื่องยนต์รันโปรแกรม)

n8n พัฒนาด้วย JavaScript บน Node.js ผู้เรียนต้องติดตั้งเวอร์ชัน LTS (แนะนำ v20.x ขึ้นไป)

ดาวน์โหลดตัวติดตั้ง: https://nodejs.org (เลือก LTS)
เมื่อติดตั้งเสร็จ ให้เปิด PowerShell ตรวจสอบคำสั่ง:
node -v
npm -v
2

คำสั่งรัน n8n บนเครื่อง Local (2 ทางเลือก)

วิธี A: รันด่วนทันที ไม่ต้องติดตั้ง (แนะนำที่สุด):
npx n8n
วิธี B: ติดตั้งแบบถาวรลงเครื่อง Windows:
npm install n8n -g
n8n start
3

เข้าใช้งาน Web UI ครั้งแรก

เมื่อขึ้นข้อความว่า "Editor is now accessible via:" ให้เปิดเบราว์เซอร์ไปที่:

กรอกอีเมลและรหัสผ่านเพื่อตั้ง Owner Account แล้วเริ่มสร้าง Workflow ได้ทันที
4

การ Export Workflow ออกเป็นไฟล์ JSON

เมื่อต่อโหนดบน Local เสร็จ ให้ดึงไฟล์ออกมาเพื่อเตรียมเอาเข้า Docker:

  • ผ่านหน้าเว็บ: กดเมนูจุด 3 จุดมุมบนขวาใน Workflow ➔ เลือก Download
  • ผ่าน Terminal: n8n export:workflow --all --output=backup.json
2

บทที่ 2: ขั้นตอนนำ n8n เข้าสู่ Docker ในโปรเจกต์ (Production 24/7)

เปลี่ยนจากการเปิด Terminal ค้างไว้ มาเป็นบริการในเบื้องหลังที่รันอัตโนมัติและเชื่อมคุยกับ Frigate

📌 ทำไมต้องย้ายเข้า Docker ในโปรเจกต์?

  • ทำงาน 24 ชั่วโมงอัตโนมัติ: รีบูตเครื่องคอมพิวเตอร์ Container ก็ยังคงสตาร์ทขึ้นมาเองอัตโนมัติ
  • เชื่อมต่อ Frigate/กล้องโดยตรง: สามารถยิง API หา Frigate ที่ http://frigate:5000 ผ่านเครือข่าย Docker ภายในได้เลย ไม่ต้องผ่านอินเทอร์เน็ต
  • Named Volume (n8n-data): โหนดและการตั้งค่าทั้งหมดถูกเซฟลงฮาร์ดดิสก์ถาวร ปิดคอนเทนเนอร์ข้อมูลก็ไม่หาย

💻 คำสั่ง Terminal ที่ใช้สอน:

# เข้าโฟลเดอร์ Frigate NVR
cd D:\BPP-VMS\frigate-nvr

# สตาร์ท n8n service บน Docker
docker compose up -d n8n

# นำไฟล์ workflow จาก Local import เข้าสู่ Container
docker exec n8n-service n8n import:workflow --input=/n8n-init/workflows/bpp_vms_master_bot_workflow.json

# สั่ง restart n8n container 1 ครั้งเพื่อโหลดค่าใหม่
docker restart n8n-service
3

บทที่ 3: สถาปัตยกรรม 7 โหนดของ BPP-VMS & การสกัดข้อมูล (Data Extraction)

ทำความเข้าใจบทบาทของแต่ละโหนดในระบบจริง พร้อมการสกัดข้อความ (AI) และภาพถ่าย (EXIF)

คลิกที่โหนดเพื่อดูการตั้งค่า
n8n Canvas Workflow: BPP-VMS Master Bot (จำลองผังกระดานจริง)
👆 คลิกโหนดด้านล่างเพื่อเปิดหน้าต่างตั้งค่าเสมือนจริง
TRIGGER
#1 Webhook
POST /bpp-vms-master
Output: body
ROUTER
#2 Switch Type
type == chat/photo/alert
0 ➔ AI Chat
1 ➔ Photo EXIF
2 ➔ LPR Alert
EXTRACTION PIPELINES
#3 AI Agent ⭐
สกัด Entity ภาษาไทย
Suspect, Drugs, Vehicle
#6 Photo Code ⭐
สกัด EXIF GPS & Dossier
lat/lng ➔ Heatmap
#5 Alert Formatter
จัดฟอร์แมต LPR Card
Telegram Card Text
AI PLUGINS
#4 Gemini Model
Flash Lite (สมอง AI)
● Sub-node: Model Pin
#8 Watchlist Tool
HTTP API เช็กทะเบียน
● Sub-node: Tool Pin
OUTPUT
#7 Respond Webhook
JSON Output
200 OK Response
โหนด #3 (คลิกเลือกอยู่)

AI Intelligence Agent (การสกัดข้อมูลข้อความภาษาไทย)

@n8n/n8n-nodes-langchain.agent
โหมดตั้งค่า: Parameters Panel Mockup
👉 1. ต้องกดตรงไหน? (ขั้นตอนเลือกโหนดและเปิดเมนูบน Canvas)
💡 3. ทำไมต้องทำแบบนี้? (เหตุผลเชิงสถาปัตยกรรม & กฎเหล็ก)
✍️ 2. ตารางเจาะลึกการตั้งค่าพารามิเตอร์ 4 มิติ (4D Parameter Deep Dive) 📐 แสดงผลเต็มความกว้าง พอดีหน้าจอ ไม่ต้องเลื่อนแนวนอน (Zero Horizontal Scroll)
📋 โค้ด/คอนฟิกเต็มของโหนดนี้ (Raw JSON / Script): พร้อมนำไปวางใน n8n

        
🔗 สเต็ปการต่อสายเชื่อมโยงสู่โหนดถัดไป (Step-by-Step Wiring & Node Navigator)
ขั้นตอนที่ 1 จาก 8
🎯 คำแนะนำการลากสายเชื่อมต่อ (Wiring Guide):
4

บทที่ 4: กฎเหล็กสากล 4 เลเยอร์ (IPO Model) & 4 ตระกูลบอทยอดนิยม

หลักคิดการออกแบบบอททุกตัวในโลก ก่อนเริ่มต่อโหนดบนกระดาน Canvas

1. INPUT (Trigger)
อะไรเป็นตัวปลุกบอท?

คนทักแชท LINE, กล้องจับป้ายทะเบียนได้, หรือถึงเวลา 08:00 น.

2. CONTEXT (Router)
เตรียมข้อมูล & แยกประเภท

แยกประเภทข้อความ (Switch), ไปดึงประวัติเก่าจาก Database มาประกอบ

3. PROCESS (Brain)
สมองกล & สกัดข้อมูล

ใช้ AI LLM ตีความภาษา สกัด Entity หรือใช้ Code คำนวณตรรกะ/GPS

4. OUTPUT (Action)
ทำอะไรต่อหลังเสร็จ?

ตอบกลับแชท, ยิง Alert เข้า Telegram, บันทึกลงตาราง หรือสั่งเปิดไม้กั้น

💬 1. Conversational & Extraction Bot

ตอบแชทเจ้าหน้าที่ รับรายงานเหตุการณ์ แล้วสกัดชื่อ, ของกลาง, ทะเบียน, พิกัด

[LINE Trigger] ➔ [AI Agent + Gemini] ➔ [Tool Search DB] ➔ [LINE Reply]

🚨 2. Event-Driven Alert Bot

เฝ้าระวังสัญญาณจากกล้อง LPR เมื่อเจอป้ายเฝ้าระวัง ให้ยิงรูปพร้อมพิกัดแจ้งชุดจับกุมทันที

[Frigate Webhook] ➔ [Switch Blacklist] ➔ [Code Formatter] ➔ [Telegram Alert]

⏰ 3. Scheduled Report Bot

ทำงานตามเวลา เช่น ทุก 07:00 น. สรุปสถิติรถผ่านด่านรอบ 24 ชม. ส่งผู้บังคับบัญชา

[Schedule 07:00] ➔ [HTTP API] ➔ [AI Summary] ➔ [Broadcast Chat]

🔄 4. Data Sync & ETL Bot

รับรูปภาพ สกัด EXIF GPS บันทึกลงฐานข้อมูล และส่งต่อไปแสดงผลบน Heatmap / i2

[Webhook Photo] ➔ [Code EXIF GPS] ➔ [Postgres Insert] ➔ [NAS Storage]
5

บทที่ 5: สารานุกรมประเภทโหนดใน n8n (5 หมวดหมู่หลัก)

คลิกเลือกหมวดหมู่โหนดด้านล่างเพื่อดูหน้าที่ การใช้งานจริง และตัวอย่างโหนด

6
🎓 ZERO TO HERO: COMPLETE JAVASCRIPT MASTERCLASS

บทที่ 6: ชำแหละสคริปต์ Code Node บอทแจ้งเตือนทุกรูปแบบ ละเอียดทุกบรรทัด ทุกคำศัพท์

คู่มือสอนเขียนสคริปต์การแจ้งเตือนทั้งหมด 4 รูปแบบจริงของระบบ BPP-VMS (ตชด.215) ฉบับคนเพิ่งเริ่มเขียนโปรแกรม พร้อมตาราง 4 มิติ และพรีวิว Telegram & LINE

Node.js ES6+ Runtime
📘

ภาคปูพื้นฐาน: "คู่มือภาษา JavaScript ฉบับคนเพิ่งเริ่มเขียนโปรแกรม" (Beginner Foundation)

💡 1. ภาษาที่ทำอยู่นี้คือภาษาอะไร?

คือภาษา JavaScript (Node.js ES6+) ซึ่งเป็นภาษายอดนิยมอันดับ 1 ของโลก และเป็นภาษาดั้งเดิม (Native) ที่ n8n ใช้งานเป็นเครื่องยนต์หลัก

ทำไม n8n ต้องใช้ภาษานี้?
เพราะ JavaScript เกิดมาเพื่อจัดการข้อมูลแบบ JSON ได้เร็วที่สุดระดับมิลลิวินาที ไม่ต้องคอมไพล์ รันคำสั่งได้ทันทีที่เหตุการณ์กล้องส่งข้อมูลเข้ามา!
📖 2. คำศัพท์โปรแกรมเมอร์ที่ต้องเจอ
  • const = กล่องเก็บของห้ามเปลี่ยนค่า (ปลอดภัยที่สุด)
  • let = กล่องเก็บของที่ยอมให้เปลี่ยนค่าใหม่ได้
  • Dot Notation (.) = เช่น body.plate คือเปิดกล่อง body แล้วหยิบของชื่อ plate
  • || (OR Fallback) = ถ้าซ้ายมือเป็นค่าว่าง ให้ใช้ค่าสำรองฝั่งขวา (กันระบบพัง)
  • `...` (Backticks) = ข้อความที่แทรกตัวแปรตรงๆ ด้วย ${ตัวแปร}
⚖️ 3. สามกฎเหล็กของ n8n ที่ห้ามลืม
1. การรับข้อมูลเข้า: ใช้คำสั่งสากล $input.item.json เพื่ออ่านก้อนข้อมูลจากโหนดก่อนหน้า
2. การส่งข้อมูลออก: ต้องครอบด้วย return { json: { ... } }; เสมอ หากส่งข้อความเปล่า โหนดถัดไปจะอ่านไม่เจอ!
3. ป้องกันค่าว่าง (Null-Safe): ใส่ || {} หรือ || 'ไม่ระบุ' เสมอ ป้องกันระบบ Crash
🎯 เลือกศึกษาและคัดลอกสคริปต์ตามรูปแบบการแจ้งเตือนจริงของระบบ BPP-VMS (4 รูปแบบ):
คลิกแท็บเพื่อสลับดูโค้ดและคำอธิบาย
✈️
ย้ายระบบไปติดตั้งเครื่องใหม่? (New Machine Deployment)
ขั้นตอนการสร้างบอตกับ @BotFather, การหา Chat ID (เดี่ยว/กลุ่มสายตรวจ), การกรอก .env และคำสั่งทดสอบ 1-Click
🚨 สคริปต์รูปแบบที่ 1: ตรวจจับป้ายทะเบียนรถต้องสงสัย/เฝ้าระวัง (LPR Watchlist Alert)

ทำงานเมื่อกล้องตรวจจับป้ายทะเบียน (LPR) อ่านแผ่นป้ายแล้วตรวจพบว่าตรงกับบัญชีเฝ้าระวัง หมายจับ หรือรถต้องสงสัยในฐานข้อมูล BPP-VMS สคริปต์นี้จะสกัดข้อมูลรถ เจ้าของ ระดับอันตราย และสร้างลิงก์คลิปวิดีโอ VMS พร้อมส่งแจ้งเตือนทันที

JAVASCRIPT CODE NODE (LPR ALERT FORMATTER) Node.js ES6+
// =========================================================================
// สคริปต์ที่ 1: จัดการแจ้งเตือนป้ายทะเบียนรถต้องสงสัย (LPR Watchlist Alert)
// ภาษา: JavaScript (Node.js ES6+) บน n8n Code Node
// =========================================================================

// 1. ดึงข้อมูล JSON ที่ได้รับจากโหนดก่อนหน้า พร้อมใส่ || {} ดักค่าว่างป้องกัน Error
const body = $input.item.json.body || {};

// 2. สกัดฟิลด์ข้อมูลสำคัญ (ตรวจจับชื่อกล้องอัตโนมัติ ถ้าไม่มีให้ใส่ชื่อกลุ่มกล้องแทน)
const plate = body.plate || '1กข 4567';
const province = body.province || 'สุรินทร์';
// ดึงชื่อกล้องก่อน (camera_name หรือ camera) หากไม่มีให้สลับไปดึงชื่อกลุ่มกล้อง (camera_group หรือ group) แทน
const camera = body.camera_name || body.camera || body.camera_group || body.group || 'cam-checkpoint-01 (ด่านตรวจหนองกวาง)';
const timestamp = body.timestamp || new Date().toLocaleString('th-TH', { timeZone: 'Asia/Bangkok' });
const dangerLevel = body.danger_level || '🔴 อันตรายสูง (หมายจับคดียาเสพติด)';
const ownerName = body.owner_name || 'นายสมพร ใจกล้า';
const eventId = body.event_id || 'ev-lpr-992140';

// 3. สร้างลิงก์คลิปวิดีโอเหตุการณ์ย้อนหลัง (VMS Clip URL) สำหรับให้เจ้าหน้าที่กดดูบนมือถือ
const serverHost = 'vms.bpp215.police.go.th';
const clipUrl = `https://${serverHost}/clip.html?id=${eventId}&cam=${encodeURIComponent(camera)}&type=lpr`;

// 4. จัดรูปแบบข้อความแจ้งเตือนสำหรับ Telegram Bot (รองรับ HTML แท็ก <b>, <a>, <code>)
const telegramMessage = `🚨 [แจ้งเตือนด่วน BPP-VMS: พบรถต้องสงสัย!]
━━━━━━━━━━━━━━━━━━
🚗 ทะเบียน: ${plate}
📍 จังหวัด: ${province}
⚠️ ระดับอันตราย: ${dangerLevel}

👤 ผู้ครอบครอง/เฝ้าระวัง: ${ownerName}
🎥 กล้องตรวจจับ: ${camera}
🕒 เวลา: ${timestamp}

🎬 ดูคลิปย้อนหลัง: <a href="${clipUrl}">▶️ คลิกเพื่อเปิดดูวิดีโอ (MP4)</a>
🆔 Event ID: <code>${eventId}</code>
━━━━━━━━━━━━━━━━━━`;

// 5. จัดรูปแบบข้อความแจ้งเตือนสำหรับ LINE (จัดแบบข้อความธรรมดาพร้อมสัญลักษณ์ชัดเจน)
const lineMessage = `🚨 [แจ้งเตือนด่วน BPP-VMS: พบรถต้องสงสัย!]
----------------------------------
🚗 ทะเบียน: ${plate}
📍 จังหวัด: ${province}
⚠️ ระดับอันตราย: ${dangerLevel}
👤 ผู้เฝ้าระวัง: ${ownerName}
🎥 กล้อง: ${camera}
🕒 เวลา: ${timestamp}
🎬 ดูคลิป: ${clipUrl}
🆔 Event: ${eventId}`;

// 6. ส่งผลลัพธ์ออกตามโครงสร้างบังคับของ n8n: ครอบด้วย { json: { ... } } เสมอ!
return {
  json: {
    alert_type: 'LPR_WATCHLIST',
    plate: plate,
    province: province,
    camera: camera,
    timestamp: timestamp,
    danger_level: dangerLevel,
    owner_name: ownerName,
    clip_url: clipUrl,
    event_id: eventId,
    telegram_text: telegramMessage,
    telegram_parse_mode: 'HTML',
    line_text: lineMessage,
    broadcast_ready: true
  }
};
🔍

คู่มือชำแหละโค้ด 6 หมวดหมู่แบบจับมือทำ: ทำไมต้องเขียนแบบนี้ & เพราะอะไร (Line-by-Line & Keyword Deep Dive)

อธิบายละเอียดยิบทุกบรรทัด ทุกคำศัพท์ ทุกตัวแปร และผลกระทบเชิงระบบหากไม่เขียนตามนี้

📌 หมวดที่ 1: การดึงข้อมูล JSON จากโหนดก่อนหน้า (Data Ingress)
const body = $input.item.json.body || {};
  • const: คำสั่งสร้างกล่องเก็บข้อมูลแบบ "ค่าคงที่" ห้ามโค้ดบรรทัดอื่นมาเขียนทับ ปลอดภัยสูงสุด
  • $input.item.json: วัตถุ Built-in ของ n8n เพื่อเข้าถึงก้อนข้อมูล JSON แถวปัจจุบันที่ส่งมาจากโหนดก่อนหน้า
  • .body: เปิดเข้าไปในคีย์ย่อยชื่อ body ซึ่งบรรจุเนื้อหาที่ Webhook ของกล้องส่งมา
  • || {} (เสื้อชูชีพกันระบบล่ม): ทำไมต้องเขียน? หาก Webhook ส่งข้อมูลว่างเปล่ามา การใส่ || {} จะเปลี่ยนค่า undefined ให้เป็นออบเจกต์ว่างเปล่า {} ทันที หากไม่ใส่ โค้ดบรรทัดต่อไปที่เรียก body.plate จะเกิดข้อผิดพลาดรุนแรง TypeError: Cannot read properties of undefined ทำให้บอทหยุดทำงานทันที!
📌 หมวดที่ 2: สกัดฟิลด์ข้อมูลสำคัญ 7 ตัวแปร พร้อมค่าสำรอง (Extraction & Fallbacks)

ทำไมต้องแยกเป็น 7 บรรทัดและมี || 'ค่าเริ่มต้น' ทุกบรรทัด?

const plate = body.plate || '1กข 4567';
• สกัดเลขทะเบียนรถ ถ้าอ่านไม่ทัน/ไม่มีค่า ให้ใช้ค่าจำลองเพื่อทดสอบรันใน n8n ได้ทันที
const province = body.province || 'สุรินทร์';
• สกัดชื่อจังหวัด ป้องกันการแสดงผลเป็นคำว่า undefined บนหน้าจอเจ้าหน้าที่
const camera = body.camera_name || body.camera || body.camera_group || body.group || '...';
• ตรวจจับชื่อกล้อง & กลุ่มกล้องอัตโนมัติ: ตรวจหาชื่อกล้องภาษาไทย (camera_name หรือ camera) ก่อนเป็นอันดับแรก หากไม่มี ให้สลับไปดึงชื่อกลุ่มกล้อง (camera_group หรือ group) แทน เพื่อให้ศูนย์สั่งการทราบโซนเกิดเหตุเสมอ ป้องกันค่าว่าง
const timestamp = body.timestamp || new Date().toLocaleString(...);
• คำศัพท์สำคัญ: new Date().toLocaleString('th-TH', { timeZone: 'Asia/Bangkok' }) บังคับเวลาไทย UTC+7 ป้องกันเวลาบอทช้ากว่าเวลาจริง 7 ชม. จาก Docker Server
const dangerLevel = body.danger_level || '🔴 อันตรายสูง...';
• สกัดระดับความอันตรายพร้อมไอคอนสี เพื่อให้เห็นระดับภัยคุกคามทันทีจาก Lock Screen
const eventId = body.event_id || 'ev-lpr-992140';
• สกัดรหัสอ้างอิงเหตุการณ์ (UUID / Primary Key) สำหรับค้นหาแฟ้มคดีและคลิปย้อนหลัง
📌 หมวดที่ 3: การสร้างลิงก์คลิปวิดีโอเหตุการณ์ย้อนหลัง (Deep Link Construction)
const serverHost = 'vms.bpp215.police.go.th';
const clipUrl = `https://${serverHost}/clip.html?id=${eventId}&cam=${encodeURIComponent(camera)}&type=lpr`;
  • serverHost: แยกโดเมนแม่ข่ายออกมา เพื่อให้แก้ไขง่ายในจุดเดียวเมื่อเปลี่ยน IP/Domain
  • เครื่องหมาย Backtick `...` & ${...}: แม่พิมพ์ข้อความเชื่อมตัวแปรโดยไม่ต้องใช้เครื่องหมายบวก + ให้ตาลาย
  • encodeURIComponent(camera): ทำไมต้องมี? ตัวแปร camera มีภาษาไทยและช่องว่าง หากใส่เข้าไปใน URL ตรงๆ ลิงก์จะขาดและกดเปิดวิดีโอบนสมาร์ตโฟนไม่ติด ฟังก์ชันนี้จะแปลงภาษาไทยเป็นรหัส URL สากล (เช่น %E0%B8...) ทำให้เปิดคลิปได้ 100%
📌 หมวดที่ 4: ฟอร์แมต Telegram Bot (HTML Mode)

• ใช้แท็ก <b>...</b> ทำตัวหนาเน้นหัวข้อ
• ใช้แท็ก <a href="...">...</a> แปลงลิงก์ยาวยืดเป็นปุ่มคลิกดูคลิปสั้นๆ
• ใช้แท็ก <code>${eventId}</code> ฟีเจอร์พิเศษของ Telegram: แตะที่ Event ID 1 ครั้งจะคัดลอกลงคลิปบอร์ดทันที!

📌 หมวดที่ 5: ฟอร์แมต LINE (Clean Text Mode)

• ทำไมต้องแยกตัวแปรกับ Telegram? เพราะ LINE ไม่รองรับแท็ก HTML หากส่ง <b> ไป LINE จะโชว์แท็กดิบๆ บนจออย่างน่าเกลียด
• LINE จึงต้องใช้ข้อความธรรมดา (Plain Text) และวางลิงก์ ${clipUrl} ตรงๆ เพื่อให้แอป LINE แปลงเป็นลิงก์กดได้อัตโนมัติ

📌 หมวดที่ 6: การส่งผลลัพธ์ออกตามโครงสร้างบังคับ n8n (Return Structure)
return { json: { alert_type: 'LPR_WATCHLIST', plate: plate, ..., broadcast_ready: true } };
  • กฎเหล็กสูงสุดของ n8n: ข้อมูลที่จะส่งให้โหนดถัดไป (เช่น โหนด Telegram Bot หรือโหนด HTTP Request ของ LINE) ต้องครอบด้วยออบเจกต์ที่มีคีย์ชื่อว่า json เสมอ หาก return ข้อความเปล่าๆ โหนดถัดไปจะอ่านไม่เจอ (No data returned)
  • telegram_parse_mode: 'HTML': สั่งให้โหนด Telegram Bot เรนเดอร์แท็กตัวหนาและลิงก์คลิป
  • broadcast_ready: true: ตัวแปรยืนยันความสมบูรณ์เพื่อให้โหนด Switch หรือ Filter ถัดไปตรวจสอบก่อนยิงแจ้งเตือน
📖 ตารางชำแหละ 4 มิติ: เจาะลึกคำสั่ง ความหมาย เหตุผล และผลลัพธ์ครบทุกบรรทัด (สคริปต์ LPR)
ท่อนโค้ด / คำสั่งสคริปต์ คืออะไร & หมายความว่ายังไง? ทำทำไม? (เหตุผล & ตรรกะ) ทำแล้วได้อะไร? (ผลลัพธ์)
const body = $input.item.json.body || {};
🔴 กฎเหล็ก
ประกาศตัวแปรคงที่ const เพื่อรับข้อมูลจากโหนดก่อนหน้าผ่าน $input.item.json และใส่ || {} ป้องกันกรณีไม่มีฟิลด์ body ป้องกัน Runtime Crash ข้อผิดพลาด Cannot read property of undefined หาก Webhook ส่งข้อมูลมาไม่ครบ ได้ออบเจกต์ body ที่ปลอดภัย 100% ต่อให้ไม่มีข้อมูลก็ไม่ทำให้สคริปต์พังกลางคัน
const plate = body.plate || '1กข 4567';
🟡 ค่าสำรอง
อ่านค่าหมายเลขทะเบียนรถจาก body.plate ถ้าไม่มีค่าให้ใช้ค่าสตริงตัวอย่าง '1กข 4567' ในการทดสอบรันบน n8n ผู้เรียนอาจยิง JSON ว่างเข้ามา การมี Fallback ช่วยให้เห็นหน้าตาผลลัพธ์โดยไม่ต้องกรอกข้อมูลครบทุกฟิลด์ ข้อความแจ้งเตือนจะมีเลขทะเบียนรถปรากฏเสมอ ไม่กลายเป็นคำว่า undefined บนหน้าจอเจ้าหน้าที่
const province = body.province || 'สุรินทร์';
อ่านค่าจังหวัดจาก body.province พร้อมสำรองค่าจังหวัดที่ตั้งศูนย์ กล้อง LPR บางมุมอาจจับภาพจังหวัดไม่ชัด การมี Fallback ช่วยรักษาฟอร์แมตข้อความให้สมบูรณ์ ข้อความแจ้งเตือนมีชื่อจังหวัดแสดงเสมอ ป้องกันเจ้าหน้าที่สับสน
const camera = body.camera_name || body.camera || body.camera_group || body.group || '...';
⚡ Auto Camera / Group Detect
ตรวจจับชื่อกล้องอัตโนมัติ (camera_name หรือ camera) หากไม่มีให้สลับไปดึงชื่อกลุ่มกล้อง (camera_group หรือ group) แทนแบบ Cascading Fallback กรณีกล้องบางจุดยังไม่ได้ตั้งชื่อเฉพาะ หรือเป็นกล้องที่ดึงผ่านสตรีมแบบกลุ่ม การดึงชื่อกลุ่มกล้องแทนจะช่วยให้ศูนย์สั่งการยังรู้ว่าเกิดเหตุในโซน/สายทางใด ได้ตำแหน่งกล้องหรือโซนกลุ่มกล้องเสมอ 100% ไม่ว่างเปล่าและไม่ขึ้นคำว่า undefined
new Date().toLocaleString('th-TH', { timeZone: 'Asia/Bangkok' })
⚡ เวลาไทย
ฟังก์ชันแปลงเวลาปัจจุบันของระบบเป็นรูปแบบภาษาไทยและล็อกเขตเวลาเป็นเวลาประเทศไทย (UTC+7) Server n8n มักตั้งค่าเวลาเครื่องเป็น UTC หากไม่กำหนดเขตเวลา เวลาในข้อความจะช้ากว่าเวลาจริง 7 ชั่วโมง วันเวลาเกิดเหตุตรงกับเวลาจริงในประเทศไทย 100% ใช้เป็นหลักฐานทางคดีได้ทันที
encodeURIComponent(camera)
🔴 สำคัญมาก
ฟังก์ชันแปลงอักขระพิเศษและภาษาไทย เช่น เว้นวรรค วงเล็บ หรือสระ ให้เป็นรหัส URL เปอร์เซ็นต์ (เช่น %20) ชื่อกล้องภาษาไทยถ้าต่อเข้าไปใน URL โดยตรง ลิงก์จะขาด หรือเบราว์เซอร์จะไม่สามารถเปิดวิดีโอคลิปได้ ได้ลิงก์เปิดคลิปวิดีโอที่คลิกแล้วทำงานได้บนสมาร์ตโฟนและคอมพิวเตอร์ทุกเครื่อง 100%
<b>...</b>, <a href="...">, <code>
แท็กภาษา HTML ที่ Telegram Bot API รองรับสำหรับการจัดรูปแบบข้อความ สร้างตัวหนา แปลง URL ยาวยืดให้เป็นปุ่มคลิก และแท็ก <code> ช่วยให้แตะเพื่อคัดลอก Event ID ได้ทันที ข้อความอ่านง่าย เป็นระเบียบ สะดวกต่อการใช้งานบนหน้าจอมือถือสายตรวจ
const lineMessage = `...`;
ตัวแปรเก็บข้อความสำหรับส่งเข้า LINE โดยใช้ Plain Text ปราศจากแท็ก HTML LINE ไม่รองรับแท็ก HTML หากนำข้อความของ Telegram มาส่งจะเห็นแท็กหลุดออกมาบนหน้าจอ ข้อความบน LINE สวยงาม สะอาดตา และลิงก์คลิปวิดีโอคลิกได้ปกติ
return { json: { ... } };
🔴 กฎเหล็ก
ส่งผลลัพธ์กลับสู่ Workflow โดยต้องครอบด้วยออบเจกต์ที่มีคีย์ชื่อว่า json n8n ถูกออกแบบให้ประมวลผลข้อมูลผ่านคีย์ json หาก return สตริงเปล่าๆ โหนดถัดไปจะแจ้งว่า "No data returned" ข้อมูลทั้งหมดถูกส่งต่อไปยังโหนด Telegram, LINE หรือ Respond to Webhook ได้อย่างราบรื่น
📦 ชุดข้อมูล JSON จำลองสำหรับทดสอบ (Test Payload):
{
  "type": "alert",
  "body": {
    "plate": "2ขง 8899",
    "province": "สุรินทร์",
    "camera_name": "กล้องด่านตรวจหนองกวาง 01",
    "camera": "cam-checkpoint-01",
    "camera_group": "กลุ่มจุดตรวจหลักสาย 333",
    "danger_level": "🔴 อันตรายสูงสุด (หมายจับคดียาเสพติดร้ายแรง)",
    "owner_name": "นายวันชัย ดวงแก้ว",
    "event_id": "ev-lpr-20260910-01"
  }
}

💡 วิธีทดสอบ: คัดลอก JSON ด้านบนนี้ไปวางในคำสั่ง cURL หรือยิงเข้า Webhook URL โหนดแรกของท่านเพื่อทดสอบการฟอร์แมต

📱 ตัวอย่างข้อความจริงที่ได้รับบน Telegram & LINE (Live Preview):
✈️ TELEGRAM BOT VIEW:
🚨 [แจ้งเตือนด่วน BPP-VMS: พบรถต้องสงสัย!] ━━━━━━━━━━━━━━━━━━ 🚗 ทะเบียน: 2ขง 8899 📍 จังหวัด: สุรินทร์ ⚠️ ระดับอันตราย: 🔴 อันตรายสูงสุด (หมายจับคดียาเสพติดร้ายแรง) 👤 ผู้ครอบครอง/เฝ้าระวัง: นายวันชัย ดวงแก้ว 🎥 กล้องตรวจจับ: cam-checkpoint-01 (ด่านตรวจหนองกวาง) 🕒 เวลา: 10/9/2026, 19:30:00 🎬 ดูคลิปย้อนหลัง: ▶️ คลิกเพื่อเปิดดูวิดีโอ (MP4) 🆔 Event ID: ev-lpr-20260910-01 ━━━━━━━━━━━━━━━━━━
💬 LINE NOTIFY / CHAT VIEW:
🚨 [แจ้งเตือนด่วน BPP-VMS: พบรถต้องสงสัย!] ---------------------------------- 🚗 ทะเบียน: 2ขง 8899 📍 จังหวัด: สุรินทร์ ⚠️ ระดับอันตราย: 🔴 อันตรายสูงสุด 👤 ผู้เฝ้าระวัง: นายวันชัย ดวงแก้ว 🎥 กล้อง: ด่านตรวจหนองกวาง 🎬 ดูคลิป: https://vms.bpp215.police.go.th/clip.html?id=...
✈️
🚀 NEW MACHINE COMMISSIONING RUNBOOK

คู่มือการติดตั้งและตั้งค่า Telegram Bot เมื่อนำระบบ BPP-VMS ไปลงเครื่องใหม่

ขั้นตอนการสร้างบอตกับ @BotFather, การหา Chat ID (แชทเดี่ยว & กลุ่มสายตรวจ), การกรอกค่าใน .env และ n8n Credential พร้อมคำสั่งทดสอบ 1-Click Verification

Zero-to-Hero Telegram Setup
🤖 ขั้นตอนที่ 1: สร้างบอตใหม่กับ @BotFather 5 นาที

เปิดแอป Telegram บนมือถือหรือ PC แล้วค้นหาผู้ใช้ชื่อ @BotFather (ต้องมีเครื่องหมายติ๊กถูกสีฟ้า Verified เท่านั้น ป้องกันบอตปลอม)

ลำดับการพิมพ์คำสั่งคุยกับ @BotFather:
1. /newbot
➔ บอตจะถามชื่อแสดงผล: พิมพ์ชื่อบอต เช่น BPP-VMS Alert Bot
2. ตั้ง Username ของบอต (ต้องลงท้ายด้วย bot)
➔ เช่น bpp215_surin_alert_bot (ห้ามซ้ำกับผู้อื่น)
3. รับ HTTP API Token
➔ @BotFather จะส่ง Token มาให้ เช่น 7182938475:AAFl9xK... (เก็บเป็นความลับ ห้ามเผยแพร่)
⚠️ กฎเหล็กเมื่อนำบอตเข้ากลุ่มสายตรวจ (ห้ามลืม):
  • พิมพ์คำสั่ง /setprivacy ➔ เลือกบอต ➔ กดเลือก Disable (เพื่อให้บอตอ่านข้อความคำสั่งและรูปภาพในกลุ่มสายตรวจได้)
  • พิมพ์คำสั่ง /setjoingroups ➔ เลือกบอต ➔ กดเลือก Enable (อนุญาตให้ดึงเข้ากลุ่มได้)
🆔 ขั้นตอนที่ 2: วิธีค้นหา Chat ID (เดี่ยว vs กลุ่มสายตรวจ) สำคัญที่สุด
👤 แบบที่ 1: ส่งแจ้งเตือนหาตัวเองคนเดียว (Direct Chat)

1. ทักบอตที่เราเพิ่งสร้าง แล้วกดปุ่ม Start (หรือพิมพ์ /start) 1 ครั้ง เพื่อเปิดสิทธิ์ให้บอตส่งข้อความหาเราได้
2. ไปที่บอต @userinfobot หรือ @RawDataBot แล้วกด Start จะได้ตัวเลข Id: 123456789 (เป็นเลขบวก)

👥 แบบที่ 2: ส่งเข้ากลุ่มศูนย์ปฏิบัติการ / สายตรวจ (Group Chat - แนะนำสูงสุด)
  1. ดึงบอตที่เราสร้างเข้ากลุ่มสายตรวจ
  2. ตั้งค่าบอตให้เป็น "Admin" ของกลุ่ม: เปิดสิทธิ์ Send Messages, Send Photos, Embed Links
  3. ดึงบอต @RawDataBot เข้ากลุ่มชั่วคราว แล้วมองหาบรรทัด "chat": { "id": -100xxxxxxxxxx }
  4. เมื่อได้เลขแล้ว ให้เตะ @RawDataBot ออกจากกลุ่มได้ทันที
🔴 จุดตายห้ามพลาด (Critical Rule):

Chat ID ของกลุ่ม Telegram ต้องมีเครื่องหมายลบ - หรือ -100 นำหน้าเสมอ! เช่น -1002148921456
หากเผลอตัดเครื่องหมายลบออก บอตจะส่งไม่ไปและเกิดข้อผิดพลาด 400 Bad Request: chat not found ทันที!

⚙️ ขั้นตอนที่ 3: นำค่าไปใส่ในเครื่องใหม่ (2 ทางเลือก) Config
ทางเลือก A: ตั้งค่าบนเซิร์ฟเวอร์ BPP-VMS (Docker Stack):

เปิดไฟล์ frigate-nvr/.env บนเครื่องเซิร์ฟเวอร์ใหม่ แล้วกรอกค่าทั้ง 3 บรรทัด:

# 1. แจ้งเตือนป้ายทะเบียน LPR และ AI ตรวจจับวัตถุประจำกล้อง
LPR_TELEGRAM_BOT_TOKEN=7182938475:AAFl9xKxxxxxxxxxxxxxxxxxxxxx
LPR_TELEGRAM_CHAT_ID=-1002148921456

# 2. บอตสืบสวนแฟ้มคดี NAS และสกัดพิกัด GPS ภาพถ่าย (nas-bot-service)
TELEGRAM_BOT_TOKEN=7182938475:AAFl9xKxxxxxxxxxxxxxxxxxxxxx
คำสั่งรีสตาร์ทคอนเทนเนอร์เพื่อให้ค่าใหม่มีผล: docker compose up -d lpr-service nas-bot-service
ทางเลือก B: ตั้งค่าบน n8n Automation Workflows:
  1. เข้าหน้าเว็บ n8n: http://<IP_เครื่องใหม่>:5678
  2. ไปที่เมนู Credentials (ไอคอนรูปกุญแจ) ➔ คลิก Add Credential
  3. พิมพ์ค้นหา Telegram API ➔ วางค่า Access Token ที่ได้จาก @BotFather ➔ คลิก Save
  4. ใน Workflow ของ Chapter 3 ให้เลือก Credential นี้ แล้วใส่ Chat ID ที่ช่องปลายทาง
🚀 ขั้นตอนที่ 4: คำสั่งทดสอบยิงข้อความจริง (1-Click Test) Verification

ก่อนเปิดใช้งานระบบจริง ให้ทดสอบยิงข้อความผ่านคำสั่ง Terminal บนเครื่องใหม่ เพื่อยืนยันว่า Token และ Chat ID ใช้งานได้ 100%:

คำสั่งทดสอบผ่าน cURL (รันได้บน PowerShell / Bash):
curl -s -X POST "https://api.telegram.org/bot<TOKEN>/sendMessage"      -d "chat_id=<CHAT_ID>"      -d "text=🚨 [ทดสอบระบบ BPP-VMS] บอตเชื่อมต่อเครื่องใหม่สำเร็จเรียบร้อยแล้วครับ! 🟢"      -d "parse_mode=HTML"
หรือทดสอบตรงจาก Docker Container ของระบบ BPP-VMS:
docker exec -it lpr-service python -c "from app import send_telegram_alert; send_telegram_alert('🚨 ทดสอบส่งแจ้งเตือนจากเครื่องใหม่สำเร็จ 100%', None)"
✅ หากมีข้อความแจ้งเตือนเด้งขึ้นใน Telegram แชทหรือกลุ่มทันที แปลว่าพร้อมเปิดใช้งานทั้งระบบ 100%!
🛠️
ตารางวิเคราะห์และแก้ปัญหาที่พบบ่อยเมื่อตั้งค่า Telegram Bot บนเครื่องใหม่ (Troubleshooting Checklist)
รหัสข้อผิดพลาด / อาการ สาเหตุที่แท้จริง วิธีแก้ไขหน้างาน วิธีป้องกันในระยะยาว
401 Unauthorized Token ไม่ถูกต้อง, มีช่องว่างเกิน หรือคัดลอกมาไม่ครบทุกตัวอักษร กลับไปที่ @BotFather พิมพ์ /token แล้วคัดลอก Token ทั้งชุดมาวางใหม่ใน .env ห้ามแก้ไข Token ด้วยมือ ให้ใช้การคลิกคัดลอกโดยตรงจาก Telegram
400 Bad Request: chat not found 1. แชทส่วนตัว: ผู้รับยังไม่เคยกด /start กับบอต
2. แชทกลุ่ม: ลืมใส่เครื่องหมายลบ -100
- แชทส่วนตัว: ทักบอตแล้วกดปุ่ม Start 1 ครั้ง
- แชทกลุ่ม: ตรวจสอบ Chat ID ให้ขึ้นต้นด้วย -100 เช่น -1002148921456
ดึงบอต @RawDataBot เข้ากลุ่มเพื่อตรวจสอบ ID ที่มีเครื่องหมายลบติดมาให้แน่นอน
400 Bad Request: can't parse entities ข้อความมีแท็ก HTML ผิดรูปแบบ เช่น มี <b> แต่ไม่มี </b> ปิด ตรวจเช็กใน Code Node ให้ปิดแท็ก HTML ให้ครบ หรือส่งข้อความสำรองแบบ Plain Text ใช้ Helper Function ปิดแท็กอัตโนมัติ หรือใช้ Fallback plain_text ในสคริปต์
403 Forbidden: not enough rights to send photos บอตในกลุ่มสายตรวจไม่ได้สิทธิ์ส่งภาพ หรือกลุ่มล็อกสิทธิ์ไม่ให้สมาชิกส่งมีเดีย เข้าไปที่ Group Settings ➔ Administrators ➔ ตั้งบอตเป็น Admin และเปิดสิทธิ์ Post Photos & Links ตั้งบอตเป็น Admin เสมอเมื่อใช้งานในกลุ่มภารกิจความมั่นคง
ศูนย์ปฏิบัติการระบบกล้องและเทคโนโลยี BPP-VMS (ตชด.215) • รวมหลักสูตรการสอน n8n ครบ 6 บทเรียนในไฟล์เดียว