gonk.go 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "gitea.com/lunny/html2md"
  6. "io/ioutil"
  7. "log"
  8. "net/http"
  9. "net/url"
  10. "os"
  11. "os/exec"
  12. "regexp"
  13. "sort"
  14. "strings"
  15. )
  16. type Colour struct {
  17. Reset string
  18. Red string
  19. Green string
  20. Yellow string
  21. Blue string
  22. Purple string
  23. Cyan string
  24. Gray string
  25. White string
  26. }
  27. type Honk struct {
  28. ID int
  29. What string
  30. Handle string
  31. Oondle string
  32. XID string
  33. Date string
  34. Noise string
  35. Donks []struct {
  36. URL string
  37. }
  38. }
  39. type HonkSet struct {
  40. Honks []Honk
  41. }
  42. func setUpColour(colourful bool) Colour {
  43. if colourful {
  44. return Colour{
  45. Reset: "\033[0m",
  46. Red: "\033[31m",
  47. Green: "\033[32m",
  48. Yellow: "\033[33m",
  49. Blue: "\033[34m",
  50. Purple: "\033[35m",
  51. Cyan: "\033[36m",
  52. Gray: "\033[37m",
  53. White: "\033[97m",
  54. }
  55. } else {
  56. return Colour{}
  57. }
  58. }
  59. func gettoken(server, username, password string) string {
  60. form := make(url.Values)
  61. form.Add("username", username)
  62. form.Add("password", password)
  63. form.Add("gettoken", "1")
  64. loginurl := fmt.Sprintf("https://%s/dologin", server)
  65. req, err := http.NewRequest("POST", loginurl, strings.NewReader(form.Encode()))
  66. if err != nil {
  67. log.Fatal(err)
  68. }
  69. req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
  70. resp, err := http.DefaultClient.Do(req)
  71. if err != nil {
  72. log.Fatal(err)
  73. }
  74. defer resp.Body.Close()
  75. answer, err := ioutil.ReadAll(resp.Body)
  76. if err != nil {
  77. log.Fatal(err)
  78. }
  79. if resp.StatusCode != 200 {
  80. log.Fatalf("status: %d: %s", resp.StatusCode, answer)
  81. }
  82. return string(answer)
  83. }
  84. func logout(server, token string) {
  85. apiurl := fmt.Sprintf("https://%s/logout", server)
  86. req, err := http.NewRequest("GET", apiurl, nil)
  87. if err != nil {
  88. log.Fatal(err)
  89. }
  90. req.Header.Add("Authorization", "Bearer "+token)
  91. resp, err := http.DefaultClient.Do(req)
  92. if err != nil {
  93. log.Fatal(err)
  94. }
  95. defer resp.Body.Close()
  96. if resp.StatusCode != 200 {
  97. answer, _ := ioutil.ReadAll(resp.Body)
  98. log.Fatalf("status: %d: %s", resp.StatusCode, answer)
  99. }
  100. }
  101. func gethonks(server, token string, after int) HonkSet {
  102. form := make(url.Values)
  103. form.Add("action", "gethonks")
  104. form.Add("page", "home")
  105. form.Add("after", fmt.Sprintf("%d", after))
  106. apiurl := fmt.Sprintf("https://%s/api?%s", server, form.Encode())
  107. req, err := http.NewRequest("GET", apiurl, nil)
  108. if err != nil {
  109. log.Fatal(err)
  110. }
  111. req.Header.Add("Authorization", "Bearer "+token)
  112. resp, err := http.DefaultClient.Do(req)
  113. if err != nil {
  114. log.Fatal(err)
  115. }
  116. defer resp.Body.Close()
  117. if resp.StatusCode != 200 {
  118. answer, _ := ioutil.ReadAll(resp.Body)
  119. log.Fatalf("status: %d: %s", resp.StatusCode, answer)
  120. }
  121. var honks HonkSet
  122. d := json.NewDecoder(resp.Body)
  123. err = d.Decode(&honks)
  124. if err != nil {
  125. log.Fatal(err)
  126. }
  127. return honks
  128. }
  129. func readkey() rune {
  130. exec.Command("stty", "-F", "/dev/tty", "cbreak", "min", "1").Run()
  131. exec.Command("stty", "-F", "/dev/tty", "-echo").Run()
  132. defer exec.Command("stty", "-F", "/dev/tty", "echo").Run()
  133. var b []byte = make([]byte, 1)
  134. os.Stdin.Read(b)
  135. key := rune(b[0])
  136. return key
  137. }
  138. func showHonks(honks HonkSet, after int, colour Colour) (int, bool) {
  139. broken := false
  140. honksNumber := len(honks.Honks)
  141. for i, honk := range honks.Honks {
  142. md := html2md.Convert(honk.Noise)
  143. md = changeA(md, colour)
  144. fmt.Printf(colour.White+"%d/%d\n%s %s %s (%s) \n%s\n\n"+colour.Reset, i+1, honksNumber, honk.Handle, honk.What, honk.Oondle, honk.Date, honk.XID)
  145. fmt.Printf("%s\n\n", md)
  146. for i, donk := range honk.Donks {
  147. fmt.Printf(colour.White+"Donk #%d: %s\n\n"+colour.Reset, i, donk.URL)
  148. }
  149. fmt.Printf(colour.Cyan + "## Press o to open in browser, n to show next, or q to exit " + colour.Reset)
  150. for {
  151. key := readkey()
  152. if key == 'n' {
  153. after = honk.ID
  154. fmt.Printf("\n\n")
  155. break
  156. }
  157. if key == 'o' {
  158. exec.Command("xdg-open", honk.XID).Run()
  159. continue
  160. }
  161. if key == 'q' {
  162. broken = true
  163. return after, broken
  164. }
  165. }
  166. }
  167. return after, broken
  168. }
  169. func changeA(noise string, colour Colour) string {
  170. reg := regexp.MustCompile("\\[([^]]+)\\]\\(([^)]+)\\)")
  171. indexes := reg.FindAllStringSubmatchIndex(noise, -1)
  172. last := 0
  173. renoise := ""
  174. for _, index := range indexes {
  175. s, t, s1, t1, s2, t2 := index[0], index[1], index[2], index[3], index[4], index[5]
  176. bf := noise[last:s]
  177. label := noise[s1:t1]
  178. href := noise[s2:t2]
  179. last = t
  180. if label == href {
  181. renoise = renoise + bf + colour.Blue + href + colour.Reset
  182. } else if label[0] == '#' {
  183. renoise = renoise + bf + colour.Green + label + colour.Reset
  184. } else if label[0] == '@' {
  185. instance, err := url.Parse(href)
  186. if err != nil {
  187. renoise = renoise + bf + "[" + colour.Green + label + colour.Reset + "](" + colour.Blue + href + colour.Reset + ")"
  188. } else {
  189. renoise = renoise + bf + colour.Green + label + "@" + instance.Host + colour.Reset
  190. }
  191. } else {
  192. renoise = renoise + bf + "[" + colour.Green + label + colour.Reset + "](" + colour.Blue + href + colour.Reset + ")"
  193. }
  194. }
  195. renoise = renoise + noise[last:]
  196. return renoise
  197. }
  198. func main() {
  199. server := ""
  200. username := ""
  201. password := ""
  202. after := 0
  203. broken := false
  204. colourful := true
  205. if len(os.Args) > 1 && os.Args[1] == "-t" {
  206. colourful = false
  207. }
  208. colour := setUpColour(colourful)
  209. data, err := ioutil.ReadFile("account")
  210. for _, line := range strings.Split(string(data), "\n") {
  211. if !strings.Contains(line, "=") {
  212. continue
  213. }
  214. kv := strings.Split(line, "=")
  215. k := strings.Trim(kv[0], " ")
  216. v := strings.Trim(kv[1], " ")
  217. switch k {
  218. case "username":
  219. username = v
  220. case "password":
  221. password = v
  222. case "server":
  223. server = v
  224. }
  225. }
  226. if err != nil || server == "" || username == "" || password == "" {
  227. log.Println("[ERR] file 'account' with username, password, and server needed")
  228. os.Exit(1)
  229. }
  230. token := gettoken(server, username, password)
  231. data, err = ioutil.ReadFile("after")
  232. if err != nil {
  233. log.Printf("[WARN] File reading error: %s\nShowing all honks", err)
  234. }
  235. fmt.Sscanf(string(data), "%d", &after)
  236. honks := gethonks(server, token, after)
  237. sort.Slice(honks.Honks, func(i, j int) bool {
  238. return honks.Honks[i].ID < honks.Honks[j].ID
  239. })
  240. after, broken = showHonks(honks, after, colour)
  241. if !broken {
  242. fmt.Println(colour.Cyan + "## No more honks" + colour.Reset)
  243. }
  244. ioutil.WriteFile("after", []byte(fmt.Sprintf("%d", after)), 0644)
  245. logout(server, token)
  246. }