mirror of
https://github.com/yusing/godoxy.git
synced 2025-05-20 04:42:33 +02:00

* chore: replace gopkg.in/yaml.v3 vs goccy/go-yaml; replace encoding/json with bytedance/sonic * fix: yaml unmarshal panic * feat: custom json marshaler implementation * chore: fix import and err marshal handling --------- Co-authored-by: yusing <yusing@6uo.me>
131 lines
3.2 KiB
Go
131 lines
3.2 KiB
Go
package notif
|
|
|
|
import (
|
|
_ "embed"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/yusing/go-proxy/pkg/json"
|
|
|
|
"github.com/yusing/go-proxy/internal/gperr"
|
|
)
|
|
|
|
type Webhook struct {
|
|
ProviderBase
|
|
Template string `json:"template"`
|
|
Payload string `json:"payload"`
|
|
Method string `json:"method"`
|
|
MIMEType string `json:"mime_type"`
|
|
ColorMode string `json:"color_mode"`
|
|
}
|
|
|
|
//go:embed templates/discord.json
|
|
var discordPayload string
|
|
|
|
var webhookTemplates = map[string]string{
|
|
"discord": discordPayload,
|
|
}
|
|
|
|
func (webhook *Webhook) Validate() gperr.Error {
|
|
if err := webhook.ProviderBase.Validate(); err != nil && !err.Is(ErrMissingToken) {
|
|
return err
|
|
}
|
|
|
|
switch webhook.MIMEType {
|
|
case "":
|
|
webhook.MIMEType = "application/json"
|
|
case "application/json", "application/x-www-form-urlencoded", "text/plain":
|
|
default:
|
|
return gperr.New("invalid mime_type, expect empty, 'application/json', 'application/x-www-form-urlencoded' or 'text/plain'")
|
|
}
|
|
|
|
switch webhook.Template {
|
|
case "":
|
|
if webhook.MIMEType == "application/json" && !json.Valid([]byte(webhook.Payload)) {
|
|
return gperr.New("invalid payload, expect valid JSON")
|
|
}
|
|
if webhook.Payload == "" {
|
|
return gperr.New("invalid payload, expect non-empty")
|
|
}
|
|
case "discord":
|
|
webhook.ColorMode = "dec"
|
|
webhook.Method = http.MethodPost
|
|
webhook.MIMEType = "application/json"
|
|
if webhook.Payload == "" {
|
|
webhook.Payload = discordPayload
|
|
}
|
|
default:
|
|
return gperr.New("invalid template, expect empty or 'discord'")
|
|
}
|
|
|
|
switch webhook.Method {
|
|
case "":
|
|
webhook.Method = http.MethodPost
|
|
case http.MethodGet, http.MethodPost, http.MethodPut:
|
|
default:
|
|
return gperr.New("invalid method, expect empty, 'GET', 'POST' or 'PUT'")
|
|
}
|
|
|
|
switch webhook.ColorMode {
|
|
case "":
|
|
webhook.ColorMode = "hex"
|
|
case "hex", "dec":
|
|
default:
|
|
return gperr.New("invalid color_mode, expect empty, 'hex' or 'dec'")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetMethod implements Provider.
|
|
func (webhook *Webhook) GetMethod() string {
|
|
return webhook.Method
|
|
}
|
|
|
|
// GetMIMEType implements Provider.
|
|
func (webhook *Webhook) GetMIMEType() string {
|
|
return webhook.MIMEType
|
|
}
|
|
|
|
// makeRespError implements Provider.
|
|
func (webhook *Webhook) makeRespError(resp *http.Response) error {
|
|
body, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return fmt.Errorf("%s status %d, failed to read body: %w", webhook.Name, resp.StatusCode, err)
|
|
}
|
|
if len(body) > 0 {
|
|
return fmt.Errorf("%s status %d: %s", webhook.Name, resp.StatusCode, body)
|
|
}
|
|
return fmt.Errorf("%s status %d", webhook.Name, resp.StatusCode)
|
|
}
|
|
|
|
func (webhook *Webhook) MakeBody(logMsg *LogMessage) (io.Reader, error) {
|
|
title := json.String(logMsg.Title)
|
|
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 := json.String(formatMarkdown(logMsg.Extras))
|
|
plTempl := strings.NewReplacer(
|
|
"$title", title,
|
|
"$message", 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
|
|
}
|