mirror of
https://github.com/go-gitea/gitea.git
synced 2024-09-06 23:44:20 -04:00
Implementation of discord webhook (#2402)
* implementation of discord webhook * fix webhooks * fix typo and unnecessary color values * fix typo * fix imports and revert changes to webhook_slack.go
This commit is contained in:
parent
e41da3845d
commit
ced50e0ec1
@ -13,15 +13,14 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-xorm/xorm"
|
|
||||||
gouuid "github.com/satori/go.uuid"
|
|
||||||
|
|
||||||
api "code.gitea.io/sdk/gitea"
|
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/httplib"
|
"code.gitea.io/gitea/modules/httplib"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/sync"
|
"code.gitea.io/gitea/modules/sync"
|
||||||
|
api "code.gitea.io/sdk/gitea"
|
||||||
|
|
||||||
|
"github.com/go-xorm/xorm"
|
||||||
|
gouuid "github.com/satori/go.uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
// HookQueue is a global queue of web hooks
|
// HookQueue is a global queue of web hooks
|
||||||
@ -150,6 +149,15 @@ func (w *Webhook) GetSlackHook() *SlackMeta {
|
|||||||
return s
|
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(4, "webhook.GetDiscordHook(%d): %v", w.ID, err)
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
// History returns history of webhook by given conditions.
|
// History returns history of webhook by given conditions.
|
||||||
func (w *Webhook) History(page int) ([]*HookTask, error) {
|
func (w *Webhook) History(page int) ([]*HookTask, error) {
|
||||||
return HookTasks(w.ID, page)
|
return HookTasks(w.ID, page)
|
||||||
@ -314,12 +322,14 @@ const (
|
|||||||
GOGS HookTaskType = iota + 1
|
GOGS HookTaskType = iota + 1
|
||||||
SLACK
|
SLACK
|
||||||
GITEA
|
GITEA
|
||||||
|
DISCORD
|
||||||
)
|
)
|
||||||
|
|
||||||
var hookTaskTypes = map[string]HookTaskType{
|
var hookTaskTypes = map[string]HookTaskType{
|
||||||
"gitea": GITEA,
|
"gitea": GITEA,
|
||||||
"gogs": GOGS,
|
"gogs": GOGS,
|
||||||
"slack": SLACK,
|
"slack": SLACK,
|
||||||
|
"discord": DISCORD,
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToHookTaskType returns HookTaskType by given name.
|
// ToHookTaskType returns HookTaskType by given name.
|
||||||
@ -336,6 +346,8 @@ func (t HookTaskType) Name() string {
|
|||||||
return "gogs"
|
return "gogs"
|
||||||
case SLACK:
|
case SLACK:
|
||||||
return "slack"
|
return "slack"
|
||||||
|
case DISCORD:
|
||||||
|
return "discord"
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
@ -515,6 +527,11 @@ func PrepareWebhooks(repo *Repository, event HookEventType, p api.Payloader) err
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("GetSlackPayload: %v", err)
|
return fmt.Errorf("GetSlackPayload: %v", err)
|
||||||
}
|
}
|
||||||
|
case DISCORD:
|
||||||
|
payloader, err = GetDiscordPayload(p, event, w.Meta)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("GetDiscordPayload: %v", err)
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
p.SetSecret(w.Secret)
|
p.SetSecret(w.Secret)
|
||||||
payloader = p
|
payloader = p
|
||||||
|
252
models/webhook_discord.go
Normal file
252
models/webhook_discord.go
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"code.gitea.io/git"
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
api "code.gitea.io/sdk/gitea"
|
||||||
|
)
|
||||||
|
|
||||||
|
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 (
|
||||||
|
successColor = color("1ac600")
|
||||||
|
warnColor = color("ffd930")
|
||||||
|
failedColor = 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: successColor,
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(text)
|
||||||
|
|
||||||
|
return &DiscordPayload{
|
||||||
|
Username: meta.Username,
|
||||||
|
AvatarURL: meta.IconURL,
|
||||||
|
Embeds: []DiscordEmbed{
|
||||||
|
{
|
||||||
|
Title: title,
|
||||||
|
Description: text,
|
||||||
|
URL: titleLink,
|
||||||
|
Color: successColor,
|
||||||
|
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 = warnColor
|
||||||
|
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 = successColor
|
||||||
|
} else {
|
||||||
|
title = fmt.Sprintf("[%s] Pull request closed: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
|
||||||
|
color = failedColor
|
||||||
|
}
|
||||||
|
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 = warnColor
|
||||||
|
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 = warnColor
|
||||||
|
case api.HookIssueAssigned:
|
||||||
|
title = fmt.Sprintf("[%s] Pull request assigned to %s: #%d %s", p.Repository.FullName,
|
||||||
|
p.PullRequest.Assignee.UserName, p.Index, p.PullRequest.Title)
|
||||||
|
text = p.PullRequest.Body
|
||||||
|
color = successColor
|
||||||
|
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 = warnColor
|
||||||
|
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 = warnColor
|
||||||
|
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 = warnColor
|
||||||
|
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 = warnColor
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 HookEventPush:
|
||||||
|
return getDiscordPushPayload(p.(*api.PushPayload), discord)
|
||||||
|
case HookEventPullRequest:
|
||||||
|
return getDiscordPullRequestPayload(p.(*api.PullRequestPayload), discord)
|
||||||
|
}
|
||||||
|
|
||||||
|
return s, nil
|
||||||
|
}
|
@ -183,6 +183,19 @@ func (f *NewSlackHookForm) Validate(ctx *macaron.Context, errs binding.Errors) b
|
|||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewDiscordHookForm form for creating discord hook
|
||||||
|
type NewDiscordHookForm struct {
|
||||||
|
PayloadURL string `binding:"Required;ValidUrl"`
|
||||||
|
Username string
|
||||||
|
IconURL string
|
||||||
|
WebhookForm
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates the fields
|
||||||
|
func (f *NewDiscordHookForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
||||||
|
return validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
|
}
|
||||||
|
|
||||||
// .___
|
// .___
|
||||||
// | | ______ ________ __ ____
|
// | | ______ ________ __ ____
|
||||||
// | |/ ___// ___/ | \_/ __ \
|
// | |/ ___// ___/ | \_/ __ \
|
||||||
|
@ -1367,7 +1367,7 @@ func newWebhookService() {
|
|||||||
Webhook.QueueLength = sec.Key("QUEUE_LENGTH").MustInt(1000)
|
Webhook.QueueLength = sec.Key("QUEUE_LENGTH").MustInt(1000)
|
||||||
Webhook.DeliverTimeout = sec.Key("DELIVER_TIMEOUT").MustInt(5)
|
Webhook.DeliverTimeout = sec.Key("DELIVER_TIMEOUT").MustInt(5)
|
||||||
Webhook.SkipTLSVerify = sec.Key("SKIP_TLS_VERIFY").MustBool()
|
Webhook.SkipTLSVerify = sec.Key("SKIP_TLS_VERIFY").MustBool()
|
||||||
Webhook.Types = []string{"gitea", "gogs", "slack"}
|
Webhook.Types = []string{"gitea", "gogs", "slack", "discord"}
|
||||||
Webhook.PagingNum = sec.Key("PAGING_NUM").MustInt(10)
|
Webhook.PagingNum = sec.Key("PAGING_NUM").MustInt(10)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -879,6 +879,8 @@ settings.content_type = Content Type
|
|||||||
settings.secret = Secret
|
settings.secret = Secret
|
||||||
settings.slack_username = Username
|
settings.slack_username = Username
|
||||||
settings.slack_icon_url = Icon URL
|
settings.slack_icon_url = Icon URL
|
||||||
|
settings.discord_username = Username
|
||||||
|
settings.discord_icon_url = Icon URL
|
||||||
settings.slack_color = Color
|
settings.slack_color = Color
|
||||||
settings.event_desc = When should this webhook be triggered?
|
settings.event_desc = When should this webhook be triggered?
|
||||||
settings.event_push_only = Just the <code>push</code> event.
|
settings.event_push_only = Just the <code>push</code> event.
|
||||||
@ -902,6 +904,7 @@ settings.add_slack_hook_desc = Add <a href="%s">Slack</a> integration to your re
|
|||||||
settings.slack_token = Token
|
settings.slack_token = Token
|
||||||
settings.slack_domain = Domain
|
settings.slack_domain = Domain
|
||||||
settings.slack_channel = Channel
|
settings.slack_channel = Channel
|
||||||
|
settings.add_discord_hook_desc = Add <a href="%s">Discord</a> integration to your repository.
|
||||||
settings.deploy_keys = Deploy Keys
|
settings.deploy_keys = Deploy Keys
|
||||||
settings.add_deploy_key = Add Deploy Key
|
settings.add_deploy_key = Add Deploy Key
|
||||||
settings.deploy_key_desc = Deploy keys have read-only access. They are not the same as personal account SSH keys.
|
settings.deploy_key_desc = Deploy keys have read-only access. They are not the same as personal account SSH keys.
|
||||||
|
BIN
public/img/discord.png
Normal file
BIN
public/img/discord.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
@ -11,16 +11,15 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/Unknwon/com"
|
|
||||||
|
|
||||||
"code.gitea.io/git"
|
"code.gitea.io/git"
|
||||||
api "code.gitea.io/sdk/gitea"
|
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/modules/auth"
|
"code.gitea.io/gitea/modules/auth"
|
||||||
"code.gitea.io/gitea/modules/base"
|
"code.gitea.io/gitea/modules/base"
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
api "code.gitea.io/sdk/gitea"
|
||||||
|
|
||||||
|
"github.com/Unknwon/com"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -96,10 +95,18 @@ func WebhooksNew(ctx *context.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Data["HookType"] = checkHookType(ctx)
|
hookType := checkHookType(ctx)
|
||||||
|
ctx.Data["HookType"] = hookType
|
||||||
if ctx.Written() {
|
if ctx.Written() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if hookType == "discord" {
|
||||||
|
ctx.Data["DiscordHook"] = map[string]interface{}{
|
||||||
|
"Username": "Gitea",
|
||||||
|
"IconURL": setting.AppURL + "img/favicon.png",
|
||||||
|
"Color": 16724530,
|
||||||
|
}
|
||||||
|
}
|
||||||
ctx.Data["BaseLink"] = orCtx.Link
|
ctx.Data["BaseLink"] = orCtx.Link
|
||||||
|
|
||||||
ctx.HTML(200, orCtx.NewTemplate)
|
ctx.HTML(200, orCtx.NewTemplate)
|
||||||
@ -213,6 +220,55 @@ func GogsHooksNewPost(ctx *context.Context, form auth.NewWebhookForm) {
|
|||||||
ctx.Redirect(orCtx.Link + "/settings/hooks")
|
ctx.Redirect(orCtx.Link + "/settings/hooks")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DiscordHooksNewPost response for creating discord hook
|
||||||
|
func DiscordHooksNewPost(ctx *context.Context, form auth.NewDiscordHookForm) {
|
||||||
|
ctx.Data["Title"] = ctx.Tr("repo.settings")
|
||||||
|
ctx.Data["PageIsSettingsHooks"] = true
|
||||||
|
ctx.Data["PageIsSettingsHooksNew"] = true
|
||||||
|
ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}
|
||||||
|
|
||||||
|
orCtx, err := getOrgRepoCtx(ctx)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Handle(500, "getOrgRepoCtx", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx.HasError() {
|
||||||
|
ctx.HTML(200, orCtx.NewTemplate)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
meta, err := json.Marshal(&models.DiscordMeta{
|
||||||
|
Username: form.Username,
|
||||||
|
IconURL: form.IconURL,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
ctx.Handle(500, "Marshal", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w := &models.Webhook{
|
||||||
|
RepoID: orCtx.RepoID,
|
||||||
|
URL: form.PayloadURL,
|
||||||
|
ContentType: models.ContentTypeJSON,
|
||||||
|
HookEvent: ParseHookEvent(form.WebhookForm),
|
||||||
|
IsActive: form.Active,
|
||||||
|
HookTaskType: models.DISCORD,
|
||||||
|
Meta: string(meta),
|
||||||
|
OrgID: orCtx.OrgID,
|
||||||
|
}
|
||||||
|
if err := w.UpdateEvent(); err != nil {
|
||||||
|
ctx.Handle(500, "UpdateEvent", err)
|
||||||
|
return
|
||||||
|
} else if err := models.CreateWebhook(w); err != nil {
|
||||||
|
ctx.Handle(500, "CreateWebhook", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
|
||||||
|
ctx.Redirect(orCtx.Link + "/settings/hooks")
|
||||||
|
}
|
||||||
|
|
||||||
// SlackHooksNewPost response for creating slack hook
|
// SlackHooksNewPost response for creating slack hook
|
||||||
func SlackHooksNewPost(ctx *context.Context, form auth.NewSlackHookForm) {
|
func SlackHooksNewPost(ctx *context.Context, form auth.NewSlackHookForm) {
|
||||||
ctx.Data["Title"] = ctx.Tr("repo.settings")
|
ctx.Data["Title"] = ctx.Tr("repo.settings")
|
||||||
@ -295,6 +351,9 @@ func checkWebhook(ctx *context.Context) (*orgRepoCtx, *models.Webhook) {
|
|||||||
ctx.Data["HookType"] = "slack"
|
ctx.Data["HookType"] = "slack"
|
||||||
case models.GOGS:
|
case models.GOGS:
|
||||||
ctx.Data["HookType"] = "gogs"
|
ctx.Data["HookType"] = "gogs"
|
||||||
|
case models.DISCORD:
|
||||||
|
ctx.Data["DiscordHook"] = w.GetDiscordHook()
|
||||||
|
ctx.Data["HookType"] = "discord"
|
||||||
default:
|
default:
|
||||||
ctx.Data["HookType"] = "gitea"
|
ctx.Data["HookType"] = "gitea"
|
||||||
}
|
}
|
||||||
@ -443,6 +502,48 @@ func SlackHooksEditPost(ctx *context.Context, form auth.NewSlackHookForm) {
|
|||||||
ctx.Redirect(fmt.Sprintf("%s/settings/hooks/%d", orCtx.Link, w.ID))
|
ctx.Redirect(fmt.Sprintf("%s/settings/hooks/%d", orCtx.Link, w.ID))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DiscordHooksEditPost response for editing discord hook
|
||||||
|
func DiscordHooksEditPost(ctx *context.Context, form auth.NewDiscordHookForm) {
|
||||||
|
ctx.Data["Title"] = ctx.Tr("repo.settings")
|
||||||
|
ctx.Data["PageIsSettingsHooks"] = true
|
||||||
|
ctx.Data["PageIsSettingsHooksEdit"] = true
|
||||||
|
|
||||||
|
orCtx, w := checkWebhook(ctx)
|
||||||
|
if ctx.Written() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.Data["Webhook"] = w
|
||||||
|
|
||||||
|
if ctx.HasError() {
|
||||||
|
ctx.HTML(200, orCtx.NewTemplate)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
meta, err := json.Marshal(&models.DiscordMeta{
|
||||||
|
Username: form.Username,
|
||||||
|
IconURL: form.IconURL,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
ctx.Handle(500, "Marshal", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.URL = form.PayloadURL
|
||||||
|
w.Meta = string(meta)
|
||||||
|
w.HookEvent = ParseHookEvent(form.WebhookForm)
|
||||||
|
w.IsActive = form.Active
|
||||||
|
if err := w.UpdateEvent(); err != nil {
|
||||||
|
ctx.Handle(500, "UpdateEvent", err)
|
||||||
|
return
|
||||||
|
} else if err := models.UpdateWebhook(w); err != nil {
|
||||||
|
ctx.Handle(500, "UpdateWebhook", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
|
||||||
|
ctx.Redirect(fmt.Sprintf("%s/settings/hooks/%d", orCtx.Link, w.ID))
|
||||||
|
}
|
||||||
|
|
||||||
// TestWebhook test if web hook is work fine
|
// TestWebhook test if web hook is work fine
|
||||||
func TestWebhook(ctx *context.Context) {
|
func TestWebhook(ctx *context.Context) {
|
||||||
// Grab latest commit or fake one if it's empty repository.
|
// Grab latest commit or fake one if it's empty repository.
|
||||||
|
@ -442,11 +442,13 @@ func RegisterRoutes(m *macaron.Macaron) {
|
|||||||
m.Post("/gitea/new", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksNewPost)
|
m.Post("/gitea/new", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksNewPost)
|
||||||
m.Post("/gogs/new", bindIgnErr(auth.NewGogshookForm{}), repo.GogsHooksNewPost)
|
m.Post("/gogs/new", bindIgnErr(auth.NewGogshookForm{}), repo.GogsHooksNewPost)
|
||||||
m.Post("/slack/new", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksNewPost)
|
m.Post("/slack/new", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksNewPost)
|
||||||
|
m.Post("/discord/new", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksNewPost)
|
||||||
m.Get("/:id", repo.WebHooksEdit)
|
m.Get("/:id", repo.WebHooksEdit)
|
||||||
m.Post("/:id/test", repo.TestWebhook)
|
m.Post("/:id/test", repo.TestWebhook)
|
||||||
m.Post("/gitea/:id", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksEditPost)
|
m.Post("/gitea/:id", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksEditPost)
|
||||||
m.Post("/gogs/:id", bindIgnErr(auth.NewGogshookForm{}), repo.GogsHooksNewPost)
|
m.Post("/gogs/:id", bindIgnErr(auth.NewGogshookForm{}), repo.GogsHooksNewPost)
|
||||||
m.Post("/slack/:id", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksEditPost)
|
m.Post("/slack/:id", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksEditPost)
|
||||||
|
m.Post("/discord/:id", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksEditPost)
|
||||||
|
|
||||||
m.Group("/git", func() {
|
m.Group("/git", func() {
|
||||||
m.Get("", repo.GitHooks)
|
m.Get("", repo.GitHooks)
|
||||||
|
19
templates/repo/settings/hook_discord.tmpl
Normal file
19
templates/repo/settings/hook_discord.tmpl
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{{if eq .HookType "discord"}}
|
||||||
|
<p>{{.i18n.Tr "repo.settings.add_discord_hook_desc" "https://discordapp.com" | Str2html}}</p>
|
||||||
|
<form class="ui form" action="{{.BaseLink}}/settings/hooks/discord/{{if .PageIsSettingsHooksNew}}new{{else}}{{.Webhook.ID}}{{end}}" method="post">
|
||||||
|
{{.CsrfTokenHtml}}
|
||||||
|
<div class="required field {{if .Err_PayloadURL}}error{{end}}">
|
||||||
|
<label for="payload_url">{{.i18n.Tr "repo.settings.payload_url"}}</label>
|
||||||
|
<input id="payload_url" name="payload_url" type="url" value="{{.Webhook.URL}}" autofocus required>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label for="username">{{.i18n.Tr "repo.settings.discord_username"}}</label>
|
||||||
|
<input id="username" name="username" value="{{.DiscordHook.Username}}" placeholder="e.g. Gitea">
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label for="icon_url">{{.i18n.Tr "repo.settings.discord_icon_url"}}</label>
|
||||||
|
<input id="icon_url" name="icon_url" value="{{.DiscordHook.IconURL}}" placeholder="e.g. https://example.com/img/favicon.png">
|
||||||
|
</div>
|
||||||
|
{{template "repo/settings/hook_settings" .}}
|
||||||
|
</form>
|
||||||
|
{{end}}
|
@ -14,6 +14,9 @@
|
|||||||
<a class="item" href="{{.BaseLink}}/settings/hooks/slack/new">
|
<a class="item" href="{{.BaseLink}}/settings/hooks/slack/new">
|
||||||
<img class="img-10" src="{{AppSubUrl}}/img/slack.png">Slack
|
<img class="img-10" src="{{AppSubUrl}}/img/slack.png">Slack
|
||||||
</a>
|
</a>
|
||||||
|
<a class="item" href="{{.BaseLink}}/settings/hooks/discord/new">
|
||||||
|
<img class="img-10" src="{{AppSubUrl}}/img/discord.png">Discord
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -13,6 +13,8 @@
|
|||||||
<img class="img-13" src="{{AppSubUrl}}/img/gogs.ico">
|
<img class="img-13" src="{{AppSubUrl}}/img/gogs.ico">
|
||||||
{{else if eq .HookType "slack"}}
|
{{else if eq .HookType "slack"}}
|
||||||
<img class="img-13" src="{{AppSubUrl}}/img/slack.png">
|
<img class="img-13" src="{{AppSubUrl}}/img/slack.png">
|
||||||
|
{{else if eq .HookType "discord"}}
|
||||||
|
<img class="img-13" src="{{AppSubUrl}}/img/discord.png">
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
</h4>
|
</h4>
|
||||||
@ -20,6 +22,7 @@
|
|||||||
{{template "repo/settings/hook_gitea" .}}
|
{{template "repo/settings/hook_gitea" .}}
|
||||||
{{template "repo/settings/hook_gogs" .}}
|
{{template "repo/settings/hook_gogs" .}}
|
||||||
{{template "repo/settings/hook_slack" .}}
|
{{template "repo/settings/hook_slack" .}}
|
||||||
|
{{template "repo/settings/hook_discord" .}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{template "repo/settings/hook_history" .}}
|
{{template "repo/settings/hook_history" .}}
|
||||||
|
Loading…
Reference in New Issue
Block a user