feat: cc-hook v1.0 - Claude Code 钩子服务
- Go HTTP 服务,监听 :8082 - 支持 Notification 和 Stop 事件 - 转发至 Gotify 推送通知 - Docker 多阶段构建 - 环境变量配置 - 详细中文 README 文档
This commit is contained in:
+106
@@ -0,0 +1,106 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// ClaudeCodeHookEvent represents the JSON payload from Claude Code hooks
|
||||
type ClaudeCodeHookEvent struct {
|
||||
SessionID string `json:"session_id"`
|
||||
CWD string `json:"cwd"`
|
||||
HookEventName string `json:"hook_event_name"`
|
||||
ToolName string `json:"tool_name,omitempty"`
|
||||
ToolInput map[string]interface{} `json:"tool_input,omitempty"`
|
||||
Matcher string `json:"matcher,omitempty"`
|
||||
StopHookActive bool `json:"stop_hook_active,omitempty"`
|
||||
}
|
||||
|
||||
type HookHandler struct {
|
||||
config Config
|
||||
}
|
||||
|
||||
func NewHookHandler(config Config) *HookHandler {
|
||||
return &HookHandler{config: config}
|
||||
}
|
||||
|
||||
func (h *HookHandler) HandleHook(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
var event ClaudeCodeHookEvent
|
||||
if err := json.NewDecoder(r.Body).Decode(&event); err != nil {
|
||||
log.Printf("failed to decode hook event: %v", err)
|
||||
http.Error(w, "bad request", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("received hook: event=%s, matcher=%s", event.HookEventName, event.Matcher)
|
||||
|
||||
var title, message string
|
||||
var priority int
|
||||
|
||||
switch event.HookEventName {
|
||||
case "Notification":
|
||||
title, message, priority = h.handleNotification(event)
|
||||
case "Stop":
|
||||
title, message, priority = h.handleStop(event)
|
||||
default:
|
||||
log.Printf("ignored event: %s", event.HookEventName)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
fmt.Fprintf(w, `{"status":"ignored"}`)
|
||||
return
|
||||
}
|
||||
|
||||
if err := SendMessage(h.config.GotifyURL, h.config.GotifyToken, title, message, priority); err != nil {
|
||||
log.Printf("failed to send gotify message: %v", err)
|
||||
http.Error(w, "internal error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("notification sent: %s", title)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
fmt.Fprintf(w, `{"status":"ok"}`)
|
||||
}
|
||||
|
||||
func (h *HookHandler) handleNotification(event ClaudeCodeHookEvent) (title, message string, priority int) {
|
||||
switch event.Matcher {
|
||||
case "permission_prompt":
|
||||
title = "Claude Code - 需要权限"
|
||||
message = "Claude 需要你批准一个操作"
|
||||
priority = 7
|
||||
case "idle_prompt":
|
||||
title = "Claude Code - 等待输入"
|
||||
message = "Claude 完成工作,等待你的下一步指令"
|
||||
priority = 5
|
||||
case "auth_success":
|
||||
title = "Claude Code - 认证成功"
|
||||
message = "身份验证完成"
|
||||
priority = 3
|
||||
default:
|
||||
title = "Claude Code - 通知"
|
||||
message = "Claude Code 需要你的关注"
|
||||
priority = 5
|
||||
}
|
||||
|
||||
return title, message, priority
|
||||
}
|
||||
|
||||
func (h *HookHandler) handleStop(event ClaudeCodeHookEvent) (title, message string, priority int) {
|
||||
if event.StopHookActive {
|
||||
// Avoid infinite loop: Stop hook triggered too many times
|
||||
title = "Claude Code - 循环停止"
|
||||
message = "Stop hook 连续触发多次,已自动停止"
|
||||
priority = 8
|
||||
} else {
|
||||
title = "Claude Code - 任务完成"
|
||||
message = "Claude 完成了本轮回复"
|
||||
priority = 5
|
||||
}
|
||||
|
||||
return title, message, priority
|
||||
}
|
||||
Reference in New Issue
Block a user