]> source.dussan.org Git - gitea.git/commitdiff
Move more webhook codes from models to webhook module (#8802)
authorLunny Xiao <xiaolunwen@gmail.com>
Sun, 3 Nov 2019 22:13:25 +0000 (06:13 +0800)
committerzeripath <art27@cantab.net>
Sun, 3 Nov 2019 22:13:25 +0000 (22:13 +0000)
* Move more webhook codes from models to webhook module

17 files changed:
models/webhook.go
models/webhook_dingtalk.go [deleted file]
models/webhook_discord.go [deleted file]
models/webhook_msteams.go [deleted file]
models/webhook_slack.go [deleted file]
models/webhook_telegram.go [deleted file]
models/webhook_test.go
modules/webhook/dingtalk.go [new file with mode: 0644]
modules/webhook/discord.go [new file with mode: 0644]
modules/webhook/msteams.go [new file with mode: 0644]
modules/webhook/slack.go [new file with mode: 0644]
modules/webhook/telegram.go [new file with mode: 0644]
modules/webhook/webhook.go
modules/webhook/webhook_test.go
routers/api/v1/convert/convert.go
routers/api/v1/utils/hook.go
routers/repo/webhook.go

index d3a8b52d86a02f82ffab15edb5fee2d5539bcbc4..7eb17caaf666d2d29739b0f969fa619903761931 100644 (file)
@@ -118,33 +118,6 @@ func (w *Webhook) AfterLoad() {
        }
 }
 
-// GetSlackHook returns slack metadata
-func (w *Webhook) GetSlackHook() *SlackMeta {
-       s := &SlackMeta{}
-       if err := json.Unmarshal([]byte(w.Meta), s); err != nil {
-               log.Error("webhook.GetSlackHook(%d): %v", w.ID, err)
-       }
-       return s
-}
-
-// GetDiscordHook returns discord metadata
-func (w *Webhook) GetDiscordHook() *DiscordMeta {
-       s := &DiscordMeta{}
-       if err := json.Unmarshal([]byte(w.Meta), s); err != nil {
-               log.Error("webhook.GetDiscordHook(%d): %v", w.ID, err)
-       }
-       return s
-}
-
-// GetTelegramHook returns telegram metadata
-func (w *Webhook) GetTelegramHook() *TelegramMeta {
-       s := &TelegramMeta{}
-       if err := json.Unmarshal([]byte(w.Meta), s); err != nil {
-               log.Error("webhook.GetTelegramHook(%d): %v", w.ID, err)
-       }
-       return s
-}
-
 // History returns history of webhook by given conditions.
 func (w *Webhook) History(page int) ([]*HookTask, error) {
        return HookTasks(w.ID, page)
diff --git a/models/webhook_dingtalk.go b/models/webhook_dingtalk.go
deleted file mode 100644 (file)
index 1c6c0a8..0000000
+++ /dev/null
@@ -1,422 +0,0 @@
-// Copyright 2017 The Gitea Authors. All rights reserved.
-// Use of this source code is governed by a MIT-style
-// license that can be found in the LICENSE file.
-
-package models
-
-import (
-       "encoding/json"
-       "fmt"
-       "strings"
-
-       "code.gitea.io/gitea/modules/git"
-       api "code.gitea.io/gitea/modules/structs"
-
-       dingtalk "github.com/lunny/dingtalk_webhook"
-)
-
-type (
-       // DingtalkPayload represents
-       DingtalkPayload dingtalk.Payload
-)
-
-// SetSecret sets the dingtalk secret
-func (p *DingtalkPayload) SetSecret(_ string) {}
-
-// JSONPayload Marshals the DingtalkPayload to json
-func (p *DingtalkPayload) JSONPayload() ([]byte, error) {
-       data, err := json.MarshalIndent(p, "", "  ")
-       if err != nil {
-               return []byte{}, err
-       }
-       return data, nil
-}
-
-func getDingtalkCreatePayload(p *api.CreatePayload) (*DingtalkPayload, error) {
-       // created tag/branch
-       refName := git.RefEndName(p.Ref)
-       title := fmt.Sprintf("[%s] %s %s created", p.Repo.FullName, p.RefType, refName)
-
-       return &DingtalkPayload{
-               MsgType: "actionCard",
-               ActionCard: dingtalk.ActionCard{
-                       Text:        title,
-                       Title:       title,
-                       HideAvatar:  "0",
-                       SingleTitle: fmt.Sprintf("view ref %s", refName),
-                       SingleURL:   p.Repo.HTMLURL + "/src/" + refName,
-               },
-       }, nil
-}
-
-func getDingtalkDeletePayload(p *api.DeletePayload) (*DingtalkPayload, error) {
-       // created tag/branch
-       refName := git.RefEndName(p.Ref)
-       title := fmt.Sprintf("[%s] %s %s deleted", p.Repo.FullName, p.RefType, refName)
-
-       return &DingtalkPayload{
-               MsgType: "actionCard",
-               ActionCard: dingtalk.ActionCard{
-                       Text:        title,
-                       Title:       title,
-                       HideAvatar:  "0",
-                       SingleTitle: fmt.Sprintf("view ref %s", refName),
-                       SingleURL:   p.Repo.HTMLURL + "/src/" + refName,
-               },
-       }, nil
-}
-
-func getDingtalkForkPayload(p *api.ForkPayload) (*DingtalkPayload, error) {
-       title := fmt.Sprintf("%s is forked to %s", p.Forkee.FullName, p.Repo.FullName)
-
-       return &DingtalkPayload{
-               MsgType: "actionCard",
-               ActionCard: dingtalk.ActionCard{
-                       Text:        title,
-                       Title:       title,
-                       HideAvatar:  "0",
-                       SingleTitle: fmt.Sprintf("view forked repo %s", p.Repo.FullName),
-                       SingleURL:   p.Repo.HTMLURL,
-               },
-       }, nil
-}
-
-func getDingtalkPushPayload(p *api.PushPayload) (*DingtalkPayload, error) {
-       var (
-               branchName = git.RefEndName(p.Ref)
-               commitDesc string
-       )
-
-       var titleLink, linkText string
-       if len(p.Commits) == 1 {
-               commitDesc = "1 new commit"
-               titleLink = p.Commits[0].URL
-               linkText = fmt.Sprintf("view commit %s", p.Commits[0].ID[:7])
-       } else {
-               commitDesc = fmt.Sprintf("%d new commits", len(p.Commits))
-               titleLink = p.CompareURL
-               linkText = fmt.Sprintf("view commit %s...%s", p.Commits[0].ID[:7], p.Commits[len(p.Commits)-1].ID[:7])
-       }
-       if titleLink == "" {
-               titleLink = p.Repo.HTMLURL + "/src/" + branchName
-       }
-
-       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 {
-                       text += "\n"
-               }
-       }
-
-       return &DingtalkPayload{
-               MsgType: "actionCard",
-               ActionCard: dingtalk.ActionCard{
-                       Text:        text,
-                       Title:       title,
-                       HideAvatar:  "0",
-                       SingleTitle: linkText,
-                       SingleURL:   titleLink,
-               },
-       }, nil
-}
-
-func getDingtalkIssuesPayload(p *api.IssuePayload) (*DingtalkPayload, error) {
-       var text, title string
-       switch p.Action {
-       case api.HookIssueOpened:
-               title = fmt.Sprintf("[%s] Issue opened: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
-               text = p.Issue.Body
-       case api.HookIssueClosed:
-               title = fmt.Sprintf("[%s] Issue closed: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
-               text = p.Issue.Body
-       case api.HookIssueReOpened:
-               title = fmt.Sprintf("[%s] Issue re-opened: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
-               text = p.Issue.Body
-       case api.HookIssueEdited:
-               title = fmt.Sprintf("[%s] Issue edited: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
-               text = p.Issue.Body
-       case api.HookIssueAssigned:
-               title = fmt.Sprintf("[%s] Issue assigned to %s: #%d %s", p.Repository.FullName,
-                       p.Issue.Assignee.UserName, p.Index, p.Issue.Title)
-               text = p.Issue.Body
-       case api.HookIssueUnassigned:
-               title = fmt.Sprintf("[%s] Issue unassigned: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
-               text = p.Issue.Body
-       case api.HookIssueLabelUpdated:
-               title = fmt.Sprintf("[%s] Issue labels updated: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
-               text = p.Issue.Body
-       case api.HookIssueLabelCleared:
-               title = fmt.Sprintf("[%s] Issue labels cleared: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
-               text = p.Issue.Body
-       case api.HookIssueSynchronized:
-               title = fmt.Sprintf("[%s] Issue synchronized: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
-               text = p.Issue.Body
-       case api.HookIssueMilestoned:
-               title = fmt.Sprintf("[%s] Issue milestone: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
-               text = p.Issue.Body
-       case api.HookIssueDemilestoned:
-               title = fmt.Sprintf("[%s] Issue clear milestone: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
-               text = p.Issue.Body
-       }
-
-       return &DingtalkPayload{
-               MsgType: "actionCard",
-               ActionCard: dingtalk.ActionCard{
-                       Text: title + "\r\n\r\n" + text,
-                       //Markdown:    "# " + title + "\n" + text,
-                       Title:       title,
-                       HideAvatar:  "0",
-                       SingleTitle: "view issue",
-                       SingleURL:   p.Issue.URL,
-               },
-       }, nil
-}
-
-func getDingtalkIssueCommentPayload(p *api.IssueCommentPayload) (*DingtalkPayload, error) {
-       title := fmt.Sprintf("#%d: %s", p.Issue.Index, p.Issue.Title)
-       url := fmt.Sprintf("%s/issues/%d#%s", p.Repository.HTMLURL, p.Issue.Index, CommentHashTag(p.Comment.ID))
-       var content string
-       switch p.Action {
-       case api.HookIssueCommentCreated:
-               if p.IsPull {
-                       title = "New comment on pull request " + title
-               } else {
-                       title = "New comment on issue " + title
-               }
-               content = p.Comment.Body
-       case api.HookIssueCommentEdited:
-               if p.IsPull {
-                       title = "Comment edited on pull request " + title
-               } else {
-                       title = "Comment edited on issue " + title
-               }
-               content = p.Comment.Body
-       case api.HookIssueCommentDeleted:
-               if p.IsPull {
-                       title = "Comment deleted on pull request " + title
-               } else {
-                       title = "Comment deleted on issue " + title
-               }
-               url = fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Issue.Index)
-               content = p.Comment.Body
-       }
-
-       title = fmt.Sprintf("[%s] %s", p.Repository.FullName, title)
-
-       return &DingtalkPayload{
-               MsgType: "actionCard",
-               ActionCard: dingtalk.ActionCard{
-                       Text:        title + "\r\n\r\n" + content,
-                       Title:       title,
-                       HideAvatar:  "0",
-                       SingleTitle: "view issue comment",
-                       SingleURL:   url,
-               },
-       }, nil
-}
-
-func getDingtalkPullRequestPayload(p *api.PullRequestPayload) (*DingtalkPayload, error) {
-       var text, title string
-       switch p.Action {
-       case api.HookIssueOpened:
-               title = fmt.Sprintf("[%s] Pull request opened: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
-               text = p.PullRequest.Body
-       case api.HookIssueClosed:
-               if p.PullRequest.HasMerged {
-                       title = fmt.Sprintf("[%s] Pull request merged: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
-               } else {
-                       title = fmt.Sprintf("[%s] Pull request closed: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
-               }
-               text = p.PullRequest.Body
-       case api.HookIssueReOpened:
-               title = fmt.Sprintf("[%s] Pull request re-opened: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
-               text = p.PullRequest.Body
-       case api.HookIssueEdited:
-               title = fmt.Sprintf("[%s] Pull request edited: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
-               text = p.PullRequest.Body
-       case api.HookIssueAssigned:
-               list := make([]string, len(p.PullRequest.Assignees))
-               for i, user := range p.PullRequest.Assignees {
-                       list[i] = user.UserName
-               }
-               title = fmt.Sprintf("[%s] Pull request assigned to %s: #%d %s", p.Repository.FullName,
-                       strings.Join(list, ", "),
-                       p.Index, p.PullRequest.Title)
-               text = p.PullRequest.Body
-       case api.HookIssueUnassigned:
-               title = fmt.Sprintf("[%s] Pull request unassigned: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
-               text = p.PullRequest.Body
-       case api.HookIssueLabelUpdated:
-               title = fmt.Sprintf("[%s] Pull request labels updated: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
-               text = p.PullRequest.Body
-       case api.HookIssueLabelCleared:
-               title = fmt.Sprintf("[%s] Pull request labels cleared: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
-               text = p.PullRequest.Body
-       case api.HookIssueSynchronized:
-               title = fmt.Sprintf("[%s] Pull request synchronized: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
-               text = p.PullRequest.Body
-       case api.HookIssueMilestoned:
-               title = fmt.Sprintf("[%s] Pull request milestone: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
-               text = p.PullRequest.Body
-       case api.HookIssueDemilestoned:
-               title = fmt.Sprintf("[%s] Pull request clear milestone: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
-               text = p.PullRequest.Body
-       }
-
-       return &DingtalkPayload{
-               MsgType: "actionCard",
-               ActionCard: dingtalk.ActionCard{
-                       Text: title + "\r\n\r\n" + text,
-                       //Markdown:    "# " + title + "\n" + text,
-                       Title:       title,
-                       HideAvatar:  "0",
-                       SingleTitle: "view pull request",
-                       SingleURL:   p.PullRequest.HTMLURL,
-               },
-       }, nil
-}
-
-func getDingtalkPullRequestApprovalPayload(p *api.PullRequestPayload, event HookEventType) (*DingtalkPayload, error) {
-       var text, title string
-       switch p.Action {
-       case api.HookIssueSynchronized:
-               action, err := parseHookPullRequestEventType(event)
-               if err != nil {
-                       return nil, err
-               }
-
-               title = fmt.Sprintf("[%s] Pull request review %s : #%d %s", p.Repository.FullName, action, p.Index, p.PullRequest.Title)
-               text = p.Review.Content
-
-       }
-
-       return &DingtalkPayload{
-               MsgType: "actionCard",
-               ActionCard: dingtalk.ActionCard{
-                       Text:        title + "\r\n\r\n" + text,
-                       Title:       title,
-                       HideAvatar:  "0",
-                       SingleTitle: "view pull request",
-                       SingleURL:   p.PullRequest.HTMLURL,
-               },
-       }, nil
-}
-
-func getDingtalkRepositoryPayload(p *api.RepositoryPayload) (*DingtalkPayload, error) {
-       var title, url string
-       switch p.Action {
-       case api.HookRepoCreated:
-               title = fmt.Sprintf("[%s] Repository created", p.Repository.FullName)
-               url = p.Repository.HTMLURL
-               return &DingtalkPayload{
-                       MsgType: "actionCard",
-                       ActionCard: dingtalk.ActionCard{
-                               Text:        title,
-                               Title:       title,
-                               HideAvatar:  "0",
-                               SingleTitle: "view repository",
-                               SingleURL:   url,
-                       },
-               }, nil
-       case api.HookRepoDeleted:
-               title = fmt.Sprintf("[%s] Repository deleted", p.Repository.FullName)
-               return &DingtalkPayload{
-                       MsgType: "text",
-                       Text: struct {
-                               Content string `json:"content"`
-                       }{
-                               Content: title,
-                       },
-               }, nil
-       }
-
-       return nil, nil
-}
-
-func getDingtalkReleasePayload(p *api.ReleasePayload) (*DingtalkPayload, error) {
-       var title, url string
-       switch p.Action {
-       case api.HookReleasePublished:
-               title = fmt.Sprintf("[%s] Release created", p.Release.TagName)
-               url = p.Release.URL
-               return &DingtalkPayload{
-                       MsgType: "actionCard",
-                       ActionCard: dingtalk.ActionCard{
-                               Text:        title,
-                               Title:       title,
-                               HideAvatar:  "0",
-                               SingleTitle: "view release",
-                               SingleURL:   url,
-                       },
-               }, nil
-       case api.HookReleaseUpdated:
-               title = fmt.Sprintf("[%s] Release updated", p.Release.TagName)
-               url = p.Release.URL
-               return &DingtalkPayload{
-                       MsgType: "actionCard",
-                       ActionCard: dingtalk.ActionCard{
-                               Text:        title,
-                               Title:       title,
-                               HideAvatar:  "0",
-                               SingleTitle: "view release",
-                               SingleURL:   url,
-                       },
-               }, nil
-
-       case api.HookReleaseDeleted:
-               title = fmt.Sprintf("[%s] Release deleted", p.Release.TagName)
-               url = p.Release.URL
-               return &DingtalkPayload{
-                       MsgType: "actionCard",
-                       ActionCard: dingtalk.ActionCard{
-                               Text:        title,
-                               Title:       title,
-                               HideAvatar:  "0",
-                               SingleTitle: "view release",
-                               SingleURL:   url,
-                       },
-               }, nil
-       }
-
-       return nil, nil
-}
-
-// GetDingtalkPayload converts a ding talk webhook into a DingtalkPayload
-func GetDingtalkPayload(p api.Payloader, event HookEventType, meta string) (*DingtalkPayload, error) {
-       s := new(DingtalkPayload)
-
-       switch event {
-       case HookEventCreate:
-               return getDingtalkCreatePayload(p.(*api.CreatePayload))
-       case HookEventDelete:
-               return getDingtalkDeletePayload(p.(*api.DeletePayload))
-       case HookEventFork:
-               return getDingtalkForkPayload(p.(*api.ForkPayload))
-       case HookEventIssues:
-               return getDingtalkIssuesPayload(p.(*api.IssuePayload))
-       case HookEventIssueComment:
-               return getDingtalkIssueCommentPayload(p.(*api.IssueCommentPayload))
-       case HookEventPush:
-               return getDingtalkPushPayload(p.(*api.PushPayload))
-       case HookEventPullRequest:
-               return getDingtalkPullRequestPayload(p.(*api.PullRequestPayload))
-       case HookEventPullRequestApproved, HookEventPullRequestRejected, HookEventPullRequestComment:
-               return getDingtalkPullRequestApprovalPayload(p.(*api.PullRequestPayload), event)
-       case HookEventRepository:
-               return getDingtalkRepositoryPayload(p.(*api.RepositoryPayload))
-       case HookEventRelease:
-               return getDingtalkReleasePayload(p.(*api.ReleasePayload))
-       }
-
-       return s, nil
-}
diff --git a/models/webhook_discord.go b/models/webhook_discord.go
deleted file mode 100644 (file)
index 32039ed..0000000
+++ /dev/null
@@ -1,585 +0,0 @@
-// Copyright 2017 The Gitea Authors. All rights reserved.
-// Use of this source code is governed by a MIT-style
-// license that can be found in the LICENSE file.
-
-package models
-
-import (
-       "encoding/json"
-       "errors"
-       "fmt"
-       "strconv"
-       "strings"
-
-       "code.gitea.io/gitea/modules/git"
-       "code.gitea.io/gitea/modules/setting"
-       api "code.gitea.io/gitea/modules/structs"
-)
-
-type (
-       // DiscordEmbedFooter for Embed Footer Structure.
-       DiscordEmbedFooter struct {
-               Text string `json:"text"`
-       }
-
-       // DiscordEmbedAuthor for Embed Author Structure
-       DiscordEmbedAuthor struct {
-               Name    string `json:"name"`
-               URL     string `json:"url"`
-               IconURL string `json:"icon_url"`
-       }
-
-       // DiscordEmbedField for Embed Field Structure
-       DiscordEmbedField struct {
-               Name  string `json:"name"`
-               Value string `json:"value"`
-       }
-
-       // DiscordEmbed is for Embed Structure
-       DiscordEmbed struct {
-               Title       string              `json:"title"`
-               Description string              `json:"description"`
-               URL         string              `json:"url"`
-               Color       int                 `json:"color"`
-               Footer      DiscordEmbedFooter  `json:"footer"`
-               Author      DiscordEmbedAuthor  `json:"author"`
-               Fields      []DiscordEmbedField `json:"fields"`
-       }
-
-       // DiscordPayload represents
-       DiscordPayload struct {
-               Wait      bool           `json:"wait"`
-               Content   string         `json:"content"`
-               Username  string         `json:"username"`
-               AvatarURL string         `json:"avatar_url"`
-               TTS       bool           `json:"tts"`
-               Embeds    []DiscordEmbed `json:"embeds"`
-       }
-
-       // DiscordMeta contains the discord metadata
-       DiscordMeta struct {
-               Username string `json:"username"`
-               IconURL  string `json:"icon_url"`
-       }
-)
-
-func color(clr string) int {
-       if clr != "" {
-               clr = strings.TrimLeft(clr, "#")
-               if s, err := strconv.ParseInt(clr, 16, 32); err == nil {
-                       return int(s)
-               }
-       }
-
-       return 0
-}
-
-var (
-       greenColor       = color("1ac600")
-       greenColorLight  = color("bfe5bf")
-       yellowColor      = color("ffd930")
-       greyColor        = color("4f545c")
-       purpleColor      = color("7289da")
-       orangeColor      = color("eb6420")
-       orangeColorLight = color("e68d60")
-       redColor         = color("ff3232")
-)
-
-// SetSecret sets the discord secret
-func (p *DiscordPayload) SetSecret(_ string) {}
-
-// JSONPayload Marshals the DiscordPayload to json
-func (p *DiscordPayload) JSONPayload() ([]byte, error) {
-       data, err := json.MarshalIndent(p, "", "  ")
-       if err != nil {
-               return []byte{}, err
-       }
-       return data, nil
-}
-
-func getDiscordCreatePayload(p *api.CreatePayload, meta *DiscordMeta) (*DiscordPayload, error) {
-       // created tag/branch
-       refName := git.RefEndName(p.Ref)
-       title := fmt.Sprintf("[%s] %s %s created", p.Repo.FullName, p.RefType, refName)
-
-       return &DiscordPayload{
-               Username:  meta.Username,
-               AvatarURL: meta.IconURL,
-               Embeds: []DiscordEmbed{
-                       {
-                               Title: title,
-                               URL:   p.Repo.HTMLURL + "/src/" + refName,
-                               Color: greenColor,
-                               Author: DiscordEmbedAuthor{
-                                       Name:    p.Sender.UserName,
-                                       URL:     setting.AppURL + p.Sender.UserName,
-                                       IconURL: p.Sender.AvatarURL,
-                               },
-                       },
-               },
-       }, nil
-}
-
-func getDiscordDeletePayload(p *api.DeletePayload, meta *DiscordMeta) (*DiscordPayload, error) {
-       // deleted tag/branch
-       refName := git.RefEndName(p.Ref)
-       title := fmt.Sprintf("[%s] %s %s deleted", p.Repo.FullName, p.RefType, refName)
-
-       return &DiscordPayload{
-               Username:  meta.Username,
-               AvatarURL: meta.IconURL,
-               Embeds: []DiscordEmbed{
-                       {
-                               Title: title,
-                               URL:   p.Repo.HTMLURL + "/src/" + refName,
-                               Color: redColor,
-                               Author: DiscordEmbedAuthor{
-                                       Name:    p.Sender.UserName,
-                                       URL:     setting.AppURL + p.Sender.UserName,
-                                       IconURL: p.Sender.AvatarURL,
-                               },
-                       },
-               },
-       }, nil
-}
-
-func getDiscordForkPayload(p *api.ForkPayload, meta *DiscordMeta) (*DiscordPayload, error) {
-       // fork
-       title := fmt.Sprintf("%s is forked to %s", p.Forkee.FullName, p.Repo.FullName)
-
-       return &DiscordPayload{
-               Username:  meta.Username,
-               AvatarURL: meta.IconURL,
-               Embeds: []DiscordEmbed{
-                       {
-                               Title: title,
-                               URL:   p.Repo.HTMLURL,
-                               Color: greenColor,
-                               Author: DiscordEmbedAuthor{
-                                       Name:    p.Sender.UserName,
-                                       URL:     setting.AppURL + p.Sender.UserName,
-                                       IconURL: p.Sender.AvatarURL,
-                               },
-                       },
-               },
-       }, nil
-}
-
-func getDiscordPushPayload(p *api.PushPayload, meta *DiscordMeta) (*DiscordPayload, error) {
-       var (
-               branchName = git.RefEndName(p.Ref)
-               commitDesc string
-       )
-
-       var titleLink string
-       if len(p.Commits) == 1 {
-               commitDesc = "1 new commit"
-               titleLink = p.Commits[0].URL
-       } else {
-               commitDesc = fmt.Sprintf("%d new commits", len(p.Commits))
-               titleLink = p.CompareURL
-       }
-       if titleLink == "" {
-               titleLink = p.Repo.HTMLURL + "/src/" + branchName
-       }
-
-       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 {
-               text += fmt.Sprintf("[%s](%s) %s - %s", commit.ID[:7], commit.URL,
-                       strings.TrimRight(commit.Message, "\r\n"), commit.Author.Name)
-               // add linebreak to each commit but the last
-               if i < len(p.Commits)-1 {
-                       text += "\n"
-               }
-       }
-
-       return &DiscordPayload{
-               Username:  meta.Username,
-               AvatarURL: meta.IconURL,
-               Embeds: []DiscordEmbed{
-                       {
-                               Title:       title,
-                               Description: text,
-                               URL:         titleLink,
-                               Color:       greenColor,
-                               Author: DiscordEmbedAuthor{
-                                       Name:    p.Sender.UserName,
-                                       URL:     setting.AppURL + p.Sender.UserName,
-                                       IconURL: p.Sender.AvatarURL,
-                               },
-                       },
-               },
-       }, nil
-}
-
-func getDiscordIssuesPayload(p *api.IssuePayload, meta *DiscordMeta) (*DiscordPayload, error) {
-       var text, title string
-       var color int
-       url := fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Issue.Index)
-       switch p.Action {
-       case api.HookIssueOpened:
-               title = fmt.Sprintf("[%s] Issue opened: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
-               text = p.Issue.Body
-               color = orangeColor
-       case api.HookIssueClosed:
-               title = fmt.Sprintf("[%s] Issue closed: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
-               color = redColor
-               text = p.Issue.Body
-       case api.HookIssueReOpened:
-               title = fmt.Sprintf("[%s] Issue re-opened: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
-               text = p.Issue.Body
-               color = yellowColor
-       case api.HookIssueEdited:
-               title = fmt.Sprintf("[%s] Issue edited: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
-               text = p.Issue.Body
-               color = yellowColor
-       case api.HookIssueAssigned:
-               title = fmt.Sprintf("[%s] Issue assigned to %s: #%d %s", p.Repository.FullName,
-                       p.Issue.Assignee.UserName, p.Index, p.Issue.Title)
-               text = p.Issue.Body
-               color = greenColor
-       case api.HookIssueUnassigned:
-               title = fmt.Sprintf("[%s] Issue unassigned: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
-               text = p.Issue.Body
-               color = yellowColor
-       case api.HookIssueLabelUpdated:
-               title = fmt.Sprintf("[%s] Issue labels updated: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
-               text = p.Issue.Body
-               color = yellowColor
-       case api.HookIssueLabelCleared:
-               title = fmt.Sprintf("[%s] Issue labels cleared: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
-               text = p.Issue.Body
-               color = yellowColor
-       case api.HookIssueSynchronized:
-               title = fmt.Sprintf("[%s] Issue synchronized: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
-               text = p.Issue.Body
-               color = yellowColor
-       case api.HookIssueMilestoned:
-               title = fmt.Sprintf("[%s] Issue milestone: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
-               text = p.Issue.Body
-               color = yellowColor
-       case api.HookIssueDemilestoned:
-               title = fmt.Sprintf("[%s] Issue clear milestone: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
-               text = p.Issue.Body
-               color = yellowColor
-       }
-
-       return &DiscordPayload{
-               Username:  meta.Username,
-               AvatarURL: meta.IconURL,
-               Embeds: []DiscordEmbed{
-                       {
-                               Title:       title,
-                               Description: text,
-                               URL:         url,
-                               Color:       color,
-                               Author: DiscordEmbedAuthor{
-                                       Name:    p.Sender.UserName,
-                                       URL:     setting.AppURL + p.Sender.UserName,
-                                       IconURL: p.Sender.AvatarURL,
-                               },
-                       },
-               },
-       }, nil
-}
-
-func getDiscordIssueCommentPayload(p *api.IssueCommentPayload, discord *DiscordMeta) (*DiscordPayload, error) {
-       title := fmt.Sprintf("#%d: %s", p.Issue.Index, p.Issue.Title)
-       url := fmt.Sprintf("%s/issues/%d#%s", p.Repository.HTMLURL, p.Issue.Index, CommentHashTag(p.Comment.ID))
-       content := ""
-       var color int
-       switch p.Action {
-       case api.HookIssueCommentCreated:
-               if p.IsPull {
-                       title = "New comment on pull request " + title
-                       color = greenColorLight
-               } else {
-                       title = "New comment on issue " + title
-                       color = orangeColorLight
-               }
-               content = p.Comment.Body
-       case api.HookIssueCommentEdited:
-               if p.IsPull {
-                       title = "Comment edited on pull request " + title
-               } else {
-                       title = "Comment edited on issue " + title
-               }
-               content = p.Comment.Body
-               color = yellowColor
-       case api.HookIssueCommentDeleted:
-               if p.IsPull {
-                       title = "Comment deleted on pull request " + title
-               } else {
-                       title = "Comment deleted on issue " + title
-               }
-               url = fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Issue.Index)
-               content = p.Comment.Body
-               color = redColor
-       }
-
-       title = fmt.Sprintf("[%s] %s", p.Repository.FullName, title)
-
-       return &DiscordPayload{
-               Username:  discord.Username,
-               AvatarURL: discord.IconURL,
-               Embeds: []DiscordEmbed{
-                       {
-                               Title:       title,
-                               Description: content,
-                               URL:         url,
-                               Color:       color,
-                               Author: DiscordEmbedAuthor{
-                                       Name:    p.Sender.UserName,
-                                       URL:     setting.AppURL + p.Sender.UserName,
-                                       IconURL: p.Sender.AvatarURL,
-                               },
-                       },
-               },
-       }, nil
-}
-
-func getDiscordPullRequestPayload(p *api.PullRequestPayload, meta *DiscordMeta) (*DiscordPayload, error) {
-       var text, title string
-       var color int
-       switch p.Action {
-       case api.HookIssueOpened:
-               title = fmt.Sprintf("[%s] Pull request opened: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
-               text = p.PullRequest.Body
-               color = greenColor
-       case api.HookIssueClosed:
-               if p.PullRequest.HasMerged {
-                       title = fmt.Sprintf("[%s] Pull request merged: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
-                       color = purpleColor
-               } else {
-                       title = fmt.Sprintf("[%s] Pull request closed: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
-                       color = redColor
-               }
-               text = p.PullRequest.Body
-       case api.HookIssueReOpened:
-               title = fmt.Sprintf("[%s] Pull request re-opened: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
-               text = p.PullRequest.Body
-               color = yellowColor
-       case api.HookIssueEdited:
-               title = fmt.Sprintf("[%s] Pull request edited: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
-               text = p.PullRequest.Body
-               color = yellowColor
-       case api.HookIssueAssigned:
-               list := make([]string, len(p.PullRequest.Assignees))
-               for i, user := range p.PullRequest.Assignees {
-                       list[i] = user.UserName
-               }
-               title = fmt.Sprintf("[%s] Pull request assigned to %s: #%d by %s", p.Repository.FullName,
-                       strings.Join(list, ", "),
-                       p.Index, p.PullRequest.Title)
-               text = p.PullRequest.Body
-               color = greenColor
-       case api.HookIssueUnassigned:
-               title = fmt.Sprintf("[%s] Pull request unassigned: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
-               text = p.PullRequest.Body
-               color = yellowColor
-       case api.HookIssueLabelUpdated:
-               title = fmt.Sprintf("[%s] Pull request labels updated: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
-               text = p.PullRequest.Body
-               color = yellowColor
-       case api.HookIssueLabelCleared:
-               title = fmt.Sprintf("[%s] Pull request labels cleared: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
-               text = p.PullRequest.Body
-               color = yellowColor
-       case api.HookIssueSynchronized:
-               title = fmt.Sprintf("[%s] Pull request synchronized: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
-               text = p.PullRequest.Body
-               color = yellowColor
-       case api.HookIssueMilestoned:
-               title = fmt.Sprintf("[%s] Pull request milestone: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
-               text = p.PullRequest.Body
-               color = yellowColor
-       case api.HookIssueDemilestoned:
-               title = fmt.Sprintf("[%s] Pull request clear milestone: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
-               text = p.PullRequest.Body
-               color = yellowColor
-       }
-
-       return &DiscordPayload{
-               Username:  meta.Username,
-               AvatarURL: meta.IconURL,
-               Embeds: []DiscordEmbed{
-                       {
-                               Title:       title,
-                               Description: text,
-                               URL:         p.PullRequest.HTMLURL,
-                               Color:       color,
-                               Author: DiscordEmbedAuthor{
-                                       Name:    p.Sender.UserName,
-                                       URL:     setting.AppURL + p.Sender.UserName,
-                                       IconURL: p.Sender.AvatarURL,
-                               },
-                       },
-               },
-       }, nil
-}
-
-func getDiscordPullRequestApprovalPayload(p *api.PullRequestPayload, meta *DiscordMeta, event HookEventType) (*DiscordPayload, error) {
-       var text, title string
-       var color int
-       switch p.Action {
-       case api.HookIssueSynchronized:
-               action, err := parseHookPullRequestEventType(event)
-               if err != nil {
-                       return nil, err
-               }
-
-               title = fmt.Sprintf("[%s] Pull request review %s: #%d %s", p.Repository.FullName, action, p.Index, p.PullRequest.Title)
-               text = p.Review.Content
-
-               switch event {
-               case HookEventPullRequestApproved:
-                       color = greenColor
-               case HookEventPullRequestRejected:
-                       color = redColor
-               case HookEventPullRequestComment:
-                       color = greyColor
-               default:
-                       color = yellowColor
-               }
-       }
-
-       return &DiscordPayload{
-               Username:  meta.Username,
-               AvatarURL: meta.IconURL,
-               Embeds: []DiscordEmbed{
-                       {
-                               Title:       title,
-                               Description: text,
-                               URL:         p.PullRequest.HTMLURL,
-                               Color:       color,
-                               Author: DiscordEmbedAuthor{
-                                       Name:    p.Sender.UserName,
-                                       URL:     setting.AppURL + p.Sender.UserName,
-                                       IconURL: p.Sender.AvatarURL,
-                               },
-                       },
-               },
-       }, nil
-}
-
-func getDiscordRepositoryPayload(p *api.RepositoryPayload, meta *DiscordMeta) (*DiscordPayload, error) {
-       var title, url string
-       var color int
-       switch p.Action {
-       case api.HookRepoCreated:
-               title = fmt.Sprintf("[%s] Repository created", p.Repository.FullName)
-               url = p.Repository.HTMLURL
-               color = greenColor
-       case api.HookRepoDeleted:
-               title = fmt.Sprintf("[%s] Repository deleted", p.Repository.FullName)
-               color = redColor
-       }
-
-       return &DiscordPayload{
-               Username:  meta.Username,
-               AvatarURL: meta.IconURL,
-               Embeds: []DiscordEmbed{
-                       {
-                               Title: title,
-                               URL:   url,
-                               Color: color,
-                               Author: DiscordEmbedAuthor{
-                                       Name:    p.Sender.UserName,
-                                       URL:     setting.AppURL + p.Sender.UserName,
-                                       IconURL: p.Sender.AvatarURL,
-                               },
-                       },
-               },
-       }, nil
-}
-
-func getDiscordReleasePayload(p *api.ReleasePayload, meta *DiscordMeta) (*DiscordPayload, error) {
-       var title, url string
-       var color int
-       switch p.Action {
-       case api.HookReleasePublished:
-               title = fmt.Sprintf("[%s] Release created", p.Release.TagName)
-               url = p.Release.URL
-               color = greenColor
-       case api.HookReleaseUpdated:
-               title = fmt.Sprintf("[%s] Release updated", p.Release.TagName)
-               url = p.Release.URL
-               color = yellowColor
-       case api.HookReleaseDeleted:
-               title = fmt.Sprintf("[%s] Release deleted", p.Release.TagName)
-               url = p.Release.URL
-               color = redColor
-       }
-
-       return &DiscordPayload{
-               Username:  meta.Username,
-               AvatarURL: meta.IconURL,
-               Embeds: []DiscordEmbed{
-                       {
-                               Title:       title,
-                               Description: p.Release.Note,
-                               URL:         url,
-                               Color:       color,
-                               Author: DiscordEmbedAuthor{
-                                       Name:    p.Sender.UserName,
-                                       URL:     setting.AppURL + p.Sender.UserName,
-                                       IconURL: p.Sender.AvatarURL,
-                               },
-                       },
-               },
-       }, nil
-}
-
-// GetDiscordPayload converts a discord webhook into a DiscordPayload
-func GetDiscordPayload(p api.Payloader, event HookEventType, meta string) (*DiscordPayload, error) {
-       s := new(DiscordPayload)
-
-       discord := &DiscordMeta{}
-       if err := json.Unmarshal([]byte(meta), &discord); err != nil {
-               return s, errors.New("GetDiscordPayload meta json:" + err.Error())
-       }
-
-       switch event {
-       case HookEventCreate:
-               return getDiscordCreatePayload(p.(*api.CreatePayload), discord)
-       case HookEventDelete:
-               return getDiscordDeletePayload(p.(*api.DeletePayload), discord)
-       case HookEventFork:
-               return getDiscordForkPayload(p.(*api.ForkPayload), discord)
-       case HookEventIssues:
-               return getDiscordIssuesPayload(p.(*api.IssuePayload), discord)
-       case HookEventIssueComment:
-               return getDiscordIssueCommentPayload(p.(*api.IssueCommentPayload), discord)
-       case HookEventPush:
-               return getDiscordPushPayload(p.(*api.PushPayload), discord)
-       case HookEventPullRequest:
-               return getDiscordPullRequestPayload(p.(*api.PullRequestPayload), discord)
-       case HookEventPullRequestRejected, HookEventPullRequestApproved, HookEventPullRequestComment:
-               return getDiscordPullRequestApprovalPayload(p.(*api.PullRequestPayload), discord, event)
-       case HookEventRepository:
-               return getDiscordRepositoryPayload(p.(*api.RepositoryPayload), discord)
-       case HookEventRelease:
-               return getDiscordReleasePayload(p.(*api.ReleasePayload), discord)
-       }
-
-       return s, nil
-}
-
-func parseHookPullRequestEventType(event HookEventType) (string, error) {
-
-       switch event {
-
-       case HookEventPullRequestApproved:
-               return "approved", nil
-       case HookEventPullRequestRejected:
-               return "rejected", nil
-       case HookEventPullRequestComment:
-               return "comment", nil
-
-       default:
-               return "", errors.New("unknown event type")
-       }
-}
diff --git a/models/webhook_msteams.go b/models/webhook_msteams.go
deleted file mode 100644 (file)
index e8cdcca..0000000
+++ /dev/null
@@ -1,729 +0,0 @@
-// Copyright 2019 The Gitea Authors. All rights reserved.
-// Use of this source code is governed by a MIT-style
-// license that can be found in the LICENSE file.
-
-package models
-
-import (
-       "encoding/json"
-       "fmt"
-       "strings"
-
-       "code.gitea.io/gitea/modules/git"
-       api "code.gitea.io/gitea/modules/structs"
-)
-
-type (
-       // MSTeamsFact for Fact Structure
-       MSTeamsFact struct {
-               Name  string `json:"name"`
-               Value string `json:"value"`
-       }
-
-       // MSTeamsSection is a MessageCard section
-       MSTeamsSection struct {
-               ActivityTitle    string        `json:"activityTitle"`
-               ActivitySubtitle string        `json:"activitySubtitle"`
-               ActivityImage    string        `json:"activityImage"`
-               Facts            []MSTeamsFact `json:"facts"`
-               Text             string        `json:"text"`
-       }
-
-       // MSTeamsAction is an action (creates buttons, links etc)
-       MSTeamsAction struct {
-               Type    string                `json:"@type"`
-               Name    string                `json:"name"`
-               Targets []MSTeamsActionTarget `json:"targets,omitempty"`
-       }
-
-       // MSTeamsActionTarget is the actual link to follow, etc
-       MSTeamsActionTarget struct {
-               Os  string `json:"os"`
-               URI string `json:"uri"`
-       }
-
-       // MSTeamsPayload is the parent object
-       MSTeamsPayload struct {
-               Type            string           `json:"@type"`
-               Context         string           `json:"@context"`
-               ThemeColor      string           `json:"themeColor"`
-               Title           string           `json:"title"`
-               Summary         string           `json:"summary"`
-               Sections        []MSTeamsSection `json:"sections"`
-               PotentialAction []MSTeamsAction  `json:"potentialAction"`
-       }
-)
-
-// SetSecret sets the MSTeams secret
-func (p *MSTeamsPayload) SetSecret(_ string) {}
-
-// JSONPayload Marshals the MSTeamsPayload to json
-func (p *MSTeamsPayload) JSONPayload() ([]byte, error) {
-       data, err := json.MarshalIndent(p, "", "  ")
-       if err != nil {
-               return []byte{}, err
-       }
-       return data, nil
-}
-
-func getMSTeamsCreatePayload(p *api.CreatePayload) (*MSTeamsPayload, error) {
-       // created tag/branch
-       refName := git.RefEndName(p.Ref)
-       title := fmt.Sprintf("[%s] %s %s created", p.Repo.FullName, p.RefType, refName)
-
-       return &MSTeamsPayload{
-               Type:       "MessageCard",
-               Context:    "https://schema.org/extensions",
-               ThemeColor: fmt.Sprintf("%x", greenColor),
-               Title:      title,
-               Summary:    title,
-               Sections: []MSTeamsSection{
-                       {
-                               ActivityTitle:    p.Sender.FullName,
-                               ActivitySubtitle: p.Sender.UserName,
-                               ActivityImage:    p.Sender.AvatarURL,
-                               Facts: []MSTeamsFact{
-                                       {
-                                               Name:  "Repository:",
-                                               Value: p.Repo.FullName,
-                                       },
-                                       {
-                                               Name:  fmt.Sprintf("%s:", p.RefType),
-                                               Value: refName,
-                                       },
-                               },
-                       },
-               },
-               PotentialAction: []MSTeamsAction{
-                       {
-                               Type: "OpenUri",
-                               Name: "View in Gitea",
-                               Targets: []MSTeamsActionTarget{
-                                       {
-                                               Os:  "default",
-                                               URI: p.Repo.HTMLURL + "/src/" + refName,
-                                       },
-                               },
-                       },
-               },
-       }, nil
-}
-
-func getMSTeamsDeletePayload(p *api.DeletePayload) (*MSTeamsPayload, error) {
-       // deleted tag/branch
-       refName := git.RefEndName(p.Ref)
-       title := fmt.Sprintf("[%s] %s %s deleted", p.Repo.FullName, p.RefType, refName)
-
-       return &MSTeamsPayload{
-               Type:       "MessageCard",
-               Context:    "https://schema.org/extensions",
-               ThemeColor: fmt.Sprintf("%x", yellowColor),
-               Title:      title,
-               Summary:    title,
-               Sections: []MSTeamsSection{
-                       {
-                               ActivityTitle:    p.Sender.FullName,
-                               ActivitySubtitle: p.Sender.UserName,
-                               ActivityImage:    p.Sender.AvatarURL,
-                               Facts: []MSTeamsFact{
-                                       {
-                                               Name:  "Repository:",
-                                               Value: p.Repo.FullName,
-                                       },
-                                       {
-                                               Name:  fmt.Sprintf("%s:", p.RefType),
-                                               Value: refName,
-                                       },
-                               },
-                       },
-               },
-               PotentialAction: []MSTeamsAction{
-                       {
-                               Type: "OpenUri",
-                               Name: "View in Gitea",
-                               Targets: []MSTeamsActionTarget{
-                                       {
-                                               Os:  "default",
-                                               URI: p.Repo.HTMLURL + "/src/" + refName,
-                                       },
-                               },
-                       },
-               },
-       }, nil
-}
-
-func getMSTeamsForkPayload(p *api.ForkPayload) (*MSTeamsPayload, error) {
-       // fork
-       title := fmt.Sprintf("%s is forked to %s", p.Forkee.FullName, p.Repo.FullName)
-
-       return &MSTeamsPayload{
-               Type:       "MessageCard",
-               Context:    "https://schema.org/extensions",
-               ThemeColor: fmt.Sprintf("%x", greenColor),
-               Title:      title,
-               Summary:    title,
-               Sections: []MSTeamsSection{
-                       {
-                               ActivityTitle:    p.Sender.FullName,
-                               ActivitySubtitle: p.Sender.UserName,
-                               ActivityImage:    p.Sender.AvatarURL,
-                               Facts: []MSTeamsFact{
-                                       {
-                                               Name:  "Forkee:",
-                                               Value: p.Forkee.FullName,
-                                       },
-                                       {
-                                               Name:  "Repository:",
-                                               Value: p.Repo.FullName,
-                                       },
-                               },
-                       },
-               },
-               PotentialAction: []MSTeamsAction{
-                       {
-                               Type: "OpenUri",
-                               Name: "View in Gitea",
-                               Targets: []MSTeamsActionTarget{
-                                       {
-                                               Os:  "default",
-                                               URI: p.Repo.HTMLURL,
-                                       },
-                               },
-                       },
-               },
-       }, nil
-}
-
-func getMSTeamsPushPayload(p *api.PushPayload) (*MSTeamsPayload, error) {
-       var (
-               branchName = git.RefEndName(p.Ref)
-               commitDesc string
-       )
-
-       var titleLink string
-       if len(p.Commits) == 1 {
-               commitDesc = "1 new commit"
-               titleLink = p.Commits[0].URL
-       } else {
-               commitDesc = fmt.Sprintf("%d new commits", len(p.Commits))
-               titleLink = p.CompareURL
-       }
-       if titleLink == "" {
-               titleLink = p.Repo.HTMLURL + "/src/" + branchName
-       }
-
-       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 {
-               text += fmt.Sprintf("[%s](%s) %s - %s", commit.ID[:7], commit.URL,
-                       strings.TrimRight(commit.Message, "\r\n"), commit.Author.Name)
-               // add linebreak to each commit but the last
-               if i < len(p.Commits)-1 {
-                       text += "\n"
-               }
-       }
-
-       return &MSTeamsPayload{
-               Type:       "MessageCard",
-               Context:    "https://schema.org/extensions",
-               ThemeColor: fmt.Sprintf("%x", greenColor),
-               Title:      title,
-               Summary:    title,
-               Sections: []MSTeamsSection{
-                       {
-                               ActivityTitle:    p.Sender.FullName,
-                               ActivitySubtitle: p.Sender.UserName,
-                               ActivityImage:    p.Sender.AvatarURL,
-                               Text:             text,
-                               Facts: []MSTeamsFact{
-                                       {
-                                               Name:  "Repository:",
-                                               Value: p.Repo.FullName,
-                                       },
-                                       {
-                                               Name:  "Commit count:",
-                                               Value: fmt.Sprintf("%d", len(p.Commits)),
-                                       },
-                               },
-                       },
-               },
-               PotentialAction: []MSTeamsAction{
-                       {
-                               Type: "OpenUri",
-                               Name: "View in Gitea",
-                               Targets: []MSTeamsActionTarget{
-                                       {
-                                               Os:  "default",
-                                               URI: titleLink,
-                                       },
-                               },
-                       },
-               },
-       }, nil
-}
-
-func getMSTeamsIssuesPayload(p *api.IssuePayload) (*MSTeamsPayload, error) {
-       var text, title string
-       var color int
-       url := fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Issue.Index)
-       switch p.Action {
-       case api.HookIssueOpened:
-               title = fmt.Sprintf("[%s] Issue opened: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
-               text = p.Issue.Body
-               color = orangeColor
-       case api.HookIssueClosed:
-               title = fmt.Sprintf("[%s] Issue closed: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
-               color = redColor
-               text = p.Issue.Body
-       case api.HookIssueReOpened:
-               title = fmt.Sprintf("[%s] Issue re-opened: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
-               text = p.Issue.Body
-               color = yellowColor
-       case api.HookIssueEdited:
-               title = fmt.Sprintf("[%s] Issue edited: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
-               text = p.Issue.Body
-               color = yellowColor
-       case api.HookIssueAssigned:
-               title = fmt.Sprintf("[%s] Issue assigned to %s: #%d %s", p.Repository.FullName,
-                       p.Issue.Assignee.UserName, p.Index, p.Issue.Title)
-               text = p.Issue.Body
-               color = greenColor
-       case api.HookIssueUnassigned:
-               title = fmt.Sprintf("[%s] Issue unassigned: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
-               text = p.Issue.Body
-               color = yellowColor
-       case api.HookIssueLabelUpdated:
-               title = fmt.Sprintf("[%s] Issue labels updated: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
-               text = p.Issue.Body
-               color = yellowColor
-       case api.HookIssueLabelCleared:
-               title = fmt.Sprintf("[%s] Issue labels cleared: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
-               text = p.Issue.Body
-               color = yellowColor
-       case api.HookIssueSynchronized:
-               title = fmt.Sprintf("[%s] Issue synchronized: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
-               text = p.Issue.Body
-               color = yellowColor
-       case api.HookIssueMilestoned:
-               title = fmt.Sprintf("[%s] Issue milestone: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
-               text = p.Issue.Body
-               color = yellowColor
-       case api.HookIssueDemilestoned:
-               title = fmt.Sprintf("[%s] Issue clear milestone: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
-               text = p.Issue.Body
-               color = yellowColor
-       }
-
-       return &MSTeamsPayload{
-               Type:       "MessageCard",
-               Context:    "https://schema.org/extensions",
-               ThemeColor: fmt.Sprintf("%x", color),
-               Title:      title,
-               Summary:    title,
-               Sections: []MSTeamsSection{
-                       {
-                               ActivityTitle:    p.Sender.FullName,
-                               ActivitySubtitle: p.Sender.UserName,
-                               ActivityImage:    p.Sender.AvatarURL,
-                               Text:             text,
-                               Facts: []MSTeamsFact{
-                                       {
-                                               Name:  "Repository:",
-                                               Value: p.Repository.FullName,
-                                       },
-                                       {
-                                               Name:  "Issue #:",
-                                               Value: fmt.Sprintf("%d", p.Issue.ID),
-                                       },
-                               },
-                       },
-               },
-               PotentialAction: []MSTeamsAction{
-                       {
-                               Type: "OpenUri",
-                               Name: "View in Gitea",
-                               Targets: []MSTeamsActionTarget{
-                                       {
-                                               Os:  "default",
-                                               URI: url,
-                                       },
-                               },
-                       },
-               },
-       }, nil
-}
-
-func getMSTeamsIssueCommentPayload(p *api.IssueCommentPayload) (*MSTeamsPayload, error) {
-       title := fmt.Sprintf("#%d: %s", p.Issue.Index, p.Issue.Title)
-       url := fmt.Sprintf("%s/issues/%d#%s", p.Repository.HTMLURL, p.Issue.Index, CommentHashTag(p.Comment.ID))
-       content := ""
-       var color int
-       switch p.Action {
-       case api.HookIssueCommentCreated:
-               if p.IsPull {
-                       title = "New comment on pull request " + title
-                       color = greenColorLight
-               } else {
-                       title = "New comment on issue " + title
-                       color = orangeColorLight
-               }
-               content = p.Comment.Body
-       case api.HookIssueCommentEdited:
-               if p.IsPull {
-                       title = "Comment edited on pull request " + title
-               } else {
-                       title = "Comment edited on issue " + title
-               }
-               content = p.Comment.Body
-               color = yellowColor
-       case api.HookIssueCommentDeleted:
-               if p.IsPull {
-                       title = "Comment deleted on pull request " + title
-               } else {
-                       title = "Comment deleted on issue " + title
-               }
-               url = fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Issue.Index)
-               content = p.Comment.Body
-               color = redColor
-       }
-
-       title = fmt.Sprintf("[%s] %s", p.Repository.FullName, title)
-
-       return &MSTeamsPayload{
-               Type:       "MessageCard",
-               Context:    "https://schema.org/extensions",
-               ThemeColor: fmt.Sprintf("%x", color),
-               Title:      title,
-               Summary:    title,
-               Sections: []MSTeamsSection{
-                       {
-                               ActivityTitle:    p.Sender.FullName,
-                               ActivitySubtitle: p.Sender.UserName,
-                               ActivityImage:    p.Sender.AvatarURL,
-                               Text:             content,
-                               Facts: []MSTeamsFact{
-                                       {
-                                               Name:  "Repository:",
-                                               Value: p.Repository.FullName,
-                                       },
-                                       {
-                                               Name:  "Issue #:",
-                                               Value: fmt.Sprintf("%d", p.Issue.ID),
-                                       },
-                               },
-                       },
-               },
-               PotentialAction: []MSTeamsAction{
-                       {
-                               Type: "OpenUri",
-                               Name: "View in Gitea",
-                               Targets: []MSTeamsActionTarget{
-                                       {
-                                               Os:  "default",
-                                               URI: url,
-                                       },
-                               },
-                       },
-               },
-       }, nil
-}
-
-func getMSTeamsPullRequestPayload(p *api.PullRequestPayload) (*MSTeamsPayload, error) {
-       var text, title string
-       var color int
-       switch p.Action {
-       case api.HookIssueOpened:
-               title = fmt.Sprintf("[%s] Pull request opened: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
-               text = p.PullRequest.Body
-               color = greenColor
-       case api.HookIssueClosed:
-               if p.PullRequest.HasMerged {
-                       title = fmt.Sprintf("[%s] Pull request merged: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
-                       color = purpleColor
-               } else {
-                       title = fmt.Sprintf("[%s] Pull request closed: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
-                       color = redColor
-               }
-               text = p.PullRequest.Body
-       case api.HookIssueReOpened:
-               title = fmt.Sprintf("[%s] Pull request re-opened: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
-               text = p.PullRequest.Body
-               color = yellowColor
-       case api.HookIssueEdited:
-               title = fmt.Sprintf("[%s] Pull request edited: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
-               text = p.PullRequest.Body
-               color = yellowColor
-       case api.HookIssueAssigned:
-               list := make([]string, len(p.PullRequest.Assignees))
-               for i, user := range p.PullRequest.Assignees {
-                       list[i] = user.UserName
-               }
-               title = fmt.Sprintf("[%s] Pull request assigned to %s: #%d by %s", p.Repository.FullName,
-                       strings.Join(list, ", "),
-                       p.Index, p.PullRequest.Title)
-               text = p.PullRequest.Body
-               color = greenColor
-       case api.HookIssueUnassigned:
-               title = fmt.Sprintf("[%s] Pull request unassigned: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
-               text = p.PullRequest.Body
-               color = yellowColor
-       case api.HookIssueLabelUpdated:
-               title = fmt.Sprintf("[%s] Pull request labels updated: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
-               text = p.PullRequest.Body
-               color = yellowColor
-       case api.HookIssueLabelCleared:
-               title = fmt.Sprintf("[%s] Pull request labels cleared: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
-               text = p.PullRequest.Body
-               color = yellowColor
-       case api.HookIssueSynchronized:
-               title = fmt.Sprintf("[%s] Pull request synchronized: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
-               text = p.PullRequest.Body
-               color = yellowColor
-       case api.HookIssueMilestoned:
-               title = fmt.Sprintf("[%s] Pull request milestone: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
-               text = p.PullRequest.Body
-               color = yellowColor
-       case api.HookIssueDemilestoned:
-               title = fmt.Sprintf("[%s] Pull request clear milestone: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
-               text = p.PullRequest.Body
-               color = yellowColor
-       }
-
-       return &MSTeamsPayload{
-               Type:       "MessageCard",
-               Context:    "https://schema.org/extensions",
-               ThemeColor: fmt.Sprintf("%x", color),
-               Title:      title,
-               Summary:    title,
-               Sections: []MSTeamsSection{
-                       {
-                               ActivityTitle:    p.Sender.FullName,
-                               ActivitySubtitle: p.Sender.UserName,
-                               ActivityImage:    p.Sender.AvatarURL,
-                               Text:             text,
-                               Facts: []MSTeamsFact{
-                                       {
-                                               Name:  "Repository:",
-                                               Value: p.Repository.FullName,
-                                       },
-                                       {
-                                               Name:  "Pull request #:",
-                                               Value: fmt.Sprintf("%d", p.PullRequest.ID),
-                                       },
-                               },
-                       },
-               },
-               PotentialAction: []MSTeamsAction{
-                       {
-                               Type: "OpenUri",
-                               Name: "View in Gitea",
-                               Targets: []MSTeamsActionTarget{
-                                       {
-                                               Os:  "default",
-                                               URI: p.PullRequest.HTMLURL,
-                                       },
-                               },
-                       },
-               },
-       }, nil
-}
-
-func getMSTeamsPullRequestApprovalPayload(p *api.PullRequestPayload, event HookEventType) (*MSTeamsPayload, error) {
-       var text, title string
-       var color int
-       switch p.Action {
-       case api.HookIssueSynchronized:
-               action, err := parseHookPullRequestEventType(event)
-               if err != nil {
-                       return nil, err
-               }
-
-               title = fmt.Sprintf("[%s] Pull request review %s: #%d %s", p.Repository.FullName, action, p.Index, p.PullRequest.Title)
-               text = p.Review.Content
-
-               switch event {
-               case HookEventPullRequestApproved:
-                       color = greenColor
-               case HookEventPullRequestRejected:
-                       color = redColor
-               case HookEventPullRequestComment:
-                       color = greyColor
-               default:
-                       color = yellowColor
-               }
-       }
-
-       return &MSTeamsPayload{
-               Type:       "MessageCard",
-               Context:    "https://schema.org/extensions",
-               ThemeColor: fmt.Sprintf("%x", color),
-               Title:      title,
-               Summary:    title,
-               Sections: []MSTeamsSection{
-                       {
-                               ActivityTitle:    p.Sender.FullName,
-                               ActivitySubtitle: p.Sender.UserName,
-                               ActivityImage:    p.Sender.AvatarURL,
-                               Text:             text,
-                               Facts: []MSTeamsFact{
-                                       {
-                                               Name:  "Repository:",
-                                               Value: p.Repository.FullName,
-                                       },
-                                       {
-                                               Name:  "Pull request #:",
-                                               Value: fmt.Sprintf("%d", p.PullRequest.ID),
-                                       },
-                               },
-                       },
-               },
-               PotentialAction: []MSTeamsAction{
-                       {
-                               Type: "OpenUri",
-                               Name: "View in Gitea",
-                               Targets: []MSTeamsActionTarget{
-                                       {
-                                               Os:  "default",
-                                               URI: p.PullRequest.HTMLURL,
-                                       },
-                               },
-                       },
-               },
-       }, nil
-}
-
-func getMSTeamsRepositoryPayload(p *api.RepositoryPayload) (*MSTeamsPayload, error) {
-       var title, url string
-       var color int
-       switch p.Action {
-       case api.HookRepoCreated:
-               title = fmt.Sprintf("[%s] Repository created", p.Repository.FullName)
-               url = p.Repository.HTMLURL
-               color = greenColor
-       case api.HookRepoDeleted:
-               title = fmt.Sprintf("[%s] Repository deleted", p.Repository.FullName)
-               color = yellowColor
-       }
-
-       return &MSTeamsPayload{
-               Type:       "MessageCard",
-               Context:    "https://schema.org/extensions",
-               ThemeColor: fmt.Sprintf("%x", color),
-               Title:      title,
-               Summary:    title,
-               Sections: []MSTeamsSection{
-                       {
-                               ActivityTitle:    p.Sender.FullName,
-                               ActivitySubtitle: p.Sender.UserName,
-                               ActivityImage:    p.Sender.AvatarURL,
-                               Facts: []MSTeamsFact{
-                                       {
-                                               Name:  "Repository:",
-                                               Value: p.Repository.FullName,
-                                       },
-                               },
-                       },
-               },
-               PotentialAction: []MSTeamsAction{
-                       {
-                               Type: "OpenUri",
-                               Name: "View in Gitea",
-                               Targets: []MSTeamsActionTarget{
-                                       {
-                                               Os:  "default",
-                                               URI: url,
-                                       },
-                               },
-                       },
-               },
-       }, nil
-}
-
-func getMSTeamsReleasePayload(p *api.ReleasePayload) (*MSTeamsPayload, error) {
-       var title, url string
-       var color int
-       switch p.Action {
-       case api.HookReleasePublished:
-               title = fmt.Sprintf("[%s] Release created", p.Release.TagName)
-               url = p.Release.URL
-               color = greenColor
-       case api.HookReleaseUpdated:
-               title = fmt.Sprintf("[%s] Release updated", p.Release.TagName)
-               url = p.Release.URL
-               color = greenColor
-       case api.HookReleaseDeleted:
-               title = fmt.Sprintf("[%s] Release deleted", p.Release.TagName)
-               url = p.Release.URL
-               color = greenColor
-       }
-
-       return &MSTeamsPayload{
-               Type:       "MessageCard",
-               Context:    "https://schema.org/extensions",
-               ThemeColor: fmt.Sprintf("%x", color),
-               Title:      title,
-               Summary:    title,
-               Sections: []MSTeamsSection{
-                       {
-                               ActivityTitle:    p.Sender.FullName,
-                               ActivitySubtitle: p.Sender.UserName,
-                               ActivityImage:    p.Sender.AvatarURL,
-                               Text:             p.Release.Note,
-                               Facts: []MSTeamsFact{
-                                       {
-                                               Name:  "Repository:",
-                                               Value: p.Repository.FullName,
-                                       },
-                                       {
-                                               Name:  "Tag:",
-                                               Value: p.Release.TagName,
-                                       },
-                               },
-                       },
-               },
-               PotentialAction: []MSTeamsAction{
-                       {
-                               Type: "OpenUri",
-                               Name: "View in Gitea",
-                               Targets: []MSTeamsActionTarget{
-                                       {
-                                               Os:  "default",
-                                               URI: url,
-                                       },
-                               },
-                       },
-               },
-       }, nil
-}
-
-// GetMSTeamsPayload converts a MSTeams webhook into a MSTeamsPayload
-func GetMSTeamsPayload(p api.Payloader, event HookEventType, meta string) (*MSTeamsPayload, error) {
-       s := new(MSTeamsPayload)
-
-       switch event {
-       case HookEventCreate:
-               return getMSTeamsCreatePayload(p.(*api.CreatePayload))
-       case HookEventDelete:
-               return getMSTeamsDeletePayload(p.(*api.DeletePayload))
-       case HookEventFork:
-               return getMSTeamsForkPayload(p.(*api.ForkPayload))
-       case HookEventIssues:
-               return getMSTeamsIssuesPayload(p.(*api.IssuePayload))
-       case HookEventIssueComment:
-               return getMSTeamsIssueCommentPayload(p.(*api.IssueCommentPayload))
-       case HookEventPush:
-               return getMSTeamsPushPayload(p.(*api.PushPayload))
-       case HookEventPullRequest:
-               return getMSTeamsPullRequestPayload(p.(*api.PullRequestPayload))
-       case HookEventPullRequestRejected, HookEventPullRequestApproved, HookEventPullRequestComment:
-               return getMSTeamsPullRequestApprovalPayload(p.(*api.PullRequestPayload), event)
-       case HookEventRepository:
-               return getMSTeamsRepositoryPayload(p.(*api.RepositoryPayload))
-       case HookEventRelease:
-               return getMSTeamsReleasePayload(p.(*api.ReleasePayload))
-       }
-
-       return s, nil
-}
diff --git a/models/webhook_slack.go b/models/webhook_slack.go
deleted file mode 100644 (file)
index 9c179bb..0000000
+++ /dev/null
@@ -1,423 +0,0 @@
-// Copyright 2014 The Gogs Authors. All rights reserved.
-// Use of this source code is governed by a MIT-style
-// license that can be found in the LICENSE file.
-
-package models
-
-import (
-       "encoding/json"
-       "errors"
-       "fmt"
-       "strings"
-
-       "code.gitea.io/gitea/modules/git"
-       "code.gitea.io/gitea/modules/setting"
-       api "code.gitea.io/gitea/modules/structs"
-)
-
-// SlackMeta contains the slack metadata
-type SlackMeta struct {
-       Channel  string `json:"channel"`
-       Username string `json:"username"`
-       IconURL  string `json:"icon_url"`
-       Color    string `json:"color"`
-}
-
-// SlackPayload contains the information about the slack channel
-type SlackPayload struct {
-       Channel     string            `json:"channel"`
-       Text        string            `json:"text"`
-       Username    string            `json:"username"`
-       IconURL     string            `json:"icon_url"`
-       UnfurlLinks int               `json:"unfurl_links"`
-       LinkNames   int               `json:"link_names"`
-       Attachments []SlackAttachment `json:"attachments"`
-}
-
-// SlackAttachment contains the slack message
-type SlackAttachment struct {
-       Fallback string `json:"fallback"`
-       Color    string `json:"color"`
-       Title    string `json:"title"`
-       Text     string `json:"text"`
-}
-
-// SetSecret sets the slack secret
-func (p *SlackPayload) SetSecret(_ string) {}
-
-// JSONPayload Marshals the SlackPayload to json
-func (p *SlackPayload) JSONPayload() ([]byte, error) {
-       data, err := json.MarshalIndent(p, "", "  ")
-       if err != nil {
-               return []byte{}, err
-       }
-       return data, nil
-}
-
-// SlackTextFormatter replaces &, <, > with HTML characters
-// see: https://api.slack.com/docs/formatting
-func SlackTextFormatter(s string) string {
-       // replace & < >
-       s = strings.Replace(s, "&", "&amp;", -1)
-       s = strings.Replace(s, "<", "&lt;", -1)
-       s = strings.Replace(s, ">", "&gt;", -1)
-       return s
-}
-
-// SlackShortTextFormatter replaces &, <, > with HTML characters
-func SlackShortTextFormatter(s string) string {
-       s = strings.Split(s, "\n")[0]
-       // replace & < >
-       s = strings.Replace(s, "&", "&amp;", -1)
-       s = strings.Replace(s, "<", "&lt;", -1)
-       s = strings.Replace(s, ">", "&gt;", -1)
-       return s
-}
-
-// SlackLinkFormatter creates a link compatible with slack
-func SlackLinkFormatter(url string, text string) string {
-       return fmt.Sprintf("<%s|%s>", url, SlackTextFormatter(text))
-}
-
-// SlackLinkToRef slack-formatter link to a repo ref
-func SlackLinkToRef(repoURL, ref string) string {
-       refName := git.RefEndName(ref)
-       switch {
-       case strings.HasPrefix(ref, git.BranchPrefix):
-               return SlackLinkFormatter(repoURL+"/src/branch/"+refName, refName)
-       case strings.HasPrefix(ref, git.TagPrefix):
-               return SlackLinkFormatter(repoURL+"/src/tag/"+refName, refName)
-       default:
-               return SlackLinkFormatter(repoURL+"/src/commit/"+refName, refName)
-       }
-}
-
-func getSlackCreatePayload(p *api.CreatePayload, slack *SlackMeta) (*SlackPayload, error) {
-       repoLink := SlackLinkFormatter(p.Repo.HTMLURL, p.Repo.Name)
-       refLink := SlackLinkToRef(p.Repo.HTMLURL, p.Ref)
-       text := fmt.Sprintf("[%s:%s] %s created by %s", repoLink, refLink, p.RefType, p.Sender.UserName)
-
-       return &SlackPayload{
-               Channel:  slack.Channel,
-               Text:     text,
-               Username: slack.Username,
-               IconURL:  slack.IconURL,
-       }, nil
-}
-
-// getSlackDeletePayload composes Slack payload for delete a branch or tag.
-func getSlackDeletePayload(p *api.DeletePayload, slack *SlackMeta) (*SlackPayload, error) {
-       refName := git.RefEndName(p.Ref)
-       repoLink := SlackLinkFormatter(p.Repo.HTMLURL, p.Repo.Name)
-       text := fmt.Sprintf("[%s:%s] %s deleted by %s", repoLink, refName, p.RefType, p.Sender.UserName)
-       return &SlackPayload{
-               Channel:  slack.Channel,
-               Text:     text,
-               Username: slack.Username,
-               IconURL:  slack.IconURL,
-       }, nil
-}
-
-// getSlackForkPayload composes Slack payload for forked by a repository.
-func getSlackForkPayload(p *api.ForkPayload, slack *SlackMeta) (*SlackPayload, error) {
-       baseLink := SlackLinkFormatter(p.Forkee.HTMLURL, p.Forkee.FullName)
-       forkLink := SlackLinkFormatter(p.Repo.HTMLURL, p.Repo.FullName)
-       text := fmt.Sprintf("%s is forked to %s", baseLink, forkLink)
-       return &SlackPayload{
-               Channel:  slack.Channel,
-               Text:     text,
-               Username: slack.Username,
-               IconURL:  slack.IconURL,
-       }, nil
-}
-
-func getSlackIssuesPayload(p *api.IssuePayload, slack *SlackMeta) (*SlackPayload, error) {
-       senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
-       titleLink := SlackLinkFormatter(fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index),
-               fmt.Sprintf("#%d %s", p.Index, p.Issue.Title))
-       var text, title, attachmentText string
-       switch p.Action {
-       case api.HookIssueOpened:
-               text = fmt.Sprintf("[%s] Issue submitted by %s", p.Repository.FullName, senderLink)
-               title = titleLink
-               attachmentText = SlackTextFormatter(p.Issue.Body)
-       case api.HookIssueClosed:
-               text = fmt.Sprintf("[%s] Issue closed: %s by %s", p.Repository.FullName, titleLink, senderLink)
-       case api.HookIssueReOpened:
-               text = fmt.Sprintf("[%s] Issue re-opened: %s by %s", p.Repository.FullName, titleLink, senderLink)
-       case api.HookIssueEdited:
-               text = fmt.Sprintf("[%s] Issue edited: %s by %s", p.Repository.FullName, titleLink, senderLink)
-               attachmentText = SlackTextFormatter(p.Issue.Body)
-       case api.HookIssueAssigned:
-               text = fmt.Sprintf("[%s] Issue assigned to %s: %s by %s", p.Repository.FullName,
-                       SlackLinkFormatter(setting.AppURL+p.Issue.Assignee.UserName, p.Issue.Assignee.UserName),
-                       titleLink, senderLink)
-       case api.HookIssueUnassigned:
-               text = fmt.Sprintf("[%s] Issue unassigned: %s by %s", p.Repository.FullName, titleLink, senderLink)
-       case api.HookIssueLabelUpdated:
-               text = fmt.Sprintf("[%s] Issue labels updated: %s by %s", p.Repository.FullName, titleLink, senderLink)
-       case api.HookIssueLabelCleared:
-               text = fmt.Sprintf("[%s] Issue labels cleared: %s by %s", p.Repository.FullName, titleLink, senderLink)
-       case api.HookIssueSynchronized:
-               text = fmt.Sprintf("[%s] Issue synchronized: %s by %s", p.Repository.FullName, titleLink, senderLink)
-       case api.HookIssueMilestoned:
-               text = fmt.Sprintf("[%s] Issue milestoned: #%s %s", p.Repository.FullName, titleLink, senderLink)
-       case api.HookIssueDemilestoned:
-               text = fmt.Sprintf("[%s] Issue milestone cleared: #%s %s", p.Repository.FullName, titleLink, senderLink)
-       }
-
-       return &SlackPayload{
-               Channel:  slack.Channel,
-               Text:     text,
-               Username: slack.Username,
-               IconURL:  slack.IconURL,
-               Attachments: []SlackAttachment{{
-                       Color: slack.Color,
-                       Title: title,
-                       Text:  attachmentText,
-               }},
-       }, nil
-}
-
-func getSlackIssueCommentPayload(p *api.IssueCommentPayload, slack *SlackMeta) (*SlackPayload, error) {
-       senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
-       titleLink := SlackLinkFormatter(fmt.Sprintf("%s/issues/%d#%s", p.Repository.HTMLURL, p.Issue.Index, CommentHashTag(p.Comment.ID)),
-               fmt.Sprintf("#%d %s", p.Issue.Index, p.Issue.Title))
-       var text, title, attachmentText string
-       switch p.Action {
-       case api.HookIssueCommentCreated:
-               text = fmt.Sprintf("[%s] New comment created by %s", p.Repository.FullName, senderLink)
-               title = titleLink
-               attachmentText = SlackTextFormatter(p.Comment.Body)
-       case api.HookIssueCommentEdited:
-               text = fmt.Sprintf("[%s] Comment edited by %s", p.Repository.FullName, senderLink)
-               title = titleLink
-               attachmentText = SlackTextFormatter(p.Comment.Body)
-       case api.HookIssueCommentDeleted:
-               text = fmt.Sprintf("[%s] Comment deleted by %s", p.Repository.FullName, senderLink)
-               title = SlackLinkFormatter(fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Issue.Index),
-                       fmt.Sprintf("#%d %s", p.Issue.Index, p.Issue.Title))
-               attachmentText = SlackTextFormatter(p.Comment.Body)
-       }
-
-       return &SlackPayload{
-               Channel:  slack.Channel,
-               Text:     text,
-               Username: slack.Username,
-               IconURL:  slack.IconURL,
-               Attachments: []SlackAttachment{{
-                       Color: slack.Color,
-                       Title: title,
-                       Text:  attachmentText,
-               }},
-       }, nil
-}
-
-func getSlackReleasePayload(p *api.ReleasePayload, slack *SlackMeta) (*SlackPayload, error) {
-       repoLink := SlackLinkFormatter(p.Repository.HTMLURL, p.Repository.Name)
-       refLink := SlackLinkFormatter(p.Repository.HTMLURL+"/src/"+p.Release.TagName, p.Release.TagName)
-       var text string
-
-       switch p.Action {
-       case api.HookReleasePublished:
-               text = fmt.Sprintf("[%s] new release %s published by %s", repoLink, refLink, p.Sender.UserName)
-       case api.HookReleaseUpdated:
-               text = fmt.Sprintf("[%s] new release %s updated by %s", repoLink, refLink, p.Sender.UserName)
-       case api.HookReleaseDeleted:
-               text = fmt.Sprintf("[%s] new release %s deleted by %s", repoLink, refLink, p.Sender.UserName)
-       }
-
-       return &SlackPayload{
-               Channel:  slack.Channel,
-               Text:     text,
-               Username: slack.Username,
-               IconURL:  slack.IconURL,
-       }, nil
-}
-
-func getSlackPushPayload(p *api.PushPayload, slack *SlackMeta) (*SlackPayload, error) {
-       // n new commits
-       var (
-               commitDesc   string
-               commitString string
-       )
-
-       if len(p.Commits) == 1 {
-               commitDesc = "1 new commit"
-       } else {
-               commitDesc = fmt.Sprintf("%d new commits", len(p.Commits))
-       }
-       if len(p.CompareURL) > 0 {
-               commitString = SlackLinkFormatter(p.CompareURL, commitDesc)
-       } else {
-               commitString = commitDesc
-       }
-
-       repoLink := SlackLinkFormatter(p.Repo.HTMLURL, p.Repo.Name)
-       branchLink := SlackLinkToRef(p.Repo.HTMLURL, p.Ref)
-       text := fmt.Sprintf("[%s:%s] %s pushed by %s", repoLink, branchLink, commitString, p.Pusher.UserName)
-
-       var attachmentText string
-       // for each commit, generate attachment text
-       for i, commit := range p.Commits {
-               attachmentText += fmt.Sprintf("%s: %s - %s", SlackLinkFormatter(commit.URL, commit.ID[:7]), SlackShortTextFormatter(commit.Message), SlackTextFormatter(commit.Author.Name))
-               // add linebreak to each commit but the last
-               if i < len(p.Commits)-1 {
-                       attachmentText += "\n"
-               }
-       }
-
-       return &SlackPayload{
-               Channel:  slack.Channel,
-               Text:     text,
-               Username: slack.Username,
-               IconURL:  slack.IconURL,
-               Attachments: []SlackAttachment{{
-                       Color: slack.Color,
-                       Text:  attachmentText,
-               }},
-       }, nil
-}
-
-func getSlackPullRequestPayload(p *api.PullRequestPayload, slack *SlackMeta) (*SlackPayload, error) {
-       senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
-       titleLink := SlackLinkFormatter(fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index),
-               fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title))
-       var text, title, attachmentText string
-       switch p.Action {
-       case api.HookIssueOpened:
-               text = fmt.Sprintf("[%s] Pull request submitted by %s", p.Repository.FullName, senderLink)
-               title = titleLink
-               attachmentText = SlackTextFormatter(p.PullRequest.Body)
-       case api.HookIssueClosed:
-               if p.PullRequest.HasMerged {
-                       text = fmt.Sprintf("[%s] Pull request merged: %s by %s", p.Repository.FullName, titleLink, senderLink)
-               } else {
-                       text = fmt.Sprintf("[%s] Pull request closed: %s by %s", p.Repository.FullName, titleLink, senderLink)
-               }
-       case api.HookIssueReOpened:
-               text = fmt.Sprintf("[%s] Pull request re-opened: %s by %s", p.Repository.FullName, titleLink, senderLink)
-       case api.HookIssueEdited:
-               text = fmt.Sprintf("[%s] Pull request edited: %s by %s", p.Repository.FullName, titleLink, senderLink)
-               attachmentText = SlackTextFormatter(p.PullRequest.Body)
-       case api.HookIssueAssigned:
-               list := make([]string, len(p.PullRequest.Assignees))
-               for i, user := range p.PullRequest.Assignees {
-                       list[i] = SlackLinkFormatter(setting.AppURL+user.UserName, user.UserName)
-               }
-               text = fmt.Sprintf("[%s] Pull request assigned to %s: %s by %s", p.Repository.FullName,
-                       strings.Join(list, ", "),
-                       titleLink, senderLink)
-       case api.HookIssueUnassigned:
-               text = fmt.Sprintf("[%s] Pull request unassigned: %s by %s", p.Repository.FullName, titleLink, senderLink)
-       case api.HookIssueLabelUpdated:
-               text = fmt.Sprintf("[%s] Pull request labels updated: %s by %s", p.Repository.FullName, titleLink, senderLink)
-       case api.HookIssueLabelCleared:
-               text = fmt.Sprintf("[%s] Pull request labels cleared: %s by %s", p.Repository.FullName, titleLink, senderLink)
-       case api.HookIssueSynchronized:
-               text = fmt.Sprintf("[%s] Pull request synchronized: %s by %s", p.Repository.FullName, titleLink, senderLink)
-       case api.HookIssueMilestoned:
-               text = fmt.Sprintf("[%s] Pull request milestoned: #%s %s", p.Repository.FullName, titleLink, senderLink)
-       case api.HookIssueDemilestoned:
-               text = fmt.Sprintf("[%s] Pull request milestone cleared: #%s %s", p.Repository.FullName, titleLink, senderLink)
-       }
-
-       return &SlackPayload{
-               Channel:  slack.Channel,
-               Text:     text,
-               Username: slack.Username,
-               IconURL:  slack.IconURL,
-               Attachments: []SlackAttachment{{
-                       Color: slack.Color,
-                       Title: title,
-                       Text:  attachmentText,
-               }},
-       }, nil
-}
-
-func getSlackPullRequestApprovalPayload(p *api.PullRequestPayload, slack *SlackMeta, event HookEventType) (*SlackPayload, error) {
-       senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
-       titleLink := SlackLinkFormatter(fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index),
-               fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title))
-       var text, title, attachmentText string
-       switch p.Action {
-       case api.HookIssueSynchronized:
-               action, err := parseHookPullRequestEventType(event)
-               if err != nil {
-                       return nil, err
-               }
-
-               text = fmt.Sprintf("[%s] Pull request review %s : %s by %s", p.Repository.FullName, action, titleLink, senderLink)
-       }
-
-       return &SlackPayload{
-               Channel:  slack.Channel,
-               Text:     text,
-               Username: slack.Username,
-               IconURL:  slack.IconURL,
-               Attachments: []SlackAttachment{{
-                       Color: slack.Color,
-                       Title: title,
-                       Text:  attachmentText,
-               }},
-       }, nil
-}
-
-func getSlackRepositoryPayload(p *api.RepositoryPayload, slack *SlackMeta) (*SlackPayload, error) {
-       senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
-       var text, title, attachmentText string
-       switch p.Action {
-       case api.HookRepoCreated:
-               text = fmt.Sprintf("[%s] Repository created by %s", p.Repository.FullName, senderLink)
-               title = p.Repository.HTMLURL
-       case api.HookRepoDeleted:
-               text = fmt.Sprintf("[%s] Repository deleted by %s", p.Repository.FullName, senderLink)
-       }
-
-       return &SlackPayload{
-               Channel:  slack.Channel,
-               Text:     text,
-               Username: slack.Username,
-               IconURL:  slack.IconURL,
-               Attachments: []SlackAttachment{{
-                       Color: slack.Color,
-                       Title: title,
-                       Text:  attachmentText,
-               }},
-       }, nil
-}
-
-// GetSlackPayload converts a slack webhook into a SlackPayload
-func GetSlackPayload(p api.Payloader, event HookEventType, meta string) (*SlackPayload, error) {
-       s := new(SlackPayload)
-
-       slack := &SlackMeta{}
-       if err := json.Unmarshal([]byte(meta), &slack); err != nil {
-               return s, errors.New("GetSlackPayload meta json:" + err.Error())
-       }
-
-       switch event {
-       case HookEventCreate:
-               return getSlackCreatePayload(p.(*api.CreatePayload), slack)
-       case HookEventDelete:
-               return getSlackDeletePayload(p.(*api.DeletePayload), slack)
-       case HookEventFork:
-               return getSlackForkPayload(p.(*api.ForkPayload), slack)
-       case HookEventIssues:
-               return getSlackIssuesPayload(p.(*api.IssuePayload), slack)
-       case HookEventIssueComment:
-               return getSlackIssueCommentPayload(p.(*api.IssueCommentPayload), slack)
-       case HookEventPush:
-               return getSlackPushPayload(p.(*api.PushPayload), slack)
-       case HookEventPullRequest:
-               return getSlackPullRequestPayload(p.(*api.PullRequestPayload), slack)
-       case HookEventPullRequestRejected, HookEventPullRequestApproved, HookEventPullRequestComment:
-               return getSlackPullRequestApprovalPayload(p.(*api.PullRequestPayload), slack, event)
-       case HookEventRepository:
-               return getSlackRepositoryPayload(p.(*api.RepositoryPayload), slack)
-       case HookEventRelease:
-               return getSlackReleasePayload(p.(*api.ReleasePayload), slack)
-       }
-
-       return s, nil
-}
diff --git a/models/webhook_telegram.go b/models/webhook_telegram.go
deleted file mode 100644 (file)
index ead669d..0000000
+++ /dev/null
@@ -1,325 +0,0 @@
-// Copyright 2019 The Gitea Authors. All rights reserved.
-// Use of this source code is governed by a MIT-style
-// license that can be found in the LICENSE file.
-
-package models
-
-import (
-       "encoding/json"
-       "fmt"
-       "html"
-       "strings"
-
-       "code.gitea.io/gitea/modules/git"
-       "code.gitea.io/gitea/modules/markup"
-       api "code.gitea.io/gitea/modules/structs"
-)
-
-type (
-       // TelegramPayload represents
-       TelegramPayload struct {
-               Message           string `json:"text"`
-               ParseMode         string `json:"parse_mode"`
-               DisableWebPreview bool   `json:"disable_web_page_preview"`
-       }
-
-       // TelegramMeta contains the telegram metadata
-       TelegramMeta struct {
-               BotToken string `json:"bot_token"`
-               ChatID   string `json:"chat_id"`
-       }
-)
-
-// SetSecret sets the telegram secret
-func (p *TelegramPayload) SetSecret(_ string) {}
-
-// JSONPayload Marshals the TelegramPayload to json
-func (p *TelegramPayload) JSONPayload() ([]byte, error) {
-       p.ParseMode = "HTML"
-       p.DisableWebPreview = true
-       p.Message = markup.Sanitize(p.Message)
-       data, err := json.MarshalIndent(p, "", "  ")
-       if err != nil {
-               return []byte{}, err
-       }
-       return data, nil
-}
-
-func getTelegramCreatePayload(p *api.CreatePayload) (*TelegramPayload, error) {
-       // created tag/branch
-       refName := git.RefEndName(p.Ref)
-       title := fmt.Sprintf(`[<a href="%s">%s</a>] %s <a href="%s">%s</a> created`, p.Repo.HTMLURL, p.Repo.FullName, p.RefType,
-               p.Repo.HTMLURL+"/src/"+refName, refName)
-
-       return &TelegramPayload{
-               Message: title,
-       }, nil
-}
-
-func getTelegramDeletePayload(p *api.DeletePayload) (*TelegramPayload, error) {
-       // created tag/branch
-       refName := git.RefEndName(p.Ref)
-       title := fmt.Sprintf(`[<a href="%s">%s</a>] %s <a href="%s">%s</a> deleted`, p.Repo.HTMLURL, p.Repo.FullName, p.RefType,
-               p.Repo.HTMLURL+"/src/"+refName, refName)
-
-       return &TelegramPayload{
-               Message: title,
-       }, nil
-}
-
-func getTelegramForkPayload(p *api.ForkPayload) (*TelegramPayload, error) {
-       title := fmt.Sprintf(`%s is forked to <a href="%s">%s</a>`, p.Forkee.FullName, p.Repo.HTMLURL, p.Repo.FullName)
-
-       return &TelegramPayload{
-               Message: title,
-       }, nil
-}
-
-func getTelegramPushPayload(p *api.PushPayload) (*TelegramPayload, error) {
-       var (
-               branchName = git.RefEndName(p.Ref)
-               commitDesc string
-       )
-
-       var titleLink string
-       if len(p.Commits) == 1 {
-               commitDesc = "1 new commit"
-               titleLink = p.Commits[0].URL
-       } else {
-               commitDesc = fmt.Sprintf("%d new commits", len(p.Commits))
-               titleLink = p.CompareURL
-       }
-       if titleLink == "" {
-               titleLink = p.Repo.HTMLURL + "/src/" + branchName
-       }
-       title := fmt.Sprintf(`[<a href="%s">%s</a>:<a href="%s">%s</a>] %s`, p.Repo.HTMLURL, p.Repo.FullName, titleLink, 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(`[<a href="%s">%s</a>] %s`, commit.URL, commit.ID[:7],
-                       strings.TrimRight(commit.Message, "\r\n")) + authorName
-               // add linebreak to each commit but the last
-               if i < len(p.Commits)-1 {
-                       text += "\n"
-               }
-       }
-
-       return &TelegramPayload{
-               Message: title + "\n" + text,
-       }, nil
-}
-
-func getTelegramIssuesPayload(p *api.IssuePayload) (*TelegramPayload, error) {
-       var text, title string
-       switch p.Action {
-       case api.HookIssueOpened:
-               title = fmt.Sprintf(`[<a href="%s">%s</a>] Issue opened: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
-                       p.Issue.URL, p.Index, p.Issue.Title)
-               text = p.Issue.Body
-       case api.HookIssueClosed:
-               title = fmt.Sprintf(`[<a href="%s">%s</a>] Issue closed: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
-                       p.Issue.URL, p.Index, p.Issue.Title)
-               text = p.Issue.Body
-       case api.HookIssueReOpened:
-               title = fmt.Sprintf(`[<a href="%s">%s</a>] Issue re-opened: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
-                       p.Issue.URL, p.Index, p.Issue.Title)
-               text = p.Issue.Body
-       case api.HookIssueEdited:
-               title = fmt.Sprintf(`[<a href="%s">%s</a>] Issue edited: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
-                       p.Issue.URL, p.Index, p.Issue.Title)
-               text = p.Issue.Body
-       case api.HookIssueAssigned:
-               title = fmt.Sprintf(`[<a href="%s">%s</a>] Issue assigned to %s: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
-                       p.Issue.Assignee.UserName, p.Issue.URL, p.Index, p.Issue.Title)
-               text = p.Issue.Body
-       case api.HookIssueUnassigned:
-               title = fmt.Sprintf(`[<a href="%s">%s</a>] Issue unassigned: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
-                       p.Issue.URL, p.Index, p.Issue.Title)
-               text = p.Issue.Body
-       case api.HookIssueLabelUpdated:
-               title = fmt.Sprintf(`[<a href="%s">%s</a>] Issue labels updated: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
-                       p.Issue.URL, p.Index, p.Issue.Title)
-               text = p.Issue.Body
-       case api.HookIssueLabelCleared:
-               title = fmt.Sprintf(`[<a href="%s">%s</a>] Issue labels cleared: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
-                       p.Issue.URL, p.Index, p.Issue.Title)
-               text = p.Issue.Body
-       case api.HookIssueSynchronized:
-               title = fmt.Sprintf(`[<a href="%s">%s</a>] Issue synchronized: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
-                       p.Issue.URL, p.Index, p.Issue.Title)
-               text = p.Issue.Body
-       case api.HookIssueMilestoned:
-               title = fmt.Sprintf(`[<a href="%s">%s</a>] Issue milestone: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
-                       p.Issue.URL, p.Index, p.Issue.Title)
-               text = p.Issue.Body
-       case api.HookIssueDemilestoned:
-               title = fmt.Sprintf(`[<a href="%s">%s</a>] Issue clear milestone: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
-                       p.Issue.URL, p.Index, p.Issue.Title)
-               text = p.Issue.Body
-       }
-
-       return &TelegramPayload{
-               Message: title + "\n\n" + text,
-       }, nil
-}
-
-func getTelegramIssueCommentPayload(p *api.IssueCommentPayload) (*TelegramPayload, error) {
-       url := fmt.Sprintf("%s/issues/%d#%s", p.Repository.HTMLURL, p.Issue.Index, CommentHashTag(p.Comment.ID))
-       title := fmt.Sprintf(`<a href="%s">#%d %s</a>`, url, p.Issue.Index, html.EscapeString(p.Issue.Title))
-       var text string
-       switch p.Action {
-       case api.HookIssueCommentCreated:
-               text = "New comment: " + title
-               text += p.Comment.Body
-       case api.HookIssueCommentEdited:
-               text = "Comment edited: " + title
-               text += p.Comment.Body
-       case api.HookIssueCommentDeleted:
-               text = "Comment deleted: " + title
-               text += p.Comment.Body
-       }
-
-       return &TelegramPayload{
-               Message: title + "\n" + text,
-       }, nil
-}
-
-func getTelegramPullRequestPayload(p *api.PullRequestPayload) (*TelegramPayload, error) {
-       var text, title string
-       switch p.Action {
-       case api.HookIssueOpened:
-               title = fmt.Sprintf(`[<a href="%s">%s</a>] Pull request opened: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
-                       p.PullRequest.HTMLURL, p.Index, p.PullRequest.Title)
-               text = p.PullRequest.Body
-       case api.HookIssueClosed:
-               if p.PullRequest.HasMerged {
-                       title = fmt.Sprintf(`[<a href="%s">%s</a>] Pull request merged: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
-                               p.PullRequest.HTMLURL, p.Index, p.PullRequest.Title)
-               } else {
-                       title = fmt.Sprintf(`[<a href="%s">%s</a>] Pull request closed: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
-                               p.PullRequest.HTMLURL, p.Index, p.PullRequest.Title)
-               }
-               text = p.PullRequest.Body
-       case api.HookIssueReOpened:
-               title = fmt.Sprintf(`[<a href="%s">%s</a>] Pull request re-opened: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
-                       p.PullRequest.HTMLURL, p.Index, p.PullRequest.Title)
-               text = p.PullRequest.Body
-       case api.HookIssueEdited:
-               title = fmt.Sprintf(`[<a href="%s">%s</a>] Pull request edited: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
-                       p.PullRequest.HTMLURL, p.Index, p.PullRequest.Title)
-               text = p.PullRequest.Body
-       case api.HookIssueAssigned:
-               list, err := MakeAssigneeList(&Issue{ID: p.PullRequest.ID})
-               if err != nil {
-                       return &TelegramPayload{}, err
-               }
-               title = fmt.Sprintf(`[<a href="%s">%s</a>] Pull request assigned to %s: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
-                       list, p.PullRequest.HTMLURL, p.Index, p.PullRequest.Title)
-               text = p.PullRequest.Body
-       case api.HookIssueUnassigned:
-               title = fmt.Sprintf(`[<a href="%s">%s</a>] Pull request unassigned: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
-                       p.PullRequest.HTMLURL, p.Index, p.PullRequest.Title)
-               text = p.PullRequest.Body
-       case api.HookIssueLabelUpdated:
-               title = fmt.Sprintf(`[<a href="%s">%s</a>] Pull request labels updated: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
-                       p.PullRequest.HTMLURL, p.Index, p.PullRequest.Title)
-               text = p.PullRequest.Body
-       case api.HookIssueLabelCleared:
-               title = fmt.Sprintf(`[<a href="%s">%s</a>] Pull request labels cleared: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
-                       p.PullRequest.HTMLURL, p.Index, p.PullRequest.Title)
-               text = p.PullRequest.Body
-       case api.HookIssueSynchronized:
-               title = fmt.Sprintf(`[<a href="%s">%s</a>] Pull request synchronized: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
-                       p.PullRequest.HTMLURL, p.Index, p.PullRequest.Title)
-               text = p.PullRequest.Body
-       case api.HookIssueMilestoned:
-               title = fmt.Sprintf(`[<a href="%s">%s</a>] Pull request milestone: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
-                       p.PullRequest.HTMLURL, p.Index, p.PullRequest.Title)
-               text = p.PullRequest.Body
-       case api.HookIssueDemilestoned:
-               title = fmt.Sprintf(`[<a href="%s">%s</a>] Pull request clear milestone: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
-                       p.PullRequest.HTMLURL, p.Index, p.PullRequest.Title)
-               text = p.PullRequest.Body
-       }
-
-       return &TelegramPayload{
-               Message: title + "\n" + text,
-       }, nil
-}
-
-func getTelegramRepositoryPayload(p *api.RepositoryPayload) (*TelegramPayload, error) {
-       var title string
-       switch p.Action {
-       case api.HookRepoCreated:
-               title = fmt.Sprintf(`[<a href="%s">%s</a>] Repository created`, p.Repository.HTMLURL, p.Repository.FullName)
-               return &TelegramPayload{
-                       Message: title,
-               }, nil
-       case api.HookRepoDeleted:
-               title = fmt.Sprintf("[%s] Repository deleted", p.Repository.FullName)
-               return &TelegramPayload{
-                       Message: title,
-               }, nil
-       }
-       return nil, nil
-}
-
-func getTelegramReleasePayload(p *api.ReleasePayload) (*TelegramPayload, error) {
-       var title, url string
-       switch p.Action {
-       case api.HookReleasePublished:
-               title = fmt.Sprintf("[%s] Release created", p.Release.TagName)
-               url = p.Release.URL
-               return &TelegramPayload{
-                       Message: title + "\n" + url,
-               }, nil
-       case api.HookReleaseUpdated:
-               title = fmt.Sprintf("[%s] Release updated", p.Release.TagName)
-               url = p.Release.URL
-               return &TelegramPayload{
-                       Message: title + "\n" + url,
-               }, nil
-
-       case api.HookReleaseDeleted:
-               title = fmt.Sprintf("[%s] Release deleted", p.Release.TagName)
-               url = p.Release.URL
-               return &TelegramPayload{
-                       Message: title + "\n" + url,
-               }, nil
-       }
-
-       return nil, nil
-}
-
-// GetTelegramPayload converts a telegram webhook into a TelegramPayload
-func GetTelegramPayload(p api.Payloader, event HookEventType, meta string) (*TelegramPayload, error) {
-       s := new(TelegramPayload)
-
-       switch event {
-       case HookEventCreate:
-               return getTelegramCreatePayload(p.(*api.CreatePayload))
-       case HookEventDelete:
-               return getTelegramDeletePayload(p.(*api.DeletePayload))
-       case HookEventFork:
-               return getTelegramForkPayload(p.(*api.ForkPayload))
-       case HookEventIssues:
-               return getTelegramIssuesPayload(p.(*api.IssuePayload))
-       case HookEventIssueComment:
-               return getTelegramIssueCommentPayload(p.(*api.IssueCommentPayload))
-       case HookEventPush:
-               return getTelegramPushPayload(p.(*api.PushPayload))
-       case HookEventPullRequest:
-               return getTelegramPullRequestPayload(p.(*api.PullRequestPayload))
-       case HookEventRepository:
-               return getTelegramRepositoryPayload(p.(*api.RepositoryPayload))
-       case HookEventRelease:
-               return getTelegramReleasePayload(p.(*api.ReleasePayload))
-       }
-
-       return s, nil
-}
index 7bdaadc5ae95183f3414e8bc4807b062d225d019..0fd9b245caf948321eb6f4b043cea97b0b25dd34 100644 (file)
@@ -24,18 +24,6 @@ func TestIsValidHookContentType(t *testing.T) {
        assert.False(t, IsValidHookContentType("invalid"))
 }
 
-func TestWebhook_GetSlackHook(t *testing.T) {
-       w := &Webhook{
-               Meta: `{"channel": "foo", "username": "username", "color": "blue"}`,
-       }
-       slackHook := w.GetSlackHook()
-       assert.Equal(t, *slackHook, SlackMeta{
-               Channel:  "foo",
-               Username: "username",
-               Color:    "blue",
-       })
-}
-
 func TestWebhook_History(t *testing.T) {
        assert.NoError(t, PrepareTestDatabase())
        webhook := AssertExistsAndLoadBean(t, &Webhook{ID: 1}).(*Webhook)
diff --git a/modules/webhook/dingtalk.go b/modules/webhook/dingtalk.go
new file mode 100644 (file)
index 0000000..b6d58f5
--- /dev/null
@@ -0,0 +1,423 @@
+// Copyright 2017 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package webhook
+
+import (
+       "encoding/json"
+       "fmt"
+       "strings"
+
+       "code.gitea.io/gitea/models"
+       "code.gitea.io/gitea/modules/git"
+       api "code.gitea.io/gitea/modules/structs"
+
+       dingtalk "github.com/lunny/dingtalk_webhook"
+)
+
+type (
+       // DingtalkPayload represents
+       DingtalkPayload dingtalk.Payload
+)
+
+// SetSecret sets the dingtalk secret
+func (p *DingtalkPayload) SetSecret(_ string) {}
+
+// JSONPayload Marshals the DingtalkPayload to json
+func (p *DingtalkPayload) JSONPayload() ([]byte, error) {
+       data, err := json.MarshalIndent(p, "", "  ")
+       if err != nil {
+               return []byte{}, err
+       }
+       return data, nil
+}
+
+func getDingtalkCreatePayload(p *api.CreatePayload) (*DingtalkPayload, error) {
+       // created tag/branch
+       refName := git.RefEndName(p.Ref)
+       title := fmt.Sprintf("[%s] %s %s created", p.Repo.FullName, p.RefType, refName)
+
+       return &DingtalkPayload{
+               MsgType: "actionCard",
+               ActionCard: dingtalk.ActionCard{
+                       Text:        title,
+                       Title:       title,
+                       HideAvatar:  "0",
+                       SingleTitle: fmt.Sprintf("view ref %s", refName),
+                       SingleURL:   p.Repo.HTMLURL + "/src/" + refName,
+               },
+       }, nil
+}
+
+func getDingtalkDeletePayload(p *api.DeletePayload) (*DingtalkPayload, error) {
+       // created tag/branch
+       refName := git.RefEndName(p.Ref)
+       title := fmt.Sprintf("[%s] %s %s deleted", p.Repo.FullName, p.RefType, refName)
+
+       return &DingtalkPayload{
+               MsgType: "actionCard",
+               ActionCard: dingtalk.ActionCard{
+                       Text:        title,
+                       Title:       title,
+                       HideAvatar:  "0",
+                       SingleTitle: fmt.Sprintf("view ref %s", refName),
+                       SingleURL:   p.Repo.HTMLURL + "/src/" + refName,
+               },
+       }, nil
+}
+
+func getDingtalkForkPayload(p *api.ForkPayload) (*DingtalkPayload, error) {
+       title := fmt.Sprintf("%s is forked to %s", p.Forkee.FullName, p.Repo.FullName)
+
+       return &DingtalkPayload{
+               MsgType: "actionCard",
+               ActionCard: dingtalk.ActionCard{
+                       Text:        title,
+                       Title:       title,
+                       HideAvatar:  "0",
+                       SingleTitle: fmt.Sprintf("view forked repo %s", p.Repo.FullName),
+                       SingleURL:   p.Repo.HTMLURL,
+               },
+       }, nil
+}
+
+func getDingtalkPushPayload(p *api.PushPayload) (*DingtalkPayload, error) {
+       var (
+               branchName = git.RefEndName(p.Ref)
+               commitDesc string
+       )
+
+       var titleLink, linkText string
+       if len(p.Commits) == 1 {
+               commitDesc = "1 new commit"
+               titleLink = p.Commits[0].URL
+               linkText = fmt.Sprintf("view commit %s", p.Commits[0].ID[:7])
+       } else {
+               commitDesc = fmt.Sprintf("%d new commits", len(p.Commits))
+               titleLink = p.CompareURL
+               linkText = fmt.Sprintf("view commit %s...%s", p.Commits[0].ID[:7], p.Commits[len(p.Commits)-1].ID[:7])
+       }
+       if titleLink == "" {
+               titleLink = p.Repo.HTMLURL + "/src/" + branchName
+       }
+
+       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 {
+                       text += "\n"
+               }
+       }
+
+       return &DingtalkPayload{
+               MsgType: "actionCard",
+               ActionCard: dingtalk.ActionCard{
+                       Text:        text,
+                       Title:       title,
+                       HideAvatar:  "0",
+                       SingleTitle: linkText,
+                       SingleURL:   titleLink,
+               },
+       }, nil
+}
+
+func getDingtalkIssuesPayload(p *api.IssuePayload) (*DingtalkPayload, error) {
+       var text, title string
+       switch p.Action {
+       case api.HookIssueOpened:
+               title = fmt.Sprintf("[%s] Issue opened: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
+               text = p.Issue.Body
+       case api.HookIssueClosed:
+               title = fmt.Sprintf("[%s] Issue closed: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
+               text = p.Issue.Body
+       case api.HookIssueReOpened:
+               title = fmt.Sprintf("[%s] Issue re-opened: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
+               text = p.Issue.Body
+       case api.HookIssueEdited:
+               title = fmt.Sprintf("[%s] Issue edited: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
+               text = p.Issue.Body
+       case api.HookIssueAssigned:
+               title = fmt.Sprintf("[%s] Issue assigned to %s: #%d %s", p.Repository.FullName,
+                       p.Issue.Assignee.UserName, p.Index, p.Issue.Title)
+               text = p.Issue.Body
+       case api.HookIssueUnassigned:
+               title = fmt.Sprintf("[%s] Issue unassigned: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
+               text = p.Issue.Body
+       case api.HookIssueLabelUpdated:
+               title = fmt.Sprintf("[%s] Issue labels updated: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
+               text = p.Issue.Body
+       case api.HookIssueLabelCleared:
+               title = fmt.Sprintf("[%s] Issue labels cleared: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
+               text = p.Issue.Body
+       case api.HookIssueSynchronized:
+               title = fmt.Sprintf("[%s] Issue synchronized: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
+               text = p.Issue.Body
+       case api.HookIssueMilestoned:
+               title = fmt.Sprintf("[%s] Issue milestone: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
+               text = p.Issue.Body
+       case api.HookIssueDemilestoned:
+               title = fmt.Sprintf("[%s] Issue clear milestone: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
+               text = p.Issue.Body
+       }
+
+       return &DingtalkPayload{
+               MsgType: "actionCard",
+               ActionCard: dingtalk.ActionCard{
+                       Text: title + "\r\n\r\n" + text,
+                       //Markdown:    "# " + title + "\n" + text,
+                       Title:       title,
+                       HideAvatar:  "0",
+                       SingleTitle: "view issue",
+                       SingleURL:   p.Issue.URL,
+               },
+       }, nil
+}
+
+func getDingtalkIssueCommentPayload(p *api.IssueCommentPayload) (*DingtalkPayload, error) {
+       title := fmt.Sprintf("#%d: %s", p.Issue.Index, p.Issue.Title)
+       url := fmt.Sprintf("%s/issues/%d#%s", p.Repository.HTMLURL, p.Issue.Index, models.CommentHashTag(p.Comment.ID))
+       var content string
+       switch p.Action {
+       case api.HookIssueCommentCreated:
+               if p.IsPull {
+                       title = "New comment on pull request " + title
+               } else {
+                       title = "New comment on issue " + title
+               }
+               content = p.Comment.Body
+       case api.HookIssueCommentEdited:
+               if p.IsPull {
+                       title = "Comment edited on pull request " + title
+               } else {
+                       title = "Comment edited on issue " + title
+               }
+               content = p.Comment.Body
+       case api.HookIssueCommentDeleted:
+               if p.IsPull {
+                       title = "Comment deleted on pull request " + title
+               } else {
+                       title = "Comment deleted on issue " + title
+               }
+               url = fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Issue.Index)
+               content = p.Comment.Body
+       }
+
+       title = fmt.Sprintf("[%s] %s", p.Repository.FullName, title)
+
+       return &DingtalkPayload{
+               MsgType: "actionCard",
+               ActionCard: dingtalk.ActionCard{
+                       Text:        title + "\r\n\r\n" + content,
+                       Title:       title,
+                       HideAvatar:  "0",
+                       SingleTitle: "view issue comment",
+                       SingleURL:   url,
+               },
+       }, nil
+}
+
+func getDingtalkPullRequestPayload(p *api.PullRequestPayload) (*DingtalkPayload, error) {
+       var text, title string
+       switch p.Action {
+       case api.HookIssueOpened:
+               title = fmt.Sprintf("[%s] Pull request opened: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
+               text = p.PullRequest.Body
+       case api.HookIssueClosed:
+               if p.PullRequest.HasMerged {
+                       title = fmt.Sprintf("[%s] Pull request merged: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
+               } else {
+                       title = fmt.Sprintf("[%s] Pull request closed: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
+               }
+               text = p.PullRequest.Body
+       case api.HookIssueReOpened:
+               title = fmt.Sprintf("[%s] Pull request re-opened: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
+               text = p.PullRequest.Body
+       case api.HookIssueEdited:
+               title = fmt.Sprintf("[%s] Pull request edited: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
+               text = p.PullRequest.Body
+       case api.HookIssueAssigned:
+               list := make([]string, len(p.PullRequest.Assignees))
+               for i, user := range p.PullRequest.Assignees {
+                       list[i] = user.UserName
+               }
+               title = fmt.Sprintf("[%s] Pull request assigned to %s: #%d %s", p.Repository.FullName,
+                       strings.Join(list, ", "),
+                       p.Index, p.PullRequest.Title)
+               text = p.PullRequest.Body
+       case api.HookIssueUnassigned:
+               title = fmt.Sprintf("[%s] Pull request unassigned: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
+               text = p.PullRequest.Body
+       case api.HookIssueLabelUpdated:
+               title = fmt.Sprintf("[%s] Pull request labels updated: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
+               text = p.PullRequest.Body
+       case api.HookIssueLabelCleared:
+               title = fmt.Sprintf("[%s] Pull request labels cleared: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
+               text = p.PullRequest.Body
+       case api.HookIssueSynchronized:
+               title = fmt.Sprintf("[%s] Pull request synchronized: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
+               text = p.PullRequest.Body
+       case api.HookIssueMilestoned:
+               title = fmt.Sprintf("[%s] Pull request milestone: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
+               text = p.PullRequest.Body
+       case api.HookIssueDemilestoned:
+               title = fmt.Sprintf("[%s] Pull request clear milestone: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
+               text = p.PullRequest.Body
+       }
+
+       return &DingtalkPayload{
+               MsgType: "actionCard",
+               ActionCard: dingtalk.ActionCard{
+                       Text: title + "\r\n\r\n" + text,
+                       //Markdown:    "# " + title + "\n" + text,
+                       Title:       title,
+                       HideAvatar:  "0",
+                       SingleTitle: "view pull request",
+                       SingleURL:   p.PullRequest.HTMLURL,
+               },
+       }, nil
+}
+
+func getDingtalkPullRequestApprovalPayload(p *api.PullRequestPayload, event models.HookEventType) (*DingtalkPayload, error) {
+       var text, title string
+       switch p.Action {
+       case api.HookIssueSynchronized:
+               action, err := parseHookPullRequestEventType(event)
+               if err != nil {
+                       return nil, err
+               }
+
+               title = fmt.Sprintf("[%s] Pull request review %s : #%d %s", p.Repository.FullName, action, p.Index, p.PullRequest.Title)
+               text = p.Review.Content
+
+       }
+
+       return &DingtalkPayload{
+               MsgType: "actionCard",
+               ActionCard: dingtalk.ActionCard{
+                       Text:        title + "\r\n\r\n" + text,
+                       Title:       title,
+                       HideAvatar:  "0",
+                       SingleTitle: "view pull request",
+                       SingleURL:   p.PullRequest.HTMLURL,
+               },
+       }, nil
+}
+
+func getDingtalkRepositoryPayload(p *api.RepositoryPayload) (*DingtalkPayload, error) {
+       var title, url string
+       switch p.Action {
+       case api.HookRepoCreated:
+               title = fmt.Sprintf("[%s] Repository created", p.Repository.FullName)
+               url = p.Repository.HTMLURL
+               return &DingtalkPayload{
+                       MsgType: "actionCard",
+                       ActionCard: dingtalk.ActionCard{
+                               Text:        title,
+                               Title:       title,
+                               HideAvatar:  "0",
+                               SingleTitle: "view repository",
+                               SingleURL:   url,
+                       },
+               }, nil
+       case api.HookRepoDeleted:
+               title = fmt.Sprintf("[%s] Repository deleted", p.Repository.FullName)
+               return &DingtalkPayload{
+                       MsgType: "text",
+                       Text: struct {
+                               Content string `json:"content"`
+                       }{
+                               Content: title,
+                       },
+               }, nil
+       }
+
+       return nil, nil
+}
+
+func getDingtalkReleasePayload(p *api.ReleasePayload) (*DingtalkPayload, error) {
+       var title, url string
+       switch p.Action {
+       case api.HookReleasePublished:
+               title = fmt.Sprintf("[%s] Release created", p.Release.TagName)
+               url = p.Release.URL
+               return &DingtalkPayload{
+                       MsgType: "actionCard",
+                       ActionCard: dingtalk.ActionCard{
+                               Text:        title,
+                               Title:       title,
+                               HideAvatar:  "0",
+                               SingleTitle: "view release",
+                               SingleURL:   url,
+                       },
+               }, nil
+       case api.HookReleaseUpdated:
+               title = fmt.Sprintf("[%s] Release updated", p.Release.TagName)
+               url = p.Release.URL
+               return &DingtalkPayload{
+                       MsgType: "actionCard",
+                       ActionCard: dingtalk.ActionCard{
+                               Text:        title,
+                               Title:       title,
+                               HideAvatar:  "0",
+                               SingleTitle: "view release",
+                               SingleURL:   url,
+                       },
+               }, nil
+
+       case api.HookReleaseDeleted:
+               title = fmt.Sprintf("[%s] Release deleted", p.Release.TagName)
+               url = p.Release.URL
+               return &DingtalkPayload{
+                       MsgType: "actionCard",
+                       ActionCard: dingtalk.ActionCard{
+                               Text:        title,
+                               Title:       title,
+                               HideAvatar:  "0",
+                               SingleTitle: "view release",
+                               SingleURL:   url,
+                       },
+               }, nil
+       }
+
+       return nil, nil
+}
+
+// GetDingtalkPayload converts a ding talk webhook into a DingtalkPayload
+func GetDingtalkPayload(p api.Payloader, event models.HookEventType, meta string) (*DingtalkPayload, error) {
+       s := new(DingtalkPayload)
+
+       switch event {
+       case models.HookEventCreate:
+               return getDingtalkCreatePayload(p.(*api.CreatePayload))
+       case models.HookEventDelete:
+               return getDingtalkDeletePayload(p.(*api.DeletePayload))
+       case models.HookEventFork:
+               return getDingtalkForkPayload(p.(*api.ForkPayload))
+       case models.HookEventIssues:
+               return getDingtalkIssuesPayload(p.(*api.IssuePayload))
+       case models.HookEventIssueComment:
+               return getDingtalkIssueCommentPayload(p.(*api.IssueCommentPayload))
+       case models.HookEventPush:
+               return getDingtalkPushPayload(p.(*api.PushPayload))
+       case models.HookEventPullRequest:
+               return getDingtalkPullRequestPayload(p.(*api.PullRequestPayload))
+       case models.HookEventPullRequestApproved, models.HookEventPullRequestRejected, models.HookEventPullRequestComment:
+               return getDingtalkPullRequestApprovalPayload(p.(*api.PullRequestPayload), event)
+       case models.HookEventRepository:
+               return getDingtalkRepositoryPayload(p.(*api.RepositoryPayload))
+       case models.HookEventRelease:
+               return getDingtalkReleasePayload(p.(*api.ReleasePayload))
+       }
+
+       return s, nil
+}
diff --git a/modules/webhook/discord.go b/modules/webhook/discord.go
new file mode 100644 (file)
index 0000000..f92157a
--- /dev/null
@@ -0,0 +1,596 @@
+// Copyright 2017 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package webhook
+
+import (
+       "encoding/json"
+       "errors"
+       "fmt"
+       "strconv"
+       "strings"
+
+       "code.gitea.io/gitea/models"
+       "code.gitea.io/gitea/modules/git"
+       "code.gitea.io/gitea/modules/log"
+       "code.gitea.io/gitea/modules/setting"
+       api "code.gitea.io/gitea/modules/structs"
+)
+
+type (
+       // DiscordEmbedFooter for Embed Footer Structure.
+       DiscordEmbedFooter struct {
+               Text string `json:"text"`
+       }
+
+       // DiscordEmbedAuthor for Embed Author Structure
+       DiscordEmbedAuthor struct {
+               Name    string `json:"name"`
+               URL     string `json:"url"`
+               IconURL string `json:"icon_url"`
+       }
+
+       // DiscordEmbedField for Embed Field Structure
+       DiscordEmbedField struct {
+               Name  string `json:"name"`
+               Value string `json:"value"`
+       }
+
+       // DiscordEmbed is for Embed Structure
+       DiscordEmbed struct {
+               Title       string              `json:"title"`
+               Description string              `json:"description"`
+               URL         string              `json:"url"`
+               Color       int                 `json:"color"`
+               Footer      DiscordEmbedFooter  `json:"footer"`
+               Author      DiscordEmbedAuthor  `json:"author"`
+               Fields      []DiscordEmbedField `json:"fields"`
+       }
+
+       // DiscordPayload represents
+       DiscordPayload struct {
+               Wait      bool           `json:"wait"`
+               Content   string         `json:"content"`
+               Username  string         `json:"username"`
+               AvatarURL string         `json:"avatar_url"`
+               TTS       bool           `json:"tts"`
+               Embeds    []DiscordEmbed `json:"embeds"`
+       }
+
+       // DiscordMeta contains the discord metadata
+       DiscordMeta struct {
+               Username string `json:"username"`
+               IconURL  string `json:"icon_url"`
+       }
+)
+
+// GetDiscordHook returns discord metadata
+func GetDiscordHook(w *models.Webhook) *DiscordMeta {
+       s := &DiscordMeta{}
+       if err := json.Unmarshal([]byte(w.Meta), s); err != nil {
+               log.Error("webhook.GetDiscordHook(%d): %v", w.ID, err)
+       }
+       return s
+}
+
+func color(clr string) int {
+       if clr != "" {
+               clr = strings.TrimLeft(clr, "#")
+               if s, err := strconv.ParseInt(clr, 16, 32); err == nil {
+                       return int(s)
+               }
+       }
+
+       return 0
+}
+
+var (
+       greenColor       = color("1ac600")
+       greenColorLight  = color("bfe5bf")
+       yellowColor      = color("ffd930")
+       greyColor        = color("4f545c")
+       purpleColor      = color("7289da")
+       orangeColor      = color("eb6420")
+       orangeColorLight = color("e68d60")
+       redColor         = color("ff3232")
+)
+
+// SetSecret sets the discord secret
+func (p *DiscordPayload) SetSecret(_ string) {}
+
+// JSONPayload Marshals the DiscordPayload to json
+func (p *DiscordPayload) JSONPayload() ([]byte, error) {
+       data, err := json.MarshalIndent(p, "", "  ")
+       if err != nil {
+               return []byte{}, err
+       }
+       return data, nil
+}
+
+func getDiscordCreatePayload(p *api.CreatePayload, meta *DiscordMeta) (*DiscordPayload, error) {
+       // created tag/branch
+       refName := git.RefEndName(p.Ref)
+       title := fmt.Sprintf("[%s] %s %s created", p.Repo.FullName, p.RefType, refName)
+
+       return &DiscordPayload{
+               Username:  meta.Username,
+               AvatarURL: meta.IconURL,
+               Embeds: []DiscordEmbed{
+                       {
+                               Title: title,
+                               URL:   p.Repo.HTMLURL + "/src/" + refName,
+                               Color: greenColor,
+                               Author: DiscordEmbedAuthor{
+                                       Name:    p.Sender.UserName,
+                                       URL:     setting.AppURL + p.Sender.UserName,
+                                       IconURL: p.Sender.AvatarURL,
+                               },
+                       },
+               },
+       }, nil
+}
+
+func getDiscordDeletePayload(p *api.DeletePayload, meta *DiscordMeta) (*DiscordPayload, error) {
+       // deleted tag/branch
+       refName := git.RefEndName(p.Ref)
+       title := fmt.Sprintf("[%s] %s %s deleted", p.Repo.FullName, p.RefType, refName)
+
+       return &DiscordPayload{
+               Username:  meta.Username,
+               AvatarURL: meta.IconURL,
+               Embeds: []DiscordEmbed{
+                       {
+                               Title: title,
+                               URL:   p.Repo.HTMLURL + "/src/" + refName,
+                               Color: redColor,
+                               Author: DiscordEmbedAuthor{
+                                       Name:    p.Sender.UserName,
+                                       URL:     setting.AppURL + p.Sender.UserName,
+                                       IconURL: p.Sender.AvatarURL,
+                               },
+                       },
+               },
+       }, nil
+}
+
+func getDiscordForkPayload(p *api.ForkPayload, meta *DiscordMeta) (*DiscordPayload, error) {
+       // fork
+       title := fmt.Sprintf("%s is forked to %s", p.Forkee.FullName, p.Repo.FullName)
+
+       return &DiscordPayload{
+               Username:  meta.Username,
+               AvatarURL: meta.IconURL,
+               Embeds: []DiscordEmbed{
+                       {
+                               Title: title,
+                               URL:   p.Repo.HTMLURL,
+                               Color: greenColor,
+                               Author: DiscordEmbedAuthor{
+                                       Name:    p.Sender.UserName,
+                                       URL:     setting.AppURL + p.Sender.UserName,
+                                       IconURL: p.Sender.AvatarURL,
+                               },
+                       },
+               },
+       }, nil
+}
+
+func getDiscordPushPayload(p *api.PushPayload, meta *DiscordMeta) (*DiscordPayload, error) {
+       var (
+               branchName = git.RefEndName(p.Ref)
+               commitDesc string
+       )
+
+       var titleLink string
+       if len(p.Commits) == 1 {
+               commitDesc = "1 new commit"
+               titleLink = p.Commits[0].URL
+       } else {
+               commitDesc = fmt.Sprintf("%d new commits", len(p.Commits))
+               titleLink = p.CompareURL
+       }
+       if titleLink == "" {
+               titleLink = p.Repo.HTMLURL + "/src/" + branchName
+       }
+
+       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 {
+               text += fmt.Sprintf("[%s](%s) %s - %s", commit.ID[:7], commit.URL,
+                       strings.TrimRight(commit.Message, "\r\n"), commit.Author.Name)
+               // add linebreak to each commit but the last
+               if i < len(p.Commits)-1 {
+                       text += "\n"
+               }
+       }
+
+       return &DiscordPayload{
+               Username:  meta.Username,
+               AvatarURL: meta.IconURL,
+               Embeds: []DiscordEmbed{
+                       {
+                               Title:       title,
+                               Description: text,
+                               URL:         titleLink,
+                               Color:       greenColor,
+                               Author: DiscordEmbedAuthor{
+                                       Name:    p.Sender.UserName,
+                                       URL:     setting.AppURL + p.Sender.UserName,
+                                       IconURL: p.Sender.AvatarURL,
+                               },
+                       },
+               },
+       }, nil
+}
+
+func getDiscordIssuesPayload(p *api.IssuePayload, meta *DiscordMeta) (*DiscordPayload, error) {
+       var text, title string
+       var color int
+       url := fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Issue.Index)
+       switch p.Action {
+       case api.HookIssueOpened:
+               title = fmt.Sprintf("[%s] Issue opened: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
+               text = p.Issue.Body
+               color = orangeColor
+       case api.HookIssueClosed:
+               title = fmt.Sprintf("[%s] Issue closed: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
+               color = redColor
+               text = p.Issue.Body
+       case api.HookIssueReOpened:
+               title = fmt.Sprintf("[%s] Issue re-opened: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
+               text = p.Issue.Body
+               color = yellowColor
+       case api.HookIssueEdited:
+               title = fmt.Sprintf("[%s] Issue edited: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
+               text = p.Issue.Body
+               color = yellowColor
+       case api.HookIssueAssigned:
+               title = fmt.Sprintf("[%s] Issue assigned to %s: #%d %s", p.Repository.FullName,
+                       p.Issue.Assignee.UserName, p.Index, p.Issue.Title)
+               text = p.Issue.Body
+               color = greenColor
+       case api.HookIssueUnassigned:
+               title = fmt.Sprintf("[%s] Issue unassigned: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
+               text = p.Issue.Body
+               color = yellowColor
+       case api.HookIssueLabelUpdated:
+               title = fmt.Sprintf("[%s] Issue labels updated: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
+               text = p.Issue.Body
+               color = yellowColor
+       case api.HookIssueLabelCleared:
+               title = fmt.Sprintf("[%s] Issue labels cleared: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
+               text = p.Issue.Body
+               color = yellowColor
+       case api.HookIssueSynchronized:
+               title = fmt.Sprintf("[%s] Issue synchronized: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
+               text = p.Issue.Body
+               color = yellowColor
+       case api.HookIssueMilestoned:
+               title = fmt.Sprintf("[%s] Issue milestone: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
+               text = p.Issue.Body
+               color = yellowColor
+       case api.HookIssueDemilestoned:
+               title = fmt.Sprintf("[%s] Issue clear milestone: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
+               text = p.Issue.Body
+               color = yellowColor
+       }
+
+       return &DiscordPayload{
+               Username:  meta.Username,
+               AvatarURL: meta.IconURL,
+               Embeds: []DiscordEmbed{
+                       {
+                               Title:       title,
+                               Description: text,
+                               URL:         url,
+                               Color:       color,
+                               Author: DiscordEmbedAuthor{
+                                       Name:    p.Sender.UserName,
+                                       URL:     setting.AppURL + p.Sender.UserName,
+                                       IconURL: p.Sender.AvatarURL,
+                               },
+                       },
+               },
+       }, nil
+}
+
+func getDiscordIssueCommentPayload(p *api.IssueCommentPayload, discord *DiscordMeta) (*DiscordPayload, error) {
+       title := fmt.Sprintf("#%d: %s", p.Issue.Index, p.Issue.Title)
+       url := fmt.Sprintf("%s/issues/%d#%s", p.Repository.HTMLURL, p.Issue.Index, models.CommentHashTag(p.Comment.ID))
+       content := ""
+       var color int
+       switch p.Action {
+       case api.HookIssueCommentCreated:
+               if p.IsPull {
+                       title = "New comment on pull request " + title
+                       color = greenColorLight
+               } else {
+                       title = "New comment on issue " + title
+                       color = orangeColorLight
+               }
+               content = p.Comment.Body
+       case api.HookIssueCommentEdited:
+               if p.IsPull {
+                       title = "Comment edited on pull request " + title
+               } else {
+                       title = "Comment edited on issue " + title
+               }
+               content = p.Comment.Body
+               color = yellowColor
+       case api.HookIssueCommentDeleted:
+               if p.IsPull {
+                       title = "Comment deleted on pull request " + title
+               } else {
+                       title = "Comment deleted on issue " + title
+               }
+               url = fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Issue.Index)
+               content = p.Comment.Body
+               color = redColor
+       }
+
+       title = fmt.Sprintf("[%s] %s", p.Repository.FullName, title)
+
+       return &DiscordPayload{
+               Username:  discord.Username,
+               AvatarURL: discord.IconURL,
+               Embeds: []DiscordEmbed{
+                       {
+                               Title:       title,
+                               Description: content,
+                               URL:         url,
+                               Color:       color,
+                               Author: DiscordEmbedAuthor{
+                                       Name:    p.Sender.UserName,
+                                       URL:     setting.AppURL + p.Sender.UserName,
+                                       IconURL: p.Sender.AvatarURL,
+                               },
+                       },
+               },
+       }, nil
+}
+
+func getDiscordPullRequestPayload(p *api.PullRequestPayload, meta *DiscordMeta) (*DiscordPayload, error) {
+       var text, title string
+       var color int
+       switch p.Action {
+       case api.HookIssueOpened:
+               title = fmt.Sprintf("[%s] Pull request opened: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
+               text = p.PullRequest.Body
+               color = greenColor
+       case api.HookIssueClosed:
+               if p.PullRequest.HasMerged {
+                       title = fmt.Sprintf("[%s] Pull request merged: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
+                       color = purpleColor
+               } else {
+                       title = fmt.Sprintf("[%s] Pull request closed: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
+                       color = redColor
+               }
+               text = p.PullRequest.Body
+       case api.HookIssueReOpened:
+               title = fmt.Sprintf("[%s] Pull request re-opened: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
+               text = p.PullRequest.Body
+               color = yellowColor
+       case api.HookIssueEdited:
+               title = fmt.Sprintf("[%s] Pull request edited: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
+               text = p.PullRequest.Body
+               color = yellowColor
+       case api.HookIssueAssigned:
+               list := make([]string, len(p.PullRequest.Assignees))
+               for i, user := range p.PullRequest.Assignees {
+                       list[i] = user.UserName
+               }
+               title = fmt.Sprintf("[%s] Pull request assigned to %s: #%d by %s", p.Repository.FullName,
+                       strings.Join(list, ", "),
+                       p.Index, p.PullRequest.Title)
+               text = p.PullRequest.Body
+               color = greenColor
+       case api.HookIssueUnassigned:
+               title = fmt.Sprintf("[%s] Pull request unassigned: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
+               text = p.PullRequest.Body
+               color = yellowColor
+       case api.HookIssueLabelUpdated:
+               title = fmt.Sprintf("[%s] Pull request labels updated: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
+               text = p.PullRequest.Body
+               color = yellowColor
+       case api.HookIssueLabelCleared:
+               title = fmt.Sprintf("[%s] Pull request labels cleared: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
+               text = p.PullRequest.Body
+               color = yellowColor
+       case api.HookIssueSynchronized:
+               title = fmt.Sprintf("[%s] Pull request synchronized: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
+               text = p.PullRequest.Body
+               color = yellowColor
+       case api.HookIssueMilestoned:
+               title = fmt.Sprintf("[%s] Pull request milestone: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
+               text = p.PullRequest.Body
+               color = yellowColor
+       case api.HookIssueDemilestoned:
+               title = fmt.Sprintf("[%s] Pull request clear milestone: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
+               text = p.PullRequest.Body
+               color = yellowColor
+       }
+
+       return &DiscordPayload{
+               Username:  meta.Username,
+               AvatarURL: meta.IconURL,
+               Embeds: []DiscordEmbed{
+                       {
+                               Title:       title,
+                               Description: text,
+                               URL:         p.PullRequest.HTMLURL,
+                               Color:       color,
+                               Author: DiscordEmbedAuthor{
+                                       Name:    p.Sender.UserName,
+                                       URL:     setting.AppURL + p.Sender.UserName,
+                                       IconURL: p.Sender.AvatarURL,
+                               },
+                       },
+               },
+       }, nil
+}
+
+func getDiscordPullRequestApprovalPayload(p *api.PullRequestPayload, meta *DiscordMeta, event models.HookEventType) (*DiscordPayload, error) {
+       var text, title string
+       var color int
+       switch p.Action {
+       case api.HookIssueSynchronized:
+               action, err := parseHookPullRequestEventType(event)
+               if err != nil {
+                       return nil, err
+               }
+
+               title = fmt.Sprintf("[%s] Pull request review %s: #%d %s", p.Repository.FullName, action, p.Index, p.PullRequest.Title)
+               text = p.Review.Content
+
+               switch event {
+               case models.HookEventPullRequestApproved:
+                       color = greenColor
+               case models.HookEventPullRequestRejected:
+                       color = redColor
+               case models.HookEventPullRequestComment:
+                       color = greyColor
+               default:
+                       color = yellowColor
+               }
+       }
+
+       return &DiscordPayload{
+               Username:  meta.Username,
+               AvatarURL: meta.IconURL,
+               Embeds: []DiscordEmbed{
+                       {
+                               Title:       title,
+                               Description: text,
+                               URL:         p.PullRequest.HTMLURL,
+                               Color:       color,
+                               Author: DiscordEmbedAuthor{
+                                       Name:    p.Sender.UserName,
+                                       URL:     setting.AppURL + p.Sender.UserName,
+                                       IconURL: p.Sender.AvatarURL,
+                               },
+                       },
+               },
+       }, nil
+}
+
+func getDiscordRepositoryPayload(p *api.RepositoryPayload, meta *DiscordMeta) (*DiscordPayload, error) {
+       var title, url string
+       var color int
+       switch p.Action {
+       case api.HookRepoCreated:
+               title = fmt.Sprintf("[%s] Repository created", p.Repository.FullName)
+               url = p.Repository.HTMLURL
+               color = greenColor
+       case api.HookRepoDeleted:
+               title = fmt.Sprintf("[%s] Repository deleted", p.Repository.FullName)
+               color = redColor
+       }
+
+       return &DiscordPayload{
+               Username:  meta.Username,
+               AvatarURL: meta.IconURL,
+               Embeds: []DiscordEmbed{
+                       {
+                               Title: title,
+                               URL:   url,
+                               Color: color,
+                               Author: DiscordEmbedAuthor{
+                                       Name:    p.Sender.UserName,
+                                       URL:     setting.AppURL + p.Sender.UserName,
+                                       IconURL: p.Sender.AvatarURL,
+                               },
+                       },
+               },
+       }, nil
+}
+
+func getDiscordReleasePayload(p *api.ReleasePayload, meta *DiscordMeta) (*DiscordPayload, error) {
+       var title, url string
+       var color int
+       switch p.Action {
+       case api.HookReleasePublished:
+               title = fmt.Sprintf("[%s] Release created", p.Release.TagName)
+               url = p.Release.URL
+               color = greenColor
+       case api.HookReleaseUpdated:
+               title = fmt.Sprintf("[%s] Release updated", p.Release.TagName)
+               url = p.Release.URL
+               color = yellowColor
+       case api.HookReleaseDeleted:
+               title = fmt.Sprintf("[%s] Release deleted", p.Release.TagName)
+               url = p.Release.URL
+               color = redColor
+       }
+
+       return &DiscordPayload{
+               Username:  meta.Username,
+               AvatarURL: meta.IconURL,
+               Embeds: []DiscordEmbed{
+                       {
+                               Title:       title,
+                               Description: p.Release.Note,
+                               URL:         url,
+                               Color:       color,
+                               Author: DiscordEmbedAuthor{
+                                       Name:    p.Sender.UserName,
+                                       URL:     setting.AppURL + p.Sender.UserName,
+                                       IconURL: p.Sender.AvatarURL,
+                               },
+                       },
+               },
+       }, nil
+}
+
+// GetDiscordPayload converts a discord webhook into a DiscordPayload
+func GetDiscordPayload(p api.Payloader, event models.HookEventType, meta string) (*DiscordPayload, error) {
+       s := new(DiscordPayload)
+
+       discord := &DiscordMeta{}
+       if err := json.Unmarshal([]byte(meta), &discord); err != nil {
+               return s, errors.New("GetDiscordPayload meta json:" + err.Error())
+       }
+
+       switch event {
+       case models.HookEventCreate:
+               return getDiscordCreatePayload(p.(*api.CreatePayload), discord)
+       case models.HookEventDelete:
+               return getDiscordDeletePayload(p.(*api.DeletePayload), discord)
+       case models.HookEventFork:
+               return getDiscordForkPayload(p.(*api.ForkPayload), discord)
+       case models.HookEventIssues:
+               return getDiscordIssuesPayload(p.(*api.IssuePayload), discord)
+       case models.HookEventIssueComment:
+               return getDiscordIssueCommentPayload(p.(*api.IssueCommentPayload), discord)
+       case models.HookEventPush:
+               return getDiscordPushPayload(p.(*api.PushPayload), discord)
+       case models.HookEventPullRequest:
+               return getDiscordPullRequestPayload(p.(*api.PullRequestPayload), discord)
+       case models.HookEventPullRequestRejected, models.HookEventPullRequestApproved, models.HookEventPullRequestComment:
+               return getDiscordPullRequestApprovalPayload(p.(*api.PullRequestPayload), discord, event)
+       case models.HookEventRepository:
+               return getDiscordRepositoryPayload(p.(*api.RepositoryPayload), discord)
+       case models.HookEventRelease:
+               return getDiscordReleasePayload(p.(*api.ReleasePayload), discord)
+       }
+
+       return s, nil
+}
+
+func parseHookPullRequestEventType(event models.HookEventType) (string, error) {
+
+       switch event {
+
+       case models.HookEventPullRequestApproved:
+               return "approved", nil
+       case models.HookEventPullRequestRejected:
+               return "rejected", nil
+       case models.HookEventPullRequestComment:
+               return "comment", nil
+
+       default:
+               return "", errors.New("unknown event type")
+       }
+}
diff --git a/modules/webhook/msteams.go b/modules/webhook/msteams.go
new file mode 100644 (file)
index 0000000..2636e29
--- /dev/null
@@ -0,0 +1,730 @@
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package webhook
+
+import (
+       "encoding/json"
+       "fmt"
+       "strings"
+
+       "code.gitea.io/gitea/models"
+       "code.gitea.io/gitea/modules/git"
+       api "code.gitea.io/gitea/modules/structs"
+)
+
+type (
+       // MSTeamsFact for Fact Structure
+       MSTeamsFact struct {
+               Name  string `json:"name"`
+               Value string `json:"value"`
+       }
+
+       // MSTeamsSection is a MessageCard section
+       MSTeamsSection struct {
+               ActivityTitle    string        `json:"activityTitle"`
+               ActivitySubtitle string        `json:"activitySubtitle"`
+               ActivityImage    string        `json:"activityImage"`
+               Facts            []MSTeamsFact `json:"facts"`
+               Text             string        `json:"text"`
+       }
+
+       // MSTeamsAction is an action (creates buttons, links etc)
+       MSTeamsAction struct {
+               Type    string                `json:"@type"`
+               Name    string                `json:"name"`
+               Targets []MSTeamsActionTarget `json:"targets,omitempty"`
+       }
+
+       // MSTeamsActionTarget is the actual link to follow, etc
+       MSTeamsActionTarget struct {
+               Os  string `json:"os"`
+               URI string `json:"uri"`
+       }
+
+       // MSTeamsPayload is the parent object
+       MSTeamsPayload struct {
+               Type            string           `json:"@type"`
+               Context         string           `json:"@context"`
+               ThemeColor      string           `json:"themeColor"`
+               Title           string           `json:"title"`
+               Summary         string           `json:"summary"`
+               Sections        []MSTeamsSection `json:"sections"`
+               PotentialAction []MSTeamsAction  `json:"potentialAction"`
+       }
+)
+
+// SetSecret sets the MSTeams secret
+func (p *MSTeamsPayload) SetSecret(_ string) {}
+
+// JSONPayload Marshals the MSTeamsPayload to json
+func (p *MSTeamsPayload) JSONPayload() ([]byte, error) {
+       data, err := json.MarshalIndent(p, "", "  ")
+       if err != nil {
+               return []byte{}, err
+       }
+       return data, nil
+}
+
+func getMSTeamsCreatePayload(p *api.CreatePayload) (*MSTeamsPayload, error) {
+       // created tag/branch
+       refName := git.RefEndName(p.Ref)
+       title := fmt.Sprintf("[%s] %s %s created", p.Repo.FullName, p.RefType, refName)
+
+       return &MSTeamsPayload{
+               Type:       "MessageCard",
+               Context:    "https://schema.org/extensions",
+               ThemeColor: fmt.Sprintf("%x", greenColor),
+               Title:      title,
+               Summary:    title,
+               Sections: []MSTeamsSection{
+                       {
+                               ActivityTitle:    p.Sender.FullName,
+                               ActivitySubtitle: p.Sender.UserName,
+                               ActivityImage:    p.Sender.AvatarURL,
+                               Facts: []MSTeamsFact{
+                                       {
+                                               Name:  "Repository:",
+                                               Value: p.Repo.FullName,
+                                       },
+                                       {
+                                               Name:  fmt.Sprintf("%s:", p.RefType),
+                                               Value: refName,
+                                       },
+                               },
+                       },
+               },
+               PotentialAction: []MSTeamsAction{
+                       {
+                               Type: "OpenUri",
+                               Name: "View in Gitea",
+                               Targets: []MSTeamsActionTarget{
+                                       {
+                                               Os:  "default",
+                                               URI: p.Repo.HTMLURL + "/src/" + refName,
+                                       },
+                               },
+                       },
+               },
+       }, nil
+}
+
+func getMSTeamsDeletePayload(p *api.DeletePayload) (*MSTeamsPayload, error) {
+       // deleted tag/branch
+       refName := git.RefEndName(p.Ref)
+       title := fmt.Sprintf("[%s] %s %s deleted", p.Repo.FullName, p.RefType, refName)
+
+       return &MSTeamsPayload{
+               Type:       "MessageCard",
+               Context:    "https://schema.org/extensions",
+               ThemeColor: fmt.Sprintf("%x", yellowColor),
+               Title:      title,
+               Summary:    title,
+               Sections: []MSTeamsSection{
+                       {
+                               ActivityTitle:    p.Sender.FullName,
+                               ActivitySubtitle: p.Sender.UserName,
+                               ActivityImage:    p.Sender.AvatarURL,
+                               Facts: []MSTeamsFact{
+                                       {
+                                               Name:  "Repository:",
+                                               Value: p.Repo.FullName,
+                                       },
+                                       {
+                                               Name:  fmt.Sprintf("%s:", p.RefType),
+                                               Value: refName,
+                                       },
+                               },
+                       },
+               },
+               PotentialAction: []MSTeamsAction{
+                       {
+                               Type: "OpenUri",
+                               Name: "View in Gitea",
+                               Targets: []MSTeamsActionTarget{
+                                       {
+                                               Os:  "default",
+                                               URI: p.Repo.HTMLURL + "/src/" + refName,
+                                       },
+                               },
+                       },
+               },
+       }, nil
+}
+
+func getMSTeamsForkPayload(p *api.ForkPayload) (*MSTeamsPayload, error) {
+       // fork
+       title := fmt.Sprintf("%s is forked to %s", p.Forkee.FullName, p.Repo.FullName)
+
+       return &MSTeamsPayload{
+               Type:       "MessageCard",
+               Context:    "https://schema.org/extensions",
+               ThemeColor: fmt.Sprintf("%x", greenColor),
+               Title:      title,
+               Summary:    title,
+               Sections: []MSTeamsSection{
+                       {
+                               ActivityTitle:    p.Sender.FullName,
+                               ActivitySubtitle: p.Sender.UserName,
+                               ActivityImage:    p.Sender.AvatarURL,
+                               Facts: []MSTeamsFact{
+                                       {
+                                               Name:  "Forkee:",
+                                               Value: p.Forkee.FullName,
+                                       },
+                                       {
+                                               Name:  "Repository:",
+                                               Value: p.Repo.FullName,
+                                       },
+                               },
+                       },
+               },
+               PotentialAction: []MSTeamsAction{
+                       {
+                               Type: "OpenUri",
+                               Name: "View in Gitea",
+                               Targets: []MSTeamsActionTarget{
+                                       {
+                                               Os:  "default",
+                                               URI: p.Repo.HTMLURL,
+                                       },
+                               },
+                       },
+               },
+       }, nil
+}
+
+func getMSTeamsPushPayload(p *api.PushPayload) (*MSTeamsPayload, error) {
+       var (
+               branchName = git.RefEndName(p.Ref)
+               commitDesc string
+       )
+
+       var titleLink string
+       if len(p.Commits) == 1 {
+               commitDesc = "1 new commit"
+               titleLink = p.Commits[0].URL
+       } else {
+               commitDesc = fmt.Sprintf("%d new commits", len(p.Commits))
+               titleLink = p.CompareURL
+       }
+       if titleLink == "" {
+               titleLink = p.Repo.HTMLURL + "/src/" + branchName
+       }
+
+       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 {
+               text += fmt.Sprintf("[%s](%s) %s - %s", commit.ID[:7], commit.URL,
+                       strings.TrimRight(commit.Message, "\r\n"), commit.Author.Name)
+               // add linebreak to each commit but the last
+               if i < len(p.Commits)-1 {
+                       text += "\n"
+               }
+       }
+
+       return &MSTeamsPayload{
+               Type:       "MessageCard",
+               Context:    "https://schema.org/extensions",
+               ThemeColor: fmt.Sprintf("%x", greenColor),
+               Title:      title,
+               Summary:    title,
+               Sections: []MSTeamsSection{
+                       {
+                               ActivityTitle:    p.Sender.FullName,
+                               ActivitySubtitle: p.Sender.UserName,
+                               ActivityImage:    p.Sender.AvatarURL,
+                               Text:             text,
+                               Facts: []MSTeamsFact{
+                                       {
+                                               Name:  "Repository:",
+                                               Value: p.Repo.FullName,
+                                       },
+                                       {
+                                               Name:  "Commit count:",
+                                               Value: fmt.Sprintf("%d", len(p.Commits)),
+                                       },
+                               },
+                       },
+               },
+               PotentialAction: []MSTeamsAction{
+                       {
+                               Type: "OpenUri",
+                               Name: "View in Gitea",
+                               Targets: []MSTeamsActionTarget{
+                                       {
+                                               Os:  "default",
+                                               URI: titleLink,
+                                       },
+                               },
+                       },
+               },
+       }, nil
+}
+
+func getMSTeamsIssuesPayload(p *api.IssuePayload) (*MSTeamsPayload, error) {
+       var text, title string
+       var color int
+       url := fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Issue.Index)
+       switch p.Action {
+       case api.HookIssueOpened:
+               title = fmt.Sprintf("[%s] Issue opened: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
+               text = p.Issue.Body
+               color = orangeColor
+       case api.HookIssueClosed:
+               title = fmt.Sprintf("[%s] Issue closed: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
+               color = redColor
+               text = p.Issue.Body
+       case api.HookIssueReOpened:
+               title = fmt.Sprintf("[%s] Issue re-opened: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
+               text = p.Issue.Body
+               color = yellowColor
+       case api.HookIssueEdited:
+               title = fmt.Sprintf("[%s] Issue edited: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
+               text = p.Issue.Body
+               color = yellowColor
+       case api.HookIssueAssigned:
+               title = fmt.Sprintf("[%s] Issue assigned to %s: #%d %s", p.Repository.FullName,
+                       p.Issue.Assignee.UserName, p.Index, p.Issue.Title)
+               text = p.Issue.Body
+               color = greenColor
+       case api.HookIssueUnassigned:
+               title = fmt.Sprintf("[%s] Issue unassigned: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
+               text = p.Issue.Body
+               color = yellowColor
+       case api.HookIssueLabelUpdated:
+               title = fmt.Sprintf("[%s] Issue labels updated: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
+               text = p.Issue.Body
+               color = yellowColor
+       case api.HookIssueLabelCleared:
+               title = fmt.Sprintf("[%s] Issue labels cleared: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
+               text = p.Issue.Body
+               color = yellowColor
+       case api.HookIssueSynchronized:
+               title = fmt.Sprintf("[%s] Issue synchronized: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
+               text = p.Issue.Body
+               color = yellowColor
+       case api.HookIssueMilestoned:
+               title = fmt.Sprintf("[%s] Issue milestone: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
+               text = p.Issue.Body
+               color = yellowColor
+       case api.HookIssueDemilestoned:
+               title = fmt.Sprintf("[%s] Issue clear milestone: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
+               text = p.Issue.Body
+               color = yellowColor
+       }
+
+       return &MSTeamsPayload{
+               Type:       "MessageCard",
+               Context:    "https://schema.org/extensions",
+               ThemeColor: fmt.Sprintf("%x", color),
+               Title:      title,
+               Summary:    title,
+               Sections: []MSTeamsSection{
+                       {
+                               ActivityTitle:    p.Sender.FullName,
+                               ActivitySubtitle: p.Sender.UserName,
+                               ActivityImage:    p.Sender.AvatarURL,
+                               Text:             text,
+                               Facts: []MSTeamsFact{
+                                       {
+                                               Name:  "Repository:",
+                                               Value: p.Repository.FullName,
+                                       },
+                                       {
+                                               Name:  "Issue #:",
+                                               Value: fmt.Sprintf("%d", p.Issue.ID),
+                                       },
+                               },
+                       },
+               },
+               PotentialAction: []MSTeamsAction{
+                       {
+                               Type: "OpenUri",
+                               Name: "View in Gitea",
+                               Targets: []MSTeamsActionTarget{
+                                       {
+                                               Os:  "default",
+                                               URI: url,
+                                       },
+                               },
+                       },
+               },
+       }, nil
+}
+
+func getMSTeamsIssueCommentPayload(p *api.IssueCommentPayload) (*MSTeamsPayload, error) {
+       title := fmt.Sprintf("#%d: %s", p.Issue.Index, p.Issue.Title)
+       url := fmt.Sprintf("%s/issues/%d#%s", p.Repository.HTMLURL, p.Issue.Index, models.CommentHashTag(p.Comment.ID))
+       content := ""
+       var color int
+       switch p.Action {
+       case api.HookIssueCommentCreated:
+               if p.IsPull {
+                       title = "New comment on pull request " + title
+                       color = greenColorLight
+               } else {
+                       title = "New comment on issue " + title
+                       color = orangeColorLight
+               }
+               content = p.Comment.Body
+       case api.HookIssueCommentEdited:
+               if p.IsPull {
+                       title = "Comment edited on pull request " + title
+               } else {
+                       title = "Comment edited on issue " + title
+               }
+               content = p.Comment.Body
+               color = yellowColor
+       case api.HookIssueCommentDeleted:
+               if p.IsPull {
+                       title = "Comment deleted on pull request " + title
+               } else {
+                       title = "Comment deleted on issue " + title
+               }
+               url = fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Issue.Index)
+               content = p.Comment.Body
+               color = redColor
+       }
+
+       title = fmt.Sprintf("[%s] %s", p.Repository.FullName, title)
+
+       return &MSTeamsPayload{
+               Type:       "MessageCard",
+               Context:    "https://schema.org/extensions",
+               ThemeColor: fmt.Sprintf("%x", color),
+               Title:      title,
+               Summary:    title,
+               Sections: []MSTeamsSection{
+                       {
+                               ActivityTitle:    p.Sender.FullName,
+                               ActivitySubtitle: p.Sender.UserName,
+                               ActivityImage:    p.Sender.AvatarURL,
+                               Text:             content,
+                               Facts: []MSTeamsFact{
+                                       {
+                                               Name:  "Repository:",
+                                               Value: p.Repository.FullName,
+                                       },
+                                       {
+                                               Name:  "Issue #:",
+                                               Value: fmt.Sprintf("%d", p.Issue.ID),
+                                       },
+                               },
+                       },
+               },
+               PotentialAction: []MSTeamsAction{
+                       {
+                               Type: "OpenUri",
+                               Name: "View in Gitea",
+                               Targets: []MSTeamsActionTarget{
+                                       {
+                                               Os:  "default",
+                                               URI: url,
+                                       },
+                               },
+                       },
+               },
+       }, nil
+}
+
+func getMSTeamsPullRequestPayload(p *api.PullRequestPayload) (*MSTeamsPayload, error) {
+       var text, title string
+       var color int
+       switch p.Action {
+       case api.HookIssueOpened:
+               title = fmt.Sprintf("[%s] Pull request opened: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
+               text = p.PullRequest.Body
+               color = greenColor
+       case api.HookIssueClosed:
+               if p.PullRequest.HasMerged {
+                       title = fmt.Sprintf("[%s] Pull request merged: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
+                       color = purpleColor
+               } else {
+                       title = fmt.Sprintf("[%s] Pull request closed: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
+                       color = redColor
+               }
+               text = p.PullRequest.Body
+       case api.HookIssueReOpened:
+               title = fmt.Sprintf("[%s] Pull request re-opened: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
+               text = p.PullRequest.Body
+               color = yellowColor
+       case api.HookIssueEdited:
+               title = fmt.Sprintf("[%s] Pull request edited: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
+               text = p.PullRequest.Body
+               color = yellowColor
+       case api.HookIssueAssigned:
+               list := make([]string, len(p.PullRequest.Assignees))
+               for i, user := range p.PullRequest.Assignees {
+                       list[i] = user.UserName
+               }
+               title = fmt.Sprintf("[%s] Pull request assigned to %s: #%d by %s", p.Repository.FullName,
+                       strings.Join(list, ", "),
+                       p.Index, p.PullRequest.Title)
+               text = p.PullRequest.Body
+               color = greenColor
+       case api.HookIssueUnassigned:
+               title = fmt.Sprintf("[%s] Pull request unassigned: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
+               text = p.PullRequest.Body
+               color = yellowColor
+       case api.HookIssueLabelUpdated:
+               title = fmt.Sprintf("[%s] Pull request labels updated: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
+               text = p.PullRequest.Body
+               color = yellowColor
+       case api.HookIssueLabelCleared:
+               title = fmt.Sprintf("[%s] Pull request labels cleared: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
+               text = p.PullRequest.Body
+               color = yellowColor
+       case api.HookIssueSynchronized:
+               title = fmt.Sprintf("[%s] Pull request synchronized: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
+               text = p.PullRequest.Body
+               color = yellowColor
+       case api.HookIssueMilestoned:
+               title = fmt.Sprintf("[%s] Pull request milestone: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
+               text = p.PullRequest.Body
+               color = yellowColor
+       case api.HookIssueDemilestoned:
+               title = fmt.Sprintf("[%s] Pull request clear milestone: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
+               text = p.PullRequest.Body
+               color = yellowColor
+       }
+
+       return &MSTeamsPayload{
+               Type:       "MessageCard",
+               Context:    "https://schema.org/extensions",
+               ThemeColor: fmt.Sprintf("%x", color),
+               Title:      title,
+               Summary:    title,
+               Sections: []MSTeamsSection{
+                       {
+                               ActivityTitle:    p.Sender.FullName,
+                               ActivitySubtitle: p.Sender.UserName,
+                               ActivityImage:    p.Sender.AvatarURL,
+                               Text:             text,
+                               Facts: []MSTeamsFact{
+                                       {
+                                               Name:  "Repository:",
+                                               Value: p.Repository.FullName,
+                                       },
+                                       {
+                                               Name:  "Pull request #:",
+                                               Value: fmt.Sprintf("%d", p.PullRequest.ID),
+                                       },
+                               },
+                       },
+               },
+               PotentialAction: []MSTeamsAction{
+                       {
+                               Type: "OpenUri",
+                               Name: "View in Gitea",
+                               Targets: []MSTeamsActionTarget{
+                                       {
+                                               Os:  "default",
+                                               URI: p.PullRequest.HTMLURL,
+                                       },
+                               },
+                       },
+               },
+       }, nil
+}
+
+func getMSTeamsPullRequestApprovalPayload(p *api.PullRequestPayload, event models.HookEventType) (*MSTeamsPayload, error) {
+       var text, title string
+       var color int
+       switch p.Action {
+       case api.HookIssueSynchronized:
+               action, err := parseHookPullRequestEventType(event)
+               if err != nil {
+                       return nil, err
+               }
+
+               title = fmt.Sprintf("[%s] Pull request review %s: #%d %s", p.Repository.FullName, action, p.Index, p.PullRequest.Title)
+               text = p.Review.Content
+
+               switch event {
+               case models.HookEventPullRequestApproved:
+                       color = greenColor
+               case models.HookEventPullRequestRejected:
+                       color = redColor
+               case models.HookEventPullRequestComment:
+                       color = greyColor
+               default:
+                       color = yellowColor
+               }
+       }
+
+       return &MSTeamsPayload{
+               Type:       "MessageCard",
+               Context:    "https://schema.org/extensions",
+               ThemeColor: fmt.Sprintf("%x", color),
+               Title:      title,
+               Summary:    title,
+               Sections: []MSTeamsSection{
+                       {
+                               ActivityTitle:    p.Sender.FullName,
+                               ActivitySubtitle: p.Sender.UserName,
+                               ActivityImage:    p.Sender.AvatarURL,
+                               Text:             text,
+                               Facts: []MSTeamsFact{
+                                       {
+                                               Name:  "Repository:",
+                                               Value: p.Repository.FullName,
+                                       },
+                                       {
+                                               Name:  "Pull request #:",
+                                               Value: fmt.Sprintf("%d", p.PullRequest.ID),
+                                       },
+                               },
+                       },
+               },
+               PotentialAction: []MSTeamsAction{
+                       {
+                               Type: "OpenUri",
+                               Name: "View in Gitea",
+                               Targets: []MSTeamsActionTarget{
+                                       {
+                                               Os:  "default",
+                                               URI: p.PullRequest.HTMLURL,
+                                       },
+                               },
+                       },
+               },
+       }, nil
+}
+
+func getMSTeamsRepositoryPayload(p *api.RepositoryPayload) (*MSTeamsPayload, error) {
+       var title, url string
+       var color int
+       switch p.Action {
+       case api.HookRepoCreated:
+               title = fmt.Sprintf("[%s] Repository created", p.Repository.FullName)
+               url = p.Repository.HTMLURL
+               color = greenColor
+       case api.HookRepoDeleted:
+               title = fmt.Sprintf("[%s] Repository deleted", p.Repository.FullName)
+               color = yellowColor
+       }
+
+       return &MSTeamsPayload{
+               Type:       "MessageCard",
+               Context:    "https://schema.org/extensions",
+               ThemeColor: fmt.Sprintf("%x", color),
+               Title:      title,
+               Summary:    title,
+               Sections: []MSTeamsSection{
+                       {
+                               ActivityTitle:    p.Sender.FullName,
+                               ActivitySubtitle: p.Sender.UserName,
+                               ActivityImage:    p.Sender.AvatarURL,
+                               Facts: []MSTeamsFact{
+                                       {
+                                               Name:  "Repository:",
+                                               Value: p.Repository.FullName,
+                                       },
+                               },
+                       },
+               },
+               PotentialAction: []MSTeamsAction{
+                       {
+                               Type: "OpenUri",
+                               Name: "View in Gitea",
+                               Targets: []MSTeamsActionTarget{
+                                       {
+                                               Os:  "default",
+                                               URI: url,
+                                       },
+                               },
+                       },
+               },
+       }, nil
+}
+
+func getMSTeamsReleasePayload(p *api.ReleasePayload) (*MSTeamsPayload, error) {
+       var title, url string
+       var color int
+       switch p.Action {
+       case api.HookReleasePublished:
+               title = fmt.Sprintf("[%s] Release created", p.Release.TagName)
+               url = p.Release.URL
+               color = greenColor
+       case api.HookReleaseUpdated:
+               title = fmt.Sprintf("[%s] Release updated", p.Release.TagName)
+               url = p.Release.URL
+               color = greenColor
+       case api.HookReleaseDeleted:
+               title = fmt.Sprintf("[%s] Release deleted", p.Release.TagName)
+               url = p.Release.URL
+               color = greenColor
+       }
+
+       return &MSTeamsPayload{
+               Type:       "MessageCard",
+               Context:    "https://schema.org/extensions",
+               ThemeColor: fmt.Sprintf("%x", color),
+               Title:      title,
+               Summary:    title,
+               Sections: []MSTeamsSection{
+                       {
+                               ActivityTitle:    p.Sender.FullName,
+                               ActivitySubtitle: p.Sender.UserName,
+                               ActivityImage:    p.Sender.AvatarURL,
+                               Text:             p.Release.Note,
+                               Facts: []MSTeamsFact{
+                                       {
+                                               Name:  "Repository:",
+                                               Value: p.Repository.FullName,
+                                       },
+                                       {
+                                               Name:  "Tag:",
+                                               Value: p.Release.TagName,
+                                       },
+                               },
+                       },
+               },
+               PotentialAction: []MSTeamsAction{
+                       {
+                               Type: "OpenUri",
+                               Name: "View in Gitea",
+                               Targets: []MSTeamsActionTarget{
+                                       {
+                                               Os:  "default",
+                                               URI: url,
+                                       },
+                               },
+                       },
+               },
+       }, nil
+}
+
+// GetMSTeamsPayload converts a MSTeams webhook into a MSTeamsPayload
+func GetMSTeamsPayload(p api.Payloader, event models.HookEventType, meta string) (*MSTeamsPayload, error) {
+       s := new(MSTeamsPayload)
+
+       switch event {
+       case models.HookEventCreate:
+               return getMSTeamsCreatePayload(p.(*api.CreatePayload))
+       case models.HookEventDelete:
+               return getMSTeamsDeletePayload(p.(*api.DeletePayload))
+       case models.HookEventFork:
+               return getMSTeamsForkPayload(p.(*api.ForkPayload))
+       case models.HookEventIssues:
+               return getMSTeamsIssuesPayload(p.(*api.IssuePayload))
+       case models.HookEventIssueComment:
+               return getMSTeamsIssueCommentPayload(p.(*api.IssueCommentPayload))
+       case models.HookEventPush:
+               return getMSTeamsPushPayload(p.(*api.PushPayload))
+       case models.HookEventPullRequest:
+               return getMSTeamsPullRequestPayload(p.(*api.PullRequestPayload))
+       case models.HookEventPullRequestRejected, models.HookEventPullRequestApproved, models.HookEventPullRequestComment:
+               return getMSTeamsPullRequestApprovalPayload(p.(*api.PullRequestPayload), event)
+       case models.HookEventRepository:
+               return getMSTeamsRepositoryPayload(p.(*api.RepositoryPayload))
+       case models.HookEventRelease:
+               return getMSTeamsReleasePayload(p.(*api.ReleasePayload))
+       }
+
+       return s, nil
+}
diff --git a/modules/webhook/slack.go b/modules/webhook/slack.go
new file mode 100644 (file)
index 0000000..7d844bf
--- /dev/null
@@ -0,0 +1,434 @@
+// Copyright 2014 The Gogs Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package webhook
+
+import (
+       "encoding/json"
+       "errors"
+       "fmt"
+       "strings"
+
+       "code.gitea.io/gitea/models"
+       "code.gitea.io/gitea/modules/git"
+       "code.gitea.io/gitea/modules/log"
+       "code.gitea.io/gitea/modules/setting"
+       api "code.gitea.io/gitea/modules/structs"
+)
+
+// SlackMeta contains the slack metadata
+type SlackMeta struct {
+       Channel  string `json:"channel"`
+       Username string `json:"username"`
+       IconURL  string `json:"icon_url"`
+       Color    string `json:"color"`
+}
+
+// GetSlackHook returns slack metadata
+func GetSlackHook(w *models.Webhook) *SlackMeta {
+       s := &SlackMeta{}
+       if err := json.Unmarshal([]byte(w.Meta), s); err != nil {
+               log.Error("webhook.GetSlackHook(%d): %v", w.ID, err)
+       }
+       return s
+}
+
+// SlackPayload contains the information about the slack channel
+type SlackPayload struct {
+       Channel     string            `json:"channel"`
+       Text        string            `json:"text"`
+       Username    string            `json:"username"`
+       IconURL     string            `json:"icon_url"`
+       UnfurlLinks int               `json:"unfurl_links"`
+       LinkNames   int               `json:"link_names"`
+       Attachments []SlackAttachment `json:"attachments"`
+}
+
+// SlackAttachment contains the slack message
+type SlackAttachment struct {
+       Fallback string `json:"fallback"`
+       Color    string `json:"color"`
+       Title    string `json:"title"`
+       Text     string `json:"text"`
+}
+
+// SetSecret sets the slack secret
+func (p *SlackPayload) SetSecret(_ string) {}
+
+// JSONPayload Marshals the SlackPayload to json
+func (p *SlackPayload) JSONPayload() ([]byte, error) {
+       data, err := json.MarshalIndent(p, "", "  ")
+       if err != nil {
+               return []byte{}, err
+       }
+       return data, nil
+}
+
+// SlackTextFormatter replaces &, <, > with HTML characters
+// see: https://api.slack.com/docs/formatting
+func SlackTextFormatter(s string) string {
+       // replace & < >
+       s = strings.Replace(s, "&", "&amp;", -1)
+       s = strings.Replace(s, "<", "&lt;", -1)
+       s = strings.Replace(s, ">", "&gt;", -1)
+       return s
+}
+
+// SlackShortTextFormatter replaces &, <, > with HTML characters
+func SlackShortTextFormatter(s string) string {
+       s = strings.Split(s, "\n")[0]
+       // replace & < >
+       s = strings.Replace(s, "&", "&amp;", -1)
+       s = strings.Replace(s, "<", "&lt;", -1)
+       s = strings.Replace(s, ">", "&gt;", -1)
+       return s
+}
+
+// SlackLinkFormatter creates a link compatible with slack
+func SlackLinkFormatter(url string, text string) string {
+       return fmt.Sprintf("<%s|%s>", url, SlackTextFormatter(text))
+}
+
+// SlackLinkToRef slack-formatter link to a repo ref
+func SlackLinkToRef(repoURL, ref string) string {
+       refName := git.RefEndName(ref)
+       switch {
+       case strings.HasPrefix(ref, git.BranchPrefix):
+               return SlackLinkFormatter(repoURL+"/src/branch/"+refName, refName)
+       case strings.HasPrefix(ref, git.TagPrefix):
+               return SlackLinkFormatter(repoURL+"/src/tag/"+refName, refName)
+       default:
+               return SlackLinkFormatter(repoURL+"/src/commit/"+refName, refName)
+       }
+}
+
+func getSlackCreatePayload(p *api.CreatePayload, slack *SlackMeta) (*SlackPayload, error) {
+       repoLink := SlackLinkFormatter(p.Repo.HTMLURL, p.Repo.Name)
+       refLink := SlackLinkToRef(p.Repo.HTMLURL, p.Ref)
+       text := fmt.Sprintf("[%s:%s] %s created by %s", repoLink, refLink, p.RefType, p.Sender.UserName)
+
+       return &SlackPayload{
+               Channel:  slack.Channel,
+               Text:     text,
+               Username: slack.Username,
+               IconURL:  slack.IconURL,
+       }, nil
+}
+
+// getSlackDeletePayload composes Slack payload for delete a branch or tag.
+func getSlackDeletePayload(p *api.DeletePayload, slack *SlackMeta) (*SlackPayload, error) {
+       refName := git.RefEndName(p.Ref)
+       repoLink := SlackLinkFormatter(p.Repo.HTMLURL, p.Repo.Name)
+       text := fmt.Sprintf("[%s:%s] %s deleted by %s", repoLink, refName, p.RefType, p.Sender.UserName)
+       return &SlackPayload{
+               Channel:  slack.Channel,
+               Text:     text,
+               Username: slack.Username,
+               IconURL:  slack.IconURL,
+       }, nil
+}
+
+// getSlackForkPayload composes Slack payload for forked by a repository.
+func getSlackForkPayload(p *api.ForkPayload, slack *SlackMeta) (*SlackPayload, error) {
+       baseLink := SlackLinkFormatter(p.Forkee.HTMLURL, p.Forkee.FullName)
+       forkLink := SlackLinkFormatter(p.Repo.HTMLURL, p.Repo.FullName)
+       text := fmt.Sprintf("%s is forked to %s", baseLink, forkLink)
+       return &SlackPayload{
+               Channel:  slack.Channel,
+               Text:     text,
+               Username: slack.Username,
+               IconURL:  slack.IconURL,
+       }, nil
+}
+
+func getSlackIssuesPayload(p *api.IssuePayload, slack *SlackMeta) (*SlackPayload, error) {
+       senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
+       titleLink := SlackLinkFormatter(fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index),
+               fmt.Sprintf("#%d %s", p.Index, p.Issue.Title))
+       var text, title, attachmentText string
+       switch p.Action {
+       case api.HookIssueOpened:
+               text = fmt.Sprintf("[%s] Issue submitted by %s", p.Repository.FullName, senderLink)
+               title = titleLink
+               attachmentText = SlackTextFormatter(p.Issue.Body)
+       case api.HookIssueClosed:
+               text = fmt.Sprintf("[%s] Issue closed: %s by %s", p.Repository.FullName, titleLink, senderLink)
+       case api.HookIssueReOpened:
+               text = fmt.Sprintf("[%s] Issue re-opened: %s by %s", p.Repository.FullName, titleLink, senderLink)
+       case api.HookIssueEdited:
+               text = fmt.Sprintf("[%s] Issue edited: %s by %s", p.Repository.FullName, titleLink, senderLink)
+               attachmentText = SlackTextFormatter(p.Issue.Body)
+       case api.HookIssueAssigned:
+               text = fmt.Sprintf("[%s] Issue assigned to %s: %s by %s", p.Repository.FullName,
+                       SlackLinkFormatter(setting.AppURL+p.Issue.Assignee.UserName, p.Issue.Assignee.UserName),
+                       titleLink, senderLink)
+       case api.HookIssueUnassigned:
+               text = fmt.Sprintf("[%s] Issue unassigned: %s by %s", p.Repository.FullName, titleLink, senderLink)
+       case api.HookIssueLabelUpdated:
+               text = fmt.Sprintf("[%s] Issue labels updated: %s by %s", p.Repository.FullName, titleLink, senderLink)
+       case api.HookIssueLabelCleared:
+               text = fmt.Sprintf("[%s] Issue labels cleared: %s by %s", p.Repository.FullName, titleLink, senderLink)
+       case api.HookIssueSynchronized:
+               text = fmt.Sprintf("[%s] Issue synchronized: %s by %s", p.Repository.FullName, titleLink, senderLink)
+       case api.HookIssueMilestoned:
+               text = fmt.Sprintf("[%s] Issue milestoned: #%s %s", p.Repository.FullName, titleLink, senderLink)
+       case api.HookIssueDemilestoned:
+               text = fmt.Sprintf("[%s] Issue milestone cleared: #%s %s", p.Repository.FullName, titleLink, senderLink)
+       }
+
+       return &SlackPayload{
+               Channel:  slack.Channel,
+               Text:     text,
+               Username: slack.Username,
+               IconURL:  slack.IconURL,
+               Attachments: []SlackAttachment{{
+                       Color: slack.Color,
+                       Title: title,
+                       Text:  attachmentText,
+               }},
+       }, nil
+}
+
+func getSlackIssueCommentPayload(p *api.IssueCommentPayload, slack *SlackMeta) (*SlackPayload, error) {
+       senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
+       titleLink := SlackLinkFormatter(fmt.Sprintf("%s/issues/%d#%s", p.Repository.HTMLURL, p.Issue.Index, models.CommentHashTag(p.Comment.ID)),
+               fmt.Sprintf("#%d %s", p.Issue.Index, p.Issue.Title))
+       var text, title, attachmentText string
+       switch p.Action {
+       case api.HookIssueCommentCreated:
+               text = fmt.Sprintf("[%s] New comment created by %s", p.Repository.FullName, senderLink)
+               title = titleLink
+               attachmentText = SlackTextFormatter(p.Comment.Body)
+       case api.HookIssueCommentEdited:
+               text = fmt.Sprintf("[%s] Comment edited by %s", p.Repository.FullName, senderLink)
+               title = titleLink
+               attachmentText = SlackTextFormatter(p.Comment.Body)
+       case api.HookIssueCommentDeleted:
+               text = fmt.Sprintf("[%s] Comment deleted by %s", p.Repository.FullName, senderLink)
+               title = SlackLinkFormatter(fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Issue.Index),
+                       fmt.Sprintf("#%d %s", p.Issue.Index, p.Issue.Title))
+               attachmentText = SlackTextFormatter(p.Comment.Body)
+       }
+
+       return &SlackPayload{
+               Channel:  slack.Channel,
+               Text:     text,
+               Username: slack.Username,
+               IconURL:  slack.IconURL,
+               Attachments: []SlackAttachment{{
+                       Color: slack.Color,
+                       Title: title,
+                       Text:  attachmentText,
+               }},
+       }, nil
+}
+
+func getSlackReleasePayload(p *api.ReleasePayload, slack *SlackMeta) (*SlackPayload, error) {
+       repoLink := SlackLinkFormatter(p.Repository.HTMLURL, p.Repository.Name)
+       refLink := SlackLinkFormatter(p.Repository.HTMLURL+"/src/"+p.Release.TagName, p.Release.TagName)
+       var text string
+
+       switch p.Action {
+       case api.HookReleasePublished:
+               text = fmt.Sprintf("[%s] new release %s published by %s", repoLink, refLink, p.Sender.UserName)
+       case api.HookReleaseUpdated:
+               text = fmt.Sprintf("[%s] new release %s updated by %s", repoLink, refLink, p.Sender.UserName)
+       case api.HookReleaseDeleted:
+               text = fmt.Sprintf("[%s] new release %s deleted by %s", repoLink, refLink, p.Sender.UserName)
+       }
+
+       return &SlackPayload{
+               Channel:  slack.Channel,
+               Text:     text,
+               Username: slack.Username,
+               IconURL:  slack.IconURL,
+       }, nil
+}
+
+func getSlackPushPayload(p *api.PushPayload, slack *SlackMeta) (*SlackPayload, error) {
+       // n new commits
+       var (
+               commitDesc   string
+               commitString string
+       )
+
+       if len(p.Commits) == 1 {
+               commitDesc = "1 new commit"
+       } else {
+               commitDesc = fmt.Sprintf("%d new commits", len(p.Commits))
+       }
+       if len(p.CompareURL) > 0 {
+               commitString = SlackLinkFormatter(p.CompareURL, commitDesc)
+       } else {
+               commitString = commitDesc
+       }
+
+       repoLink := SlackLinkFormatter(p.Repo.HTMLURL, p.Repo.Name)
+       branchLink := SlackLinkToRef(p.Repo.HTMLURL, p.Ref)
+       text := fmt.Sprintf("[%s:%s] %s pushed by %s", repoLink, branchLink, commitString, p.Pusher.UserName)
+
+       var attachmentText string
+       // for each commit, generate attachment text
+       for i, commit := range p.Commits {
+               attachmentText += fmt.Sprintf("%s: %s - %s", SlackLinkFormatter(commit.URL, commit.ID[:7]), SlackShortTextFormatter(commit.Message), SlackTextFormatter(commit.Author.Name))
+               // add linebreak to each commit but the last
+               if i < len(p.Commits)-1 {
+                       attachmentText += "\n"
+               }
+       }
+
+       return &SlackPayload{
+               Channel:  slack.Channel,
+               Text:     text,
+               Username: slack.Username,
+               IconURL:  slack.IconURL,
+               Attachments: []SlackAttachment{{
+                       Color: slack.Color,
+                       Text:  attachmentText,
+               }},
+       }, nil
+}
+
+func getSlackPullRequestPayload(p *api.PullRequestPayload, slack *SlackMeta) (*SlackPayload, error) {
+       senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
+       titleLink := SlackLinkFormatter(fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index),
+               fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title))
+       var text, title, attachmentText string
+       switch p.Action {
+       case api.HookIssueOpened:
+               text = fmt.Sprintf("[%s] Pull request submitted by %s", p.Repository.FullName, senderLink)
+               title = titleLink
+               attachmentText = SlackTextFormatter(p.PullRequest.Body)
+       case api.HookIssueClosed:
+               if p.PullRequest.HasMerged {
+                       text = fmt.Sprintf("[%s] Pull request merged: %s by %s", p.Repository.FullName, titleLink, senderLink)
+               } else {
+                       text = fmt.Sprintf("[%s] Pull request closed: %s by %s", p.Repository.FullName, titleLink, senderLink)
+               }
+       case api.HookIssueReOpened:
+               text = fmt.Sprintf("[%s] Pull request re-opened: %s by %s", p.Repository.FullName, titleLink, senderLink)
+       case api.HookIssueEdited:
+               text = fmt.Sprintf("[%s] Pull request edited: %s by %s", p.Repository.FullName, titleLink, senderLink)
+               attachmentText = SlackTextFormatter(p.PullRequest.Body)
+       case api.HookIssueAssigned:
+               list := make([]string, len(p.PullRequest.Assignees))
+               for i, user := range p.PullRequest.Assignees {
+                       list[i] = SlackLinkFormatter(setting.AppURL+user.UserName, user.UserName)
+               }
+               text = fmt.Sprintf("[%s] Pull request assigned to %s: %s by %s", p.Repository.FullName,
+                       strings.Join(list, ", "),
+                       titleLink, senderLink)
+       case api.HookIssueUnassigned:
+               text = fmt.Sprintf("[%s] Pull request unassigned: %s by %s", p.Repository.FullName, titleLink, senderLink)
+       case api.HookIssueLabelUpdated:
+               text = fmt.Sprintf("[%s] Pull request labels updated: %s by %s", p.Repository.FullName, titleLink, senderLink)
+       case api.HookIssueLabelCleared:
+               text = fmt.Sprintf("[%s] Pull request labels cleared: %s by %s", p.Repository.FullName, titleLink, senderLink)
+       case api.HookIssueSynchronized:
+               text = fmt.Sprintf("[%s] Pull request synchronized: %s by %s", p.Repository.FullName, titleLink, senderLink)
+       case api.HookIssueMilestoned:
+               text = fmt.Sprintf("[%s] Pull request milestoned: #%s %s", p.Repository.FullName, titleLink, senderLink)
+       case api.HookIssueDemilestoned:
+               text = fmt.Sprintf("[%s] Pull request milestone cleared: #%s %s", p.Repository.FullName, titleLink, senderLink)
+       }
+
+       return &SlackPayload{
+               Channel:  slack.Channel,
+               Text:     text,
+               Username: slack.Username,
+               IconURL:  slack.IconURL,
+               Attachments: []SlackAttachment{{
+                       Color: slack.Color,
+                       Title: title,
+                       Text:  attachmentText,
+               }},
+       }, nil
+}
+
+func getSlackPullRequestApprovalPayload(p *api.PullRequestPayload, slack *SlackMeta, event models.HookEventType) (*SlackPayload, error) {
+       senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
+       titleLink := SlackLinkFormatter(fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index),
+               fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title))
+       var text, title, attachmentText string
+       switch p.Action {
+       case api.HookIssueSynchronized:
+               action, err := parseHookPullRequestEventType(event)
+               if err != nil {
+                       return nil, err
+               }
+
+               text = fmt.Sprintf("[%s] Pull request review %s : %s by %s", p.Repository.FullName, action, titleLink, senderLink)
+       }
+
+       return &SlackPayload{
+               Channel:  slack.Channel,
+               Text:     text,
+               Username: slack.Username,
+               IconURL:  slack.IconURL,
+               Attachments: []SlackAttachment{{
+                       Color: slack.Color,
+                       Title: title,
+                       Text:  attachmentText,
+               }},
+       }, nil
+}
+
+func getSlackRepositoryPayload(p *api.RepositoryPayload, slack *SlackMeta) (*SlackPayload, error) {
+       senderLink := SlackLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
+       var text, title, attachmentText string
+       switch p.Action {
+       case api.HookRepoCreated:
+               text = fmt.Sprintf("[%s] Repository created by %s", p.Repository.FullName, senderLink)
+               title = p.Repository.HTMLURL
+       case api.HookRepoDeleted:
+               text = fmt.Sprintf("[%s] Repository deleted by %s", p.Repository.FullName, senderLink)
+       }
+
+       return &SlackPayload{
+               Channel:  slack.Channel,
+               Text:     text,
+               Username: slack.Username,
+               IconURL:  slack.IconURL,
+               Attachments: []SlackAttachment{{
+                       Color: slack.Color,
+                       Title: title,
+                       Text:  attachmentText,
+               }},
+       }, nil
+}
+
+// GetSlackPayload converts a slack webhook into a SlackPayload
+func GetSlackPayload(p api.Payloader, event models.HookEventType, meta string) (*SlackPayload, error) {
+       s := new(SlackPayload)
+
+       slack := &SlackMeta{}
+       if err := json.Unmarshal([]byte(meta), &slack); err != nil {
+               return s, errors.New("GetSlackPayload meta json:" + err.Error())
+       }
+
+       switch event {
+       case models.HookEventCreate:
+               return getSlackCreatePayload(p.(*api.CreatePayload), slack)
+       case models.HookEventDelete:
+               return getSlackDeletePayload(p.(*api.DeletePayload), slack)
+       case models.HookEventFork:
+               return getSlackForkPayload(p.(*api.ForkPayload), slack)
+       case models.HookEventIssues:
+               return getSlackIssuesPayload(p.(*api.IssuePayload), slack)
+       case models.HookEventIssueComment:
+               return getSlackIssueCommentPayload(p.(*api.IssueCommentPayload), slack)
+       case models.HookEventPush:
+               return getSlackPushPayload(p.(*api.PushPayload), slack)
+       case models.HookEventPullRequest:
+               return getSlackPullRequestPayload(p.(*api.PullRequestPayload), slack)
+       case models.HookEventPullRequestRejected, models.HookEventPullRequestApproved, models.HookEventPullRequestComment:
+               return getSlackPullRequestApprovalPayload(p.(*api.PullRequestPayload), slack, event)
+       case models.HookEventRepository:
+               return getSlackRepositoryPayload(p.(*api.RepositoryPayload), slack)
+       case models.HookEventRelease:
+               return getSlackReleasePayload(p.(*api.ReleasePayload), slack)
+       }
+
+       return s, nil
+}
diff --git a/modules/webhook/telegram.go b/modules/webhook/telegram.go
new file mode 100644 (file)
index 0000000..4e67b22
--- /dev/null
@@ -0,0 +1,336 @@
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package webhook
+
+import (
+       "encoding/json"
+       "fmt"
+       "html"
+       "strings"
+
+       "code.gitea.io/gitea/models"
+       "code.gitea.io/gitea/modules/git"
+       "code.gitea.io/gitea/modules/log"
+       "code.gitea.io/gitea/modules/markup"
+       api "code.gitea.io/gitea/modules/structs"
+)
+
+type (
+       // TelegramPayload represents
+       TelegramPayload struct {
+               Message           string `json:"text"`
+               ParseMode         string `json:"parse_mode"`
+               DisableWebPreview bool   `json:"disable_web_page_preview"`
+       }
+
+       // TelegramMeta contains the telegram metadata
+       TelegramMeta struct {
+               BotToken string `json:"bot_token"`
+               ChatID   string `json:"chat_id"`
+       }
+)
+
+// GetTelegramHook returns telegram metadata
+func GetTelegramHook(w *models.Webhook) *TelegramMeta {
+       s := &TelegramMeta{}
+       if err := json.Unmarshal([]byte(w.Meta), s); err != nil {
+               log.Error("webhook.GetTelegramHook(%d): %v", w.ID, err)
+       }
+       return s
+}
+
+// SetSecret sets the telegram secret
+func (p *TelegramPayload) SetSecret(_ string) {}
+
+// JSONPayload Marshals the TelegramPayload to json
+func (p *TelegramPayload) JSONPayload() ([]byte, error) {
+       p.ParseMode = "HTML"
+       p.DisableWebPreview = true
+       p.Message = markup.Sanitize(p.Message)
+       data, err := json.MarshalIndent(p, "", "  ")
+       if err != nil {
+               return []byte{}, err
+       }
+       return data, nil
+}
+
+func getTelegramCreatePayload(p *api.CreatePayload) (*TelegramPayload, error) {
+       // created tag/branch
+       refName := git.RefEndName(p.Ref)
+       title := fmt.Sprintf(`[<a href="%s">%s</a>] %s <a href="%s">%s</a> created`, p.Repo.HTMLURL, p.Repo.FullName, p.RefType,
+               p.Repo.HTMLURL+"/src/"+refName, refName)
+
+       return &TelegramPayload{
+               Message: title,
+       }, nil
+}
+
+func getTelegramDeletePayload(p *api.DeletePayload) (*TelegramPayload, error) {
+       // created tag/branch
+       refName := git.RefEndName(p.Ref)
+       title := fmt.Sprintf(`[<a href="%s">%s</a>] %s <a href="%s">%s</a> deleted`, p.Repo.HTMLURL, p.Repo.FullName, p.RefType,
+               p.Repo.HTMLURL+"/src/"+refName, refName)
+
+       return &TelegramPayload{
+               Message: title,
+       }, nil
+}
+
+func getTelegramForkPayload(p *api.ForkPayload) (*TelegramPayload, error) {
+       title := fmt.Sprintf(`%s is forked to <a href="%s">%s</a>`, p.Forkee.FullName, p.Repo.HTMLURL, p.Repo.FullName)
+
+       return &TelegramPayload{
+               Message: title,
+       }, nil
+}
+
+func getTelegramPushPayload(p *api.PushPayload) (*TelegramPayload, error) {
+       var (
+               branchName = git.RefEndName(p.Ref)
+               commitDesc string
+       )
+
+       var titleLink string
+       if len(p.Commits) == 1 {
+               commitDesc = "1 new commit"
+               titleLink = p.Commits[0].URL
+       } else {
+               commitDesc = fmt.Sprintf("%d new commits", len(p.Commits))
+               titleLink = p.CompareURL
+       }
+       if titleLink == "" {
+               titleLink = p.Repo.HTMLURL + "/src/" + branchName
+       }
+       title := fmt.Sprintf(`[<a href="%s">%s</a>:<a href="%s">%s</a>] %s`, p.Repo.HTMLURL, p.Repo.FullName, titleLink, 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(`[<a href="%s">%s</a>] %s`, commit.URL, commit.ID[:7],
+                       strings.TrimRight(commit.Message, "\r\n")) + authorName
+               // add linebreak to each commit but the last
+               if i < len(p.Commits)-1 {
+                       text += "\n"
+               }
+       }
+
+       return &TelegramPayload{
+               Message: title + "\n" + text,
+       }, nil
+}
+
+func getTelegramIssuesPayload(p *api.IssuePayload) (*TelegramPayload, error) {
+       var text, title string
+       switch p.Action {
+       case api.HookIssueOpened:
+               title = fmt.Sprintf(`[<a href="%s">%s</a>] Issue opened: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
+                       p.Issue.URL, p.Index, p.Issue.Title)
+               text = p.Issue.Body
+       case api.HookIssueClosed:
+               title = fmt.Sprintf(`[<a href="%s">%s</a>] Issue closed: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
+                       p.Issue.URL, p.Index, p.Issue.Title)
+               text = p.Issue.Body
+       case api.HookIssueReOpened:
+               title = fmt.Sprintf(`[<a href="%s">%s</a>] Issue re-opened: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
+                       p.Issue.URL, p.Index, p.Issue.Title)
+               text = p.Issue.Body
+       case api.HookIssueEdited:
+               title = fmt.Sprintf(`[<a href="%s">%s</a>] Issue edited: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
+                       p.Issue.URL, p.Index, p.Issue.Title)
+               text = p.Issue.Body
+       case api.HookIssueAssigned:
+               title = fmt.Sprintf(`[<a href="%s">%s</a>] Issue assigned to %s: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
+                       p.Issue.Assignee.UserName, p.Issue.URL, p.Index, p.Issue.Title)
+               text = p.Issue.Body
+       case api.HookIssueUnassigned:
+               title = fmt.Sprintf(`[<a href="%s">%s</a>] Issue unassigned: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
+                       p.Issue.URL, p.Index, p.Issue.Title)
+               text = p.Issue.Body
+       case api.HookIssueLabelUpdated:
+               title = fmt.Sprintf(`[<a href="%s">%s</a>] Issue labels updated: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
+                       p.Issue.URL, p.Index, p.Issue.Title)
+               text = p.Issue.Body
+       case api.HookIssueLabelCleared:
+               title = fmt.Sprintf(`[<a href="%s">%s</a>] Issue labels cleared: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
+                       p.Issue.URL, p.Index, p.Issue.Title)
+               text = p.Issue.Body
+       case api.HookIssueSynchronized:
+               title = fmt.Sprintf(`[<a href="%s">%s</a>] Issue synchronized: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
+                       p.Issue.URL, p.Index, p.Issue.Title)
+               text = p.Issue.Body
+       case api.HookIssueMilestoned:
+               title = fmt.Sprintf(`[<a href="%s">%s</a>] Issue milestone: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
+                       p.Issue.URL, p.Index, p.Issue.Title)
+               text = p.Issue.Body
+       case api.HookIssueDemilestoned:
+               title = fmt.Sprintf(`[<a href="%s">%s</a>] Issue clear milestone: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
+                       p.Issue.URL, p.Index, p.Issue.Title)
+               text = p.Issue.Body
+       }
+
+       return &TelegramPayload{
+               Message: title + "\n\n" + text,
+       }, nil
+}
+
+func getTelegramIssueCommentPayload(p *api.IssueCommentPayload) (*TelegramPayload, error) {
+       url := fmt.Sprintf("%s/issues/%d#%s", p.Repository.HTMLURL, p.Issue.Index, models.CommentHashTag(p.Comment.ID))
+       title := fmt.Sprintf(`<a href="%s">#%d %s</a>`, url, p.Issue.Index, html.EscapeString(p.Issue.Title))
+       var text string
+       switch p.Action {
+       case api.HookIssueCommentCreated:
+               text = "New comment: " + title
+               text += p.Comment.Body
+       case api.HookIssueCommentEdited:
+               text = "Comment edited: " + title
+               text += p.Comment.Body
+       case api.HookIssueCommentDeleted:
+               text = "Comment deleted: " + title
+               text += p.Comment.Body
+       }
+
+       return &TelegramPayload{
+               Message: title + "\n" + text,
+       }, nil
+}
+
+func getTelegramPullRequestPayload(p *api.PullRequestPayload) (*TelegramPayload, error) {
+       var text, title string
+       switch p.Action {
+       case api.HookIssueOpened:
+               title = fmt.Sprintf(`[<a href="%s">%s</a>] Pull request opened: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
+                       p.PullRequest.HTMLURL, p.Index, p.PullRequest.Title)
+               text = p.PullRequest.Body
+       case api.HookIssueClosed:
+               if p.PullRequest.HasMerged {
+                       title = fmt.Sprintf(`[<a href="%s">%s</a>] Pull request merged: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
+                               p.PullRequest.HTMLURL, p.Index, p.PullRequest.Title)
+               } else {
+                       title = fmt.Sprintf(`[<a href="%s">%s</a>] Pull request closed: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
+                               p.PullRequest.HTMLURL, p.Index, p.PullRequest.Title)
+               }
+               text = p.PullRequest.Body
+       case api.HookIssueReOpened:
+               title = fmt.Sprintf(`[<a href="%s">%s</a>] Pull request re-opened: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
+                       p.PullRequest.HTMLURL, p.Index, p.PullRequest.Title)
+               text = p.PullRequest.Body
+       case api.HookIssueEdited:
+               title = fmt.Sprintf(`[<a href="%s">%s</a>] Pull request edited: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
+                       p.PullRequest.HTMLURL, p.Index, p.PullRequest.Title)
+               text = p.PullRequest.Body
+       case api.HookIssueAssigned:
+               list, err := models.MakeAssigneeList(&models.Issue{ID: p.PullRequest.ID})
+               if err != nil {
+                       return &TelegramPayload{}, err
+               }
+               title = fmt.Sprintf(`[<a href="%s">%s</a>] Pull request assigned to %s: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
+                       list, p.PullRequest.HTMLURL, p.Index, p.PullRequest.Title)
+               text = p.PullRequest.Body
+       case api.HookIssueUnassigned:
+               title = fmt.Sprintf(`[<a href="%s">%s</a>] Pull request unassigned: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
+                       p.PullRequest.HTMLURL, p.Index, p.PullRequest.Title)
+               text = p.PullRequest.Body
+       case api.HookIssueLabelUpdated:
+               title = fmt.Sprintf(`[<a href="%s">%s</a>] Pull request labels updated: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
+                       p.PullRequest.HTMLURL, p.Index, p.PullRequest.Title)
+               text = p.PullRequest.Body
+       case api.HookIssueLabelCleared:
+               title = fmt.Sprintf(`[<a href="%s">%s</a>] Pull request labels cleared: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
+                       p.PullRequest.HTMLURL, p.Index, p.PullRequest.Title)
+               text = p.PullRequest.Body
+       case api.HookIssueSynchronized:
+               title = fmt.Sprintf(`[<a href="%s">%s</a>] Pull request synchronized: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
+                       p.PullRequest.HTMLURL, p.Index, p.PullRequest.Title)
+               text = p.PullRequest.Body
+       case api.HookIssueMilestoned:
+               title = fmt.Sprintf(`[<a href="%s">%s</a>] Pull request milestone: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
+                       p.PullRequest.HTMLURL, p.Index, p.PullRequest.Title)
+               text = p.PullRequest.Body
+       case api.HookIssueDemilestoned:
+               title = fmt.Sprintf(`[<a href="%s">%s</a>] Pull request clear milestone: <a href="%s">#%d %s</a>`, p.Repository.HTMLURL, p.Repository.FullName,
+                       p.PullRequest.HTMLURL, p.Index, p.PullRequest.Title)
+               text = p.PullRequest.Body
+       }
+
+       return &TelegramPayload{
+               Message: title + "\n" + text,
+       }, nil
+}
+
+func getTelegramRepositoryPayload(p *api.RepositoryPayload) (*TelegramPayload, error) {
+       var title string
+       switch p.Action {
+       case api.HookRepoCreated:
+               title = fmt.Sprintf(`[<a href="%s">%s</a>] Repository created`, p.Repository.HTMLURL, p.Repository.FullName)
+               return &TelegramPayload{
+                       Message: title,
+               }, nil
+       case api.HookRepoDeleted:
+               title = fmt.Sprintf("[%s] Repository deleted", p.Repository.FullName)
+               return &TelegramPayload{
+                       Message: title,
+               }, nil
+       }
+       return nil, nil
+}
+
+func getTelegramReleasePayload(p *api.ReleasePayload) (*TelegramPayload, error) {
+       var title, url string
+       switch p.Action {
+       case api.HookReleasePublished:
+               title = fmt.Sprintf("[%s] Release created", p.Release.TagName)
+               url = p.Release.URL
+               return &TelegramPayload{
+                       Message: title + "\n" + url,
+               }, nil
+       case api.HookReleaseUpdated:
+               title = fmt.Sprintf("[%s] Release updated", p.Release.TagName)
+               url = p.Release.URL
+               return &TelegramPayload{
+                       Message: title + "\n" + url,
+               }, nil
+
+       case api.HookReleaseDeleted:
+               title = fmt.Sprintf("[%s] Release deleted", p.Release.TagName)
+               url = p.Release.URL
+               return &TelegramPayload{
+                       Message: title + "\n" + url,
+               }, nil
+       }
+
+       return nil, nil
+}
+
+// GetTelegramPayload converts a telegram webhook into a TelegramPayload
+func GetTelegramPayload(p api.Payloader, event models.HookEventType, meta string) (*TelegramPayload, error) {
+       s := new(TelegramPayload)
+
+       switch event {
+       case models.HookEventCreate:
+               return getTelegramCreatePayload(p.(*api.CreatePayload))
+       case models.HookEventDelete:
+               return getTelegramDeletePayload(p.(*api.DeletePayload))
+       case models.HookEventFork:
+               return getTelegramForkPayload(p.(*api.ForkPayload))
+       case models.HookEventIssues:
+               return getTelegramIssuesPayload(p.(*api.IssuePayload))
+       case models.HookEventIssueComment:
+               return getTelegramIssueCommentPayload(p.(*api.IssueCommentPayload))
+       case models.HookEventPush:
+               return getTelegramPushPayload(p.(*api.PushPayload))
+       case models.HookEventPullRequest:
+               return getTelegramPullRequestPayload(p.(*api.PullRequestPayload))
+       case models.HookEventRepository:
+               return getTelegramRepositoryPayload(p.(*api.RepositoryPayload))
+       case models.HookEventRelease:
+               return getTelegramReleasePayload(p.(*api.ReleasePayload))
+       }
+
+       return s, nil
+}
index 623a475df9b715698751c210725f39df1539c596..410e47461fe68305e0f7699bd0f06b6b149f22ef 100644 (file)
@@ -90,27 +90,27 @@ func prepareWebhook(w *models.Webhook, repo *models.Repository, event models.Hoo
        // Use separate objects so modifications won't be made on payload on non-Gogs/Gitea type hooks.
        switch w.HookTaskType {
        case models.SLACK:
-               payloader, err = models.GetSlackPayload(p, event, w.Meta)
+               payloader, err = GetSlackPayload(p, event, w.Meta)
                if err != nil {
                        return fmt.Errorf("GetSlackPayload: %v", err)
                }
        case models.DISCORD:
-               payloader, err = models.GetDiscordPayload(p, event, w.Meta)
+               payloader, err = GetDiscordPayload(p, event, w.Meta)
                if err != nil {
                        return fmt.Errorf("GetDiscordPayload: %v", err)
                }
        case models.DINGTALK:
-               payloader, err = models.GetDingtalkPayload(p, event, w.Meta)
+               payloader, err = GetDingtalkPayload(p, event, w.Meta)
                if err != nil {
                        return fmt.Errorf("GetDingtalkPayload: %v", err)
                }
        case models.TELEGRAM:
-               payloader, err = models.GetTelegramPayload(p, event, w.Meta)
+               payloader, err = GetTelegramPayload(p, event, w.Meta)
                if err != nil {
                        return fmt.Errorf("GetTelegramPayload: %v", err)
                }
        case models.MSTEAMS:
-               payloader, err = models.GetMSTeamsPayload(p, event, w.Meta)
+               payloader, err = GetMSTeamsPayload(p, event, w.Meta)
                if err != nil {
                        return fmt.Errorf("GetMSTeamsPayload: %v", err)
                }
index c944bc477d3bd61f7ed06b60cadc5ec9bc6bd14c..e88e67e9bfefe6e72e4d251a79abba816c1b4055 100644 (file)
@@ -12,6 +12,18 @@ import (
        "github.com/stretchr/testify/assert"
 )
 
+func TestWebhook_GetSlackHook(t *testing.T) {
+       w := &models.Webhook{
+               Meta: `{"channel": "foo", "username": "username", "color": "blue"}`,
+       }
+       slackHook := GetSlackHook(w)
+       assert.Equal(t, *slackHook, SlackMeta{
+               Channel:  "foo",
+               Username: "username",
+               Color:    "blue",
+       })
+}
+
 func TestPrepareWebhooks(t *testing.T) {
        assert.NoError(t, models.PrepareTestDatabase())
 
index 07456f8dd687a59124dfc542c2a4fac2fdc85e55..6da53d62750c056fe14b2a98708f0f512d88dd3d 100644 (file)
@@ -15,6 +15,7 @@ import (
        "code.gitea.io/gitea/modules/structs"
        api "code.gitea.io/gitea/modules/structs"
        "code.gitea.io/gitea/modules/util"
+       "code.gitea.io/gitea/modules/webhook"
 
        "github.com/unknwon/com"
 )
@@ -166,7 +167,7 @@ func ToHook(repoLink string, w *models.Webhook) *api.Hook {
                "content_type": w.ContentType.Name(),
        }
        if w.HookTaskType == models.SLACK {
-               s := w.GetSlackHook()
+               s := webhook.GetSlackHook(w)
                config["channel"] = s.Channel
                config["username"] = s.Username
                config["icon_url"] = s.IconURL
index 7903d58334ac1f1196f642104f931b6753c64cc0..6f72e99b714a6b1cfabdc84f5323a2584fa89a51 100644 (file)
@@ -12,6 +12,7 @@ import (
        "code.gitea.io/gitea/models"
        "code.gitea.io/gitea/modules/context"
        api "code.gitea.io/gitea/modules/structs"
+       "code.gitea.io/gitea/modules/webhook"
        "code.gitea.io/gitea/routers/api/v1/convert"
        "code.gitea.io/gitea/routers/utils"
 
@@ -129,7 +130,7 @@ func addHook(ctx *context.APIContext, form *api.CreateHookOption, orgID, repoID
                        return nil, false
                }
 
-               meta, err := json.Marshal(&models.SlackMeta{
+               meta, err := json.Marshal(&webhook.SlackMeta{
                        Channel:  strings.TrimSpace(channel),
                        Username: form.Config["username"],
                        IconURL:  form.Config["icon_url"],
@@ -203,7 +204,7 @@ func editHook(ctx *context.APIContext, form *api.EditHookOption, w *models.Webho
 
                if w.HookTaskType == models.SLACK {
                        if channel, ok := form.Config["channel"]; ok {
-                               meta, err := json.Marshal(&models.SlackMeta{
+                               meta, err := json.Marshal(&webhook.SlackMeta{
                                        Channel:  channel,
                                        Username: form.Config["username"],
                                        IconURL:  form.Config["icon_url"],
index a6bd3af264a3a63c23d53de03a9630b97de055ee..9ae15882c14884cb773deb5354700aca015c9f65 100644 (file)
@@ -268,7 +268,7 @@ func DiscordHooksNewPost(ctx *context.Context, form auth.NewDiscordHookForm) {
                return
        }
 
-       meta, err := json.Marshal(&models.DiscordMeta{
+       meta, err := json.Marshal(&webhook.DiscordMeta{
                Username: form.Username,
                IconURL:  form.IconURL,
        })
@@ -357,7 +357,7 @@ func TelegramHooksNewPost(ctx *context.Context, form auth.NewTelegramHookForm) {
                return
        }
 
-       meta, err := json.Marshal(&models.TelegramMeta{
+       meta, err := json.Marshal(&webhook.TelegramMeta{
                BotToken: form.BotToken,
                ChatID:   form.ChatID,
        })
@@ -452,7 +452,7 @@ func SlackHooksNewPost(ctx *context.Context, form auth.NewSlackHookForm) {
                return
        }
 
-       meta, err := json.Marshal(&models.SlackMeta{
+       meta, err := json.Marshal(&webhook.SlackMeta{
                Channel:  strings.TrimSpace(form.Channel),
                Username: form.Username,
                IconURL:  form.IconURL,
@@ -515,11 +515,11 @@ func checkWebhook(ctx *context.Context) (*orgRepoCtx, *models.Webhook) {
        ctx.Data["HookType"] = w.HookTaskType.Name()
        switch w.HookTaskType {
        case models.SLACK:
-               ctx.Data["SlackHook"] = w.GetSlackHook()
+               ctx.Data["SlackHook"] = webhook.GetSlackHook(w)
        case models.DISCORD:
-               ctx.Data["DiscordHook"] = w.GetDiscordHook()
+               ctx.Data["DiscordHook"] = webhook.GetDiscordHook(w)
        case models.TELEGRAM:
-               ctx.Data["TelegramHook"] = w.GetTelegramHook()
+               ctx.Data["TelegramHook"] = webhook.GetTelegramHook(w)
        }
 
        ctx.Data["History"], err = w.History(1)
@@ -646,7 +646,7 @@ func SlackHooksEditPost(ctx *context.Context, form auth.NewSlackHookForm) {
                return
        }
 
-       meta, err := json.Marshal(&models.SlackMeta{
+       meta, err := json.Marshal(&webhook.SlackMeta{
                Channel:  strings.TrimSpace(form.Channel),
                Username: form.Username,
                IconURL:  form.IconURL,
@@ -690,7 +690,7 @@ func DiscordHooksEditPost(ctx *context.Context, form auth.NewDiscordHookForm) {
                return
        }
 
-       meta, err := json.Marshal(&models.DiscordMeta{
+       meta, err := json.Marshal(&webhook.DiscordMeta{
                Username: form.Username,
                IconURL:  form.IconURL,
        })
@@ -763,7 +763,7 @@ func TelegramHooksEditPost(ctx *context.Context, form auth.NewTelegramHookForm)
                ctx.HTML(200, orCtx.NewTemplate)
                return
        }
-       meta, err := json.Marshal(&models.TelegramMeta{
+       meta, err := json.Marshal(&webhook.TelegramMeta{
                BotToken: form.BotToken,
                ChatID:   form.ChatID,
        })