0
0
mirror of https://github.com/go-gitea/gitea.git synced 2025-11-12 08:47:10 +01:00

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)
}