github.com/diamondburned/arikawa@v1.3.14/api/message.go (about)

     1  package api
     2  
     3  import (
     4  	"github.com/pkg/errors"
     5  
     6  	"github.com/diamondburned/arikawa/discord"
     7  	"github.com/diamondburned/arikawa/utils/httputil"
     8  	"github.com/diamondburned/arikawa/utils/json/option"
     9  )
    10  
    11  // the limit of max messages per request, as imposed by Discord
    12  const maxMessageFetchLimit = 100
    13  
    14  // Messages returns a slice filled with the most recent messages sent in the
    15  // channel with the passed ID. The method automatically paginates until it
    16  // reaches the passed limit, or, if the limit is set to 0, has fetched all
    17  // messages in the channel.
    18  //
    19  // As the underlying endpoint is capped at a maximum of 100 messages per
    20  // request, at maximum a total of limit/100 rounded up requests will be made,
    21  // although they may be less, if no more messages are available.
    22  //
    23  // When fetching the messages, those with the highest ID, will be fetched
    24  // first.
    25  // The returned slice will be sorted from latest to oldest.
    26  func (c *Client) Messages(channelID discord.ChannelID, limit uint) ([]discord.Message, error) {
    27  	// Since before is 0 it will be omitted by the http lib, which in turn
    28  	// will lead discord to send us the most recent messages without having to
    29  	// specify a Snowflake.
    30  	return c.MessagesBefore(channelID, 0, limit)
    31  }
    32  
    33  // MessagesAround returns messages around the ID, with a limit of 100.
    34  func (c *Client) MessagesAround(
    35  	channelID discord.ChannelID, around discord.MessageID, limit uint) ([]discord.Message, error) {
    36  
    37  	return c.messagesRange(channelID, 0, 0, around, limit)
    38  }
    39  
    40  // MessagesBefore returns a slice filled with the messages sent in the channel
    41  // with the passed id. The method automatically paginates until it reaches the
    42  // passed limit, or, if the limit is set to 0, has fetched all messages in the
    43  // channel with an id smaller than before.
    44  //
    45  // As the underlying endpoint has a maximum of 100 messages per request, at
    46  // maximum a total of limit/100 rounded up requests will be made, although they
    47  // may be less, if no more messages are available.
    48  //
    49  // The returned slice will be sorted from latest to oldest.
    50  func (c *Client) MessagesBefore(
    51  	channelID discord.ChannelID, before discord.MessageID, limit uint) ([]discord.Message, error) {
    52  
    53  	msgs := make([]discord.Message, 0, limit)
    54  
    55  	fetch := uint(maxMessageFetchLimit)
    56  
    57  	// Check if we are truly fetching unlimited messages to avoid confusion
    58  	// later on, if the limit reaches 0.
    59  	unlimited := limit == 0
    60  
    61  	for limit > 0 || unlimited {
    62  		if limit > 0 {
    63  			// Only fetch as much as we need. Since limit gradually decreases,
    64  			// we only need to fetch min(fetch, limit).
    65  			if fetch > limit {
    66  				fetch = limit
    67  			}
    68  			limit -= maxMessageFetchLimit
    69  		}
    70  
    71  		m, err := c.messagesRange(channelID, before, 0, 0, fetch)
    72  		if err != nil {
    73  			return msgs, err
    74  		}
    75  		// Append the older messages into the list of newer messages.
    76  		msgs = append(msgs, m...)
    77  
    78  		if len(m) < maxMessageFetchLimit {
    79  			break
    80  		}
    81  
    82  		before = m[len(m)-1].ID
    83  	}
    84  
    85  	if len(msgs) == 0 {
    86  		return nil, nil
    87  	}
    88  
    89  	return msgs, nil
    90  }
    91  
    92  // MessagesAfter returns a slice filled with the messages sent in the channel
    93  // with the passed ID. The method automatically paginates until it reaches the
    94  // passed limit, or, if the limit is set to 0, has fetched all messages in the
    95  // channel with an id higher than after.
    96  //
    97  // As the underlying endpoint has a maximum of 100 messages per request, at
    98  // maximum a total of limit/100 rounded up requests will be made, although they
    99  // may be less, if no more messages are available.
   100  //
   101  // The returned slice will be sorted from latest to oldest.
   102  func (c *Client) MessagesAfter(
   103  	channelID discord.ChannelID, after discord.MessageID, limit uint) ([]discord.Message, error) {
   104  
   105  	// 0 is uint's zero value and will lead to the after param getting omitted,
   106  	// which in turn will lead to the most recent messages being returned.
   107  	// Setting this to 1 will prevent that.
   108  	if after == 0 {
   109  		after = 1
   110  	}
   111  
   112  	var msgs []discord.Message
   113  
   114  	fetch := uint(maxMessageFetchLimit)
   115  
   116  	// Check if we are truly fetching unlimited messages to avoid confusion
   117  	// later on, if the limit reaches 0.
   118  	unlimited := limit == 0
   119  
   120  	for limit > 0 || unlimited {
   121  		if limit > 0 {
   122  			// Only fetch as much as we need. Since limit gradually decreases,
   123  			// we only need to fetch min(fetch, limit).
   124  			if fetch > limit {
   125  				fetch = limit
   126  			}
   127  			limit -= maxMessageFetchLimit
   128  		}
   129  
   130  		m, err := c.messagesRange(channelID, 0, after, 0, fetch)
   131  		if err != nil {
   132  			return msgs, err
   133  		}
   134  		// Prepend the older messages into the newly-fetched messages list.
   135  		msgs = append(m, msgs...)
   136  
   137  		if len(m) < maxMessageFetchLimit {
   138  			break
   139  		}
   140  
   141  		after = m[0].ID
   142  	}
   143  
   144  	if len(msgs) == 0 {
   145  		return nil, nil
   146  	}
   147  
   148  	return msgs, nil
   149  }
   150  
   151  func (c *Client) messagesRange(
   152  	channelID discord.ChannelID, before, after, around discord.MessageID, limit uint) ([]discord.Message, error) {
   153  
   154  	switch {
   155  	case limit == 0:
   156  		limit = 50
   157  	case limit > 100:
   158  		limit = 100
   159  	}
   160  
   161  	var param struct {
   162  		Before discord.MessageID `schema:"before,omitempty"`
   163  		After  discord.MessageID `schema:"after,omitempty"`
   164  		Around discord.MessageID `schema:"around,omitempty"`
   165  
   166  		Limit uint `schema:"limit"`
   167  	}
   168  
   169  	param.Before = before
   170  	param.After = after
   171  	param.Around = around
   172  	param.Limit = limit
   173  
   174  	var msgs []discord.Message
   175  	return msgs, c.RequestJSON(
   176  		&msgs, "GET",
   177  		EndpointChannels+channelID.String()+"/messages",
   178  		httputil.WithSchema(c, param),
   179  	)
   180  }
   181  
   182  // Message returns a specific message in the channel.
   183  //
   184  // If operating on a guild channel, this endpoint requires the
   185  // READ_MESSAGE_HISTORY permission to be present on the current user.
   186  func (c *Client) Message(channelID discord.ChannelID, messageID discord.MessageID) (*discord.Message, error) {
   187  	var msg *discord.Message
   188  	return msg, c.RequestJSON(&msg, "GET",
   189  		EndpointChannels+channelID.String()+"/messages/"+messageID.String())
   190  }
   191  
   192  // SendText posts a only-text message to a guild text or DM channel.
   193  //
   194  // If operating on a guild channel, this endpoint requires the SEND_MESSAGES
   195  // permission to be present on the current user.
   196  //
   197  // Fires a Message Create Gateway event.
   198  func (c *Client) SendText(channelID discord.ChannelID, content string) (*discord.Message, error) {
   199  	return c.SendMessageComplex(channelID, SendMessageData{
   200  		Content: content,
   201  	})
   202  }
   203  
   204  // SendEmbed posts an Embed to a guild text or DM channel.
   205  //
   206  // If operating on a guild channel, this endpoint requires the SEND_MESSAGES
   207  // permission to be present on the current user.
   208  //
   209  // Fires a Message Create Gateway event.
   210  func (c *Client) SendEmbed(
   211  	channelID discord.ChannelID, e discord.Embed) (*discord.Message, error) {
   212  
   213  	return c.SendMessageComplex(channelID, SendMessageData{
   214  		Embed: &e,
   215  	})
   216  }
   217  
   218  // SendMessage posts a message to a guild text or DM channel.
   219  //
   220  // If operating on a guild channel, this endpoint requires the SEND_MESSAGES
   221  // permission to be present on the current user.
   222  //
   223  // Fires a Message Create Gateway event.
   224  func (c *Client) SendMessage(
   225  	channelID discord.ChannelID, content string, embed *discord.Embed) (*discord.Message, error) {
   226  
   227  	return c.SendMessageComplex(channelID, SendMessageData{
   228  		Content: content,
   229  		Embed:   embed,
   230  	})
   231  }
   232  
   233  // https://discord.com/developers/docs/resources/channel#edit-message-json-params
   234  type EditMessageData struct {
   235  	// Content is the new message contents (up to 2000 characters).
   236  	Content option.NullableString `json:"content,omitempty"`
   237  	// Embed contains embedded rich content.
   238  	Embed *discord.Embed `json:"embed,omitempty"`
   239  	// AllowedMentions are the allowed mentions for a message.
   240  	AllowedMentions *AllowedMentions `json:"allowed_mentions,omitempty"`
   241  	// Flags edits the flags of a message (only SUPPRESS_EMBEDS can currently
   242  	// be set/unset)
   243  	//
   244  	// This field is nullable.
   245  	Flags *discord.MessageFlags `json:"flags,omitempty"`
   246  }
   247  
   248  // EditText edits the contents of a previously sent message. For more
   249  // documentation, refer to EditMessageComplex.
   250  func (c *Client) EditText(
   251  	channelID discord.ChannelID, messageID discord.MessageID, content string) (*discord.Message, error) {
   252  
   253  	return c.EditMessageComplex(channelID, messageID, EditMessageData{
   254  		Content: option.NewNullableString(content),
   255  	})
   256  }
   257  
   258  // EditEmbed edits the embed of a previously sent message. For more
   259  // documentation, refer to EditMessageComplex.
   260  func (c *Client) EditEmbed(
   261  	channelID discord.ChannelID, messageID discord.MessageID, embed discord.Embed) (*discord.Message, error) {
   262  
   263  	return c.EditMessageComplex(channelID, messageID, EditMessageData{
   264  		Embed: &embed,
   265  	})
   266  }
   267  
   268  // EditMessage edits a previously sent message. For more documentation, refer to
   269  // EditMessageComplex.
   270  func (c *Client) EditMessage(
   271  	channelID discord.ChannelID, messageID discord.MessageID, content string,
   272  	embed *discord.Embed, suppressEmbeds bool) (*discord.Message, error) {
   273  
   274  	var data = EditMessageData{
   275  		Content: option.NewNullableString(content),
   276  		Embed:   embed,
   277  	}
   278  	if suppressEmbeds {
   279  		data.Flags = &discord.SuppressEmbeds
   280  	}
   281  
   282  	return c.EditMessageComplex(channelID, messageID, data)
   283  }
   284  
   285  // EditMessageComplex edits a previously sent message. The fields Content,
   286  // Embed, AllowedMentions and Flags can be edited by the original message
   287  // author. Other users can only edit flags and only if they have the
   288  // MANAGE_MESSAGES permission in the corresponding channel. When specifying
   289  // flags, ensure to include all previously set flags/bits in addition to ones
   290  // that you are modifying. Only flags documented in EditMessageData may be
   291  // modified by users (unsupported flag changes are currently ignored without
   292  // error).
   293  //
   294  // Fires a Message Update Gateway event.
   295  func (c *Client) EditMessageComplex(
   296  	channelID discord.ChannelID, messageID discord.MessageID, data EditMessageData) (*discord.Message, error) {
   297  
   298  	if data.AllowedMentions != nil {
   299  		if err := data.AllowedMentions.Verify(); err != nil {
   300  			return nil, errors.Wrap(err, "allowedMentions error")
   301  		}
   302  	}
   303  
   304  	if data.Embed != nil {
   305  		if err := data.Embed.Validate(); err != nil {
   306  			return nil, errors.Wrap(err, "embed error")
   307  		}
   308  	}
   309  
   310  	var msg *discord.Message
   311  	return msg, c.RequestJSON(
   312  		&msg, "PATCH",
   313  		EndpointChannels+channelID.String()+"/messages/"+messageID.String(),
   314  		httputil.WithJSONBody(data),
   315  	)
   316  }
   317  
   318  // DeleteMessage delete a message. If operating on a guild channel and trying
   319  // to delete a message that was not sent by the current user, this endpoint
   320  // requires the MANAGE_MESSAGES permission.
   321  func (c *Client) DeleteMessage(channelID discord.ChannelID, messageID discord.MessageID) error {
   322  	return c.FastRequest("DELETE", EndpointChannels+channelID.String()+
   323  		"/messages/"+messageID.String())
   324  }
   325  
   326  // DeleteMessages deletes multiple messages in a single request. This endpoint
   327  // can only be used on guild channels and requires the MANAGE_MESSAGES
   328  // permission. This endpoint only works for bots.
   329  //
   330  // This endpoint will not delete messages older than 2 weeks, and will fail if
   331  // any message provided is older than that or if any duplicate message IDs are
   332  // provided.
   333  //
   334  // Fires a Message Delete Bulk Gateway event.
   335  func (c *Client) DeleteMessages(channelID discord.ChannelID, messageIDs []discord.MessageID) error {
   336  	var param struct {
   337  		Messages []discord.MessageID `json:"messages"`
   338  	}
   339  
   340  	param.Messages = messageIDs
   341  
   342  	return c.FastRequest(
   343  		"POST",
   344  		EndpointChannels+channelID.String()+"/messages/bulk-delete",
   345  		httputil.WithJSONBody(param),
   346  	)
   347  }