Files
gh-nanasess-eccube-dev-agen…/hooks/slack-notify.sh
2025-11-30 08:41:46 +08:00

336 lines
8.7 KiB
Bash
Executable File

#!/bin/bash
#
# Slack Notification Script for Claude Code Hooks
# =================================================
# This script extracts user prompts and assistant responses from conversation
# history, generates a summary using Gemini AI, and sends it to Slack.
#
# Usage:
# echo '{"transcript_path": "/path/to/file.jsonl", "title": "Task"}' | ./slack-notify.sh [notification|stop]
#
# Environment Variables:
# ECCUBE_DEV_AGENTS_SLACK_WEBHOOK_URL - Slack webhook URL (required)
# DEBUG - Set to 1 to enable debug output
#
set -euo pipefail
# -----------------------------------------------------------------------------
# Configuration
# -----------------------------------------------------------------------------
readonly SCRIPT_NAME="$(basename "$0")"
readonly MESSAGE_LIMIT=10 # Number of recent messages to extract
readonly DEBUG="${DEBUG:-0}"
# -----------------------------------------------------------------------------
# Utility Functions
# -----------------------------------------------------------------------------
# Print error message to stderr
error() {
echo "ERROR: $*" >&2
}
# Print debug message if DEBUG is enabled
debug() {
if [[ "$DEBUG" == "1" ]]; then
echo "DEBUG: $*" >&2
fi
}
# Print info message to stderr
info() {
echo "INFO: $*" >&2
}
# Exit with error message
die() {
error "$@"
exit 1
}
# -----------------------------------------------------------------------------
# Input Processing
# -----------------------------------------------------------------------------
# Read and parse JSON input from stdin
read_input() {
local input
input=$(cat)
debug "Received input: ${input:0:100}..."
echo "$input"
}
# Extract transcript path from input JSON
extract_transcript_path() {
local input="$1"
local path
path=$(echo "$input" | jq -r '.transcript_path')
if [[ "$path" == "null" || -z "$path" ]]; then
die "transcript_path not found in input JSON"
fi
if [[ ! -f "$path" ]]; then
die "Transcript file not found: $path"
fi
debug "Transcript path: $path"
echo "$path"
}
# Extract title from input JSON
extract_title() {
local input="$1"
local title
title=$(echo "$input" | jq -r '.title // "Untitled"')
debug "Title: $title"
echo "$title"
}
# Determine event type (notification or stop)
determine_event_type() {
local event_type="${1:-notification}"
if [[ "$event_type" != "notification" && "$event_type" != "stop" ]]; then
error "Unknown event type: $event_type, defaulting to notification"
event_type="notification"
fi
debug "Event type: $event_type"
echo "$event_type"
}
# -----------------------------------------------------------------------------
# Message Extraction
# -----------------------------------------------------------------------------
# Extract recent messages from transcript file
extract_messages() {
local transcript_path="$1"
local messages
messages=$(jq -s ".[-${MESSAGE_LIMIT}:]" "$transcript_path")
debug "Extracted ${MESSAGE_LIMIT} recent messages"
echo "$messages"
}
# Extract user prompts from messages
extract_user_prompts() {
local messages="$1"
local prompts
prompts=$(echo "$messages" | jq -r '
[.[] |
select(.type == "user" and .message.content[0].text != null) |
.message.content[0].text
] | join("\n---\n")
')
debug "Extracted user prompts (length: ${#prompts})"
echo "$prompts"
}
# Extract assistant responses from messages
extract_assistant_responses() {
local messages="$1"
local responses
responses=$(echo "$messages" | jq -r '
[.[] |
select(.type == "assistant" and .message.content[0].text != null) |
.message.content[0].text
] | join("\n---\n")
')
debug "Extracted assistant responses (length: ${#responses})"
echo "$responses"
}
# -----------------------------------------------------------------------------
# Summary Generation
# -----------------------------------------------------------------------------
# Build Gemini prompt based on event type
build_gemini_prompt() {
local event_type="$1"
local title="$2"
local user_prompts="$3"
local assistant_responses="$4"
local prompt
local context="タイトル: $title
【ユーザーの入力】
$user_prompts
【Claudeの応答】
$assistant_responses"
if [[ "$event_type" == "stop" ]]; then
prompt="タスク完了の通知です。以下の会話を要約してください:
$context
以下の形式で日本語で要約してください:
📝 *ユーザーの依頼:*
[ユーザーが入力したプロンプト/質問の要点を簡潔に]
🤖 *対応内容:*
[Claude Codeが実施した作業の概要]
✅ *結果:*
[完了したタスクの成果]
絵文字を使って読みやすくし、Slack の mrkdwn 形式を使用してください。"
else
prompt="タスク確認の通知です。以下の会話を要約してください:
$context
以下の形式で日本語で要約してください:
📝 *ユーザーの依頼:*
[ユーザーが入力したプロンプト/質問の要点を簡潔に]
🤖 *対応内容:*
[Claude Codeが実施した作業の概要]
絵文字を使って読みやすくし、Slack の mrkdwn 形式を使用してください。"
fi
debug "Built Gemini prompt (length: ${#prompt})"
echo "$prompt"
}
# Generate summary using Gemini AI
generate_summary() {
local prompt="$1"
local summary
info "Generating summary with Gemini AI..."
if ! summary=$(echo "$prompt" | gemini -m gemini-2.5-flash 2>&1); then
die "Failed to generate summary with Gemini: $summary"
fi
debug "Summary generated (length: ${#summary})"
echo "$summary"
}
# -----------------------------------------------------------------------------
# Slack Integration
# -----------------------------------------------------------------------------
# Check if Slack webhook URL is configured
check_slack_webhook() {
if [[ -z "${ECCUBE_DEV_AGENTS_SLACK_WEBHOOK_URL:-}" ]]; then
error "ECCUBE_DEV_AGENTS_SLACK_WEBHOOK_URL is not set"
info "Skipping Slack notification"
return 1
fi
debug "Slack webhook URL is configured"
return 0
}
# Build Slack message payload
build_slack_payload() {
local summary="$1"
local payload
payload=$(jq -n --arg text "$summary" '{
blocks: [
{
type: "section",
text: {
type: "mrkdwn",
text: $text
}
}
]
}')
debug "Built Slack payload"
echo "$payload"
}
# Send notification to Slack
send_to_slack() {
local payload="$1"
local response
info "Sending notification to Slack..."
if ! response=$(curl -s -w "\n%{http_code}" -X POST \
-H 'Content-type: application/json' \
-d "$payload" \
"${ECCUBE_DEV_AGENTS_SLACK_WEBHOOK_URL}" 2>&1); then
die "Failed to send to Slack: $response"
fi
local http_code
http_code=$(echo "$response" | tail -n1)
if [[ "$http_code" != "200" ]]; then
die "Slack API returned error code: $http_code"
fi
info "Slack notification sent successfully"
}
# -----------------------------------------------------------------------------
# Main Function
# -----------------------------------------------------------------------------
main() {
local event_type
event_type=$(determine_event_type "${1:-}")
# Read input from stdin
local input
input=$(read_input)
# Extract parameters
local transcript_path
local title
transcript_path=$(extract_transcript_path "$input")
title=$(extract_title "$input")
# Extract messages
local messages
local user_prompts
local assistant_responses
messages=$(extract_messages "$transcript_path")
user_prompts=$(extract_user_prompts "$messages")
assistant_responses=$(extract_assistant_responses "$messages")
# Generate summary
local gemini_prompt
local summary
gemini_prompt=$(build_gemini_prompt "$event_type" "$title" "$user_prompts" "$assistant_responses")
summary=$(generate_summary "$gemini_prompt")
# Send to Slack (if configured)
if check_slack_webhook; then
local payload
payload=$(build_slack_payload "$summary")
send_to_slack "$payload"
else
# Just print summary if Slack is not configured
echo "=== Generated Summary ==="
echo "$summary"
echo "========================="
fi
}
# -----------------------------------------------------------------------------
# Script Entry Point
# -----------------------------------------------------------------------------
main "$@"