twitch-bot/internal/actors/messagehook/discord.go
Knut Ahlers c78356f68f
Lint: Update linter config, improve code quality
Signed-off-by: Knut Ahlers <knut@ahlers.me>
2024-01-01 17:52:39 +01:00

304 lines
10 KiB
Go

package messagehook
import (
"encoding/json"
"net/http"
"github.com/pkg/errors"
"gopkg.in/irc.v4"
"github.com/Luzifer/twitch-bot/v3/plugins"
)
type (
discordActor struct {
plugins.ActorKit
}
discordPayload struct {
Content string `json:"content"`
Username string `json:"username,omitempty"`
AvatarURL string `json:"avatar_url,omitempty"`
Embeds []discordPayloadEmbed `json:"embeds,omitempty"`
}
discordPayloadEmbed struct {
Title string `json:"title,omitempty"`
Description string `json:"description,omitempty"`
URL string `json:"url,omitempty"`
Color int64 `json:"color,omitempty"`
Image *discordPayloadEmbedImage `json:"image,omitempty"`
Thumbnail *discordPayloadEmbedImage `json:"thumbnail,omitempty"`
Author *discordPayloadEmbedAuthor `json:"author,omitempty"`
Fields []discordPayloadEmbedField `json:"fields,omitempty"`
}
discordPayloadEmbedAuthor struct {
Name string `json:"name"`
URL string `json:"url,omitempty"`
IconURL string `json:"icon_url,omitempty"`
}
discordPayloadEmbedField struct {
Name string `json:"name"`
Value string `json:"value"`
Inline bool `json:"inline"`
}
discordPayloadEmbedImage struct {
URL string `json:"url"`
}
)
func (d discordActor) Execute(_ *irc.Client, m *irc.Message, r *plugins.Rule, eventData *plugins.FieldCollection, attrs *plugins.FieldCollection) (preventCooldown bool, err error) {
var payload discordPayload
if payload.Content, err = formatMessage(attrs.MustString("content", ptrStringEmpty), m, r, eventData); err != nil {
return false, errors.Wrap(err, "parsing content")
}
if payload.Username, err = formatMessage(attrs.MustString("username", ptrStringEmpty), m, r, eventData); err != nil {
return false, errors.Wrap(err, "parsing username")
}
if payload.AvatarURL, err = formatMessage(attrs.MustString("avatar_url", ptrStringEmpty), m, r, eventData); err != nil {
return false, errors.Wrap(err, "parsing avatar_url")
}
if err = d.addEmbed(&payload, m, r, eventData, attrs); err != nil {
return false, err
}
return sendPayload(attrs.MustString("hook_url", ptrStringEmpty), payload, http.StatusNoContent)
}
func (discordActor) IsAsync() bool { return false }
func (discordActor) Name() string { return "discordhook" }
func (d discordActor) Validate(tplValidator plugins.TemplateValidatorFunc, attrs *plugins.FieldCollection) (err error) {
if err = d.ValidateRequireNonEmpty(attrs, "hook_url"); err != nil {
return err //nolint:wrapcheck
}
if err = d.ValidateRequireValidTemplate(tplValidator, attrs, "content"); err != nil {
return err //nolint:wrapcheck
}
if err = d.ValidateRequireValidTemplateIfSet(tplValidator, attrs, "avatar_url", "username"); err != nil {
return err //nolint:wrapcheck
}
if !attrs.MustBool("add_embed", ptrBoolFalse) {
// We're not validating the rest if embeds are disabled but in
// this case the content is mandatory
return d.ValidateRequireNonEmpty(attrs, "content") //nolint:wrapcheck
}
//nolint:wrapcheck
return d.ValidateRequireValidTemplateIfSet(
tplValidator, attrs,
"embed_title",
"embed_description",
"embed_url",
"embed_image",
"embed_thumbnail",
"embed_author_name",
"embed_author_url",
"embed_author_icon_url",
"embed_fields",
)
}
//nolint:gocyclo // It's complex but just a bunch of converters
func (discordActor) addEmbed(payload *discordPayload, m *irc.Message, r *plugins.Rule, eventData *plugins.FieldCollection, attrs *plugins.FieldCollection) (err error) {
if !attrs.MustBool("add_embed", ptrBoolFalse) {
// No embed? No problem!
return nil
}
var (
embed discordPayloadEmbed
sv string
)
if embed.Title, err = formatMessage(attrs.MustString("embed_title", ptrStringEmpty), m, r, eventData); err != nil {
return errors.Wrap(err, "parsing embed_title")
}
if embed.Description, err = formatMessage(attrs.MustString("embed_description", ptrStringEmpty), m, r, eventData); err != nil {
return errors.Wrap(err, "parsing embed_description")
}
if embed.URL, err = formatMessage(attrs.MustString("embed_url", ptrStringEmpty), m, r, eventData); err != nil {
return errors.Wrap(err, "parsing embed_url")
}
if sv, err = formatMessage(attrs.MustString("embed_image", ptrStringEmpty), m, r, eventData); err != nil {
return errors.Wrap(err, "parsing embed_image")
} else if sv != "" {
embed.Image = &discordPayloadEmbedImage{URL: sv}
}
if sv, err = formatMessage(attrs.MustString("embed_thumbnail", ptrStringEmpty), m, r, eventData); err != nil {
return errors.Wrap(err, "parsing embed_thumbnail")
} else if sv != "" {
embed.Thumbnail = &discordPayloadEmbedImage{URL: sv}
}
if sv, err = formatMessage(attrs.MustString("embed_author_name", ptrStringEmpty), m, r, eventData); err != nil {
return errors.Wrap(err, "parsing embed_author_name")
} else if sv != "" {
embed.Author = &discordPayloadEmbedAuthor{Name: sv}
if embed.Author.URL, err = formatMessage(attrs.MustString("embed_author_url", ptrStringEmpty), m, r, eventData); err != nil {
return errors.Wrap(err, "parsing embed_author_url")
}
if embed.Author.IconURL, err = formatMessage(attrs.MustString("embed_author_icon_url", ptrStringEmpty), m, r, eventData); err != nil {
return errors.Wrap(err, "parsing embed_author_icon_url")
}
}
if sv, err = formatMessage(attrs.MustString("embed_fields", ptrStringEmpty), m, r, eventData); err != nil {
return errors.Wrap(err, "parsing embed_fields")
} else if sv != "" {
var flds []discordPayloadEmbedField
if err = json.Unmarshal([]byte(sv), &flds); err != nil {
return errors.Wrap(err, "unmarshalling embed_fields")
}
embed.Fields = flds
}
payload.Embeds = append(payload.Embeds, embed)
return nil
}
//nolint:funlen // This is just a bunch of field descriptions
func (discordActor) register(args plugins.RegistrationArguments) {
args.RegisterActor("discordhook", func() plugins.Actor { return &discordActor{} })
args.RegisterActorDocumentation(plugins.ActionDocumentation{
Description: "Sends a message to a Discord Web-hook",
Name: "Discord Message-Webhook",
Type: "discordhook",
Fields: []plugins.ActionDocumentationField{
{
Description: "URL to send the POST request to",
Key: "hook_url",
Name: "Hook URL",
Optional: false,
SupportTemplate: false,
Type: plugins.ActionDocumentationFieldTypeString,
},
{
Description: "Overwrites the username set in the webhook configuration",
Key: "username",
Name: "Username",
Optional: true,
SupportTemplate: true,
Type: plugins.ActionDocumentationFieldTypeString,
},
{
Description: "Overwrites the avatar set in the webhook configuration",
Key: "avatar_url",
Name: "Avatar URL",
Optional: true,
SupportTemplate: true,
Type: plugins.ActionDocumentationFieldTypeString,
},
{
Description: "Message content to send to the web-hook (this must be set if embed is disabled)",
Key: "content",
Name: "Message",
Optional: true,
SupportTemplate: true,
Type: plugins.ActionDocumentationFieldTypeString,
},
{
Default: "false",
Description: "Whether to include the embed in the post",
Key: "add_embed",
Name: "Add Embed",
Optional: true,
SupportTemplate: false,
Type: plugins.ActionDocumentationFieldTypeBool,
},
{
Description: "Title of the embed",
Key: "embed_title",
Name: "Embed Title",
Optional: true,
SupportTemplate: true,
Type: plugins.ActionDocumentationFieldTypeString,
},
{
Description: "Description of the embed",
Key: "embed_description",
Name: "Embed Description",
Optional: true,
SupportTemplate: true,
Type: plugins.ActionDocumentationFieldTypeString,
},
{
Description: "URL the title should link to",
Key: "embed_url",
Name: "Embed URL",
Optional: true,
SupportTemplate: true,
Type: plugins.ActionDocumentationFieldTypeString,
},
{
Description: "URL of the big image displayed in the embed",
Key: "embed_image",
Name: "Embed Image URL",
Optional: true,
SupportTemplate: true,
Type: plugins.ActionDocumentationFieldTypeString,
},
{
Description: "URL of the small image displayed in the embed",
Key: "embed_thumbnail",
Name: "Embed Thumbnail URL",
Optional: true,
SupportTemplate: true,
Type: plugins.ActionDocumentationFieldTypeString,
},
{
Description: "Name of the post author (if empty all other author-fields are ignored)",
Key: "embed_author_name",
Name: "Embed Author Name",
Optional: true,
SupportTemplate: true,
Type: plugins.ActionDocumentationFieldTypeString,
},
{
Description: "URL the author name should link to",
Key: "embed_author_url",
Name: "Embed Author URL",
Optional: true,
SupportTemplate: true,
Type: plugins.ActionDocumentationFieldTypeString,
},
{
Description: "URL of the author avatar",
Key: "embed_author_icon_url",
Name: "Embed Author Avatar URL",
Optional: true,
SupportTemplate: true,
Type: plugins.ActionDocumentationFieldTypeString,
},
{
Description: "Fields to display in the embed (must yield valid JSON: `[{\"name\": \"\", \"value\": \"\", \"inline\": false}]`)",
Key: "embed_fields",
Name: "Embed Fields",
Optional: true,
SupportTemplate: true,
Type: plugins.ActionDocumentationFieldTypeString,
},
},
})
}