Compare commits
1 commit
57973f63d1
...
786d62fc69
Author | SHA1 | Date | |
---|---|---|---|
786d62fc69 |
8 changed files with 69 additions and 493 deletions
101
docs/manual.md
101
docs/manual.md
|
@ -28,10 +28,6 @@
|
||||||
- [View your follow requests](#view-your-follow-requests)
|
- [View your follow requests](#view-your-follow-requests)
|
||||||
- [Accept a follow request](#accept-a-follow-request)
|
- [Accept a follow request](#accept-a-follow-request)
|
||||||
- [Reject a follow request](#reject-a-follow-request)
|
- [Reject a follow request](#reject-a-follow-request)
|
||||||
- [Media Attachments](#media-attachments)
|
|
||||||
- [Create a media attachment](#create-a-media-attachment)
|
|
||||||
- [Edit a media attachment](#edit-a-media-attachment)
|
|
||||||
- [View a media attachment](#view-a-media-attachment)
|
|
||||||
- [Statuses](#statuses)
|
- [Statuses](#statuses)
|
||||||
- [View a status](#view-a-status)
|
- [View a status](#view-a-status)
|
||||||
- [Create a status](#create-a-status)
|
- [Create a status](#create-a-status)
|
||||||
|
@ -58,6 +54,8 @@
|
||||||
- [Remove accounts from a list](#remove-accounts-from-a-list)
|
- [Remove accounts from a list](#remove-accounts-from-a-list)
|
||||||
- [Timelines](#timelines)
|
- [Timelines](#timelines)
|
||||||
- [View a timeline](#view-a-timeline)
|
- [View a timeline](#view-a-timeline)
|
||||||
|
- [Media Attachment](#media-attachment)
|
||||||
|
- [View media attachment](#view-media-attachment)
|
||||||
- [Media](#media)
|
- [Media](#media)
|
||||||
- [View media from a status](#view-media-from-a-status)
|
- [View media from a status](#view-media-from-a-status)
|
||||||
- [Bookmarks](#bookmarks)
|
- [Bookmarks](#bookmarks)
|
||||||
|
@ -372,64 +370,6 @@ enbas reject --type follow-request --account-name @person.example.social
|
||||||
| `type` | string | true | The resource you want to accept.<br>Here this should be `follow-request`. | |
|
| `type` | string | true | The resource you want to accept.<br>Here this should be `follow-request`. | |
|
||||||
| `account-name` | string | true | The name of the account that you want to reject. | |
|
| `account-name` | string | true | The name of the account that you want to reject. | |
|
||||||
|
|
||||||
## Media Attachments
|
|
||||||
|
|
||||||
### Create a media attachment
|
|
||||||
|
|
||||||
Uploads media from a file to the instance and creates a media attachment.
|
|
||||||
You can write a description of the media in a text file and specify the path with the `media-description` flag (see the examples below).
|
|
||||||
|
|
||||||
- Create a media attachment with a simple description and a focus of x=-0.1, y=0.5
|
|
||||||
```
|
|
||||||
enbas create --type media-attachment \
|
|
||||||
--media-file picture.png \
|
|
||||||
--media-description "A picture of an old, slanted wooden bench in front of the woods." \
|
|
||||||
--media-focus "-0.1,0.5"
|
|
||||||
```
|
|
||||||
- Create a media attachment using a description written in the `description.txt` text file.
|
|
||||||
```
|
|
||||||
enbas create --type media-attachment \
|
|
||||||
--media-file picture.png \
|
|
||||||
--media-description file@description.txt
|
|
||||||
```
|
|
||||||
|
|
||||||
| flag | type | required | description | default |
|
|
||||||
|------|------|----------|-------------|---------|
|
|
||||||
| `type` | string | true | The resource you want to create.<br>Here this should be `media-attachment`. | |
|
|
||||||
| `media-file` | string | true | The path to the media file. | |
|
|
||||||
| `media-description` | string | false | The description of the media attachment which will be used as the media's alt-text.<br>To use a description from a text file, use the `flag@` prefix followed by the path to the file (e.g. `file@description.txt`)| |
|
|
||||||
| `media-focus` | string | false | The media's focus values. This should be in the form of two comma-separated numbers between -1 and 1 (e.g. 0.25,-0.34) | |
|
|
||||||
|
|
||||||
### Edit a media attachment
|
|
||||||
|
|
||||||
Edits the description and/or the focus of a media attachment that you own.
|
|
||||||
|
|
||||||
```
|
|
||||||
enbas edit --type media-attachment \
|
|
||||||
--attachment-id 01J5B9A8WFK59W11MS6AHPYWBR \
|
|
||||||
--media-description "An updated description of a picture."
|
|
||||||
```
|
|
||||||
|
|
||||||
| flag | type | required | description | default |
|
|
||||||
|------|------|----------|-------------|---------|
|
|
||||||
| `type` | string | true | The resource you want to edit.<br>Here this should be `media-attachment`. | |
|
|
||||||
| `media-description` | string | false | The description of the media attachment to edit.<br>To use a description from a text file, use the `flag@` prefix followed by the path to the file (e.g. `file@description.txt`)| |
|
|
||||||
| `media-focus` | string | false | The media's focus values. This should be in the form of two comma-separated numbers between -1 and 1 (e.g. 0.25,-0.34) | |
|
|
||||||
|
|
||||||
### View a media attachment
|
|
||||||
|
|
||||||
Prints information about a given media attachment that you own.
|
|
||||||
You can only see information about the media attachment that you own.
|
|
||||||
|
|
||||||
```
|
|
||||||
enbas show --type media-attachment --attachment-id 01J0N0RQSJ7CFGKHA30F7GBQXT
|
|
||||||
```
|
|
||||||
|
|
||||||
| flag | type | required | description | default |
|
|
||||||
|------|------|----------|-------------|---------|
|
|
||||||
| `type` | string | true | The resource you want to view.<br>Here this should be `media`. | |
|
|
||||||
| `attachment-id` | string | true | The ID of the media attachment to view. | |
|
|
||||||
|
|
||||||
## Statuses
|
## Statuses
|
||||||
|
|
||||||
### View a status
|
### View a status
|
||||||
|
@ -481,28 +421,10 @@ enbas show --type status --status-id 01J1Z9PT0243JT9QNQ5W96Z8CA
|
||||||
--poll-option "other (please comment)"
|
--poll-option "other (please comment)"
|
||||||
```
|
```
|
||||||
![A screenshot of a status with a poll](../assets/images/created_poll.png "A status with a poll")
|
![A screenshot of a status with a poll](../assets/images/created_poll.png "A status with a poll")
|
||||||
- Create a status with a media attachment that you have created.
|
|
||||||
```
|
|
||||||
enbas create \
|
|
||||||
--type status \
|
|
||||||
--attachment-id 01J5BDHYJ7MWMMG76FP49H7SWD \
|
|
||||||
--content "I went out for a walk in the woods and found this interesting looking wooden bench."
|
|
||||||
```
|
|
||||||
- Upload and attach 4 media files to a new status. You must set the same number of `media-description` and `media-focus` flags **must** as the `media-file` flags.
|
|
||||||
The first `media-description` and `media-focus` flags correspond to the first `media-file` flag and so on.
|
|
||||||
```
|
|
||||||
enbas create --type status --visibility public \
|
|
||||||
--content "This post has a picture of a cat, a dog, a bee and a bird." \
|
|
||||||
--media-file cat.jpg --media-description file@cat.txt --media-focus "0,0" \
|
|
||||||
--media-file dog.jpg --media-description file@dog.txt --media-focus "-0.1,0.25" \
|
|
||||||
--media-file bee.jpg --media-description file@bee.txt --media-focus "1,1" \
|
|
||||||
--media-file bird.webp --media-description file@bird.txt --media-focus "0,0"
|
|
||||||
```
|
|
||||||
|
|
||||||
| flag | type | required | description | default |
|
| flag | type | required | description | default |
|
||||||
|------|------|----------|-------------|---------|
|
|------|------|----------|-------------|---------|
|
||||||
| `type` | string | true | The resource you want to create.<br>Here this should be `status`. | |
|
| `type` | string | true | The resource you want to create.<br>Here this should be `status`. | |
|
||||||
| `attachment-id` | string | false | The ID of the media attachment to attach to the status.<br>Use this flag multiple times to attach multiple media. |
|
|
||||||
| `content` | string | false | The content of the status.<br>This flag takes precedence over `from-file`.| |
|
| `content` | string | false | The content of the status.<br>This flag takes precedence over `from-file`.| |
|
||||||
| `content-type` | string | false | The format that the content is created in.<br>Valid values are `plain` and `markdown`. | plain |
|
| `content-type` | string | false | The format that the content is created in.<br>Valid values are `plain` and `markdown`. | plain |
|
||||||
| `enable-reposts` | boolean | false | The status can be reposted (boosted) by others. | true |
|
| `enable-reposts` | boolean | false | The status can be reposted (boosted) by others. | true |
|
||||||
|
@ -512,9 +434,6 @@ enbas show --type status --status-id 01J1Z9PT0243JT9QNQ5W96Z8CA
|
||||||
| `from-file` | string | false | The path to the file where to read the contents of the status from. | |
|
| `from-file` | string | false | The path to the file where to read the contents of the status from. | |
|
||||||
| `in-reply-to` | string | false | The ID of the status that you want to reply to. | |
|
| `in-reply-to` | string | false | The ID of the status that you want to reply to. | |
|
||||||
| `language` | string | false | The ISO 639 language code that the status is written in.<br>If this is not specified then the default language from your posting preferences will be used. | |
|
| `language` | string | false | The ISO 639 language code that the status is written in.<br>If this is not specified then the default language from your posting preferences will be used. | |
|
||||||
| `media-file` | string | false | The path to the media file.<br>Use this flag multiple times to upload multiple media files. | |
|
|
||||||
| `media-description` | string | false | The description of the media attachment which will be used as the media's alt-text.<br>To use a description from a text file, use the `flag@` prefix followed by the path to the file (e.g. `file@description.txt`)<br>Use this flag multiple times to set multiple descriptions.| |
|
|
||||||
| `media-focus` | string | false | The media's focus values. This should be in the form of two comma-separated numbers between -1 and 1 (e.g. 0.25,-0.34).<br>Use this flag multiple times to set multiple focus values. | |
|
|
||||||
| `sensitive` | string | false | The status should be marked as sensitive.<br>If this is not specified then the default sensitivity from your posting preferences will be used. | |
|
| `sensitive` | string | false | The status should be marked as sensitive.<br>If this is not specified then the default sensitivity from your posting preferences will be used. | |
|
||||||
| `spoiler-text` | string | false | The text to display as the status' warning or subject. | |
|
| `spoiler-text` | string | false | The text to display as the status' warning or subject. | |
|
||||||
| `visibility` | string | false | The visibility of the status.<br>Valid values are `public`, `private`, `unlisted`, `mutuals_only` and `direct`.<br>If this is not specified then the default visibility from your posting preferences will be used. | |
|
| `visibility` | string | false | The visibility of the status.<br>Valid values are `public`, `private`, `unlisted`, `mutuals_only` and `direct`.<br>If this is not specified then the default visibility from your posting preferences will be used. | |
|
||||||
|
@ -764,6 +683,22 @@ Prints a list of statuses from a timeline.
|
||||||
| `tag` | string | false | The hashtag you want to view.<br>This is only required if `timeline-category` is set to `tag`. | |
|
| `tag` | string | false | The hashtag you want to view.<br>This is only required if `timeline-category` is set to `tag`. | |
|
||||||
| `limit` | integer | false | The maximum number of statuses to print. | 20 |
|
| `limit` | integer | false | The maximum number of statuses to print. | 20 |
|
||||||
|
|
||||||
|
## Media Attachment
|
||||||
|
|
||||||
|
### View media attachment
|
||||||
|
|
||||||
|
Prints information about a given media attachment that you own.
|
||||||
|
You can only see information about the media attachment that you own.
|
||||||
|
|
||||||
|
```
|
||||||
|
enbas show --type media-attachment --attachment-id 01J0N0RQSJ7CFGKHA30F7GBQXT
|
||||||
|
```
|
||||||
|
|
||||||
|
| flag | type | required | description | default |
|
||||||
|
|------|------|----------|-------------|---------|
|
||||||
|
| `type` | string | true | The resource you want to view.<br>Here this should be `media`. | |
|
||||||
|
| `attachment-id` | string | true | The ID of the media attachment to view. | |
|
||||||
|
|
||||||
## Media
|
## Media
|
||||||
|
|
||||||
### View media from a status
|
### View media from a status
|
||||||
|
|
|
@ -1,14 +1,8 @@
|
||||||
package client
|
package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"mime/multipart"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/model"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/model"
|
||||||
)
|
)
|
||||||
|
@ -37,123 +31,12 @@ func (g *Client) GetMediaAttachment(mediaAttachmentID string) (model.Attachment,
|
||||||
return attachment, nil
|
return attachment, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Client) CreateMediaAttachment(path, description, focus string) (model.Attachment, error) {
|
//type CreateMediaAttachmentForm struct {
|
||||||
file, err := os.Open(path)
|
// Description string
|
||||||
if err != nil {
|
// Focus string
|
||||||
return model.Attachment{}, fmt.Errorf("unable to open the file: %w", err)
|
// Filepath string
|
||||||
}
|
//}
|
||||||
defer file.Close()
|
//
|
||||||
|
//func (g *Client) CreateMediaAttachment(form CreateMediaAttachmentForm) (model.Attachment, error) {
|
||||||
// create the request body using a writer from the multipart package
|
// return model.Attachment{}, nil
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *Client) UpdateMediaAttachment(mediaAttachmentID, description, focus string) (model.Attachment, error) {
|
|
||||||
form := struct {
|
|
||||||
Description string `json:"description"`
|
|
||||||
Focus string `json:"focus"`
|
|
||||||
}{
|
|
||||||
Description: description,
|
|
||||||
Focus: focus,
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := json.Marshal(form)
|
|
||||||
if err != nil {
|
|
||||||
return model.Attachment{}, fmt.Errorf("unable to marshal the form: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
requestBody := bytes.NewBuffer(data)
|
|
||||||
url := g.Authentication.Instance + baseMediaPath + "/" + mediaAttachmentID
|
|
||||||
|
|
||||||
var updatedMediaAttachment model.Attachment
|
|
||||||
|
|
||||||
params := requestParameters{
|
|
||||||
httpMethod: http.MethodPut,
|
|
||||||
url: url,
|
|
||||||
requestBody: requestBody,
|
|
||||||
contentType: applicationJSON,
|
|
||||||
output: &updatedMediaAttachment,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := g.sendRequest(params); err != nil {
|
|
||||||
return model.Attachment{}, fmt.Errorf(
|
|
||||||
"received an error after sending the request to update the media attachment: %w",
|
|
||||||
err,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return updatedMediaAttachment, nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -50,7 +50,6 @@ type CreateStatusForm struct {
|
||||||
Poll *CreateStatusPollForm `json:"poll,omitempty"`
|
Poll *CreateStatusPollForm `json:"poll,omitempty"`
|
||||||
ContentType model.StatusContentType `json:"content_type"`
|
ContentType model.StatusContentType `json:"content_type"`
|
||||||
Visibility model.StatusVisibility `json:"visibility"`
|
Visibility model.StatusVisibility `json:"visibility"`
|
||||||
AttachmentIDs []string `json:"media_ids,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type CreateStatusPollForm struct {
|
type CreateStatusPollForm struct {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package executor
|
package executor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
||||||
|
@ -22,7 +21,6 @@ func (c *CreateExecutor) Execute() error {
|
||||||
funcMap := map[string]func(*client.Client) error{
|
funcMap := map[string]func(*client.Client) error{
|
||||||
resourceList: c.createList,
|
resourceList: c.createList,
|
||||||
resourceStatus: c.createStatus,
|
resourceStatus: c.createStatus,
|
||||||
resourceMediaAttachment: c.createMediaAttachment,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
doFunc, ok := funcMap[c.resourceType]
|
doFunc, ok := funcMap[c.resourceType]
|
||||||
|
@ -68,94 +66,20 @@ func (c *CreateExecutor) createStatus(gtsClient *client.Client) error {
|
||||||
sensitive bool
|
sensitive bool
|
||||||
)
|
)
|
||||||
|
|
||||||
attachmentIDs := []string(c.attachmentIDs)
|
|
||||||
|
|
||||||
if !c.mediaFiles.Empty() {
|
|
||||||
descriptionsExists := false
|
|
||||||
focusValuesExists := false
|
|
||||||
expectedLength := len(c.mediaFiles)
|
|
||||||
mediaDescriptions := make([]string, expectedLength)
|
|
||||||
|
|
||||||
if !c.mediaDescriptions.Empty() {
|
|
||||||
descriptionsExists = true
|
|
||||||
|
|
||||||
if !c.mediaDescriptions.ExpectedLength(expectedLength) {
|
|
||||||
return errors.New("the number of media descriptions does not match the number of media files")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !c.mediaFocusValues.Empty() {
|
|
||||||
focusValuesExists = true
|
|
||||||
|
|
||||||
if !c.mediaFocusValues.ExpectedLength(expectedLength) {
|
|
||||||
return errors.New("the number of media focus values does not match the number of media files")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if descriptionsExists {
|
|
||||||
for ind := 0; ind < expectedLength; ind++ {
|
|
||||||
content, err := utilities.ReadContents(c.mediaDescriptions[ind])
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to read the contents from %s: %w", c.mediaDescriptions[ind], err)
|
|
||||||
}
|
|
||||||
|
|
||||||
mediaDescriptions[ind] = content
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for ind := 0; ind < expectedLength; ind++ {
|
|
||||||
var (
|
|
||||||
mediaFile string
|
|
||||||
description string
|
|
||||||
focus string
|
|
||||||
)
|
|
||||||
|
|
||||||
mediaFile = c.mediaFiles[ind]
|
|
||||||
|
|
||||||
if descriptionsExists {
|
|
||||||
description = mediaDescriptions[ind]
|
|
||||||
}
|
|
||||||
|
|
||||||
if focusValuesExists {
|
|
||||||
focus = c.mediaFocusValues[ind]
|
|
||||||
}
|
|
||||||
|
|
||||||
attachment, err := gtsClient.CreateMediaAttachment(
|
|
||||||
mediaFile,
|
|
||||||
description,
|
|
||||||
focus,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to create the media attachment for %s: %w", mediaFile, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
attachmentIDs = append(attachmentIDs, attachment.ID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case c.content != "":
|
case c.content != "":
|
||||||
content = c.content
|
content = c.content
|
||||||
case c.fromFile != "":
|
case c.fromFile != "":
|
||||||
content, err = utilities.ReadTextFile(c.fromFile)
|
content, err = utilities.ReadFile(c.fromFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to get the status contents from %q: %w", c.fromFile, err)
|
return fmt.Errorf("unable to get the status contents from %q: %w", c.fromFile, err)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
if len(attachmentIDs) == 0 {
|
|
||||||
// TODO: revisit this error type
|
|
||||||
return EmptyContentError{
|
return EmptyContentError{
|
||||||
ResourceType: resourceStatus,
|
ResourceType: resourceStatus,
|
||||||
Hint: "please use --" + flagContent + " or --" + flagFromFile,
|
Hint: "please use --" + flagContent + " or --" + flagFromFile,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
numAttachmentIDs := len(attachmentIDs)
|
|
||||||
|
|
||||||
if c.addPoll && numAttachmentIDs > 0 {
|
|
||||||
return fmt.Errorf("attaching media to a poll is not allowed")
|
|
||||||
}
|
|
||||||
|
|
||||||
preferences, err := gtsClient.GetUserPreferences()
|
preferences, err := gtsClient.GetUserPreferences()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -203,11 +127,6 @@ func (c *CreateExecutor) createStatus(gtsClient *client.Client) error {
|
||||||
Sensitive: sensitive,
|
Sensitive: sensitive,
|
||||||
Visibility: parsedVisibility,
|
Visibility: parsedVisibility,
|
||||||
Poll: nil,
|
Poll: nil,
|
||||||
AttachmentIDs: nil,
|
|
||||||
}
|
|
||||||
|
|
||||||
if numAttachmentIDs > 0 {
|
|
||||||
form.AttachmentIDs = attachmentIDs
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.addPoll {
|
if c.addPoll {
|
||||||
|
@ -234,56 +153,3 @@ func (c *CreateExecutor) createStatus(gtsClient *client.Client) error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CreateExecutor) createMediaAttachment(gtsClient *client.Client) error {
|
|
||||||
expectedNumValues := 1
|
|
||||||
if !c.mediaFiles.ExpectedLength(expectedNumValues) {
|
|
||||||
return fmt.Errorf(
|
|
||||||
"received an unexpected number of media files: want %d",
|
|
||||||
expectedNumValues,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
description := ""
|
|
||||||
if !c.mediaDescriptions.Empty() {
|
|
||||||
if !c.mediaDescriptions.ExpectedLength(expectedNumValues) {
|
|
||||||
return fmt.Errorf(
|
|
||||||
"received an unexpected number of media descriptions: want %d",
|
|
||||||
expectedNumValues,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
var err error
|
|
||||||
description, err = utilities.ReadContents(c.mediaDescriptions[0])
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf(
|
|
||||||
"unable to read the contents from %s: %w",
|
|
||||||
c.mediaDescriptions[0],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
focus := ""
|
|
||||||
if !c.mediaFocusValues.Empty() {
|
|
||||||
if !c.mediaFocusValues.ExpectedLength(expectedNumValues) {
|
|
||||||
return fmt.Errorf(
|
|
||||||
"received an unexpected number of media focus values: want %d",
|
|
||||||
expectedNumValues,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
focus = c.mediaFocusValues[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
attachment, err := gtsClient.CreateMediaAttachment(
|
|
||||||
c.mediaFiles[0],
|
|
||||||
description,
|
|
||||||
focus,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to create the media attachment: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.printer.PrintSuccess("Successfully created the media attachment with ID: " + attachment.ID)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -5,7 +5,6 @@ import (
|
||||||
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/model"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/model"
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/utilities"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (e *EditExecutor) Execute() error {
|
func (e *EditExecutor) Execute() error {
|
||||||
|
@ -15,7 +14,6 @@ func (e *EditExecutor) Execute() error {
|
||||||
|
|
||||||
funcMap := map[string]func(*client.Client) error{
|
funcMap := map[string]func(*client.Client) error{
|
||||||
resourceList: e.editList,
|
resourceList: e.editList,
|
||||||
resourceMediaAttachment: e.editMediaAttachment,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
doFunc, ok := funcMap[e.resourceType]
|
doFunc, ok := funcMap[e.resourceType]
|
||||||
|
@ -59,62 +57,8 @@ func (e *EditExecutor) editList(gtsClient *client.Client) error {
|
||||||
return fmt.Errorf("unable to update the list: %w", err)
|
return fmt.Errorf("unable to update the list: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
e.printer.PrintSuccess("Successfully edited the list.")
|
e.printer.PrintSuccess("Successfully updated the list.")
|
||||||
e.printer.PrintList(updatedList)
|
e.printer.PrintList(updatedList)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *EditExecutor) editMediaAttachment(gtsClient *client.Client) error {
|
|
||||||
expectedNumValues := 1
|
|
||||||
|
|
||||||
if !e.attachmentIDs.ExpectedLength(expectedNumValues) {
|
|
||||||
return fmt.Errorf(
|
|
||||||
"received an unexpected number of media attachment IDs: want %d",
|
|
||||||
expectedNumValues,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
attachment, err := gtsClient.GetMediaAttachment(e.attachmentIDs[0])
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to get the media attachment: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
description := attachment.Description
|
|
||||||
if !e.mediaDescriptions.Empty() {
|
|
||||||
if !e.mediaDescriptions.ExpectedLength(expectedNumValues) {
|
|
||||||
return fmt.Errorf(
|
|
||||||
"received an unexpected number of media descriptions: want %d",
|
|
||||||
expectedNumValues,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
var err error
|
|
||||||
description, err = utilities.ReadContents(e.mediaDescriptions[0])
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf(
|
|
||||||
"unable to read the contents from %s: %w",
|
|
||||||
e.mediaDescriptions[0],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
focus := fmt.Sprintf("%f,%f", attachment.Meta.Focus.X, attachment.Meta.Focus.Y)
|
|
||||||
if !e.mediaFocusValues.Empty() {
|
|
||||||
if !e.mediaFocusValues.ExpectedLength(expectedNumValues) {
|
|
||||||
return fmt.Errorf(
|
|
||||||
"received an unexpected number of media focus values: want %d",
|
|
||||||
expectedNumValues,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
focus = e.mediaFocusValues[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = gtsClient.UpdateMediaAttachment(attachment.ID, description, focus); err != nil {
|
|
||||||
return fmt.Errorf("unable to update the media attachment: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
e.printer.PrintSuccess("Successfully edited the media attachment.")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -121,7 +121,6 @@ type CreateExecutor struct {
|
||||||
printer *printer.Printer
|
printer *printer.Printer
|
||||||
config *config.Config
|
config *config.Config
|
||||||
addPoll bool
|
addPoll bool
|
||||||
attachmentIDs internalFlag.StringSliceValue
|
|
||||||
content string
|
content string
|
||||||
contentType string
|
contentType string
|
||||||
federated bool
|
federated bool
|
||||||
|
@ -133,9 +132,6 @@ type CreateExecutor struct {
|
||||||
language string
|
language string
|
||||||
listRepliesPolicy string
|
listRepliesPolicy string
|
||||||
listTitle string
|
listTitle string
|
||||||
mediaDescriptions internalFlag.StringSliceValue
|
|
||||||
mediaFocusValues internalFlag.StringSliceValue
|
|
||||||
mediaFiles internalFlag.StringSliceValue
|
|
||||||
pollAllowsMultipleChoices bool
|
pollAllowsMultipleChoices bool
|
||||||
pollExpiresIn internalFlag.TimeDurationValue
|
pollExpiresIn internalFlag.TimeDurationValue
|
||||||
pollHidesVoteCounts bool
|
pollHidesVoteCounts bool
|
||||||
|
@ -154,10 +150,6 @@ func NewCreateExecutor(
|
||||||
FlagSet: flag.NewFlagSet("create", flag.ExitOnError),
|
FlagSet: flag.NewFlagSet("create", flag.ExitOnError),
|
||||||
printer: printer,
|
printer: printer,
|
||||||
config: config,
|
config: config,
|
||||||
attachmentIDs: internalFlag.NewStringSliceValue(),
|
|
||||||
mediaDescriptions: internalFlag.NewStringSliceValue(),
|
|
||||||
mediaFocusValues: internalFlag.NewStringSliceValue(),
|
|
||||||
mediaFiles: internalFlag.NewStringSliceValue(),
|
|
||||||
pollExpiresIn: internalFlag.NewTimeDurationValue(),
|
pollExpiresIn: internalFlag.NewTimeDurationValue(),
|
||||||
pollOptions: internalFlag.NewStringSliceValue(),
|
pollOptions: internalFlag.NewStringSliceValue(),
|
||||||
sensitive: internalFlag.NewBoolPtrValue(),
|
sensitive: internalFlag.NewBoolPtrValue(),
|
||||||
|
@ -166,7 +158,6 @@ func NewCreateExecutor(
|
||||||
exe.Usage = usage.ExecutorUsageFunc("create", "Creates a specific resource", exe.FlagSet)
|
exe.Usage = usage.ExecutorUsageFunc("create", "Creates a specific resource", exe.FlagSet)
|
||||||
|
|
||||||
exe.BoolVar(&exe.addPoll, "add-poll", false, "Set to true to add a poll when creating a status")
|
exe.BoolVar(&exe.addPoll, "add-poll", false, "Set to true to add a poll when creating a status")
|
||||||
exe.Var(&exe.attachmentIDs, "attachment-id", "The ID of the media attachment")
|
|
||||||
exe.StringVar(&exe.content, "content", "", "The content of the created resource")
|
exe.StringVar(&exe.content, "content", "", "The content of the created resource")
|
||||||
exe.StringVar(&exe.contentType, "content-type", "plain", "The type that the contents should be parsed from (valid values are plain and markdown)")
|
exe.StringVar(&exe.contentType, "content-type", "plain", "The type that the contents should be parsed from (valid values are plain and markdown)")
|
||||||
exe.BoolVar(&exe.federated, "enable-federation", true, "Set to true to federate the status beyond the local timelines")
|
exe.BoolVar(&exe.federated, "enable-federation", true, "Set to true to federate the status beyond the local timelines")
|
||||||
|
@ -178,9 +169,6 @@ func NewCreateExecutor(
|
||||||
exe.StringVar(&exe.language, "language", "", "The ISO 639 language code for this status")
|
exe.StringVar(&exe.language, "language", "", "The ISO 639 language code for this status")
|
||||||
exe.StringVar(&exe.listRepliesPolicy, "list-replies-policy", "list", "The replies policy of the list")
|
exe.StringVar(&exe.listRepliesPolicy, "list-replies-policy", "list", "The replies policy of the list")
|
||||||
exe.StringVar(&exe.listTitle, "list-title", "", "The title of the list")
|
exe.StringVar(&exe.listTitle, "list-title", "", "The title of the list")
|
||||||
exe.Var(&exe.mediaDescriptions, "media-description", "The description of the media attachment which will be used as the alt-text")
|
|
||||||
exe.Var(&exe.mediaFocusValues, "media-focus", "The focus of the media file")
|
|
||||||
exe.Var(&exe.mediaFiles, "media-file", "The path to the media file")
|
|
||||||
exe.BoolVar(&exe.pollAllowsMultipleChoices, "poll-allows-multiple-choices", false, "Set to true to allow viewers to make multiple choices in the poll")
|
exe.BoolVar(&exe.pollAllowsMultipleChoices, "poll-allows-multiple-choices", false, "Set to true to allow viewers to make multiple choices in the poll")
|
||||||
exe.Var(&exe.pollExpiresIn, "poll-expires-in", "The duration in which the poll is open for")
|
exe.Var(&exe.pollExpiresIn, "poll-expires-in", "The duration in which the poll is open for")
|
||||||
exe.BoolVar(&exe.pollHidesVoteCounts, "poll-hides-vote-counts", false, "Set to true to hide the vote count until the poll is closed")
|
exe.BoolVar(&exe.pollHidesVoteCounts, "poll-hides-vote-counts", false, "Set to true to hide the vote count until the poll is closed")
|
||||||
|
@ -225,12 +213,9 @@ type EditExecutor struct {
|
||||||
*flag.FlagSet
|
*flag.FlagSet
|
||||||
printer *printer.Printer
|
printer *printer.Printer
|
||||||
config *config.Config
|
config *config.Config
|
||||||
attachmentIDs internalFlag.StringSliceValue
|
|
||||||
listID string
|
listID string
|
||||||
listTitle string
|
listTitle string
|
||||||
listRepliesPolicy string
|
listRepliesPolicy string
|
||||||
mediaDescriptions internalFlag.StringSliceValue
|
|
||||||
mediaFocusValues internalFlag.StringSliceValue
|
|
||||||
resourceType string
|
resourceType string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,19 +227,13 @@ func NewEditExecutor(
|
||||||
FlagSet: flag.NewFlagSet("edit", flag.ExitOnError),
|
FlagSet: flag.NewFlagSet("edit", flag.ExitOnError),
|
||||||
printer: printer,
|
printer: printer,
|
||||||
config: config,
|
config: config,
|
||||||
attachmentIDs: internalFlag.NewStringSliceValue(),
|
|
||||||
mediaDescriptions: internalFlag.NewStringSliceValue(),
|
|
||||||
mediaFocusValues: internalFlag.NewStringSliceValue(),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
exe.Usage = usage.ExecutorUsageFunc("edit", "Edit a specific resource", exe.FlagSet)
|
exe.Usage = usage.ExecutorUsageFunc("edit", "Edit a specific resource", exe.FlagSet)
|
||||||
|
|
||||||
exe.Var(&exe.attachmentIDs, "attachment-id", "The ID of the media attachment")
|
|
||||||
exe.StringVar(&exe.listID, "list-id", "", "The ID of the list in question")
|
exe.StringVar(&exe.listID, "list-id", "", "The ID of the list in question")
|
||||||
exe.StringVar(&exe.listTitle, "list-title", "", "The title of the list")
|
exe.StringVar(&exe.listTitle, "list-title", "", "The title of the list")
|
||||||
exe.StringVar(&exe.listRepliesPolicy, "list-replies-policy", "", "The replies policy of the list")
|
exe.StringVar(&exe.listRepliesPolicy, "list-replies-policy", "", "The replies policy of the list")
|
||||||
exe.Var(&exe.mediaDescriptions, "media-description", "The description of the media attachment which will be used as the alt-text")
|
|
||||||
exe.Var(&exe.mediaFocusValues, "media-focus", "The focus of the media file")
|
|
||||||
exe.StringVar(&exe.resourceType, "type", "", "The type of resource you want to action on (e.g. account, status)")
|
exe.StringVar(&exe.resourceType, "type", "", "The type of resource you want to action on (e.g. account, status)")
|
||||||
|
|
||||||
return &exe
|
return &exe
|
||||||
|
|
|
@ -5,20 +5,9 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const filePrefix string = "file@"
|
func ReadFile(path string) (string, error) {
|
||||||
|
|
||||||
func ReadContents(text string) (string, error) {
|
|
||||||
if !strings.HasPrefix(text, filePrefix) {
|
|
||||||
return text, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return ReadTextFile(strings.TrimPrefix(text, filePrefix))
|
|
||||||
}
|
|
||||||
|
|
||||||
func ReadTextFile(path string) (string, error) {
|
|
||||||
file, err := os.Open(path)
|
file, err := os.Open(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("unable to open %q: %w", path, err)
|
return "", fmt.Errorf("unable to open %q: %w", path, err)
|
||||||
|
|
|
@ -96,18 +96,6 @@
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The replies policy of the list"
|
"description": "The replies policy of the list"
|
||||||
},
|
},
|
||||||
"media-description": {
|
|
||||||
"type": "StringSliceValue",
|
|
||||||
"description": "The description of the media attachment which will be used as the alt-text"
|
|
||||||
},
|
|
||||||
"media-file": {
|
|
||||||
"type": "StringSliceValue",
|
|
||||||
"description": "The path to the media file"
|
|
||||||
},
|
|
||||||
"media-focus": {
|
|
||||||
"type": "StringSliceValue",
|
|
||||||
"description": "The focus of the media file"
|
|
||||||
},
|
|
||||||
"mute-duration": {
|
"mute-duration": {
|
||||||
"type": "TimeDurationValue",
|
"type": "TimeDurationValue",
|
||||||
"description": "Specify how long the mute should last for. To mute indefinitely, set this to 0s"
|
"description": "Specify how long the mute should last for. To mute indefinitely, set this to 0s"
|
||||||
|
@ -246,7 +234,6 @@
|
||||||
"additionalFields": [],
|
"additionalFields": [],
|
||||||
"flags": [
|
"flags": [
|
||||||
{ "flag": "add-poll", "default": "false" },
|
{ "flag": "add-poll", "default": "false" },
|
||||||
{ "flag": "attachment-id", "fieldName": "attachmentIDs" },
|
|
||||||
{ "flag": "content", "default": "" },
|
{ "flag": "content", "default": "" },
|
||||||
{ "flag": "content-type", "default": "plain" },
|
{ "flag": "content-type", "default": "plain" },
|
||||||
{ "flag": "enable-federation", "fieldName": "federated", "default": "true" },
|
{ "flag": "enable-federation", "fieldName": "federated", "default": "true" },
|
||||||
|
@ -258,9 +245,6 @@
|
||||||
{ "flag": "language", "default": "" },
|
{ "flag": "language", "default": "" },
|
||||||
{ "flag": "list-replies-policy", "default": "list" },
|
{ "flag": "list-replies-policy", "default": "list" },
|
||||||
{ "flag": "list-title", "default": "" },
|
{ "flag": "list-title", "default": "" },
|
||||||
{ "flag": "media-description", "fieldName": "mediaDescriptions" },
|
|
||||||
{ "flag": "media-focus", "fieldName": "mediaFocusValues" },
|
|
||||||
{ "flag": "media-file", "fieldName": "mediaFiles" },
|
|
||||||
{ "flag": "poll-allows-multiple-choices", "default": "false" },
|
{ "flag": "poll-allows-multiple-choices", "default": "false" },
|
||||||
{ "flag": "poll-expires-in" },
|
{ "flag": "poll-expires-in" },
|
||||||
{ "flag": "poll-hides-vote-counts", "default": "false" },
|
{ "flag": "poll-hides-vote-counts", "default": "false" },
|
||||||
|
@ -287,12 +271,9 @@
|
||||||
"edit": {
|
"edit": {
|
||||||
"additionalFields": [],
|
"additionalFields": [],
|
||||||
"flags": [
|
"flags": [
|
||||||
{ "flag": "attachment-id", "fieldName": "attachmentIDs" },
|
|
||||||
{ "flag": "list-id", "fieldName": "listID", "default": ""},
|
{ "flag": "list-id", "fieldName": "listID", "default": ""},
|
||||||
{ "flag": "list-title", "default": "" },
|
{ "flag": "list-title", "default": "" },
|
||||||
{ "flag": "list-replies-policy", "default": "" },
|
{ "flag": "list-replies-policy", "default": "" },
|
||||||
{ "flag": "media-description", "fieldName": "mediaDescriptions" },
|
|
||||||
{ "flag": "media-focus", "fieldName": "mediaFocusValues" },
|
|
||||||
{ "flag": "type", "fieldName": "resourceType", "default": "" }
|
{ "flag": "type", "fieldName": "resourceType", "default": "" }
|
||||||
],
|
],
|
||||||
"summary": "Edit a specific resource",
|
"summary": "Edit a specific resource",
|
||||||
|
|
Loading…
Reference in a new issue