diff --git a/broker/job.go b/broker/job.go index 5678184..66731c1 100644 --- a/broker/job.go +++ b/broker/job.go @@ -3,7 +3,7 @@ package broker import ( "fmt" "local/truckstop/config" - "log" + "local/truckstop/logtr" "strings" "time" ) @@ -65,7 +65,7 @@ func (j Job) FormatMultilineText() string { out := "" clients := config.Clients(j.Pickup.Date) for k := range clients { - log.Printf("job multiline: %+v contains %s then use %v", clients[k].States, j.Pickup.State, k) + logtr.Debugf("job multiline: %+v contains %s then use %v", clients[k].States, j.Pickup.State, k) if strings.Contains(fmt.Sprint(clients[k].States), j.Pickup.State) { if len(out) > 0 { out += "\n" diff --git a/broker/ntgvision.go b/broker/ntgvision.go index 49b22bb..9e46b58 100644 --- a/broker/ntgvision.go +++ b/broker/ntgvision.go @@ -8,7 +8,7 @@ import ( "io" "io/ioutil" "local/truckstop/config" - "log" + "local/truckstop/logtr" "net/http" "time" ) @@ -120,7 +120,7 @@ func (ntg NTGVision) searchJob(id int64) (io.ReadCloser, error) { } defer resp.Body.Close() b, _ := ioutil.ReadAll(resp.Body) - log.Printf("fetch ntg job info %+v: %d: %s", request, resp.StatusCode, b) + logtr.Debugf("fetch ntg job info %+v: %d: %s", request, resp.StatusCode, b) if resp.StatusCode > 400 && resp.StatusCode < 500 && resp.StatusCode != 404 && resp.StatusCode != 410 { return nil, ErrNoAuth } @@ -149,7 +149,7 @@ func (ntgJob *ntgVisionJob) Job() Job { secrets: func() interface{} { jobInfo, err := ntgJob.JobInfo() if err != nil { - log.Printf("failed to get jobinfo: %v", err) + logtr.Errorf("failed to get jobinfo: %v", err) return nil } return jobInfo.String() @@ -199,7 +199,7 @@ func (ntg NTGVision) Search(states []config.State) ([]Job, error) { return nil, err } - log.Printf("ntg search for %+v: %s", states, b) + logtr.Debugf("ntg search for %+v: %s", states, b) var ntgjobs []ntgVisionJob err = json.Unmarshal(b, &ntgjobs) @@ -243,7 +243,7 @@ func (ntg NTGVision) search(states []config.State) (io.ReadCloser, error) { } func (ntg NTGVision) refreshAuth() error { - log.Printf("refreshing ntg auth...") + logtr.Infof("refreshing ntg auth...") b, _ := json.Marshal(map[string]string{ "username": config.Get().Brokers.NTG.Username, "password": config.Get().Brokers.NTG.Password, diff --git a/config.json b/config.json index 6f679d6..703744f 100644 --- a/config.json +++ b/config.json @@ -1,4 +1,8 @@ { + "Log": { + "Path": "/tmp/truckstop.log", + "Level": "err" + }, "Interval": { "Input": "5s..10s", "OK": "6h0m0s..6h0m0s", @@ -21,7 +25,7 @@ "Pickup": false, "Dropoff": false, "Pathed": { - "Enabled": true, + "Enabled": false, "DirectionsURIFormat": "https://maps.googleapis.com/maps/api/directions/json?origin=%s\u0026destination=%s\u0026mode=driving\u0026key=AIzaSyBkACm-LQkoSfsTO5_XAzBVZE9-JQzcNkg", "PathedURIFormat": "https://maps.googleapis.com/maps/api/staticmap?size=250x250\u0026path=%s\u0026format=jpeg\u0026maptype=roadmap\u0026key=AIzaSyBkACm-LQkoSfsTO5_XAzBVZE9-JQzcNkg\u0026size=250x250\u0026markers=%s|%s", "Zoom": { @@ -48,7 +52,7 @@ "Message": { "Matrix": { "ReceiveEnabled": true, - "Mock": false, + "Mock": true, "Homeserver": "https://m.bltrucks.top", "Username": "@bot.m.bltrucks.top", "Token": "mvDWB96KXMF8XhOam8EC5XVdQvSEw0CDeClcSWocBcYkwZX3FPNWZ5uOnQk2EmT1cjpzfeuD7gDYPPjOuyZlI3bE9TE35UjNOlZgi0Tugm25s91iVsbIF6kMZsCIhVMSmEf6w3jxX6wQYOWvmDZ4mu6f5c8wr221EMDcOpEzQV09d1zuBSWgKLBgjqAkYHJZ5dTRIWpEDpPgujhOFZa2ld1HiAOxrJKlIrlfDBN0CUsTlGOGplujDAr4VtpFzNRS", @@ -56,7 +60,7 @@ "Room": "!OYZqtInrBCn1cyz90D:m.bltrucks.top" } }, - "Once": false, + "Once": true, "Brokers": { "NTG": { "JobInfo": true, @@ -66,4 +70,4 @@ "Password": "thumper123" } } -} \ No newline at end of file +} diff --git a/config/config.go b/config/config.go index 91a4271..3e63b8e 100644 --- a/config/config.go +++ b/config/config.go @@ -4,12 +4,17 @@ import ( "encoding/json" "io/ioutil" "local/storage" + "local/truckstop/logtr" "os" "sync" "time" ) type Config struct { + Log struct { + Path string + Level logtr.Level + } Interval struct { Input Duration OK Duration @@ -121,6 +126,8 @@ func Refresh() error { if err := json.Unmarshal(b, &c); err != nil { return err } + logtr.SetLogpath(c.Log.Path) + logtr.SetLevel(c.Log.Level) if live.db != nil { live.db.Close() } diff --git a/logtr/log.go b/logtr/log.go new file mode 100644 index 0000000..c2c71d2 --- /dev/null +++ b/logtr/log.go @@ -0,0 +1,113 @@ +package logtr + +import ( + "encoding/json" + "errors" + "fmt" + "io" + "os" + "strings" + "sync" + "time" +) + +type Level int + +const ( + VERBOSE = Level(7) + DEBUG = Level(8) + INFO = Level(9) + ERROR = Level(10) +) + +func (l Level) MarshalJSON() ([]byte, error) { + return json.Marshal(l.String()) +} + +func (l *Level) UnmarshalJSON(b []byte) error { + var s string + if err := json.Unmarshal(b, &s); err != nil { + return err + } + s = strings.TrimSpace(s) + s = strings.ToUpper(s) + if len(s) > 3 { + s = s[:3] + } + for i := 0; i < int(ERROR)+5; i++ { + l2 := Level(i) + if l2.String() == s { + *l = l2 + return nil + } + } + return errors.New("unknown log level: " + s) +} + +var logger io.Writer = os.Stderr +var loggerPath string = "" +var lock = &sync.Mutex{} +var level Level = INFO + +func SetLogpath(p string) { + lock.Lock() + defer lock.Unlock() + if p == loggerPath { + return + } + f, err := os.OpenFile(p, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + panic(err) + } + logger = f + loggerPath = p +} + +func SetLevel(l Level) { + level = l +} + +func logf(l Level, format string, args []interface{}) { + format = fmt.Sprintf("%v: %v: %s\n", time.Now().Format("15:04:05"), l.String(), strings.TrimSpace(format)) + lock.Lock() + defer lock.Unlock() + if level <= l { + fmt.Fprintf(os.Stderr, format, args...) + } + fmt.Fprintf(logger, format, args...) +} + +func Infof(format string, args ...interface{}) { + logf(INFO, format, args) +} + +func Printf(format string, args ...interface{}) { + Infof(format, args...) +} + +func Debugf(format string, args ...interface{}) { + logf(DEBUG, format, args) +} + +func Verbosef(format string, args ...interface{}) { + logf(VERBOSE, format, args) +} + +func Errorf(format string, args ...interface{}) { + logf(ERROR, format, args) +} + +func (l Level) String() string { + switch l { + case ERROR: + return "ERR" + case INFO: + return "INF" + case DEBUG: + return "DEB" + case VERBOSE: + return "VER" + default: + return "?" + } +} diff --git a/main.go b/main.go index 90f48b2..090139b 100644 --- a/main.go +++ b/main.go @@ -7,8 +7,8 @@ import ( "local/storage" "local/truckstop/broker" "local/truckstop/config" + "local/truckstop/logtr" "local/truckstop/message" - "log" "net/http" "net/url" "regexp" @@ -34,12 +34,12 @@ func main() { for { time.Sleep(config.Get().Interval.Input.Get()) if err := config.Refresh(); err != nil { - log.Println(err) + logtr.Errorf("failed parsing config: %v", err) } else { if config.Get().Message.Matrix.ReceiveEnabled { lock.Lock() if err := matrixrecv(); err != nil { - log.Print(err) + logtr.Errorf("failed receiving and parsing matrix: %v", err) } lock.Unlock() } @@ -53,15 +53,15 @@ func main() { } func matrixrecv() error { - log.Printf("checking matrix...") - defer log.Printf("/checking matrix...") + logtr.Debugf("checking matrix...") + defer logtr.Debugf("/checking matrix...") sender := message.NewMatrix() messages, err := sender.Receive() if err != nil { return err } func() { - log.Printf("looking for help") + logtr.Debugf("looking for help") printed := false for _, msg := range messages { if !strings.HasPrefix(msg.Content, "!help") { @@ -71,26 +71,26 @@ func matrixrecv() error { db := config.Get().DB() if !printed { if _, err := db.Get(key); err == storage.ErrNotFound { - log.Printf("sending help") + logtr.Debugf("sending help") help := fmt.Sprintf("commands:\n...`!help` print this help\n...`!state nc NC nC Nc` set states for self\n...`!available 2022-12-31` set date self is available for work\n\nrun a command for someone else: `!state ga @caleb`") if err := sender.Send(help); err != nil { - log.Printf("failed to send help: %v", err) + logtr.Errorf("failed to send help: %v", err) } else { printed = true if err := db.Set(key, []byte{'k'}); err != nil { - log.Printf("failed to mark help given @%s: %v", key, err) + logtr.Errorf("failed to mark help given @%s: %v", key, err) } } } } else { if err := db.Set(key, []byte{'k'}); err != nil { - log.Printf("failed to mark help given @%s: %v", key, err) + logtr.Errorf("failed to mark help given @%s: %v", key, err) } } } }() func() { - log.Printf("looking for states") + logtr.Debugf("looking for states") db := config.Get().DB() states := map[string]map[config.State]struct{}{} for _, msg := range messages { @@ -108,13 +108,13 @@ func matrixrecv() error { } } if err := db.Set(key, []byte{'k'}); err != nil { - log.Printf("failed to mark state gathered @%s: %v", key, err) + logtr.Errorf("failed to mark state gathered @%s: %v", key, err) } } setNewStates(states) }() func() { - log.Printf("looking for pauses") + logtr.Debugf("looking for pauses") db := config.Get().DB() pauses := map[string]time.Time{} for _, msg := range messages { @@ -136,7 +136,7 @@ func matrixrecv() error { } } if err := db.Set(key, []byte{'k'}); err != nil { - log.Printf("failed to mark state gathered @%s: %v", key, err) + logtr.Errorf("failed to mark state gathered @%s: %v", key, err) } } setNewPauses(pauses) @@ -149,7 +149,7 @@ func setNewPauses(pauses map[string]time.Time) { if len(pauses) == 0 { return } - log.Printf("set new pauses: %+v", pauses) + logtr.Debugf("set new pauses: %+v", pauses) conf := *config.Get() changed := map[string]time.Time{} for client, pause := range pauses { @@ -164,11 +164,11 @@ func setNewPauses(pauses map[string]time.Time) { if len(changed) == 0 { return } - log.Printf("updating config new pauses: %+v", conf) + logtr.Infof("updating config new pauses: %+v", conf) config.Set(conf) for client, pause := range changed { if err := sendNewPause(client, pause); err != nil { - log.Printf("failed to send new pause %s/%+v: %v", client, pause, err) + logtr.Errorf("failed to send new pause %s/%+v: %v", client, pause, err) } } } @@ -199,11 +199,11 @@ func setNewStates(states map[string]map[config.State]struct{}) { if len(changed) == 0 { return } - log.Printf("updating config new states: %+v", conf) + logtr.Infof("updating config new states: %+v", conf) config.Set(conf) for client, states := range changed { if err := sendNewStates(client, states); err != nil { - log.Printf("failed to send new states %s/%+v: %v", client, states, err) + logtr.Errorf("failed to send new states %s/%+v: %v", client, states, err) } } } @@ -230,7 +230,7 @@ func _main() error { for { err := _mainOne() if err != nil { - log.Println(err) + logtr.Errorf("failed _main: %v", err) } if config.Get().Once { time.Sleep(time.Second) @@ -246,15 +246,15 @@ func _main() error { } func _mainOne() error { - log.Println("config.refreshing...") + logtr.Debugf("config.refreshing...") if err := config.Refresh(); err != nil { return err } - log.Println("once...") + logtr.Debugf("once...") if err := once(); err != nil { return err } - log.Println("/_mainOne") + logtr.Debugf("/_mainOne") return nil } @@ -263,26 +263,26 @@ func once() error { if err != nil { return err } - log.Printf("once: all jobs: %+v", alljobs) + logtr.Debugf("once: all jobs: %+v", alljobs) newjobs, err := dropStaleJobs(alljobs) if err != nil { return err } - log.Printf("once: new jobs: %+v", newjobs) + logtr.Debugf("once: new jobs: %+v", newjobs) jobs, err := dropBanlistJobs(newjobs) if err != nil { return err } - log.Printf("once: loading job secrets: %+v", jobs) + logtr.Debugf("once: loading job secrets: %+v", jobs) for i := range jobs { jobs[i].Secrets() } - log.Printf("once: sending jobs: %+v", jobs) + logtr.Infof("once: sending jobs: %+v", jobs) for i := range jobs { if ok, err := sendJob(jobs[i]); err != nil { return err } else if ok { - log.Println("sent job", jobs[i]) + logtr.Debugf("sent job", jobs[i]) if err := config.Get().DB().Set(jobs[i].ID, []byte(`sent`)); err != nil { return err } @@ -327,7 +327,7 @@ func dropBanlistJobs(jobs []broker.Job) ([]broker.Job, error) { func sendJob(job broker.Job) (bool, error) { sender := message.NewMatrix() payload := job.FormatMultilineText() - log.Printf("once: send job %s if nonzero: %s", job.String(), payload) + logtr.Debugf("once: send job %s if nonzero: %s", job.String(), payload) if len(payload) == 0 { return false, nil } @@ -390,7 +390,7 @@ func sendJob(job broker.Job) (bool, error) { if maxLat-minLat <= maps.Pathed.Zoom.AcceptableLatLngDelta && maxLng-minLng <= maps.Pathed.Zoom.AcceptableLatLngDelta { uri = fmt.Sprintf("%s&zoom=%d", uri, maps.Pathed.Zoom.Override) } - log.Printf("sending pathed image: %s", uri) + logtr.Debugf("sending pathed image: %s", uri) if err := sender.SendImage(uri); err != nil { return true, err } @@ -398,14 +398,14 @@ func sendJob(job broker.Job) (bool, error) { } if maps.Pickup { uri := fmt.Sprintf(maps.URIFormat, pickup, pickup) - log.Printf("sending pickup image: %s", uri) + logtr.Debugf("sending pickup image: %s", uri) if err := sender.SendImage(uri); err != nil { return true, err } } if maps.Dropoff { uri := fmt.Sprintf(maps.URIFormat, dropoff, dropoff) - log.Printf("sending dropoff image: %s", uri) + logtr.Debugf("sending dropoff image: %s", uri) if err := sender.SendImage(uri); err != nil { return true, err } diff --git a/message/images.go b/message/images.go index 5448b9a..bfb91ba 100644 --- a/message/images.go +++ b/message/images.go @@ -7,7 +7,7 @@ import ( "fmt" "io/ioutil" "local/truckstop/config" - "log" + "local/truckstop/logtr" "mime/multipart" "net/http" "net/url" @@ -40,7 +40,7 @@ func uploadImage(b []byte) (string, error) { } else if s, ok := u.Query()["name"]; !ok { } else { name = s[0] - log.Printf("found name in upload uri: %s", name) + logtr.Debugf("found name in upload uri: %s", name) } part, err := writer.CreateFormFile("image", name) if err != nil { diff --git a/message/matrix.go b/message/matrix.go index c6f8ce2..37028f8 100644 --- a/message/matrix.go +++ b/message/matrix.go @@ -5,7 +5,7 @@ import ( "fmt" "io/ioutil" "local/truckstop/config" - "log" + "local/truckstop/logtr" "net/http" "regexp" "strings" @@ -45,7 +45,7 @@ func (m Matrix) Continuation() string { func (m *Matrix) Receive() ([]Message, error) { if m.mock { - log.Printf("matrix.Receive()") + logtr.Infof("matrix.Receive()") messages := make([]Message, 0) for k := range config.Get().Clients { messages = append(messages, Message{Timestamp: time.Now(), Sender: k, Content: "!state nc"}) @@ -75,10 +75,9 @@ func (m *Matrix) Receive() ([]Message, error) { if err != nil { return nil, err } - log.Printf("%s => {Start:%s End:%v};; %v, (%d)", m.Continuation(), result.Start, result.End, err, len(result.Chunk)) + logtr.Debugf("%s => {Start:%s End:%v};; %v, (%d)", m.Continuation(), result.Start, result.End, err, len(result.Chunk)) m.continuation = result.End for _, event := range result.Chunk { - //log.Printf("%+v", event) if _, ok := matrixIDs[event.Sender]; !ok { continue } @@ -91,7 +90,7 @@ func (m *Matrix) Receive() ([]Message, error) { } } clientChange := regexp.MustCompile("@[a-z]+$") - log.Printf("rewriting messages based on @abc") + logtr.Debugf("rewriting messages based on @abc") for i := range messages { if found := clientChange.FindString(messages[i].Content); found != "" { messages[i].Content = strings.TrimSpace(strings.ReplaceAll(messages[i].Content, found, "")) @@ -105,7 +104,7 @@ func (m *Matrix) Receive() ([]Message, error) { } messages[i].Content = strings.TrimSpace(messages[i].Content) } - log.Printf("rewriting messages based on ! CoMmAnD ...") + logtr.Debugf("rewriting messages based on ! CoMmAnD ...") for i := range messages { if strings.HasPrefix(messages[i].Content, "!") { messages[i].Content = "!" + strings.TrimSpace(messages[i].Content[1:]) @@ -118,7 +117,7 @@ func (m *Matrix) Receive() ([]Message, error) { func (m Matrix) Send(text string) error { if m.mock { - log.Printf("matrix.Send(%s)", text) + logtr.Infof("matrix.Send(%s)", text) return nil } c, err := m.getclient() @@ -131,7 +130,7 @@ func (m Matrix) Send(text string) error { func (m Matrix) SendImage(uri string) error { if m.mock { - log.Printf("matrix.SendImage(%s)", uri) + logtr.Infof("matrix.SendImage(%s)", uri) return nil } response, err := http.Get(uri) @@ -158,7 +157,7 @@ func (m Matrix) SendImage(uri string) error { } publicURI := mediaUpload.ContentURI resp, err := c.SendImage(m.room, "img", publicURI) - log.Printf("sent image %s => %s: %+v", uri, publicURI, resp) + logtr.Debugf("sent image %s => %s: %+v", uri, publicURI, resp) return err } diff --git a/todo.yaml b/todo.yaml index 0f56f66..4f5b9b0 100644 --- a/todo.yaml +++ b/todo.yaml @@ -1,6 +1,6 @@ todo: +- help() log on truckstop for stuff like perma 403 - TEST its falling apart -- figure out zoom on maps;; is there like an auto-zoom I can leverage? - mark jobs no longer avail by modifying in matrix - write-as for clients so ma can write to pa by default - continuation is garbo, but I can still do better client side to avoid get-set high level @@ -15,6 +15,7 @@ todo: - banlist criteria like vendors, brokers, metadata - set up copy for caleb, broc done: +- figure out zoom on maps;; is there like an auto-zoom I can leverage? - mock is nigh useless - mark consumed;; save scroll id for matrix - todo: switch house to selfhosted for rate limit