func (r *Resource) AddReaction(ctx context.Context, messageID, emoji string) error {
	e := endpoint.CreateReaction(r.channelID, messageID, url.PathEscape(emoji))
	resp, err := r.client.Do(ctx, e, nil)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusNoContent {
		return discord.NewAPIError(resp)
	}
	return nil
}
AddReaction adds a reaction to a message in the channel. This endpoint requires
the 'READ_MESSAGE_HISTORY' permission to be present on the current user. Additionally,
if nobody else has reacted to the message using this emoji, this endpoint requires
the 'ADD_REACTIONS' permission to be present on the current user.
package channel
import (
	"context"
	"encoding/json"
	"errors"
	"net/http"
	"net/url"
	"strconv"
	"github.com/skwair/harmony/discord"
	"github.com/skwair/harmony/internal/endpoint"
	"github.com/skwair/harmony/internal/rest"
)
func (r *Resource) Messages(ctx context.Context, query string, limit int) ([]discord.Message, error) {
	if query == "" {
		return nil, errors.New("empty query")
	}
	q := url.Values{}
	switch query[0] {
	case '>':
		q.Add("after", query[1:])
	case '<':
		q.Add("before", query[1:])
	case '~':
		q.Add("around", query[1:])
	default:
		return nil, errors.New("lll-formatted query: prefix the message ID with '>' (after), '<' (before) or '~' (around)")
	}
	if limit > 0 {
		if limit > 100 {
			limit = 100
		}
		q.Set("limit", strconv.Itoa(limit))
	}
	e := endpoint.GetChannelMessages(r.channelID, q.Encode())
	resp, err := r.client.Do(ctx, e, nil)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return nil, discord.NewAPIError(resp)
	}
	var msgs []discord.Message
	if err = json.NewDecoder(resp.Body).Decode(&msgs); err != nil {
		return nil, err
	}
	return msgs, nil
}
func (r *Resource) Message(ctx context.Context, id string) (*discord.Message, error) {
	e := endpoint.GetChannelMessage(r.channelID, id)
	resp, err := r.client.Do(ctx, e, nil)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return nil, discord.NewAPIError(resp)
	}
	var msg discord.Message
	if err = json.NewDecoder(resp.Body).Decode(&msg); err != nil {
		return nil, err
	}
	return &msg, nil
}
func (r *Resource) DeleteMessage(ctx context.Context, messageID string) error {
	return r.DeleteMessageWithReason(ctx, messageID, "")
}
func (r *Resource) DeleteMessageWithReason(ctx context.Context, messageID, reason string) error {
	e := endpoint.DeleteMessage(r.channelID, messageID)
	resp, err := r.client.DoWithHeader(ctx, e, nil, rest.ReasonHeader(reason))
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusNoContent {
		return discord.NewAPIError(resp)
	}
	return nil
}
func (r *Resource) DeleteMessageBulk(ctx context.Context, messageIDs []string) error {
	st := struct {
		Messages []string `json:"messages"`
	}{
		Messages: messageIDs,
	}
	b, err := json.Marshal(st)
	if err != nil {
		return err
	}
	e := endpoint.BulkDeleteMessage(r.channelID)
	resp, err := r.client.Do(ctx, e, rest.JSONPayload(b))
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusNoContent {
		return discord.NewAPIError(resp)
	}
	return nil
}
type MessageOption func(*createMessage)
func WithMessageContent(text string) MessageOption {
	return func(m *createMessage) {
		m.Content = text
	}
}
func WithMessageEmbed(e *discord.MessageEmbed) MessageOption {
	return func(m *createMessage) {
		m.Embed = e
	}
}
func WithMessageFiles(files ...*discord.File) MessageOption {
	return func(m *createMessage) {
		for _, f := range files {
			m.files = append(m.files, *f)
		}
	}
}
func WithMessageTTS() MessageOption {
	return func(m *createMessage) {
		m.TTS = true
	}
}
func WithMessageNonce(n string) MessageOption {
	return func(m *createMessage) {
		m.Nonce = n
	}
}
func (r *Resource) Send(ctx context.Context, opts ...MessageOption) (*discord.Message, error) {
	var msg createMessage
	for _, opt := range opts {
		opt(&msg)
	}
	if msg.Content == "" && msg.Embed == nil && len(msg.files) == 0 {
		return nil, discord.ErrInvalidMessageSend
	}
	return r.sendMessage(ctx, r.channelID, &msg)
}
func (r *Resource) SendMessage(ctx context.Context, text string) (*discord.Message, error) {
	return r.Send(ctx, WithMessageContent(text))
}
type createMessage struct {
	Content string                `json:"content,omitempty"`  
	Nonce   string                `json:"nonce,omitempty"`
	TTS     bool                  `json:"tts,omitempty"`
	Embed   *discord.MessageEmbed `json:"embed,omitempty"`
	files []discord.File
}
func (cm *createMessage) Bytes() ([]byte, error) {
	return json.Marshal(cm)
}
func (r *Resource) sendMessage(ctx context.Context, channelID string, msg *createMessage) (*discord.Message, error) {
	if msg.Embed != nil && msg.Embed.Type == "" {
		msg.Embed.Type = "rich"
	}
	var payload *rest.Payload
	if len(msg.files) > 0 {
		b, contentType, err := rest.MultipartFromFiles(msg, msg.files...)
		if err != nil {
			return nil, err
		}
		payload = rest.CustomPayload(b, contentType)
	} else {
		b, err := json.Marshal(msg)
		if err != nil {
			return nil, err
		}
		payload = rest.JSONPayload(b)
	}
	e := endpoint.CreateMessage(channelID)
	resp, err := r.client.Do(ctx, e, payload)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return nil, discord.NewAPIError(resp)
	}
	var m discord.Message
	if err = json.NewDecoder(resp.Body).Decode(&m); err != nil {
		return nil, err
	}
	return &m, nil
}
type editMessage struct {
	Content string                `json:"content,omitempty"`
	Embed   *discord.MessageEmbed `json:"embed,omitempty"`
}
func (r *Resource) EditMessage(ctx context.Context, messageID, content string) (*discord.Message, error) {
	return r.editMessage(ctx, r.channelID, messageID, &editMessage{content, nil})
}
func (r *Resource) EditEmbed(ctx context.Context, messageID, content string, embed *discord.MessageEmbed) (*discord.Message, error) {
	return r.editMessage(ctx, r.channelID, messageID, &editMessage{content, embed})
}
func (r *Resource) editMessage(ctx context.Context, channelID, messageID string, edit *editMessage) (*discord.Message, error) {
	b, err := json.Marshal(edit)
	if err != nil {
		return nil, err
	}
	e := endpoint.EditMessage(channelID, messageID)
	resp, err := r.client.Do(ctx, e, rest.JSONPayload(b))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return nil, discord.NewAPIError(resp)
	}
	var msg discord.Message
	if err = json.NewDecoder(resp.Body).Decode(&msg); err != nil {
		return nil, err
	}
	return &msg, nil
}
func (r *Resource) CrossPostMessage(ctx context.Context, messageID string) (*discord.Message, error) {
	e := endpoint.CrossPostMessage(r.channelID, messageID)
	resp, err := r.client.Do(ctx, e, nil)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return nil, discord.NewAPIError(resp)
	}
	var msg discord.Message
	if err = json.NewDecoder(resp.Body).Decode(&msg); err != nil {
		return nil, err
	}
	return &msg, nil
}
func (r *Resource) Reactions(ctx context.Context, messageID, emoji string, limit int, before, after string) ([]discord.User, error) {
	q := url.Values{}
	if limit > 0 {
		q.Set("limit", strconv.Itoa(limit))
	}
	if before != "" {
		q.Set("before", before)
	}
	if after != "" {
		q.Set("after", after)
	}
	e := endpoint.GetReactions(r.channelID, messageID, url.PathEscape(emoji), q.Encode())
	resp, err := r.client.Do(ctx, e, nil)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return nil, discord.NewAPIError(resp)
	}
	var users []discord.User
	if err = json.NewDecoder(resp.Body).Decode(&users); err != nil {
		return nil, err
	}
	return users, nil
}