code.gitea.io/gitea@v1.21.7/services/webhook/general.go (about)

     1  // Copyright 2019 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package webhook
     5  
     6  import (
     7  	"fmt"
     8  	"html"
     9  	"net/url"
    10  	"strings"
    11  
    12  	webhook_model "code.gitea.io/gitea/models/webhook"
    13  	"code.gitea.io/gitea/modules/setting"
    14  	api "code.gitea.io/gitea/modules/structs"
    15  	"code.gitea.io/gitea/modules/util"
    16  	webhook_module "code.gitea.io/gitea/modules/webhook"
    17  )
    18  
    19  type linkFormatter = func(string, string) string
    20  
    21  // noneLinkFormatter does not create a link but just returns the text
    22  func noneLinkFormatter(url, text string) string {
    23  	return text
    24  }
    25  
    26  // htmlLinkFormatter creates a HTML link
    27  func htmlLinkFormatter(url, text string) string {
    28  	return fmt.Sprintf(`<a href="%s">%s</a>`, html.EscapeString(url), html.EscapeString(text))
    29  }
    30  
    31  // getPullRequestInfo gets the information for a pull request
    32  func getPullRequestInfo(p *api.PullRequestPayload) (title, link, by, operator, operateResult, assignees string) {
    33  	title = fmt.Sprintf("[PullRequest-%s #%d]: %s\n%s", p.Repository.FullName, p.PullRequest.Index, p.Action, p.PullRequest.Title)
    34  	assignList := p.PullRequest.Assignees
    35  	assignStringList := make([]string, len(assignList))
    36  
    37  	for i, user := range assignList {
    38  		assignStringList[i] = user.UserName
    39  	}
    40  	if p.Action == api.HookIssueAssigned {
    41  		operateResult = fmt.Sprintf("%s assign this to %s", p.Sender.UserName, assignList[len(assignList)-1].UserName)
    42  	} else if p.Action == api.HookIssueUnassigned {
    43  		operateResult = fmt.Sprintf("%s unassigned this for someone", p.Sender.UserName)
    44  	} else if p.Action == api.HookIssueMilestoned {
    45  		operateResult = fmt.Sprintf("%s/milestone/%d", p.Repository.HTMLURL, p.PullRequest.Milestone.ID)
    46  	}
    47  	link = p.PullRequest.HTMLURL
    48  	by = fmt.Sprintf("PullRequest by %s", p.PullRequest.Poster.UserName)
    49  	if len(assignStringList) > 0 {
    50  		assignees = fmt.Sprintf("Assignees: %s", strings.Join(assignStringList, ", "))
    51  	}
    52  	operator = fmt.Sprintf("Operator: %s", p.Sender.UserName)
    53  	return title, link, by, operator, operateResult, assignees
    54  }
    55  
    56  // getIssuesInfo gets the information for an issue
    57  func getIssuesInfo(p *api.IssuePayload) (issueTitle, link, by, operator, operateResult, assignees string) {
    58  	issueTitle = fmt.Sprintf("[Issue-%s #%d]: %s\n%s", p.Repository.FullName, p.Issue.Index, p.Action, p.Issue.Title)
    59  	assignList := p.Issue.Assignees
    60  	assignStringList := make([]string, len(assignList))
    61  
    62  	for i, user := range assignList {
    63  		assignStringList[i] = user.UserName
    64  	}
    65  	if p.Action == api.HookIssueAssigned {
    66  		operateResult = fmt.Sprintf("%s assign this to %s", p.Sender.UserName, assignList[len(assignList)-1].UserName)
    67  	} else if p.Action == api.HookIssueUnassigned {
    68  		operateResult = fmt.Sprintf("%s unassigned this for someone", p.Sender.UserName)
    69  	} else if p.Action == api.HookIssueMilestoned {
    70  		operateResult = fmt.Sprintf("%s/milestone/%d", p.Repository.HTMLURL, p.Issue.Milestone.ID)
    71  	}
    72  	link = p.Issue.HTMLURL
    73  	by = fmt.Sprintf("Issue by %s", p.Issue.Poster.UserName)
    74  	if len(assignStringList) > 0 {
    75  		assignees = fmt.Sprintf("Assignees: %s", strings.Join(assignStringList, ", "))
    76  	}
    77  	operator = fmt.Sprintf("Operator: %s", p.Sender.UserName)
    78  	return issueTitle, link, by, operator, operateResult, assignees
    79  }
    80  
    81  // getIssuesCommentInfo gets the information for a comment
    82  func getIssuesCommentInfo(p *api.IssueCommentPayload) (title, link, by, operator string) {
    83  	title = fmt.Sprintf("[Comment-%s #%d]: %s\n%s", p.Repository.FullName, p.Issue.Index, p.Action, p.Issue.Title)
    84  	link = p.Issue.HTMLURL
    85  	if p.IsPull {
    86  		by = fmt.Sprintf("PullRequest by %s", p.Issue.Poster.UserName)
    87  	} else {
    88  		by = fmt.Sprintf("Issue by %s", p.Issue.Poster.UserName)
    89  	}
    90  	operator = fmt.Sprintf("Operator: %s", p.Sender.UserName)
    91  	return title, link, by, operator
    92  }
    93  
    94  func getIssuesPayloadInfo(p *api.IssuePayload, linkFormatter linkFormatter, withSender bool) (string, string, string, int) {
    95  	repoLink := linkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
    96  	issueTitle := fmt.Sprintf("#%d %s", p.Index, p.Issue.Title)
    97  	titleLink := linkFormatter(fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Index), issueTitle)
    98  	var text string
    99  	color := yellowColor
   100  
   101  	switch p.Action {
   102  	case api.HookIssueOpened:
   103  		text = fmt.Sprintf("[%s] Issue opened: %s", repoLink, titleLink)
   104  		color = orangeColor
   105  	case api.HookIssueClosed:
   106  		text = fmt.Sprintf("[%s] Issue closed: %s", repoLink, titleLink)
   107  		color = redColor
   108  	case api.HookIssueReOpened:
   109  		text = fmt.Sprintf("[%s] Issue re-opened: %s", repoLink, titleLink)
   110  	case api.HookIssueEdited:
   111  		text = fmt.Sprintf("[%s] Issue edited: %s", repoLink, titleLink)
   112  	case api.HookIssueAssigned:
   113  		list := make([]string, len(p.Issue.Assignees))
   114  		for i, user := range p.Issue.Assignees {
   115  			list[i] = linkFormatter(setting.AppURL+url.PathEscape(user.UserName), user.UserName)
   116  		}
   117  		text = fmt.Sprintf("[%s] Issue assigned to %s: %s", repoLink, strings.Join(list, ", "), titleLink)
   118  		color = greenColor
   119  	case api.HookIssueUnassigned:
   120  		text = fmt.Sprintf("[%s] Issue unassigned: %s", repoLink, titleLink)
   121  	case api.HookIssueLabelUpdated:
   122  		text = fmt.Sprintf("[%s] Issue labels updated: %s", repoLink, titleLink)
   123  	case api.HookIssueLabelCleared:
   124  		text = fmt.Sprintf("[%s] Issue labels cleared: %s", repoLink, titleLink)
   125  	case api.HookIssueSynchronized:
   126  		text = fmt.Sprintf("[%s] Issue synchronized: %s", repoLink, titleLink)
   127  	case api.HookIssueMilestoned:
   128  		mileStoneLink := fmt.Sprintf("%s/milestone/%d", p.Repository.HTMLURL, p.Issue.Milestone.ID)
   129  		text = fmt.Sprintf("[%s] Issue milestoned to %s: %s", repoLink,
   130  			linkFormatter(mileStoneLink, p.Issue.Milestone.Title), titleLink)
   131  	case api.HookIssueDemilestoned:
   132  		text = fmt.Sprintf("[%s] Issue milestone cleared: %s", repoLink, titleLink)
   133  	}
   134  	if withSender {
   135  		text += fmt.Sprintf(" by %s", linkFormatter(setting.AppURL+url.PathEscape(p.Sender.UserName), p.Sender.UserName))
   136  	}
   137  
   138  	var attachmentText string
   139  	if p.Action == api.HookIssueOpened || p.Action == api.HookIssueEdited {
   140  		attachmentText = p.Issue.Body
   141  	}
   142  
   143  	return text, issueTitle, attachmentText, color
   144  }
   145  
   146  func getPullRequestPayloadInfo(p *api.PullRequestPayload, linkFormatter linkFormatter, withSender bool) (string, string, string, int) {
   147  	repoLink := linkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
   148  	issueTitle := fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title)
   149  	titleLink := linkFormatter(p.PullRequest.URL, issueTitle)
   150  	var text string
   151  	var attachmentText string
   152  	color := yellowColor
   153  
   154  	switch p.Action {
   155  	case api.HookIssueOpened:
   156  		text = fmt.Sprintf("[%s] Pull request opened: %s", repoLink, titleLink)
   157  		attachmentText = p.PullRequest.Body
   158  		color = greenColor
   159  	case api.HookIssueClosed:
   160  		if p.PullRequest.HasMerged {
   161  			text = fmt.Sprintf("[%s] Pull request merged: %s", repoLink, titleLink)
   162  			color = purpleColor
   163  		} else {
   164  			text = fmt.Sprintf("[%s] Pull request closed: %s", repoLink, titleLink)
   165  			color = redColor
   166  		}
   167  	case api.HookIssueReOpened:
   168  		text = fmt.Sprintf("[%s] Pull request re-opened: %s", repoLink, titleLink)
   169  	case api.HookIssueEdited:
   170  		text = fmt.Sprintf("[%s] Pull request edited: %s", repoLink, titleLink)
   171  		attachmentText = p.PullRequest.Body
   172  	case api.HookIssueAssigned:
   173  		list := make([]string, len(p.PullRequest.Assignees))
   174  		for i, user := range p.PullRequest.Assignees {
   175  			list[i] = linkFormatter(setting.AppURL+user.UserName, user.UserName)
   176  		}
   177  		text = fmt.Sprintf("[%s] Pull request assigned to %s: %s", repoLink,
   178  			strings.Join(list, ", "), titleLink)
   179  		color = greenColor
   180  	case api.HookIssueUnassigned:
   181  		text = fmt.Sprintf("[%s] Pull request unassigned: %s", repoLink, titleLink)
   182  	case api.HookIssueLabelUpdated:
   183  		text = fmt.Sprintf("[%s] Pull request labels updated: %s", repoLink, titleLink)
   184  	case api.HookIssueLabelCleared:
   185  		text = fmt.Sprintf("[%s] Pull request labels cleared: %s", repoLink, titleLink)
   186  	case api.HookIssueSynchronized:
   187  		text = fmt.Sprintf("[%s] Pull request synchronized: %s", repoLink, titleLink)
   188  	case api.HookIssueMilestoned:
   189  		mileStoneLink := fmt.Sprintf("%s/milestone/%d", p.Repository.HTMLURL, p.PullRequest.Milestone.ID)
   190  		text = fmt.Sprintf("[%s] Pull request milestoned to %s: %s", repoLink,
   191  			linkFormatter(mileStoneLink, p.PullRequest.Milestone.Title), titleLink)
   192  	case api.HookIssueDemilestoned:
   193  		text = fmt.Sprintf("[%s] Pull request milestone cleared: %s", repoLink, titleLink)
   194  	case api.HookIssueReviewed:
   195  		text = fmt.Sprintf("[%s] Pull request reviewed: %s", repoLink, titleLink)
   196  		attachmentText = p.Review.Content
   197  	case api.HookIssueReviewRequested:
   198  		text = fmt.Sprintf("[%s] Pull request review requested: %s", repoLink, titleLink)
   199  	case api.HookIssueReviewRequestRemoved:
   200  		text = fmt.Sprintf("[%s] Pull request review request removed: %s", repoLink, titleLink)
   201  	}
   202  	if withSender {
   203  		text += fmt.Sprintf(" by %s", linkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName))
   204  	}
   205  
   206  	return text, issueTitle, attachmentText, color
   207  }
   208  
   209  func getReleasePayloadInfo(p *api.ReleasePayload, linkFormatter linkFormatter, withSender bool) (text string, color int) {
   210  	repoLink := linkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
   211  	refLink := linkFormatter(p.Repository.HTMLURL+"/releases/tag/"+util.PathEscapeSegments(p.Release.TagName), p.Release.TagName)
   212  
   213  	switch p.Action {
   214  	case api.HookReleasePublished:
   215  		text = fmt.Sprintf("[%s] Release created: %s", repoLink, refLink)
   216  		color = greenColor
   217  	case api.HookReleaseUpdated:
   218  		text = fmt.Sprintf("[%s] Release updated: %s", repoLink, refLink)
   219  		color = yellowColor
   220  	case api.HookReleaseDeleted:
   221  		text = fmt.Sprintf("[%s] Release deleted: %s", repoLink, refLink)
   222  		color = redColor
   223  	}
   224  	if withSender {
   225  		text += fmt.Sprintf(" by %s", linkFormatter(setting.AppURL+url.PathEscape(p.Sender.UserName), p.Sender.UserName))
   226  	}
   227  
   228  	return text, color
   229  }
   230  
   231  func getWikiPayloadInfo(p *api.WikiPayload, linkFormatter linkFormatter, withSender bool) (string, int, string) {
   232  	repoLink := linkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
   233  	pageLink := linkFormatter(p.Repository.HTMLURL+"/wiki/"+url.PathEscape(p.Page), p.Page)
   234  
   235  	var text string
   236  	color := greenColor
   237  
   238  	switch p.Action {
   239  	case api.HookWikiCreated:
   240  		text = fmt.Sprintf("[%s] New wiki page '%s'", repoLink, pageLink)
   241  	case api.HookWikiEdited:
   242  		text = fmt.Sprintf("[%s] Wiki page '%s' edited", repoLink, pageLink)
   243  		color = yellowColor
   244  	case api.HookWikiDeleted:
   245  		text = fmt.Sprintf("[%s] Wiki page '%s' deleted", repoLink, pageLink)
   246  		color = redColor
   247  	}
   248  
   249  	if p.Action != api.HookWikiDeleted && p.Comment != "" {
   250  		text += fmt.Sprintf(" (%s)", p.Comment)
   251  	}
   252  
   253  	if withSender {
   254  		text += fmt.Sprintf(" by %s", linkFormatter(setting.AppURL+url.PathEscape(p.Sender.UserName), p.Sender.UserName))
   255  	}
   256  
   257  	return text, color, pageLink
   258  }
   259  
   260  func getIssueCommentPayloadInfo(p *api.IssueCommentPayload, linkFormatter linkFormatter, withSender bool) (string, string, int) {
   261  	repoLink := linkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
   262  	issueTitle := fmt.Sprintf("#%d %s", p.Issue.Index, p.Issue.Title)
   263  
   264  	var text, typ, titleLink string
   265  	color := yellowColor
   266  
   267  	if p.IsPull {
   268  		typ = "pull request"
   269  		titleLink = linkFormatter(p.Comment.PRURL, issueTitle)
   270  	} else {
   271  		typ = "issue"
   272  		titleLink = linkFormatter(p.Comment.IssueURL, issueTitle)
   273  	}
   274  
   275  	switch p.Action {
   276  	case api.HookIssueCommentCreated:
   277  		text = fmt.Sprintf("[%s] New comment on %s %s", repoLink, typ, titleLink)
   278  		if p.IsPull {
   279  			color = greenColorLight
   280  		} else {
   281  			color = orangeColorLight
   282  		}
   283  	case api.HookIssueCommentEdited:
   284  		text = fmt.Sprintf("[%s] Comment edited on %s %s", repoLink, typ, titleLink)
   285  	case api.HookIssueCommentDeleted:
   286  		text = fmt.Sprintf("[%s] Comment deleted on %s %s", repoLink, typ, titleLink)
   287  		color = redColor
   288  	}
   289  	if withSender {
   290  		text += fmt.Sprintf(" by %s", linkFormatter(setting.AppURL+url.PathEscape(p.Sender.UserName), p.Sender.UserName))
   291  	}
   292  
   293  	return text, issueTitle, color
   294  }
   295  
   296  func getPackagePayloadInfo(p *api.PackagePayload, linkFormatter linkFormatter, withSender bool) (text string, color int) {
   297  	refLink := linkFormatter(p.Package.HTMLURL, p.Package.Name+":"+p.Package.Version)
   298  
   299  	switch p.Action {
   300  	case api.HookPackageCreated:
   301  		text = fmt.Sprintf("Package created: %s", refLink)
   302  		color = greenColor
   303  	case api.HookPackageDeleted:
   304  		text = fmt.Sprintf("Package deleted: %s", refLink)
   305  		color = redColor
   306  	}
   307  	if withSender {
   308  		text += fmt.Sprintf(" by %s", linkFormatter(setting.AppURL+url.PathEscape(p.Sender.UserName), p.Sender.UserName))
   309  	}
   310  
   311  	return text, color
   312  }
   313  
   314  // ToHook convert models.Webhook to api.Hook
   315  // This function is not part of the convert package to prevent an import cycle
   316  func ToHook(repoLink string, w *webhook_model.Webhook) (*api.Hook, error) {
   317  	config := map[string]string{
   318  		"url":          w.URL,
   319  		"content_type": w.ContentType.Name(),
   320  	}
   321  	if w.Type == webhook_module.SLACK {
   322  		s := GetSlackHook(w)
   323  		config["channel"] = s.Channel
   324  		config["username"] = s.Username
   325  		config["icon_url"] = s.IconURL
   326  		config["color"] = s.Color
   327  	}
   328  
   329  	authorizationHeader, err := w.HeaderAuthorization()
   330  	if err != nil {
   331  		return nil, err
   332  	}
   333  
   334  	return &api.Hook{
   335  		ID:                  w.ID,
   336  		Type:                w.Type,
   337  		URL:                 fmt.Sprintf("%s/settings/hooks/%d", repoLink, w.ID),
   338  		Active:              w.IsActive,
   339  		Config:              config,
   340  		Events:              w.EventsArray(),
   341  		AuthorizationHeader: authorizationHeader,
   342  		Updated:             w.UpdatedUnix.AsTime(),
   343  		Created:             w.CreatedUnix.AsTime(),
   344  		BranchFilter:        w.BranchFilter,
   345  	}, nil
   346  }