1
0
Fork 0
telegram/upload.go

187 lines
5.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package telegram
import (
"bytes"
"errors"
"io"
"mime/multipart"
"os"
"path"
http "github.com/valyala/fasthttp"
)
type UploadStickerFileParameters struct {
// User identifier of sticker file owner
UserID int `json:"user_id"`
// Png image with the sticker, must be up to 512 kilobytes in size,
// dimensions must not exceed 512px, and either width or height
// must be exactly 512px.
PNGSticker interface{} `json:"png_sticker"`
}
// ErrBadFileType describes error of the unsupported file data type for uploading
var ErrBadFileType = errors.New("bad file type")
/*
Upload is a helper method which provide are three ways to send files (photos, stickers, audio,
media, etc.):
1. If the file is already stored somewhere on the Telegram servers, you don't need to reupload it:
each file object has a file_id field, simply pass this file_id as a parameter instead of uploading.
There are no limits for files sent this way.
2. Provide Telegram with an *fasthttp.URI for the file to be sent. Telegram will download and send the
file. 5 MB max size for photos and 20 MB max for other types of content.
3. Post the file using multipart/form-data in the usual way that files are uploaded via the
browser. Use []byte or io.Reader for this. 10 MB max size for photos, 50 MB for other files.
Sending by FileID
* It is not possible to change the file type when resending by file_id. I.e. a video can't be sent
as a photo, a photo can't be sent as a document, etc.
* It is not possible to resend thumbnails.
* Resending a photo by file_id will send all of its sizes.
* file_id is unique for each individual bot and can't be transferred from one bot to another.
Sending by URL
* When sending by *fasthttp.URI the target file must have the correct MIME type (e.g., audio/mpeg for
sendAudio, etc.).
* In sendDocument, sending by URL will currently only work for gif, pdf and zip files.
* To use SendVoice, the file must have the type audio/ogg and be no more than 1MB in size. 120MB
voice notes will be sent as files.
* Other configurations may work but we can't guarantee that they will.
*/
func (b *Bot) Upload(method, key, name string, file InputFile, args *http.Args) (*Response, error) {
buffer := bytes.NewBuffer(nil)
multi := multipart.NewWriter(buffer)
requestURI := http.AcquireURI()
requestURI.SetScheme("https")
requestURI.SetHost("api.telegram.org")
requestURI.SetPath(path.Join("bot"+b.AccessToken, method))
args.VisitAll(func(key, value []byte) {
_ = multi.WriteField(string(key), string(value))
})
if err := createFileField(multi, file, key, name); err != nil {
return nil, err
}
if err := multi.Close(); err != nil {
return nil, err
}
req := http.AcquireRequest()
defer http.ReleaseRequest(req)
req.SetBody(buffer.Bytes())
req.Header.SetContentType(multi.FormDataContentType())
req.Header.SetMethod("POST")
req.Header.SetRequestURI(requestURI.String())
req.Header.SetUserAgent(path.Join("telegram", Version))
req.Header.SetHostBytes(requestURI.Host())
resp := http.AcquireResponse()
defer http.ReleaseResponse(resp)
if err := http.Do(req, resp); err != nil {
return nil, err
}
var response Response
if err := parser.Unmarshal(resp.Body(), &response); err != nil {
return nil, err
}
if !response.Ok {
return nil, errors.New(response.Description)
}
return &response, nil
}
func createFileField(w *multipart.Writer, file interface{}, key, val string) (err error) {
switch src := file.(type) {
case string: // Send FileID of file on disk path
return createFileFieldString(w, key, src)
case *http.URI: // Send by URL
return w.WriteField(key, src.String())
case []byte: // Upload new
return createFileFieldRaw(w, key, val, bytes.NewReader(src))
case io.Reader: // Upload new
return createFileFieldRaw(w, key, val, src)
default:
return ErrBadFileType
}
}
func createFileFieldString(w *multipart.Writer, key, src string) (err error) {
_, err = os.Stat(src)
switch {
case os.IsNotExist(err):
err = w.WriteField(key, src)
case os.IsExist(err):
err = uploadFromDisk(w, key, src)
}
return
}
func uploadFromDisk(w *multipart.Writer, key, src string) error {
file, err := os.Open(src)
if err != nil {
return err
}
defer func() {
_ = file.Close()
}()
var formFile io.Writer
formFile, err = w.CreateFormFile(key, file.Name())
if err != nil {
return err
}
_, err = io.Copy(formFile, file)
return err
}
func createFileFieldRaw(w *multipart.Writer, key, value string, src io.Reader) error {
field, err := w.CreateFormFile(key, value)
if err != nil {
return err
}
_, err = io.Copy(field, src)
return err
}
// UploadStickerFile upload a .png file with a sticker for later use in
// createNewStickerSet and addStickerToSet methods (can be used multiple times).
// Returns the uploaded File on success.
func (b *Bot) UploadStickerFile(userID int, pngSticker interface{}) (*File, error) {
args := http.AcquireArgs()
defer http.ReleaseArgs(args)
args.SetUint("user_id", userID)
resp, err := b.Upload(MethodUploadStickerFile, TypeSticker, "sticker", pngSticker, args)
if err != nil {
return nil, err
}
var f File
err = parser.Unmarshal(resp.Result, &f)
return &f, err
}