123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312 |
- package main
- import (
- "encoding/json"
- "fmt"
- "io/ioutil"
- "log"
- "net/http"
- "net/url"
- "os"
- "os/exec"
- "regexp"
- "sort"
- "strings"
- "golang.org/x/net/html"
- "gitea.com/lunny/html2md"
- )
- type Colour struct {
- Reset string
- Red string
- Green string
- Yellow string
- Blue string
- Purple string
- Cyan string
- Gray string
- White string
- }
- type Honk struct {
- ID int
- What string
- Handle string
- Oondle string
- XID string
- Date string
- Noise string
- Donks []struct {
- URL string
- }
- }
- type HonkSet struct {
- Honks []Honk
- }
- func isPgDn(b []byte) bool {
- return len(b) == 4 && b[0] == 27 && b[1] == 91 && b[2] == 54 && b[3] == 126
- }
- func dataHome() string {
- xdgDataHome, _ := os.LookupEnv("XDG_DATA_HOME")
- if xdgDataHome == "" {
- user, err := os.UserHomeDir()
- if err != nil {
- panic(err)
- }
- xdgDataHome = user + "/.local/share"
- }
- return xdgDataHome + "/gonk"
- }
- func configHome() string {
- xdgConfigHome, _ := os.LookupEnv("XDG_CONFIG_HOME")
- if xdgConfigHome == "" {
- user, err := os.UserHomeDir()
- if err != nil {
- panic(err)
- }
- xdgConfigHome = user + "/.config"
- }
- return xdgConfigHome + "/gonk"
- }
- func setUpColour(colourful bool) Colour {
- if colourful {
- return Colour{
- Reset: "\033[0m",
- Red: "\033[31m",
- Green: "\033[32m",
- Yellow: "\033[33m",
- Blue: "\033[34m",
- Purple: "\033[35m",
- Cyan: "\033[36m",
- Gray: "\033[37m",
- White: "\033[97m",
- }
- } else {
- return Colour{}
- }
- }
- func gettoken(server, username, password string) string {
- form := make(url.Values)
- form.Add("username", username)
- form.Add("password", password)
- form.Add("gettoken", "1")
- loginurl := fmt.Sprintf("https://%s/dologin", server)
- req, err := http.NewRequest("POST", loginurl, strings.NewReader(form.Encode()))
- if err != nil {
- log.Fatal(err)
- }
- req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
- resp, err := http.DefaultClient.Do(req)
- if err != nil {
- log.Fatal(err)
- }
- defer resp.Body.Close()
- answer, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- log.Fatal(err)
- }
- if resp.StatusCode != 200 {
- log.Fatalf("status: %d: %s", resp.StatusCode, answer)
- }
- return string(answer)
- }
- func logout(server, token string) {
- apiurl := fmt.Sprintf("https://%s/logout", server)
- req, err := http.NewRequest("GET", apiurl, nil)
- if err != nil {
- log.Fatal(err)
- }
- req.Header.Add("Authorization", "Bearer "+token)
- resp, err := http.DefaultClient.Do(req)
- if err != nil {
- log.Fatal(err)
- }
- defer resp.Body.Close()
- if resp.StatusCode != 200 {
- answer, _ := ioutil.ReadAll(resp.Body)
- log.Fatalf("status: %d: %s", resp.StatusCode, answer)
- }
- }
- func gethonks(server, token string, after int) HonkSet {
- form := make(url.Values)
- form.Add("action", "gethonks")
- form.Add("page", "home")
- form.Add("after", fmt.Sprintf("%d", after))
- apiurl := fmt.Sprintf("https://%s/api?%s", server, form.Encode())
- req, err := http.NewRequest("GET", apiurl, nil)
- if err != nil {
- log.Fatal(err)
- }
- req.Header.Add("Authorization", "Bearer "+token)
- resp, err := http.DefaultClient.Do(req)
- if err != nil {
- log.Fatal(err)
- }
- defer resp.Body.Close()
- if resp.StatusCode != 200 {
- answer, _ := ioutil.ReadAll(resp.Body)
- log.Fatalf("status: %d: %s", resp.StatusCode, answer)
- }
- var honks HonkSet
- d := json.NewDecoder(resp.Body)
- err = d.Decode(&honks)
- if err != nil {
- log.Fatal(err)
- }
- return honks
- }
- func readkey() []byte {
- exec.Command("stty", "-F", "/dev/tty", "cbreak", "min", "1").Run()
- exec.Command("stty", "-F", "/dev/tty", "-echo").Run()
- defer exec.Command("stty", "-F", "/dev/tty", "echo").Run()
- var b []byte = make([]byte, 4)
- os.Stdin.Read(b)
- return b
- }
- func showHonks(honks HonkSet, after int, colour Colour) (int, bool) {
- broken := false
- honksNumber := len(honks.Honks)
- fmt.Println(colour.Cyan + "## o to open in browser, n/Space/PgDn for next, d to show donks, q to exit " + colour.Reset)
- for i, honk := range honks.Honks {
- md := html2md.Convert(honk.Noise)
- md = html.UnescapeString(md)
- md = changeA(md, colour)
- head := fmt.Sprintf(colour.White+"%d/%d\n%s %s %s (%s) \n%s\n"+colour.Reset, i+1, honksNumber, honk.Handle, honk.What, honk.Oondle, honk.Date, honk.XID)
- head = strings.ReplaceAll(head, " ", " ")
- fmt.Println(head)
- fmt.Printf("%s\n\n", md)
- if len(honk.Donks) > 0 {
- fmt.Println(colour.White + "## has donks" + colour.Reset)
- }
- for {
- key := readkey()
- if rune(key[0]) == 'n' || rune(key[0]) == ' ' || isPgDn(key) {
- after = honk.ID
- fmt.Printf("\n\n")
- break
- }
- if rune(key[0]) == 'o' {
- exec.Command("xdg-open", honk.XID).Run()
- continue
- }
- if rune(key[0]) == 'd' {
- for i, donk := range honk.Donks {
- fmt.Printf(colour.White+"Donk #%d: %s\n"+colour.Reset, i, donk.URL)
- }
- }
- if rune(key[0]) == 'q' {
- broken = true
- return after, broken
- }
- }
- }
- return after, broken
- }
- func changeA(noise string, colour Colour) string {
- reg := regexp.MustCompile("\\[([^]]+)\\]\\(([^)]+)\\)")
- indexes := reg.FindAllStringSubmatchIndex(noise, -1)
- last := 0
- renoise := ""
- for _, index := range indexes {
- s, t, s1, t1, s2, t2 := index[0], index[1], index[2], index[3], index[4], index[5]
- bf := noise[last:s]
- label := noise[s1:t1]
- href := noise[s2:t2]
- last = t
- if label == href {
- renoise = renoise + bf + colour.Blue + href + colour.Reset
- } else if label[0] == '#' {
- renoise = renoise + bf + colour.Green + label + colour.Reset
- } else if label[0] == '@' {
- instance, err := url.Parse(href)
- if err != nil {
- renoise = renoise + bf + "[" + colour.Green + label + colour.Reset + "](" + colour.Blue + href + colour.Reset + ")"
- } else {
- renoise = renoise + bf + colour.Green + label + "@" + instance.Host + colour.Reset
- }
- } else {
- renoise = renoise + bf + "[" + colour.Green + label + colour.Reset + "](" + colour.Blue + href + colour.Reset + ")"
- }
- }
- renoise = renoise + noise[last:]
- return renoise
- }
- func main() {
- server := ""
- username := ""
- password := ""
- after := 0
- broken := false
- colourful := true
- if len(os.Args) > 1 && os.Args[1] == "-t" {
- colourful = false
- }
- colour := setUpColour(colourful)
- err := os.MkdirAll(dataHome(), 0755)
- if err != nil {
- fmt.Println("[ERR] while making home directory: %v", err)
- os.Exit(1)
- }
- data, err := ioutil.ReadFile(configHome() + "/account")
- if err != nil {
- fmt.Printf("[ERR] cannot open account file (%s): %v\n", configHome()+"/account", err)
- os.Exit(1)
- }
- for _, line := range strings.Split(string(data), "\n") {
- if !strings.Contains(line, "=") {
- continue
- }
- kv := strings.Split(line, "=")
- k := strings.Trim(kv[0], " ")
- v := strings.Trim(kv[1], " ")
- switch k {
- case "username":
- username = v
- case "password":
- password = v
- case "server":
- server = v
- }
- }
- if server == "" || username == "" || password == "" {
- log.Println("[ERR] file 'account' with username, password, and server needed")
- os.Exit(1)
- }
- token := gettoken(server, username, password)
- data, err = ioutil.ReadFile(dataHome() + "/after")
- if err != nil {
- log.Printf("[WARN] file reading error: %s\nShowing all honks", err)
- }
- fmt.Sscanf(string(data), "%d", &after)
- honks := gethonks(server, token, after)
- sort.Slice(honks.Honks, func(i, j int) bool {
- return honks.Honks[i].ID < honks.Honks[j].ID
- })
- after, broken = showHonks(honks, after, colour)
- if !broken {
- fmt.Println(colour.Cyan + "## No more honks" + colour.Reset)
- }
- ioutil.WriteFile(dataHome()+"/after", []byte(fmt.Sprintf("%d", after)), 0644)
- logout(server, token)
- }
|