Compare commits
2 commits
1361482dd4
...
310ecb0b39
Author | SHA1 | Date | |
---|---|---|---|
310ecb0b39 | |||
bec69011ac |
13 changed files with 492 additions and 142 deletions
|
@ -1,64 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
|
||||||
)
|
|
||||||
|
|
||||||
var instanceDetailsFormat = `INSTANCE:
|
|
||||||
%s - %s
|
|
||||||
|
|
||||||
DOMAIN:
|
|
||||||
%s
|
|
||||||
|
|
||||||
VERSION:
|
|
||||||
Running GoToSocial %s
|
|
||||||
|
|
||||||
CONTACT:
|
|
||||||
name: %s
|
|
||||||
username: %s
|
|
||||||
email: %s
|
|
||||||
`
|
|
||||||
|
|
||||||
type instanceCommand struct {
|
|
||||||
*flag.FlagSet
|
|
||||||
summary string
|
|
||||||
}
|
|
||||||
|
|
||||||
func newInstanceCommand(name, summary string) *instanceCommand {
|
|
||||||
command := instanceCommand{
|
|
||||||
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
|
||||||
summary: summary,
|
|
||||||
}
|
|
||||||
|
|
||||||
command.Usage = commandUsageFunc(command.Name(), command.summary, command.FlagSet)
|
|
||||||
|
|
||||||
return &command
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *instanceCommand) Execute() error {
|
|
||||||
gtsClient, err := client.NewClientFromConfig()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to create the GoToSocial client; %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
instance, err := gtsClient.GetInstance()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to retrieve the instance details; %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf(
|
|
||||||
instanceDetailsFormat,
|
|
||||||
instance.Title,
|
|
||||||
instance.Description,
|
|
||||||
instance.Domain,
|
|
||||||
instance.Version,
|
|
||||||
instance.Contact.Account.DisplayName,
|
|
||||||
instance.Contact.Account.Username,
|
|
||||||
instance.Contact.Email,
|
|
||||||
)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -17,12 +17,13 @@ import (
|
||||||
|
|
||||||
type loginCommand struct {
|
type loginCommand struct {
|
||||||
*flag.FlagSet
|
*flag.FlagSet
|
||||||
summary string
|
|
||||||
instance string
|
instance string
|
||||||
}
|
}
|
||||||
|
|
||||||
var errEmptyAccessToken = errors.New("received an empty access token")
|
var (
|
||||||
var errInstanceNotSet = errors.New("the instance flag is not set")
|
errEmptyAccessToken = errors.New("received an empty access token")
|
||||||
|
errInstanceNotSet = errors.New("the instance flag is not set")
|
||||||
|
)
|
||||||
|
|
||||||
var consentMessageFormat = `
|
var consentMessageFormat = `
|
||||||
You'll need to sign into your GoToSocial's consent page in order to generate the out-of-band token to continue with
|
You'll need to sign into your GoToSocial's consent page in order to generate the out-of-band token to continue with
|
||||||
|
@ -37,13 +38,13 @@ Once you have the code please copy and paste it below.
|
||||||
|
|
||||||
func newLoginCommand(name, summary string) *loginCommand {
|
func newLoginCommand(name, summary string) *loginCommand {
|
||||||
command := loginCommand{
|
command := loginCommand{
|
||||||
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
||||||
summary: summary,
|
instance: "",
|
||||||
}
|
}
|
||||||
|
|
||||||
command.StringVar(&command.instance, "instance", "", "specify the instance that you want to login to.")
|
command.StringVar(&command.instance, "instance", "", "specify the instance that you want to login to.")
|
||||||
|
|
||||||
command.Usage = commandUsageFunc(command.Name(), command.summary, command.FlagSet)
|
command.Usage = commandUsageFunc(name, summary, command.FlagSet)
|
||||||
|
|
||||||
return &command
|
return &command
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,23 +8,32 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
login string = "login"
|
|
||||||
version string = "version"
|
|
||||||
instance string = "instance"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Executor interface {
|
type Executor interface {
|
||||||
Parse([]string) error
|
|
||||||
Name() string
|
Name() string
|
||||||
|
Parse([]string) error
|
||||||
Execute() error
|
Execute() error
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
if err := run(); err != nil {
|
||||||
|
fmt.Printf("ERROR: %v.\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func run() error {
|
||||||
|
const (
|
||||||
|
login string = "login"
|
||||||
|
version string = "version"
|
||||||
|
show string = "show"
|
||||||
|
switchAccount string = "switch"
|
||||||
|
)
|
||||||
|
|
||||||
summaries := map[string]string{
|
summaries := map[string]string{
|
||||||
login: "login to an account on GoToSocial",
|
login: "login to an account on GoToSocial",
|
||||||
version: "print the application's version and build information",
|
version: "print the application's version and build information",
|
||||||
instance: "print the instance information",
|
show: "print details about a specified resource",
|
||||||
|
switchAccount: "switch to an account",
|
||||||
}
|
}
|
||||||
|
|
||||||
flag.Usage = enbasUsageFunc(summaries)
|
flag.Usage = enbasUsageFunc(summaries)
|
||||||
|
@ -33,7 +42,8 @@ func main() {
|
||||||
|
|
||||||
if flag.NArg() < 1 {
|
if flag.NArg() < 1 {
|
||||||
flag.Usage()
|
flag.Usage()
|
||||||
os.Exit(0)
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
subcommand := flag.Arg(0)
|
subcommand := flag.Arg(0)
|
||||||
|
@ -46,33 +56,46 @@ func main() {
|
||||||
executor = newLoginCommand(login, summaries[login])
|
executor = newLoginCommand(login, summaries[login])
|
||||||
case version:
|
case version:
|
||||||
executor = newVersionCommand(version, summaries[version])
|
executor = newVersionCommand(version, summaries[version])
|
||||||
case instance:
|
case show:
|
||||||
executor = newInstanceCommand(instance, summaries[instance])
|
executor = newShowCommand(show, summaries[show])
|
||||||
|
case switchAccount:
|
||||||
|
executor = newSwitchCommand(switchAccount, summaries[switchAccount])
|
||||||
default:
|
default:
|
||||||
fmt.Printf("ERROR: Unknown subcommand: %s\n", subcommand)
|
|
||||||
flag.Usage()
|
flag.Usage()
|
||||||
os.Exit(1)
|
return fmt.Errorf("unknown subcommand %q", subcommand)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := executor.Parse(args); err != nil {
|
if err := executor.Parse(args); err != nil {
|
||||||
fmt.Printf("ERROR: Unable to parse the command line flags; %v.\n", err)
|
return fmt.Errorf("unable to parse the command line flags; %w", err)
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := executor.Execute(); err != nil {
|
if err := executor.Execute(); err != nil {
|
||||||
fmt.Printf("ERROR: Unable to run %q; %v.\n", executor.Name(), err)
|
return fmt.Errorf("received an error after executing %q; %w", executor.Name(), err)
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func commandUsageFunc(name, summary string, flagset *flag.FlagSet) func() {
|
func commandUsageFunc(name, summary string, flagset *flag.FlagSet) func() {
|
||||||
return func() {
|
return func() {
|
||||||
var builder strings.Builder
|
var builder strings.Builder
|
||||||
|
|
||||||
fmt.Fprintf(&builder, "SUMMARY:\n %s - %s\n\nUSAGE:\n enbas %s [flags]\n\nFLAGS:", name, summary, name)
|
fmt.Fprintf(
|
||||||
|
&builder,
|
||||||
|
"SUMMARY:\n %s - %s\n\nUSAGE:\n enbas %s [flags]\n\nFLAGS:",
|
||||||
|
name,
|
||||||
|
summary,
|
||||||
|
name,
|
||||||
|
)
|
||||||
|
|
||||||
flagset.VisitAll(func(f *flag.Flag) {
|
flagset.VisitAll(func(f *flag.Flag) {
|
||||||
fmt.Fprintf(&builder, "\n -%s, --%s\n %s", f.Name, f.Name, f.Usage)
|
fmt.Fprintf(
|
||||||
|
&builder,
|
||||||
|
"\n -%s, --%s\n %s",
|
||||||
|
f.Name,
|
||||||
|
f.Name,
|
||||||
|
f.Usage,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
builder.WriteString("\n")
|
builder.WriteString("\n")
|
||||||
|
@ -97,21 +120,21 @@ func enbasUsageFunc(summaries map[string]string) func() {
|
||||||
return func() {
|
return func() {
|
||||||
var builder strings.Builder
|
var builder strings.Builder
|
||||||
|
|
||||||
builder.WriteString("SUMMARY:\n enbas - A GoToSocial client for the terminal.\n\n")
|
builder.WriteString("SUMMARY:\n enbas - A GoToSocial client for the terminal.\n\n")
|
||||||
|
|
||||||
//if binaryVersion != "" {
|
if binaryVersion != "" {
|
||||||
// builder.WriteString("VERSION:\n " + binaryVersion + "\n\n")
|
builder.WriteString("VERSION:\n " + binaryVersion + "\n\n")
|
||||||
//}
|
|
||||||
|
|
||||||
builder.WriteString("USAGE:\n enbas [flags]\n enbas [command]\n\nCOMMANDS:")
|
|
||||||
|
|
||||||
for _, cmd := range cmds {
|
|
||||||
fmt.Fprintf(&builder, "\n %s\t%s", cmd, summaries[cmd])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.WriteString("\n\nFLAGS:\n -help, --help\n print the help message\n")
|
builder.WriteString("USAGE:\n enbas [flags]\n enbas [command]\n\nCOMMANDS:")
|
||||||
|
|
||||||
|
for _, cmd := range cmds {
|
||||||
|
fmt.Fprintf(&builder, "\n %s\t%s", cmd, summaries[cmd])
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.WriteString("\n\nFLAGS:\n -help, --help\n print the help message\n")
|
||||||
flag.VisitAll(func(f *flag.Flag) {
|
flag.VisitAll(func(f *flag.Flag) {
|
||||||
fmt.Fprintf(&builder, "\n -%s, --%s\n %s\n", f.Name, f.Name, f.Usage)
|
fmt.Fprintf(&builder, "\n -%s, --%s\n %s\n", f.Name, f.Name, f.Usage)
|
||||||
})
|
})
|
||||||
|
|
||||||
builder.WriteString("\nUse \"enbas [command] --help\" for more information about a command.\n")
|
builder.WriteString("\nUse \"enbas [command] --help\" for more information about a command.\n")
|
||||||
|
|
155
cmd/enbas/show.go
Normal file
155
cmd/enbas/show.go
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
||||||
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
var instanceDetailsFormat = `INSTANCE:
|
||||||
|
%s - %s
|
||||||
|
|
||||||
|
DOMAIN:
|
||||||
|
%s
|
||||||
|
|
||||||
|
VERSION:
|
||||||
|
Running GoToSocial %s
|
||||||
|
|
||||||
|
CONTACT:
|
||||||
|
name: %s
|
||||||
|
username: %s
|
||||||
|
email: %s
|
||||||
|
`
|
||||||
|
|
||||||
|
var accountDetailsFormat = `
|
||||||
|
%s (@%s)
|
||||||
|
|
||||||
|
ACCOUNT ID:
|
||||||
|
%s
|
||||||
|
|
||||||
|
CREATED AT:
|
||||||
|
%s
|
||||||
|
|
||||||
|
STATS:
|
||||||
|
Followers: %d
|
||||||
|
Following: %d
|
||||||
|
Statuses: %d
|
||||||
|
|
||||||
|
BIOGRAPHY:
|
||||||
|
%s
|
||||||
|
|
||||||
|
METADATA: %s
|
||||||
|
|
||||||
|
ACCOUNT URL:
|
||||||
|
%s
|
||||||
|
`
|
||||||
|
|
||||||
|
type showCommand struct {
|
||||||
|
*flag.FlagSet
|
||||||
|
targetType string
|
||||||
|
account string
|
||||||
|
myAccount bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func newShowCommand(name, summary string) *showCommand {
|
||||||
|
command := showCommand{
|
||||||
|
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
||||||
|
targetType: "",
|
||||||
|
myAccount: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
command.StringVar(&command.targetType, "type", "", "specify the type of resource to display")
|
||||||
|
command.StringVar(&command.account, "account", "", "specify the account URI to lookup")
|
||||||
|
command.BoolVar(&command.myAccount, "my-account", false, "set to true to lookup your account")
|
||||||
|
command.Usage = commandUsageFunc(name, summary, command.FlagSet)
|
||||||
|
|
||||||
|
return &command
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *showCommand) Execute() error {
|
||||||
|
gtsClient, err := client.NewClientFromConfig()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to create the GoToSocial client; %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
funcMap := map[string]func(*client.Client) error{
|
||||||
|
"instance": c.showInstance,
|
||||||
|
"account": c.showAccount,
|
||||||
|
}
|
||||||
|
|
||||||
|
doFunc, ok := funcMap[c.targetType]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("unsupported type %q", c.targetType)
|
||||||
|
}
|
||||||
|
|
||||||
|
return doFunc(gtsClient)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *showCommand) showInstance(gts *client.Client) error {
|
||||||
|
instance, err := gts.GetInstance()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to retrieve the instance details; %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf(
|
||||||
|
instanceDetailsFormat,
|
||||||
|
instance.Title,
|
||||||
|
instance.Description,
|
||||||
|
instance.Domain,
|
||||||
|
instance.Version,
|
||||||
|
instance.Contact.Account.DisplayName,
|
||||||
|
instance.Contact.Account.Username,
|
||||||
|
instance.Contact.Email,
|
||||||
|
)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *showCommand) showAccount(gts *client.Client) error {
|
||||||
|
var accountURI string
|
||||||
|
|
||||||
|
if c.myAccount {
|
||||||
|
authConfig, err := config.NewAuthenticationConfigFromFile()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to retrieve the authentication configuration; %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
accountURI = authConfig.CurrentAccount
|
||||||
|
} else {
|
||||||
|
if c.account == "" {
|
||||||
|
return errors.New("the account flag is not set")
|
||||||
|
}
|
||||||
|
|
||||||
|
accountURI = c.account
|
||||||
|
}
|
||||||
|
|
||||||
|
account, err := gts.GetAccount(accountURI)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to retrieve the account details; %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
metadata := ""
|
||||||
|
|
||||||
|
for _, field := range account.Fields {
|
||||||
|
metadata += fmt.Sprintf("\n %s: %s", field.Name, field.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf(
|
||||||
|
accountDetailsFormat,
|
||||||
|
account.DisplayName,
|
||||||
|
account.Username,
|
||||||
|
account.ID,
|
||||||
|
account.CreatedAt,
|
||||||
|
account.FollowersCount,
|
||||||
|
account.FollowingCount,
|
||||||
|
account.StatusCount,
|
||||||
|
account.Note,
|
||||||
|
metadata,
|
||||||
|
account.URL,
|
||||||
|
)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
35
cmd/enbas/switch.go
Normal file
35
cmd/enbas/switch.go
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
type switchCommand struct {
|
||||||
|
*flag.FlagSet
|
||||||
|
toAccount string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSwitchCommand(name, summary string) *switchCommand {
|
||||||
|
command := switchCommand{
|
||||||
|
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
||||||
|
toAccount: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
command.StringVar(&command.toAccount, "to-account", "", "the account to switch to")
|
||||||
|
command.Usage = commandUsageFunc(name, summary, command.FlagSet)
|
||||||
|
|
||||||
|
return &command
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *switchCommand) Execute() error {
|
||||||
|
if err := config.UpdateCurrentAccount(c.toAccount); err != nil {
|
||||||
|
return fmt.Errorf("unable to switch accounts; %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("The current account is now set to %q.\n", c.toAccount)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -16,7 +16,6 @@ var (
|
||||||
|
|
||||||
type versionCommand struct {
|
type versionCommand struct {
|
||||||
*flag.FlagSet
|
*flag.FlagSet
|
||||||
summary string
|
|
||||||
showFullVersion bool
|
showFullVersion bool
|
||||||
binaryVersion string
|
binaryVersion string
|
||||||
buildTime string
|
buildTime string
|
||||||
|
@ -31,13 +30,12 @@ func newVersionCommand(name, summary string) *versionCommand {
|
||||||
buildTime: buildTime,
|
buildTime: buildTime,
|
||||||
goVersion: goVersion,
|
goVersion: goVersion,
|
||||||
gitCommit: gitCommit,
|
gitCommit: gitCommit,
|
||||||
summary: summary,
|
|
||||||
showFullVersion: false,
|
showFullVersion: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
command.BoolVar(&command.showFullVersion, "full", false, "prints the full build information")
|
command.BoolVar(&command.showFullVersion, "full", false, "prints the full build information")
|
||||||
|
|
||||||
command.Usage = commandUsageFunc(command.Name(), command.summary, command.FlagSet)
|
command.Usage = commandUsageFunc(name, summary, command.FlagSet)
|
||||||
|
|
||||||
return &command
|
return &command
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,6 +85,27 @@ func (g *Client) GetInstance() (model.InstanceV2, error) {
|
||||||
return instance, nil
|
return instance, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g *Client) GetAccount(accountURI string) (model.Account, error) {
|
||||||
|
path := "/api/v1/accounts/lookup"
|
||||||
|
url := g.Authentication.Instance + path + "?acct=" + accountURI
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), g.Timeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
request, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return model.Account{}, fmt.Errorf("unable to create the HTTP request, %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var account model.Account
|
||||||
|
|
||||||
|
if err := g.sendRequest(request, &account); err != nil {
|
||||||
|
return model.Account{}, fmt.Errorf("received an error after sending the request to get the account information; %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return account, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (g *Client) sendRequest(request *http.Request, object any) error {
|
func (g *Client) sendRequest(request *http.Request, object any) error {
|
||||||
request.Header.Set("Content-Type", "application/json; charset=utf-8")
|
request.Header.Set("Content-Type", "application/json; charset=utf-8")
|
||||||
request.Header.Set("Accept", "application/json; charset=utf-8")
|
request.Header.Set("Accept", "application/json; charset=utf-8")
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal"
|
||||||
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
type RegisterRequest struct {
|
type RegisterRequest struct {
|
||||||
|
@ -17,16 +18,6 @@ type RegisterRequest struct {
|
||||||
Website string `json:"website"`
|
Website string `json:"website"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type RegisterResponse struct {
|
|
||||||
ClientID string `json:"client_id"`
|
|
||||||
ClientSecret string `json:"client_secret"`
|
|
||||||
ID string `json:"id"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
RedirectUri string `json:"redirect_uri"`
|
|
||||||
VapidKey string `json:"vapid_key"`
|
|
||||||
Website string `json:"website"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *Client) Register() error {
|
func (g *Client) Register() error {
|
||||||
params := RegisterRequest{
|
params := RegisterRequest{
|
||||||
ClientName: internal.ApplicationName,
|
ClientName: internal.ApplicationName,
|
||||||
|
@ -53,14 +44,14 @@ func (g *Client) Register() error {
|
||||||
return fmt.Errorf("unable to create the HTTP request; %w", err)
|
return fmt.Errorf("unable to create the HTTP request; %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var registerResponse RegisterResponse
|
var app model.Application
|
||||||
|
|
||||||
if err := g.sendRequest(request, ®isterResponse); err != nil {
|
if err := g.sendRequest(request, &app); err != nil {
|
||||||
return fmt.Errorf("received an error after sending the registration request; %w", err)
|
return fmt.Errorf("received an error after sending the registration request; %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
g.Authentication.ClientID = registerResponse.ClientID
|
g.Authentication.ClientID = app.ClientID
|
||||||
g.Authentication.ClientSecret = registerResponse.ClientSecret
|
g.Authentication.ClientSecret = app.ClientSecret
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ func SaveAuthentication(username string, authentication Authentication) (string,
|
||||||
return "", fmt.Errorf("unable to ensure the configuration directory; %w", err)
|
return "", fmt.Errorf("unable to ensure the configuration directory; %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var config AuthenticationConfig
|
var authConfig AuthenticationConfig
|
||||||
|
|
||||||
filepath := authenticationConfigFile()
|
filepath := authenticationConfigFile()
|
||||||
|
|
||||||
|
@ -37,9 +37,9 @@ func SaveAuthentication(username string, authentication Authentication) (string,
|
||||||
return "", fmt.Errorf("unknown error received when running stat on %s; %w", filepath, err)
|
return "", fmt.Errorf("unknown error received when running stat on %s; %w", filepath, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
config.Authentications = make(map[string]Authentication)
|
authConfig.Authentications = make(map[string]Authentication)
|
||||||
} else {
|
} else {
|
||||||
config, err = NewAuthenticationConfigFromFile()
|
authConfig, err = NewAuthenticationConfigFromFile()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("unable to retrieve the existing authentication configuration; %w", err)
|
return "", fmt.Errorf("unable to retrieve the existing authentication configuration; %w", err)
|
||||||
}
|
}
|
||||||
|
@ -55,17 +55,12 @@ func SaveAuthentication(username string, authentication Authentication) (string,
|
||||||
|
|
||||||
authenticationName := username + "@" + instance
|
authenticationName := username + "@" + instance
|
||||||
|
|
||||||
config.CurrentAccount = authenticationName
|
authConfig.CurrentAccount = authenticationName
|
||||||
|
|
||||||
config.Authentications[authenticationName] = authentication
|
authConfig.Authentications[authenticationName] = authentication
|
||||||
|
|
||||||
file, err := os.Create(authenticationConfigFile())
|
if err := saveAuthenticationFile(authConfig); err != nil {
|
||||||
if err != nil {
|
return "", fmt.Errorf("unable to save the authentication configuration to file; %w", err)
|
||||||
return "", fmt.Errorf("unable to open the config file; %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := json.NewEncoder(file).Encode(config); err != nil {
|
|
||||||
return "", fmt.Errorf("unable to save the JSON data to the authentication config file; %w", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return authenticationName, nil
|
return authenticationName, nil
|
||||||
|
@ -80,13 +75,32 @@ func NewAuthenticationConfigFromFile() (AuthenticationConfig, error) {
|
||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
|
|
||||||
var config AuthenticationConfig
|
var authConfig AuthenticationConfig
|
||||||
|
|
||||||
if err := json.NewDecoder(file).Decode(&config); err != nil {
|
if err := json.NewDecoder(file).Decode(&authConfig); err != nil {
|
||||||
return AuthenticationConfig{}, fmt.Errorf("unable to decode the JSON data; %w", err)
|
return AuthenticationConfig{}, fmt.Errorf("unable to decode the JSON data; %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return config, nil
|
return authConfig, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateCurrentAccount(account string) error {
|
||||||
|
authConfig, err := NewAuthenticationConfigFromFile()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to retrieve the existing authentication configuration; %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := authConfig.Authentications[account]; !ok {
|
||||||
|
return fmt.Errorf("account %s is not found", account)
|
||||||
|
}
|
||||||
|
|
||||||
|
authConfig.CurrentAccount = account
|
||||||
|
|
||||||
|
if err := saveAuthenticationFile(authConfig); err != nil {
|
||||||
|
return fmt.Errorf("unable to save the authentication configuration to file; %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func authenticationConfigFile() string {
|
func authenticationConfigFile() string {
|
||||||
|
@ -117,3 +131,21 @@ func ensureConfigDir() error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func saveAuthenticationFile(authConfig AuthenticationConfig) error {
|
||||||
|
file, err := os.Create(authenticationConfigFile())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to open the config file; %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
encoder := json.NewEncoder(file)
|
||||||
|
encoder.SetIndent("", " ")
|
||||||
|
|
||||||
|
if err := encoder.Encode(authConfig); err != nil {
|
||||||
|
return fmt.Errorf("unable to save the JSON data to the authentication config file; %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -29,14 +29,6 @@ type Account struct {
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Emoji struct {
|
|
||||||
Category string `json:"category"`
|
|
||||||
Shortcode string `json:"shortcode"`
|
|
||||||
StaticURL string `json:"static_url"`
|
|
||||||
URL string `json:"url"`
|
|
||||||
VisibleInPicker bool `json:"visible_in_picker"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type AccountRole struct {
|
type AccountRole struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
}
|
}
|
||||||
|
|
11
internal/model/application.go
Normal file
11
internal/model/application.go
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
package model
|
||||||
|
|
||||||
|
type Application struct {
|
||||||
|
ClientID string `json:"client_id"`
|
||||||
|
ClientSecret string `json:"client_secret"`
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
RedirectUri string `json:"redirect_uri"`
|
||||||
|
VapidKey string `json:"vapid_key"`
|
||||||
|
Website string `json:"website"`
|
||||||
|
}
|
9
internal/model/emoji.go
Normal file
9
internal/model/emoji.go
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
package model
|
||||||
|
|
||||||
|
type Emoji struct {
|
||||||
|
Category string `json:"category"`
|
||||||
|
Shortcode string `json:"shortcode"`
|
||||||
|
StaticURL string `json:"static_url"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
VisibleInPicker bool `json:"visible_in_picker"`
|
||||||
|
}
|
146
internal/model/status.go
Normal file
146
internal/model/status.go
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
package model
|
||||||
|
|
||||||
|
type Status struct {
|
||||||
|
Account Account `json:"account"`
|
||||||
|
Application Application `json:"application"`
|
||||||
|
Bookmarked bool `json:"bookmarked"`
|
||||||
|
Card Card `json:"card"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
CreatedAt string `json:"created_at"`
|
||||||
|
Emojis []Emoji `json:"emojis"`
|
||||||
|
Favourited bool `json:"favourited"`
|
||||||
|
FavouritesCount int `json:"favourites_count"`
|
||||||
|
ID string `json:"id"`
|
||||||
|
InReplyToAccountID string `json:"in_reply_to_account_id"`
|
||||||
|
InReplyToID string `json:"in_reply_to_id"`
|
||||||
|
Language string `json:"language"`
|
||||||
|
MediaAttachments []Attachment `json:"media_attachments"`
|
||||||
|
Mentions []Mention `json:"mentions"`
|
||||||
|
Muted bool `json:"muted"`
|
||||||
|
Pinned bool `json:"pinned"`
|
||||||
|
Poll Poll `json:"poll"`
|
||||||
|
Reblog StatusReblogged `json:"reblog"`
|
||||||
|
Reblogged bool `json:"reblogged"`
|
||||||
|
RebloggsCount int `json:"reblogs_count"`
|
||||||
|
RepliesCount int `json:"replies_count"`
|
||||||
|
Sensitive bool `json:"sensitive"`
|
||||||
|
SpolierText string `json:"spoiler_text"`
|
||||||
|
Tags []Tag `json:"tags"`
|
||||||
|
Text string `json:"text"`
|
||||||
|
URI string `json:"uri"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
Visibility string `json:"visibility"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Card struct {
|
||||||
|
AuthorName string `json:"author_name"`
|
||||||
|
AuthorURL string `json:"author_url"`
|
||||||
|
Blurhash string `json:"blurhash"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
EmbedURL string `json:"embed_url"`
|
||||||
|
HTML string `json:"html"`
|
||||||
|
Image string `json:"image"`
|
||||||
|
ProviderName string `json:"provider_name"`
|
||||||
|
ProviderURL string `json:"provider_url"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
Height int `json:"height"`
|
||||||
|
Width int `json:"width"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Mention struct {
|
||||||
|
Acct string `json:"acct"`
|
||||||
|
ID string `json:"id"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Poll struct {
|
||||||
|
Emojis []Emoji `json:"emojis"`
|
||||||
|
Expired bool `json:"expired"`
|
||||||
|
Voted bool `json:"voted"`
|
||||||
|
Multiple bool `json:"multiple"`
|
||||||
|
ExpiredAt string `json:"expires_at"`
|
||||||
|
ID string `json:"id"`
|
||||||
|
OwnVotes []int `json:"own_votes"`
|
||||||
|
VotersCount int `json:"voters_count"`
|
||||||
|
VotesCount int `json:"votes_count"`
|
||||||
|
Options []PollOption `json:"options"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PollOption struct {
|
||||||
|
Title string `json:"title"`
|
||||||
|
VotesCount string `json:"votes_count"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type StatusReblogged struct {
|
||||||
|
Account Account `json:"account"`
|
||||||
|
Application Application `json:"application"`
|
||||||
|
Bookmarked bool `json:"bookmarked"`
|
||||||
|
Card Card `json:"card"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
CreatedAt string `json:"created_at"`
|
||||||
|
Emojis []Emoji `json:"emojis"`
|
||||||
|
Favourited bool `json:"favourited"`
|
||||||
|
FavouritesCount int `json:"favourites_count"`
|
||||||
|
ID string `json:"id"`
|
||||||
|
InReplyToAccountID string `json:"in_reply_to_account_id"`
|
||||||
|
InReplyToID string `json:"in_reply_to_id"`
|
||||||
|
Language string `json:"language"`
|
||||||
|
MediaAttachments []Attachment `json:"media_attachments"`
|
||||||
|
Mentions []Mention `json:"mentions"`
|
||||||
|
Muted bool `json:"muted"`
|
||||||
|
Pinned bool `json:"pinned"`
|
||||||
|
Poll Poll `json:"poll"`
|
||||||
|
Reblogged bool `json:"reblogged"`
|
||||||
|
RebloggsCount int `json:"reblogs_count"`
|
||||||
|
RepliesCount int `json:"replies_count"`
|
||||||
|
Sensitive bool `json:"sensitive"`
|
||||||
|
SpolierText string `json:"spoiler_text"`
|
||||||
|
Tags []Tag `json:"tags"`
|
||||||
|
Text string `json:"text"`
|
||||||
|
URI string `json:"uri"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
Visibility string `json:"visibility"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Tag struct {
|
||||||
|
History []any `json:"history"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Attachment struct {
|
||||||
|
Meta MediaMeta `json:"meta"`
|
||||||
|
Blurhash string `json:"blurhash"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
ID string `json:"id"`
|
||||||
|
PreviewRemoteURL string `json:"preview_remote_url"`
|
||||||
|
PreviewURL string `json:"preview_url"`
|
||||||
|
RemoteURL string `json:"remote_url"`
|
||||||
|
TextURL string `json:"text_url"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MediaMeta struct {
|
||||||
|
Focus MediaFocus `json:"focus"`
|
||||||
|
Original MediaDimensions `json:"original"`
|
||||||
|
Small MediaDimensions `json:"small"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MediaFocus struct {
|
||||||
|
X float64 `json:"x"`
|
||||||
|
Y float64 `json:"y"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MediaDimensions struct {
|
||||||
|
Aspect float64 `json:"aspect"`
|
||||||
|
Bitrate int `json:"bitrate"`
|
||||||
|
Duration float64 `json:"duration"`
|
||||||
|
FrameRate string `json:"frame_rate"`
|
||||||
|
Size string `json:"size"`
|
||||||
|
Height int `json:"height"`
|
||||||
|
Width int `json:"width"`
|
||||||
|
}
|
Loading…
Reference in a new issue