diff --git a/services/webhook/general_test.go b/services/webhook/general_test.go index abd2b2bd0e8..f0e17501bbb 100644 --- a/services/webhook/general_test.go +++ b/services/webhook/general_test.go @@ -104,10 +104,12 @@ func pushTestPayloadWithCommitMessage(message string) *api.PushPayload { Pusher: &api.User{ UserName: "user1", AvatarURL: "http://localhost:3000/user1/avatar", + HTMLURL: "http://localhost:3000/user1", }, Sender: &api.User{ UserName: "user1", AvatarURL: "http://localhost:3000/user1/avatar", + HTMLURL: "http://localhost:3000/user1", }, } } @@ -118,6 +120,7 @@ func issueTestPayload() *api.IssuePayload { Sender: &api.User{ UserName: "user1", AvatarURL: "http://localhost:3000/user1/avatar", + HTMLURL: "http://localhost:3000/user1", }, Repository: &api.Repository{ HTMLURL: "http://localhost:3000/test/repo", @@ -156,6 +159,7 @@ func issueCommentTestPayload() *api.IssueCommentPayload { Sender: &api.User{ UserName: "user1", AvatarURL: "http://localhost:3000/user1/avatar", + HTMLURL: "http://localhost:3000/user1", }, Repository: &api.Repository{ HTMLURL: "http://localhost:3000/test/repo", @@ -260,6 +264,7 @@ func pullRequestTestPayload() *api.PullRequestPayload { Sender: &api.User{ UserName: "user1", AvatarURL: "http://localhost:3000/user1/avatar", + HTMLURL: "http://localhost:3000/user1", }, Repository: &api.Repository{ HTMLURL: "http://localhost:3000/test/repo", diff --git a/services/webhook/slack.go b/services/webhook/slack.go index 293336059e7..f76490d4fdb 100644 --- a/services/webhook/slack.go +++ b/services/webhook/slack.go @@ -47,12 +47,26 @@ type SlackPayload struct { } // SlackAttachment contains the slack message +// +// This is Slack's "secondary message attachment" format, documented (as +// legacy, but still functional and with no newer replacement for this exact +// author-row layout) at: +// https://docs.slack.dev/legacy/legacy-messaging/legacy-secondary-message-attachments/ +// Gitea has sent every Slack webhook notification through this same struct +// since Slack support was first added, so this predates and is unrelated to +// the author fields below. type SlackAttachment struct { - Fallback string `json:"fallback"` - Color string `json:"color"` - Title string `json:"title"` - TitleLink string `json:"title_link"` - Text string `json:"text"` + Fallback string `json:"fallback"` + Color string `json:"color"` + // AuthorName, AuthorLink, and AuthorIcon render as a small avatar and a + // linked username in a row above the title (see the package doc above). + // Populated from the event's Sender (or Pusher, for push events). + AuthorName string `json:"author_name,omitempty"` + AuthorLink string `json:"author_link,omitempty"` + AuthorIcon string `json:"author_icon,omitempty"` + Title string `json:"title"` + TitleLink string `json:"title_link"` + Text string `json:"text"` } // SlackTextFormatter replaces &, <, > with HTML characters @@ -124,10 +138,13 @@ func (s slackConvertor) Issue(p *api.IssuePayload) (SlackPayload, error) { extraMarkdown = SlackTextFormatter(extraMarkdown) issueTitle = SlackTextFormatter(issueTitle) attachments = append(attachments, SlackAttachment{ - Color: fmt.Sprintf("%x", color), - Title: issueTitle, - TitleLink: p.Issue.HTMLURL, - Text: extraMarkdown, + Color: fmt.Sprintf("%x", color), + AuthorName: p.Sender.UserName, + AuthorLink: p.Sender.HTMLURL, + AuthorIcon: p.Sender.AvatarURL, + Title: issueTitle, + TitleLink: p.Issue.HTMLURL, + Text: extraMarkdown, }) } @@ -139,10 +156,13 @@ func (s slackConvertor) IssueComment(p *api.IssueCommentPayload) (SlackPayload, text, issueTitle, color := getIssueCommentPayloadInfo(p, SlackLinkFormatter, true) return s.createPayload(text, []SlackAttachment{{ - Color: fmt.Sprintf("%x", color), - Title: issueTitle, - TitleLink: p.Comment.HTMLURL, - Text: SlackTextFormatter(p.Comment.Body), + Color: fmt.Sprintf("%x", color), + AuthorName: p.Sender.UserName, + AuthorLink: p.Sender.HTMLURL, + AuthorIcon: p.Sender.AvatarURL, + Title: issueTitle, + TitleLink: p.Comment.HTMLURL, + Text: SlackTextFormatter(p.Comment.Body), }}), nil } @@ -218,10 +238,13 @@ func (s slackConvertor) Push(p *api.PushPayload) (SlackPayload, error) { } return s.createPayload(text, []SlackAttachment{{ - Color: s.Color, - Title: p.Repo.HTMLURL, - TitleLink: p.Repo.HTMLURL, - Text: attachmentText.String(), + Color: s.Color, + AuthorName: p.Pusher.UserName, + AuthorLink: p.Pusher.HTMLURL, + AuthorIcon: p.Pusher.AvatarURL, + Title: p.Repo.HTMLURL, + TitleLink: p.Repo.HTMLURL, + Text: attachmentText.String(), }}), nil } @@ -234,10 +257,13 @@ func (s slackConvertor) PullRequest(p *api.PullRequestPayload) (SlackPayload, er extraMarkdown = SlackTextFormatter(p.PullRequest.Body) issueTitle = SlackTextFormatter(issueTitle) attachments = append(attachments, SlackAttachment{ - Color: fmt.Sprintf("%x", color), - Title: issueTitle, - TitleLink: p.PullRequest.HTMLURL, - Text: extraMarkdown, + Color: fmt.Sprintf("%x", color), + AuthorName: p.Sender.UserName, + AuthorLink: p.Sender.HTMLURL, + AuthorIcon: p.Sender.AvatarURL, + Title: issueTitle, + TitleLink: p.PullRequest.HTMLURL, + Text: extraMarkdown, }) } diff --git a/services/webhook/slack_test.go b/services/webhook/slack_test.go index 99f8aa7ced8..8d672799d6a 100644 --- a/services/webhook/slack_test.go +++ b/services/webhook/slack_test.go @@ -52,6 +52,10 @@ func TestSlackPayload(t *testing.T) { require.NoError(t, err) assert.Equal(t, "[:] 2 new commits pushed by user1", pl.Text) + require.Len(t, pl.Attachments, 1) + assert.Equal(t, "user1", pl.Attachments[0].AuthorName) + assert.Equal(t, "http://localhost:3000/user1/avatar", pl.Attachments[0].AuthorIcon) + assert.Equal(t, "http://localhost:3000/user1", pl.Attachments[0].AuthorLink) }) t.Run("Issue", func(t *testing.T) { @@ -62,6 +66,10 @@ func TestSlackPayload(t *testing.T) { require.NoError(t, err) assert.Equal(t, "[] Issue opened: by ", pl.Text) + require.Len(t, pl.Attachments, 1) + assert.Equal(t, "user1", pl.Attachments[0].AuthorName) + assert.Equal(t, "http://localhost:3000/user1/avatar", pl.Attachments[0].AuthorIcon) + assert.Equal(t, "http://localhost:3000/user1", pl.Attachments[0].AuthorLink) p.Action = api.HookIssueClosed pl, err = sc.Issue(p) @@ -77,6 +85,10 @@ func TestSlackPayload(t *testing.T) { require.NoError(t, err) assert.Equal(t, "[] New comment on issue by ", pl.Text) + require.Len(t, pl.Attachments, 1) + assert.Equal(t, "user1", pl.Attachments[0].AuthorName) + assert.Equal(t, "http://localhost:3000/user1/avatar", pl.Attachments[0].AuthorIcon) + assert.Equal(t, "http://localhost:3000/user1", pl.Attachments[0].AuthorLink) }) t.Run("PullRequest", func(t *testing.T) { @@ -86,6 +98,10 @@ func TestSlackPayload(t *testing.T) { require.NoError(t, err) assert.Equal(t, "[] Pull request opened: by ", pl.Text) + require.Len(t, pl.Attachments, 1) + assert.Equal(t, "user1", pl.Attachments[0].AuthorName) + assert.Equal(t, "http://localhost:3000/user1/avatar", pl.Attachments[0].AuthorIcon) + assert.Equal(t, "http://localhost:3000/user1", pl.Attachments[0].AuthorLink) }) t.Run("PullRequestComment", func(t *testing.T) {