ผมชอบโพสต์แจกของบนเพจ Tommy Talk แล้วให้คนคอมเมนต์คำสั้นๆ ใต้โพสต์ เช่น "skill" จากนั้นระบบจะทักไปส่งลิงก์ให้ใน inbox ระบบตอบกลับอัตโนมัติของ Facebook ทำแบบแยกตามโพสต์ไม่ได้ เครื่องมือแชทบอทในตลาดทำได้แต่ต้องจ่ายค่าบริการ ผมเลยให้ Claude Code เขียนระบบนี้ให้ใช้เอง คู่มือนี้เล่าแนวคิด วิธีทำงาน และขั้นตอนสร้างทั้งหมด พร้อมภาพจากระบบที่ใช้งานจริง
จากวางแผนถึงขึ้นจริง
1 วัน
ฐานข้อมูลทั้งระบบ
3 ตาราง
ครอบคลุมจับคำไทย
29 เทสต์
ค่าฐานข้อมูล (Neon)
ฟรี

ที่มา: ทำไมไม่ใช้ระบบตอบกลับอัตโนมัติของ Facebook
ระบบตอบกลับอัตโนมัติที่ Facebook มีให้ ตั้งคีย์เวิร์ดได้ชุดเดียวกับทั้งเพจ ถ้ามีโพสต์แจกของ 3 โพสต์ แล้วทุกโพสต์ให้คอมเมนต์ว่า "สนใจ" เหมือนกัน ระบบจะส่งของชิ้นเดียวกันหมด
เครื่องมือแชทบอทในตลาดทำเรื่องนี้ได้ แต่ต้องจ่ายค่าบริการ ผมเลยให้ Claude Code เขียนให้ ตั้งแต่วางแผนจนขึ้น production ภายในวันเดียว แล้วใช้กับเพจจริงต่อเลย ค่าใช้จ่ายที่เหลือคือค่าโฮสต์บน Vercel กับฐานข้อมูล Neon ซึ่งผมใช้แพ็กเกจฟรี
ทำไปทำไม: แจกของดี เพื่อให้คนที่ใช่เดินเข้ามาเอง
สิ่งที่ทำอยู่เรียกว่า lead magnet คือแจกของที่มีประโยชน์จริงให้ฟรี แลกกับการที่คนสนใจยอมเดินเข้ามาคุยกับเรา เป้าหมายของระบบนี้คือเปลี่ยน "คนที่กดไลก์แล้วเลื่อนผ่าน" ให้กลายเป็น "คนที่อยู่ใน inbox ของเพจ" ส่วนการไม่ต้องนั่งตอบคอมเมนต์เองเป็นผลพลอยได้
ทำไมคนที่เข้ามาถึงมีแนวโน้มเป็นลูกค้าเป้าหมาย
คนที่ยอมพิมพ์คำว่า "skill" เพื่อขอคู่มือ "ตัวตน x AI" คือคนที่อยากใช้ AI สร้างตัวตนบนโซเชียลอยู่แล้ว ไม่มีใครคอมเมนต์ขอคู่มือเรื่องที่ตัวเองไม่สนใจ ของที่แจกเลยทำหน้าที่คัดคนให้เราไปในตัว แต่ละแคมเปญจึงได้รายชื่อคนที่สนใจเรื่องเดียวกัน แยกกันตามโพสต์
ได้อะไรเพิ่มจากการให้คนคอมเมนต์
- ▸คนพิมพ์ตอบใต้โพสต์ ทำให้โพสต์มีความเคลื่อนไหว คนอื่นที่ผ่านมาเห็นว่ามีคนขอกันเยอะก็อยากได้บ้าง
- ▸ข้อความเข้า inbox เป็นจุดเริ่มบทสนทนา จากนี้เพจคุยต่อได้ ไม่ต้องรอให้ลูกค้าทักมาเอง
- ▸ถ้าใส่ "คำตัวเลือก" ไว้ในข้อความ (เช่น "ได้รับแล้ว" หรือ "อยากรู้เพิ่ม") พอลูกค้ากด แชทจะเปิด และเพจคุยต่อได้อีก 24 ชั่วโมงตามกฎของ Messenger ส่งข้อมูลคอร์สหรือบริการที่เกี่ยวกับของที่แจกตามไปได้เลย
ของที่แจกแล้วได้ผล 4 ข้อ
- 01เป็นเรื่องเดียวกับสิ่งที่เราขาย ถ้าขายเวิร์กช็อป LINE OA ก็แจกคู่มือ LINE OA ถ้าแจกของที่คนทั่วไปชอบแต่ไม่เกี่ยวกับธุรกิจเรา จะได้ inbox เยอะแต่ไม่มีใครซื้อ
- 02ใช้ได้ทันที คู่มือ เทมเพลต prompt หรือ skill ที่เปิดแล้วทำตามได้เลย ดีกว่าบทความยาวที่ต้องอ่านอีกนาน
- 03ของต้องดีจริง คนที่ได้ของดีจากเราฟรี จะเชื่อว่าของที่เราขายก็น่าจะดี ถ้าแจกของห่วย เขาจะไม่เปิดข้อความถัดไปของเราอีก
- 04ให้คอมเมนต์คำสั้นๆ คำเดียว "skill" หรือ "สนใจ" พิมพ์ง่าย และระบบจับได้แม่น
เคล็ดลับเล็กๆ
ตั้งคำให้ต่างกันในแต่ละโพสต์ (เช่น "skill" กับ "line") เวลาย้อนดูประวัติ จะรู้ทันทีว่าคนไหนมาจากของชิ้นไหน
แนวคิดหลัก 5 ข้อ
| ข้อ | แนวคิด | ความหมาย |
|---|---|---|
| 1 | หนึ่งแคมเปญ = หนึ่งของที่จะแจก ผูกกับโพสต์ที่เลือก | คำว่า "สนใจ" ใต้โพสต์ A ได้คู่มือ A ใต้โพสต์ B ได้คู่มือ B ถ้าไม่ตรงแคมเปญของโพสต์นั้น ค่อยไปดูแคมเปญที่ตั้งเป็น "ทุกโพสต์" |
| 2 | จับคำ 3 แบบ | เหมือนกับ, ประโยค, และคีย์เวิร์ด (ดูตารางด้านล่าง) |
| 3 | ทำสองอย่างพร้อมกัน | ส่งข้อความเข้า inbox ของคนคอมเมนต์ แล้วตอบใต้คอมเมนต์ว่า "ส่งให้ทาง inbox แล้ว 🙏" (สุ่มจากหลายข้อความ จะได้ไม่ดูเป็นสแปม) |
| 4 | ส่ง inbox ไม่ผ่าน ห้ามตอบใต้คอมเมนต์ | คำตอบใต้คอมเมนต์มักบอกว่าส่งของไปแล้ว ถ้าส่งไม่สำเร็จแต่ยังตอบไป ลูกค้าจะเห็นข้อความที่ไม่จริง ข้อนี้เพิ่มทีหลังหลังจากเจอเคสจริง |
| 5 | เก็บข้อมูลเท่าที่ต้องใช้ | ระบบบันทึกเฉพาะคอมเมนต์ที่ตรงแคมเปญ คอมเมนต์ทั่วไปใต้โพสต์ไม่ถูกเก็บ |
จับคำ 3 แบบ แบบเดียวกับที่คนทำเพจคุ้นมือ
| แบบ | ทำงานยังไง | ตัวอย่าง |
|---|---|---|
| เหมือนกับ | คอมเมนต์ต้องเป็นคำนี้พอดี ไม่นับช่องว่าง เครื่องหมาย อีโมจิ | ตั้ง "skill" จับ "Skill 🙏" แต่ไม่จับ "skill ครับ" |
| ประโยค | มีวลีนี้อยู่ในคอมเมนต์ พิมพ์เว้นวรรคต่างกันก็จับได้ | ตั้ง "สนใจ" จับ "ส น ใ จ ค่ะ" |
| คีย์เวิร์ด | มีคำนี้อยู่ตรงไหนก็ได้ | ตั้ง "skill" จับ "skillครับ" |
ถ้าตรงหลายแบบ ระบบเลือก "เหมือนกับ" ก่อน แล้วตามด้วย "ประโยค" แล้วค่อย "คีย์เวิร์ด" ภาษาไทยไม่เว้นวรรคระหว่างคำ คีย์เวิร์ดภาษาไทยจึงต้องจับแบบ "มีอยู่ข้างใน" ส่วนคำภาษาอังกฤษต้องเป็นคำเต็ม ไม่อย่างนั้นตั้งคำว่า "ai" แล้วจะไปจับ "said" ด้วย
ระบบทำงานยังไง
คนคอมเมนต์ "skill" ↓ Facebook ส่ง webhook (field: feed) มาที่ /api/webhooks/facebook ↓ ตรวจลายเซ็น X-Hub-Signature-256 ว่ามาจาก Meta จริง ↓ ตอบ 200 กลับทันที แล้วค่อยทำงานต่อเบื้องหลัง ↓ ข้ามคอมเมนต์ของเพจเอง / คอมเมนต์ที่แก้ไข / สติกเกอร์ล้วน ↓ หาแคมเปญ (โพสต์ที่เลือกก่อน → แคมเปญทุกโพสต์) แล้วจับคำ ↓ ไม่ตรง: จบ ไม่บันทึกอะไร ↓ ตรง: กันซ้ำ (comment_id ห้ามซ้ำ / คนเดิมได้ครั้งเดียวต่อแคมเปญ) ↓ ส่ง Private Reply เข้า inbox → ผ่านแล้วค่อยตอบใต้คอมเมนต์ ↓ บันทึกผล: ส่งแล้ว / ส่งได้บางส่วน / ล้มเหลว + เหตุผล
ต้องตอบ 200 ทันที
ถ้าตอบ Meta ช้า Meta จะส่ง webhook ซ้ำ และถ้าช้าบ่อยอาจปิด webhook ทิ้ง ใน Next.js ใช้ after() ให้งานรันต่อหลังส่ง response ไปแล้ว
กฎของ Facebook ที่ต้องรู้ก่อนเริ่ม
- 01Private Reply ส่งได้ 1 ข้อความต่อ 1 คอมเมนต์ ภายใน 7 วัน ถ้าอยากส่งทั้งรูปและลิงก์ ให้ใช้ Generic Template คือการ์ดที่มีรูป หัวข้อ และปุ่ม รวมอยู่ในข้อความเดียว
- 02คอมเมนต์หนึ่งอัน ใครส่งก่อนได้สิทธิ์ ถ้าเพจมีระบบอื่นตั้งแคมเปญไว้ที่โพสต์เดียวกัน ระบบที่ช้ากว่าจะได้ error (#10900) Activity already replied to
- 03ใส่ Quick Replies (คำตัวเลือก) ได้ พอลูกค้ากด แชทจะเปิด และคุยต่อได้ 24 ชั่วโมงตามกฎ Messenger
- 04แอปที่ยังอยู่ Development mode ใช้ได้แค่กับบัญชีที่มี role ในแอป ถ้าจะใช้กับคนทั่วไปแบบระยะยาว ต้องทำ Business Verification แล้วยื่น App Review ขอ Advanced Access
- 05สิทธิ์ที่ต้องใช้มี 6 ตัว ดูตารางด้านล่าง
| สิทธิ์ (permission) | ใช้ทำอะไร |
|---|---|
| pages_show_list | ดูรายชื่อเพจ |
| pages_read_engagement | อ่านข้อมูลการมีส่วนร่วมของเพจ |
| pages_read_user_content | อ่านข้อความในคอมเมนต์ |
| pages_manage_metadata | subscribe webhook |
| pages_manage_engagement | ตอบใต้คอมเมนต์ |
| pages_messaging | ส่งข้อความเข้า inbox |
เครื่องมือที่ใช้
| ส่วน | ใช้อะไร |
|---|---|
| เว็บ + API | Next.js 16 (App Router) บน Vercel |
| ฐานข้อมูล | Neon Postgres ผ่าน Vercel Marketplace (แพ็กเกจฟรี) + Drizzle ORM |
| Graph API: Webhooks, Private Replies, Facebook Login | |
| หน้าตา | Tailwind CSS ฟอนต์ Fredoka + Mitr |
| เทสต์ | Vitest (29 เทสต์ ครอบคลุมการจับคำภาษาไทยและลอจิกส่งข้อความ) |
ฐานข้อมูลมีแค่ 3 ตาราง
| ตาราง | เก็บอะไร |
|---|---|
| pages | เพจที่เชื่อมไว้ และ page token ที่เข้ารหัส AES-256-GCM ก่อนเก็บ |
| campaigns | ชื่อ เปิด/ปิด โพสต์ที่เลือก คำที่จับ ข้อความเข้า inbox ปุ่ม คำตัวเลือก ข้อความตอบใต้คอมเมนต์ |
| comment_events | ประวัติคอมเมนต์ที่ตรงแคมเปญ comment_id ตั้งเป็น unique เพื่อกันส่งซ้ำตอน Meta ยิง webhook ซ้ำ |
คู่มือสร้างทีละขั้น (8 ขั้น)
เริ่มโปรเจกต์
สร้างโปรเจกต์และฐานข้อมูล
ใช้ Vercel CLI สร้างโปรเจกต์แล้วเพิ่ม Neon ได้จากเทอร์มินัลเลย
vercel project add comment-bot vercel link --yes --project comment-bot vercel integration add neon -n comment-bot-db -m region=sin1 -m auth=false -p free_v3
คำสั่งสุดท้ายสร้างฐานข้อมูลที่สิงคโปร์ แล้วดึง DATABASE_URL ลง .env.local ให้อัตโนมัติ
อย่าลืม vercel.json
ถ้าสร้างโปรเจกต์ผ่าน CLI แบบนี้ ให้ใส่ vercel.json ที่มี {"framework": "nextjs"} ด้วย ไม่อย่างนั้นตอน deploy จะฟ้องว่าหาโฟลเดอร์ public ไม่เจอ
ตั้งค่า
ตั้งค่า env
| ตัวแปร | ใช้ทำอะไร |
|---|---|
| META_APP_ID, META_APP_SECRET | จากหน้า App settings → Basic ของ Meta App |
| META_VERIFY_TOKEN | คำลับอะไรก็ได้ ใช้ตอนยืนยัน webhook |
| ENCRYPTION_KEY | กุญแจเข้ารหัส page token |
| AUTH_SECRET | ใช้เซ็น cookie ของหน้าแอดมิน |
| ADMIN_PASSWORD | รหัสเข้าหน้าแอดมิน (ตั้งยาวๆ) |
| APP_URL | URL ของเว็บ เช่น https://your-app.vercel.app |
สุ่มค่าลับด้วยคำสั่งด้านล่าง แล้วใส่ทั้งใน .env.local และ Vercel
openssl rand -base64 32 vercel env add META_APP_SECRET production
Webhook
ทำ route รับ webhook
route เดียวรับสองงาน GET ใช้ตอนตั้งค่าครั้งแรก Meta ส่ง hub.verify_token มา ถ้าตรงกับ META_VERIFY_TOKEN ให้ตอบ hub.challenge กลับไป ส่วน POST เกิดทุกครั้งที่มีคอมเมนต์ ต้องตรวจลายเซ็นก่อนเสมอ
const expected = "sha256=" + createHmac("sha256", appSecret).update(rawBody).digest("hex");
// เทียบกับ header x-hub-signature-256 ด้วย timingSafeEqualต้องอ่าน body ดิบ
ต้องอ่าน body เป็นข้อความดิบ (await req.text()) ก่อน parse JSON ไม่อย่างนั้นลายเซ็นจะไม่ตรง
ส่งของ
ส่งของเข้า inbox และตอบใต้คอมเมนต์
// ส่งเข้า inbox: ใช้ comment_id เป็นผู้รับ
POST /{page-id}/messages
{ "recipient": { "comment_id": "..." }, "message": { ... } }
// ตอบใต้คอมเมนต์
POST /{comment-id}/comments
{ "message": "ส่งให้ทาง inbox แล้ว 🙏" }ฟิลด์ message เป็นได้ 3 แบบตามที่เลือกในหน้าแคมเปญ คือข้อความธรรมดา, button template (ข้อความ + ปุ่มลิงก์ได้ 3 ปุ่ม) หรือ generic template (การ์ดรูป + ปุ่ม) ใส่ {name} ในข้อความได้ ระบบแทนด้วยชื่อคนคอมเมนต์
เชื่อมเพจ
ปุ่ม "เชื่อมเพจ Facebook"
ขั้นตอน OAuth ที่ใช้งานได้จริงกับเพจใน Business Portfolio
- 01พาไปหน้าขอสิทธิ์ของ Facebook พร้อมสิทธิ์ 6 ตัวข้างบน
- 02แลก code เป็น user token แล้วแลกต่อเป็น token อายุยาว
- 03ถาม /debug_token ว่าผู้ใช้ติ๊กให้สิทธิ์เพจไหนบ้าง (ดูจาก granular_scopes)
- 04ดึง page token ทีละเพจด้วย GET /{page-id}?fields=name,access_token
- 05สั่ง POST /{page-id}/subscribed_apps ให้เพจส่ง webhook มาที่แอปเรา
- 06เข้ารหัส token แล้วเก็บลงฐานข้อมูล
ข้อ 3 สำคัญที่สุด
ตอนแรกผมใช้ /me/accounts ตามตัวอย่างทั่วไป แล้วได้กลับมา 28 เพจที่ไม่ได้ให้สิทธิ์ (token ใช้ไม่ได้ทุกอัน) และเพจที่ติ๊กให้สิทธิ์จริงกลับไม่อยู่ในรายชื่อ เพราะเพจนั้นอยู่ใน Business Portfolio
page token ที่ได้จาก user token อายุยาวจะไม่หมดอายุ เชื่อมครั้งเดียวใช้ได้ยาว
Meta App
ตั้งค่า Meta App
ไปที่ developers.facebook.com แล้วสร้างแอป จากนั้นใส่ค่าตามนี้
- 01App settings → Basic: ช่อง App domains ใส่โดเมนของเว็บ แล้วกด Enter ให้กลายเป็นป้าย ก่อนกด Save
- 02Use cases: เพิ่ม "Manage everything on your Page" แล้วเพิ่มสิทธิ์ให้ครบ 6 ตัว
- 03Facebook Login → Settings: Valid OAuth Redirect URIs ใส่ https://your-app.vercel.app/api/auth/facebook/callback (กด Enter อีกเหมือนกัน)
- 04Webhooks → Page: ใส่ Callback URL, Verify Token แล้ว Subscribe feed, messages, messaging_postbacks
ในหน้าตั้งค่าของระบบ ผมทำช่องพร้อมปุ่มคัดลอกไว้ให้ทุกค่า จะได้ไม่ต้องพิมพ์เอง ส่วน Verify Token ซ่อนเป็นจุดไว้ กดรูปตาเพื่อดู

ตั้งค่าเพจ
ตั้งค่าเพจ (ถ้าเคยใช้แชทบอทตัวอื่นมาก่อน)
- 01ไปที่การตั้งค่าเพจ → Advanced messaging → กด "แก้ไข" ที่แอปของเรา → เปิด "ควบคุมการสนทนา"
- 02ปิดแคมเปญในระบบเดิมที่ผูกโพสต์เดียวกัน หรือถอดระบบเดิมออกที่ "การผสานรวมธุรกิจ (Business integrations)"
ทดสอบ
สร้างแคมเปญและทดสอบ
- 01เข้าเว็บ → ตั้งค่า → เชื่อมเพจ Facebook → ติ๊กเพจที่จะใช้
- 02กด "+ แคมเปญใหม่" → เลือกโพสต์ → ใส่คำ → ใส่ข้อความเข้า inbox และข้อความตอบใต้คอมเมนต์ → เปิด ON → บันทึก
- 03คอมเมนต์ทดสอบด้วยบัญชีส่วนตัว (ไม่ใช่ในนามเพจ)
- 04ดูผลที่หน้า "ประวัติคอมเมนต์" ต้องขึ้นสถานะ "ส่งแล้ว"


ปัญหาที่เจอจริงระหว่างทำ
| อาการ | สาเหตุ | วิธีแก้ |
|---|---|---|
| Facebook ขึ้น "ไม่สามารถโหลด URL ได้" ตอนกดเชื่อมเพจ | App domains / Redirect URI ว่าง พิมพ์ไว้แต่ไม่ได้กด Enter | ใส่ใหม่ กด Enter ให้เป็นป้าย แล้วกด Save |
| "Invalid Scopes: pages_read_user_content, pages_manage_engagement" | แอปยังไม่มี use case ที่มีสิทธิ์สองตัวนี้ | เพิ่ม use case "Manage everything on your Page" |
| เชื่อมแล้วได้เพจที่ไม่ได้เลือก ดึงโพสต์ไม่ได้ | /me/accounts คืนเพจผิดชุด | ใช้ granular_scopes จาก /debug_token แทน (ขั้นที่ 5) |
| (#10) แอพอื่นกำลังควบคุมเธรดนี้อยู่ | ระบบแชทบอทเดิมถือสิทธิ์แชทของเพจ | เปิด "ควบคุมการสนทนา" ให้แอปเรา |
| (#10900) Activity already replied to | ระบบเดิมยังตั้งแคมเปญที่โพสต์เดียวกัน และตอบเร็วกว่าเรา 1 ถึง 2 วินาที | ปิดหรือถอดระบบเดิม |
| This user cant reply to this activity | คนคอมเมนต์ในนามเพจ ปิดรับข้อความ หรือคอมเมนต์ถูกลบ | แก้ไม่ได้ฝั่งเรา ระบบข้ามการตอบใต้คอมเมนต์ให้เอง |
| ลูกค้าได้คำตอบใต้คอมเมนต์ 2 รอบ | สองระบบตอบโพสต์เดียวกัน | ใช้ระบบเดียวต่อโพสต์ |
| deploy แล้วฟ้อง "No Output Directory named public" | โปรเจกต์ Vercel ยังไม่รู้ว่าเป็น Next.js | ใส่ vercel.json ที่มี {"framework": "nextjs"} |
ผมเอา error ทุกตัวในตารางนี้ไปแปลเป็นภาษาไทยในหน้าประวัติคอมเมนต์ด้วย เห็นแล้วรู้เลยว่าต้องไปแก้ตรงไหน ไม่ต้องไปค้นรหัส error เอง
เรื่องความปลอดภัยและข้อมูล
- ▸ตรวจลายเซ็นทุก webhook ใครยิงข้อมูลปลอมเข้ามาจะโดนตอบ 401
- ▸page token เข้ารหัสก่อนเก็บลงฐานข้อมูล
- ▸หน้าแอดมินล็อกด้วยรหัสผ่าน ตั้งรหัสยาวๆ เพราะหน้านี้คุมเพจได้
- ▸เก็บเฉพาะคอมเมนต์ที่ตรงแคมเปญ
- ▸ห้าม commit .env.local ขึ้น git
ต่อยอดได้อีก
- ▸พอลูกค้ากดคำตัวเลือก ให้ส่งข้อความชุดถัดไป (ต้อง subscribe messages แล้วรับ event ฝั่ง Messenger)
- ▸รองรับ Instagram ซึ่งมี Private Reply แบบเดียวกัน
- ▸สถิติรายวันต่อแคมเปญ ว่าแต่ละโพสต์แจกของไปกี่คน
- ▸ยื่น App Review ให้ครบ จะได้ใช้กับคนทั่วไปได้โดยไม่ต้องห่วงข้อจำกัดของ Development mode




