2017-10-25 13:13:45 +08:00
|
|
|
// Copyright 2017 The Gitea Authors. All rights reserved.
|
2022-11-27 13:20:29 -05:00
|
|
|
// SPDX-License-Identifier: MIT
|
2017-10-25 13:13:45 +08:00
|
|
|
|
2019-11-04 06:13:25 +08:00
|
|
|
package webhook
|
2017-08-28 13:06:45 +08:00
|
|
|
|
|
|
|
import (
|
2024-03-07 23:18:38 +01:00
|
|
|
"context"
|
2017-08-28 13:06:45 +08:00
|
|
|
"errors"
|
|
|
|
"fmt"
|
2024-03-22 16:02:48 +01:00
|
|
|
"html/template"
|
2024-03-07 23:18:38 +01:00
|
|
|
"net/http"
|
2022-09-04 21:54:23 +02:00
|
|
|
"net/url"
|
2024-11-18 21:47:11 +00:00
|
|
|
"regexp"
|
2017-08-28 13:06:45 +08:00
|
|
|
"strconv"
|
|
|
|
"strings"
|
2024-09-23 20:38:08 -07:00
|
|
|
"unicode/utf8"
|
2017-08-28 13:06:45 +08:00
|
|
|
|
2025-03-27 19:40:14 +00:00
|
|
|
webhook_model "forgejo.org/models/webhook"
|
2025-04-10 13:49:18 +02:00
|
|
|
"forgejo.org/modules/base"
|
2025-03-27 19:40:14 +00:00
|
|
|
"forgejo.org/modules/git"
|
|
|
|
"forgejo.org/modules/json"
|
|
|
|
"forgejo.org/modules/log"
|
|
|
|
"forgejo.org/modules/setting"
|
|
|
|
api "forgejo.org/modules/structs"
|
|
|
|
"forgejo.org/modules/util"
|
|
|
|
webhook_module "forgejo.org/modules/webhook"
|
|
|
|
gitea_context "forgejo.org/services/context"
|
|
|
|
"forgejo.org/services/forms"
|
|
|
|
"forgejo.org/services/webhook/shared"
|
2024-10-07 22:47:52 +02:00
|
|
|
|
2024-11-05 21:40:44 +01:00
|
|
|
"code.forgejo.org/go-chi/binding"
|
2017-08-28 13:06:45 +08:00
|
|
|
)
|
|
|
|
|
2024-03-20 15:44:01 +01:00
|
|
|
type discordHandler struct{}
|
|
|
|
|
|
|
|
func (discordHandler) Type() webhook_module.HookType { return webhook_module.DISCORD }
|
2024-04-03 14:22:36 +02:00
|
|
|
func (discordHandler) Icon(size int) template.HTML { return shared.ImgIcon("discord.png", size) }
|
2024-03-20 15:44:01 +01:00
|
|
|
|
2024-10-07 22:47:45 +02:00
|
|
|
type discordForm struct {
|
|
|
|
forms.WebhookCoreForm
|
|
|
|
PayloadURL string `binding:"Required;ValidUrl"`
|
2024-10-07 22:47:52 +02:00
|
|
|
Username string `binding:"Required;MaxSize(80)"`
|
|
|
|
IconURL string `binding:"ValidUrl"`
|
2024-10-07 22:47:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
var _ binding.Validator = &discordForm{}
|
|
|
|
|
|
|
|
// Validate implements binding.Validator.
|
|
|
|
func (d *discordForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
|
|
|
ctx := gitea_context.GetWebContext(req)
|
|
|
|
if len([]rune(d.IconURL)) > 2048 {
|
|
|
|
errs = append(errs, binding.Error{
|
|
|
|
FieldNames: []string{"IconURL"},
|
|
|
|
Message: ctx.Locale.TrString("repo.settings.discord_icon_url.exceeds_max_length"),
|
|
|
|
})
|
2024-03-21 13:54:27 +01:00
|
|
|
}
|
2024-10-07 22:47:45 +02:00
|
|
|
return errs
|
|
|
|
}
|
|
|
|
|
|
|
|
func (discordHandler) UnmarshalForm(bind func(any)) forms.WebhookForm {
|
|
|
|
var form discordForm
|
2024-03-21 13:54:27 +01:00
|
|
|
bind(&form)
|
|
|
|
|
2024-04-03 14:22:36 +02:00
|
|
|
return forms.WebhookForm{
|
|
|
|
WebhookCoreForm: form.WebhookCoreForm,
|
|
|
|
URL: form.PayloadURL,
|
|
|
|
ContentType: webhook_model.ContentTypeJSON,
|
|
|
|
Secret: "",
|
|
|
|
HTTPMethod: http.MethodPost,
|
2024-03-21 13:54:27 +01:00
|
|
|
Metadata: &DiscordMeta{
|
|
|
|
Username: form.Username,
|
|
|
|
IconURL: form.IconURL,
|
|
|
|
},
|
|
|
|
}
|
2024-03-21 13:23:27 +01:00
|
|
|
}
|
|
|
|
|
2017-08-28 13:06:45 +08:00
|
|
|
type (
|
|
|
|
// DiscordEmbedFooter for Embed Footer Structure.
|
|
|
|
DiscordEmbedFooter struct {
|
2024-10-07 22:47:52 +02:00
|
|
|
Text string `json:"text,omitempty"`
|
2017-08-28 13:06:45 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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"`
|
2024-10-07 22:47:52 +02:00
|
|
|
Fields []DiscordEmbedField `json:"fields,omitempty"`
|
2017-08-28 13:06:45 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// DiscordPayload represents
|
|
|
|
DiscordPayload struct {
|
2024-10-07 22:47:52 +02:00
|
|
|
Wait bool `json:"-"`
|
|
|
|
Content string `json:"-"`
|
2017-08-28 13:06:45 +08:00
|
|
|
Username string `json:"username"`
|
2023-01-10 13:01:52 -06:00
|
|
|
AvatarURL string `json:"avatar_url,omitempty"`
|
2024-10-07 22:47:52 +02:00
|
|
|
TTS bool `json:"-"`
|
2017-08-28 13:06:45 +08:00
|
|
|
Embeds []DiscordEmbed `json:"embeds"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// DiscordMeta contains the discord metadata
|
|
|
|
DiscordMeta struct {
|
|
|
|
Username string `json:"username"`
|
|
|
|
IconURL string `json:"icon_url"`
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
2024-03-20 15:44:01 +01:00
|
|
|
// Metadata returns discord metadata
|
|
|
|
func (discordHandler) Metadata(w *webhook_model.Webhook) any {
|
2019-11-04 06:13:25 +08:00
|
|
|
s := &DiscordMeta{}
|
|
|
|
if err := json.Unmarshal([]byte(w.Meta), s); err != nil {
|
2024-03-20 15:44:01 +01:00
|
|
|
log.Error("discordHandler.Metadata(%d): %v", w.ID, err)
|
2019-11-04 06:13:25 +08:00
|
|
|
}
|
|
|
|
return s
|
|
|
|
}
|
|
|
|
|
2017-08-28 13:06:45 +08:00
|
|
|
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 (
|
2019-10-18 17:42:04 -05:00
|
|
|
greenColor = color("1ac600")
|
|
|
|
greenColorLight = color("bfe5bf")
|
|
|
|
yellowColor = color("ffd930")
|
|
|
|
greyColor = color("4f545c")
|
|
|
|
purpleColor = color("7289da")
|
|
|
|
orangeColor = color("eb6420")
|
|
|
|
orangeColorLight = color("e68d60")
|
|
|
|
redColor = color("ff3232")
|
2017-08-28 13:06:45 +08:00
|
|
|
)
|
|
|
|
|
2025-04-04 21:09:40 +03:00
|
|
|
// https://discord.com/developers/docs/resources/message#embed-object-embed-limits
|
|
|
|
// Discord has some limits in place for the embeds.
|
|
|
|
// According to some tests, there is no consistent limit for different character sets.
|
|
|
|
// For example: 4096 ASCII letters are allowed, but only 2490 emoji characters are allowed.
|
|
|
|
// To keep it simple, we currently truncate at 2000.
|
|
|
|
const discordDescriptionCharactersLimit = 2000
|
|
|
|
|
|
|
|
type discordConvertor struct {
|
|
|
|
Username string
|
|
|
|
AvatarURL string
|
|
|
|
}
|
|
|
|
|
2020-09-05 10:57:13 +08:00
|
|
|
// Create implements PayloadConvertor Create method
|
2024-03-07 23:18:38 +01:00
|
|
|
func (d discordConvertor) Create(p *api.CreatePayload) (DiscordPayload, error) {
|
2017-08-28 13:06:45 +08:00
|
|
|
// created tag/branch
|
2023-05-26 09:04:48 +08:00
|
|
|
refName := git.RefName(p.Ref).ShortName()
|
2017-08-28 13:06:45 +08:00
|
|
|
title := fmt.Sprintf("[%s] %s %s created", p.Repo.FullName, p.RefType, refName)
|
|
|
|
|
2021-11-16 18:18:25 +00:00
|
|
|
return d.createPayload(p.Sender, title, "", p.Repo.HTMLURL+"/src/"+util.PathEscapeSegments(refName), greenColor), nil
|
2017-08-28 13:06:45 +08:00
|
|
|
}
|
|
|
|
|
2020-09-05 10:57:13 +08:00
|
|
|
// Delete implements PayloadConvertor Delete method
|
2024-03-07 23:18:38 +01:00
|
|
|
func (d discordConvertor) Delete(p *api.DeletePayload) (DiscordPayload, error) {
|
2018-05-16 22:01:55 +08:00
|
|
|
// deleted tag/branch
|
2023-05-26 09:04:48 +08:00
|
|
|
refName := git.RefName(p.Ref).ShortName()
|
2018-05-16 22:01:55 +08:00
|
|
|
title := fmt.Sprintf("[%s] %s %s deleted", p.Repo.FullName, p.RefType, refName)
|
|
|
|
|
2021-11-16 18:18:25 +00:00
|
|
|
return d.createPayload(p.Sender, title, "", p.Repo.HTMLURL+"/src/"+util.PathEscapeSegments(refName), redColor), nil
|
2018-05-16 22:01:55 +08:00
|
|
|
}
|
|
|
|
|
2020-09-05 10:57:13 +08:00
|
|
|
// Fork implements PayloadConvertor Fork method
|
2024-03-07 23:18:38 +01:00
|
|
|
func (d discordConvertor) Fork(p *api.ForkPayload) (DiscordPayload, error) {
|
2018-05-16 22:01:55 +08:00
|
|
|
title := fmt.Sprintf("%s is forked to %s", p.Forkee.FullName, p.Repo.FullName)
|
|
|
|
|
2021-06-21 04:12:19 +02:00
|
|
|
return d.createPayload(p.Sender, title, "", p.Repo.HTMLURL, greenColor), nil
|
2018-05-16 22:01:55 +08:00
|
|
|
}
|
|
|
|
|
2020-09-05 10:57:13 +08:00
|
|
|
// Push implements PayloadConvertor Push method
|
2024-03-07 23:18:38 +01:00
|
|
|
func (d discordConvertor) Push(p *api.PushPayload) (DiscordPayload, error) {
|
2017-08-28 13:06:45 +08:00
|
|
|
var (
|
2023-05-26 09:04:48 +08:00
|
|
|
branchName = git.RefName(p.Ref).ShortName()
|
2017-08-28 13:06:45 +08:00
|
|
|
commitDesc string
|
|
|
|
)
|
|
|
|
|
|
|
|
var titleLink string
|
2022-10-16 18:22:34 +02:00
|
|
|
if p.TotalCommits == 1 {
|
2017-08-28 13:06:45 +08:00
|
|
|
commitDesc = "1 new commit"
|
|
|
|
titleLink = p.Commits[0].URL
|
|
|
|
} else {
|
2022-10-16 18:22:34 +02:00
|
|
|
commitDesc = fmt.Sprintf("%d new commits", p.TotalCommits)
|
2017-08-28 13:06:45 +08:00
|
|
|
titleLink = p.CompareURL
|
|
|
|
}
|
|
|
|
if titleLink == "" {
|
2021-11-16 18:18:25 +00:00
|
|
|
titleLink = p.Repo.HTMLURL + "/src/" + util.PathEscapeSegments(branchName)
|
2017-08-28 13:06:45 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
2024-09-23 20:38:08 -07:00
|
|
|
// limit the commit message display to just the summary, otherwise it would be hard to read
|
|
|
|
message := strings.TrimRight(strings.SplitN(commit.Message, "\n", 1)[0], "\r")
|
|
|
|
|
2024-11-18 21:47:11 +00:00
|
|
|
// Escaping markdown character
|
|
|
|
message = escapeMarkdown(message)
|
|
|
|
|
2024-09-23 20:38:08 -07:00
|
|
|
// a limit of 50 is set because GitHub does the same
|
|
|
|
if utf8.RuneCountInString(message) > 50 {
|
|
|
|
message = fmt.Sprintf("%.47s...", message)
|
|
|
|
}
|
|
|
|
text += fmt.Sprintf("[%s](%s) %s - %s", commit.ID[:7], commit.URL, message, commit.Author.Name)
|
2017-08-28 13:06:45 +08:00
|
|
|
// add linebreak to each commit but the last
|
|
|
|
if i < len(p.Commits)-1 {
|
|
|
|
text += "\n"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-21 04:12:19 +02:00
|
|
|
return d.createPayload(p.Sender, title, text, titleLink, greenColor), nil
|
2017-08-28 13:06:45 +08:00
|
|
|
}
|
|
|
|
|
2020-09-05 10:57:13 +08:00
|
|
|
// Issue implements PayloadConvertor Issue method
|
2024-03-07 23:18:38 +01:00
|
|
|
func (d discordConvertor) Issue(p *api.IssuePayload) (DiscordPayload, error) {
|
2021-06-21 04:12:19 +02:00
|
|
|
title, _, text, color := getIssuesPayloadInfo(p, noneLinkFormatter, false)
|
2018-05-16 22:01:55 +08:00
|
|
|
|
2021-06-21 04:12:19 +02:00
|
|
|
return d.createPayload(p.Sender, title, text, p.Issue.HTMLURL, color), nil
|
2018-05-16 22:01:55 +08:00
|
|
|
}
|
|
|
|
|
2020-09-05 10:57:13 +08:00
|
|
|
// IssueComment implements PayloadConvertor IssueComment method
|
2024-03-07 23:18:38 +01:00
|
|
|
func (d discordConvertor) IssueComment(p *api.IssueCommentPayload) (DiscordPayload, error) {
|
2021-06-21 04:12:19 +02:00
|
|
|
title, _, color := getIssueCommentPayloadInfo(p, noneLinkFormatter, false)
|
2019-10-18 17:42:04 -05:00
|
|
|
|
2021-06-21 04:12:19 +02:00
|
|
|
return d.createPayload(p.Sender, title, p.Comment.Body, p.Comment.HTMLURL, color), nil
|
2018-05-16 22:01:55 +08:00
|
|
|
}
|
|
|
|
|
2020-09-05 10:57:13 +08:00
|
|
|
// PullRequest implements PayloadConvertor PullRequest method
|
2024-03-07 23:18:38 +01:00
|
|
|
func (d discordConvertor) PullRequest(p *api.PullRequestPayload) (DiscordPayload, error) {
|
2021-06-21 04:12:19 +02:00
|
|
|
title, _, text, color := getPullRequestPayloadInfo(p, noneLinkFormatter, false)
|
2017-08-28 13:06:45 +08:00
|
|
|
|
2021-06-21 04:12:19 +02:00
|
|
|
return d.createPayload(p.Sender, title, text, p.PullRequest.HTMLURL, color), nil
|
2017-08-28 13:06:45 +08:00
|
|
|
}
|
|
|
|
|
2020-09-05 10:57:13 +08:00
|
|
|
// Review implements PayloadConvertor Review method
|
2024-03-07 23:18:38 +01:00
|
|
|
func (d discordConvertor) Review(p *api.PullRequestPayload, event webhook_module.HookEventType) (DiscordPayload, error) {
|
2018-12-27 19:04:30 +01:00
|
|
|
var text, title string
|
|
|
|
var color int
|
2024-07-17 19:19:36 +02:00
|
|
|
if p.Action == api.HookIssueReviewed {
|
2018-12-27 19:04:30 +01:00
|
|
|
action, err := parseHookPullRequestEventType(event)
|
|
|
|
if err != nil {
|
2024-03-07 23:18:38 +01:00
|
|
|
return DiscordPayload{}, err
|
2018-12-27 19:04:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
title = fmt.Sprintf("[%s] Pull request review %s: #%d %s", p.Repository.FullName, action, p.Index, p.PullRequest.Title)
|
2019-10-18 17:42:04 -05:00
|
|
|
text = p.Review.Content
|
2019-10-18 03:33:19 -05:00
|
|
|
|
|
|
|
switch event {
|
2023-01-01 16:23:15 +01:00
|
|
|
case webhook_module.HookEventPullRequestReviewApproved:
|
2019-10-18 17:42:04 -05:00
|
|
|
color = greenColor
|
2023-01-01 16:23:15 +01:00
|
|
|
case webhook_module.HookEventPullRequestReviewRejected:
|
2019-10-18 17:42:04 -05:00
|
|
|
color = redColor
|
2023-03-24 13:13:04 +08:00
|
|
|
case webhook_module.HookEventPullRequestReviewComment:
|
2019-10-18 17:42:04 -05:00
|
|
|
color = greyColor
|
2019-10-18 03:33:19 -05:00
|
|
|
default:
|
2019-10-18 17:42:04 -05:00
|
|
|
color = yellowColor
|
2019-10-18 03:33:19 -05:00
|
|
|
}
|
2018-12-27 19:04:30 +01:00
|
|
|
}
|
|
|
|
|
2021-06-21 04:12:19 +02:00
|
|
|
return d.createPayload(p.Sender, title, text, p.PullRequest.HTMLURL, color), nil
|
2018-12-27 19:04:30 +01:00
|
|
|
}
|
|
|
|
|
2020-09-05 10:57:13 +08:00
|
|
|
// Repository implements PayloadConvertor Repository method
|
2024-03-07 23:18:38 +01:00
|
|
|
func (d discordConvertor) Repository(p *api.RepositoryPayload) (DiscordPayload, error) {
|
2017-09-03 01:20:24 -07:00
|
|
|
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
|
2019-10-18 17:42:04 -05:00
|
|
|
color = greenColor
|
2017-09-03 01:20:24 -07:00
|
|
|
case api.HookRepoDeleted:
|
|
|
|
title = fmt.Sprintf("[%s] Repository deleted", p.Repository.FullName)
|
2019-10-18 17:42:04 -05:00
|
|
|
color = redColor
|
2017-09-03 01:20:24 -07:00
|
|
|
}
|
|
|
|
|
2021-06-21 04:12:19 +02:00
|
|
|
return d.createPayload(p.Sender, title, "", url, color), nil
|
2017-09-03 01:20:24 -07:00
|
|
|
}
|
|
|
|
|
2022-09-04 21:54:23 +02:00
|
|
|
// Wiki implements PayloadConvertor Wiki method
|
2024-03-07 23:18:38 +01:00
|
|
|
func (d discordConvertor) Wiki(p *api.WikiPayload) (DiscordPayload, error) {
|
2022-09-04 21:54:23 +02:00
|
|
|
text, color, _ := getWikiPayloadInfo(p, noneLinkFormatter, false)
|
|
|
|
htmlLink := p.Repository.HTMLURL + "/wiki/" + url.PathEscape(p.Page)
|
|
|
|
|
|
|
|
var description string
|
|
|
|
if p.Action != api.HookWikiDeleted {
|
|
|
|
description = p.Comment
|
|
|
|
}
|
|
|
|
|
|
|
|
return d.createPayload(p.Sender, text, description, htmlLink, color), nil
|
|
|
|
}
|
|
|
|
|
2020-09-05 10:57:13 +08:00
|
|
|
// Release implements PayloadConvertor Release method
|
2024-03-07 23:18:38 +01:00
|
|
|
func (d discordConvertor) Release(p *api.ReleasePayload) (DiscordPayload, error) {
|
2020-01-04 16:20:15 -06:00
|
|
|
text, color := getReleasePayloadInfo(p, noneLinkFormatter, false)
|
2018-05-16 22:01:55 +08:00
|
|
|
|
2023-09-21 17:55:09 -05:00
|
|
|
return d.createPayload(p.Sender, text, p.Release.Note, p.Release.HTMLURL, color), nil
|
2018-05-16 22:01:55 +08:00
|
|
|
}
|
|
|
|
|
2024-03-07 23:18:38 +01:00
|
|
|
func (d discordConvertor) Package(p *api.PackagePayload) (DiscordPayload, error) {
|
2023-10-31 12:43:38 +08:00
|
|
|
text, color := getPackagePayloadInfo(p, noneLinkFormatter, false)
|
|
|
|
|
|
|
|
return d.createPayload(p.Sender, text, "", p.Package.HTMLURL, color), nil
|
|
|
|
}
|
|
|
|
|
Actions Failure, Succes, Recover Webhooks (#7508)
Implement Actions Success, Failure and Recover webhooks for Forgejo, Gitea, Gogs, Slack, Discord, DingTalk, Telegram, Microsoft Teams, Feishu / Lark Suite, Matrix, WeCom (Wechat Work), Packagist. Some of these webhooks have not been manually tested.
Implement settings for these new webhooks.
## Checklist
The [contributor guide](https://forgejo.org/docs/next/contributor/) contains information that will be helpful to first time contributors. There also are a few [conditions for merging Pull Requests in Forgejo repositories](https://codeberg.org/forgejo/governance/src/branch/main/PullRequestsAgreement.md). You are also welcome to join the [Forgejo development chatroom](https://matrix.to/#/#forgejo-development:matrix.org).
### Tests
- I added test coverage for Go changes...
- [x] in their respective `*_test.go` for unit tests.
- [x] in the `tests/integration` directory if it involves interactions with a live Forgejo server.
- I added test coverage for JavaScript changes...
- [ ] in `web_src/js/*.test.js` if it can be unit tested.
- [ ] in `tests/e2e/*.test.e2e.js` if it requires interactions with a live Forgejo server (see also the [developer guide for JavaScript testing](https://codeberg.org/forgejo/forgejo/src/branch/forgejo/tests/e2e/README.md#end-to-end-tests)).
### Documentation
- [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change.
- [x] I did not document these changes and I do not expect someone else to do it.
### Release notes
- [ ] I do not want this change to show in the release notes.
- [x] I want the title to show in the release notes with a link to this pull request.
- [ ] I want the content of the `release-notes/<pull request number>.md` to be be used for the release notes instead of the title.
<!--start release-notes-assistant-->
## Release notes
<!--URL:https://codeberg.org/forgejo/forgejo-->
- Features
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7508): <!--number 7508 --><!--line 0 --><!--description QWN0aW9ucyBGYWlsdXJlLCBTdWNjZXMsIFJlY292ZXIgV2ViaG9va3M=-->Actions Failure, Succes, Recover Webhooks<!--description-->
<!--end release-notes-assistant-->
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7508
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
Co-authored-by: christopher-besch <mail@chris-besch.com>
Co-committed-by: christopher-besch <mail@chris-besch.com>
2025-06-03 14:29:19 +02:00
|
|
|
func (d discordConvertor) Action(p *api.ActionPayload) (DiscordPayload, error) {
|
|
|
|
text, color := getActionPayloadInfo(p, noneLinkFormatter)
|
|
|
|
|
|
|
|
return d.createPayload(p.Run.TriggerUser, text, "", p.Run.HTMLURL, color), nil
|
|
|
|
}
|
|
|
|
|
2024-04-03 14:22:36 +02:00
|
|
|
var _ shared.PayloadConvertor[DiscordPayload] = discordConvertor{}
|
2017-08-28 13:06:45 +08:00
|
|
|
|
2024-03-20 15:44:01 +01:00
|
|
|
func (discordHandler) NewRequest(ctx context.Context, w *webhook_model.Webhook, t *webhook_model.HookTask) (*http.Request, []byte, error) {
|
2024-03-07 23:18:38 +01:00
|
|
|
meta := &DiscordMeta{}
|
|
|
|
if err := json.Unmarshal([]byte(w.Meta), meta); err != nil {
|
2024-03-20 15:44:01 +01:00
|
|
|
return nil, nil, fmt.Errorf("discordHandler.NewRequest meta json: %w", err)
|
2024-03-07 23:18:38 +01:00
|
|
|
}
|
|
|
|
sc := discordConvertor{
|
|
|
|
Username: meta.Username,
|
|
|
|
AvatarURL: meta.IconURL,
|
|
|
|
}
|
2024-04-03 14:22:36 +02:00
|
|
|
return shared.NewJSONRequest(sc, w, t, true)
|
2017-08-28 13:06:45 +08:00
|
|
|
}
|
2018-12-27 19:04:30 +01:00
|
|
|
|
2023-01-01 16:23:15 +01:00
|
|
|
func parseHookPullRequestEventType(event webhook_module.HookEventType) (string, error) {
|
2018-12-27 19:04:30 +01:00
|
|
|
switch event {
|
2023-01-01 16:23:15 +01:00
|
|
|
case webhook_module.HookEventPullRequestReviewApproved:
|
2018-12-27 19:04:30 +01:00
|
|
|
return "approved", nil
|
2023-01-01 16:23:15 +01:00
|
|
|
case webhook_module.HookEventPullRequestReviewRejected:
|
[gitea] week 2025-19 cherry pick (gitea/main -> forgejo) (#7909)
## Checklist
- [x] go to the last cherry-pick PR (forgejo/forgejo#7804) to figure out how far it went: [gitea@a2024953c5](https://github.com/go-gitea/gitea/commit/a2024953c5914c5a7d59d236262f9bd94b65b996)
- [x] cherry-pick and open PR (forgejo/forgejo#7909)
- [ ] have the PR pass the CI
- end-to-end (specially important if there are actions related changes)
- [ ] add `run-end-to-end` label
- [ ] check the result
- [ ] write release notes
- [ ] assign reviewers
- [ ] 48h later, last call
- merge 1 hour after the last call
## Legend
- :question: - No decision about the commit has been made.
- :cherries: - The commit has been cherry picked.
- :fast_forward: - The commit has been skipped.
- :bulb: - The commit has been skipped, but should be ported to Forgejo.
- :writing_hand: - The commit has been skipped, and a port to Forgejo already exists.
## Commits
- :cherries: [`gitea`](https://github.com/go-gitea/gitea/commit/e92c4f18083ed312b69591ebb77e0f504ee77025) -> [`forgejo`](https://codeberg.org/forgejo/forgejo/commit/56fa2caef32c4b0e5017f4b09188ad1dfc8d3603) Add missing setting load in dump-repo command ([gitea#34479](https://github.com/go-gitea/gitea/pull/34479))
- :cherries: [`gitea`](https://github.com/go-gitea/gitea/commit/7b518bc6c79035a53c0b752680d833fce5e1a2fe) -> [`forgejo`](https://codeberg.org/forgejo/forgejo/commit/6e5299606a1bd42cb45ed472a84ba797cf2fa790) Change "rejected" to "changes requested" in 3rd party PR review notification ([gitea#34481](https://github.com/go-gitea/gitea/pull/34481))
## TODO
- :bulb: [`gitea`](https://github.com/go-gitea/gitea/commit/972381097c6b3488bf9d5c3c4fde04775b2e3a3c) Fix url validation in webhook add/edit API ([gitea#34492](https://github.com/go-gitea/gitea/pull/34492))
Relevant input validation but test needs more backport.
------
- :bulb: [`gitea`](https://github.com/go-gitea/gitea/commit/59df03b5542f05a1a927996fbf6483f7da363e03) Fix get / delete runner to use consistent http 404 and 500 status ([gitea#34480](https://github.com/go-gitea/gitea/pull/34480))
It may be relevant to Forgejo as well
------
- :bulb: [`gitea`](https://github.com/go-gitea/gitea/commit/1e2f3514b9afd903e19a0413e5d925515e13abf8) Add endpoint deleting workflow run ([gitea#34337](https://github.com/go-gitea/gitea/pull/34337))
Actions, it would be worth having in Forgejo as well.
------
- :bulb: [`gitea`](https://github.com/go-gitea/gitea/commit/5cb4cbf044e2f0483afc92516bb4b9aff6ea2b9a) Fix repo broken check ([gitea#34444](https://github.com/go-gitea/gitea/pull/34444))
Check wether this is relevant to us, port if yes.
------
- :bulb: [`gitea`](https://github.com/go-gitea/gitea/commit/355e9a9d544aa2d3f3a17b06cdb2bf1ceb290fd7) Add a webhook push test for dev branch ([gitea#34421](https://github.com/go-gitea/gitea/pull/34421))
Enhances webhook integration tests.
------
- :bulb: [`gitea`](https://github.com/go-gitea/gitea/commit/34281bc198a5ad9a1faa5285d4648b05d7218aaa) Fix bug webhook milestone is not right. ([gitea#34419](https://github.com/go-gitea/gitea/pull/34419))
Testcode diverged, port required.
------
- :bulb: [`gitea`](https://github.com/go-gitea/gitea/commit/780e92ea99646dfefbe11734a4845fbf304be83c) Only git operations should update `last changed` of a repository ([gitea#34388](https://github.com/go-gitea/gitea/pull/34388))
Port required, would benefit from additional tests.
------
- :bulb: [`gitea`](https://github.com/go-gitea/gitea/commit/b07e03956af8f29464067b19cb5cacee358b592f) When updating comment, if the content is the same, just return and not update the databse ([gitea#34422](https://github.com/go-gitea/gitea/pull/34422))
Codebase diverged, port required.
------
- :bulb: [`gitea`](https://github.com/go-gitea/gitea/commit/71a11872091634f1370374ef123d32798ec0447d) Fix incorrect divergence cache after switching default branch ([gitea#34370](https://github.com/go-gitea/gitea/pull/34370))
Depends on previous gitea changes, port needed.
------
- :bulb: [`gitea`](https://github.com/go-gitea/gitea/commit/4c611bf280c501c22c6a58e94d9e3ce6a73214df) Add a button editing action secret ([gitea#34348](https://github.com/go-gitea/gitea/pull/34348))
This is an interesting feature and it has tests as well. Feature request covering this: https://codeberg.org/forgejo/forgejo/issues/7882
------
- :bulb: [`gitea`](https://github.com/go-gitea/gitea/commit/2fbc8f9e87fc37f21997bf32d9b29fc16e92780c) Fix LFS file not stored in LFS when uploaded/edited via API or web UI ([gitea#34367](https://github.com/go-gitea/gitea/pull/34367))
Our code diverged - pls. check relevance & maybe port.
------
- :bulb: [`gitea`](https://github.com/go-gitea/gitea/commit/020e774b915512815637aac743ddbe595c5eede5) feat: add label 'state' to metric 'gitea_users' ([gitea#34326](https://github.com/go-gitea/gitea/pull/34326))
Adjust our existing tests while porting this.
------
## Skipped
- :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/ec10c6ba5a6c4c3a5ab9bdf47616d6058c6fb59c) [skip ci] Updated translations via Crowdin
------
- :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/d89eed998f9e4dc84d0ef02451703590d7a8ef51) Fix edithook api can not update package, status and workflow_job events ([gitea#34495](https://github.com/go-gitea/gitea/pull/34495))
- gitea actions specific specific
------
- :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/b6c066747400927407a6b4807165d2de40c7495b) Add R-HNF to the TRANSLATORS file ([gitea#34494](https://github.com/go-gitea/gitea/pull/34494))
- gitea translators update specific
------
- :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/6fbf0e67383dd2970d8e6c309ebc5c634732866c) nix flake update ([gitea#34476](https://github.com/go-gitea/gitea/pull/34476))
- gitea dependency update specific
------
- :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/c24f4b3d2912224164ee3a75850a6a31bdd041f1) Add migrations tests ([gitea#34456](https://github.com/go-gitea/gitea/pull/34456))
------
- :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/bf338bb9e231a8f9ccef7de2e13a0fdf871fc680) Fix project board view ([gitea#34470](https://github.com/go-gitea/gitea/pull/34470))
- gitea ui specific specific
------
- :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/319d03fbc049de34a6fa0bc3019107ec5b724ff2) [skip ci] Updated translations via Crowdin
------
- :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/dd500ce5598848e4f50999b627155ec8520932d3) Fix Workflow run Not Found page ([gitea#34459](https://github.com/go-gitea/gitea/pull/34459))
- gitea actions specific specific
------
- :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/b6bf128f1e1a943df85c1ce276d0317c8689ffca) [skip ci] Updated translations via Crowdin
------
- :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/a0595add72db4a5fb421579b9c6bb7dae1392c86) Fix remove org user failure on mssql ([gitea#34449](https://github.com/go-gitea/gitea/pull/34449))
------
- :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/b5fd3e7210cfbcb87015f3a5b8c3f25eab2a5715) Fix comment textarea scroll issue in Firefox ([gitea#34438](https://github.com/go-gitea/gitea/pull/34438))
- gitea ui specific specific
------
- :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/4011e2245bcd96f53077f73b7a33b1a754f7151f) Fix releases sidebar navigation link ([gitea#34436](https://github.com/go-gitea/gitea/pull/34436))
- gitea ui specific specific
------
- :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/0902d42fc753cd5f266046f003307285fe9507d5) [skip ci] Updated translations via Crowdin
------
- :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/4a98ab05403ef1900937487d434bc075812b0303) Remove legacy template helper functions ([gitea#34426](https://github.com/go-gitea/gitea/pull/34426))
- gitea specific specific
------
- :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/9b8609e017aef8376eb59d9fd3e428e35f9caeda) Fix GetUsersByEmails ([gitea#34423](https://github.com/go-gitea/gitea/pull/34423))
- gitea specific specific
------
- :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/0f63a5ef48b23c6ab26a4b13cfd26edbe4efbfa3) [skip ci] Updated translations via Crowdin
------
- :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/ad271444e912ddf44591451292b39b0d6b859955) Fix a bug when uploading file via lfs ssh command ([gitea#34408](https://github.com/go-gitea/gitea/pull/34408))
:skiP: present with PR #7752
------
- :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/8b16ab719cab24805beb2189af7ee960ca94d524) Merge and tweak markup editor expander CSS ([gitea#34409](https://github.com/go-gitea/gitea/pull/34409))
- gitea ui specific specific
------
- :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/2ecd73d2e586cd4ff2001246d54c4affe0e1ccec) Bump `@github/relative-time-element` to v4.4.8 ([gitea#34413](https://github.com/go-gitea/gitea/pull/34413))
- gitea dependency update specific
------
- :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/179068fddbb463f3a34162730649d82acec522d3) Refactor commit message rendering and fix bugs ([gitea#34412](https://github.com/go-gitea/gitea/pull/34412))
- gitea ui specific specific
------
- :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/44aadc37c9c0810f3a41189929ae21c613b6bc98) [skip ci] Updated translations via Crowdin
------
- :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/f63822fe64b0759dc7e38b467eaa7c41b71d8c5d) Fix autofocus behavior ([gitea#34397](https://github.com/go-gitea/gitea/pull/34397))
- gitea ui specific specific
------
- :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/82071ee7300d478f56519ec30be0213b18a7882c) [skip ci] Updated translations via Crowdin
------
- :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/bbfc21e74f69a9b1ac83271923210c731edd2873) Fix "The sidebar of the repository file list does not have a fixed height #34298" ([gitea#34321](https://github.com/go-gitea/gitea/pull/34321))
- gitea ui specific specific
------
- :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/dd886d729f6206ad878aa5e0f0f3d4d20ea9a208) Update JS and PY dependencies ([gitea#34391](https://github.com/go-gitea/gitea/pull/34391))
- gitea dependency update specific
------
- :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/2a660b4a1b6913f80981d8840b3dc129a4eb7f26) Upgrade go-github v61 -> v71 ([gitea#34385](https://github.com/go-gitea/gitea/pull/34385))
- gitea dependency update specific
------
- :fast_forward: [`gitea`](https://github.com/go-gitea/gitea/commit/6bd8fe53537172fb4df976962115f1b928bf2993) Bump `@github/relative-time-element` to v4.4.7 ([gitea#34384](https://github.com/go-gitea/gitea/pull/34384))
- gitea dependency update specific
------
<details>
<summary><h2>Stats</h2></summary>
<br>
Between [`gitea@a2024953c5`](https://github.com/go-gitea/gitea/commit/a2024953c5914c5a7d59d236262f9bd94b65b996) and [`gitea@ec10c6ba5a`](https://github.com/go-gitea/gitea/commit/ec10c6ba5a6c4c3a5ab9bdf47616d6058c6fb59c), **41** commits have been reviewed. We picked **2**, skipped **27**, and decided to port **12**.
</details>
Co-authored-by: Sebastian Weigand <s.weigand.phy@gmail.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7909
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
Co-authored-by: Michael Jerger <michael.jerger@meissa-gmbh.de>
Co-committed-by: Michael Jerger <michael.jerger@meissa-gmbh.de>
2025-06-27 13:59:07 +02:00
|
|
|
return "requested changes", nil
|
2023-03-24 13:13:04 +08:00
|
|
|
case webhook_module.HookEventPullRequestReviewComment:
|
2018-12-27 19:04:30 +01:00
|
|
|
return "comment", nil
|
|
|
|
default:
|
|
|
|
return "", errors.New("unknown event type")
|
|
|
|
}
|
|
|
|
}
|
2021-06-21 04:12:19 +02:00
|
|
|
|
2024-03-07 23:18:38 +01:00
|
|
|
func (d discordConvertor) createPayload(s *api.User, title, text, url string, color int) DiscordPayload {
|
2024-10-07 22:47:52 +02:00
|
|
|
if len([]rune(title)) > 256 {
|
|
|
|
title = fmt.Sprintf("%.253s...", title)
|
|
|
|
}
|
|
|
|
if len([]rune(text)) > 4096 {
|
|
|
|
text = fmt.Sprintf("%.4093s...", text)
|
|
|
|
}
|
2024-03-07 23:18:38 +01:00
|
|
|
return DiscordPayload{
|
2021-06-21 04:12:19 +02:00
|
|
|
Username: d.Username,
|
|
|
|
AvatarURL: d.AvatarURL,
|
|
|
|
Embeds: []DiscordEmbed{
|
|
|
|
{
|
|
|
|
Title: title,
|
2025-04-10 13:49:18 +02:00
|
|
|
Description: base.TruncateString(text, discordDescriptionCharactersLimit),
|
2021-06-21 04:12:19 +02:00
|
|
|
URL: url,
|
|
|
|
Color: color,
|
|
|
|
Author: DiscordEmbedAuthor{
|
|
|
|
Name: s.UserName,
|
|
|
|
URL: setting.AppURL + s.UserName,
|
|
|
|
IconURL: s.AvatarURL,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
2024-11-18 21:47:11 +00:00
|
|
|
|
|
|
|
var orderedListPattern = regexp.MustCompile(`(\d+)\.`)
|
|
|
|
|
|
|
|
var markdownPatterns = map[string]*regexp.Regexp{
|
|
|
|
"~": regexp.MustCompile(`\~(.*?)\~`),
|
|
|
|
"*": regexp.MustCompile(`\*(.*?)\*`),
|
|
|
|
"_": regexp.MustCompile(`\_(.*?)\_`),
|
|
|
|
}
|
|
|
|
|
|
|
|
var markdownToEscape = strings.NewReplacer(
|
|
|
|
"* ", "\\* ",
|
|
|
|
"`", "\\`",
|
|
|
|
"[", "\\[",
|
|
|
|
"]", "\\]",
|
|
|
|
"(", "\\(",
|
|
|
|
")", "\\)",
|
|
|
|
"#", "\\#",
|
|
|
|
"+ ", "\\+ ",
|
|
|
|
"- ", "\\- ",
|
|
|
|
"---", "\\---",
|
|
|
|
"!", "\\!",
|
|
|
|
"|", "\\|",
|
|
|
|
"<", "\\<",
|
|
|
|
">", "\\>",
|
|
|
|
)
|
|
|
|
|
|
|
|
// Escape Markdown characters
|
|
|
|
func escapeMarkdown(input string) string {
|
|
|
|
// Escaping ordered list
|
|
|
|
output := orderedListPattern.ReplaceAllString(input, "$1\\.")
|
|
|
|
|
|
|
|
for char, pattern := range markdownPatterns {
|
|
|
|
output = pattern.ReplaceAllString(output, fmt.Sprintf(`\%s$1\%s`, char, char))
|
|
|
|
}
|
|
|
|
|
|
|
|
return markdownToEscape.Replace(output)
|
|
|
|
}
|