github.com/goreleaser/goreleaser@v1.25.1/internal/pipe/discord/discord.go (about)

     1  package discord
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"net/http"
     8  	"net/url"
     9  	"strconv"
    10  
    11  	"github.com/caarlos0/env/v9"
    12  	"github.com/caarlos0/log"
    13  	"github.com/goreleaser/goreleaser/internal/tmpl"
    14  	"github.com/goreleaser/goreleaser/pkg/context"
    15  )
    16  
    17  const (
    18  	defaultAuthor          = `GoReleaser`
    19  	defaultColor           = "3888754"
    20  	defaultIcon            = "https://goreleaser.com/static/avatar.png"
    21  	defaultMessageTemplate = `{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .ReleaseURL }}`
    22  )
    23  
    24  type Pipe struct{}
    25  
    26  func (Pipe) String() string                 { return "discord" }
    27  func (Pipe) Skip(ctx *context.Context) bool { return !ctx.Config.Announce.Discord.Enabled }
    28  
    29  type Config struct {
    30  	WebhookID    string `env:"DISCORD_WEBHOOK_ID,notEmpty"`
    31  	WebhookToken string `env:"DISCORD_WEBHOOK_TOKEN,notEmpty"`
    32  }
    33  
    34  func (p Pipe) Default(ctx *context.Context) error {
    35  	if ctx.Config.Announce.Discord.MessageTemplate == "" {
    36  		ctx.Config.Announce.Discord.MessageTemplate = defaultMessageTemplate
    37  	}
    38  	if ctx.Config.Announce.Discord.IconURL == "" {
    39  		ctx.Config.Announce.Discord.IconURL = defaultIcon
    40  	}
    41  	if ctx.Config.Announce.Discord.Author == "" {
    42  		ctx.Config.Announce.Discord.Author = defaultAuthor
    43  	}
    44  	if ctx.Config.Announce.Discord.Color == "" {
    45  		ctx.Config.Announce.Discord.Color = defaultColor
    46  	}
    47  	return nil
    48  }
    49  
    50  func (p Pipe) Announce(ctx *context.Context) error {
    51  	msg, err := tmpl.New(ctx).Apply(ctx.Config.Announce.Discord.MessageTemplate)
    52  	if err != nil {
    53  		return fmt.Errorf("discord: %w", err)
    54  	}
    55  
    56  	var cfg Config
    57  	if err = env.Parse(&cfg); err != nil {
    58  		return fmt.Errorf("discord: %w", err)
    59  	}
    60  
    61  	log.Infof("posting: '%s'", msg)
    62  
    63  	color, err := strconv.Atoi(ctx.Config.Announce.Discord.Color)
    64  	if err != nil {
    65  		return fmt.Errorf("discord: %w", err)
    66  	}
    67  
    68  	u, err := url.Parse("https://discord.com/api")
    69  	if err != nil {
    70  		return fmt.Errorf("discord: %w", err)
    71  	}
    72  	u = u.JoinPath("webhooks", cfg.WebhookID, cfg.WebhookToken)
    73  
    74  	bts, err := json.Marshal(WebhookMessageCreate{
    75  		Embeds: []Embed{
    76  			{
    77  				Author: &EmbedAuthor{
    78  					Name:    ctx.Config.Announce.Discord.Author,
    79  					IconURL: ctx.Config.Announce.Discord.IconURL,
    80  				},
    81  				Description: msg,
    82  				Color:       color,
    83  			},
    84  		},
    85  	})
    86  	if err != nil {
    87  		return fmt.Errorf("discord: %w", err)
    88  	}
    89  
    90  	resp, err := http.Post(u.String(), "application/json", bytes.NewReader(bts))
    91  	if err != nil {
    92  		return fmt.Errorf("discord: %w", err)
    93  	}
    94  
    95  	if resp.StatusCode != 204 && resp.StatusCode != 200 {
    96  		return fmt.Errorf("discord: %s", resp.Status)
    97  	}
    98  
    99  	return nil
   100  }
   101  
   102  type WebhookMessageCreate struct {
   103  	Embeds []Embed `json:"embeds,omitempty"`
   104  }
   105  
   106  type Embed struct {
   107  	Description string       `json:"description,omitempty"`
   108  	Color       int          `json:"color,omitempty"`
   109  	Author      *EmbedAuthor `json:"author,omitempty"`
   110  }
   111  
   112  type EmbedAuthor struct {
   113  	Name    string `json:"name,omitempty"`
   114  	IconURL string `json:"icon_url,omitempty"`
   115  }