2019-12-28 11:55:09 +03:00
// Copyright 2019 The Gitea Authors. All rights reserved.
2022-11-27 21:20:29 +03:00
// SPDX-License-Identifier: MIT
2019-12-28 11:55:09 +03:00
package webhook
import (
"fmt"
"html"
2021-11-16 21:18:25 +03:00
"net/url"
2019-12-28 11:55:09 +03:00
"strings"
2023-01-01 18:23:15 +03:00
webhook_model "code.gitea.io/gitea/models/webhook"
2019-12-28 11:55:09 +03:00
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
2021-11-16 21:18:25 +03:00
"code.gitea.io/gitea/modules/util"
2023-01-01 18:23:15 +03:00
webhook_module "code.gitea.io/gitea/modules/webhook"
2019-12-28 11:55:09 +03:00
)
type linkFormatter = func ( string , string ) string
// noneLinkFormatter does not create a link but just returns the text
2021-12-20 07:41:31 +03:00
func noneLinkFormatter ( url , text string ) string {
2019-12-28 11:55:09 +03:00
return text
}
// htmlLinkFormatter creates a HTML link
2021-12-20 07:41:31 +03:00
func htmlLinkFormatter ( url , text string ) string {
2021-11-16 21:18:25 +03:00
return fmt . Sprintf ( ` <a href="%s">%s</a> ` , html . EscapeString ( url ) , html . EscapeString ( text ) )
2019-12-28 11:55:09 +03:00
}
2020-01-05 01:20:15 +03:00
func getIssuesPayloadInfo ( p * api . IssuePayload , linkFormatter linkFormatter , withSender bool ) ( string , string , string , int ) {
2019-12-28 11:55:09 +03:00
repoLink := linkFormatter ( p . Repository . HTMLURL , p . Repository . FullName )
issueTitle := fmt . Sprintf ( "#%d %s" , p . Index , p . Issue . Title )
titleLink := linkFormatter ( fmt . Sprintf ( "%s/issues/%d" , p . Repository . HTMLURL , p . Index ) , issueTitle )
var text string
color := yellowColor
switch p . Action {
case api . HookIssueOpened :
2020-01-05 01:20:15 +03:00
text = fmt . Sprintf ( "[%s] Issue opened: %s" , repoLink , titleLink )
2019-12-28 11:55:09 +03:00
color = orangeColor
case api . HookIssueClosed :
2020-01-05 01:20:15 +03:00
text = fmt . Sprintf ( "[%s] Issue closed: %s" , repoLink , titleLink )
2019-12-28 11:55:09 +03:00
color = redColor
case api . HookIssueReOpened :
2020-01-05 01:20:15 +03:00
text = fmt . Sprintf ( "[%s] Issue re-opened: %s" , repoLink , titleLink )
2019-12-28 11:55:09 +03:00
case api . HookIssueEdited :
2020-01-05 01:20:15 +03:00
text = fmt . Sprintf ( "[%s] Issue edited: %s" , repoLink , titleLink )
2019-12-28 11:55:09 +03:00
case api . HookIssueAssigned :
2021-06-21 05:12:19 +03:00
list := make ( [ ] string , len ( p . Issue . Assignees ) )
for i , user := range p . Issue . Assignees {
2021-11-16 21:18:25 +03:00
list [ i ] = linkFormatter ( setting . AppURL + url . PathEscape ( user . UserName ) , user . UserName )
2021-06-21 05:12:19 +03:00
}
text = fmt . Sprintf ( "[%s] Issue assigned to %s: %s" , repoLink , strings . Join ( list , ", " ) , titleLink )
2019-12-28 11:55:09 +03:00
color = greenColor
case api . HookIssueUnassigned :
2020-01-05 01:20:15 +03:00
text = fmt . Sprintf ( "[%s] Issue unassigned: %s" , repoLink , titleLink )
2019-12-28 11:55:09 +03:00
case api . HookIssueLabelUpdated :
2020-01-05 01:20:15 +03:00
text = fmt . Sprintf ( "[%s] Issue labels updated: %s" , repoLink , titleLink )
2019-12-28 11:55:09 +03:00
case api . HookIssueLabelCleared :
2020-01-05 01:20:15 +03:00
text = fmt . Sprintf ( "[%s] Issue labels cleared: %s" , repoLink , titleLink )
2019-12-28 11:55:09 +03:00
case api . HookIssueSynchronized :
2020-01-05 01:20:15 +03:00
text = fmt . Sprintf ( "[%s] Issue synchronized: %s" , repoLink , titleLink )
2019-12-28 11:55:09 +03:00
case api . HookIssueMilestoned :
mileStoneLink := fmt . Sprintf ( "%s/milestone/%d" , p . Repository . HTMLURL , p . Issue . Milestone . ID )
2020-01-05 01:20:15 +03:00
text = fmt . Sprintf ( "[%s] Issue milestoned to %s: %s" , repoLink ,
linkFormatter ( mileStoneLink , p . Issue . Milestone . Title ) , titleLink )
2019-12-28 11:55:09 +03:00
case api . HookIssueDemilestoned :
2020-01-05 01:20:15 +03:00
text = fmt . Sprintf ( "[%s] Issue milestone cleared: %s" , repoLink , titleLink )
}
if withSender {
2021-11-16 21:18:25 +03:00
text += fmt . Sprintf ( " by %s" , linkFormatter ( setting . AppURL + url . PathEscape ( p . Sender . UserName ) , p . Sender . UserName ) )
2019-12-28 11:55:09 +03:00
}
var attachmentText string
if p . Action == api . HookIssueOpened || p . Action == api . HookIssueEdited {
attachmentText = p . Issue . Body
}
return text , issueTitle , attachmentText , color
}
2020-01-05 01:20:15 +03:00
func getPullRequestPayloadInfo ( p * api . PullRequestPayload , linkFormatter linkFormatter , withSender bool ) ( string , string , string , int ) {
2019-12-28 11:55:09 +03:00
repoLink := linkFormatter ( p . Repository . HTMLURL , p . Repository . FullName )
issueTitle := fmt . Sprintf ( "#%d %s" , p . Index , p . PullRequest . Title )
titleLink := linkFormatter ( p . PullRequest . URL , issueTitle )
var text string
2022-11-13 19:57:52 +03:00
var attachmentText string
2019-12-28 11:55:09 +03:00
color := yellowColor
switch p . Action {
case api . HookIssueOpened :
2020-01-05 01:20:15 +03:00
text = fmt . Sprintf ( "[%s] Pull request opened: %s" , repoLink , titleLink )
2022-11-13 19:57:52 +03:00
attachmentText = p . PullRequest . Body
2019-12-28 11:55:09 +03:00
color = greenColor
case api . HookIssueClosed :
if p . PullRequest . HasMerged {
2020-01-05 01:20:15 +03:00
text = fmt . Sprintf ( "[%s] Pull request merged: %s" , repoLink , titleLink )
2019-12-28 11:55:09 +03:00
color = purpleColor
} else {
2020-01-05 01:20:15 +03:00
text = fmt . Sprintf ( "[%s] Pull request closed: %s" , repoLink , titleLink )
2019-12-28 11:55:09 +03:00
color = redColor
}
case api . HookIssueReOpened :
2020-01-05 01:20:15 +03:00
text = fmt . Sprintf ( "[%s] Pull request re-opened: %s" , repoLink , titleLink )
2019-12-28 11:55:09 +03:00
case api . HookIssueEdited :
2020-01-05 01:20:15 +03:00
text = fmt . Sprintf ( "[%s] Pull request edited: %s" , repoLink , titleLink )
2022-11-13 19:57:52 +03:00
attachmentText = p . PullRequest . Body
2019-12-28 11:55:09 +03:00
case api . HookIssueAssigned :
list := make ( [ ] string , len ( p . PullRequest . Assignees ) )
for i , user := range p . PullRequest . Assignees {
list [ i ] = linkFormatter ( setting . AppURL + user . UserName , user . UserName )
}
2021-06-21 05:12:19 +03:00
text = fmt . Sprintf ( "[%s] Pull request assigned to %s: %s" , repoLink ,
2020-01-05 01:20:15 +03:00
strings . Join ( list , ", " ) , titleLink )
2019-12-28 11:55:09 +03:00
color = greenColor
case api . HookIssueUnassigned :
2020-01-05 01:20:15 +03:00
text = fmt . Sprintf ( "[%s] Pull request unassigned: %s" , repoLink , titleLink )
2019-12-28 11:55:09 +03:00
case api . HookIssueLabelUpdated :
2020-01-05 01:20:15 +03:00
text = fmt . Sprintf ( "[%s] Pull request labels updated: %s" , repoLink , titleLink )
2019-12-28 11:55:09 +03:00
case api . HookIssueLabelCleared :
2020-01-05 01:20:15 +03:00
text = fmt . Sprintf ( "[%s] Pull request labels cleared: %s" , repoLink , titleLink )
2019-12-28 11:55:09 +03:00
case api . HookIssueSynchronized :
2020-01-05 01:20:15 +03:00
text = fmt . Sprintf ( "[%s] Pull request synchronized: %s" , repoLink , titleLink )
2019-12-28 11:55:09 +03:00
case api . HookIssueMilestoned :
mileStoneLink := fmt . Sprintf ( "%s/milestone/%d" , p . Repository . HTMLURL , p . PullRequest . Milestone . ID )
2021-06-21 05:12:19 +03:00
text = fmt . Sprintf ( "[%s] Pull request milestoned to %s: %s" , repoLink ,
2020-01-05 01:20:15 +03:00
linkFormatter ( mileStoneLink , p . PullRequest . Milestone . Title ) , titleLink )
2019-12-28 11:55:09 +03:00
case api . HookIssueDemilestoned :
2020-01-05 01:20:15 +03:00
text = fmt . Sprintf ( "[%s] Pull request milestone cleared: %s" , repoLink , titleLink )
2020-06-25 06:39:43 +03:00
case api . HookIssueReviewed :
text = fmt . Sprintf ( "[%s] Pull request reviewed: %s" , repoLink , titleLink )
2022-11-13 19:57:52 +03:00
attachmentText = p . Review . Content
2023-08-09 11:30:34 +03:00
case api . HookIssueReviewRequested :
text = fmt . Sprintf ( "[%s] Pull request review requested: %s" , repoLink , titleLink )
case api . HookIssueReviewRequestRemoved :
text = fmt . Sprintf ( "[%s] Pull request review request removed: %s" , repoLink , titleLink )
2020-01-05 01:20:15 +03:00
}
if withSender {
text += fmt . Sprintf ( " by %s" , linkFormatter ( setting . AppURL + p . Sender . UserName , p . Sender . UserName ) )
2019-12-28 11:55:09 +03:00
}
return text , issueTitle , attachmentText , color
}
2020-01-05 01:20:15 +03:00
func getReleasePayloadInfo ( p * api . ReleasePayload , linkFormatter linkFormatter , withSender bool ) ( text string , color int ) {
2019-12-28 11:55:09 +03:00
repoLink := linkFormatter ( p . Repository . HTMLURL , p . Repository . FullName )
2022-05-28 23:44:51 +03:00
refLink := linkFormatter ( p . Repository . HTMLURL + "/releases/tag/" + util . PathEscapeSegments ( p . Release . TagName ) , p . Release . TagName )
2019-12-28 11:55:09 +03:00
switch p . Action {
case api . HookReleasePublished :
2020-01-05 01:20:15 +03:00
text = fmt . Sprintf ( "[%s] Release created: %s" , repoLink , refLink )
2019-12-28 11:55:09 +03:00
color = greenColor
case api . HookReleaseUpdated :
2020-01-05 01:20:15 +03:00
text = fmt . Sprintf ( "[%s] Release updated: %s" , repoLink , refLink )
2019-12-28 11:55:09 +03:00
color = yellowColor
case api . HookReleaseDeleted :
2020-01-05 01:20:15 +03:00
text = fmt . Sprintf ( "[%s] Release deleted: %s" , repoLink , refLink )
2019-12-28 11:55:09 +03:00
color = redColor
}
2020-01-05 01:20:15 +03:00
if withSender {
2021-11-16 21:18:25 +03:00
text += fmt . Sprintf ( " by %s" , linkFormatter ( setting . AppURL + url . PathEscape ( p . Sender . UserName ) , p . Sender . UserName ) )
2020-01-05 01:20:15 +03:00
}
2019-12-28 11:55:09 +03:00
return text , color
}
2022-09-04 22:54:23 +03:00
func getWikiPayloadInfo ( p * api . WikiPayload , linkFormatter linkFormatter , withSender bool ) ( string , int , string ) {
repoLink := linkFormatter ( p . Repository . HTMLURL , p . Repository . FullName )
pageLink := linkFormatter ( p . Repository . HTMLURL + "/wiki/" + url . PathEscape ( p . Page ) , p . Page )
var text string
color := greenColor
switch p . Action {
case api . HookWikiCreated :
text = fmt . Sprintf ( "[%s] New wiki page '%s'" , repoLink , pageLink )
case api . HookWikiEdited :
text = fmt . Sprintf ( "[%s] Wiki page '%s' edited" , repoLink , pageLink )
color = yellowColor
case api . HookWikiDeleted :
text = fmt . Sprintf ( "[%s] Wiki page '%s' deleted" , repoLink , pageLink )
color = redColor
}
if p . Action != api . HookWikiDeleted && p . Comment != "" {
text += fmt . Sprintf ( " (%s)" , p . Comment )
}
if withSender {
text += fmt . Sprintf ( " by %s" , linkFormatter ( setting . AppURL + url . PathEscape ( p . Sender . UserName ) , p . Sender . UserName ) )
}
return text , color , pageLink
}
2020-01-05 01:20:15 +03:00
func getIssueCommentPayloadInfo ( p * api . IssueCommentPayload , linkFormatter linkFormatter , withSender bool ) ( string , string , int ) {
2019-12-28 11:55:09 +03:00
repoLink := linkFormatter ( p . Repository . HTMLURL , p . Repository . FullName )
issueTitle := fmt . Sprintf ( "#%d %s" , p . Issue . Index , p . Issue . Title )
var text , typ , titleLink string
color := yellowColor
if p . IsPull {
typ = "pull request"
titleLink = linkFormatter ( p . Comment . PRURL , issueTitle )
} else {
typ = "issue"
titleLink = linkFormatter ( p . Comment . IssueURL , issueTitle )
}
switch p . Action {
case api . HookIssueCommentCreated :
2020-01-05 01:20:15 +03:00
text = fmt . Sprintf ( "[%s] New comment on %s %s" , repoLink , typ , titleLink )
2019-12-28 11:55:09 +03:00
if p . IsPull {
color = greenColorLight
} else {
color = orangeColorLight
}
case api . HookIssueCommentEdited :
2020-01-05 01:20:15 +03:00
text = fmt . Sprintf ( "[%s] Comment edited on %s %s" , repoLink , typ , titleLink )
2019-12-28 11:55:09 +03:00
case api . HookIssueCommentDeleted :
2020-01-05 01:20:15 +03:00
text = fmt . Sprintf ( "[%s] Comment deleted on %s %s" , repoLink , typ , titleLink )
2019-12-28 11:55:09 +03:00
color = redColor
}
2020-01-05 01:20:15 +03:00
if withSender {
2021-11-16 21:18:25 +03:00
text += fmt . Sprintf ( " by %s" , linkFormatter ( setting . AppURL + url . PathEscape ( p . Sender . UserName ) , p . Sender . UserName ) )
2020-01-05 01:20:15 +03:00
}
2019-12-28 11:55:09 +03:00
return text , issueTitle , color
}
2023-01-01 18:23:15 +03:00
// ToHook convert models.Webhook to api.Hook
// This function is not part of the convert package to prevent an import cycle
func ToHook ( repoLink string , w * webhook_model . Webhook ) ( * api . Hook , error ) {
config := map [ string ] string {
"url" : w . URL ,
"content_type" : w . ContentType . Name ( ) ,
}
if w . Type == webhook_module . SLACK {
s := GetSlackHook ( w )
config [ "channel" ] = s . Channel
config [ "username" ] = s . Username
config [ "icon_url" ] = s . IconURL
config [ "color" ] = s . Color
}
authorizationHeader , err := w . HeaderAuthorization ( )
if err != nil {
return nil , err
}
return & api . Hook {
ID : w . ID ,
Type : w . Type ,
URL : fmt . Sprintf ( "%s/settings/hooks/%d" , repoLink , w . ID ) ,
Active : w . IsActive ,
Config : config ,
Events : w . EventsArray ( ) ,
AuthorizationHeader : authorizationHeader ,
Updated : w . UpdatedUnix . AsTime ( ) ,
Created : w . CreatedUnix . AsTime ( ) ,
2023-08-21 14:43:52 +03:00
BranchFilter : w . BranchFilter ,
2023-01-01 18:23:15 +03:00
} , nil
}