mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2025-09-15 18:56:59 +00:00
fix: improve discord webhook api conformance
This commit corrects some cases in the discord webhook payload that do
not align with the discord documentation
(cherry picked from commit 6ea6f224b8)
This commit is contained in:
parent
2c0c6f408e
commit
e2ffe12e50
5 changed files with 76 additions and 13 deletions
|
|
@ -14,8 +14,6 @@ import (
|
|||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"gitea.com/go-chi/binding"
|
||||
|
||||
webhook_model "code.gitea.io/gitea/models/webhook"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
|
|
@ -27,6 +25,8 @@ import (
|
|||
gitea_context "code.gitea.io/gitea/services/context"
|
||||
"code.gitea.io/gitea/services/forms"
|
||||
"code.gitea.io/gitea/services/webhook/shared"
|
||||
|
||||
"gitea.com/go-chi/binding"
|
||||
)
|
||||
|
||||
type discordHandler struct{}
|
||||
|
|
@ -37,8 +37,8 @@ func (discordHandler) Icon(size int) template.HTML { return shared.ImgIcon("di
|
|||
type discordForm struct {
|
||||
forms.WebhookCoreForm
|
||||
PayloadURL string `binding:"Required;ValidUrl"`
|
||||
Username string
|
||||
IconURL string
|
||||
Username string `binding:"Required;MaxSize(80)"`
|
||||
IconURL string `binding:"ValidUrl"`
|
||||
}
|
||||
|
||||
var _ binding.Validator = &discordForm{}
|
||||
|
|
@ -75,7 +75,7 @@ func (discordHandler) UnmarshalForm(bind func(any)) forms.WebhookForm {
|
|||
type (
|
||||
// DiscordEmbedFooter for Embed Footer Structure.
|
||||
DiscordEmbedFooter struct {
|
||||
Text string `json:"text"`
|
||||
Text string `json:"text,omitempty"`
|
||||
}
|
||||
|
||||
// DiscordEmbedAuthor for Embed Author Structure
|
||||
|
|
@ -99,16 +99,16 @@ type (
|
|||
Color int `json:"color"`
|
||||
Footer DiscordEmbedFooter `json:"footer"`
|
||||
Author DiscordEmbedAuthor `json:"author"`
|
||||
Fields []DiscordEmbedField `json:"fields"`
|
||||
Fields []DiscordEmbedField `json:"fields,omitempty"`
|
||||
}
|
||||
|
||||
// DiscordPayload represents
|
||||
DiscordPayload struct {
|
||||
Wait bool `json:"wait"`
|
||||
Content string `json:"content"`
|
||||
Wait bool `json:"-"`
|
||||
Content string `json:"-"`
|
||||
Username string `json:"username"`
|
||||
AvatarURL string `json:"avatar_url,omitempty"`
|
||||
TTS bool `json:"tts"`
|
||||
TTS bool `json:"-"`
|
||||
Embeds []DiscordEmbed `json:"embeds"`
|
||||
}
|
||||
|
||||
|
|
@ -341,6 +341,12 @@ func parseHookPullRequestEventType(event webhook_module.HookEventType) (string,
|
|||
}
|
||||
|
||||
func (d discordConvertor) createPayload(s *api.User, title, text, url string, color int) DiscordPayload {
|
||||
if len([]rune(title)) > 256 {
|
||||
title = fmt.Sprintf("%.253s...", title)
|
||||
}
|
||||
if len([]rune(text)) > 4096 {
|
||||
text = fmt.Sprintf("%.4093s...", text)
|
||||
}
|
||||
return DiscordPayload{
|
||||
Username: d.Username,
|
||||
AvatarURL: d.AvatarURL,
|
||||
|
|
|
|||
|
|
@ -120,6 +120,49 @@ func TestDiscordPayload(t *testing.T) {
|
|||
assert.Equal(t, p.Sender.UserName, pl.Embeds[0].Author.Name)
|
||||
assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.Embeds[0].Author.URL)
|
||||
assert.Equal(t, p.Sender.AvatarURL, pl.Embeds[0].Author.IconURL)
|
||||
|
||||
j, err := json.Marshal(pl)
|
||||
require.NoError(t, err)
|
||||
|
||||
unsetFields := struct {
|
||||
Content *string `json:"content"`
|
||||
TTS *bool `json:"tts"`
|
||||
Wait *bool `json:"wait"`
|
||||
Fields []any `json:"fields"`
|
||||
Footer struct {
|
||||
Text *string `json:"text"`
|
||||
} `json:"footer"`
|
||||
}{}
|
||||
|
||||
err = json.Unmarshal(j, &unsetFields)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, unsetFields.Content)
|
||||
assert.Nil(t, unsetFields.TTS)
|
||||
assert.Nil(t, unsetFields.Wait)
|
||||
assert.Nil(t, unsetFields.Fields)
|
||||
assert.Nil(t, unsetFields.Footer.Text)
|
||||
})
|
||||
|
||||
t.Run("Issue with long title", func(t *testing.T) {
|
||||
p := issueTestPayloadWithLongTitle()
|
||||
|
||||
p.Action = api.HookIssueOpened
|
||||
pl, err := dc.Issue(p)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Len(t, pl.Embeds, 1)
|
||||
assert.Len(t, pl.Embeds[0].Title, 256)
|
||||
})
|
||||
|
||||
t.Run("Issue with long body", func(t *testing.T) {
|
||||
p := issueTestPayloadWithLongBody()
|
||||
|
||||
p.Action = api.HookIssueOpened
|
||||
pl, err := dc.Issue(p)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Len(t, pl.Embeds, 1)
|
||||
assert.Len(t, pl.Embeds[0].Description, 4096)
|
||||
})
|
||||
|
||||
t.Run("IssueComment", func(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
package webhook
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
|
|
@ -113,6 +114,18 @@ func pushTestPayloadWithCommitMessage(message string) *api.PushPayload {
|
|||
}
|
||||
|
||||
func issueTestPayload() *api.IssuePayload {
|
||||
return issuePayloadWithTitleAndBody("crash", "issue body")
|
||||
}
|
||||
|
||||
func issueTestPayloadWithLongBody() *api.IssuePayload {
|
||||
return issuePayloadWithTitleAndBody("crash", strings.Repeat("issue body", 4097))
|
||||
}
|
||||
|
||||
func issueTestPayloadWithLongTitle() *api.IssuePayload {
|
||||
return issuePayloadWithTitleAndBody(strings.Repeat("a", 257), "issue body")
|
||||
}
|
||||
|
||||
func issuePayloadWithTitleAndBody(title, body string) *api.IssuePayload {
|
||||
return &api.IssuePayload{
|
||||
Index: 2,
|
||||
Sender: &api.User{
|
||||
|
|
@ -129,8 +142,8 @@ func issueTestPayload() *api.IssuePayload {
|
|||
Index: 2,
|
||||
URL: "http://localhost:3000/api/v1/repos/test/repo/issues/2",
|
||||
HTMLURL: "http://localhost:3000/test/repo/issues/2",
|
||||
Title: "crash",
|
||||
Body: "issue body",
|
||||
Title: title,
|
||||
Body: body,
|
||||
Poster: &api.User{
|
||||
UserName: "user1",
|
||||
AvatarURL: "http://localhost:3000/user1/avatar",
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@
|
|||
<label for="payload_url">{{ctx.Locale.Tr "repo.settings.payload_url"}}</label>
|
||||
<input id="payload_url" name="payload_url" type="url" value="{{.Webhook.URL}}" autofocus required>
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="required field {{if .Err_PayloadURL}}error{{end}}">
|
||||
<label for="username">{{ctx.Locale.Tr "repo.settings.discord_username"}}</label>
|
||||
<input id="username" name="username" value="{{.HookMetadata.Username}}" placeholder="Forgejo">
|
||||
<input id="username" name="username" value="{{.HookMetadata.Username}}" autofocus required placeholder="Forgejo">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="icon_url">{{ctx.Locale.Tr "repo.settings.discord_icon_url"}}</label>
|
||||
|
|
|
|||
|
|
@ -172,6 +172,7 @@ func TestWebhookForms(t *testing.T) {
|
|||
}))
|
||||
|
||||
t.Run("discord/required", testWebhookForms("discord", session, map[string]string{
|
||||
"username": "john",
|
||||
"payload_url": "https://discord.example.com",
|
||||
}))
|
||||
t.Run("discord/optional", testWebhookForms("discord", session, map[string]string{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue