code.gitea.io/gitea@v1.22.3/services/webhook/dingtalk.go (about)

     1  // Copyright 2017 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package webhook
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"net/http"
    10  	"net/url"
    11  	"strings"
    12  
    13  	webhook_model "code.gitea.io/gitea/models/webhook"
    14  	"code.gitea.io/gitea/modules/git"
    15  	api "code.gitea.io/gitea/modules/structs"
    16  	"code.gitea.io/gitea/modules/util"
    17  	webhook_module "code.gitea.io/gitea/modules/webhook"
    18  
    19  	dingtalk "gitea.com/lunny/dingtalk_webhook"
    20  )
    21  
    22  type (
    23  	// DingtalkPayload represents
    24  	DingtalkPayload dingtalk.Payload
    25  )
    26  
    27  // Create implements PayloadConvertor Create method
    28  func (dc dingtalkConvertor) Create(p *api.CreatePayload) (DingtalkPayload, error) {
    29  	// created tag/branch
    30  	refName := git.RefName(p.Ref).ShortName()
    31  	title := fmt.Sprintf("[%s] %s %s created", p.Repo.FullName, p.RefType, refName)
    32  
    33  	return createDingtalkPayload(title, title, fmt.Sprintf("view ref %s", refName), p.Repo.HTMLURL+"/src/"+util.PathEscapeSegments(refName)), nil
    34  }
    35  
    36  // Delete implements PayloadConvertor Delete method
    37  func (dc dingtalkConvertor) Delete(p *api.DeletePayload) (DingtalkPayload, error) {
    38  	// created tag/branch
    39  	refName := git.RefName(p.Ref).ShortName()
    40  	title := fmt.Sprintf("[%s] %s %s deleted", p.Repo.FullName, p.RefType, refName)
    41  
    42  	return createDingtalkPayload(title, title, fmt.Sprintf("view ref %s", refName), p.Repo.HTMLURL+"/src/"+util.PathEscapeSegments(refName)), nil
    43  }
    44  
    45  // Fork implements PayloadConvertor Fork method
    46  func (dc dingtalkConvertor) Fork(p *api.ForkPayload) (DingtalkPayload, error) {
    47  	title := fmt.Sprintf("%s is forked to %s", p.Forkee.FullName, p.Repo.FullName)
    48  
    49  	return createDingtalkPayload(title, title, fmt.Sprintf("view forked repo %s", p.Repo.FullName), p.Repo.HTMLURL), nil
    50  }
    51  
    52  // Push implements PayloadConvertor Push method
    53  func (dc dingtalkConvertor) Push(p *api.PushPayload) (DingtalkPayload, error) {
    54  	var (
    55  		branchName = git.RefName(p.Ref).ShortName()
    56  		commitDesc string
    57  	)
    58  
    59  	var titleLink, linkText string
    60  	if p.TotalCommits == 1 {
    61  		commitDesc = "1 new commit"
    62  		titleLink = p.Commits[0].URL
    63  		linkText = "view commit"
    64  	} else {
    65  		commitDesc = fmt.Sprintf("%d new commits", p.TotalCommits)
    66  		titleLink = p.CompareURL
    67  		linkText = "view commits"
    68  	}
    69  	if titleLink == "" {
    70  		titleLink = p.Repo.HTMLURL + "/src/" + util.PathEscapeSegments(branchName)
    71  	}
    72  
    73  	title := fmt.Sprintf("[%s:%s] %s", p.Repo.FullName, branchName, commitDesc)
    74  
    75  	var text string
    76  	// for each commit, generate attachment text
    77  	for i, commit := range p.Commits {
    78  		var authorName string
    79  		if commit.Author != nil {
    80  			authorName = " - " + commit.Author.Name
    81  		}
    82  		text += fmt.Sprintf("[%s](%s) %s", commit.ID[:7], commit.URL,
    83  			strings.TrimRight(commit.Message, "\r\n")) + authorName
    84  		// add linebreak to each commit but the last
    85  		if i < len(p.Commits)-1 {
    86  			text += "\r\n"
    87  		}
    88  	}
    89  
    90  	return createDingtalkPayload(title, text, linkText, titleLink), nil
    91  }
    92  
    93  // Issue implements PayloadConvertor Issue method
    94  func (dc dingtalkConvertor) Issue(p *api.IssuePayload) (DingtalkPayload, error) {
    95  	text, issueTitle, attachmentText, _ := getIssuesPayloadInfo(p, noneLinkFormatter, true)
    96  
    97  	return createDingtalkPayload(issueTitle, text+"\r\n\r\n"+attachmentText, "view issue", p.Issue.HTMLURL), nil
    98  }
    99  
   100  // Wiki implements PayloadConvertor Wiki method
   101  func (dc dingtalkConvertor) Wiki(p *api.WikiPayload) (DingtalkPayload, error) {
   102  	text, _, _ := getWikiPayloadInfo(p, noneLinkFormatter, true)
   103  	url := p.Repository.HTMLURL + "/wiki/" + url.PathEscape(p.Page)
   104  
   105  	return createDingtalkPayload(text, text, "view wiki", url), nil
   106  }
   107  
   108  // IssueComment implements PayloadConvertor IssueComment method
   109  func (dc dingtalkConvertor) IssueComment(p *api.IssueCommentPayload) (DingtalkPayload, error) {
   110  	text, issueTitle, _ := getIssueCommentPayloadInfo(p, noneLinkFormatter, true)
   111  
   112  	return createDingtalkPayload(issueTitle, text+"\r\n\r\n"+p.Comment.Body, "view issue comment", p.Comment.HTMLURL), nil
   113  }
   114  
   115  // PullRequest implements PayloadConvertor PullRequest method
   116  func (dc dingtalkConvertor) PullRequest(p *api.PullRequestPayload) (DingtalkPayload, error) {
   117  	text, issueTitle, attachmentText, _ := getPullRequestPayloadInfo(p, noneLinkFormatter, true)
   118  
   119  	return createDingtalkPayload(issueTitle, text+"\r\n\r\n"+attachmentText, "view pull request", p.PullRequest.HTMLURL), nil
   120  }
   121  
   122  // Review implements PayloadConvertor Review method
   123  func (dc dingtalkConvertor) Review(p *api.PullRequestPayload, event webhook_module.HookEventType) (DingtalkPayload, error) {
   124  	var text, title string
   125  	switch p.Action {
   126  	case api.HookIssueReviewed:
   127  		action, err := parseHookPullRequestEventType(event)
   128  		if err != nil {
   129  			return DingtalkPayload{}, err
   130  		}
   131  
   132  		title = fmt.Sprintf("[%s] Pull request review %s : #%d %s", p.Repository.FullName, action, p.Index, p.PullRequest.Title)
   133  		text = p.Review.Content
   134  	}
   135  
   136  	return createDingtalkPayload(title, title+"\r\n\r\n"+text, "view pull request", p.PullRequest.HTMLURL), nil
   137  }
   138  
   139  // Repository implements PayloadConvertor Repository method
   140  func (dc dingtalkConvertor) Repository(p *api.RepositoryPayload) (DingtalkPayload, error) {
   141  	switch p.Action {
   142  	case api.HookRepoCreated:
   143  		title := fmt.Sprintf("[%s] Repository created", p.Repository.FullName)
   144  		return createDingtalkPayload(title, title, "view repository", p.Repository.HTMLURL), nil
   145  	case api.HookRepoDeleted:
   146  		title := fmt.Sprintf("[%s] Repository deleted", p.Repository.FullName)
   147  		return DingtalkPayload{
   148  			MsgType: "text",
   149  			Text: struct {
   150  				Content string `json:"content"`
   151  			}{
   152  				Content: title,
   153  			},
   154  		}, nil
   155  	}
   156  
   157  	return DingtalkPayload{}, nil
   158  }
   159  
   160  // Release implements PayloadConvertor Release method
   161  func (dc dingtalkConvertor) Release(p *api.ReleasePayload) (DingtalkPayload, error) {
   162  	text, _ := getReleasePayloadInfo(p, noneLinkFormatter, true)
   163  
   164  	return createDingtalkPayload(text, text, "view release", p.Release.HTMLURL), nil
   165  }
   166  
   167  func (dc dingtalkConvertor) Package(p *api.PackagePayload) (DingtalkPayload, error) {
   168  	text, _ := getPackagePayloadInfo(p, noneLinkFormatter, true)
   169  
   170  	return createDingtalkPayload(text, text, "view package", p.Package.HTMLURL), nil
   171  }
   172  
   173  func createDingtalkPayload(title, text, singleTitle, singleURL string) DingtalkPayload {
   174  	return DingtalkPayload{
   175  		MsgType: "actionCard",
   176  		ActionCard: dingtalk.ActionCard{
   177  			Text:        strings.TrimSpace(text),
   178  			Title:       strings.TrimSpace(title),
   179  			HideAvatar:  "0",
   180  			SingleTitle: singleTitle,
   181  
   182  			// https://developers.dingtalk.com/document/app/message-link-description
   183  			// 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.
   184  			SingleURL: "dingtalk://dingtalkclient/page/link?pc_slide=false&url=" + url.QueryEscape(singleURL),
   185  		},
   186  	}
   187  }
   188  
   189  type dingtalkConvertor struct{}
   190  
   191  var _ payloadConvertor[DingtalkPayload] = dingtalkConvertor{}
   192  
   193  func newDingtalkRequest(ctx context.Context, w *webhook_model.Webhook, t *webhook_model.HookTask) (*http.Request, []byte, error) {
   194  	return newJSONRequest(dingtalkConvertor{}, w, t, true)
   195  }