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