From 41770e2b59aa26057040ffb354f6109755ea3db8 Mon Sep 17 00:00:00 2001 From: Dan Anglin Date: Fri, 2 Aug 2024 17:08:41 +0100 Subject: [PATCH] checkpoint: can now create media attachments --- internal/client/media.go | 97 +++++++++++++++++++++++++++++++++---- internal/executor/create.go | 25 +++++++++- internal/executor/flags.go | 2 + 3 files changed, 113 insertions(+), 11 deletions(-) diff --git a/internal/client/media.go b/internal/client/media.go index a855071..f055611 100644 --- a/internal/client/media.go +++ b/internal/client/media.go @@ -1,8 +1,13 @@ package client import ( + "bytes" "fmt" + "io" + "mime/multipart" "net/http" + "os" + "path/filepath" "codeflow.dananglin.me.uk/apollo/enbas/internal/model" ) @@ -31,12 +36,86 @@ func (g *Client) GetMediaAttachment(mediaAttachmentID string) (model.Attachment, return attachment, nil } -//type CreateMediaAttachmentForm struct { -// Description string -// Focus string -// Filepath string -//} -// -//func (g *Client) CreateMediaAttachment(form CreateMediaAttachmentForm) (model.Attachment, error) { -// return model.Attachment{}, nil -//} +func (g *Client) CreateMediaAttachment(path, description, focus string) (model.Attachment, error) { + file, err := os.Open(path) + if err != nil { + return model.Attachment{}, fmt.Errorf("unable to open the file: %w", err) + } + defer file.Close() + + // create the request body using a writer from the multipart package + requestBody := bytes.Buffer{} + requestBodyWriter := multipart.NewWriter(&requestBody) + + filename := filepath.Base(path) + + part, err := requestBodyWriter.CreateFormFile("file", filename) + if err != nil { + return model.Attachment{}, fmt.Errorf("unable to create the new part: %w", err) + } + + if _, err := io.Copy(part, file); err != nil { + return model.Attachment{}, fmt.Errorf("unable to copy the file contents to the form: %w", err) + } + + // add the description + if description != "" { + descriptionFormFieldWriter, err := requestBodyWriter.CreateFormField("description") + if err != nil { + return model.Attachment{}, fmt.Errorf( + "unable to create the writer for the 'description' form field: %w", + err, + ) + } + + if _, err := io.WriteString(descriptionFormFieldWriter, description); err != nil { + return model.Attachment{}, fmt.Errorf( + "unable to write the description to the form: %w", + err, + ) + } + } + + // add the focus values + if focus != "" { + focusFormFieldWriter, err := requestBodyWriter.CreateFormField("focus") + if err != nil { + return model.Attachment{}, fmt.Errorf( + "unable to create the writer for the 'focus' form field: %w", + err, + ) + } + + if _, err := io.WriteString(focusFormFieldWriter, focus); err != nil { + return model.Attachment{}, fmt.Errorf( + "unable to write the focus values to the form: %w", + err, + ) + } + } + + if err := requestBodyWriter.Close(); err != nil { + return model.Attachment{}, fmt.Errorf("unable to close the writer: %w", err) + } + + url := g.Authentication.Instance + baseMediaPath + + var attachment model.Attachment + + params := requestParameters{ + httpMethod: http.MethodPost, + url: url, + requestBody: &requestBody, + contentType: requestBodyWriter.FormDataContentType(), + output: &attachment, + } + + if err := g.sendRequest(params); err != nil { + return model.Attachment{}, fmt.Errorf( + "received an error after sending the request to create the media attachment: %w", + err, + ) + } + + return attachment, nil +} diff --git a/internal/executor/create.go b/internal/executor/create.go index 0360ab0..45a0a1a 100644 --- a/internal/executor/create.go +++ b/internal/executor/create.go @@ -27,7 +27,9 @@ type CreateExecutor struct { sensitive *bool content string contentType string + description string fromFile string + focus string inReplyTo string language string resourceType string @@ -83,6 +85,8 @@ func NewCreateExecutor(printer *printer.Printer, config *config.Config, name, su // Flags for lists createExe.StringVar(&createExe.listTitle, flagListTitle, "", "Specify the title of the list") createExe.StringVar(&createExe.listRepliesPolicy, flagListRepliesPolicy, "list", "Specify the policy of the replies for this list (valid values are followed, list and none)") + createExe.StringVar(&createExe.description, flagDescription, "", "The description of the media attachment that will be used as the alt-text") + createExe.StringVar(&createExe.focus, flagFocus, "", "The focus of the media file.") createExe.Usage = commandUsageFunc(name, summary, createExe.FlagSet) @@ -100,8 +104,9 @@ func (c *CreateExecutor) Execute() error { } funcMap := map[string]func(*client.Client) error{ - resourceList: c.createList, - resourceStatus: c.createStatus, + resourceList: c.createList, + resourceStatus: c.createStatus, + resourceMediaAttachment: c.createMediaAttachment, } doFunc, ok := funcMap[c.resourceType] @@ -235,3 +240,19 @@ func (c *CreateExecutor) createStatus(gtsClient *client.Client) error { return nil } + +func (c *CreateExecutor) createMediaAttachment(gtsClient *client.Client) error { + if c.fromFile == "" { + return FlagNotSetError{flagText: flagFromFile} + } + + attachment, err := gtsClient.CreateMediaAttachment(c.fromFile, c.description, c.focus); + if err != nil { + return fmt.Errorf("unable to create the media attachment: %w", err) + } + + c.printer.PrintSuccess("Successfully created the following media attachment:") + c.printer.PrintMediaAttachment(attachment) + + return nil +} diff --git a/internal/executor/flags.go b/internal/executor/flags.go index ceebaa4..01d5338 100644 --- a/internal/executor/flags.go +++ b/internal/executor/flags.go @@ -16,12 +16,14 @@ const ( flagBrowser = "browser" flagContentType = "content-type" flagContent = "content" + flagDescription = "description" flagEnableFederation = "enable-federation" flagEnableLikes = "enable-likes" flagEnableReplies = "enable-replies" flagEnableReposts = "enable-reposts" flagExcludeBoosts = "exclude-boosts" flagExcludeReplies = "exclude-replies" + flagFocus = "focus" flagFrom = "from" flagFromFile = "from-file" flagFull = "full"