+35
-27
@@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
@@ -133,6 +134,7 @@ func (r UploaderRequest[R, P]) doRequest(ctx context.Context, up *Uploader) (R,
|
||||
}
|
||||
url := fmt.Sprintf("%s/bot%s%s/%s", up.api.apiURL, up.api.token, methodPrefix, r.method)
|
||||
|
||||
retries := 0
|
||||
for {
|
||||
if up.api.Limiter != nil {
|
||||
if err := up.api.Limiter.Check(ctx, up.api.dropOverflowLimit, r.chatID); err != nil {
|
||||
@@ -140,23 +142,21 @@ func (r UploaderRequest[R, P]) doRequest(ctx context.Context, up *Uploader) (R,
|
||||
}
|
||||
}
|
||||
|
||||
buf, contentType, err := prepareMultipart(r.files, r.params)
|
||||
requestBody, contentType := prepareMultipartStream(r.files, r.params)
|
||||
req, err := http.NewRequestWithContext(ctx, "POST", url, requestBody)
|
||||
if err != nil {
|
||||
return zero, err
|
||||
}
|
||||
req, err := http.NewRequestWithContext(ctx, "POST", url, buf)
|
||||
if err != nil {
|
||||
return zero, err
|
||||
_ = requestBody.Close()
|
||||
return zero, fmt.Errorf("failed to create upload request: %w", err)
|
||||
}
|
||||
req.Header.Set("Content-Type", contentType)
|
||||
req.Header.Set("Accept", "application/json")
|
||||
req.Header.Set("User-Agent", fmt.Sprintf("Laniakea/%s", utils.VersionString))
|
||||
req.ContentLength = int64(buf.Len())
|
||||
|
||||
up.logger.Debugln("UPLOADER REQ", url)
|
||||
resp, err := up.api.client.Do(req)
|
||||
_ = requestBody.Close()
|
||||
if err != nil {
|
||||
return zero, err
|
||||
return zero, fmt.Errorf("HTTP upload request failed: %w", err)
|
||||
}
|
||||
|
||||
body, err := readBody(resp.Body)
|
||||
@@ -172,6 +172,11 @@ func (r UploaderRequest[R, P]) doRequest(ctx context.Context, up *Uploader) (R,
|
||||
}
|
||||
|
||||
if !response.Ok {
|
||||
responseErr := &ResponseError{
|
||||
Code: response.ErrorCode,
|
||||
Description: response.Description,
|
||||
Parameters: response.Parameters,
|
||||
}
|
||||
if response.ErrorCode == 429 && response.Parameters != nil && response.Parameters.RetryAfter != nil {
|
||||
after := *response.Parameters.RetryAfter
|
||||
up.logger.Warnf("Rate limited, retry after %d seconds (chat: %d)", after, r.chatID)
|
||||
@@ -182,19 +187,19 @@ func (r UploaderRequest[R, P]) doRequest(ctx context.Context, up *Uploader) (R,
|
||||
up.api.Limiter.SetGlobalLock(after)
|
||||
}
|
||||
}
|
||||
if retries >= up.api.maxRetries {
|
||||
return zero, fmt.Errorf("%w after %d retries: %w", ErrRetryLimit, retries, responseErr)
|
||||
}
|
||||
retries++
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return zero, ctx.Err()
|
||||
case <-time.After(time.Duration(after) * time.Second):
|
||||
case <-time.After(retryDelay(after)):
|
||||
continue
|
||||
}
|
||||
}
|
||||
return zero, &ResponseError{
|
||||
Code: response.ErrorCode,
|
||||
Description: response.Description,
|
||||
Parameters: response.Parameters,
|
||||
}
|
||||
return zero, responseErr
|
||||
}
|
||||
return response.Result, nil
|
||||
}
|
||||
@@ -232,36 +237,39 @@ func (r UploaderRequest[R, P]) Do(up *Uploader) (R, error) {
|
||||
return r.DoWithContext(context.Background(), up)
|
||||
}
|
||||
|
||||
func prepareMultipart[P any](files []UploaderFile, params P) (*bytes.Buffer, string, error) {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
w := multipart.NewWriter(buf)
|
||||
func prepareMultipartStream[P any](files []UploaderFile, params P) (io.ReadCloser, string) {
|
||||
reader, writer := io.Pipe()
|
||||
multipartWriter := multipart.NewWriter(writer)
|
||||
contentType := multipartWriter.FormDataContentType()
|
||||
go func() {
|
||||
err := writeMultipart(multipartWriter, files, params)
|
||||
_ = writer.CloseWithError(err)
|
||||
}()
|
||||
return reader, contentType
|
||||
}
|
||||
|
||||
func writeMultipart[P any](w *multipart.Writer, files []UploaderFile, params P) error {
|
||||
for _, file := range files {
|
||||
fw, err := w.CreateFormFile(string(file.field), file.filename)
|
||||
if err != nil {
|
||||
_ = w.Close()
|
||||
return nil, "", err
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = fw.Write(file.data)
|
||||
_, err = io.Copy(fw, bytes.NewReader(file.data))
|
||||
if err != nil {
|
||||
_ = w.Close()
|
||||
return nil, "", err
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err := utils.Encode(w, params)
|
||||
if err != nil {
|
||||
_ = w.Close()
|
||||
return nil, "", err
|
||||
return err
|
||||
}
|
||||
|
||||
err = w.Close()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
return buf, w.FormDataContentType(), nil
|
||||
return w.Close()
|
||||
}
|
||||
|
||||
func uploaderTypeByExt(filename string) UploaderFileType {
|
||||
|
||||
Reference in New Issue
Block a user