Loading...
Sends development activity updates to Discord channel for team collaboration
{
"hookConfig": {
"hooks": {
"notification": {
"script": "./.claude/hooks/discord-activity-notifier.sh"
}
}
},
"scriptContent": "#!/usr/bin/env bash\n\n# Read the tool input from stdin\nINPUT=$(cat)\nTOOL_NAME=$(echo \"$INPUT\" | jq -r '.tool_name')\nFILE_PATH=$(echo \"$INPUT\" | jq -r '.tool_input.file_path // .tool_input.path // \"\"')\n\n# Check if Discord webhook URL is configured\nif [ -z \"$DISCORD_WEBHOOK_URL\" ]; then\n echo \"💡 Set DISCORD_WEBHOOK_URL environment variable to enable Discord notifications\" >&2\n exit 0\nfi\n\necho \"📤 Sending Discord notification for tool: $TOOL_NAME\" >&2\n\n# Determine color based on tool name or file type\nCOLOR=\"3447003\" # Default blue\n\n# Success indicators\nif [[ \"$TOOL_NAME\" == *\"Success\"* ]] || [[ \"$TOOL_NAME\" == *\"Complete\"* ]]; then\n COLOR=\"3066993\" # Green\n# Error indicators\nelif [[ \"$TOOL_NAME\" == *\"Error\"* ]] || [[ \"$TOOL_NAME\" == *\"Fail\"* ]]; then\n COLOR=\"15158332\" # Red\n# Warning indicators\nelif [[ \"$TOOL_NAME\" == *\"Warning\"* ]] || [[ \"$TOOL_NAME\" == *\"Alert\"* ]]; then\n COLOR=\"16776960\" # Yellow\n# Edit/Write operations\nelif [[ \"$TOOL_NAME\" == \"Edit\" ]] || [[ \"$TOOL_NAME\" == \"Write\" ]] || [[ \"$TOOL_NAME\" == \"MultiEdit\" ]]; then\n COLOR=\"5793266\" # Purple\nfi\n\n# Get file information\nif [ -n \"$FILE_PATH\" ]; then\n FILENAME=$(basename \"$FILE_PATH\" 2>/dev/null || echo \"Unknown file\")\n FILE_EXT=\"${FILENAME##*.}\"\n \n # Add file type icon based on extension\n case \"$FILE_EXT\" in\n js|jsx|ts|tsx) FILE_ICON=\"⚛️\" ;;\n py) FILE_ICON=\"🐍\" ;;\n rb) FILE_ICON=\"💎\" ;;\n go) FILE_ICON=\"🐹\" ;;\n rs) FILE_ICON=\"🦀\" ;;\n java) FILE_ICON=\"☕\" ;;\n cpp|c|cc) FILE_ICON=\"⚙️\" ;;\n html) FILE_ICON=\"🌐\" ;;\n css|scss) FILE_ICON=\"🎨\" ;;\n json) FILE_ICON=\"📋\" ;;\n md) FILE_ICON=\"📝\" ;;\n *) FILE_ICON=\"📄\" ;;\n esac\n \n FILE_DISPLAY=\"$FILE_ICON $FILENAME\"\nelse\n FILE_DISPLAY=\"📂 General activity\"\nfi\n\n# Get current timestamp\nTIMESTAMP=$(date +\"%H:%M:%S\")\nDATE_TIME=$(date +\"%Y-%m-%d %H:%M:%S\")\n\n# Get Git information if available\nGIT_BRANCH=\"\"\nGIT_COMMIT=\"\"\nif command -v git &> /dev/null && git rev-parse --git-dir > /dev/null 2>&1; then\n GIT_BRANCH=$(git branch --show-current 2>/dev/null || echo \"\")\n GIT_COMMIT=$(git rev-parse --short HEAD 2>/dev/null || echo \"\")\nfi\n\n# Build the Discord embed JSON\nEMBED_DESCRIPTION=\"**Tool:** \\`$TOOL_NAME\\`\"\nif [ -n \"$GIT_BRANCH\" ]; then\n EMBED_DESCRIPTION=\"$EMBED_DESCRIPTION\\n**Branch:** \\`$GIT_BRANCH\\`\"\nfi\n\n# Create fields array\nFIELDS='['\nFIELDS=\"$FIELDS{\\\"name\\\": \\\"File\\\", \\\"value\\\": \\\"$FILE_DISPLAY\\\", \\\"inline\\\": true}\"\nFIELDS=\"$FIELDS,{\\\"name\\\": \\\"Time\\\", \\\"value\\\": \\\"$TIMESTAMP\\\", \\\"inline\\\": true}\"\n\nif [ -n \"$GIT_COMMIT\" ]; then\n FIELDS=\"$FIELDS,{\\\"name\\\": \\\"Commit\\\", \\\"value\\\": \\\"\\`$GIT_COMMIT\\`\\\", \\\"inline\\\": true}\"\nfi\n\nFIELDS=\"$FIELDS]\"\n\n# Create the complete webhook payload\nPAYLOAD=$(cat <<EOF\n{\n \"embeds\": [{\n \"title\": \"🤖 Claude Code Activity\",\n \"description\": \"$EMBED_DESCRIPTION\",\n \"color\": $COLOR,\n \"fields\": $FIELDS,\n \"footer\": {\n \"text\": \"Claude Code • $DATE_TIME\",\n \"icon_url\": \"https://claude.ai/favicon.ico\"\n },\n \"timestamp\": \"$(date -u +%Y-%m-%dT%H:%M:%S.000Z)\"\n }]\n}\nEOF\n)\n\n# Send the webhook\nif command -v curl &> /dev/null; then\n RESPONSE=$(curl -s -w \"%{http_code}\" -H \"Content-Type: application/json\" -X POST -d \"$PAYLOAD\" \"$DISCORD_WEBHOOK_URL\" 2>/dev/null)\n HTTP_CODE=\"${RESPONSE: -3}\"\n \n if [ \"$HTTP_CODE\" = \"204\" ]; then\n echo \"✅ Discord notification sent successfully\" >&2\n else\n echo \"⚠️ Discord notification failed with HTTP code: $HTTP_CODE\" >&2\n fi\nelse\n echo \"⚠️ curl not available - cannot send Discord notification\" >&2\nfi\n\nexit 0"
}.claude/hooks/~/.claude/hooks/{
"hooks": {
"notification": {
"script": "./.claude/hooks/discord-activity-notifier.sh"
}
}
}#!/usr/bin/env bash
# Read the tool input from stdin
INPUT=$(cat)
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name')
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.path // ""')
# Check if Discord webhook URL is configured
if [ -z "$DISCORD_WEBHOOK_URL" ]; then
echo "💡 Set DISCORD_WEBHOOK_URL environment variable to enable Discord notifications" >&2
exit 0
fi
echo "📤 Sending Discord notification for tool: $TOOL_NAME" >&2
# Determine color based on tool name or file type
COLOR="3447003" # Default blue
# Success indicators
if [[ "$TOOL_NAME" == *"Success"* ]] || [[ "$TOOL_NAME" == *"Complete"* ]]; then
COLOR="3066993" # Green
# Error indicators
elif [[ "$TOOL_NAME" == *"Error"* ]] || [[ "$TOOL_NAME" == *"Fail"* ]]; then
COLOR="15158332" # Red
# Warning indicators
elif [[ "$TOOL_NAME" == *"Warning"* ]] || [[ "$TOOL_NAME" == *"Alert"* ]]; then
COLOR="16776960" # Yellow
# Edit/Write operations
elif [[ "$TOOL_NAME" == "Edit" ]] || [[ "$TOOL_NAME" == "Write" ]] || [[ "$TOOL_NAME" == "MultiEdit" ]]; then
COLOR="5793266" # Purple
fi
# Get file information
if [ -n "$FILE_PATH" ]; then
FILENAME=$(basename "$FILE_PATH" 2>/dev/null || echo "Unknown file")
FILE_EXT="${FILENAME##*.}"
# Add file type icon based on extension
case "$FILE_EXT" in
js|jsx|ts|tsx) FILE_ICON="⚛️" ;;
py) FILE_ICON="🐍" ;;
rb) FILE_ICON="💎" ;;
go) FILE_ICON="🐹" ;;
rs) FILE_ICON="🦀" ;;
java) FILE_ICON="☕" ;;
cpp|c|cc) FILE_ICON="⚙️" ;;
html) FILE_ICON="🌐" ;;
css|scss) FILE_ICON="🎨" ;;
json) FILE_ICON="📋" ;;
md) FILE_ICON="📝" ;;
*) FILE_ICON="📄" ;;
esac
FILE_DISPLAY="$FILE_ICON $FILENAME"
else
FILE_DISPLAY="📂 General activity"
fi
# Get current timestamp
TIMESTAMP=$(date +"%H:%M:%S")
DATE_TIME=$(date +"%Y-%m-%d %H:%M:%S")
# Get Git information if available
GIT_BRANCH=""
GIT_COMMIT=""
if command -v git &> /dev/null && git rev-parse --git-dir > /dev/null 2>&1; then
GIT_BRANCH=$(git branch --show-current 2>/dev/null || echo "")
GIT_COMMIT=$(git rev-parse --short HEAD 2>/dev/null || echo "")
fi
# Build the Discord embed JSON
EMBED_DESCRIPTION="**Tool:** \`$TOOL_NAME\`"
if [ -n "$GIT_BRANCH" ]; then
EMBED_DESCRIPTION="$EMBED_DESCRIPTION\n**Branch:** \`$GIT_BRANCH\`"
fi
# Create fields array
FIELDS='['
FIELDS="$FIELDS{\"name\": \"File\", \"value\": \"$FILE_DISPLAY\", \"inline\": true}"
FIELDS="$FIELDS,{\"name\": \"Time\", \"value\": \"$TIMESTAMP\", \"inline\": true}"
if [ -n "$GIT_COMMIT" ]; then
FIELDS="$FIELDS,{\"name\": \"Commit\", \"value\": \"\`$GIT_COMMIT\`\", \"inline\": true}"
fi
FIELDS="$FIELDS]"
# Create the complete webhook payload
PAYLOAD=$(cat <<EOF
{
"embeds": [{
"title": "🤖 Claude Code Activity",
"description": "$EMBED_DESCRIPTION",
"color": $COLOR,
"fields": $FIELDS,
"footer": {
"text": "Claude Code • $DATE_TIME",
"icon_url": "https://claude.ai/favicon.ico"
},
"timestamp": "$(date -u +%Y-%m-%dT%H:%M:%S.000Z)"
}]
}
EOF
)
# Send the webhook
if command -v curl &> /dev/null; then
RESPONSE=$(curl -s -w "%{http_code}" -H "Content-Type: application/json" -X POST -d "$PAYLOAD" "$DISCORD_WEBHOOK_URL" 2>/dev/null)
HTTP_CODE="${RESPONSE: -3}"
if [ "$HTTP_CODE" = "204" ]; then
echo "✅ Discord notification sent successfully" >&2
else
echo "⚠️ Discord notification failed with HTTP code: $HTTP_CODE" >&2
fi
else
echo "⚠️ curl not available - cannot send Discord notification" >&2
fi
exit 0DISCORD_WEBHOOK_URL environment variable not available in hook execution context
Export webhook URL in shell profile (.bashrc/.zshrc) or add to Claude Code config. Test with 'echo $DISCORD_WEBHOOK_URL' in hook script to verify environment variable persists.
Discord webhook returns HTTP 400 with 'invalid JSON body' error message
Validate PAYLOAD JSON structure before sending with 'echo $PAYLOAD | jq' command. Ensure special characters in file names are properly escaped within JSON string values.
Notifications flood Discord channel during rapid-fire Edit or MultiEdit operations
Add rate limiting by checking notification count per minute using temporary file counter. Skip notification if threshold exceeded, or batch multiple operations into single embed with field array.
Git branch detection fails when hook runs in detached HEAD state
Add fallback to display commit SHA instead of branch name when 'git branch --show-current' returns empty. Use 'git describe --tags --always' for readable detached HEAD representation.
Timestamp format incompatible with Discord embed RFC3339 requirement causes validation errors
Ensure 'date -u +%Y-%m-%dT%H:%M:%S.000Z' command generates UTC ISO 8601 format. Use gdate on macOS if BSD date lacks proper UTC formatting support: 'brew install coreutils'.
Loading reviews...
Join our community of Claude power users. No spam, unsubscribe anytime.
Automated accessibility testing and compliance checking for web applications following WCAG guidelines
Automatically generates or updates API documentation when endpoint files are modified
Automatically formats code files after Claude writes or edits them using Prettier, Black, or other formatters