arikawa/api/message_send.go

134 lines
2.8 KiB
Go
Raw Normal View History

2020-01-02 05:39:52 +00:00
package api
import (
"fmt"
"io"
2020-01-19 02:27:30 +00:00
"log"
2020-01-02 05:39:52 +00:00
"mime/multipart"
"net/http"
"net/textproto"
"strconv"
"strings"
"github.com/diamondburned/arikawa/discord"
2020-01-15 18:32:54 +00:00
"github.com/diamondburned/arikawa/internal/json"
2020-01-02 05:39:52 +00:00
"github.com/pkg/errors"
)
2020-01-19 02:27:30 +00:00
var quoteEscaper = strings.NewReplacer(`\`, `\\`, `"`, `\"`)
type SendMessageFile struct {
Name string
ContentType string // auto-detect if empty
Reader io.Reader
}
2020-01-02 05:39:52 +00:00
type SendMessageData struct {
2020-01-19 02:27:30 +00:00
Content string `json:"content,omitempty"`
Nonce string `json:"nonce,omitempty"`
2020-01-02 05:39:52 +00:00
TTS bool `json:"tts"`
2020-01-19 02:27:30 +00:00
Embed *discord.Embed `json:"embed,omitempty"`
2020-01-02 05:39:52 +00:00
Files []SendMessageFile `json:"-"`
}
2020-01-19 02:27:30 +00:00
func (data *SendMessageData) WriteMultipart(
c json.Driver, w io.Writer) error {
return writeMultipart(c, w, data, data.Files)
2020-01-02 05:39:52 +00:00
}
2020-01-19 02:27:30 +00:00
type ExecuteWebhookData struct {
SendMessageData
Username string `json:"username,omitempty"`
AvatarURL discord.URL `json:"avatar_url,omitempty"`
}
func (data *ExecuteWebhookData) WriteMultipart(
c json.Driver, w io.Writer) error {
return writeMultipart(c, w, data, data.Files)
}
func writeMultipart(
c json.Driver, w io.Writer,
item interface{}, files []SendMessageFile) error {
2020-01-02 05:39:52 +00:00
body := multipart.NewWriter(w)
// Encode the JSON body first
h := textproto.MIMEHeader{}
h.Set("Content-Disposition", `form-data; name="payload_json"`)
h.Set("Content-Type", "application/json")
w, err := body.CreatePart(h)
if err != nil {
return errors.Wrap(err, "Failed to create bodypart for JSON")
}
2020-01-19 02:27:30 +00:00
j, err := c.Marshal(item)
log.Println(string(j), err)
if err := c.EncodeStream(w, item); err != nil {
2020-01-02 05:39:52 +00:00
return errors.Wrap(err, "Failed to encode JSON")
}
// Content-Type buffer
var buf []byte
2020-01-19 02:27:30 +00:00
for i, file := range files {
2020-01-02 05:39:52 +00:00
h := textproto.MIMEHeader{}
h.Set("Content-Disposition", fmt.Sprintf(
`form-data; name="file%d"; filename="%s"`,
2020-01-02 05:39:52 +00:00
i, quoteEscaper.Replace(file.Name),
))
2020-01-19 02:27:30 +00:00
var bufUsed int
2020-01-02 05:39:52 +00:00
if file.ContentType == "" {
if buf == nil {
buf = make([]byte, 512)
}
n, err := file.Reader.Read(buf)
if err != nil {
return errors.Wrap(err, "Failed to read first 512 bytes for "+
strconv.Itoa(i))
}
file.ContentType = http.DetectContentType(buf[:n])
2020-01-19 02:27:30 +00:00
files[i] = file
bufUsed = n
}
h.Set("Content-Type", file.ContentType)
2020-01-02 05:39:52 +00:00
2020-01-19 02:27:30 +00:00
w, err := body.CreatePart(h)
if err != nil {
return errors.Wrap(err, "Failed to create bodypart for "+
strconv.Itoa(i))
}
2020-01-02 05:39:52 +00:00
2020-01-19 02:27:30 +00:00
if bufUsed > 0 {
2020-01-02 05:39:52 +00:00
// Prematurely write
2020-01-19 02:27:30 +00:00
if _, err := w.Write(buf[:bufUsed]); err != nil {
2020-01-02 05:39:52 +00:00
return errors.Wrap(err, "Failed to write buffer for "+
strconv.Itoa(i))
}
}
if _, err := io.Copy(w, file.Reader); err != nil {
return errors.Wrap(err, "Failed to write file for "+
strconv.Itoa(i))
}
}
if err := body.Close(); err != nil {
return errors.Wrap(err, "Failed to close body writer")
}
return nil
}