ref: 130a67e3ea3b577055f9779f90a5d10fb8f1d85e
dir: /hellclient.go/
package main
import (
"context"
"io"
"os"
"sync"
"time"
mastodon "codeberg.org/penny64/hellclient-go-mastodon"
"github.com/ergochat/readline"
)
var (
ErrInterrupt = readline.ErrInterrupt
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.Instance
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 := &readline.Config{
Prompt: "Hell> ",
FuncFilterInputRune: func(r rune) (rune, bool) {
if r == readline.CharCtrlJ {
hc.multiLineMode = !hc.multiLineMode
hc.prompt.UpdatePrompt()
return r, false
}
if r == readline.CharInterrupt {
return toPUA(r), true
}
if r == readline.CharEnter && hc.multiLineMode {
return toPUA(r), true
}
return r, true
},
Listener: func(line []rune, pos int, key rune) ([]rune, int, bool) {
if key == toPUA(readline.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(readline.CharInterrupt) {
if len(line) > 1 {
return nil, 0, true
}
hc.rl.Close()
os.Exit(0)
}
return nil, 0, false
},
}
rl, err := readline.NewEx(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()
}