multi-client but one ntg search, nontrivial config.json change

master
Bel LaPointe 2022-01-11 22:54:18 -05:00
parent 5a4bcecac7
commit e546034c26
8 changed files with 138 additions and 54 deletions

View File

@ -1,6 +1,12 @@
package broker package broker
import "local/truckstop/config" import (
"local/truckstop/config"
"golang.org/x/time/rate"
)
var limiter = rate.NewLimiter(rate.Limit(0.3), 1)
type Broker interface { type Broker interface {
Search([]config.State) ([]Job, error) Search([]config.State) ([]Job, error)

View File

@ -3,6 +3,8 @@ package broker
import ( import (
"fmt" "fmt"
"local/truckstop/config" "local/truckstop/config"
"log"
"strings"
"time" "time"
) )
@ -37,9 +39,10 @@ func (j JobLocation) String() string {
} }
func (j Job) FormatMultilineText() string { func (j Job) FormatMultilineText() string {
foo := func(client string) string {
return fmt.Sprintf( return fmt.Sprintf(
"--- %s: %s => %s ---\nPickup: %s\nDropoff: %s\nNotes: %d lbs, %d miles, %s", "--- %s: %s => %s ---\nPickup: %s\nDropoff: %s\nNotes: %d lbs, %d miles, %s",
config.Get().Name, client,
j.Pickup.State, j.Pickup.State,
j.Dropoff.State, j.Dropoff.State,
j.Pickup.String(), j.Pickup.String(),
@ -49,3 +52,16 @@ func (j Job) FormatMultilineText() string {
j.Meta, j.Meta,
) )
} }
out := ""
clients := config.Get().Clients
for k := range clients {
log.Printf("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\n"
}
out += foo(k)
}
}
return out
}

View File

@ -1,23 +1,33 @@
{ {
"Name": "pa",
"Interval": { "Interval": {
"Input": "10s..30s", "Input": "10s..30s",
"OK": "6h0m0s..6h0m0s", "OK": "6h0m0s..6h0m0s",
"Error": "6h0m0s..6h0m0s" "Error": "6h0m0s..6h0m0s"
}, },
"Clients": {
"pa": {
"States": [ "States": [
"FL", "OH"
"GA",
"NC"
], ],
"IDs": {
"Matrix": "@belandbroc:matrix.org"
}
},
"caleb": {
"States": [
"OH"
],
"IDs": {
"Matrix": "@belandbroc:matrix.org"
}
}
},
"Storage": [ "Storage": [
"map" "map"
], ],
"Client": "breellocaldev@gmail.com",
"Message": { "Message": {
"Matrix": { "Matrix": {
"ReceiveEnabled": true, "ReceiveEnabled": true,
"Client": "@belandbroc:matrix.org",
"Mock": true, "Mock": true,
"Homeserver": "https://matrix-client.matrix.org", "Homeserver": "https://matrix-client.matrix.org",
"Username": "@breellocaldev:matrix.org", "Username": "@breellocaldev:matrix.org",

View File

@ -9,19 +9,21 @@ import (
) )
type Config struct { type Config struct {
Name string
Interval struct { Interval struct {
Input Duration Input Duration
OK Duration OK Duration
Error Duration Error Duration
} }
Clients map[string]struct {
States []State States []State
IDs struct {
Matrix string
}
}
Storage []string Storage []string
Client string
Message struct { Message struct {
Matrix struct { Matrix struct {
ReceiveEnabled bool ReceiveEnabled bool
Client string
Mock bool Mock bool
Homeserver string Homeserver string
Username string Username string
@ -54,6 +56,21 @@ func configPath() string {
return p return p
} }
func AllStates() []State {
c := Get()
statem := map[State]struct{}{}
for _, v := range c.Clients {
for _, state := range v.States {
statem[state] = struct{}{}
}
}
states := make([]State, 0, len(statem)+1)
for k := range statem {
states = append(states, k)
}
return states
}
func Refresh() error { func Refresh() error {
b, err := ioutil.ReadFile(configPath()) b, err := ioutil.ReadFile(configPath())
if err != nil { if err != nil {

40
main.go
View File

@ -49,39 +49,51 @@ func matrixrecv() error {
if err != nil { if err != nil {
return err return err
} }
states := map[config.State]struct{}{} states := map[string]map[config.State]struct{}{}
for _, msg := range messages { for _, msg := range messages {
if len(states) > 0 { if _, ok := states[msg.Sender]; ok {
continue continue
} }
for _, state := range parseOutStates([]byte(msg)) { states[msg.Sender] = map[config.State]struct{}{}
states[state] = struct{}{} for _, state := range parseOutStates([]byte(msg.Content)) {
states[msg.Sender][state] = struct{}{}
} }
} }
setNewStates(states) setNewStates(states)
return nil return nil
} }
func setNewStates(states map[config.State]struct{}) { func setNewStates(states map[string]map[config.State]struct{}) {
if len(states) == 0 { if len(states) == 0 {
return return
} }
conf := *config.Get()
changed := map[string][]config.State{}
for client, clientStates := range states {
newstates := []config.State{} newstates := []config.State{}
for k := range states { for k := range clientStates {
newstates = append(newstates, k) newstates = append(newstates, k)
} }
sort.Slice(newstates, func(i, j int) bool { sort.Slice(newstates, func(i, j int) bool {
return newstates[i] < newstates[j] return newstates[i] < newstates[j]
}) })
conf := *config.Get() clientconf := conf.Clients[client]
if fmt.Sprint(newstates) == fmt.Sprint(conf.States) { if fmt.Sprint(newstates) == fmt.Sprint(clientconf.States) {
continue
}
clientconf.States = newstates
conf.Clients[client] = clientconf
changed[client] = newstates
}
if len(changed) == 0 {
return return
} }
conf.States = newstates
log.Printf("updating config new states: %+v", conf) log.Printf("updating config new states: %+v", conf)
config.Set(conf) config.Set(conf)
if err := sendNewStates(conf.States); err != nil { for client, states := range changed {
log.Printf("failed to send new states %+v: %v", conf.States, err) if err := sendNewStates(client, states); err != nil {
log.Printf("failed to send new states %s/%+v: %v", client, states, err)
}
} }
} }
@ -160,7 +172,7 @@ func once() error {
} }
func getJobs() ([]broker.Job, error) { func getJobs() ([]broker.Job, error) {
states := config.Get().States states := config.AllStates()
ntg := broker.NewNTGVision() ntg := broker.NewNTGVision()
if config.Get().Brokers.NTG.Mock { if config.Get().Brokers.NTG.Mock {
ntg = ntg.WithMock() ntg = ntg.WithMock()
@ -200,7 +212,7 @@ func sendJob(job broker.Job) error {
return sender.Send(job.FormatMultilineText()) return sender.Send(job.FormatMultilineText())
} }
func sendNewStates(states []config.State) error { func sendNewStates(client string, states []config.State) error {
sender := message.NewMatrix() sender := message.NewMatrix()
return sender.Send(fmt.Sprintf("now searching for loads from: %+v", states)) return sender.Send(fmt.Sprintf("%s: now searching for loads from: %+v", client, states))
} }

View File

@ -13,7 +13,6 @@ type Matrix struct {
username string username string
token string token string
room string room string
client string
} }
func NewMatrix() Matrix { func NewMatrix() Matrix {
@ -24,7 +23,6 @@ func NewMatrix() Matrix {
token: conf.Token, token: conf.Token,
room: conf.Room, room: conf.Room,
mock: conf.Mock, mock: conf.Mock,
client: conf.Client,
} }
} }
@ -32,32 +30,51 @@ func (m Matrix) getclient() (*gomatrix.Client, error) {
return gomatrix.NewClient(m.homeserver, m.username, m.token) return gomatrix.NewClient(m.homeserver, m.username, m.token)
} }
func (m Matrix) Receive() ([]string, error) { func (m Matrix) Receive() ([]Message, error) {
if m.mock { if m.mock {
log.Printf("matrix.Receive()") log.Printf("matrix.Receive()")
return []string{"FL, GA, NC"}, nil messages := make([]Message, 0)
for k := range config.Get().Clients {
messages = append(messages, Message{Sender: k, Content: "OH"})
}
return messages, nil
}
clients := config.Get().Clients
matrixIDs := map[string]struct{}{}
for k := range clients {
matrixIDs[clients[k].IDs.Matrix] = struct{}{}
}
if len(matrixIDs) == 0 {
return nil, nil
} }
c, err := m.getclient() c, err := m.getclient()
if err != nil { if err != nil {
return nil, err return nil, err
} }
messages := make([]string, 0) messages := make([]Message, 0)
result, err := c.Messages(m.room, "", "", 'b', 50) result, err := c.Messages(m.room, "", "", 'b', 50)
for _, event := range result.Chunk { for _, event := range result.Chunk {
if event.Sender != m.client { if _, ok := matrixIDs[event.Sender]; !ok {
continue continue
} }
switch event.Type { switch event.Type {
case "m.room.message": case "m.room.message":
b, ok := event.Body() b, ok := event.Body()
if ok { if ok {
messages = append(messages, b) messages = append(messages, Message{Sender: event.Sender, Content: b})
} }
} }
} }
if err != nil { if err != nil {
return nil, err return nil, err
} }
for i := range messages {
for k, v := range config.Get().Clients {
if v.IDs.Matrix == messages[i].Sender {
messages[i].Sender = k
}
}
}
return messages, nil return messages, nil
} }

View File

@ -2,5 +2,10 @@ package message
type Sender interface { type Sender interface {
Send(string) error Send(string) error
Receive() ([]string, error) Receive() ([]Message, error)
}
type Message struct {
Sender string
Content string
} }

View File

@ -1,8 +1,7 @@
todo: todo:
- modify old items once no longer available
- many users -> 1 ntg query
- accept after date - accept after date
- "caleb: my-usual-stuff" to alias - modify old items once no longer available; drop stale jobs good candidate but requires new matrix interaction
- "caleb: commands: args"
- rate LIMIT - rate LIMIT
- more than NTG - more than NTG
- accept pause commands - accept pause commands
@ -16,6 +15,8 @@ todo:
- accept states via element for one system - accept states via element for one system
- set up copy for caleb, broc - set up copy for caleb, broc
done: done:
- many users -> 1 ntg query
- multi client
- rm email - rm email
- send matrix msg on config change - send matrix msg on config change
- setup pa on element - setup pa on element