shithub: hell

ref: e93a019a1689d280d8ef5cdb9a19dd43f18a1ee3
dir: /hellclient.go/

View raw version
package main

import (
	"context"
	"io"
	"os"
	"sync"
	"time"

	mastodon "codeberg.org/penny64/hellclient-go-mastodon"
)

var EOF = io.EOF


type postref struct {
	prefix  string
	ref     string
	postmap map[string]*mastodon.Status
	urlmap  map[string][]string
}

type Hellclient struct {
	//if you're gonna touch or read anything here lock the mutex with hc.lock()
	isPaused      bool
	rl            *readline
	prompt        *PromptBar
	attacher      *StatusAttachmentHolder
	client        *mastodon.Client
	currentuser   *mastodon.Account
	dispatch      chan *mastodon.Toot
	jobdispatch   chan Job
	block         sync.Mutex
	recentpost    *mastodon.Status
	preferences   *Hellprefs
	multiLineMode bool
	configPath    string

	//Global status map for status indexes
	//Needs to be converted to a postref struct
	homeMap map[string]*mastodon.Status
	homeref *postref
	//Contextual indexes for commands
	ctxref       *postref
	debugMap     map[string]any
	actionBuffer []func()

	//pointer to our current page item
	page  *Page
	stats *Stats
}

type Stats struct {
	slock            sync.Mutex
	APICalls         int64
	IncomingStatuses int64
	StartedTime      time.Time
}

// Use this to make private versions of runes to stop default behavior
func toPUA(r rune) rune {
	const (
		puaStart = 0xE000
		puaEnd   = 0xF8FF
		puaSize  = puaEnd - puaStart + 1
	)
	return rune(int32(puaStart) + (int32(r) % int32(puaSize)))
}

func NewHellclient() (*Hellclient, error) {
	var hc Hellclient
	config := &Config{
		Prompt: "Hell> ",
		FuncFilterInputRune: func(r rune) (rune, bool) {
			if r == CharCtrlJ {
				hc.multiLineMode = !hc.multiLineMode
				hc.prompt.UpdatePrompt()
				return r, false
			}
			if r == CharInterrupt {
				return toPUA(r), true
			}
			if r == CharEnter && hc.multiLineMode {
				return toPUA(r), true
			}
			return r, true
		},
		Listener: func(line []rune, pos int, key rune) ([]rune, int, bool) {
			if key == toPUA(CharEnter) && hc.multiLineMode {
				// handle multi-line input
				line = line[:len(line)-1]
				line = append(line, '\n')
				return line, pos, true
			}

			//If we get an interupt just clear the line if it's not empty
			if key == toPUA(CharInterrupt) {
				if len(line) > 1 {
					return nil, 0, true
				}
				hc.rl.Close()
				os.Exit(0)
			}
			return nil, 0, false
		},
	}
	rl, err := NewReadline(config)
	if err != nil {
		return nil, err
	}

	account, configpath, err := loadConfig()
	if err != nil {
		return nil, err
	}
	client := initClient(account)
	currentuser, err := client.GetAccountCurrentUser(context.Background())
	if err != nil {
		return nil, err
	}

	dispatch := make(chan *mastodon.Toot, 15)
	jobdispatch := make(chan Job)

	defer func() {
		//Got some stuff to do when we're done
		hc.prompt.UpdatePrompt()
		//start up post dispatcher
		go hc.clientDispatch()

		markers, err := hc.client.GetTimelineMarkers(context.Background(), []string{"home"})
		if err != nil {
			return
		}

		initReferenceSystem()

		lastReadID := markers["home"].LastID
		statuses, err := hc.GetStatusesSince(lastReadID, hc.client.GetTimelineHome)
		if err != nil {
			return
		}
		if len(statuses) > 0 {
			hc.updateReadMarker(&statuses[len(statuses)-1].ID, "home")
		}

		for _, status := range statuses {
			hc.printAndIncrement(hc.ctxref, status)
		}
		//Record start time when everything is set up and connected
		hc.stats.StartedTime = time.Now()
	}()

	homeMap := make(map[string]*mastodon.Status)
	urlMap := make(map[string][]string)

	ctxref := &postref{
		prefix:  "?",
		ref:     "a",
		postmap: homeMap,
		urlmap:  urlMap,
	}

	homeref := &postref{
		ref:     "a",
		postmap: homeMap,
		urlmap:  urlMap,
	}

	prompt := &PromptBar{prompt: "Hell> ", rl: rl}

	attacher := &StatusAttachmentHolder{Hellclient: &hc}

	prompt.items = []PromptItem{
		&NotificationCounter{Hellclient: &hc},
		&MultiLineIndicator{Hellclient: &hc},
		&PauseIndicator{Hellclient: &hc},
		attacher,
	}

	debugMap := make(map[string]any)
	prefs := &account.Preferences
	hc = Hellclient{rl: rl,
		homeMap:     homeMap,
		ctxref:      ctxref,
		homeref:     homeref,
		prompt:      prompt,
		debugMap:    debugMap,
		isPaused:    false,
		client:      client,
		currentuser: currentuser,
		dispatch:    dispatch,
		jobdispatch: jobdispatch,
		preferences: prefs,
		stats:       &Stats{},
		configPath:  configpath,
		attacher:    attacher,
	}
	return &hc, nil
}

func (hc *Hellclient) pause(on bool) {
	hc.isPaused = on
	hc.prompt.UpdatePrompt()
	hc.printPauseBuffer()
}

func (hc *Hellclient) togglepause() {
	hc.isPaused = !hc.isPaused
	hc.prompt.UpdatePrompt()
	hc.printPauseBuffer()
}

func (hc *Hellclient) printPauseBuffer() {
	if !hc.isPaused {
		for _, action := range hc.actionBuffer {
			action()
		}
		hc.actionBuffer = nil
	}
}

func (hc *Hellclient) lock() {
	hc.block.Lock()
}

func (hc *Hellclient) unlock() {
	hc.block.Unlock()
}