diff options
author | Lunny Xiao <xiaolunwen@gmail.com> | 2020-12-08 18:41:14 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-12-08 11:41:14 +0100 |
commit | 42354dfe45fa0cabb59674b896c44a55a56cf163 (patch) | |
tree | 86b859881da6ef6bf288183933d7bc519dedc3d4 /services/webhook/discord.go | |
parent | 4d66ee1f74799196cbdbfd925c3f95e552584b42 (diff) | |
download | gitea-42354dfe45fa0cabb59674b896c44a55a56cf163.tar.gz gitea-42354dfe45fa0cabb59674b896c44a55a56cf163.zip |
Move webhook type from int to string (#13664)
* Move webhook type from int to string
* rename webhook_services
* finish refactor
* Fix merge
* Ignore unnecessary ci
Co-authored-by: 6543 <6543@obermui.de>
Co-authored-by: zeripath <art27@cantab.net>
Diffstat (limited to 'services/webhook/discord.go')
-rw-r--r-- | services/webhook/discord.go | 432 |
1 files changed, 432 insertions, 0 deletions
diff --git a/services/webhook/discord.go b/services/webhook/discord.go new file mode 100644 index 0000000000..530e7adbda --- /dev/null +++ b/services/webhook/discord.go @@ -0,0 +1,432 @@ +// Copyright 2017 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package webhook + +import ( + "encoding/json" + "errors" + "fmt" + "strconv" + "strings" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" + api "code.gitea.io/gitea/modules/structs" +) + +type ( + // DiscordEmbedFooter for Embed Footer Structure. + DiscordEmbedFooter struct { + Text string `json:"text"` + } + + // DiscordEmbedAuthor for Embed Author Structure + DiscordEmbedAuthor struct { + Name string `json:"name"` + URL string `json:"url"` + IconURL string `json:"icon_url"` + } + + // DiscordEmbedField for Embed Field Structure + DiscordEmbedField struct { + Name string `json:"name"` + Value string `json:"value"` + } + + // DiscordEmbed is for Embed Structure + DiscordEmbed struct { + Title string `json:"title"` + Description string `json:"description"` + URL string `json:"url"` + Color int `json:"color"` + Footer DiscordEmbedFooter `json:"footer"` + Author DiscordEmbedAuthor `json:"author"` + Fields []DiscordEmbedField `json:"fields"` + } + + // DiscordPayload represents + DiscordPayload struct { + Wait bool `json:"wait"` + Content string `json:"content"` + Username string `json:"username"` + AvatarURL string `json:"avatar_url"` + TTS bool `json:"tts"` + Embeds []DiscordEmbed `json:"embeds"` + } + + // DiscordMeta contains the discord metadata + DiscordMeta struct { + Username string `json:"username"` + IconURL string `json:"icon_url"` + } +) + +// GetDiscordHook returns discord metadata +func GetDiscordHook(w *models.Webhook) *DiscordMeta { + s := &DiscordMeta{} + if err := json.Unmarshal([]byte(w.Meta), s); err != nil { + log.Error("webhook.GetDiscordHook(%d): %v", w.ID, err) + } + return s +} + +func color(clr string) int { + if clr != "" { + clr = strings.TrimLeft(clr, "#") + if s, err := strconv.ParseInt(clr, 16, 32); err == nil { + return int(s) + } + } + + return 0 +} + +var ( + greenColor = color("1ac600") + greenColorLight = color("bfe5bf") + yellowColor = color("ffd930") + greyColor = color("4f545c") + purpleColor = color("7289da") + orangeColor = color("eb6420") + orangeColorLight = color("e68d60") + redColor = color("ff3232") +) + +// SetSecret sets the discord secret +func (d *DiscordPayload) SetSecret(_ string) {} + +// JSONPayload Marshals the DiscordPayload to json +func (d *DiscordPayload) JSONPayload() ([]byte, error) { + data, err := json.MarshalIndent(d, "", " ") + if err != nil { + return []byte{}, err + } + return data, nil +} + +var ( + _ PayloadConvertor = &DiscordPayload{} +) + +// Create implements PayloadConvertor Create method +func (d *DiscordPayload) Create(p *api.CreatePayload) (api.Payloader, error) { + // created tag/branch + refName := git.RefEndName(p.Ref) + title := fmt.Sprintf("[%s] %s %s created", p.Repo.FullName, p.RefType, refName) + + return &DiscordPayload{ + Username: d.Username, + AvatarURL: d.AvatarURL, + Embeds: []DiscordEmbed{ + { + Title: title, + URL: p.Repo.HTMLURL + "/src/" + refName, + Color: greenColor, + Author: DiscordEmbedAuthor{ + Name: p.Sender.UserName, + URL: setting.AppURL + p.Sender.UserName, + IconURL: p.Sender.AvatarURL, + }, + }, + }, + }, nil +} + +// Delete implements PayloadConvertor Delete method +func (d *DiscordPayload) Delete(p *api.DeletePayload) (api.Payloader, error) { + // deleted tag/branch + refName := git.RefEndName(p.Ref) + title := fmt.Sprintf("[%s] %s %s deleted", p.Repo.FullName, p.RefType, refName) + + return &DiscordPayload{ + Username: d.Username, + AvatarURL: d.AvatarURL, + Embeds: []DiscordEmbed{ + { + Title: title, + URL: p.Repo.HTMLURL + "/src/" + refName, + Color: redColor, + Author: DiscordEmbedAuthor{ + Name: p.Sender.UserName, + URL: setting.AppURL + p.Sender.UserName, + IconURL: p.Sender.AvatarURL, + }, + }, + }, + }, nil +} + +// Fork implements PayloadConvertor Fork method +func (d *DiscordPayload) Fork(p *api.ForkPayload) (api.Payloader, error) { + title := fmt.Sprintf("%s is forked to %s", p.Forkee.FullName, p.Repo.FullName) + + return &DiscordPayload{ + Username: d.Username, + AvatarURL: d.AvatarURL, + Embeds: []DiscordEmbed{ + { + Title: title, + URL: p.Repo.HTMLURL, + Color: greenColor, + Author: DiscordEmbedAuthor{ + Name: p.Sender.UserName, + URL: setting.AppURL + p.Sender.UserName, + IconURL: p.Sender.AvatarURL, + }, + }, + }, + }, nil +} + +// Push implements PayloadConvertor Push method +func (d *DiscordPayload) Push(p *api.PushPayload) (api.Payloader, error) { + var ( + branchName = git.RefEndName(p.Ref) + commitDesc string + ) + + var titleLink string + if len(p.Commits) == 1 { + commitDesc = "1 new commit" + titleLink = p.Commits[0].URL + } else { + commitDesc = fmt.Sprintf("%d new commits", len(p.Commits)) + titleLink = p.CompareURL + } + if titleLink == "" { + titleLink = p.Repo.HTMLURL + "/src/" + branchName + } + + title := fmt.Sprintf("[%s:%s] %s", p.Repo.FullName, branchName, commitDesc) + + var text string + // for each commit, generate attachment text + for i, commit := range p.Commits { + text += fmt.Sprintf("[%s](%s) %s - %s", commit.ID[:7], commit.URL, + strings.TrimRight(commit.Message, "\r\n"), commit.Author.Name) + // add linebreak to each commit but the last + if i < len(p.Commits)-1 { + text += "\n" + } + } + + return &DiscordPayload{ + Username: d.Username, + AvatarURL: d.AvatarURL, + Embeds: []DiscordEmbed{ + { + Title: title, + Description: text, + URL: titleLink, + Color: greenColor, + Author: DiscordEmbedAuthor{ + Name: p.Sender.UserName, + URL: setting.AppURL + p.Sender.UserName, + IconURL: p.Sender.AvatarURL, + }, + }, + }, + }, nil +} + +// Issue implements PayloadConvertor Issue method +func (d *DiscordPayload) Issue(p *api.IssuePayload) (api.Payloader, error) { + text, _, attachmentText, color := getIssuesPayloadInfo(p, noneLinkFormatter, false) + + return &DiscordPayload{ + Username: d.Username, + AvatarURL: d.AvatarURL, + Embeds: []DiscordEmbed{ + { + Title: text, + Description: attachmentText, + URL: p.Issue.HTMLURL, + Color: color, + Author: DiscordEmbedAuthor{ + Name: p.Sender.UserName, + URL: setting.AppURL + p.Sender.UserName, + IconURL: p.Sender.AvatarURL, + }, + }, + }, + }, nil +} + +// IssueComment implements PayloadConvertor IssueComment method +func (d *DiscordPayload) IssueComment(p *api.IssueCommentPayload) (api.Payloader, error) { + text, _, color := getIssueCommentPayloadInfo(p, noneLinkFormatter, false) + + return &DiscordPayload{ + Username: d.Username, + AvatarURL: d.AvatarURL, + Embeds: []DiscordEmbed{ + { + Title: text, + Description: p.Comment.Body, + URL: p.Comment.HTMLURL, + Color: color, + Author: DiscordEmbedAuthor{ + Name: p.Sender.UserName, + URL: setting.AppURL + p.Sender.UserName, + IconURL: p.Sender.AvatarURL, + }, + }, + }, + }, nil +} + +// PullRequest implements PayloadConvertor PullRequest method +func (d *DiscordPayload) PullRequest(p *api.PullRequestPayload) (api.Payloader, error) { + text, _, attachmentText, color := getPullRequestPayloadInfo(p, noneLinkFormatter, false) + + return &DiscordPayload{ + Username: d.Username, + AvatarURL: d.AvatarURL, + Embeds: []DiscordEmbed{ + { + Title: text, + Description: attachmentText, + URL: p.PullRequest.HTMLURL, + Color: color, + Author: DiscordEmbedAuthor{ + Name: p.Sender.UserName, + URL: setting.AppURL + p.Sender.UserName, + IconURL: p.Sender.AvatarURL, + }, + }, + }, + }, nil +} + +// Review implements PayloadConvertor Review method +func (d *DiscordPayload) Review(p *api.PullRequestPayload, event models.HookEventType) (api.Payloader, error) { + var text, title string + var color int + switch p.Action { + case api.HookIssueReviewed: + action, err := parseHookPullRequestEventType(event) + if err != nil { + return nil, err + } + + title = fmt.Sprintf("[%s] Pull request review %s: #%d %s", p.Repository.FullName, action, p.Index, p.PullRequest.Title) + text = p.Review.Content + + switch event { + case models.HookEventPullRequestReviewApproved: + color = greenColor + case models.HookEventPullRequestReviewRejected: + color = redColor + case models.HookEventPullRequestComment: + color = greyColor + default: + color = yellowColor + } + } + + return &DiscordPayload{ + Username: d.Username, + AvatarURL: d.AvatarURL, + Embeds: []DiscordEmbed{ + { + Title: title, + Description: text, + URL: p.PullRequest.HTMLURL, + Color: color, + Author: DiscordEmbedAuthor{ + Name: p.Sender.UserName, + URL: setting.AppURL + p.Sender.UserName, + IconURL: p.Sender.AvatarURL, + }, + }, + }, + }, nil +} + +// Repository implements PayloadConvertor Repository method +func (d *DiscordPayload) Repository(p *api.RepositoryPayload) (api.Payloader, error) { + var title, url string + var color int + switch p.Action { + case api.HookRepoCreated: + title = fmt.Sprintf("[%s] Repository created", p.Repository.FullName) + url = p.Repository.HTMLURL + color = greenColor + case api.HookRepoDeleted: + title = fmt.Sprintf("[%s] Repository deleted", p.Repository.FullName) + color = redColor + } + + return &DiscordPayload{ + Username: d.Username, + AvatarURL: d.AvatarURL, + Embeds: []DiscordEmbed{ + { + Title: title, + URL: url, + Color: color, + Author: DiscordEmbedAuthor{ + Name: p.Sender.UserName, + URL: setting.AppURL + p.Sender.UserName, + IconURL: p.Sender.AvatarURL, + }, + }, + }, + }, nil +} + +// Release implements PayloadConvertor Release method +func (d *DiscordPayload) Release(p *api.ReleasePayload) (api.Payloader, error) { + text, color := getReleasePayloadInfo(p, noneLinkFormatter, false) + + return &DiscordPayload{ + Username: d.Username, + AvatarURL: d.AvatarURL, + Embeds: []DiscordEmbed{ + { + Title: text, + Description: p.Release.Note, + URL: p.Release.URL, + Color: color, + Author: DiscordEmbedAuthor{ + Name: p.Sender.UserName, + URL: setting.AppURL + p.Sender.UserName, + IconURL: p.Sender.AvatarURL, + }, + }, + }, + }, nil +} + +// GetDiscordPayload converts a discord webhook into a DiscordPayload +func GetDiscordPayload(p api.Payloader, event models.HookEventType, meta string) (api.Payloader, error) { + s := new(DiscordPayload) + + discord := &DiscordMeta{} + if err := json.Unmarshal([]byte(meta), &discord); err != nil { + return s, errors.New("GetDiscordPayload meta json:" + err.Error()) + } + s.Username = discord.Username + s.AvatarURL = discord.IconURL + + return convertPayloader(s, p, event) +} + +func parseHookPullRequestEventType(event models.HookEventType) (string, error) { + switch event { + + case models.HookEventPullRequestReviewApproved: + return "approved", nil + case models.HookEventPullRequestReviewRejected: + return "rejected", nil + case models.HookEventPullRequestComment: + return "comment", nil + + default: + return "", errors.New("unknown event type") + } +} |