diff --git a/bot/command.go b/bot/command.go index 3a498b2..1d27870 100644 --- a/bot/command.go +++ b/bot/command.go @@ -2,6 +2,10 @@ package bot import ( "fmt" + "strings" + "time" + + timeago "github.com/ararog/timeago" "github.com/genofire/logmania/log" ) @@ -37,7 +41,7 @@ func (b *Bot) sendTo(answer func(string), from string, params []string) { answer(fmt.Sprintf("added %s in list of %s", to, host)) } -//TODO add a chat to send log to a chat +//add a chat to send log to a chat func (b *Bot) sendRemove(answer func(string), from string, params []string) { if len(params) < 1 { answer("invalid: CMD IPAddress\n or\n CMD IPAddress to") @@ -62,10 +66,27 @@ func (b *Bot) sendRemove(answer func(string), from string, params []string) { // list all hostname with the chat where it send to func (b *Bot) sendList(answer func(string), from string, params []string) { msg := "sending:\n" + all := false + of := from + if len(params) > 0 { + if params[0] == "all" { + all = true + } else { + of = params[0] + } + } for ip, toMap := range b.state.HostTo { toList := "" + show := all for to := range toMap { - toList = fmt.Sprintf("%s , %s", toList, to) + if all { + toList = fmt.Sprintf("%s , %s", toList, to) + } else if to == of { + show = true + } + } + if !show { + continue } if len(toList) > 3 { toList = toList[3:] @@ -76,6 +97,7 @@ func (b *Bot) sendList(answer func(string), from string, params []string) { msg = fmt.Sprintf("%s%s: %s\n", msg, ip, toList) } } + answer(msg) } @@ -83,7 +105,12 @@ func (b *Bot) sendList(answer func(string), from string, params []string) { func (b *Bot) listHostname(answer func(string), from string, params []string) { msg := "hostnames:\n" for ip, hostname := range b.state.Hostname { - msg = fmt.Sprintf("%s%s - %s\n", msg, ip, hostname) + if last, ok := b.state.Lastseen[ip]; ok { + got, _ := timeago.TimeAgoWithTime(time.Now(), last) + msg = fmt.Sprintf("%s%s - %s (%s)\n", msg, ip, hostname, got) + } else { + msg = fmt.Sprintf("%s%s - %s\n", msg, ip, hostname) + } } answer(msg) } @@ -104,9 +131,20 @@ func (b *Bot) setHostname(answer func(string), from string, params []string) { // set a filter by max func (b *Bot) listMaxfilter(answer func(string), from string, params []string) { - msg := "filters:\n" - for to, filter := range b.state.MaxPrioIn { - msg = fmt.Sprintf("%s%s - %s\n", msg, to, filter.String()) + msg := "filters: " + if len(params) > 0 && params[0] == "all" { + msg = fmt.Sprintf("%s\n", msg) + for to, filter := range b.state.MaxPrioIn { + msg = fmt.Sprintf("%s%s - %s\n", msg, to, filter.String()) + } + } else { + of := from + if len(params) > 0 { + of = params[0] + } + if filter, ok := b.state.MaxPrioIn[of]; ok { + msg = fmt.Sprintf("%s of %s is %s", msg, of, filter) + } } answer(msg) } @@ -129,3 +167,54 @@ func (b *Bot) setMaxfilter(answer func(string), from string, params []string) { answer(fmt.Sprintf("set filter for %s to %s", to, max.String())) } + +// list of regex filter +func (b *Bot) listRegex(answer func(string), from string, params []string) { + msg := "regexs:\n" + if len(params) > 0 && params[0] == "all" { + for to, regexs := range b.state.RegexIn { + msg = fmt.Sprintf("%s%s\n-------------\n", msg, to) + for expression := range regexs { + msg = fmt.Sprintf("%s - %s\n", msg, expression) + } + } + } else { + of := from + if len(params) > 0 { + of = params[0] + } + if regexs, ok := b.state.RegexIn[of]; ok { + msg = fmt.Sprintf("%s%s\n-------------\n", msg, from) + for expression := range regexs { + msg = fmt.Sprintf("%s - %s\n", msg, expression) + } + } + } + answer(msg) +} + +// add a regex filter +func (b *Bot) addRegex(answer func(string), from string, params []string) { + if len(params) < 1 { + answer("invalid: CMD regex\n or\n CMD channel regex") + return + } + regex := strings.Join(params, " ") + + if err := b.state.AddRegex(from, regex); err == nil { + answer(fmt.Sprintf("add regex for \"%s\" to %s", from, regex)) + } else { + answer(fmt.Sprintf("\"%s\" is no valid regex expression: %s", regex, err)) + } +} + +// del a regex filter +func (b *Bot) delRegex(answer func(string), from string, params []string) { + if len(params) < 1 { + answer("invalid: CMD regex\n or\n CMD channel regex") + return + } + regex := strings.Join(params, " ") + delete(b.state.RegexIn[from], regex) + b.listRegex(answer, from, []string{}) +} diff --git a/bot/main.go b/bot/main.go index 6580250..0db4800 100644 --- a/bot/main.go +++ b/bot/main.go @@ -25,6 +25,9 @@ func NewBot(state *configNotify.NotifyState) *Bot { "hostname-list": b.listHostname, "filter-set": b.setMaxfilter, "filter-list": b.listMaxfilter, + "regex-add": b.addRegex, + "regex-list": b.listRegex, + "regex-rm": b.delRegex, } return b } diff --git a/notify/config/config.go b/notify/config/config.go index 70ed5f2..3294bef 100644 --- a/notify/config/config.go +++ b/notify/config/config.go @@ -11,20 +11,34 @@ import ( ) type NotifyState struct { - Hostname map[string]string `json:"hostname"` - HostTo map[string]map[string]bool `json:"host_to"` - MaxPrioIn map[string]log.LogLevel `json:"maxLevel"` - RegexIn map[string]map[string]bool `json:"regexIn"` - regexIn map[string]map[string]*regexp.Regexp `json:"-"` + Hostname map[string]string `json:"hostname"` + HostTo map[string]map[string]bool `json:"host_to"` + MaxPrioIn map[string]log.LogLevel `json:"maxLevel"` + RegexIn map[string]map[string]*regexp.Regexp `json:"regexIn"` + Lastseen map[string]time.Time `json:"lastseen,omitempty"` + LastseenNotify map[string]time.Time `json:"lastseen_notify,omitempty"` } func (state *NotifyState) SendTo(e *log.Entry) []string { if to, ok := state.HostTo[e.Hostname]; ok { + state.Lastseen[e.Hostname] = time.Now() var toList []string for toEntry, _ := range to { if lvl := state.MaxPrioIn[toEntry]; e.Level < lvl { continue } + if regex, ok := state.RegexIn[toEntry]; ok { + stopForTo := false + for _, expr := range regex { + if expr.MatchString(e.Text) { + stopForTo = true + continue + } + } + if stopForTo { + continue + } + } toList = append(toList, toEntry) } if hostname, ok := state.Hostname[e.Hostname]; ok { @@ -37,12 +51,38 @@ func (state *NotifyState) SendTo(e *log.Entry) []string { return nil } +func (state *NotifyState) AddRegex(to, expression string) error { + regex, err := regexp.Compile(expression) + if err == nil { + if _, ok := state.RegexIn[to]; !ok { + state.RegexIn[to] = make(map[string]*regexp.Regexp) + } + state.RegexIn[to][expression] = regex + return nil + } + return err +} + func ReadStateFile(path string) *NotifyState { var state NotifyState if f, err := os.Open(path); err == nil { // transform data to legacy meshviewer if err = json.NewDecoder(f).Decode(&state); err == nil { fmt.Println("loaded", len(state.HostTo), "hosts") - state.regexIn = make(map[string]map[string]*regexp.Regexp) + if state.Lastseen == nil { + state.Lastseen = make(map[string]time.Time) + } + if state.LastseenNotify == nil { + state.LastseenNotify = make(map[string]time.Time) + } + if state.RegexIn == nil { + state.RegexIn = make(map[string]map[string]*regexp.Regexp) + } else { + for to, regexs := range state.RegexIn { + for exp, _ := range regexs { + state.AddRegex(to, exp) + } + } + } return &state } else { fmt.Println("failed to unmarshal nodes:", err) @@ -51,11 +91,12 @@ func ReadStateFile(path string) *NotifyState { fmt.Println("failed to open state notify file: ", path, ":", err) } return &NotifyState{ - Hostname: make(map[string]string), - HostTo: make(map[string]map[string]bool), - MaxPrioIn: make(map[string]log.LogLevel), - RegexIn: make(map[string]map[string]bool), - regexIn: make(map[string]map[string]*regexp.Regexp), + Hostname: make(map[string]string), + HostTo: make(map[string]map[string]bool), + MaxPrioIn: make(map[string]log.LogLevel), + RegexIn: make(map[string]map[string]*regexp.Regexp), + Lastseen: make(map[string]time.Time), + LastseenNotify: make(map[string]time.Time), } }