mirror of
https://github.com/go-gitea/gitea.git
synced 2025-11-12 08:47:10 +01:00
350 lines
10 KiB
Go
350 lines
10 KiB
Go
// Copyright 2025 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package webhook
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
|
|
webhook_model "code.gitea.io/gitea/models/webhook"
|
|
"code.gitea.io/gitea/modules/base"
|
|
"code.gitea.io/gitea/modules/git"
|
|
"code.gitea.io/gitea/modules/json"
|
|
"code.gitea.io/gitea/modules/log"
|
|
api "code.gitea.io/gitea/modules/structs"
|
|
"code.gitea.io/gitea/modules/util"
|
|
webhook_module "code.gitea.io/gitea/modules/webhook"
|
|
)
|
|
|
|
type (
|
|
// BarkPayload represents the payload for Bark notifications
|
|
BarkPayload struct {
|
|
Title string `json:"title"`
|
|
Body string `json:"body"`
|
|
URL string `json:"url,omitempty"`
|
|
Group string `json:"group,omitempty"`
|
|
Sound string `json:"sound,omitempty"`
|
|
Icon string `json:"icon,omitempty"`
|
|
}
|
|
|
|
// BarkMeta contains the metadata for the webhook
|
|
BarkMeta struct {
|
|
Sound string `json:"sound"`
|
|
Group string `json:"group"`
|
|
}
|
|
|
|
barkConvertor struct {
|
|
Sound string
|
|
Group string
|
|
}
|
|
)
|
|
|
|
// GetBarkHook returns bark metadata
|
|
func GetBarkHook(w *webhook_model.Webhook) *BarkMeta {
|
|
s := &BarkMeta{}
|
|
if err := json.Unmarshal([]byte(w.Meta), s); err != nil {
|
|
log.Error("webhook.GetBarkHook(%d): %v", w.ID, err)
|
|
}
|
|
return s
|
|
}
|
|
|
|
func (bc barkConvertor) getGroup(defaultGroup string) string {
|
|
if bc.Group != "" {
|
|
return bc.Group
|
|
}
|
|
return defaultGroup
|
|
}
|
|
|
|
// Create implements PayloadConvertor Create method
|
|
func (bc barkConvertor) Create(p *api.CreatePayload) (BarkPayload, error) {
|
|
refName := git.RefName(p.Ref).ShortName()
|
|
title := fmt.Sprintf("[%s] %s %s created", p.Repo.FullName, p.RefType, refName)
|
|
body := fmt.Sprintf("%s created %s %s", p.Sender.UserName, p.RefType, refName)
|
|
return BarkPayload{
|
|
Title: title,
|
|
Body: body,
|
|
URL: p.Repo.HTMLURL + "/src/" + util.PathEscapeSegments(refName),
|
|
Group: bc.getGroup(p.Repo.FullName),
|
|
Sound: bc.Sound,
|
|
}, nil
|
|
}
|
|
|
|
// Delete implements PayloadConvertor Delete method
|
|
func (bc barkConvertor) Delete(p *api.DeletePayload) (BarkPayload, error) {
|
|
refName := git.RefName(p.Ref).ShortName()
|
|
title := fmt.Sprintf("[%s] %s %s deleted", p.Repo.FullName, p.RefType, refName)
|
|
body := fmt.Sprintf("%s deleted %s %s", p.Sender.UserName, p.RefType, refName)
|
|
return BarkPayload{
|
|
Title: title,
|
|
Body: body,
|
|
URL: p.Repo.HTMLURL,
|
|
Group: bc.getGroup(p.Repo.FullName),
|
|
Sound: bc.Sound,
|
|
}, nil
|
|
}
|
|
|
|
// Fork implements PayloadConvertor Fork method
|
|
func (bc barkConvertor) Fork(p *api.ForkPayload) (BarkPayload, error) {
|
|
title := fmt.Sprintf("[%s] Repository forked", p.Forkee.FullName)
|
|
body := fmt.Sprintf("%s forked %s to %s", p.Sender.UserName, p.Forkee.FullName, p.Repo.FullName)
|
|
return BarkPayload{
|
|
Title: title,
|
|
Body: body,
|
|
URL: p.Repo.HTMLURL,
|
|
Group: bc.getGroup(p.Forkee.FullName),
|
|
Sound: bc.Sound,
|
|
}, nil
|
|
}
|
|
|
|
// Push implements PayloadConvertor Push method
|
|
func (bc barkConvertor) Push(p *api.PushPayload) (BarkPayload, error) {
|
|
branchName := git.RefName(p.Ref).ShortName()
|
|
|
|
var titleLink string
|
|
if p.TotalCommits == 1 {
|
|
titleLink = p.Commits[0].URL
|
|
} else {
|
|
titleLink = p.CompareURL
|
|
}
|
|
if titleLink == "" {
|
|
titleLink = p.Repo.HTMLURL + "/src/" + util.PathEscapeSegments(branchName)
|
|
}
|
|
|
|
title := fmt.Sprintf("[%s:%s] %d new commit(s)", p.Repo.FullName, branchName, p.TotalCommits)
|
|
|
|
var body strings.Builder
|
|
body.WriteString(fmt.Sprintf("%s pushed to %s\n", p.Pusher.UserName, branchName))
|
|
for i, commit := range p.Commits {
|
|
body.WriteString(fmt.Sprintf("%s: %s", commit.ID[:7], strings.TrimRight(commit.Message, "\r\n")))
|
|
if commit.Author != nil {
|
|
body.WriteString(" - " + commit.Author.Name)
|
|
}
|
|
if i < len(p.Commits)-1 {
|
|
body.WriteString("\n")
|
|
}
|
|
}
|
|
|
|
return BarkPayload{
|
|
Title: title,
|
|
Body: body.String(),
|
|
URL: titleLink,
|
|
Group: bc.getGroup(p.Repo.FullName),
|
|
Sound: bc.Sound,
|
|
}, nil
|
|
}
|
|
|
|
// Issue implements PayloadConvertor Issue method
|
|
func (bc barkConvertor) Issue(p *api.IssuePayload) (BarkPayload, error) {
|
|
title := fmt.Sprintf("[%s] Issue #%d: %s", p.Repository.FullName, p.Index, p.Action)
|
|
body := fmt.Sprintf("%s %s issue #%d: %s", p.Sender.UserName, p.Action, p.Index, p.Issue.Title)
|
|
|
|
return BarkPayload{
|
|
Title: title,
|
|
Body: body,
|
|
URL: p.Issue.HTMLURL,
|
|
Group: bc.getGroup(p.Repository.FullName),
|
|
Sound: bc.Sound,
|
|
}, nil
|
|
}
|
|
|
|
// Wiki implements PayloadConvertor Wiki method
|
|
func (bc barkConvertor) Wiki(p *api.WikiPayload) (BarkPayload, error) {
|
|
title := fmt.Sprintf("[%s] Wiki %s", p.Repository.FullName, p.Action)
|
|
body := fmt.Sprintf("%s %s wiki page: %s", p.Sender.UserName, p.Action, p.Page)
|
|
wikiURL := p.Repository.HTMLURL + "/wiki/" + url.PathEscape(p.Page)
|
|
|
|
return BarkPayload{
|
|
Title: title,
|
|
Body: body,
|
|
URL: wikiURL,
|
|
Group: bc.getGroup(p.Repository.FullName),
|
|
Sound: bc.Sound,
|
|
}, nil
|
|
}
|
|
|
|
// IssueComment implements PayloadConvertor IssueComment method
|
|
func (bc barkConvertor) IssueComment(p *api.IssueCommentPayload) (BarkPayload, error) {
|
|
title := fmt.Sprintf("[%s] New comment on #%d", p.Repository.FullName, p.Issue.Index)
|
|
body := fmt.Sprintf("%s commented on issue #%d: %s\n%s",
|
|
p.Sender.UserName, p.Issue.Index, p.Issue.Title,
|
|
truncateString(p.Comment.Body, 100))
|
|
|
|
return BarkPayload{
|
|
Title: title,
|
|
Body: body,
|
|
URL: p.Comment.HTMLURL,
|
|
Group: bc.getGroup(p.Repository.FullName),
|
|
Sound: bc.Sound,
|
|
}, nil
|
|
}
|
|
|
|
// PullRequest implements PayloadConvertor PullRequest method
|
|
func (bc barkConvertor) PullRequest(p *api.PullRequestPayload) (BarkPayload, error) {
|
|
title := fmt.Sprintf("[%s] PR #%d: %s", p.Repository.FullName, p.Index, p.Action)
|
|
body := fmt.Sprintf("%s %s pull request #%d: %s",
|
|
p.Sender.UserName, p.Action, p.Index, p.PullRequest.Title)
|
|
|
|
return BarkPayload{
|
|
Title: title,
|
|
Body: body,
|
|
URL: p.PullRequest.HTMLURL,
|
|
Group: bc.getGroup(p.Repository.FullName),
|
|
Sound: bc.Sound,
|
|
}, nil
|
|
}
|
|
|
|
// Review implements PayloadConvertor Review method
|
|
func (bc barkConvertor) Review(p *api.PullRequestPayload, event webhook_module.HookEventType) (BarkPayload, error) {
|
|
var action string
|
|
switch p.Action {
|
|
case api.HookIssueReviewed:
|
|
var err error
|
|
action, err = parseHookPullRequestEventType(event)
|
|
if err != nil {
|
|
return BarkPayload{}, err
|
|
}
|
|
}
|
|
|
|
title := fmt.Sprintf("[%s] PR #%d review %s", p.Repository.FullName, p.Index, action)
|
|
body := fmt.Sprintf("PR #%d: %s", p.Index, p.PullRequest.Title)
|
|
if p.Review != nil && p.Review.Content != "" {
|
|
body += "\n" + truncateString(p.Review.Content, 100)
|
|
}
|
|
|
|
return BarkPayload{
|
|
Title: title,
|
|
Body: body,
|
|
URL: p.PullRequest.HTMLURL,
|
|
Group: bc.getGroup(p.Repository.FullName),
|
|
Sound: bc.Sound,
|
|
}, nil
|
|
}
|
|
|
|
// Repository implements PayloadConvertor Repository method
|
|
func (bc barkConvertor) Repository(p *api.RepositoryPayload) (BarkPayload, error) {
|
|
var title, body string
|
|
switch p.Action {
|
|
case api.HookRepoCreated:
|
|
title = fmt.Sprintf("[%s] Repository created", p.Repository.FullName)
|
|
body = p.Sender.UserName + "created repository"
|
|
case api.HookRepoDeleted:
|
|
title = fmt.Sprintf("[%s] Repository deleted", p.Repository.FullName)
|
|
body = p.Sender.UserName + "deleted repository"
|
|
default:
|
|
return BarkPayload{}, nil
|
|
}
|
|
|
|
return BarkPayload{
|
|
Title: title,
|
|
Body: body,
|
|
URL: p.Repository.HTMLURL,
|
|
Group: bc.getGroup(p.Repository.FullName),
|
|
Sound: bc.Sound,
|
|
}, nil
|
|
}
|
|
|
|
// Release implements PayloadConvertor Release method
|
|
func (bc barkConvertor) Release(p *api.ReleasePayload) (BarkPayload, error) {
|
|
title := fmt.Sprintf("[%s] Release %s", p.Repository.FullName, p.Action)
|
|
body := fmt.Sprintf("%s %s release %s", p.Sender.UserName, p.Action, p.Release.TagName)
|
|
if p.Release.Title != "" {
|
|
body += ": " + p.Release.Title
|
|
}
|
|
|
|
return BarkPayload{
|
|
Title: title,
|
|
Body: body,
|
|
URL: p.Release.HTMLURL,
|
|
Group: bc.getGroup(p.Repository.FullName),
|
|
Sound: bc.Sound,
|
|
}, nil
|
|
}
|
|
|
|
// Package implements PayloadConvertor Package method
|
|
func (bc barkConvertor) Package(p *api.PackagePayload) (BarkPayload, error) {
|
|
title := fmt.Sprintf("[%s] Package %s", p.Repository.FullName, p.Action)
|
|
body := fmt.Sprintf("%s %s package %s:%s",
|
|
p.Sender.UserName, p.Action, p.Package.Name, p.Package.Version)
|
|
|
|
return BarkPayload{
|
|
Title: title,
|
|
Body: body,
|
|
URL: p.Package.HTMLURL,
|
|
Group: bc.getGroup(p.Repository.FullName),
|
|
Sound: bc.Sound,
|
|
}, nil
|
|
}
|
|
|
|
// Status implements PayloadConvertor Status method
|
|
func (bc barkConvertor) Status(p *api.CommitStatusPayload) (BarkPayload, error) {
|
|
title := fmt.Sprintf("[%s] Commit status: %s", p.Repo.FullName, p.State)
|
|
body := fmt.Sprintf("Commit %s: %s", base.ShortSha(p.SHA), p.Description)
|
|
if p.Context != "" {
|
|
body = fmt.Sprintf("%s (%s)", body, p.Context)
|
|
}
|
|
|
|
return BarkPayload{
|
|
Title: title,
|
|
Body: body,
|
|
URL: p.TargetURL,
|
|
Group: bc.getGroup(p.Repo.FullName),
|
|
Sound: bc.Sound,
|
|
}, nil
|
|
}
|
|
|
|
// WorkflowRun implements PayloadConvertor WorkflowRun method
|
|
func (bc barkConvertor) WorkflowRun(p *api.WorkflowRunPayload) (BarkPayload, error) {
|
|
title := fmt.Sprintf("[%s] Workflow %s", p.Repo.FullName, p.WorkflowRun.Status)
|
|
body := fmt.Sprintf("Workflow '%s' %s", p.WorkflowRun.DisplayTitle, p.WorkflowRun.Status)
|
|
|
|
return BarkPayload{
|
|
Title: title,
|
|
Body: body,
|
|
URL: p.WorkflowRun.HTMLURL,
|
|
Group: bc.getGroup(p.Repo.FullName),
|
|
Sound: bc.Sound,
|
|
}, nil
|
|
}
|
|
|
|
// WorkflowJob implements PayloadConvertor WorkflowJob method
|
|
func (bc barkConvertor) WorkflowJob(p *api.WorkflowJobPayload) (BarkPayload, error) {
|
|
title := fmt.Sprintf("[%s] Job %s", p.Repo.FullName, p.WorkflowJob.Status)
|
|
body := fmt.Sprintf("Job '%s' %s", p.WorkflowJob.Name, p.WorkflowJob.Status)
|
|
|
|
return BarkPayload{
|
|
Title: title,
|
|
Body: body,
|
|
URL: p.WorkflowJob.HTMLURL,
|
|
Group: bc.getGroup(p.Repo.FullName),
|
|
Sound: bc.Sound,
|
|
}, nil
|
|
}
|
|
|
|
// truncateString truncates a string to the specified length
|
|
func truncateString(s string, maxLen int) string {
|
|
if len(s) <= maxLen {
|
|
return s
|
|
}
|
|
return s[:maxLen] + "..."
|
|
}
|
|
|
|
func newBarkRequest(_ context.Context, w *webhook_model.Webhook, t *webhook_model.HookTask) (*http.Request, []byte, error) {
|
|
meta := &BarkMeta{}
|
|
if err := json.Unmarshal([]byte(w.Meta), meta); err != nil {
|
|
return nil, nil, fmt.Errorf("newBarkRequest meta json: %w", err)
|
|
}
|
|
var pc payloadConvertor[BarkPayload] = barkConvertor{
|
|
Sound: meta.Sound,
|
|
Group: meta.Group,
|
|
}
|
|
return newJSONRequest(pc, w, t, true)
|
|
}
|
|
|
|
func init() {
|
|
RegisterWebhookRequester(webhook_module.BARK, newBarkRequest)
|
|
}
|