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. 1–20MB 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 }