Added a MediaUploadService option to the configuration loader and documented it in the README so operators can pick the attachment backend (defaulting to Litterbox) straight from .env.
Refactored the Discord bridge to build a media service during initialization, route attachment uploads through it, and dynamically label the status embeds and error diagnostics based on the selected provider while preserving the existing progress messaging flow. Introduced a new media package that defines the uploader interface and ships a Litterbox implementation responsible for fetching Discord attachments and posting them to Catbox while reporting HTTP status codes back to the bridge.
This commit is contained in:
84
media/litterbox.go
Normal file
84
media/litterbox.go
Normal file
@@ -0,0 +1,84 @@
|
||||
package media
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/bwmarrin/discordgo"
|
||||
)
|
||||
|
||||
const (
|
||||
litterboxTTL = "72h"
|
||||
litterboxEndpoint = "https://litterbox.catbox.moe/resources/internals/api.php"
|
||||
defaultHTTPTimeout = 60 * time.Second
|
||||
)
|
||||
|
||||
type LitterboxService struct {
|
||||
client *http.Client
|
||||
}
|
||||
|
||||
func (s *LitterboxService) Name() string { return DefaultService }
|
||||
|
||||
func (s *LitterboxService) Upload(ctx context.Context, attachment *discordgo.MessageAttachment) (string, int, error) {
|
||||
client := s.client
|
||||
if client == nil {
|
||||
client = &http.Client{Timeout: defaultHTTPTimeout}
|
||||
}
|
||||
if client.Timeout == 0 {
|
||||
client.Timeout = defaultHTTPTimeout
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, attachment.URL, nil)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return "", resp.StatusCode, fmt.Errorf("HTTP %d", resp.StatusCode)
|
||||
}
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
|
||||
body := &bytes.Buffer{}
|
||||
writer := multipart.NewWriter(body)
|
||||
_ = writer.WriteField("reqtype", "fileupload")
|
||||
_ = writer.WriteField("time", litterboxTTL)
|
||||
part, _ := writer.CreateFormFile("fileToUpload", attachment.Filename)
|
||||
_, _ = part.Write(data)
|
||||
_ = writer.Close()
|
||||
|
||||
uploadReq, err := http.NewRequestWithContext(ctx, http.MethodPost, litterboxEndpoint, body)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
uploadReq.Header.Set("Content-Type", writer.FormDataContentType())
|
||||
|
||||
uploadResp, err := client.Do(uploadReq)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
defer uploadResp.Body.Close()
|
||||
if uploadResp.StatusCode != http.StatusOK {
|
||||
bodyBytes, _ := io.ReadAll(uploadResp.Body)
|
||||
reason := strings.TrimSpace(string(bodyBytes))
|
||||
if reason != "" {
|
||||
return "", uploadResp.StatusCode, fmt.Errorf("Litterbox returned HTTP %d: %s", uploadResp.StatusCode, reason)
|
||||
}
|
||||
return "", uploadResp.StatusCode, fmt.Errorf("Litterbox returned HTTP %d", uploadResp.StatusCode)
|
||||
}
|
||||
out, _ := io.ReadAll(uploadResp.Body)
|
||||
url := strings.TrimSpace(string(out))
|
||||
return url, uploadResp.StatusCode, nil
|
||||
}
|
||||
Reference in New Issue
Block a user