Yuki N. > COMMIT;
Signed-off-by: Shin'ya Minazuki <shinyoukai@laidback.moe>
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/yuki
|
||||
15
COPYING
Normal file
15
COPYING
Normal file
@@ -0,0 +1,15 @@
|
||||
ISC License (ISCL)
|
||||
|
||||
Copyright (C) 2026 Shin'ya Minazuki <shinyoukai@laidback.moe>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||
OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THIS SOFTWARE.
|
||||
3
README.md
Normal file
3
README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Yuki (有希)
|
||||
An incomplete client for ActivityPub, which shouldn't even be paid attention to.
|
||||
|
||||
30
Taskfile.yml
Normal file
30
Taskfile.yml
Normal file
@@ -0,0 +1,30 @@
|
||||
# https://taskfile.dev
|
||||
|
||||
version: '3'
|
||||
|
||||
env:
|
||||
GO: go
|
||||
GOTELEMETRY: off
|
||||
|
||||
vars:
|
||||
IMPORT: git.laidback.moe/shinyoukai/yuki
|
||||
VERSION: 0.0.0
|
||||
tasks:
|
||||
default:
|
||||
cmds:
|
||||
- task: build
|
||||
build:
|
||||
desc: Build the interface
|
||||
cmds:
|
||||
- $GO build -v -ldflags='-w -X {{.IMPORT}}.Version=$VERSION' -buildvcs=false -o yuki
|
||||
silent: true
|
||||
clean:
|
||||
desc: Remove generated files
|
||||
cmds:
|
||||
- rm -f yuki
|
||||
silent: true
|
||||
tidy:
|
||||
desc: Update go.mod
|
||||
cmds:
|
||||
- $GO mod tidy
|
||||
silent: true
|
||||
86
auth.go
Normal file
86
auth.go
Normal file
@@ -0,0 +1,86 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/url"
|
||||
"os"
|
||||
|
||||
"github.com/mattn/go-mastodon"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var authCmd = &cobra.Command{
|
||||
Use: "auth",
|
||||
Short: "Authenticate to a Mastodon/Pleroma instance",
|
||||
Run: func(_ *cobra.Command, args []string) {
|
||||
doLogin()
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
ConfInit()
|
||||
rootCmd.AddCommand(authCmd)
|
||||
}
|
||||
|
||||
func doLogin() {
|
||||
client := &mastodon.AppConfig{
|
||||
Server: Config.Host,
|
||||
ClientName: "Yuki",
|
||||
Scopes: "read write follow",
|
||||
Website: "https://projects.laidback.moe/yuki/",
|
||||
RedirectURIs: "urn:ietf:wg:oauth:2.0:oob",
|
||||
}
|
||||
|
||||
app, err := mastodon.RegisterApp(context.Background(), client)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
u, err := url.Parse(app.AuthURI)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Printf("Go to \n%s\n and copy/paste the given auth code\n", u)
|
||||
var authCode string
|
||||
fmt.Print("Paste the code here:")
|
||||
fmt.Scanln(&authCode)
|
||||
|
||||
conf := &mastodon.Config{
|
||||
Server: Config.Host,
|
||||
ClientID: app.ClientID,
|
||||
ClientSecret: app.ClientSecret,
|
||||
}
|
||||
|
||||
c := mastodon.NewClient(conf)
|
||||
|
||||
err = c.GetUserAccessToken(context.Background(), authCode, app.RedirectURI)
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
config, err := os.UserConfigDir()
|
||||
if err != nil {
|
||||
log.Println("Unable to obtain user's configuration directory")
|
||||
log.Fatal(err)
|
||||
}
|
||||
configPath := config + "/yuki.ini"
|
||||
|
||||
f, err := os.OpenFile(configPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
|
||||
client_id := c.Config.ClientID
|
||||
client_secret := c.Config.ClientSecret
|
||||
access_token := c.Config.AccessToken
|
||||
|
||||
f.WriteString("client_id = " + client_id + "\n")
|
||||
f.WriteString("client_secret = " + client_secret + "\n")
|
||||
f.WriteString("token = " + access_token + "\n")
|
||||
}
|
||||
38
config.go
Normal file
38
config.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"gopkg.in/ini.v1"
|
||||
)
|
||||
|
||||
var Config struct {
|
||||
ClientID string
|
||||
ClientSecret string
|
||||
Host string
|
||||
Token string
|
||||
}
|
||||
|
||||
func ConfInit() {
|
||||
config, err := os.UserConfigDir()
|
||||
if err != nil {
|
||||
log.Println("Unable to obtain user's configuration directory")
|
||||
log.Fatal(err)
|
||||
}
|
||||
configPath := config + "/yuki.ini"
|
||||
Parse(configPath)
|
||||
}
|
||||
|
||||
func Parse(file string) error {
|
||||
cfg, err := ini.Load(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
Config.ClientID = cfg.Section("yuki").Key("client_id").String()
|
||||
Config.ClientSecret = cfg.Section("yuki").Key("client_secret").String()
|
||||
Config.Host = cfg.Section("yuki").Key("host").String()
|
||||
Config.Token = cfg.Section("yuki").Key("token").String()
|
||||
|
||||
return nil
|
||||
}
|
||||
19
go.mod
Normal file
19
go.mod
Normal file
@@ -0,0 +1,19 @@
|
||||
module git.laidback.moe/shinyoukai/yuki
|
||||
|
||||
go 1.25.2
|
||||
|
||||
require (
|
||||
github.com/mattn/go-mastodon v0.0.10
|
||||
github.com/spf13/cobra v1.10.2
|
||||
github.com/tj/go-editor v1.0.0
|
||||
gopkg.in/ini.v1 v1.67.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/gorilla/websocket v1.5.3 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/pkg/errors v0.8.1 // indirect
|
||||
github.com/spf13/pflag v1.0.9 // indirect
|
||||
github.com/stretchr/testify v1.11.1 // indirect
|
||||
github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 // indirect
|
||||
)
|
||||
30
go.sum
Normal file
30
go.sum
Normal file
@@ -0,0 +1,30 @@
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
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/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/mattn/go-mastodon v0.0.10 h1:wz1d/aCkJOIkz46iv4eAqXHVreUMxydY1xBWrPBdDeE=
|
||||
github.com/mattn/go-mastodon v0.0.10/go.mod h1:YBofeqh7G6s787787NQR8erBYz6fKDu+KNMrn5RuD6Y=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
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/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=
|
||||
github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 h1:nrZ3ySNYwJbSpD6ce9duiP+QkD3JuLCcWkdaehUS/3Y=
|
||||
github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80/go.mod h1:iFyPdL66DjUD96XmzVL3ZntbzcflLnznH0fr99w5VqE=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
66
post.go
Normal file
66
post.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
|
||||
"github.com/mattn/go-mastodon"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/tj/go-editor"
|
||||
)
|
||||
|
||||
var (
|
||||
visibility string
|
||||
)
|
||||
|
||||
var postCmd = &cobra.Command{
|
||||
Use: "post",
|
||||
Short: "Send a message",
|
||||
Run: func(_ *cobra.Command, args []string) {
|
||||
doPost(args)
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
ConfInit()
|
||||
rootCmd.AddCommand(postCmd)
|
||||
|
||||
postCmd.Flags().StringVarP(&visibility, "scope", "s", "public", "Visibility")
|
||||
}
|
||||
|
||||
func doPost(args []string) {
|
||||
var text string
|
||||
|
||||
data, err := editor.Read()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
text = string(data)
|
||||
|
||||
conf := &mastodon.Config{
|
||||
Server: Config.Host,
|
||||
ClientID: Config.ClientID,
|
||||
ClientSecret: Config.ClientSecret,
|
||||
AccessToken: Config.Token,
|
||||
}
|
||||
|
||||
c := mastodon.NewClient(conf)
|
||||
|
||||
_, err = c.GetAccountCurrentUser(context.Background())
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
|
||||
note := mastodon.Toot{
|
||||
Status: text,
|
||||
Visibility: visibility,
|
||||
}
|
||||
|
||||
_, err = c.PostStatus(context.Background(), ¬e)
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("%#v\n", err)
|
||||
}
|
||||
}
|
||||
18
root.go
Normal file
18
root.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "yuki",
|
||||
Short: "An extraterrestial (but limited) activitypub client",
|
||||
}
|
||||
|
||||
func Execute() {
|
||||
err := rootCmd.Execute()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
17
version.go
Normal file
17
version.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var (
|
||||
Revision = "0"
|
||||
Version = "0"
|
||||
)
|
||||
|
||||
func FullVersion() string {
|
||||
return fmt.Sprintf("%s (r%s)", Version, Revision)
|
||||
}
|
||||
func PrintVersion() string {
|
||||
return fmt.Sprintf("%s", Version)
|
||||
}
|
||||
Reference in New Issue
Block a user