สร้าง LINE Bot ง่ายโคตร ด้วย Python Flask

เฮ้ยๆ รู้ยังว่า LINE Bot มันโคตรง่ายเลยนะ ถ้ามี Python กับ Flask นี่คือแทบจะเสกได้เลย ไม่ต้องไปงมหาเฟรมเวิร์คยากๆ หรือเสียเงินเยอะแยะ วันนี้จะมาลองทำบอทง่ายๆ ที่ตอบโต้ข้อความได้ เริ่มจากศูนย์เลย มาดูกัน!

ของที่ต้องมี (โคตรจะพื้นฐาน)

ก่อนจะเริ่มโค้ดดิ้ง เราต้องเตรียมของนิดหน่อย ไม่เยอะเลย:

  1. บัญชี LINE Developers: เข้าไปสมัคร หรือล็อกอินที่ developers.line.biz เพื่อสร้าง Provider กับ Channel ใหม่ เลือก Messaging API channel นะ
    • เสร็จแล้วจะเจอ Channel Access Token กับ Channel Secret เก็บไว้ให้ดีๆ เลย อันนี้คือหัวใจสำคัญ
  2. Ngrok: เอาไว้เปิด localhost ของเราออกเน็ต เพื่อให้ LINE Server ส่ง Webhook มาหาบอทเราได้ (เดี๋ยวสอนใช้)
  3. Python: แน่นอนอยู่แล้ว!
  4. Library:
pip install Flask line-bot-sdk python-dotenv # python-dotenv เอาไว้เก็บค่าลับๆ จะได้ไม่โชว์ในโค้ด

ตั้งค่า LINE Developers Console

พอสร้าง Channel เสร็จละ มันจะมีแท็บ Messaging API ให้เรา * หา Channel Access Token (Long-lived) แล้วก็ Channel Secret เอาสองตัวนี้ไปใส่ในไฟล์ .env ของโปรเจกต์เรา เช่น:

CHANNEL_ACCESS_TOKEN="นี่คือค่าลับที่โคตรยาวของ Token"
CHANNEL_SECRET="นี่คือค่าลับอันที่สอง"
  • ส่วน Webhook URL ยังไม่ต้องใส่ตอนนี้ เพราะเรายังไม่รู้ URL ของ Ngrok

มาเขียนโค้ดกัน! (โค้ดเยอะนิดนึงนะ)

สร้างไฟล์ app.py ขึ้นมา แล้วเริ่มใส่โค้ดเลย

import os
import logging
from dotenv import load_dotenv # ดึงค่าจาก .env
from flask import Flask, request, abort
from linebot import LineBotApi, WebhookHandler
from linebot.exceptions import InvalidSignatureError
from linebot.models import MessageEvent, TextMessage, TextSendMessage

# โหลดค่าจากไฟล์ .env ก่อนเลยนะ สำคัญ!
load_dotenv()

# ตั้งค่า Logging ไว้ดู Error หน่อยก็ดีนะ
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

app = Flask(__name__)

# ดึงค่าจาก .env
CHANNEL_ACCESS_TOKEN = os.getenv('CHANNEL_ACCESS_TOKEN')
CHANNEL_SECRET = os.getenv('CHANNEL_SECRET')

# เช็คหน่อยว่าค่ามันมาครบมั้ย
if not CHANNEL_ACCESS_TOKEN:
    logger.error("CHANNEL_ACCESS_TOKEN is not set.")
    raise ValueError("CHANNEL_ACCESS_TOKEN is not set. Please set it in your .env file.")
if not CHANNEL_SECRET:
    logger.error("CHANNEL_SECRET is not set.")
    raise ValueError("CHANNEL_SECRET is not set. Please set it in your .env file.")

line_bot_api = LineBotApi(CHANNEL_ACCESS_TOKEN)
handler = WebhookHandler(CHANNEL_SECRET)

@app.route("/callback", methods=['POST'])
def callback():
    signature = request.headers['X-Line-Signature']
    body = request.get_data(as_text=True)
    app.logger.info("Request body: " + body) # อยากดูว่า LINE ส่งอะไรมาบ้างก็ดูตรงนี้

    try:
        handler.handle(body, signature)
    except InvalidSignatureError:
        # เฮ้ยยย! นี่แหละ error ยอดฮิตที่เคยเจอมาบ่อยมากกกก โคตรปวดหัวเลย!
        # แปลว่า Signature ไม่ตรง หรือ Channel Secret ผิด
        logger.error("Got InvalidSignatureError. Check your channel secret and webhook setup.")
        # ลองปริ้นท์ header ดูก็ได้เผื่ออยากดีบั๊กเพิ่ม
        # logger.error(f"Headers: {request.headers}")
        # logger.error(f"Body: {body}")
        abort(400) # โยน 400 ไปเลย LINE จะได้รู้ว่ามีปัญหา
    except Exception as e:
        logger.error(f"An unexpected error occurred: {e}")
        abort(500)

    return 'OK'

@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
    msg_from_user = event.message.text
    reply_text = f"คุณพูดว่า: {msg_from_user} ใช่ปะ? เราตอบเอง"

    # ถ้าเจอคำว่า "สวัสดี" ให้ตอบอีกแบบ
    if "สวัสดี" in msg_from_user:
        reply_text = "สวัสดีครับคุณผู้ใช้! ยินดีที่ได้รู้จักนะ"
    elif "ขอบคุณ" in msg_from_user:
        reply_text = "ยินดีครับ! 😊"
    elif "คืออะไร" in msg_from_user:
        reply_text = "ก็คือบอทไง ถามแปลกๆ 😅"
    else:
        reply_text = f"ได้ยินว่า: '{msg_from_user}' อะนะ เราไม่รู้จะตอบอะไรดี"


    line_bot_api.reply_message(
        event.reply_token,
        TextSendMessage(text=reply_text)
    )

if __name__ == "__main__":
    app.run(port=5000, debug=True) # debug=True นี่ไว้ตอน dev นะ พอขึ้น production อย่าลืมปิด!

เรื่อง Error InvalidSignatureError (นี่แหละที่ชอบเจอ!)

ไอ้ Error InvalidSignatureError เนี่ย มันเกิดบ่อยมากตอนเราเริ่มทำ LINE Bot แรกๆ คือตัว LINE Server มันจะส่ง X-Line-Signature มาให้เราใน Header เพื่อยืนยันว่า Request นี้มาจาก LINE จริงๆ ไม่ใช่ใครที่ไหนมั่วส่งมา สาเหตุหลักๆ ที่มันขึ้นก็คือ:

  1. CHANNEL_SECRET ที่ใส่ในโค้ดเราไม่ตรงกับที่อยู่ใน LINE Developers Console: อันนี้คือบ่อยสุดๆ เช็คดีๆ เลยนะว่าก๊อปมาถูกตัวอักษรมั้ย บางทีมีช่องว่างเกินมาก็เป็นนะ
  2. Body ของ Request ถูกแก้ไข: อันนี้ไม่ค่อยเจอเท่าไหร่ถ้าไม่ได้ไปยุ่งอะไรกับ Request ระหว่างทาง
  3. URL Webhook เราถูกตั้งค่าผิด: ใน LINE Developers Console ต้องใส่ URL ที่ถูกต้องของ Ngrok (หรือเซิร์ฟเวอร์เราจริงๆ) แล้วตามด้วย /callback เช่น https://abcdefg.ngrok-free.app/callback

ถ้าเจอ error นี้ ลองเพิ่ม logger.error(f"Headers: {request.headers}") กับ logger.error(f"Body: {body}") ใน except InvalidSignatureError ดู จะได้เห็นว่า LINE ส่งอะไรมาจริงๆ แล้วเอาไปเทียบกับ CHANNEL_SECRET เราดูว่ามีอะไรพลาดรึเปล่า

มาลองรันกัน!

  1. รัน Ngrok: เปิด Terminal ใหม่ขึ้นมา แล้วพิมพ์:
ngrok http 5000
เดี๋ยว Ngrok มันจะขึ้น URL ให้เรามา 2 อัน (HTTP กับ HTTPS) ให้ก๊อปอันที่เป็น **HTTPS** นะ เช่น `https://xxxxxx.ngrok-free.app`
  1. ตั้งค่า Webhook ใน LINE Developers: กลับไปที่ LINE Developers Console ของ Channel เรา แล้วเอา URL จาก Ngrok เมื่อกี้ไปใส่ในช่อง Webhook URL (อย่าลืมเติม /callback ต่อท้ายด้วยนะ!) เช่น https://xxxxxx.ngrok-free.app/callback แล้วก็กดปุ่ม Verify ให้มันขึ้นว่า Success ด้วยนะ! สำคัญมากกกก! ถ้าไม่ Success แปลว่าบอทเรายังไม่พร้อมรับ Request จาก LINE
  2. รัน Flask: เปิด Terminal อีกอัน (หรือใช้ Terminal ที่รันโค้ดเมื่อกี้) แล้วรัน:
python app.py
หรือถ้าลง `python-dotenv` แล้วก็รันแบบนี้จะดีกว่า:
flask run
เพราะถ้าเราใช้ `flask run` มันจะโหลด `dotenv` ให้อัตโนมัติ (ขึ้นอยู่กับเวอร์ชั่น Flask ด้วยนะ)
  1. ลองแชทกับบอท: เข้า LINE ของเรา แล้วหา Channel ID ของบอทเรา (มันจะอยู่ในหน้า LINE Developers นั่นแหละ) หรือถ้าตั้งเป็น Public ก็ Search ชื่อได้เลย ลองส่งข้อความไปหาบอทเราดูสิ!

สรุป

เห็นไหม๊ว่าการทำ LINE Bot เนี่ยมันง่ายกว่าที่คิดเยอะเลยนะ แค่มี Python, Flask แล้วก็ Line SDK นี่ก็แทบจะต่อยอดทำอะไรได้อีกเยอะแยะเลย ไม่ว่าจะเป็นตอบคำถามลูกค้า, แจ้งเตือนนู่นนี่นั่น, หรือแม้แต่ทำเกมส์ง่ายๆ เล่นกับเพื่อนใน LINE ก็ยังได้เลยนะ! ลองเอาไปปรับใช้ดู หรือจะเพิ่มลูกเล่นอื่นๆ เช่น Flex Message, Quick Reply หรือเชื่อมต่อกับ Database ก็ทำได้หมดเลย สู้ๆ!

Read more

PSG vs Monaco: ศึก 100 นัดเดือด ลีกเอิง และบทเรียนที่ปาร์ค เดส์ แพร็งซ์

PSG vs Monaco: ศึก 100 นัดเดือด ลีกเอิง และบทเรียนที่ปาร์ค เดส์ แพร็งซ์

สรุปผลและวิเคราะห์เกมเดือด PSG พบ Monaco ในลีกเอิงนัดที่ 25 ซึ่งเป็นการพบกันครั้งที่ 100 ในประวัติศาสตร์ลีก ความพ่ายแพ้ 1-3 คาบ้านของ PSG และบทบาทของ Akliouche พร้อมผลกระทบต่อเส้นทางแชมเปี้ยนส์ลีก

By ทีมงาน devdog
บาเยิร์นผงาดไร้เคน! ถล่มกลัดบัค 4-1 โชว์ความลึกของทีมก่อนลุยศึก UCL

บาเยิร์นผงาดไร้เคน! ถล่มกลัดบัค 4-1 โชว์ความลึกของทีมก่อนลุยศึก UCL

บาเยิร์น มิวนิค โชว์ฟอร์มแกร่ง แม้ไม่มีแฮร์รี่ เคน ถล่ม โบรุสเซีย มึนเชนกลัดบัค 4-1 ก่อนเตรียมลุยศึกแชมเปียนส์ลีกกับอตาลันต้า!

By ทีมงาน devdog
PSG: มหาอำนาจลูกหนังฝรั่งเศส กับศึกดวลเดือดโมนาโก และเป้าหมายสู่บัลลังก์ยุโรป

PSG: มหาอำนาจลูกหนังฝรั่งเศส กับศึกดวลเดือดโมนาโก และเป้าหมายสู่บัลลังก์ยุโรป

เจาะลึกเส้นทาง PSG สู่มหาอำนาจลูกหนัง วิเคราะห์สถานการณ์ลีกเอิง เตรียมพร้อมศึกใหญ่กับโมนาโก พร้อมความมุ่งมั่นสู่แชมป์ยุโรป

By ทีมงาน devdog
ลาลีกา: มนต์เสน่ห์ฟุตบอลสเปน, นวัตกรรมเรโทร, และบิ๊กแมตช์แห่งอนาคต

ลาลีกา: มนต์เสน่ห์ฟุตบอลสเปน, นวัตกรรมเรโทร, และบิ๊กแมตช์แห่งอนาคต

สำรวจลาลีกา ฟุตบอลสเปนอันทรงเสน่ห์ พร้อมไฮไลต์บิ๊กแมตช์ 2025/26 นวัตกรรมสัปดาห์เรโทร และบทบาทต่อวัฒนธรรมและเศรษฐกิจ.

By ทีมงาน devdog