ref: 73c533fd15674bab9fa0b33e1a133a9b91e812f7
dir: /main.go/
package main
import (
"context"
"fmt"
"io"
"os"
"os/exec"
"strconv"
"strings"
"github.com/k3a/html2text"
"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
lastindex := ""
recentpost := &hc.recentpost
if err != nil {
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 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 != "" {
hc.dispatchStatus(fmt.Sprintf(line), "public")
return
}
hc.lock()
defer hc.unlock()
index, content, _ := strings.Cut(arguments, " ")
postItem, postOK := homeMap[index]
debugItem, debugOK := debugMap[index]
//If there's no index selected load the last post we operated on
if postOK {
lastindex = index
} else {
postItem, postOK = homeMap[lastindex]
}
//Okay now see if the post we end up with is a reblog
if postOK {
if postItem.Reblog != nil {
postItem = postItem.Reblog
}
}
//Contextual commands that need to handle their own requirements
switch command {
case "notice":
notifications, _ := hc.GetUnreadNotifications()
hc.PrintNotifications(notifications)
err = hc.SetNotificationsRead(notifications[0].ID)
if err != nil {
fmt.Println(err)
return
}
hc.updatePrompt()
hc.pause(true)
return
case "pause":
hc.togglepause()
return
case "resume":
hc.pause(false)
return
case "rm":
if index == "" && *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":
hc.dispatchStatus(arguments, "direct")
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 {
hc.PrintObjectProperties(postItem)
return
}
hc.PrintObjectProperties(debugItem)
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
hc.printAndIncrement(hc.ctxref, rtStatus)
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)))
hc.printAndIncrement(hc.ctxref, parentStatus)
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 {
hc.printAndIncrement(hc.ctxref, context.Descendants[post])
}
return
case "edit":
if content == "" || content == " " {
rl.SetDefault(fmt.Sprintf("/edit %v %v", index, html2text.HTML2Text(postItem.Content)))
return
}
var MediaIDs []mastodon.ID
for _, media := range postItem.MediaAttachments {
MediaIDs = append(MediaIDs, media.ID)
}
toot := &mastodon.Toot{
Status: content,
MediaIDs: MediaIDs,
Sensitive: postItem.Sensitive,
SpoilerText: postItem.SpoilerText,
Visibility: postItem.Visibility,
Language: postItem.Language,
}
if postItem.InReplyToID != nil {
id := mastodon.ID(postItem.InReplyToID.(string))
toot.InReplyToID = id
}
_, err = client.UpdateStatus(context.Background(), toot, postItem.ID)
if err != nil {
fmt.Println(err)
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 {
hc.printAndIncrement(hc.ctxref, context.Ancestors[post])
}
hc.printPost(index, postItem)
for post := range context.Descendants {
hc.printAndIncrement(hc.ctxref, context.Descendants[post])
}
return
case "account":
account := postItem.Account
fmt.Printf(formatAccount(&account))
return
case "fpost":
_, err := hc.filterStatus(postItem)
if err != nil {
fmt.Printf("Error filtering post: %v\n", err)
return
}
url := fmt.Sprintf("%v/statuses/%v", client.Config.Server, postItem.ID)
fmt.Printf("Filtered %v\n", url)
return
case "ufpost":
_, err := hc.unfilterStatus(postItem)
if err != nil {
fmt.Printf("Error unfiltering post: %v\n", err)
return
}
fmt.Printf(hc.formatWithPrefix(postItem, index, "Unfiltered:") + "\n")
return
}
//Posts that need an index and an argument
if content == "" {
fmt.Printf("\"%v\" requires an argument\n", command)
return
}
switch command {
case "reply":
hc.dispatchReply(content, postItem.ID, postItem)
return
}
}()
}
}