From 3a14a69e8aed9bcaac8fa75c5b0cb87d90d0de30 Mon Sep 17 00:00:00 2001 From: Cornel Date: Wed, 18 Dec 2019 20:56:35 +0800 Subject: [PATCH] Fix Slack webhook payload title generation to work with Mattermost (#9378) (#9404) --- models/webhook_slack.go | 169 ++++++++++++++++++++++------------------ 1 file changed, 93 insertions(+), 76 deletions(-) diff --git a/models/webhook_slack.go b/models/webhook_slack.go index 9c179bb24a0..fc7263862c7 100644 --- a/models/webhook_slack.go +++ b/models/webhook_slack.go @@ -36,10 +36,11 @@ type SlackPayload struct { // SlackAttachment contains the slack message type SlackAttachment struct { - Fallback string `json:"fallback"` - Color string `json:"color"` - Title string `json:"title"` - Text string `json:"text"` + Fallback string `json:"fallback"` + Color string `json:"color"` + Title string `json:"title"` + TitleLink string `json:"title_link"` + Text string `json:"text"` } // SetSecret sets the slack secret @@ -133,70 +134,78 @@ func getSlackForkPayload(p *api.ForkPayload, slack *SlackMeta) (*SlackPayload, e func getSlackIssuesPayload(p *api.IssuePayload, 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.Issue.Title)) - var text, title, attachmentText string + title := SlackTextFormatter(fmt.Sprintf("#%d %s", p.Index, p.Issue.Title)) + titleLink := fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Index) + repoLink := SlackLinkFormatter(p.Repository.HTMLURL, p.Repository.FullName) + var text, attachmentText string + switch p.Action { case api.HookIssueOpened: - text = fmt.Sprintf("[%s] Issue submitted by %s", p.Repository.FullName, senderLink) - title = titleLink + text = fmt.Sprintf("[%s] Issue opened by %s", repoLink, senderLink) attachmentText = SlackTextFormatter(p.Issue.Body) case api.HookIssueClosed: - text = fmt.Sprintf("[%s] Issue closed: %s by %s", p.Repository.FullName, titleLink, senderLink) + text = fmt.Sprintf("[%s] Issue closed: [%s](%s) by %s", repoLink, title, titleLink, senderLink) case api.HookIssueReOpened: - text = fmt.Sprintf("[%s] Issue re-opened: %s by %s", p.Repository.FullName, titleLink, senderLink) + text = fmt.Sprintf("[%s] Issue re-opened: [%s](%s) by %s", repoLink, title, titleLink, senderLink) case api.HookIssueEdited: - text = fmt.Sprintf("[%s] Issue edited: %s by %s", p.Repository.FullName, titleLink, senderLink) + text = fmt.Sprintf("[%s] Issue edited: [%s](%s) by %s", repoLink, title, titleLink, senderLink) attachmentText = SlackTextFormatter(p.Issue.Body) case api.HookIssueAssigned: - text = fmt.Sprintf("[%s] Issue assigned to %s: %s by %s", p.Repository.FullName, + text = fmt.Sprintf("[%s] Issue assigned to %s: [%s](%s) by %s", repoLink, SlackLinkFormatter(setting.AppURL+p.Issue.Assignee.UserName, p.Issue.Assignee.UserName), - titleLink, senderLink) + title, titleLink, senderLink) case api.HookIssueUnassigned: - text = fmt.Sprintf("[%s] Issue unassigned: %s by %s", p.Repository.FullName, titleLink, senderLink) + text = fmt.Sprintf("[%s] Issue unassigned: [%s](%s) by %s", repoLink, title, titleLink, senderLink) case api.HookIssueLabelUpdated: - text = fmt.Sprintf("[%s] Issue labels updated: %s by %s", p.Repository.FullName, titleLink, senderLink) + text = fmt.Sprintf("[%s] Issue labels updated: [%s](%s) by %s", repoLink, title, titleLink, senderLink) case api.HookIssueLabelCleared: - text = fmt.Sprintf("[%s] Issue labels cleared: %s by %s", p.Repository.FullName, titleLink, senderLink) + text = fmt.Sprintf("[%s] Issue labels cleared: [%s](%s) by %s", repoLink, title, titleLink, senderLink) case api.HookIssueSynchronized: - text = fmt.Sprintf("[%s] Issue synchronized: %s by %s", p.Repository.FullName, titleLink, senderLink) + text = fmt.Sprintf("[%s] Issue synchronized: [%s](%s) by %s", repoLink, title, titleLink, senderLink) case api.HookIssueMilestoned: - text = fmt.Sprintf("[%s] Issue milestoned: #%s %s", p.Repository.FullName, titleLink, senderLink) + mileStoneLink := fmt.Sprintf("%s/milestone/%d", p.Repository.HTMLURL, p.Issue.Milestone.ID) + text = fmt.Sprintf("[%s] Issue milestoned to [%s](%s): [%s](%s) by %s", repoLink, + p.Issue.Milestone.Title, mileStoneLink, title, titleLink, senderLink) case api.HookIssueDemilestoned: - text = fmt.Sprintf("[%s] Issue milestone cleared: #%s %s", p.Repository.FullName, titleLink, senderLink) + text = fmt.Sprintf("[%s] Issue milestone cleared: [%s](%s) by %s", repoLink, title, titleLink, senderLink) } - return &SlackPayload{ + pl := &SlackPayload{ Channel: slack.Channel, Text: text, Username: slack.Username, IconURL: slack.IconURL, - Attachments: []SlackAttachment{{ - Color: slack.Color, - Title: title, - Text: attachmentText, - }}, - }, nil + } + if attachmentText != "" { + pl.Attachments = []SlackAttachment{{ + Color: slack.Color, + Title: title, + TitleLink: titleLink, + Text: attachmentText, + }} + } + + return pl, nil } func getSlackIssueCommentPayload(p *api.IssueCommentPayload, slack *SlackMeta) (*SlackPayload, error) { senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName) - titleLink := SlackLinkFormatter(fmt.Sprintf("%s/issues/%d#%s", p.Repository.HTMLURL, p.Issue.Index, CommentHashTag(p.Comment.ID)), - fmt.Sprintf("#%d %s", p.Issue.Index, p.Issue.Title)) - var text, title, attachmentText string + title := SlackTextFormatter(fmt.Sprintf("#%d %s", p.Issue.Index, p.Issue.Title)) + repoLink := SlackLinkFormatter(p.Repository.HTMLURL, p.Repository.FullName) + var text, titleLink, attachmentText string + switch p.Action { case api.HookIssueCommentCreated: - text = fmt.Sprintf("[%s] New comment created by %s", p.Repository.FullName, senderLink) - title = titleLink + text = fmt.Sprintf("[%s] New comment created by %s", repoLink, senderLink) + titleLink = fmt.Sprintf("%s/issues/%d#%s", p.Repository.HTMLURL, p.Issue.Index, CommentHashTag(p.Comment.ID)) attachmentText = SlackTextFormatter(p.Comment.Body) case api.HookIssueCommentEdited: - text = fmt.Sprintf("[%s] Comment edited by %s", p.Repository.FullName, senderLink) - title = titleLink + text = fmt.Sprintf("[%s] Comment edited by %s", repoLink, senderLink) + titleLink = fmt.Sprintf("%s/issues/%d#%s", p.Repository.HTMLURL, p.Issue.Index, CommentHashTag(p.Comment.ID)) attachmentText = SlackTextFormatter(p.Comment.Body) case api.HookIssueCommentDeleted: - text = fmt.Sprintf("[%s] Comment deleted by %s", p.Repository.FullName, senderLink) - title = SlackLinkFormatter(fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Issue.Index), - fmt.Sprintf("#%d %s", p.Issue.Index, p.Issue.Title)) + text = fmt.Sprintf("[%s] Comment deleted by %s", repoLink, senderLink) + titleLink = fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Issue.Index) attachmentText = SlackTextFormatter(p.Comment.Body) } @@ -206,9 +215,10 @@ func getSlackIssueCommentPayload(p *api.IssueCommentPayload, slack *SlackMeta) ( Username: slack.Username, IconURL: slack.IconURL, Attachments: []SlackAttachment{{ - Color: slack.Color, - Title: title, - Text: attachmentText, + Color: slack.Color, + Title: title, + TitleLink: titleLink, + Text: attachmentText, }}, }, nil } @@ -281,65 +291,75 @@ func getSlackPushPayload(p *api.PushPayload, slack *SlackMeta) (*SlackPayload, e 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)) - var text, title, attachmentText string + title := fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title) + titleLink := fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index) + repoLink := SlackLinkFormatter(p.Repository.HTMLURL, p.Repository.FullName) + var text, attachmentText string + switch p.Action { case api.HookIssueOpened: - text = fmt.Sprintf("[%s] Pull request submitted by %s", p.Repository.FullName, senderLink) - title = titleLink + text = fmt.Sprintf("[%s] Pull request opened by %s", repoLink, senderLink) attachmentText = SlackTextFormatter(p.PullRequest.Body) case api.HookIssueClosed: if p.PullRequest.HasMerged { - text = fmt.Sprintf("[%s] Pull request merged: %s by %s", p.Repository.FullName, titleLink, senderLink) + text = fmt.Sprintf("[%s] Pull request merged: [%s](%s) by %s", repoLink, title, titleLink, senderLink) } else { - text = fmt.Sprintf("[%s] Pull request closed: %s by %s", p.Repository.FullName, titleLink, senderLink) + text = fmt.Sprintf("[%s] Pull request closed: [%s](%s) by %s", repoLink, title, titleLink, senderLink) } case api.HookIssueReOpened: - text = fmt.Sprintf("[%s] Pull request re-opened: %s by %s", p.Repository.FullName, titleLink, senderLink) + text = fmt.Sprintf("[%s] Pull request re-opened: [%s](%s) by %s", repoLink, title, titleLink, senderLink) case api.HookIssueEdited: - text = fmt.Sprintf("[%s] Pull request edited: %s by %s", p.Repository.FullName, titleLink, senderLink) + text = fmt.Sprintf("[%s] Pull request edited: [%s](%s) by %s", repoLink, title, titleLink, senderLink) attachmentText = SlackTextFormatter(p.PullRequest.Body) case api.HookIssueAssigned: list := make([]string, len(p.PullRequest.Assignees)) for i, user := range p.PullRequest.Assignees { list[i] = SlackLinkFormatter(setting.AppURL+user.UserName, user.UserName) } - text = fmt.Sprintf("[%s] Pull request assigned to %s: %s by %s", p.Repository.FullName, + text = fmt.Sprintf("[%s] Pull request assigned to %s: [%s](%s) by %s", repoLink, strings.Join(list, ", "), - titleLink, senderLink) + title, titleLink, senderLink) case api.HookIssueUnassigned: - text = fmt.Sprintf("[%s] Pull request unassigned: %s by %s", p.Repository.FullName, titleLink, senderLink) + text = fmt.Sprintf("[%s] Pull request unassigned: [%s](%s) by %s", repoLink, title, titleLink, senderLink) case api.HookIssueLabelUpdated: - text = fmt.Sprintf("[%s] Pull request labels updated: %s by %s", p.Repository.FullName, titleLink, senderLink) + text = fmt.Sprintf("[%s] Pull request labels updated: [%s](%s) by %s", repoLink, title, titleLink, senderLink) case api.HookIssueLabelCleared: - text = fmt.Sprintf("[%s] Pull request labels cleared: %s by %s", p.Repository.FullName, titleLink, senderLink) + text = fmt.Sprintf("[%s] Pull request labels cleared: [%s](%s) by %s", repoLink, title, titleLink, senderLink) case api.HookIssueSynchronized: - text = fmt.Sprintf("[%s] Pull request synchronized: %s by %s", p.Repository.FullName, titleLink, senderLink) + text = fmt.Sprintf("[%s] Pull request synchronized: [%s](%s) by %s", repoLink, title, titleLink, senderLink) case api.HookIssueMilestoned: - text = fmt.Sprintf("[%s] Pull request milestoned: #%s %s", p.Repository.FullName, titleLink, senderLink) + mileStoneLink := fmt.Sprintf("%s/milestone/%d", p.Repository.HTMLURL, p.PullRequest.Milestone.ID) + text = fmt.Sprintf("[%s] Pull request milestoned to [%s](%s): [%s](%s) %s", repoLink, + p.PullRequest.Milestone.Title, mileStoneLink, title, titleLink, senderLink) case api.HookIssueDemilestoned: - text = fmt.Sprintf("[%s] Pull request milestone cleared: #%s %s", p.Repository.FullName, titleLink, senderLink) + text = fmt.Sprintf("[%s] Pull request milestone cleared: [%s](%s) %s", repoLink, title, titleLink, senderLink) } - return &SlackPayload{ + pl := &SlackPayload{ Channel: slack.Channel, Text: text, Username: slack.Username, IconURL: slack.IconURL, - Attachments: []SlackAttachment{{ - Color: slack.Color, - Title: title, - Text: attachmentText, - }}, - }, nil + } + if attachmentText != "" { + pl.Attachments = []SlackAttachment{{ + Color: slack.Color, + Title: title, + TitleLink: titleLink, + Text: attachmentText, + }} + } + + return pl, nil } func getSlackPullRequestApprovalPayload(p *api.PullRequestPayload, slack *SlackMeta, event HookEventType) (*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)) - var text, title, attachmentText string + title := fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title) + titleLink := fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index) + repoLink := SlackLinkFormatter(p.Repository.HTMLURL, p.Repository.FullName) + var text string + switch p.Action { case api.HookIssueSynchronized: action, err := parseHookPullRequestEventType(event) @@ -347,7 +367,7 @@ func getSlackPullRequestApprovalPayload(p *api.PullRequestPayload, slack *SlackM return nil, err } - text = fmt.Sprintf("[%s] Pull request review %s : %s by %s", p.Repository.FullName, action, titleLink, senderLink) + text = fmt.Sprintf("[%s] Pull request review %s: [%s](%s) by %s", repoLink, action, title, titleLink, senderLink) } return &SlackPayload{ @@ -355,17 +375,13 @@ func getSlackPullRequestApprovalPayload(p *api.PullRequestPayload, slack *SlackM Text: text, Username: slack.Username, IconURL: slack.IconURL, - Attachments: []SlackAttachment{{ - Color: slack.Color, - Title: title, - Text: attachmentText, - }}, }, nil } func getSlackRepositoryPayload(p *api.RepositoryPayload, slack *SlackMeta) (*SlackPayload, error) { senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName) var text, title, attachmentText string + switch p.Action { case api.HookRepoCreated: text = fmt.Sprintf("[%s] Repository created by %s", p.Repository.FullName, senderLink) @@ -380,9 +396,10 @@ func getSlackRepositoryPayload(p *api.RepositoryPayload, slack *SlackMeta) (*Sla Username: slack.Username, IconURL: slack.IconURL, Attachments: []SlackAttachment{{ - Color: slack.Color, - Title: title, - Text: attachmentText, + Color: slack.Color, + Title: title, + TitleLink: title, + Text: attachmentText, }}, }, nil }