ใช้ Hooks ส่งแจ้งเตือนเมื่องานเสร็จ: ให้ Claude บอกคุณเองอัตโนมัติ

ตั้งค่า Stop Hook ให้ Claude แจ้งเตือนอัตโนมัติผ่านเดสก์ท็อป, Slack หรือ Telegram เมื่อทำงานเสร็จ


Claude Code มักใช้เวลาหลายนาทีหรือนานกว่านั้นในการทำงานให้เสร็จ คุณสั่งให้มัน refactor โมดูล รัน test suite หรือประมวลผลไฟล์จำนวนมาก — แล้วก็สลับไปทำอย่างอื่น ปัญหาคือ คุณไม่รู้ว่ามันทำเสร็จเมื่อไหร่

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

ด้วย Stop Hook คุณสามารถรับแจ้งเตือนอัตโนมัติทุกครั้งที่ Claude ทำงานเสร็จ (หรือต้องการให้คุณเข้ามาดู) ไม่ว่าคุณจะอยู่หน้าต่างไหน อุปกรณ์ไหน แจ้งเตือนก็ส่งมาถึงทันที


กลไกหลัก

Claude Code มี event ชื่อ Stop — จะถูกเรียกทุกครั้งที่ Claude หยุดทำงาน ไม่ว่าจะเป็นเพราะทำงานเสร็จ เจอ error ที่ต้องยืนยัน หรือรอ input จากผู้ใช้

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "bash .claude/hooks/notify.sh"
          }
        ]
      }
    ]
  }
}

Hook จะได้รับ JSON ผ่าน stdin ซึ่งมี stop_reason (เหตุผลที่หยุด) และ stop_message (ข้อความสุดท้ายที่ Claude พูด) คุณสามารถปรับแต่งเนื้อหาแจ้งเตือนตามข้อมูลเหล่านี้ได้

โครงสร้างข้อมูล stdin:

{
  "session_id": "xxx",
  "stop_reason": "end_turn",
  "stop_message": "重构完成,所有测试通过。"
}

วิธีที่ 1: แจ้งเตือนบนเดสก์ท็อป (ง่ายที่สุด)

Linux ใช้ notify-send, macOS ใช้ osascript ไม่ต้องพึ่งบริการภายนอกใดๆ:

#!/bin/bash
input=$(cat)
reason=$(echo "$input" | jq -r '.stop_reason // "unknown"')
message=$(echo "$input" | jq -r '.stop_message // "Claude 已停止"' | head -c 200)

case "$(uname)" in
  Linux)
    notify-send "Claude Code" "$message" --urgency=normal
    ;;
  Darwin)
    osascript -e "display notification \"$message\" with title \"Claude Code\""
    ;;
esac

ข้อดี: ไม่ต้องตั้งค่าอะไร ใช้ได้เลย
ข้อเสีย: รับได้แค่บนเครื่องปัจจุบัน ถ้าย้ายไปอุปกรณ์อื่นจะไม่เห็น


วิธีที่ 2: Slack Webhook (แนะนำสำหรับทีม)

ใช้ Slack Incoming Webhook ส่งแจ้งเตือนไปยังแชนเนลหรือข้อความส่วนตัวที่กำหนด

เตรียมการ:
1. สร้าง App ในหน้าจัดการ App ของ Slack
2. เปิดใช้งาน Incoming Webhooks แล้วเอา Webhook URL มา
3. เก็บ URL ไว้ใน environment variable SLACK_WEBHOOK_URL

#!/bin/bash
input=$(cat)
reason=$(echo "$input" | jq -r '.stop_reason // "unknown"')
message=$(echo "$input" | jq -r '.stop_message // "Claude 已停止"' | head -c 300)

webhook_url="${SLACK_WEBHOOK_URL}"
[[ -z "$webhook_url" ]] && exit 0

# 根据停止原因选 emoji
case "$reason" in
  end_turn)    emoji="✅" ;;
  user_input)  emoji="⏳" ;;
  *)           emoji="🔔" ;;
esac

payload=$(jq -n \
  --arg text "$emoji *Claude Code* | \`$reason\`
$message" \
  '{text: $text}')

curl -s -X POST "$webhook_url" \
  -H 'Content-Type: application/json' \
  -d "$payload" > /dev/null 2>&1 &

เครื่องหมาย & ท้ายบรรทัดสำคัญมาก — ทำให้ curl ทำงานใน background ไม่บล็อก Claude


วิธีที่ 3: Telegram Bot

Telegram Bot เหมาะกับการใช้งานส่วนตัว ฟรี แจ้งเตือนทันที รับได้บนมือถือเลย

เตรียมการ:
1. สร้าง Bot ผ่าน @BotFather แล้วเอา Token มา
2. หา Chat ID ของคุณ (ส่งข้อความให้ Bot แล้วเรียก API getUpdates)
3. ตั้ง environment variable TELEGRAM_BOT_TOKEN และ TELEGRAM_CHAT_ID

#!/bin/bash
input=$(cat)
reason=$(echo "$input" | jq -r '.stop_reason // "unknown"')
message=$(echo "$input" | jq -r '.stop_message // "Claude 已停止"' | head -c 300)

token="${TELEGRAM_BOT_TOKEN}"
chat_id="${TELEGRAM_CHAT_ID}"
[[ -z "$token" || -z "$chat_id" ]] && exit 0

text="🤖 *Claude Code*
状态:\`$reason\`
$message"

curl -s "https://api.telegram.org/bot${token}/sendMessage" \
  -d chat_id="$chat_id" \
  -d text="$text" \
  -d parse_mode="Markdown" > /dev/null 2>&1 &

วิธีที่ 4: เสียงแจ้งเตือน (ใช้ได้ดีแม้ตอน SSH)

ถ้าคุณยังอยู่บนเครื่องเดิมแต่สลับไปหน้าต่างอื่น เสียงเตือนจะได้ผลกว่า popup มาก:

#!/bin/bash
input=$(cat)
reason=$(echo "$input" | jq -r '.stop_reason // "unknown"')

case "$(uname)" in
  Linux)
    paplay /usr/share/sounds/freedesktop/stereo/complete.oga 2>/dev/null &
    ;;
  Darwin)
    afplay /System/Library/Sounds/Glass.aiff &
    ;;
esac

ใช้ร่วมกับวิธีอื่นได้ — เล่นเสียงในเครื่อง + ส่งแจ้งเตือนไปอุปกรณ์อื่น


การตั้งค่าแบบเต็ม: รวมหลายวิธีแจ้งเตือนเข้าด้วยกัน

ในการใช้งานจริง แนะนำให้รวมหลายวิธีไว้ใน script เดียว:

.claude/hooks/notify.sh:

#!/bin/bash
input=$(cat)
reason=$(echo "$input" | jq -r '.stop_reason // "unknown"')
message=$(echo "$input" | jq -r '.stop_message // "Claude 已停止"' | head -c 300)

# 根据原因选 emoji
case "$reason" in
  end_turn)    emoji="✅"; title="任务完成" ;;
  user_input)  emoji="⏳"; title="等待输入" ;;
  *)           emoji="🔔"; title="Claude 已停止" ;;
esac

# 1. 桌面通知(本地)
case "$(uname)" in
  Linux)  notify-send "Claude Code: $title" "$message" --urgency=normal ;;
  Darwin) osascript -e "display notification \"$message\" with title \"Claude Code: $title\"" ;;
esac

# 2. 声音提示
case "$(uname)" in
  Linux)  paplay /usr/share/sounds/freedesktop/stereo/complete.oga 2>/dev/null & ;;
  Darwin) afplay /System/Library/Sounds/Glass.aiff & ;;
esac

# 3. Slack(如果配了 Webhook)
if [[ -n "$SLACK_WEBHOOK_URL" ]]; then
  payload=$(jq -n --arg text "$emoji *$title*
$message" '{text: $text}')
  curl -s -X POST "$SLACK_WEBHOOK_URL" \
    -H 'Content-Type: application/json' \
    -d "$payload" > /dev/null 2>&1 &
fi

# 4. Telegram(如果配了 Bot)
if [[ -n "$TELEGRAM_BOT_TOKEN" && -n "$TELEGRAM_CHAT_ID" ]]; then
  curl -s "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
    -d chat_id="$TELEGRAM_CHAT_ID" \
    -d text="$emoji *$title*
$message" \
    -d parse_mode="Markdown" > /dev/null 2>&1 &
fi

.claude/settings.json:

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "bash .claude/hooks/notify.sh"
          }
        ]
      }
    ]
  }
}

ข้อควรระวัง

ทุก request ที่ต้องใช้เน็ตต้องรันใน background

คำสั่ง curl ใน script แจ้งเตือนต้องเติม & เพื่อรันใน background เสมอ ถ้าเน็ตช้าหรือ timeout จะบล็อก session ของ Claude ทั้งหมด การเติม > /dev/null 2>&1 & เป็นแนวปฏิบัติมาตรฐาน

Stop ไม่ได้แปลว่า "ทำสำเร็จแล้ว"

stop_reason เป็น end_turn มักหมายความว่างานเสร็จแล้ว แต่ก็อาจเป็นเพราะ Claude ไม่แน่ใจว่าขั้นตอนถัดไปคืออะไร ถ้าต้องการรับแจ้งเตือนเฉพาะตอนที่เสร็จจริงๆ ให้เพิ่ม filter ใน script:

[[ "$reason" != "end_turn" ]] && exit 0

ตัดเนื้อหาแจ้งเตือนให้สั้น

ข้อความสุดท้ายของ Claude อาจยาวมาก (มี code block, output ยาวๆ) ก่อนส่งไป Slack หรือ Telegram ให้ใช้ head -c 300 ตัดให้สั้น เพื่อไม่ให้ข้อความยาวเกินไป

เก็บ environment variable ไว้ในการตั้งค่าส่วนกลาง

Webhook URL และ Bot Token ไม่ควรเก็บไว้ใน repository ของโปรเจกต์ แนะนำให้ใส่ใน ~/.zshrc (หรือ ~/.bashrc) หรือจัดการด้วย direnv


ผลลัพธ์

หลังจากตั้งค่าแล้ว วิธีการทำงานของคุณจะเปลี่ยนไป:

  1. สั่งงาน Claude
  2. สลับไปหน้าต่าง/อุปกรณ์อื่น
  3. Claude ทำเสร็จ → แจ้งเตือนส่งมาทันที
  4. กลับมาดูผลลัพธ์ แล้วทำขั้นตอนถัดไป

ไม่ต้องนั่งจ้อง terminal ไม่ต้องสลับหน้าต่างไปมาเพื่อเช็คความคืบหน้า Claude ทำงานของ Claude คุณทำงานของคุณ เสร็จแล้วมันจะบอกเอง