shithub: hell

ref: 3bf4db591828464cac77a8555359b38c233c3bce
dir: /main.go/

View raw version
package main

import (
	"context"
	"fmt"
	"io"
	"os"
	"os/exec"
	"strconv"
	"strings"

	"github.com/mattn/go-mastodon"
)

func main() {
	hc, err := NewHellclient()
	if err != nil {
		fmt.Printf("Error starting account: %v\n", err)
		return
	}
	rl := hc.rl
	client := hc.client


	//Horrible io pipe hack
	//Replaces system stdout with the readline one
	r, w, _ := os.Pipe()
	os.Stdout = w

	go func() {
		io.Copy(rl.Stdout(), r)
	}()

	homeMap := hc.homeMap
	debugMap := hc.debugMap
	postref := "a"
	lastindex := ""
	interupted := false //use this to check if we were interupted last turn
	var recentpost *mastodon.Status

	currentUser, err := client.GetAccountCurrentUser(context.Background())

	if err != nil {
		fmt.Println("Couldn't get our own profile: %v\n", err)
		return
	}

	go StreamHomeTimeline(client, homeMap, hc)

	for {
		func() {
			line, err := rl.Readline()
			//If we get an interupt error, we'll return to read the next line
			//If the next line is empty, exit the program
			//If it isn't empty, we were just clearing the line
			if interupted {
				if(len(line) == 0) {
					os.Exit(0)
				} else {
					interupted = false
					return
				}
			}
			if err != nil {
				if err == EOF || err == ErrInterrupt {
					interupted = true
					return
				}
				if err != nil {
					fmt.Println("Error:", err)
					return
				}
			}
			command, arguments := processInput(line)

			//empty line
			if command == "" && arguments == "" {
				if hc.isPaused {
					hc.pause(false)
				}
				return
			}

			//if we didn't get a slash command then the user is just posting
			if command == "" && arguments != "" {
				recentpost, err = postStatus(fmt.Sprintf(line), *client, "public")
				if err != nil {
					fmt.Println(err)
				}
				return
			}

			hc.lock()
			defer hc.unlock()

			index, content, _ := strings.Cut(arguments, " ")
			
			postItem, postOK := homeMap[index]
			debugItem, debugOK := debugMap[index]
			
			if (postOK) {
				lastindex = index
			} else {
				postItem, postOK = homeMap[lastindex]
			}

			//Contextual commands that need to handle their own requirements
			switch command {
			case "pause":
				hc.togglepause()
				return
			case "resume":
				hc.pause(false)
				return
			case "rm":
				if !postOK && recentpost != nil {
					err = client.DeleteStatus(context.Background(), recentpost.ID)
					if err != nil {
						fmt.Println(err)
					}
					recentpost = nil
					return
				}
				if !postOK {
					fmt.Println("No recent status to delete or post index not valid")
					return
				}
				err = client.DeleteStatus(context.Background(), postItem.ID)
				if err != nil {
					fmt.Println(err)
				}
				return
			}

			if arguments == "" && !postOK {
				fmt.Printf("%v requires an argument\n", command)
				return
			}

			//Commands that don't take an index
			switch command {
			case "dm":
				recentpost, err = postStatus(arguments, *client, "direct")
				if err != nil {
					fmt.Println(err)
				}
				return
				if !postOK && !debugOK {
					fmt.Printf("\"%v\" not a valid index\n", index)
					return
				}
			}

			//Commands that accept debug indexes
			switch command {
			case "examine":
				if postOK {
					PrintObjectProperties(postItem, debugMap)
					return
				}
				PrintObjectProperties(debugItem, debugMap)
				return
			}

			//if a user passes a debug index to a status command
			if !postOK {
				fmt.Printf("\"%v\" not a valid post index\n", index)
				return
			}
			//Commands require status indexes
			switch command {
			case "like":
				_, err := client.Favourite(context.Background(), postItem.ID)
				if err != nil {
					printMastodonErr(err)
				} else {
					fmt.Printf(hc.formatFavorite(postItem, arguments) + "\n")
				}
				return
			case "mark":
				_, err := client.Bookmark(context.Background(), postItem.ID)
				if err != nil {
					printMastodonErr(err)
				} else {
					fmt.Printf(hc.formatBookmark(postItem, arguments) + "\n")
				}
				return
			case "unmark":
				postCopy, err := client.GetStatus(context.Background(), postItem.ID)
				if postCopy.Bookmarked.(bool) && err == nil {
					fmt.Printf("Post not bookmarked.\n")
					return
				}
				_, err = client.Unbookmark(context.Background(), postItem.ID)
				if err != nil {
					printMastodonErr(err)
				} else {
					fmt.Printf(hc.formatUnbookmark(postItem, arguments) + "\n")
				}
				return
			case "open":
				url := fmt.Sprintf("%v/statuses/%v", client.Config.Server, postItem.ID)
				cmd := exec.Command("open", url, "-a", "Eldritch Café")
				cmd.Run()
				return
			case "url":
				_, indexstr, _ := strings.Cut(arguments, " ")
				urlindex, err := strconv.Atoi(indexstr)
				if err != nil {
					urlindex = 1
				}
				if urlindex > len(hc.urlMap[index]) {
					fmt.Printf("Bad url index\n")
					return
				}
				cmd := exec.Command("open", hc.urlMap[index][urlindex-1])
				cmd.Run()
				return
			case "preview":
				err := hc.previewPostImages(postItem, "open -W -a \"Quick Look\"")
				if err != nil {
					fmt.Printf("Image preview failed: %v\n", err)
				}
				return
			case "import":
				err := hc.previewPostImages(postItem, "shortcuts run \"media collector\" --input-path")
				if err != nil {
					fmt.Printf("Image preview failed: %v\n", err)
				}
				return
			case "download":
				savePostImages(postItem, "/Users/penny/Downloads/")
				return
			case "rt":
				rtStatus, err := client.Reblog(context.Background(), postItem.ID)
				if err != nil {
					fmt.Println(err)
					return
				}
				recentpost = rtStatus
				saveWorkRef(homeMap, rtStatus, postref)
				hc.printPost("?"+postref, rtStatus)
				postref = IncrementString(postref)
				return
			case "parent":
				if postItem.InReplyToID == nil {
					fmt.Printf("%v doesn't have a parent\n", index)
					return
				}
				parentStatus, _ := client.GetStatus(context.Background(), mastodon.ID(postItem.InReplyToID.(string)))
				saveWorkRef(homeMap, parentStatus, postref)
				hc.printPost("?"+postref, parentStatus)
				postref = IncrementString(postref)
				return
			case "children":
				context, err := client.GetStatusContext(context.Background(), postItem.ID)
				if err != nil {
					fmt.Println(err)
					return
				}
				if len(context.Descendants) == 0 {
					fmt.Printf("\"%v\" has no children\n")
				}
				for post := range context.Descendants {
					saveWorkRef(homeMap, context.Descendants[post], postref)
					hc.printPost("?"+postref, context.Descendants[post])
					postref = IncrementString(postref)
				}
				return
			case "thread":
				context, err := client.GetStatusContext(context.Background(), postItem.ID)
				if err != nil {
					fmt.Println(err)
					return
				}
				hc.pause(true) // pause so user can read the thread
				for post := range context.Ancestors {
					saveWorkRef(homeMap, context.Ancestors[post], postref)
					hc.printPost("?"+postref, context.Ancestors[post])
					postref = IncrementString(postref)
				}

				hc.printPost(index, postItem)

				for post := range context.Descendants {
					saveWorkRef(homeMap, context.Descendants[post], postref)
					hc.printPost("?"+postref, context.Descendants[post])
					postref = IncrementString(postref)
				}
				return
			case "account":
				account := postItem.Account
				fmt.Printf(formatAccount(&account))
				return
			}
			
			//Posts that need an index and an argument
			if content == "" {
				fmt.Printf("\"%v\" requires an argument\n", command)
				return
			}
			
			switch command {
			case "reply":
				recentpost, err = postReply(content, *client, postItem.ID, currentUser.ID, postItem)
				if err != nil {
					fmt.Println(err)
				}
				return
			}
		}()
	}
}