package telegram import ( "bytes" "errors" "fmt" "io" "mime/multipart" "net/url" "os" log "github.com/kirillDanshin/dlog" json "github.com/pquerna/ffjson/ffjson" http "github.com/valyala/fasthttp" ) 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 *url.URL 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 file_id - 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 *url.URL 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 (bot *Bot) Upload(method, key, name string, file InputFile, args fmt.Stringer) (*Response, error) { buffer := bytes.NewBuffer(nil) multi := multipart.NewWriter(buffer) requestURI := &url.URL{ Scheme: "https", Host: "api.telegram.org", Path: fmt.Sprint("/bot", bot.AccessToken, "/", method), } query, err := url.ParseQuery(args.String()) if err != nil { return nil, err } for key, val := range query { if err = multi.WriteField(key, val[0]); err != nil { return nil, err } } switch src := file.(type) { case string: err = uploadByString(multi, key, src) case *url.URL: // Send by URL err = uploadFromURL(multi, key, src) case []byte: // Upload new err = uploadFromMemory(multi, key, name, bytes.NewReader(src)) case io.Reader: // Upload new err = uploadFromMemory(multi, key, name, src) default: return nil, ErrBadFileType } if 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("go-telegram/3.5") req.Header.SetHost("api.telegram.org") log.Ln("Request:") log.D(req) resp := http.AcquireResponse() defer http.ReleaseResponse(resp) if err = http.Do(req, resp); err != nil { log.Ln("Resp:") log.D(resp) return nil, err } log.Ln("Resp:") log.D(resp) var data Response if err = json.Unmarshal(resp.Body(), &data); err != nil { return nil, err } if !data.Ok { return nil, errors.New(data.Description) } return &data, nil } func uploadByString(w *multipart.Writer, key, src string) error { _, err := os.Stat(src) switch { case os.IsNotExist(err): err = uploadFromFileID(w, key, src) case os.IsExist(err): err = uploadFromDisk(w, key, src) } return err } func uploadFromFileID(w *multipart.Writer, key, src string) error { return w.WriteField(key, src) } 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 uploadFromURL(w *multipart.Writer, key string, src *url.URL) error { return w.WriteField(key, src.String()) } func uploadFromMemory(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 }