2017-11-21 09:56:43 +05:30
// Copyright 2017 The Gitea Authors. All rights reserved.
2022-11-27 23:50:29 +05:30
// SPDX-License-Identifier: MIT
2017-11-21 09:56:43 +05:30
2019-11-04 03:43:25 +05:30
package webhook
2017-11-21 09:56:43 +05:30
import (
2024-03-08 03:48:38 +05:30
"context"
2017-11-21 09:56:43 +05:30
"fmt"
2024-03-08 03:48:38 +05:30
"net/http"
2021-09-19 01:05:23 +05:30
"net/url"
2017-11-21 09:56:43 +05:30
"strings"
2024-03-08 03:48:38 +05:30
webhook_model "code.gitea.io/gitea/models/webhook"
2019-03-27 15:03:00 +05:30
"code.gitea.io/gitea/modules/git"
2019-05-11 15:51:34 +05:30
api "code.gitea.io/gitea/modules/structs"
2021-11-16 23:48:25 +05:30
"code.gitea.io/gitea/modules/util"
2023-01-01 20:53:15 +05:30
webhook_module "code.gitea.io/gitea/modules/webhook"
2024-03-21 18:27:14 +05:30
"code.gitea.io/gitea/services/forms"
2017-11-21 09:56:43 +05:30
)
2024-03-20 20:14:01 +05:30
type dingtalkHandler struct { }
func ( dingtalkHandler ) Type ( ) webhook_module . HookType { return webhook_module . DINGTALK }
func ( dingtalkHandler ) Metadata ( * webhook_model . Webhook ) any { return nil }
2024-03-21 17:53:27 +05:30
func ( dingtalkHandler ) FormFields ( bind func ( any ) ) FormFields {
2024-03-21 18:27:14 +05:30
var form struct {
forms . WebhookForm
PayloadURL string ` binding:"Required;ValidUrl" `
}
bind ( & form )
return FormFields {
WebhookForm : form . WebhookForm ,
URL : form . PayloadURL ,
ContentType : webhook_model . ContentTypeJSON ,
Secret : "" ,
HTTPMethod : http . MethodPost ,
Metadata : nil ,
}
2024-03-21 17:53:27 +05:30
}
2024-03-20 20:14:01 +05:30
2017-11-21 09:56:43 +05:30
type (
2024-03-30 04:18:47 +05:30
// DingtalkPayload represents an dingtalk payload.
DingtalkPayload struct {
MsgType string ` json:"msgtype" `
Text struct {
Content string ` json:"content" `
} ` json:"text" `
ActionCard DingtalkActionCard ` json:"actionCard" `
}
DingtalkActionCard struct {
Text string ` json:"text" `
Title string ` json:"title" `
HideAvatar string ` json:"hideAvatar" `
SingleTitle string ` json:"singleTitle" `
SingleURL string ` json:"singleURL" `
}
2017-11-21 09:56:43 +05:30
)
2020-09-05 08:27:13 +05:30
// Create implements PayloadConvertor Create method
2024-03-08 03:48:38 +05:30
func ( dc dingtalkConvertor ) Create ( p * api . CreatePayload ) ( DingtalkPayload , error ) {
2017-11-21 09:56:43 +05:30
// created tag/branch
2023-05-26 06:34:48 +05:30
refName := git . RefName ( p . Ref ) . ShortName ( )
2017-11-21 09:56:43 +05:30
title := fmt . Sprintf ( "[%s] %s %s created" , p . Repo . FullName , p . RefType , refName )
2021-11-16 23:48:25 +05:30
return createDingtalkPayload ( title , title , fmt . Sprintf ( "view ref %s" , refName ) , p . Repo . HTMLURL + "/src/" + util . PathEscapeSegments ( refName ) ) , nil
2017-11-21 09:56:43 +05:30
}
2020-09-05 08:27:13 +05:30
// Delete implements PayloadConvertor Delete method
2024-03-08 03:48:38 +05:30
func ( dc dingtalkConvertor ) Delete ( p * api . DeletePayload ) ( DingtalkPayload , error ) {
2018-05-16 19:31:55 +05:30
// created tag/branch
2023-05-26 06:34:48 +05:30
refName := git . RefName ( p . Ref ) . ShortName ( )
2018-05-16 19:31:55 +05:30
title := fmt . Sprintf ( "[%s] %s %s deleted" , p . Repo . FullName , p . RefType , refName )
2021-11-16 23:48:25 +05:30
return createDingtalkPayload ( title , title , fmt . Sprintf ( "view ref %s" , refName ) , p . Repo . HTMLURL + "/src/" + util . PathEscapeSegments ( refName ) ) , nil
2018-05-16 19:31:55 +05:30
}
2020-09-05 08:27:13 +05:30
// Fork implements PayloadConvertor Fork method
2024-03-08 03:48:38 +05:30
func ( dc dingtalkConvertor ) Fork ( p * api . ForkPayload ) ( DingtalkPayload , error ) {
2018-05-16 19:31:55 +05:30
title := fmt . Sprintf ( "%s is forked to %s" , p . Forkee . FullName , p . Repo . FullName )
2021-06-21 07:42:19 +05:30
return createDingtalkPayload ( title , title , fmt . Sprintf ( "view forked repo %s" , p . Repo . FullName ) , p . Repo . HTMLURL ) , nil
2018-05-16 19:31:55 +05:30
}
2020-09-05 08:27:13 +05:30
// Push implements PayloadConvertor Push method
2024-03-08 03:48:38 +05:30
func ( dc dingtalkConvertor ) Push ( p * api . PushPayload ) ( DingtalkPayload , error ) {
2017-11-21 09:56:43 +05:30
var (
2023-05-26 06:34:48 +05:30
branchName = git . RefName ( p . Ref ) . ShortName ( )
2017-11-21 09:56:43 +05:30
commitDesc string
)
var titleLink , linkText string
2022-10-16 21:52:34 +05:30
if p . TotalCommits == 1 {
2017-11-21 09:56:43 +05:30
commitDesc = "1 new commit"
titleLink = p . Commits [ 0 ] . URL
2022-10-16 21:52:34 +05:30
linkText = "view commit"
2017-11-21 09:56:43 +05:30
} else {
2022-10-16 21:52:34 +05:30
commitDesc = fmt . Sprintf ( "%d new commits" , p . TotalCommits )
2017-11-21 09:56:43 +05:30
titleLink = p . CompareURL
2022-10-16 21:52:34 +05:30
linkText = "view commits"
2017-11-21 09:56:43 +05:30
}
if titleLink == "" {
2021-11-16 23:48:25 +05:30
titleLink = p . Repo . HTMLURL + "/src/" + util . PathEscapeSegments ( branchName )
2017-11-21 09:56:43 +05:30
}
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 {
var authorName string
if commit . Author != nil {
authorName = " - " + commit . Author . Name
}
text += fmt . Sprintf ( "[%s](%s) %s" , commit . ID [ : 7 ] , commit . URL ,
strings . TrimRight ( commit . Message , "\r\n" ) ) + authorName
// add linebreak to each commit but the last
if i < len ( p . Commits ) - 1 {
2021-06-21 07:42:19 +05:30
text += "\r\n"
2017-11-21 09:56:43 +05:30
}
}
2021-06-21 07:42:19 +05:30
return createDingtalkPayload ( title , text , linkText , titleLink ) , nil
2017-11-21 09:56:43 +05:30
}
2020-09-05 08:27:13 +05:30
// Issue implements PayloadConvertor Issue method
2024-03-08 03:48:38 +05:30
func ( dc dingtalkConvertor ) Issue ( p * api . IssuePayload ) ( DingtalkPayload , error ) {
2020-01-05 03:50:15 +05:30
text , issueTitle , attachmentText , _ := getIssuesPayloadInfo ( p , noneLinkFormatter , true )
2018-05-16 19:31:55 +05:30
2021-06-21 07:42:19 +05:30
return createDingtalkPayload ( issueTitle , text + "\r\n\r\n" + attachmentText , "view issue" , p . Issue . HTMLURL ) , nil
2018-05-16 19:31:55 +05:30
}
2022-09-05 01:24:23 +05:30
// Wiki implements PayloadConvertor Wiki method
2024-03-08 03:48:38 +05:30
func ( dc dingtalkConvertor ) Wiki ( p * api . WikiPayload ) ( DingtalkPayload , error ) {
2022-09-05 01:24:23 +05:30
text , _ , _ := getWikiPayloadInfo ( p , noneLinkFormatter , true )
url := p . Repository . HTMLURL + "/wiki/" + url . PathEscape ( p . Page )
return createDingtalkPayload ( text , text , "view wiki" , url ) , nil
}
2020-09-05 08:27:13 +05:30
// IssueComment implements PayloadConvertor IssueComment method
2024-03-08 03:48:38 +05:30
func ( dc dingtalkConvertor ) IssueComment ( p * api . IssueCommentPayload ) ( DingtalkPayload , error ) {
2020-01-05 03:50:15 +05:30
text , issueTitle , _ := getIssueCommentPayloadInfo ( p , noneLinkFormatter , true )
2019-10-19 04:12:04 +05:30
2021-06-21 07:42:19 +05:30
return createDingtalkPayload ( issueTitle , text + "\r\n\r\n" + p . Comment . Body , "view issue comment" , p . Comment . HTMLURL ) , nil
2018-05-16 19:31:55 +05:30
}
2020-09-05 08:27:13 +05:30
// PullRequest implements PayloadConvertor PullRequest method
2024-03-08 03:48:38 +05:30
func ( dc dingtalkConvertor ) PullRequest ( p * api . PullRequestPayload ) ( DingtalkPayload , error ) {
2020-01-05 03:50:15 +05:30
text , issueTitle , attachmentText , _ := getPullRequestPayloadInfo ( p , noneLinkFormatter , true )
2017-11-21 09:56:43 +05:30
2021-06-21 07:42:19 +05:30
return createDingtalkPayload ( issueTitle , text + "\r\n\r\n" + attachmentText , "view pull request" , p . PullRequest . HTMLURL ) , nil
2017-11-21 09:56:43 +05:30
}
2020-09-05 08:27:13 +05:30
// Review implements PayloadConvertor Review method
2024-03-08 03:48:38 +05:30
func ( dc dingtalkConvertor ) Review ( p * api . PullRequestPayload , event webhook_module . HookEventType ) ( DingtalkPayload , error ) {
2018-12-27 23:34:30 +05:30
var text , title string
switch p . Action {
2020-03-06 10:40:48 +05:30
case api . HookIssueReviewed :
2018-12-27 23:34:30 +05:30
action , err := parseHookPullRequestEventType ( event )
if err != nil {
2024-03-08 03:48:38 +05:30
return DingtalkPayload { } , err
2018-12-27 23:34:30 +05:30
}
title = fmt . Sprintf ( "[%s] Pull request review %s : #%d %s" , p . Repository . FullName , action , p . Index , p . PullRequest . Title )
2019-10-19 04:12:04 +05:30
text = p . Review . Content
2018-12-27 23:34:30 +05:30
}
2021-06-21 07:42:19 +05:30
return createDingtalkPayload ( title , title + "\r\n\r\n" + text , "view pull request" , p . PullRequest . HTMLURL ) , nil
2018-12-27 23:34:30 +05:30
}
2020-09-05 08:27:13 +05:30
// Repository implements PayloadConvertor Repository method
2024-03-08 03:48:38 +05:30
func ( dc dingtalkConvertor ) Repository ( p * api . RepositoryPayload ) ( DingtalkPayload , error ) {
2017-11-21 09:56:43 +05:30
switch p . Action {
case api . HookRepoCreated :
2021-06-21 07:42:19 +05:30
title := fmt . Sprintf ( "[%s] Repository created" , p . Repository . FullName )
return createDingtalkPayload ( title , title , "view repository" , p . Repository . HTMLURL ) , nil
2017-11-21 09:56:43 +05:30
case api . HookRepoDeleted :
2021-06-21 07:42:19 +05:30
title := fmt . Sprintf ( "[%s] Repository deleted" , p . Repository . FullName )
2024-03-08 03:48:38 +05:30
return DingtalkPayload {
2017-11-21 09:56:43 +05:30
MsgType : "text" ,
Text : struct {
Content string ` json:"content" `
} {
Content : title ,
} ,
} , nil
}
2024-03-08 03:48:38 +05:30
return DingtalkPayload { } , nil
2017-11-21 09:56:43 +05:30
}
2020-09-05 08:27:13 +05:30
// Release implements PayloadConvertor Release method
2024-03-08 03:48:38 +05:30
func ( dc dingtalkConvertor ) Release ( p * api . ReleasePayload ) ( DingtalkPayload , error ) {
2020-01-05 03:50:15 +05:30
text , _ := getReleasePayloadInfo ( p , noneLinkFormatter , true )
2018-05-21 07:58:29 +05:30
2023-09-22 04:25:09 +05:30
return createDingtalkPayload ( text , text , "view release" , p . Release . HTMLURL ) , nil
2021-06-21 07:42:19 +05:30
}
2024-03-08 03:48:38 +05:30
func ( dc dingtalkConvertor ) Package ( p * api . PackagePayload ) ( DingtalkPayload , error ) {
2023-10-31 10:13:38 +05:30
text , _ := getPackagePayloadInfo ( p , noneLinkFormatter , true )
return createDingtalkPayload ( text , text , "view package" , p . Package . HTMLURL ) , nil
}
2024-03-08 03:48:38 +05:30
func createDingtalkPayload ( title , text , singleTitle , singleURL string ) DingtalkPayload {
return DingtalkPayload {
2019-12-28 14:25:09 +05:30
MsgType : "actionCard" ,
2024-03-30 04:18:47 +05:30
ActionCard : DingtalkActionCard {
2021-06-21 07:42:19 +05:30
Text : strings . TrimSpace ( text ) ,
Title : strings . TrimSpace ( title ) ,
2019-12-28 14:25:09 +05:30
HideAvatar : "0" ,
2021-06-21 07:42:19 +05:30
SingleTitle : singleTitle ,
2021-09-19 01:05:23 +05:30
// https://developers.dingtalk.com/document/app/message-link-description
// to open the link in browser, we should use this URL, otherwise the page is displayed inside DingTalk client, very difficult to visit non-public URLs.
SingleURL : "dingtalk://dingtalkclient/page/link?pc_slide=false&url=" + url . QueryEscape ( singleURL ) ,
2019-12-28 14:25:09 +05:30
} ,
2021-06-21 07:42:19 +05:30
}
2018-05-16 19:31:55 +05:30
}
2024-03-08 03:48:38 +05:30
type dingtalkConvertor struct { }
var _ payloadConvertor [ DingtalkPayload ] = dingtalkConvertor { }
2024-03-20 20:14:01 +05:30
func ( dingtalkHandler ) NewRequest ( ctx context . Context , w * webhook_model . Webhook , t * webhook_model . HookTask ) ( * http . Request , [ ] byte , error ) {
2024-03-08 03:48:38 +05:30
return newJSONRequest ( dingtalkConvertor { } , w , t , true )
2017-11-21 09:56:43 +05:30
}