ソースを参照

Modify the content format of the Feishu webhook (#25106)

close https://github.com/go-gitea/gitea/issues/24368

## what my pull request does

Since the official documentation states that custom bots do not support
hyperlink functionality, simply adding it without making some formatting
changes would result in an unappealing output. Therefore, I have
modified the formatting of the output. Currently, it is only used for
Feishu.

--- 


[docs](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/im-v1/message/create_json#%E8%B6%85%E9%93%BE%E6%8E%A5%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E)

<img width="641" alt="image"
src="https://github.com/go-gitea/gitea/assets/75628309/360e1c81-ab64-4ef6-851e-aa450d6e85a4">

- Issue

<img width="423" alt="image"
src="https://github.com/go-gitea/gitea/assets/75628309/519f6fae-81ab-4ec8-89b8-f6a73ff93783">


- Issue Comment


<img width="548" alt="image"
src="https://github.com/go-gitea/gitea/assets/75628309/749c0d1c-3657-431e-b787-8bf4c23cce83">


- Assign

<img width="431" alt="image"
src="https://github.com/go-gitea/gitea/assets/75628309/066f99e5-eabb-455d-91fb-a8359cc26dc7">

<img width="457" alt="image"
src="https://github.com/go-gitea/gitea/assets/75628309/c6c10f99-db83-46ef-a775-4c91979fa68f">

- Merge

<img width="408" alt="image"
src="https://github.com/go-gitea/gitea/assets/75628309/e627bf43-5954-45aa-acf6-261ee046802f">

- PullRequest

<img width="425" alt="image"
src="https://github.com/go-gitea/gitea/assets/75628309/72cfa714-d3fa-4fb9-abdd-e8508d756056">
tags/v1.21.0-rc0
谈笑风生间 10ヶ月前
コミット
5104c887d3
コミッターのメールアドレスに関連付けられたアカウントが存在しません
4個のファイルの変更111行の追加15行の削除
  1. 26
    9
      services/webhook/feishu.go
  2. 5
    5
      services/webhook/feishu_test.go
  3. 63
    0
      services/webhook/general.go
  4. 17
    1
      services/webhook/general_test.go

+ 26
- 9
services/webhook/feishu.go ファイルの表示

@@ -97,23 +97,40 @@ func (f *FeishuPayload) Push(p *api.PushPayload) (api.Payloader, error) {

// Issue implements PayloadConvertor Issue method
func (f *FeishuPayload) Issue(p *api.IssuePayload) (api.Payloader, error) {
text, issueTitle, attachmentText, _ := getIssuesPayloadInfo(p, noneLinkFormatter, true)

return newFeishuTextPayload(issueTitle + "\r\n" + text + "\r\n\r\n" + attachmentText), nil
title, link, by, operator, result, assignees := getIssuesInfo(p)
var res api.Payloader
if assignees != "" {
if p.Action == api.HookIssueAssigned || p.Action == api.HookIssueUnassigned || p.Action == api.HookIssueMilestoned {
res = newFeishuTextPayload(fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s\n\n%s", title, link, by, operator, result, assignees, p.Issue.Body))
} else {
res = newFeishuTextPayload(fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n\n%s", title, link, by, operator, assignees, p.Issue.Body))
}
} else {
res = newFeishuTextPayload(fmt.Sprintf("%s\n%s\n%s\n%s\n\n%s", title, link, by, operator, p.Issue.Body))
}
return res, nil
}

// IssueComment implements PayloadConvertor IssueComment method
func (f *FeishuPayload) IssueComment(p *api.IssueCommentPayload) (api.Payloader, error) {
text, issueTitle, _ := getIssueCommentPayloadInfo(p, noneLinkFormatter, true)

return newFeishuTextPayload(issueTitle + "\r\n" + text + "\r\n\r\n" + p.Comment.Body), nil
title, link, by, operator := getIssuesCommentInfo(p)
return newFeishuTextPayload(fmt.Sprintf("%s\n%s\n%s\n%s\n\n%s", title, link, by, operator, p.Comment.Body)), nil
}

// PullRequest implements PayloadConvertor PullRequest method
func (f *FeishuPayload) PullRequest(p *api.PullRequestPayload) (api.Payloader, error) {
text, issueTitle, attachmentText, _ := getPullRequestPayloadInfo(p, noneLinkFormatter, true)

return newFeishuTextPayload(issueTitle + "\r\n" + text + "\r\n\r\n" + attachmentText), nil
title, link, by, operator, result, assignees := getPullRequestInfo(p)
var res api.Payloader
if assignees != "" {
if p.Action == api.HookIssueAssigned || p.Action == api.HookIssueUnassigned || p.Action == api.HookIssueMilestoned {
res = newFeishuTextPayload(fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s\n\n%s", title, link, by, operator, result, assignees, p.PullRequest.Body))
} else {
res = newFeishuTextPayload(fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n\n%s", title, link, by, operator, assignees, p.PullRequest.Body))
}
} else {
res = newFeishuTextPayload(fmt.Sprintf("%s\n%s\n%s\n%s\n\n%s", title, link, by, operator, p.PullRequest.Body))
}
return res, nil
}

// Review implements PayloadConvertor Review method

+ 5
- 5
services/webhook/feishu_test.go ファイルの表示

@@ -72,7 +72,7 @@ func TestFeishuPayload(t *testing.T) {
require.NotNil(t, pl)
require.IsType(t, &FeishuPayload{}, pl)

assert.Equal(t, "#2 crash\r\n[test/repo] Issue opened: #2 crash by user1\r\n\r\nissue body", pl.(*FeishuPayload).Content.Text)
assert.Equal(t, "[Issue-test/repo #2]: opened\ncrash\nhttp://localhost:3000/test/repo/issues/2\nIssue by user1\nOperator: user1\nAssignees: user1\n\nissue body", pl.(*FeishuPayload).Content.Text)

p.Action = api.HookIssueClosed
pl, err = d.Issue(p)
@@ -80,7 +80,7 @@ func TestFeishuPayload(t *testing.T) {
require.NotNil(t, pl)
require.IsType(t, &FeishuPayload{}, pl)

assert.Equal(t, "#2 crash\r\n[test/repo] Issue closed: #2 crash by user1", pl.(*FeishuPayload).Content.Text)
assert.Equal(t, "[Issue-test/repo #2]: closed\ncrash\nhttp://localhost:3000/test/repo/issues/2\nIssue by user1\nOperator: user1\nAssignees: user1\n\nissue body", pl.(*FeishuPayload).Content.Text)
})

t.Run("IssueComment", func(t *testing.T) {
@@ -92,7 +92,7 @@ func TestFeishuPayload(t *testing.T) {
require.NotNil(t, pl)
require.IsType(t, &FeishuPayload{}, pl)

assert.Equal(t, "#2 crash\r\n[test/repo] New comment on issue #2 crash by user1\r\n\r\nmore info needed", pl.(*FeishuPayload).Content.Text)
assert.Equal(t, "[Comment-test/repo #2]: created\ncrash\nhttp://localhost:3000/test/repo/issues/2\nIssue by user1\nOperator: user1\n\nmore info needed", pl.(*FeishuPayload).Content.Text)
})

t.Run("PullRequest", func(t *testing.T) {
@@ -104,7 +104,7 @@ func TestFeishuPayload(t *testing.T) {
require.NotNil(t, pl)
require.IsType(t, &FeishuPayload{}, pl)

assert.Equal(t, "#12 Fix bug\r\n[test/repo] Pull request opened: #12 Fix bug by user1\r\n\r\nfixes bug #2", pl.(*FeishuPayload).Content.Text)
assert.Equal(t, "[PullRequest-test/repo #12]: opened\nFix bug\nhttp://localhost:3000/test/repo/pulls/12\nPullRequest by user1\nOperator: user1\nAssignees: user1\n\nfixes bug #2", pl.(*FeishuPayload).Content.Text)
})

t.Run("PullRequestComment", func(t *testing.T) {
@@ -116,7 +116,7 @@ func TestFeishuPayload(t *testing.T) {
require.NotNil(t, pl)
require.IsType(t, &FeishuPayload{}, pl)

assert.Equal(t, "#12 Fix bug\r\n[test/repo] New comment on pull request #12 Fix bug by user1\r\n\r\nchanges requested", pl.(*FeishuPayload).Content.Text)
assert.Equal(t, "[Comment-test/repo #12]: created\nFix bug\nhttp://localhost:3000/test/repo/pulls/12\nPullRequest by user1\nOperator: user1\n\nchanges requested", pl.(*FeishuPayload).Content.Text)
})

t.Run("Review", func(t *testing.T) {

+ 63
- 0
services/webhook/general.go ファイルの表示

@@ -28,6 +28,69 @@ func htmlLinkFormatter(url, text string) string {
return fmt.Sprintf(`<a href="%s">%s</a>`, html.EscapeString(url), html.EscapeString(text))
}

// getPullRequestInfo gets the information for a pull request
func getPullRequestInfo(p *api.PullRequestPayload) (title, link, by, operator, operateResult, assignees string) {
title = fmt.Sprintf("[PullRequest-%s #%d]: %s\n%s", p.Repository.FullName, p.PullRequest.Index, p.Action, p.PullRequest.Title)
assignList := p.PullRequest.Assignees
assignStringList := make([]string, len(assignList))

for i, user := range assignList {
assignStringList[i] = user.UserName
}
if p.Action == api.HookIssueAssigned {
operateResult = fmt.Sprintf("%s assign this to %s", p.Sender.UserName, assignList[len(assignList)-1].UserName)
} else if p.Action == api.HookIssueUnassigned {
operateResult = fmt.Sprintf("%s unassigned this for someone", p.Sender.UserName)
} else if p.Action == api.HookIssueMilestoned {
operateResult = fmt.Sprintf("%s/milestone/%d", p.Repository.HTMLURL, p.PullRequest.Milestone.ID)
}
link = p.PullRequest.HTMLURL
by = fmt.Sprintf("PullRequest by %s", p.PullRequest.Poster.UserName)
if len(assignStringList) > 0 {
assignees = fmt.Sprintf("Assignees: %s", strings.Join(assignStringList, ", "))
}
operator = fmt.Sprintf("Operator: %s", p.Sender.UserName)
return title, link, by, operator, operateResult, assignees
}

// getIssuesInfo gets the information for an issue
func getIssuesInfo(p *api.IssuePayload) (issueTitle, link, by, operator, operateResult, assignees string) {
issueTitle = fmt.Sprintf("[Issue-%s #%d]: %s\n%s", p.Repository.FullName, p.Issue.Index, p.Action, p.Issue.Title)
assignList := p.Issue.Assignees
assignStringList := make([]string, len(assignList))

for i, user := range assignList {
assignStringList[i] = user.UserName
}
if p.Action == api.HookIssueAssigned {
operateResult = fmt.Sprintf("%s assign this to %s", p.Sender.UserName, assignList[len(assignList)-1].UserName)
} else if p.Action == api.HookIssueUnassigned {
operateResult = fmt.Sprintf("%s unassigned this for someone", p.Sender.UserName)
} else if p.Action == api.HookIssueMilestoned {
operateResult = fmt.Sprintf("%s/milestone/%d", p.Repository.HTMLURL, p.Issue.Milestone.ID)
}
link = p.Issue.HTMLURL
by = fmt.Sprintf("Issue by %s", p.Issue.Poster.UserName)
if len(assignStringList) > 0 {
assignees = fmt.Sprintf("Assignees: %s", strings.Join(assignStringList, ", "))
}
operator = fmt.Sprintf("Operator: %s", p.Sender.UserName)
return issueTitle, link, by, operator, operateResult, assignees
}

// getIssuesCommentInfo gets the information for a comment
func getIssuesCommentInfo(p *api.IssueCommentPayload) (title, link, by, operator string) {
title = fmt.Sprintf("[Comment-%s #%d]: %s\n%s", p.Repository.FullName, p.Issue.Index, p.Action, p.Issue.Title)
link = p.Issue.HTMLURL
if p.IsPull {
by = fmt.Sprintf("PullRequest by %s", p.Issue.Poster.UserName)
} else {
by = fmt.Sprintf("Issue by %s", p.Issue.Poster.UserName)
}
operator = fmt.Sprintf("Operator: %s", p.Sender.UserName)
return title, link, by, operator
}

func getIssuesPayloadInfo(p *api.IssuePayload, linkFormatter linkFormatter, withSender bool) (string, string, string, int) {
repoLink := linkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
issueTitle := fmt.Sprintf("#%d %s", p.Index, p.Issue.Title)

+ 17
- 1
services/webhook/general_test.go ファイルの表示

@@ -123,6 +123,10 @@ func issueTestPayload() *api.IssuePayload {
HTMLURL: "http://localhost:3000/test/repo/issues/2",
Title: "crash",
Body: "issue body",
Poster: &api.User{
UserName: "user1",
AvatarURL: "http://localhost:3000/user1/avatar",
},
Assignees: []*api.User{
{
UserName: "user1",
@@ -161,7 +165,11 @@ func issueCommentTestPayload() *api.IssueCommentPayload {
URL: "http://localhost:3000/api/v1/repos/test/repo/issues/2",
HTMLURL: "http://localhost:3000/test/repo/issues/2",
Title: "crash",
Body: "this happened",
Poster: &api.User{
UserName: "user1",
AvatarURL: "http://localhost:3000/user1/avatar",
},
Body: "this happened",
},
}
}
@@ -190,6 +198,10 @@ func pullRequestCommentTestPayload() *api.IssueCommentPayload {
HTMLURL: "http://localhost:3000/test/repo/pulls/12",
Title: "Fix bug",
Body: "fixes bug #2",
Poster: &api.User{
UserName: "user1",
AvatarURL: "http://localhost:3000/user1/avatar",
},
},
IsPull: true,
}
@@ -254,6 +266,10 @@ func pullRequestTestPayload() *api.PullRequestPayload {
Title: "Fix bug",
Body: "fixes bug #2",
Mergeable: true,
Poster: &api.User{
UserName: "user1",
AvatarURL: "http://localhost:3000/user1/avatar",
},
Assignees: []*api.User{
{
UserName: "user1",

読み込み中…
キャンセル
保存