diff options
Diffstat (limited to 'models')
-rw-r--r-- | models/action.go | 26 | ||||
-rw-r--r-- | models/webhook.go | 14 | ||||
-rw-r--r-- | models/webhook_discord.go | 213 | ||||
-rw-r--r-- | models/webhook_slack.go | 57 |
4 files changed, 261 insertions, 49 deletions
diff --git a/models/action.go b/models/action.go index e4c73f3a..a88968d3 100644 --- a/models/action.go +++ b/models/action.go @@ -505,26 +505,30 @@ func CommitRepoAction(opts CommitRepoActionOptions) error { apiRepo := repo.APIFormat(nil) switch opType { case ACTION_COMMIT_REPO: // Push + compareURL := setting.AppUrl + opts.Commits.CompareURL + if isNewBranch { + compareURL = "" + if err = PrepareWebhooks(repo, HOOK_EVENT_CREATE, &api.CreatePayload{ + Ref: refName, + RefType: "branch", + Repo: apiRepo, + Sender: apiPusher, + }); err != nil { + return fmt.Errorf("PrepareWebhooks (new branch): %v", err) + } + } + if err = PrepareWebhooks(repo, HOOK_EVENT_PUSH, &api.PushPayload{ Ref: opts.RefFullName, Before: opts.OldCommitID, After: opts.NewCommitID, - CompareURL: setting.AppUrl + opts.Commits.CompareURL, + CompareURL: compareURL, Commits: opts.Commits.ToApiPayloadCommits(repo.HTMLURL()), Repo: apiRepo, Pusher: apiPusher, Sender: apiPusher, }); err != nil { - return fmt.Errorf("PrepareWebhooks: %v", err) - } - - if isNewBranch { - return PrepareWebhooks(repo, HOOK_EVENT_CREATE, &api.CreatePayload{ - Ref: refName, - RefType: "branch", - Repo: apiRepo, - Sender: apiPusher, - }) + return fmt.Errorf("PrepareWebhooks (new commit): %v", err) } case ACTION_PUSH_TAG: // Create diff --git a/models/webhook.go b/models/webhook.go index c8e0559e..c68c82bd 100644 --- a/models/webhook.go +++ b/models/webhook.go @@ -292,6 +292,7 @@ type HookTaskType int const ( GOGS HookTaskType = iota + 1 SLACK + DISCORD ) var hookTaskTypes = map[string]HookTaskType{ @@ -458,6 +459,10 @@ func PrepareWebhooks(repo *Repository, event HookEventType, p api.Payloader) err var payloader api.Payloader for _, w := range ws { + if !w.IsActive { + continue + } + switch event { case HOOK_EVENT_CREATE: if !w.HasCreateEvent() { @@ -476,12 +481,15 @@ func PrepareWebhooks(repo *Repository, event HookEventType, p api.Payloader) err // Use separate objects so modifcations won't be made on payload on non-Gogs type hooks. switch w.HookTaskType { case SLACK: - // FIXME: dirty fix for buggy support of Discord for Slack-type webhook. - // Should remove this if we want to support Discord fully as its own. - payloader, err = GetSlackPayload(strings.Contains(w.URL, ".discordapp.com/"), p, event, w.Meta) + payloader, err = GetSlackPayload(p, event, w.Meta) if err != nil { return fmt.Errorf("GetSlackPayload: %v", err) } + case DISCORD: + payloader, err = GetDiscordPayload(p, event, w.Meta) + if err != nil { + return fmt.Errorf("GetDiscordPayload: %v", err) + } default: p.SetSecret(w.Secret) payloader = p diff --git a/models/webhook_discord.go b/models/webhook_discord.go new file mode 100644 index 00000000..097c6f89 --- /dev/null +++ b/models/webhook_discord.go @@ -0,0 +1,213 @@ +// Copyright 2017 The Gogs 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 models + +import ( + "encoding/json" + "fmt" + "strings" + + "github.com/gogits/git-module" + api "github.com/gogits/go-gogs-client" +) + +type DiscordEmbedFooterObject struct { + Text string `json:"text"` +} + +type DiscordEmbedAuthorObject struct { + Name string `json:"name"` + URL string `json:"url"` + IconURL string `json:"icon_url"` +} + +type DiscordEmbedFieldObject struct { + Name string `json:"name"` + Value string `json:"value"` +} + +type DiscordEmbedObject struct { + Title string `json:"title"` + Description string `json:"description"` + URL string `json:"url"` + Footer *DiscordEmbedFooterObject `json:"footer"` + Author *DiscordEmbedAuthorObject `json:"author"` + Fields []*DiscordEmbedFieldObject `json:"fields"` +} + +type DiscordPayload struct { + Content string `json:"content"` + Username string `json:"username"` + AvatarURL string `json:"avatar_url"` + Embeds []*DiscordEmbedObject `json:"embeds"` +} + +func (p *DiscordPayload) SetSecret(_ string) {} + +func (p *DiscordPayload) JSONPayload() ([]byte, error) { + data, err := json.MarshalIndent(p, "", " ") + if err != nil { + return []byte{}, err + } + return data, nil +} + +func DiscordLinkFormatter(url string, text string) string { + return fmt.Sprintf("[%s](%s)", text, url) +} + +func DiscordSHALinkFormatter(url string, text string) string { + return fmt.Sprintf("[`%s`](%s)", text, url) +} + +func getDiscordCreatePayload(p *api.CreatePayload, slack *SlackMeta) (*DiscordPayload, error) { + // Created tag/branch + refName := git.RefEndName(p.Ref) + + repoLink := DiscordLinkFormatter(p.Repo.HTMLURL, p.Repo.Name) + refLink := DiscordLinkFormatter(p.Repo.HTMLURL+"/src/"+refName, refName) + content := fmt.Sprintf("Created new %s: %s/%s", p.RefType, repoLink, refLink) + + return &DiscordPayload{ + Username: slack.Username, + AvatarURL: slack.IconURL, + Embeds: []*DiscordEmbedObject{{ + Description: content, + Author: &DiscordEmbedAuthorObject{ + Name: p.Sender.UserName, + IconURL: p.Sender.AvatarUrl, + }, + }}, + }, nil +} + +func getDiscordPushPayload(p *api.PushPayload, slack *SlackMeta) (*DiscordPayload, error) { + // n new commits + var ( + branchName = git.RefEndName(p.Ref) + commitDesc string + commitString string + ) + + if len(p.Commits) == 1 { + commitDesc = "1 new commit" + } else { + commitDesc = fmt.Sprintf("%d new commits", len(p.Commits)) + } + + if len(p.CompareURL) > 0 { + commitString = DiscordLinkFormatter(p.CompareURL, commitDesc) + } else { + commitString = commitDesc + } + + repoLink := DiscordLinkFormatter(p.Repo.HTMLURL, p.Repo.Name) + branchLink := DiscordLinkFormatter(p.Repo.HTMLURL+"/src/"+branchName, branchName) + content := fmt.Sprintf("Pushed %s to %s/%s:\n", commitString, repoLink, branchLink) + + // for each commit, generate attachment text + for i, commit := range p.Commits { + content += fmt.Sprintf("%s %s - %s", DiscordSHALinkFormatter(commit.URL, commit.ID[:7]), SlackShortTextFormatter(commit.Message), commit.Author.Name) + // add linebreak to each commit but the last + if i < len(p.Commits)-1 { + content += "\n" + } + } + + return &DiscordPayload{ + Username: slack.Username, + AvatarURL: slack.IconURL, + Embeds: []*DiscordEmbedObject{{ + Description: content, + Author: &DiscordEmbedAuthorObject{ + Name: p.Sender.UserName, + IconURL: p.Sender.AvatarUrl, + }, + }}, + }, nil +} + +func getDiscordPullRequestPayload(p *api.PullRequestPayload, slack *SlackMeta) (*DiscordPayload, error) { + title := fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title) + url := fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index) + content := "" + fields := make([]*DiscordEmbedFieldObject, 0, 1) + switch p.Action { + case api.HOOK_ISSUE_OPENED: + title = "New pull request: " + title + content = p.PullRequest.Body + case api.HOOK_ISSUE_CLOSED: + if p.PullRequest.HasMerged { + title = "Pull request merged: " + title + } else { + title = "Pull request closed: " + title + } + case api.HOOK_ISSUE_REOPENED: + title = "Pull request re-opened: " + title + case api.HOOK_ISSUE_EDITED: + title = "Pull request edited: " + title + content = p.PullRequest.Body + case api.HOOK_ISSUE_ASSIGNED: + title = "Pull request assigned: " + title + fields = []*DiscordEmbedFieldObject{{ + Name: "New Assignee", + Value: p.PullRequest.Assignee.UserName, + }} + case api.HOOK_ISSUE_UNASSIGNED: + title = "Pull request unassigned: " + title + case api.HOOK_ISSUE_LABEL_UPDATED: + title = "Pull request labels updated: " + title + labels := make([]string, len(p.PullRequest.Labels)) + for i := range p.PullRequest.Labels { + labels[i] = p.PullRequest.Labels[i].Name + } + fields = []*DiscordEmbedFieldObject{{ + Name: "Labels", + Value: strings.Join(labels, ", "), + }} + case api.HOOK_ISSUE_LABEL_CLEARED: + title = "Pull request labels cleared: " + title + case api.HOOK_ISSUE_SYNCHRONIZED: + title = "Pull request synchronized: " + title + } + + return &DiscordPayload{ + Username: slack.Username, + AvatarURL: slack.IconURL, + Embeds: []*DiscordEmbedObject{{ + Title: title, + Description: content, + URL: url, + Footer: &DiscordEmbedFooterObject{ + Text: p.Repository.FullName, + }, + Author: &DiscordEmbedAuthorObject{ + Name: p.Sender.UserName, + IconURL: p.Sender.AvatarUrl, + }, + Fields: fields, + }}, + }, nil +} + +func GetDiscordPayload(p api.Payloader, event HookEventType, meta string) (*DiscordPayload, error) { + d := new(DiscordPayload) + + slack := &SlackMeta{} + if err := json.Unmarshal([]byte(meta), &slack); err != nil { + return d, fmt.Errorf("GetDiscordPayload meta json: %v", err) + } + + switch event { + case HOOK_EVENT_CREATE: + return getDiscordCreatePayload(p.(*api.CreatePayload), slack) + case HOOK_EVENT_PUSH: + return getDiscordPushPayload(p.(*api.PushPayload), slack) + case HOOK_EVENT_PULL_REQUEST: + return getDiscordPullRequestPayload(p.(*api.PullRequestPayload), slack) + } + + return d, nil +} diff --git a/models/webhook_slack.go b/models/webhook_slack.go index 6e4b9408..213421a5 100644 --- a/models/webhook_slack.go +++ b/models/webhook_slack.go @@ -6,7 +6,6 @@ package models import ( "encoding/json" - "errors" "fmt" "strings" @@ -23,16 +22,6 @@ type SlackMeta struct { Color string `json:"color"` } -type SlackPayload struct { - Channel string `json:"channel"` - Text string `json:"text"` - Username string `json:"username"` - IconURL string `json:"icon_url"` - UnfurlLinks int `json:"unfurl_links"` - LinkNames int `json:"link_names"` - Attachments []SlackAttachment `json:"attachments"` -} - type SlackAttachment struct { Fallback string `json:"fallback"` Color string `json:"color"` @@ -40,6 +29,16 @@ type SlackAttachment struct { Text string `json:"text"` } +type SlackPayload struct { + Channel string `json:"channel"` + Text string `json:"text"` + Username string `json:"username"` + IconURL string `json:"icon_url"` + UnfurlLinks int `json:"unfurl_links"` + LinkNames int `json:"link_names"` + Attachments []*SlackAttachment `json:"attachments"` +} + func (p *SlackPayload) SetSecret(_ string) {} func (p *SlackPayload) JSONPayload() ([]byte, error) { @@ -72,21 +71,13 @@ func SlackLinkFormatter(url string, text string) string { return fmt.Sprintf("<%s|%s>", url, SlackTextFormatter(text)) } -func replaceBadCharsForDiscord(in string) string { - return strings.NewReplacer("[", "", "]", ":", ":", "/").Replace(in) -} - -func getSlackCreatePayload(isDiscord bool, p *api.CreatePayload, slack *SlackMeta) (*SlackPayload, error) { +func getSlackCreatePayload(p *api.CreatePayload, slack *SlackMeta) (*SlackPayload, error) { // Created tag/branch refName := git.RefEndName(p.Ref) repoLink := SlackLinkFormatter(p.Repo.HTMLURL, p.Repo.Name) refLink := SlackLinkFormatter(p.Repo.HTMLURL+"/src/"+refName, refName) - format := "[%s:%s] %s created by %s" - if isDiscord { - format = replaceBadCharsForDiscord(format) - } - text := fmt.Sprintf(format, repoLink, refLink, p.RefType, p.Sender.UserName) + text := fmt.Sprintf("[%s:%s] %s created by %s", repoLink, refLink, p.RefType, p.Sender.UserName) return &SlackPayload{ Channel: slack.Channel, @@ -96,7 +87,7 @@ func getSlackCreatePayload(isDiscord bool, p *api.CreatePayload, slack *SlackMet }, nil } -func getSlackPushPayload(isDiscord bool, p *api.PushPayload, slack *SlackMeta) (*SlackPayload, error) { +func getSlackPushPayload(p *api.PushPayload, slack *SlackMeta) (*SlackPayload, error) { // n new commits var ( branchName = git.RefEndName(p.Ref) @@ -117,11 +108,7 @@ func getSlackPushPayload(isDiscord bool, p *api.PushPayload, slack *SlackMeta) ( repoLink := SlackLinkFormatter(p.Repo.HTMLURL, p.Repo.Name) branchLink := SlackLinkFormatter(p.Repo.HTMLURL+"/src/"+branchName, branchName) - format := "[%s:%s] %s pushed by %s" - if isDiscord { - format = replaceBadCharsForDiscord(format) - } - text := fmt.Sprintf(format, repoLink, branchLink, commitString, p.Pusher.UserName) + text := fmt.Sprintf("[%s:%s] %s pushed by %s", repoLink, branchLink, commitString, p.Pusher.UserName) var attachmentText string // for each commit, generate attachment text @@ -138,14 +125,14 @@ func getSlackPushPayload(isDiscord bool, p *api.PushPayload, slack *SlackMeta) ( Text: text, Username: slack.Username, IconURL: slack.IconURL, - Attachments: []SlackAttachment{{ + Attachments: []*SlackAttachment{{ Color: slack.Color, Text: attachmentText, }}, }, nil } -func getSlackPullRequestPayload(isDiscord bool, p *api.PullRequestPayload, slack *SlackMeta) (*SlackPayload, error) { +func getSlackPullRequestPayload(p *api.PullRequestPayload, slack *SlackMeta) (*SlackPayload, error) { senderLink := SlackLinkFormatter(setting.AppUrl+p.Sender.UserName, p.Sender.UserName) titleLink := SlackLinkFormatter(fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index), fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title)) @@ -185,7 +172,7 @@ func getSlackPullRequestPayload(isDiscord bool, p *api.PullRequestPayload, slack Text: text, Username: slack.Username, IconURL: slack.IconURL, - Attachments: []SlackAttachment{{ + Attachments: []*SlackAttachment{{ Color: slack.Color, Title: title, Text: attachmentText, @@ -193,21 +180,21 @@ func getSlackPullRequestPayload(isDiscord bool, p *api.PullRequestPayload, slack }, nil } -func GetSlackPayload(isDiscord bool, p api.Payloader, event HookEventType, meta string) (*SlackPayload, error) { +func GetSlackPayload(p api.Payloader, event HookEventType, meta string) (*SlackPayload, error) { s := new(SlackPayload) slack := &SlackMeta{} if err := json.Unmarshal([]byte(meta), &slack); err != nil { - return s, errors.New("GetSlackPayload meta json:" + err.Error()) + return s, fmt.Errorf("GetSlackPayload meta json: %v", err) } switch event { case HOOK_EVENT_CREATE: - return getSlackCreatePayload(isDiscord, p.(*api.CreatePayload), slack) + return getSlackCreatePayload(p.(*api.CreatePayload), slack) case HOOK_EVENT_PUSH: - return getSlackPushPayload(isDiscord, p.(*api.PushPayload), slack) + return getSlackPushPayload(p.(*api.PushPayload), slack) case HOOK_EVENT_PULL_REQUEST: - return getSlackPullRequestPayload(isDiscord, p.(*api.PullRequestPayload), slack) + return getSlackPullRequestPayload(p.(*api.PullRequestPayload), slack) } return s, nil |