Skip to content

Commit

Permalink
Add -y flag to post command, Add upload support (with optional captio…
Browse files Browse the repository at this point in the history
…n), Better error messages
  • Loading branch information
aerth committed Feb 22, 2017
1 parent ff0316c commit c97e2c7
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 36 deletions.
107 changes: 93 additions & 14 deletions 00-quitter.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ Package quitter is a Go library to interact with GNU Social instances.
The MIT License (MIT)
Copyright (c) 2016 aerth
Copyright (c) 2016-2017 aerth <[email protected]>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand All @@ -18,10 +18,17 @@ Package quitter is a Go library to interact with GNU Social instances.
package quitter

import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"mime/multipart"
"net/http"
"net/url"
"os"
"path/filepath"
"strings"
)

// GetPublic shows 20 new messages.
Expand Down Expand Up @@ -49,7 +56,7 @@ func (a Account) GetPublic() ([]Quip, error) {
// GetMentions shows 20 newest mentions of your username.
func (a Account) GetMentions() ([]Quip, error) {
if a.Username == "" || a.Password == "" {
return nil, errors.New("No user/password")
return nil, errors.New("Invalid Credentials")
}
path := "/api/statuses/mentions.json"
body, err := a.fireGET(path)
Expand All @@ -65,7 +72,7 @@ func (a Account) GetMentions() ([]Quip, error) {
// GetHome shows 20 from home timeline.
func (a Account) GetHome() ([]Quip, error) {
if a.Username == "" || a.Password == "" {
return nil, errors.New("No user/password")
return nil, errors.New("Invalid Credentials")
}

path := "/api/statuses/home_timeline.json"
Expand All @@ -84,7 +91,7 @@ func (a Account) GetHome() ([]Quip, error) {
// Search returns results for query searchstr. Does send auth info.
func (a Account) Search(searchstr string) ([]Quip, error) {
if a.Username == "" || a.Password == "" {
return nil, errors.New("No user/password")
return nil, errors.New("Invalid Credentials")
}
if searchstr == "" {
return nil, errors.New("No query")
Expand Down Expand Up @@ -138,11 +145,11 @@ func (a Account) PublicSearch(searchstr string) ([]Quip, error) {
// Follow sends a request to follow a user
func (a Account) Follow(followstr string) (user User, err error) {
if a.Username == "" || a.Password == "" {
return user, errors.New("No user/password")
return user, errors.New("Invalid Credentials")
}

if followstr == "" {
return user, errors.New("No query.")
return user, errors.New("no query")
}

v := url.Values{}
Expand All @@ -166,7 +173,7 @@ func (a Account) Follow(followstr string) (user User, err error) {
// UnFollow sends a request to unfollow a user
func (a Account) UnFollow(followstr string) (user User, err error) {
if a.Username == "" || a.Password == "" {
return user, errors.New("No user/password")
return user, errors.New("Invalid Credentials")
}
if followstr == "" {
return user, errors.New("No query")
Expand Down Expand Up @@ -202,7 +209,7 @@ func (a Account) GetUserTimeline(userlookup string) ([]Quip, error) {
// PostNew publishes content on a.Node, returns the new quip or an error.
func (a Account) PostNew(content string) (q Quip, err error) {
if a.Username == "" || a.Password == "" {
return q, errors.New("No user/password")
return q, errors.New("Invalid Credentials")
}

if content == "" {
Expand All @@ -227,7 +234,7 @@ func (a Account) PostNew(content string) (q Quip, err error) {
// Some nodes don't return anything.
func (a Account) ListAllGroups() ([]Group, error) {
if a.Username == "" || a.Password == "" {
return nil, errors.New("No user/password")
return nil, errors.New("Invalid Credentials")
}

path := "/api/statusnet/groups/list_all.json"
Expand All @@ -245,7 +252,7 @@ func (a Account) ListAllGroups() ([]Group, error) {
// ListMyGroups lists each group a a.Username is a member of
func (a Account) ListMyGroups() ([]Group, error) {
if a.Username == "" || a.Password == "" {
return nil, errors.New("No user/password")
return nil, errors.New("Invalid Credentials")
}

path := "/api/statusnet/groups/list.json"
Expand All @@ -264,11 +271,11 @@ func (a Account) ListMyGroups() ([]Group, error) {
// JoinGroup sends a request to join group grp.
func (a Account) JoinGroup(grp string) (g Group, err error) {
if a.Username == "" || a.Password == "" {
return g, errors.New("No user/password")
return g, errors.New("Invalid Credentials")
}

if grp == "" {
return g, errors.New("Blank group detected. Not going furthur.")
return g, errors.New("no group")
}
v := url.Values{}

Expand All @@ -292,11 +299,11 @@ func (a Account) JoinGroup(grp string) (g Group, err error) {
// PartGroup sends a request to part group grp.
func (a Account) PartGroup(grp string) (g Group, err error) {
if a.Username == "" || a.Password == "" {
return g, errors.New("No user/password")
return g, errors.New("Invalid Credentials")
}

if grp == "" {
return g, errors.New("Blank group detected. Not going furthur.")
return g, errors.New("no group")
}

v := url.Values{}
Expand All @@ -315,3 +322,75 @@ func (a Account) PartGroup(grp string) (g Group, err error) {

return g, err
}

func (a Account) Upload(fpath string, content ...string) (link string, err error) {

// Optional Caption
var caption string
if content != nil {
caption = strings.Join(content, "\n")
} else {
caption = " "
}

// Multipart Form
var b = new(bytes.Buffer)
w := multipart.NewWriter(b)
f, err := os.Open(fpath)
if err != nil {
return "", err
}
defer f.Close()
fw, err := w.CreateFormFile("media", filepath.Base(fpath))
if err != nil {
return "", err
}
fb, err := ioutil.ReadAll(f)
if err != nil {
return "", err
}
n, err := fw.Write(fb)
if err != nil {
panic(err)
}
if fw, err = w.CreateFormField("status"); err != nil {
return "", err
}
if _, err = fw.Write([]byte(caption)); err != nil {
return
}
w.Close()

// Request
uploadURL := a.Scheme + a.Node + "/api/statuses/update.json"
req, err := http.NewRequest("POST", uploadURL, b)

if err != nil {
return "", err
}
req.SetBasicAuth(a.Username, a.Password)
req.Header.Set("User-Agent", goquitter)
req.Header.Set("Content-Type", w.FormDataContentType())
fmt.Printf("%v bytes uploading\n", n)
res, err := apigun.Do(req)
if err != nil {
return "", err
}


// Response
if res.StatusCode != http.StatusOK {
err = fmt.Errorf("bad status: %s", res.Status)
return "", err
}

var bb []byte
bb, err = ioutil.ReadAll(res.Body)
bstr := string(bb)
if strings.Contains("Page not found", bstr) {
return "404 Page not found", err
}

return string(bb), err

}
2 changes: 1 addition & 1 deletion 01-internal.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ func Init() {
}

func redirectPolicyFunc(req *http.Request, reqs []*http.Request) error {
req.Header.Add("Content-Type", "[application/json; charset=utf-8")
req.Header.Add("Content-Type", "application/json; charset=utf-8")
req.Header.Set("User-Agent", goquitter)
return nil
}
2 changes: 1 addition & 1 deletion 02-http.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func (a Account) firePOST(path string, v url.Values) ([]byte, error) {
if path == "" {
return nil, errors.New("No path")
}
if v.Encode() == "" && !strings.Contains(path, "update") { // update needs a blank post request..
if v.Encode() == "" && !strings.Contains(path, "update") { // update can use a blank post request..
return nil, errors.New("No values to post")
}
apipath := a.Scheme + a.Node + path
Expand Down
68 changes: 48 additions & 20 deletions cmd/go-quitter/00-main.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,19 +68,21 @@ Using environmental variables will override the config:
GNUSOCIALNODE
GNUSOCIALPASS
GNUSOCIALUSER
GNUSOCIALPATH
GNUSOCIALPATH - path to config file (default ~/.go-quitter)
Set your environmental variable to change nodes, use a different config,
or change user or password for a one-time session.
For example: "export GNUSOCIALNODE=gs.sdf.org" in your ~/.shrc or ~/.profile
Want to use a SOCKS proxy? Set the SOCKS environmental variable. Here is an example:
SOCKS=socks5://127.0.0.1:1080 ./go-quitter
`

var q *quitter.Account
var allCommands = []string{"help", "config",
"read", "user", "search", "home", "follow", "unfollow",
"post", "mentions", "groups", "mygroups", "join", "leave",
"part", "mention", "replies", "gui"}
"part", "mention", "replies", "gui-test", "upload"}

func main() {

Expand Down Expand Up @@ -111,9 +113,9 @@ func main() {
}

// command requires login credentials
needLogin := []string{"gui", "home", "follow", "unfollow",
needLogin := []string{"gui-test", "home", "follow", "unfollow",
"post", "mentions", "mygroups", "groups", "search", "join", "leave", "mention",
"replies", "direct", "inbox", "sent"}
"replies", "direct", "inbox", "sent", "upload"}
if containsString(needLogin, os.Args[1]) {
needConfig()
} else { // command doesn't need login
Expand All @@ -137,7 +139,7 @@ func main() {
// command: go-quitter read
case "test":
// runtests()
case "gui":
case "gui-test":

initgui()
os.Exit(0)
Expand Down Expand Up @@ -261,26 +263,56 @@ func main() {
PrintGroup(q.PartGroup(content))
os.Exit(0)

// go-quitter
// command: go-quitter post
case "post":
content := ""
var content string
if len(os.Args) > 1 {
content = strings.Join(os.Args[2:], " ") // go-quitter post wow this is a post\!
}
if content == "" {
content = getTypin()
}

fmt.Println("Preview:\n\n[" + q.Username + "] " + content)
fmt.Println("\nType YES to publish!")
if askForConfirmation() == false {
fmt.Println("Your status was not updated.")
os.Exit(0)
// go-quitter post -y hello world
if !strings.HasPrefix(content, "-y ") {
fmt.Println("Preview:\n\n[" + q.Username + "] " + content)
fmt.Println("\nType YES to publish!")
if askForConfirmation() == false {
fmt.Println("Your status was not updated.")
os.Exit(0)
}
} else {
content = strings.TrimPrefix(content, "-y ")
}

PrintQuip(q.PostNew(content))
os.Exit(0)

os.Exit(0)
// command: go-quitter post
case "upload":
var path, content string // go-quitter upload cat.gif lol
if len(os.Args) > 1 {
path = os.Args[2] // cat.gif
}
if path == "" {
path = getTypin()
}
if len(os.Args) > 2 {
content = strings.Join(os.Args[3:], " ") // lol
}
if content == "" {
content = getTypin()
}
fmt.Printf("Uploading %q", path)
if content != "" {
fmt.Printf(" with caption %q", content)
}
fmt.Println()
time.Sleep(time.Second)
out, err := q.Upload(path, content)
if err != nil {
fmt.Println("Error:", err)
}
fmt.Println(out)
default:
// this happens if we invoke with somehing like "go-quitter test"
fmt.Println("Command not found, try ", os.Args[0]+" help")
Expand Down Expand Up @@ -425,13 +457,9 @@ func init() {
if len(os.Args) < 2 {

fmt.Println("\n\n" + versionbar)

fmt.Println("\n\nPlease report any bugs or issues at:\n\thttps://github.com/aerth/go-quitter")
fmt.Println("This message (and hopefully bugs) will be removed before v0.1.0!!\n\n")
time.Sleep(1 * time.Second)
fmt.Println("Current list of commands:")
fmt.Println(allCommands)
fmt.Printf("Run '%s -help' for more information.\n\n", os.Args[0])
fmt.Printf("\nRun '%s -help' for more information.\n\n", os.Args[0])

os.Exit(0)
}
Expand Down

0 comments on commit c97e2c7

Please sign in to comment.