package main import ( "bytes" "encoding/json" "errors" "fmt" "io" "log" "mime" "mime/multipart" "net/http" "net/textproto" telegram "github.com/go-telegram-bot-api/telegram-bot-api" ) func (this *NTFServer) ContentedEnabled() bool { return this.contentedMaxBytes > 0 } func (this *NTFServer) ContentedUploadFromSync(main *telegram.File, thumbs *[]telegram.PhotoSize) (string, error) { // If file fits under size limit, take it if int64(main.FileSize) < this.contentedMaxBytes { return this.ContentedUploadSync(main.FileID, int64(main.FileSize)) } // Otherwise, we'll settle for the highest-res thumbnail we can take if thumbs == nil || len(*thumbs) == 0 { return "", errors.New("The file was too large for the image host server (and no smaller thumbnail is available)") } // bestKnownIdx := -1 bestKnownMpx := int64(-1) for idx, img := range *thumbs { if int64(img.FileSize) > this.contentedMaxBytes { continue // no good } // Highest total pixel count mpx := int64(img.Width) * int64(img.Height) if mpx > bestKnownMpx { bestKnownIdx = idx bestKnownMpx = mpx } } if bestKnownIdx == -1 { return "", errors.New("The file was too large for the image host server (and no smaller thumbnail is available)") } return this.ContentedUploadSync((*thumbs)[bestKnownIdx].FileID, int64((*thumbs)[bestKnownIdx].FileSize)) } func (this *NTFServer) ContentedUploadSync(fileId string, expectSizeBytes int64) (string, error) { // If file fits under size limit, take it if expectSizeBytes > this.contentedMaxBytes { return "", errors.New("The file was too large for the image host server (and no smaller thumbnail is available)") } // Prepare to download the file from telegram url, err := this.bot.GetFileDirectURL(fileId) if err != nil { return "", fmt.Errorf("TelegramGetFileDirectURL: %s", err.Error()) } if this.verbose { log.Printf("Downloading telegram file %s", url) } resp, err := http.Get(url) if err != nil { return "", fmt.Errorf("HttpGet: %s", err.Error()) } defer resp.Body.Close() // Determine a suitable pseudo-filename from the telegram content-type fileContentType := resp.Header.Get("Content-Type") // is it blank? who knows? if this.verbose { log.Printf("Downloaded telegram file response headers: %#v\n", resp.Header) } fileName := "telegram-upload" if fileExts, err := mime.ExtensionsByType(fileContentType); err == nil && len(fileExts) > 0 { fileName += fileExts[0] // Just pick the first one. They all contain a leading period } // Contented uploads are in multipart mime format uploadBody := bytes.Buffer{} formValues := multipart.NewWriter(&uploadBody) // Adapted from (mime/multipart)Writer.CreateFormFile to support a custom interior content type multipartFileHeader := make(textproto.MIMEHeader) multipartFileHeader.Set("Content-Disposition", fmt.Sprintf(`form-data; name="%s"; filename="%s"`, "f", fileName)) multipartFileHeader.Set("Content-Type", fileContentType) filePart, err := formValues.CreatePart(multipartFileHeader) if err != nil { return "", err } // Download _, err = io.CopyN(filePart, resp.Body, expectSizeBytes) // CopyN asserts either err!=nil, or copiedBytes == expectSizeBytes. So we're safe if err != nil { return "", fmt.Errorf("CopyN: %s", err.Error()) } // Upload err = formValues.Close() if err != nil { return "", fmt.Errorf("Close: %s", err.Error()) } req, err := http.NewRequest("POST", this.config.ContentedURL+"upload", &uploadBody) if err != nil { return "", err } req.Header.Set("Content-Type", formValues.FormDataContentType()) // it's "multipart/form-data; boundary=xxxx" with a magic string uploadResp, err := http.DefaultClient.Do(req) if err != nil { return "", fmt.Errorf("Upload failed: %s", err.Error()) } defer uploadResp.Body.Close() // Read back the response from contented // Should be a 200 error code, JSON content type, array of strings of shortcodes to the uploaded file(s) if uploadResp.StatusCode != 200 { return "", fmt.Errorf("Unexpected HTTP %d response back from file hosting server", uploadResp.StatusCode) } shortCodes := make([]string, 0, 1) err = json.NewDecoder(uploadResp.Body).Decode(&shortCodes) if err != nil { return "", fmt.Errorf("Decoding response from file hosting server: %s", err.Error()) } if len(shortCodes) != 1 || len(shortCodes[0]) == 0 { return "", errors.New("Unexpected blank response back from file hosting server") } // DONE return this.config.ContentedURL + "p/" + shortCodes[0], nil }