Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v3: Update more endpoints to Discord API v9 #230

Merged
merged 1 commit into from
Jun 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion _example/advanced_bot/bot.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func (bot *Bot) GuildInfo(m *gateway.MessageCreateEvent) (string, error) {
// Repeat tells the bot to wait for the user's response, then repeat what they
// said.
func (bot *Bot) Repeat(m *gateway.MessageCreateEvent) (string, error) {
_, err := bot.Ctx.SendMessage(m.ChannelID, "What do you want me to say?", nil)
_, err := bot.Ctx.SendMessage(m.ChannelID, "What do you want me to say?")
if err != nil {
return "", err
}
Expand Down
2 changes: 1 addition & 1 deletion api/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ func TestReactions(t *testing.T) {
msg := fmt.Sprintf("This is a message sent at %v.", time.Now())

// Send a new message.
m, err := client.SendMessage(cfg.ChannelID, msg, nil)
m, err := client.SendMessage(cfg.ChannelID, msg)
if err != nil {
t.Fatal("Failed to send message:", err)
}
Expand Down
87 changes: 55 additions & 32 deletions api/message.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
package api

import (
"mime/multipart"
"strconv"

"github.com/pkg/errors"

"github.com/diamondburned/arikawa/v3/discord"
"github.com/diamondburned/arikawa/v3/internal/intmath"
"github.com/diamondburned/arikawa/v3/utils/httputil"
"github.com/diamondburned/arikawa/v3/utils/json/option"
"github.com/diamondburned/arikawa/v3/utils/sendpart"
)

const (
Expand Down Expand Up @@ -220,17 +224,17 @@ func (c *Client) SendTextReply(
})
}

// SendEmbed posts an Embed to a guild text or DM channel.
// SendEmbeds sends embeds to a guild text or DM channel.
//
// If operating on a guild channel, this endpoint requires the SEND_MESSAGES
// permission to be present on the current user.
//
// Fires a Message Create Gateway event.
func (c *Client) SendEmbed(
channelID discord.ChannelID, e discord.Embed) (*discord.Message, error) {
func (c *Client) SendEmbeds(
channelID discord.ChannelID, e ...discord.Embed) (*discord.Message, error) {

return c.SendMessageComplex(channelID, SendMessageData{
Embed: &e,
Embeds: e,
})
}

Expand All @@ -246,7 +250,7 @@ func (c *Client) SendEmbedReply(
referenceID discord.MessageID) (*discord.Message, error) {

return c.SendMessageComplex(channelID, SendMessageData{
Embed: &e,
Embeds: []discord.Embed{e},
Reference: &discord.MessageReference{MessageID: referenceID},
})
}
Expand All @@ -258,12 +262,12 @@ func (c *Client) SendEmbedReply(
//
// Fires a Message Create Gateway event.
func (c *Client) SendMessage(
channelID discord.ChannelID, content string, embed *discord.Embed) (*discord.Message, error) {

return c.SendMessageComplex(channelID, SendMessageData{
channelID discord.ChannelID, content string, embeds ...discord.Embed) (*discord.Message, error) {
data := SendMessageData{
Content: content,
Embed: embed,
})
Embeds: embeds,
}
return c.SendMessageComplex(channelID, data)
}

// SendMessageReply posts a reply to a message ID in a guild text or DM channel.
Expand All @@ -277,29 +281,44 @@ func (c *Client) SendMessageReply(
content string,
embed *discord.Embed,
referenceID discord.MessageID) (*discord.Message, error) {

return c.SendMessageComplex(channelID, SendMessageData{
data := SendMessageData{
Content: content,
Embed: embed,
Reference: &discord.MessageReference{MessageID: referenceID},
})
}
if embed != nil {
data.Embeds = []discord.Embed{*embed}
}
return c.SendMessageComplex(channelID, data)
}

// https://discord.com/developers/docs/resources/channel#edit-message-json-params
// https://discord.com/developers/docs/resources/channel#edit-message
type EditMessageData struct {
// Content is the new message contents (up to 2000 characters).
Content option.NullableString `json:"content,omitempty"`
// Embed contains embedded rich content.
Embed *discord.Embed `json:"embed,omitempty"`
// Embeds contains embedded rich content.
Embeds *[]discord.Embed `json:"embeds,omitempty"`
// Components contains the new components to attach.
Components *[]discord.Component `json:"components,omitempty"`
// AllowedMentions are the allowed mentions for a message.
AllowedMentions *AllowedMentions `json:"allowed_mentions,omitempty"`
// Attachments are the attached files to keep
Attachments *[]discord.Attachment `json:"attachments,omitempty"`
// Flags edits the flags of a message (only SUPPRESS_EMBEDS can currently
// be set/unset)
//
// This field is nullable.
Flags *discord.MessageFlags `json:"flags,omitempty"`

Files []sendpart.File `json:"-"`
}

// NeedsMultipart returns true if the SendMessageData has files.
func (data EditMessageData) NeedsMultipart() bool {
return len(data.Files) > 0
}

func (data EditMessageData) WriteMultipart(body *multipart.Writer) error {
return sendpart.Write(body, data, data.Files)
}

// EditText edits the contents of a previously sent message. For more
Expand All @@ -312,13 +331,13 @@ func (c *Client) EditText(
})
}

// EditEmbed edits the embed of a previously sent message. For more
// EditEmbeds edits the embed of a previously sent message. For more
// documentation, refer to EditMessageComplex.
func (c *Client) EditEmbed(
channelID discord.ChannelID, messageID discord.MessageID, embed discord.Embed) (*discord.Message, error) {
func (c *Client) EditEmbeds(
channelID discord.ChannelID, messageID discord.MessageID, embeds ...discord.Embed) (*discord.Message, error) {

return c.EditMessageComplex(channelID, messageID, EditMessageData{
Embed: &embed,
Embeds: &embeds,
})
}

Expand All @@ -330,7 +349,9 @@ func (c *Client) EditMessage(

var data = EditMessageData{
Content: option.NewNullableString(content),
Embed: embed,
}
if embed != nil {
data.Embeds = &[]discord.Embed{*embed}
}
if suppressEmbeds {
v := discord.SuppressEmbeds
Expand All @@ -352,25 +373,27 @@ func (c *Client) EditMessage(
// Fires a Message Update Gateway event.
func (c *Client) EditMessageComplex(
channelID discord.ChannelID, messageID discord.MessageID, data EditMessageData) (*discord.Message, error) {

if data.AllowedMentions != nil {
if err := data.AllowedMentions.Verify(); err != nil {
return nil, errors.Wrap(err, "allowedMentions error")
}
}

if data.Embed != nil {
if err := data.Embed.Validate(); err != nil {
return nil, errors.Wrap(err, "embed error")
if data.Embeds != nil {
sum := 0
for i, embed := range *data.Embeds {
if err := embed.Validate(); err != nil {
return nil, errors.Wrap(err, "embed error at "+strconv.Itoa(i))
}
sum += embed.Length()
if sum > 6000 {
return nil, &discord.OverboundError{sum, 6000, "sum of all text in embeds"}
}
}
}

var msg *discord.Message
return msg, c.RequestJSON(
&msg, "PATCH",
EndpointChannels+channelID.String()+"/messages/"+messageID.String(),
httputil.WithJSONBody(data),
)
return msg, sendpart.PATCH(c.Client, data, &msg,
EndpointChannels+channelID.String()+"/messages/"+messageID.String())
}

// CrosspostMessage crossposts a message in a news channel to following channels.
Expand Down
19 changes: 12 additions & 7 deletions api/send.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package api

import (
"mime/multipart"
"strconv"

"github.com/pkg/errors"

Expand Down Expand Up @@ -87,7 +88,7 @@ func (am AllowedMentions) Verify() error {
}

// ErrEmptyMessage is returned if either a SendMessageData or an
// ExecuteWebhookData has both an empty Content and no Embed(s).
// ExecuteWebhookData is missing content, embeds, and files.
var ErrEmptyMessage = errors.New("message is empty")

// SendMessageData is the full structure to send a new message to Discord with.
Expand All @@ -100,7 +101,7 @@ type SendMessageData struct {
// TTS is true if this is a TTS message.
TTS bool `json:"tts,omitempty"`
// Embed is embedded rich content.
Embed *discord.Embed `json:"embed,omitempty"`
Embeds []discord.Embed `json:"embeds,omitempty"`

// Files is the list of file attachments to be uploaded. To reference a file
// in an embed, use (sendpart.File).AttachmentURI().
Expand Down Expand Up @@ -150,8 +151,7 @@ func (data SendMessageData) WriteMultipart(body *multipart.Writer) error {
// Content-Disposition subpart header MUST contain a filename parameter.
func (c *Client) SendMessageComplex(
channelID discord.ChannelID, data SendMessageData) (*discord.Message, error) {

if data.Content == "" && data.Embed == nil && len(data.Files) == 0 {
if data.Content == "" && len(data.Embeds) == 0 && len(data.Files) == 0 {
return nil, ErrEmptyMessage
}

Expand All @@ -161,9 +161,14 @@ func (c *Client) SendMessageComplex(
}
}

if data.Embed != nil {
if err := data.Embed.Validate(); err != nil {
return nil, errors.Wrap(err, "embed error")
sum := 0
for i, embed := range data.Embeds {
if err := embed.Validate(); err != nil {
return nil, errors.Wrap(err, "embed error at "+strconv.Itoa(i))
}
sum += embed.Length()
if sum > 6000 {
return nil, &discord.OverboundError{sum, 6000, "sum of all text in embeds"}
}
}

Expand Down
9 changes: 3 additions & 6 deletions api/send_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,7 @@ func TestSendMessage(t *testing.T) {
}

t.Run("empty", func(t *testing.T) {
var empty = SendMessageData{
Content: "",
Embed: nil,
}
var empty SendMessageData

if err := send(empty); err != ErrEmptyMessage {
t.Fatal("Unexpected error:", err)
Expand Down Expand Up @@ -136,10 +133,10 @@ func TestSendMessage(t *testing.T) {

t.Run("invalid embed", func(t *testing.T) {
var data = SendMessageData{
Embed: &discord.Embed{
Embeds: []discord.Embed{{
// max 256
Title: spaces(257),
},
}},
}

err := send(data)
Expand Down
57 changes: 49 additions & 8 deletions api/webhook/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,10 @@ type ExecuteData struct {
// Required: one of content, file, embeds
Embeds []discord.Embed `json:"embeds,omitempty"`

// Components is the list of components (such as buttons) to be attached to
// the message.
Components []discord.Component `json:"components,omitempty"`

// Files represents a list of files to upload. This will not be JSON-encoded
// and will only be available through WriteMultipart.
Files []sendpart.File `json:"-"`
Expand Down Expand Up @@ -185,10 +189,15 @@ func (c *Client) execute(data ExecuteData, wait bool) (*discord.Message, error)
}
}

sum := 0
for i, embed := range data.Embeds {
if err := embed.Validate(); err != nil {
return nil, errors.Wrap(err, "embed error at "+strconv.Itoa(i))
}
sum += embed.Length()
if sum > 6000 {
return nil, &discord.OverboundError{sum, 6000, "sum of all text in embeds"}
}
}

var param url.Values
Expand All @@ -208,21 +217,53 @@ func (c *Client) execute(data ExecuteData, wait bool) (*discord.Message, error)
}

// https://discord.com/developers/docs/resources/webhook#edit-webhook-message-jsonform-params

type EditMessageData struct {
// Content are the message contents. They may be up to 2000 characters
// characters long.
// Content is the new message contents (up to 2000 characters).
Content option.NullableString `json:"content,omitempty"`
// Embeds is an array of up to 10 discord.Embeds.
// Embeds contains embedded rich content.
Embeds *[]discord.Embed `json:"embeds,omitempty"`
// AllowedMentions are the AllowedMentions for the message.
// Components contains the new components to attach.
Components *[]discord.Component `json:"components,omitempty"`
// AllowedMentions are the allowed mentions for a message.
AllowedMentions *api.AllowedMentions `json:"allowed_mentions,omitempty"`
// Attachments are the attached files to keep
Attachments *[]discord.Attachment `json:"attachments,omitempty"`

Files []sendpart.File `json:"-"`
}

// EditMessage edits a previously-sent webhook message from the same webhook.
func (c *Client) EditMessage(messageID discord.MessageID, data EditMessageData) error {
return c.FastRequest("PATCH",
api.EndpointWebhooks+c.ID.String()+"/"+c.Token+"/messages/"+messageID.String(),
httputil.WithJSONBody(data))
func (c *Client) EditMessage(messageID discord.MessageID, data EditMessageData) (*discord.Message, error) {
if data.AllowedMentions != nil {
if err := data.AllowedMentions.Verify(); err != nil {
return nil, errors.Wrap(err, "allowedMentions error")
}
}
if data.Embeds != nil {
sum := 0
for _, e := range *data.Embeds {
if err := e.Validate(); err != nil {
return nil, errors.Wrap(err, "embed error")
}
sum += e.Length()
if sum > 6000 {
return nil, &discord.OverboundError{sum, 6000, "sum of text in embeds"}
}
}
}
var msg *discord.Message
return msg, sendpart.PATCH(c.Client, data, &msg,
api.EndpointWebhooks+c.ID.String()+"/"+c.Token+"/messages/"+messageID.String())
}

// NeedsMultipart returns true if the SendMessageData has files.
func (data EditMessageData) NeedsMultipart() bool {
return len(data.Files) > 0
}

func (data EditMessageData) WriteMultipart(body *multipart.Writer) error {
return sendpart.Write(body, data, data.Files)
}

// DeleteMessage deletes a message that was previously created by the same
Expand Down
2 changes: 1 addition & 1 deletion bot/ctx_call.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ func (ctx *Context) callMessageCreate(
case string:
data.Content = v
case *discord.Embed:
data.Embed = v
data.Embeds = []discord.Embed{*v}
case *api.SendMessageData:
data = *v
default:
Expand Down
2 changes: 0 additions & 2 deletions discord/message.go
Original file line number Diff line number Diff line change
Expand Up @@ -281,8 +281,6 @@ func (m MessageApplication) CreatedAt() time.Time {
return m.ID.Time()
}

//

// MessageReference is used in four situations:
//
// Crosspost messages
Expand Down
Loading