1 Commits

Author SHA1 Message Date
f045e36119 Switch to Cobra
Also:
- Fixed the Makefile
- Added a Taskfile.yml (see: https://taskfile.dev)
- Updated the manual page

Signed-off-by: Shin'ya Minazuki <shinyoukai@laidback.moe>
2025-12-30 12:11:34 -03:00
12 changed files with 168 additions and 84 deletions

2
.gitignore vendored
View File

@@ -1 +1 @@
/mikuru-*
/mikuru

View File

@@ -1,13 +1,10 @@
VERSION = `git describe --tags` || echo MIRAI
GO = go
GOFLAGS = -v -buildvcs=false -buildmode=exe -ldflags "-w -X `${GO} list`.Version=${VERSION}"
GOFLAGS = -v -buildvcs=false -buildmode=exe -ldflags='-w -X "`${GO} list`.Version=${VERSION}" -X "`${GO} list`.Revision=${REVISION}"'
build: mikuru-login mikuru-post
VERSION ?= `git describe --tags`
REVISION ?= `git rev-list --all | wc -l`
mikuru-login:
${GO} build ${GOFLAGS} ./cmd/$@
mikuru-post:
${GO} build ${GOFLAGS} ./cmd/$@
build:
@${GO} build ${GOFLAGS} -o mikuru ./cmd
clean:
rm -f mikuru-*
@rm -f mikuru

View File

@@ -1,5 +1,5 @@
# Mikuru
[Yarn.social](https://yarn.social) client where each task (except for logout) is handled by a separate program.
A [yarn.social](https://yarn.social) client from the future
## Current status
* [ ] Follow/Unfollow

31
Taskfile.yml Normal file
View File

@@ -0,0 +1,31 @@
# https://taskfile.dev
version: '3'
env:
GO: go
vars:
IMPORT: git.laidback.moe/shinyoukai/mikuru
DIR: ./cmd
tasks:
default:
cmds:
- task: build
build:
desc: Build the client
cmds:
- $GO build -ldflags='-s -w -X "{{.IMPORT}}.Version={{.VERSION}}" -X "{{.IMPORT}}.Revision={{.REVISION}}"' -v -o mikuru {{.DIR}}
vars:
REVISION:
sh: git rev-list --all | wc -l | tr -d ' '
VERSION:
sh: git describe --tags
clean:
desc: Remove generated files
cmds:
- rm -f mikuru
tidy:
desc: Update go.mod
cmds:
- $GO mod tidy

View File

@@ -2,14 +2,15 @@ package main
import (
"fmt"
"log"
"os"
"strings"
"syscall"
"golang.org/x/term"
"go.yarn.social/client"
"git.laidback.moe/shinyoukai/mikuru"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
var (
@@ -17,37 +18,38 @@ var (
password string
)
var loginCmd = &cobra.Command{
Use: "login",
Short: "Authenticate against a Yarn.social pod",
Aliases: []string{"auth", "signin"},
Args: cobra.MaximumNArgs(0),
Run: func(_ *cobra.Command, _ []string) {
cli, err := client.NewClient(client.WithURI(mikuru.Config.Host))
if err != nil {
log.Fatal("Unable to create client", err)
}
signin(cli)
},
}
func init() {
mikuru.ConfInit()
rootCmd.AddCommand(loginCmd)
}
func main() {
cli, err := client.NewClient(client.WithURI(mikuru.Config.Host))
if err != nil {
log.Println("Unable to create client")
log.Fatal(err)
}
fmt.Println("Welcome to Mikuru!")
fmt.Printf("%s\n", mikuru.PrintVersion())
signin(cli)
}
func signin(cli *client.Client) {
fmt.Printf("Username: ")
fmt.Scanln(&username)
if len(username) == 0 {
fmt.Println("No value. Bailing out")
os.Exit(1)
log.Fatal("No value. Bailing out")
}
fmt.Printf("Password: ")
data, err := term.ReadPassword(int(syscall.Stdin))
if err != nil {
fmt.Printf("Unable to obtain password: %s\n", err)
os.Exit(1)
log.Fatal("Unable to obtain password", err)
}
password := string(data)
@@ -55,15 +57,14 @@ func signin(cli *client.Client) {
res, err := cli.Login(username, password)
if err != nil {
fmt.Println("Unable to login")
os.Exit(1)
log.Fatal("Unable to login", err)
}
token := strings.Trim(fmt.Sprintf(res.Token), "{}")
fmt.Println("Login successful")
fmt.Println("Place this token in your configuration file for later use")
fmt.Println("Do not share it with anyone, it's classified information")
fmt.Println("Do not share it with anyone, it's classified information!")
fmt.Println("")
fmt.Printf("token = %v\n", token)
}

5
cmd/main.go Normal file
View File

@@ -0,0 +1,5 @@
package main
func main() {
Execute()
}

View File

@@ -1,43 +0,0 @@
package main
import (
"log"
"git.laidback.moe/shinyoukai/mikuru"
"go.yarn.social/client"
"github.com/tj/go-editor"
)
func init() {
mikuru.ConfInit()
}
func main() {
cli, err := client.NewClient(
client.WithURI(mikuru.Config.Host),
client.WithToken(mikuru.Config.Token),
)
if err != nil {
log.Fatal(err)
}
write(cli)
}
func write(cli *client.Client) {
var post string
data, err := editor.Read()
if err != nil {
log.Fatal("Unable to read content from editor")
}
post = string(data)
_, err = cli.Post(post, "")
if err != nil {
log.Fatal("Unable to publish tweet")
}
}

48
cmd/post.go Normal file
View File

@@ -0,0 +1,48 @@
package main
import (
"git.laidback.moe/shinyoukai/mikuru"
"go.yarn.social/client"
"github.com/tj/go-editor"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
var postCmd = &cobra.Command{
Use: "post",
Aliases: []string{"tweet"},
Short: "Publish a new post to a Yarn pod",
Run: func(_ *cobra.Command, args []string) {
cli, err := client.NewClient(
client.WithURI(mikuru.Config.Host),
client.WithToken(mikuru.Config.Token),
)
if err != nil {
log.Fatal("Unable to create client", err)
}
write(cli)
},
}
func init() {
rootCmd.AddCommand(postCmd)
mikuru.ConfInit()
}
func write(cli *client.Client) {
var post string
data, err := editor.Read()
if err != nil {
log.Fatal("Unable to read content from editor", err)
}
post = string(data)
_, err = cli.Post(post, "")
if err != nil {
log.Fatal("Unable to publish tweet", err)
}
}

20
cmd/root.go Normal file
View File

@@ -0,0 +1,20 @@
package main
import (
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"git.laidback.moe/shinyoukai/mikuru"
)
var rootCmd = &cobra.Command{
Use: "mikuru",
Short: "A client for Yarn.social from the future",
Version: mikuru.Version,
}
func Execute() {
err := rootCmd.Execute()
if err != nil {
log.Fatal(err)
}
}

5
go.mod
View File

@@ -3,6 +3,8 @@ module git.laidback.moe/shinyoukai/mikuru
go 1.24.0
require (
github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.10.2
github.com/tj/go-editor v1.0.0
go.yarn.social/client v0.0.0-20250420114029-410ad71a453e
golang.org/x/term v0.38.0
@@ -10,8 +12,9 @@ require (
)
require (
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/pkg/errors v0.8.1 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/spf13/pflag v1.0.9 // indirect
github.com/stretchr/testify v1.11.1 // indirect
go.yarn.social/types v0.0.0-20250420113154-c5a6df6d2f22 // indirect
golang.org/x/sys v0.39.0 // indirect

9
go.sum
View File

@@ -1,6 +1,9 @@
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@@ -11,14 +14,20 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/tj/go-editor v1.0.0 h1:VduwRZWk5gxHbzRkS4buB9BIFzAf6Jc+vd2bLZpHzSw=
github.com/tj/go-editor v1.0.0/go.mod h1:Jj9Ze2Rn2yj5DmMppydZqr9LbkIcCWrehzZ+7udOT+w=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
go.yarn.social/client v0.0.0-20250420114029-410ad71a453e h1:A4wok2/cSAbvWLjHhtCrW8dvv1XlgcGRj7H15150HhA=
go.yarn.social/client v0.0.0-20250420114029-410ad71a453e/go.mod h1:+VjffjmcZMHIudGsZlnbPnavk0oSK6xf4olzExiojhg=
go.yarn.social/types v0.0.0-20250420113154-c5a6df6d2f22 h1:M34aQ3AaRKE2t7JOLwoemJ65r0e8c1z4TwuHzogHvZE=

View File

@@ -3,15 +3,28 @@
.Os
.Sh NAME
.Nm mikuru
.Nd A yarn-compatible twtxt client from the future
.Nd A yarn.social client from the future
.Sh SYNOPSIS
.Nm
.Op Cm login
.Op Cm post
.Sh DESCRIPTION
This client has every single part of its functionality
handled by a separate program.
.Sh SEE ALSO
.Xr mikuru-login 1
.Xr mikuru-post 1
.Xr mikuru-timeline 1
This program was made by reverse engineering the reference client
due to lack of known API documentation for the client library it uses.
.Pp
It doesn't share any code with it, however.
.Sh USAGE
.Bl -tag -width 11n
.It login
Authenticate with a Yarn.social pod,
it will ask you for your credentials
(passwords are not echoed)
.It post
Publish a tweet to a Yarn.social pod,
it will open your text editor (usually $EDITOR),
and will fail if the variable is unset.
.El
.Sh FILES
.Pa ~/.config/mikuru.ini
.Sh AUTHORS
.An Shin'ya Minazuki Aq Mt shinyoukai@laidback.moe