shithub: hell

Download patch

ref: a6ca677b242d01fac6c36c00f755416e98ec296c
parent: 64e034333bc1a4e35c778bb4508d1f4be913722c
author: penny <penny@limitedideas.org>
date: Mon Sep 22 11:19:22 EDT 2025

Add job dispatch system for API calls and stats

--- a/commands.go
+++ b/commands.go
@@ -4,7 +4,7 @@
 	"strings"
 )
 
-var commands = []string{"examine", "reply", "like", "thread", "open", "preview", "download", "dm", "rt", "parent", "children", "rm", "mark", "unmark", "account", "vim", "import", "pause", "resume", "url", "fpost", "ufpost", "edit", "notice"}
+var commands = []string{"examine", "reply", "like", "thread", "open", "preview", "download", "dm", "rt", "parent", "children", "rm", "mark", "unmark", "account", "vim", "import", "pause", "resume", "url", "fpost", "ufpost", "edit", "notice", "stats"}
 
 func processInput(input string) (command string, arguments string, found bool) {
 
--- a/dispatch.go
+++ b/dispatch.go
@@ -1,18 +1,53 @@
 package main
 
 import (
+	"context"
 	"fmt"
+	"sync"
 	"time"
 
 	"github.com/mattn/go-mastodon"
 )
 
-func (hc *Hellclient) queueManager() {
+const ()
 
+type Job interface {
+	Do()
+	Init()
+	Wait()
 }
 
+type StatusJob struct {
+	context context.Context
+	status  *mastodon.Status
+	ID      *mastodon.ID
+	jobfunc func(context.Context, *mastodon.ID)
+	err     error
+}
+
+type GenericJob struct {
+	jobfunc func(job *GenericJob)
+	result  string
+	err     error
+	wg      sync.WaitGroup
+}
+
+func (job *GenericJob) Wait() {
+	job.wg.Wait()
+}
+
+func (job *GenericJob) Init() {
+	job.wg.Add(1)
+}
+
+func (job *GenericJob) Do() {
+	job.jobfunc(job)
+	job.wg.Done()
+}
+
 func (hc *Hellclient) clientDispatch() {
 	var tootQueue []*mastodon.Toot
+	var jobQueue []Job
 	//Last time we sent an API call
 	var lastfire time.Time
 	//Last time the user sent us a line
@@ -28,16 +63,26 @@
 		tootQueue = append(tootQueue, statustoot)
 	}
 
+	receiveJob := func(job Job) {
+		jobQueue = append(jobQueue, job)
+	}
+
 	for {
 		select {
+		case job := <-hc.jobdispatch:
+			receiveJob(job)
 		case statustoot := <-hc.dispatch:
 			receiveStatus(statustoot)
 		//API delay needs to be tracked without being reset by new inputs
-		case <-time.After(hc.preferences.apidelay - time.Since(lastfire)):
-			if 1 > len(tootQueue) {
-				//Queue is empty, block the loop until we get a new input
-				statustoot := <-hc.dispatch
-				receiveStatus(statustoot)
+		case <-time.After(time.Duration((hc.preferences.Apidelay * time.Second)) - time.Since(lastfire)):
+			if 1 > len(tootQueue) && 1 > len(jobQueue) {
+				//Queues are empty, block the loop until we get a new input
+				select {
+				case statustoot := <-hc.dispatch:
+					receiveStatus(statustoot)
+				case job := <-hc.jobdispatch:
+					receiveJob(job)
+				}
 				break
 			}
 			//User is sending lines faster than one a second, flood control
@@ -58,17 +103,28 @@
 				break
 			}
 
-			toot := tootQueue[0]
-			lastfire = time.Now()
-			var err error
-			status, err := postStatusDetailed(*hc.client, *toot)
-			if err != nil {
-				fmt.Println(err)
+			hc.stats.slock.Lock()
+			hc.stats.APICalls++
+			hc.stats.slock.Unlock()
+			if len(tootQueue) > 0 {
+				toot := tootQueue[0]
+				lastfire = time.Now()
+				var err error
+				status, err := postStatusDetailed(*hc.client, *toot)
+				if err != nil {
+					fmt.Println(err)
+				}
+				hc.lock()
+				hc.recentpost = status
+				hc.unlock()
+				tootQueue = tootQueue[1:]
+				break
 			}
-			hc.lock()
-			hc.recentpost = status
-			hc.unlock()
-			tootQueue = tootQueue[1:]
+			//We wouldn't be here if there wasn't something in some queue
+			job := jobQueue[0]
+			jobQueue = jobQueue[1:]
+			job.Do()
+			lastfire = time.Now()
 		}
 	}
 }
@@ -79,4 +135,18 @@
 
 func (hc *Hellclient) dispatchReply(posttext string, replyto mastodon.ID, postItem *mastodon.Status) {
 	hc.dispatch <- postReply(posttext, replyto, hc.currentuser.ID, postItem)
+}
+
+func (hc *Hellclient) dispatchJob(job Job) {
+	job.Init()
+	hc.jobdispatch <- job
+}
+
+func (hc *Hellclient) dispatchFunc(enclosure func(*GenericJob)) Job {
+	noticeJob := &GenericJob{
+		jobfunc: enclosure,
+	}
+	noticeJob.Init()
+	hc.jobdispatch <- noticeJob
+	return noticeJob
 }
--- a/hellclient.go
+++ b/hellclient.go
@@ -7,7 +7,6 @@
 	"os"
 	"strings"
 	"sync"
-	"time"
 
 	"github.com/ergochat/readline"
 	"github.com/mattn/go-mastodon"
@@ -31,6 +30,7 @@
 	client        *mastodon.Client
 	currentuser   *mastodon.Account
 	dispatch      chan *mastodon.Toot
+	jobdispatch   chan Job
 	block         sync.Mutex
 	recentpost    *mastodon.Status
 	preferences   *Hellprefs
@@ -45,10 +45,13 @@
 	urlMap       map[string][]string
 	debugMap     map[string]interface{}
 	actionBuffer []func()
+
+	stats *Stats
 }
 
-type Hellprefs struct {
-	apidelay time.Duration
+type Stats struct {
+	slock    sync.Mutex
+	APICalls int64
 }
 
 // Use this to make private versions of runes to stop default behavior
@@ -113,6 +116,7 @@
 	}
 
 	dispatch := make(chan *mastodon.Toot, 15)
+	jobdispatch := make(chan Job)
 
 	defer func() {
 		//Got some stuff to do when we're done
@@ -128,7 +132,7 @@
 		initReferenceSystem()
 
 		lastReadID := markers["home"].LastID
-		statuses, err := GetStatusesSince(lastReadID, hc.client.GetTimelineHome)
+		statuses, err := hc.GetStatusesSince(lastReadID, hc.client.GetTimelineHome)
 		if err != nil {
 			return
 		}
@@ -156,7 +160,7 @@
 
 	debugMap := make(map[string]interface{})
 	urlMap := make(map[string][]string)
-	prefs := &Hellprefs{apidelay: time.Second * 3}
+	prefs := &account.Preferences
 	hc = Hellclient{rl: rl,
 		homeMap:     homeMap,
 		ctxref:      ctxref,
@@ -167,7 +171,9 @@
 		client:      client,
 		currentuser: currentuser,
 		dispatch:    dispatch,
+		jobdispatch: jobdispatch,
 		preferences: prefs,
+		stats:       &Stats{},
 	}
 	return &hc, nil
 }
--- a/main.go
+++ b/main.go
@@ -99,11 +99,16 @@
 
 			//Contextual commands that need to handle their own requirements
 			switch command {
+			case "stats":
+				hc.stats.slock.Lock()
+				fmt.Printf("API Calls: %v\n", hc.stats.APICalls)
+				hc.stats.slock.Unlock()
+				return
 			case "notice":
 				notifications, err := hc.GetUnreadNotifications()
 				if len(notifications) > 0 {
 					hc.PrintNotifications(notifications)
-					err = hc.SetNotificationsRead(notifications[0].ID)
+					err = hc.SetNotificationsRead(notifications[len(notifications)-1].ID)
 				}
 				if err != nil {
 					fmt.Println(err)
@@ -119,8 +124,12 @@
 				hc.pause(false)
 				return
 			case "rm":
-				if index == "" && *recentpost != nil {
+				deletefunc := func(job *GenericJob) {
 					err = client.DeleteStatus(context.Background(), (*recentpost).ID)
+				}
+				if index == "" && *recentpost != nil {
+					deleteJob := hc.dispatchFunc(deletefunc)
+					deleteJob.Wait()
 					if err != nil {
 						fmt.Println(err)
 					}
@@ -131,7 +140,8 @@
 					fmt.Println("No recent status to delete or post index not valid")
 					return
 				}
-				err = client.DeleteStatus(context.Background(), postItem.ID)
+				deleteJob := hc.dispatchFunc(deletefunc)
+				deleteJob.Wait()
 				if err != nil {
 					fmt.Println(err)
 				}
--- a/mastodon.go
+++ b/mastodon.go
@@ -113,7 +113,7 @@
 				hc.urlMap[index] = append(hc.urlMap[index], href)
 				refnode := &html.Node{
 					Type: html.TextNode,
-					Data: fmt.Sprintf("[%v]", len(hc.urlMap[index]))}
+					Data: fmt.Sprintf(" [%v]", len(hc.urlMap[index]))}
 				if node.Parent != nil {
 					node.Parent.InsertBefore(refnode, node.NextSibling)
 				}
@@ -459,7 +459,16 @@
 		Timeline: timeline,
 		ID:       *ID,
 	}
-	hc.client.SetTimelineMarkers(context.Background(), &[]mastodon.Marker{*marker})
+	var err error
+	setmarker := func(job *GenericJob) {
+		err = hc.client.SetTimelineMarkers(context.Background(), &[]mastodon.Marker{*marker})
+	}
+	job := hc.dispatchFunc(setmarker)
+	job.Wait()
+	if err != nil {
+		fmt.Printf("Error: %s", err)
+	}
+
 }
 
 // Periodically set the timeline read marker to the most recent status
--- a/notifications.go
+++ b/notifications.go
@@ -15,7 +15,17 @@
 
 	LastID := markers["notifications"].LastID
 	page := &mastodon.Pagination{SinceID: LastID}
-	notificationbuffer, err := hc.client.GetNotificationsExclude(context.Background(), nil, page)
+
+	var notificationbuffer []*mastodon.Notification
+
+	noticeFunc := func(job *GenericJob) {
+		notificationbuffer, err = hc.client.GetNotificationsExclude(context.Background(), nil, page)
+	}
+	noticeJob := hc.dispatchFunc(noticeFunc)
+	noticeJob.Wait()
+	if err != nil {
+		return
+	}
 
 	//Reverse to print from oldest to newest
 	for i := len(notificationbuffer) - 1; i >= 0; i-- {
--- a/status.go
+++ b/status.go
@@ -3,12 +3,11 @@
 import (
 	"context"
 	"fmt"
-	"time"
 
 	"github.com/mattn/go-mastodon"
 )
 
-func GetStatusesSince(ID mastodon.ID, GetTimeline func(ctx context.Context, pg *mastodon.Pagination) ([]*mastodon.Status, error)) ([]*mastodon.Status, error) {
+func (hc *Hellclient) GetStatusesSince(ID mastodon.ID, GetTimeline func(ctx context.Context, pg *mastodon.Pagination) ([]*mastodon.Status, error)) ([]*mastodon.Status, error) {
 
 	page := &mastodon.Pagination{MinID: ID}
 
@@ -16,7 +15,11 @@
 	var statuses []*mastodon.Status
 
 	for {
-		statusbatch, err := GetTimeline(context.Background(), page)
+		var statusbatch []*mastodon.Status
+
+		statusfunc := func(job *GenericJob) { statusbatch, err = GetTimeline(context.Background(), page) }
+		statusjob := hc.dispatchFunc(statusfunc)
+		statusjob.Wait()
 		if err != nil {
 			return statuses, err
 		}
@@ -32,7 +35,6 @@
 		//I really don't understand the server's results but erase the max ID and it paginates up I don't know man
 		page.MaxID = ""
 		fmt.Printf("Loaded %v statuses....", len(statuses))
-		time.Sleep(1 * time.Second)
 	}
 
 	return statuses, err
--