mirror of
https://codeberg.org/davrot/forgejo.git
synced 2025-05-20 14:00:04 +02:00
add Slack API webhook support
This commit is contained in:
parent
5e6091a30a
commit
2bce24068d
15 changed files with 485 additions and 79 deletions
|
@ -7,6 +7,7 @@ package models
|
|||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"time"
|
||||
|
||||
"github.com/gogits/gogs/modules/httplib"
|
||||
|
@ -33,15 +34,17 @@ type HookEvent struct {
|
|||
|
||||
// Webhook represents a web hook object.
|
||||
type Webhook struct {
|
||||
Id int64
|
||||
RepoId int64
|
||||
Url string `xorm:"TEXT"`
|
||||
ContentType HookContentType
|
||||
Secret string `xorm:"TEXT"`
|
||||
Events string `xorm:"TEXT"`
|
||||
*HookEvent `xorm:"-"`
|
||||
IsSsl bool
|
||||
IsActive bool
|
||||
Id int64
|
||||
RepoId int64
|
||||
Url string `xorm:"TEXT"`
|
||||
ContentType HookContentType
|
||||
Secret string `xorm:"TEXT"`
|
||||
Events string `xorm:"TEXT"`
|
||||
*HookEvent `xorm:"-"`
|
||||
IsSsl bool
|
||||
IsActive bool
|
||||
HookTaskType HookTaskType
|
||||
Meta string `xorm:"TEXT"` // store hook-specific attributes
|
||||
}
|
||||
|
||||
// GetEvent handles conversion from Events to HookEvent.
|
||||
|
@ -52,6 +55,14 @@ func (w *Webhook) GetEvent() {
|
|||
}
|
||||
}
|
||||
|
||||
func (w *Webhook) GetSlackHook() *Slack {
|
||||
s := &Slack{}
|
||||
if err := json.Unmarshal([]byte(w.Meta), s); err != nil {
|
||||
log.Error(4, "webhook.GetSlackHook(%d): %v", w.Id, err)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// UpdateEvent handles conversion from HookEvent to Events.
|
||||
func (w *Webhook) UpdateEvent() error {
|
||||
data, err := json.Marshal(w.HookEvent)
|
||||
|
@ -119,8 +130,8 @@ func DeleteWebhook(hookId int64) error {
|
|||
type HookTaskType int
|
||||
|
||||
const (
|
||||
WEBHOOK HookTaskType = iota + 1
|
||||
SERVICE
|
||||
GOGS HookTaskType = iota + 1
|
||||
SLACK
|
||||
)
|
||||
|
||||
type HookEventType string
|
||||
|
@ -152,6 +163,10 @@ type PayloadRepo struct {
|
|||
Private bool `json:"private"`
|
||||
}
|
||||
|
||||
type BasePayload interface {
|
||||
GetJSONPayload() ([]byte, error)
|
||||
}
|
||||
|
||||
// Payload represents a payload information of hook.
|
||||
type Payload struct {
|
||||
Secret string `json:"secret"`
|
||||
|
@ -161,25 +176,33 @@ type Payload struct {
|
|||
Pusher *PayloadAuthor `json:"pusher"`
|
||||
}
|
||||
|
||||
func (p Payload) GetJSONPayload() ([]byte, error) {
|
||||
data, err := json.Marshal(p)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// HookTask represents a hook task.
|
||||
type HookTask struct {
|
||||
Id int64
|
||||
Uuid string
|
||||
Type HookTaskType
|
||||
Url string
|
||||
*Payload `xorm:"-"`
|
||||
BasePayload `xorm:"-"`
|
||||
PayloadContent string `xorm:"TEXT"`
|
||||
ContentType HookContentType
|
||||
EventType HookEventType
|
||||
IsSsl bool
|
||||
IsDeliveried bool
|
||||
IsDelivered bool
|
||||
IsSucceed bool
|
||||
}
|
||||
|
||||
// CreateHookTask creates a new hook task,
|
||||
// it handles conversion from Payload to PayloadContent.
|
||||
func CreateHookTask(t *HookTask) error {
|
||||
data, err := json.Marshal(t.Payload)
|
||||
data, err := t.BasePayload.GetJSONPayload()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -198,7 +221,7 @@ func UpdateHookTask(t *HookTask) error {
|
|||
// DeliverHooks checks and delivers undelivered hooks.
|
||||
func DeliverHooks() {
|
||||
timeout := time.Duration(setting.WebhookDeliverTimeout) * time.Second
|
||||
x.Where("is_deliveried=?", false).Iterate(new(HookTask),
|
||||
x.Where("is_delivered=?", false).Iterate(new(HookTask),
|
||||
func(idx int, bean interface{}) error {
|
||||
t := bean.(*HookTask)
|
||||
req := httplib.Post(t.Url).SetTimeout(timeout, timeout).
|
||||
|
@ -212,13 +235,36 @@ func DeliverHooks() {
|
|||
req.Param("payload", t.PayloadContent)
|
||||
}
|
||||
|
||||
t.IsDeliveried = true
|
||||
t.IsDelivered = true
|
||||
|
||||
// TODO: record response.
|
||||
if _, err := req.Response(); err != nil {
|
||||
log.Error(4, "Delivery: %v", err)
|
||||
} else {
|
||||
t.IsSucceed = true
|
||||
switch t.Type {
|
||||
case GOGS:
|
||||
{
|
||||
if _, err := req.Response(); err != nil {
|
||||
log.Error(4, "Delivery: %v", err)
|
||||
} else {
|
||||
t.IsSucceed = true
|
||||
}
|
||||
}
|
||||
case SLACK:
|
||||
{
|
||||
if res, err := req.Response(); err != nil {
|
||||
log.Error(4, "Delivery: %v", err)
|
||||
} else {
|
||||
defer res.Body.Close()
|
||||
contents, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
log.Error(4, "%s", err)
|
||||
} else {
|
||||
if string(contents) != "ok" {
|
||||
log.Error(4, "slack failed with: %s", string(contents))
|
||||
} else {
|
||||
t.IsSucceed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := UpdateHookTask(t); err != nil {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue