PPURIPAT
HOME/BLOG
↩ BACK

สร้างแอปโหวตแบบเรียลไทม์ ฟรี 100% ด้วย Google Apps Script — copy ชีตเดียวจบ

แจกแอปโหวตเรียลไทม์บน Google Apps Script + Sheets ไม่ต้องเขียนโค้ด ไม่ต้องมีเซิร์ฟเวอร์ แค่ copy ชีตเทมเพลตแล้ว deploy — ใช้งานได้จริงใน 10 นาที

BY
PURIPAT
PUBLISHED
2026.07.18
READ TIME
3 MIN
WORDS
561
GHXIN
สร้างแอปโหวตแบบเรียลไทม์ ฟรี 100% ด้วย Google Apps Script — copy ชีตเดียวจบ
FIG. 01 — สร้างคำถามโพล ได้ลิงก์โหวตพร้อมแชร์ทันทีSOURCE: AUTHOR
หน้าจอสร้างโพล ใส่คำถามและตัวเลือก 2-6 ข้อ
หน้าจอสร้างโพล ใส่คำถามและตัวเลือก 2-6 ข้อ
FIG. 01 — หน้าสร้างโพลของโฮสต์

Quick Poll คือแอปโหวตสำหรับชุมชน: มีคนตั้งคำถาม แชร์ลิงก์ คนอื่นโหวตจากมือถือ แล้วหน้าผลโหวตก็อัปเดตแบบเกือบเรียลไทม์ พอโฮสต์กดปิดโหวต ผลลัพธ์จะฟรีซกลายเป็น "มติ" สุดท้าย — ตัว UI เป็นภาษาไทยสำหรับชุมชนไทยโดยเฉพาะ แต่ตัวโค้ดข้างในไม่มีอะไรผูกกับภาษาไทยเลย มีแค่ข้อความหน้าจอเท่านั้น

ส่วนที่น่าสนใจจริง ๆ ไม่ใช่ตัวแอป แต่คือมันรันอยู่บน Google Apps Script + Google Sheets และฟรีสนิท ไม่มีเซิร์ฟเวอร์ ไม่มีค่าฐานข้อมูล ไม่มีค่าโฮสติ้ง ไม่มีขั้นตอน build ใด ๆ ทั้งสิ้น แค่มีบัญชี Google กับ Google Sheet หนึ่งชีตที่กด copy ได้ ก็ตั้งแอปนี้ให้รันได้จริงภายในเวลาไม่นานกว่าที่ใช้อ่านโพสต์นี้

ทดลองใช้ของจริง: https://script.google.com/macros/s/AKfycbzSLqqmyW6JALmtsT49oJZhcWz7KMTvl4FR3QL3nF2CZM2516WYiclAO4PSmq6r9EYApg/exec

โค้ดทั้งหมด: ทุกไฟล์ที่พูดถึงด้านล่าง — Code.gs, create.html, vote.html, results.html — ติดมาในชีตเทมเพลตด้านล่างนี้แล้ว ไม่ต้องไปหาที่อื่น


§01ทำไมต้อง Apps Script

Apps Script คือชั้นสคริปต์ฟรีของ Google ที่อยู่เบื้องหลัง Sheets, Docs, Gmail สองฟีเจอร์ที่ทำให้มันกลายเป็นแพลตฟอร์มสร้างแอปได้จริง (แม้จะดูแปลก ๆ) คือ

  • HtmlService ให้ Apps Script โปรเจกต์เดียวเสิร์ฟหน้าเว็บจริง ๆ ได้ ผ่านฟังก์ชัน doGet(e) ตัวเดียว โดยอ่าน URL parameter เพื่อ route หน้า
  • SpreadsheetApp ทำให้ Google Sheet กลายเป็นฐานข้อมูลที่โค้ดฝั่งเซิร์ฟเวอร์อ่าน/เขียนได้ตรง ๆ

ฝั่ง client กับ server คุยกันผ่าน google.script.run — เป็น same-origin ไม่ต้องกังวลเรื่อง CORS ไม่ต้อง fetch() ไปที่ไหน มันเป็นรูปแบบแอปที่ต่างจากเว็บแอปทั่วไปจริง ๆ แต่สำหรับงานขนาดนี้ มันตัดโครงสร้างพื้นฐานที่ต้องดูแลออกไปเกือบหมด

§02สถาปัตยกรรมโดยสรุป

doGet(e) ตัวเดียว route ตาม query string: ไม่มี param → หน้าสร้างโพลของโฮสต์; ?poll=X → หน้าโหวต; ?poll=X&view=results → หน้าผลโหวตแบบสด; ?poll=X&admin=KEY → หน้าผลโหวตเดิม แต่มีปุ่มปิดโหวตโผล่มาด้วย Google Sheet หนึ่งไฟล์มีสองแท็บคือ Polls กับ Votes ทุกครั้งที่โหวตจะผ่าน LockService.getScriptLock() เพื่อไม่ให้การโหวตพร้อมกันไปชนกันตอน appendRow และผลรวมคะแนนจะถูก cache ไว้ 2 วินาทีผ่าน CacheService เพื่อไม่ให้หน้าผลโหวตที่เปิดค้างไว้ ยิงถามทุก 3 วินาที ไปรัว ๆ จน Sheet หรือโควตาการรันของ Apps Script รับไม่ไหว

การกันโหวตซ้ำตั้งใจทำแบบหลวม ๆ: มี token สุ่มถูกเขียนลง localStorage ตอนเข้าครั้งแรก แล้วเช็กซ้ำฝั่งเซิร์ฟเวอร์กับข้อมูลใน Votes sheet ไม่มีระบบล็อกอิน นี่คือ trade-off ที่ตั้งใจ ไม่ใช่ความหลวมที่ลืมทำ — อ่านรายละเอียดที่หัวข้อ "สิ่งที่แอปนี้ตั้งใจไม่ทำ" ด้านล่าง


§03วิธีที่ง่ายที่สุด: copy ชีตเทมเพลต

นี่คือทางลัดที่ไม่ต้องรู้ tech อะไรเลยก็ตั้งแอปของตัวเองได้ เพราะ Apps Script ผูกอยู่กับตัว Sheet โดยตรงอยู่แล้ว (container-bound script) แปลว่าตอน copy ชีต โค้ดทั้งหมดก็ copy ตามไปด้วย ไม่ต้องแยก clone อะไรเพิ่ม

ชีตเทมเพลต: https://docs.google.com/spreadsheets/d/116I13EBsyjFXXyF-txfLWGmTIavvUYegjFcZOY9a2mQ/edit?gid=0#gid=0

  1. เปิดลิงก์ชีตด้านบน แล้วกด File → Make a copy เพื่อสำเนาไปเก็บใน Google Drive ของตัวเอง (ในสำเนานี้จะมีโพลตัวอย่าง 1 อันติดมาด้วย — ปิดโหวตไว้แล้ว ไว้ดูเป็นตัวอย่างข้อมูลได้ ลบทิ้งหรือปล่อยไว้ก็ได้ ไม่กระทบการใช้งาน)
  2. เปิดสำเนาของตัวเอง → Extensions → Apps Script จะเจอไฟล์ Code.gs, create.html, vote.html, results.html ครบทุกไฟล์อยู่แล้ว
  3. เลือกฟังก์ชัน setupSheet จาก dropdown แล้วกด Run แล้วกดอนุญาต (authorize) ตามที่ระบบขอ ขั้นตอนนี้จะตรวจ/สร้างแท็บและหัวตารางให้ครบ แล้วเก็บ Sheet ID ของสำเนาไว้ใน Script Properties ให้เอง
  4. Deploy → New deployment → Web app ตั้งค่า Execute as: Me และ Who has access: Anyone สองอันนี้ต้องตั้งเองในหน้า editor
  5. เปิดลิงก์ /exec จาก deployment นั้น เท่านี้ก็ได้ Quick Poll ของตัวเองที่ใช้งานได้จริงแล้ว

ลองครบวงจร: สร้างคำถาม จะได้ลิงก์ 3 แบบ (โหวต / ผลโหวต / แอดมิน)

หน้าจอหลังสร้างโพลสำเร็จ แสดงลิงก์ 3 แบบ: โหวต, ผลโหวต, แอดมิน
หน้าจอหลังสร้างโพลสำเร็จ แสดงลิงก์ 3 แบบ: โหวต, ผลโหวต, แอดมิน
FIG. 02 — ได้ลิงก์ 3 แบบทันทีหลังสร้างโพล

แชร์ลิงก์โหวตออกไป ผู้โหวตกดเลือกตัวเลือกจากมือถือได้เลย

หน้าโหวต ผู้ใช้เลือกตัวเลือกจากรายการ
หน้าโหวต ผู้ใช้เลือกตัวเลือกจากรายการ
FIG. 03 — หน้าโหวตที่ผู้ใช้เห็นเมื่อกดลิงก์
หน้ายืนยันหลังโหวตเสร็จ
หน้ายืนยันหลังโหวตเสร็จ
FIG. 04 — โหวตแล้วจะเข้าหน้านี้ กดซ้ำไม่ได้จาก token เดิม

ดูแท่งกราฟ CSS ในหน้าผลโหวตขยับตามคะแนนที่เข้ามาแบบเกือบเรียลไทม์

หน้าผลโหวตแบบกราฟแท่ง อัปเดตตามคะแนนที่เข้ามา
หน้าผลโหวตแบบกราฟแท่ง อัปเดตตามคะแนนที่เข้ามา
FIG. 05 — ผลโหวตสด อัปเดตทุก 3 วินาทีขณะโพลยังเปิด

แล้วกดปิดโหวตจากลิงก์แอดมิน หน้าผลโหวตจะเปลี่ยนเป็นหัวข้อ "มติ" แบบนิ่ง ๆ ทันที

หน้าแอดมินกดปิดโหวต ผลลัพธ์ฟรีซกลายเป็นมติ
หน้าแอดมินกดปิดโหวต ผลลัพธ์ฟรีซกลายเป็นมติ
FIG. 06 — กดปิดโหวตจากลิงก์แอดมิน ผลลัพธ์ฟรีซทันที

§04ถ้าอยากได้โค้ดเป็นไฟล์แยก ไม่ยึดกับชีตต้นฉบับ

วิธี copy ชีตด้านบนเหมาะกับ "อยากใช้เลย" แต่ถ้าอยากเก็บโค้ดไว้ใน git ของตัวเอง แก้แล้ว push กลับขึ้นไปด้วย clasp ก็ทำได้เหมือนเดิม ต้องมี Node 22 ขึ้นไป, @google/clasp, และบัญชี Google

npm i -g @google/clasp
clasp login
git clone <this-repo>
cd quick-poll
clasp create-script --type standalone --title "Quick Poll"
clasp push --force

จากนั้นเปิด script editor (clasp open-script) แล้วทำตามข้อ 3-5 ในหัวข้อก่อนหน้าเหมือนเดิม (setupSheet → Deploy → Execute as Me / Anyone)


§05สิ่งที่แอปนี้ตั้งใจไม่ทำ

  • ไม่มีระบบบัญชีผู้ใช้ การกันโหวตซ้ำเป็นแค่ token ใน localStorage ไม่ใช่ identity จริง — เปิด incognito ก็ได้ token ใหม่แล้วโหวตซ้ำได้ เหมาะกับโพลสบาย ๆ แบบ "เที่ยงนี้กินอะไรดี" ในกลุ่มเพื่อน ไม่ใช่การลงคะแนนที่ต้องกันโกงระดับสูง
  • 1 โพล = 1 คำถาม 2–6 ตัวเลือก แก้ไขหลังสร้างไม่ได้ ไม่มีปิดโหวตอัตโนมัติตามเวลา ไม่มีปุ่ม export (ตัว Sheet เองก็คือไฟล์ export อยู่แล้ว)
  • "เรียลไทม์" ในที่นี้คือ polling ไม่ใช่ push — โพลที่ยังเปิดอยู่จะถามทุก 3 วินาที พอปิดโพลแล้วจะถี่ทุก 15 วินาที Apps Script ทำ websocket ไม่ได้ แต่สำหรับโพลในกลุ่มไม่กี่สิบคน แทบไม่มีใครสังเกตความต่างนี้

ข้อจำกัดพวกนี้ไม่ใช่กำแพงที่แก้ไม่ได้ในอนาคต เช่นการกันโหวตซ้ำที่แน่นขึ้น ก็แค่ต้องบังคับให้ล็อกอิน Google แล้ว key ด้วยอีเมลของผู้โหวตแทน token แต่นั่นคือการขยาย scope ที่ตั้งใจ ไม่ใช่สิ่งที่เวอร์ชันนี้ต้องการ


§06เอาไปใช้ต่อ

Copy ชีตเทมเพลตด้านบน รัน setupSheet() แล้ว deploy — ได้แอปโหวตเรียลไทม์ฟรีของตัวเองภายในเวลาประมาณสิบนาที ถ้าอยากแก้โค้ด logic เอง ทั้งหมด — manifest, backend, และหน้า HTML ทั้งสามหน้า — อยู่ใน Extensions → Apps Script ของสำเนาที่ copy มาแล้ว แก้ตรงนั้นได้เลยโดยไม่กระทบต้นฉบับ

💬 ถ้ามีคำถาม comment ใต้วิดีโอ หรือเปิด issue ใน GitHub repo ครับ

— FIN —P. 01 / 01 · 561 WORDS
WRITTEN BY
P
Puripat
@PURIPAT · ENG / WRITER

Engineer, writer, occasional GPU-melter. Writes every Sunday about applied AI — agents, evals, and whatever broke that week.

SHARE THIS

If you got something out of this, share it with someone who's trying to ship their first agent.

← PREV
05 · เอา QR Room View ไปใช้กับห้องเช่าของคุณเอง — แค่ Copy Sheet ไม่ต้องเขียนโค้ด
2026.07.18