package notif import ( _ "embed" "encoding/json" "fmt" "io" "net/http" "strings" "github.com/go-playground/validator/v10" "github.com/yusing/go-proxy/internal/utils" ) type Webhook struct { N string `json:"name" validate:"required"` U string `json:"url" validate:"url"` Template string `json:"template" validate:"omitempty,oneof=discord"` Payload string `json:"payload" validate:"jsonIfTemplateNotUsed"` Tok string `json:"token"` Meth string `json:"method" validate:"oneof=GET POST PUT"` MIMETyp string `json:"mime_type"` ColorM string `json:"color_mode" validate:"oneof=hex dec"` } //go:embed templates/discord.json var discordPayload string var webhookTemplates = map[string]string{ "discord": discordPayload, } func DefaultValue() *Webhook { return &Webhook{ Meth: "POST", ColorM: "hex", MIMETyp: "application/json", } } func jsonIfTemplateNotUsed(fl validator.FieldLevel) bool { template := fl.Parent().FieldByName("Template").String() if template != "" { return true } payload := fl.Field().String() return json.Valid([]byte(payload)) } func init() { utils.RegisterDefaultValueFactory(DefaultValue) err := utils.Validator().RegisterValidation("jsonIfTemplateNotUsed", jsonIfTemplateNotUsed) if err != nil { panic(err) } } // Name implements Provider. func (webhook *Webhook) Name() string { return webhook.N } // Method implements Provider. func (webhook *Webhook) Method() string { return webhook.Meth } // URL implements Provider. func (webhook *Webhook) URL() string { return webhook.U } // Token implements Provider. func (webhook *Webhook) Token() string { return webhook.Tok } // MIMEType implements Provider. func (webhook *Webhook) MIMEType() string { return webhook.MIMETyp } func (webhook *Webhook) ColorMode() string { switch webhook.Template { case "discord": return "dec" default: return webhook.ColorM } } // makeRespError implements Provider. func (webhook *Webhook) makeRespError(resp *http.Response) error { body, err := io.ReadAll(resp.Body) if err != nil { return fmt.Errorf("webhook status %d, failed to read body: %w", resp.StatusCode, err) } if len(body) > 0 { return fmt.Errorf("webhook status %d: %s", resp.StatusCode, body) } return fmt.Errorf("webhook status %d", resp.StatusCode) } func (webhook *Webhook) MakeBody(logMsg *LogMessage) (io.Reader, error) { title, err := json.Marshal(logMsg.Title) if err != nil { return nil, err } fields, err := formatDiscord(logMsg.Extras) if err != nil { return nil, err } var color string if webhook.ColorMode() == "hex" { color = logMsg.Color.HexString() } else { color = logMsg.Color.DecString() } message, err := json.Marshal(formatMarkdown(logMsg.Extras)) if err != nil { return nil, err } plTempl := strings.NewReplacer( "$title", string(title), "$message", string(message), "$fields", fields, "$color", color, ) var pl string if webhook.Template != "" { pl = webhookTemplates[webhook.Template] } else { pl = webhook.Payload } pl = plTempl.Replace(pl) return strings.NewReader(pl), nil }