Added timers, error handling, validation on item number #1

Open
symys wants to merge 5 commits from symys/layerzero-consensus-bot:symys-dev into main
2 changed files with 109 additions and 16 deletions

View file

@ -2,5 +2,7 @@
"MatrixRoom": "!exampleEXAMPLEexample:cyberia.club", "MatrixRoom": "!exampleEXAMPLEexample:cyberia.club",
"MatrixToken": "syt_exampleEXAMPLE_exampleEXAMPLE_example", "MatrixToken": "syt_exampleEXAMPLE_exampleEXAMPLE_example",
"MatrixUsername": "@consensus-bot:cyberia.club", "MatrixUsername": "@consensus-bot:cyberia.club",
"MatrixURL": "https://matrix.cyberia.club" "MatrixURL": "https://matrix.cyberia.club",
"WarningTime": "144h",
"ActivationTimeAfterWarning":"24h"
} }

101
main.go
View file

@ -10,6 +10,7 @@ import (
"path" "path"
"path/filepath" "path/filepath"
"reflect" "reflect"
"strconv"
"strings" "strings"
"time" "time"
@ -29,6 +30,8 @@ type Config struct {
MatrixURL string MatrixURL string
MatrixUsername string MatrixUsername string
MatrixToken string MatrixToken string
WarningTime string
ActivationTimeAfterWarning string
} }
type RecordCount struct { type RecordCount struct {
@ -43,6 +46,7 @@ type Item struct {
number string number string
name string name string
path string path string
currentTimer *time.Timer
} }
func (i Item) Status() string { func (i Item) Status() string {
@ -300,6 +304,47 @@ important: let all items sit for awhile prior to closure
return nil return nil
} }
func createTriggerOneDayAutoDecide(item Item) func() {
//TODO: add functions to extend the timer, and to cancel the timer if the item is closed before it fires
return func() {
records := countRecords(item.path)
if records.approvals != 0 && records.concerns == 0 {
handleClose(item.number, "approved")
}
if records.concerns > 0 {
handleClose(item.number, "cancelled")
}
if records.approvals == 0 && records.concerns == 0 {
openDays := time.Since(item.date).Hours() / 24
msg := fmt.Sprintf("consensus item %s is %v days old... but nobody has approved or concerned yet. Let's give it another %s, shall we?", item.number, openDays, config.WarningTime)
err := sendMessage(msg)
if err != nil {
log.Println(fmt.Sprintf("Error (from notifFunc) sending message for item %s: %v", item.name, err))
}
notifFunc := createNotifFunc(item)
warningTime, _ := time.ParseDuration(config.WarningTime)
time.AfterFunc(warningTime, notifFunc)
}
}
}
func createNotifFunc(item Item) func() {
//TODO: add functions to extend the timer, and to cancel the timer if the item is closed before it fires
return func() {
openDays := time.Since(item.date).Hours() / 24
msg := fmt.Sprintf("consensus item %s is now %v days old: %s\nThe item will auto-pass after %s if there is unanimous approval, and auto-fail otherwise! So, now is a good time to weigh in if you haven't yet and have an opinion!", item.number, openDays, item.name, config.ActivationTimeAfterWarning)
err := sendMessage(msg)
if err != nil {
log.Println(fmt.Sprintf("Error (from notifFunc) sending message for item %s: %v", item.name, err))
}
triggerOneDayAutoDecide := createTriggerOneDayAutoDecide(item)
activationTime, _ := time.ParseDuration(config.ActivationTimeAfterWarning)
time.AfterFunc(activationTime, triggerOneDayAutoDecide)
}
}
func handleNew(event *gomatrix.Event) error { func handleNew(event *gomatrix.Event) error {
body, ok := event.Body() body, ok := event.Body()
if !ok { if !ok {
@ -330,6 +375,12 @@ func handleNew(event *gomatrix.Event) error {
if err != nil { if err != nil {
return err return err
} }
//automatically check-in after time specified in configuration, then after the other time specified in configuration
//approve if it has unanimous approval or cancel if any concerns
notifFunc := createNotifFunc(item)
warningTime, _ := time.ParseDuration(config.WarningTime)
time.AfterFunc(warningTime, notifFunc)
return nil return nil
} }
@ -489,6 +540,11 @@ func handleClose(itemNumber string, status string) {
sendMessage("please specify an item number. see !help") sendMessage("please specify an item number. see !help")
return return
} }
if !isValidItemNumber(itemNumber) {
return
}
itemPath := recordDir + "/" + itemNumber + ".txt" itemPath := recordDir + "/" + itemNumber + ".txt"
if !fileExists(itemPath) { if !fileExists(itemPath) {
@ -523,12 +579,28 @@ func handleClose(itemNumber string, status string) {
sendMessage(msg) sendMessage(msg)
} }
func isValidItemNumber(itemNumberCandidate string) bool {
_, err := strconv.Atoi(itemNumberCandidate)
if err != nil {
err = sendMessage("hold yer horses there pard, that ain't no item number like I ever seen before...")
if err != nil {
log.Println(fmt.Sprintf("Error (from isValidItemNumber) sending message: %v", err))
}
return false
}
return true
}
func handleShow(itemNumber string) { func handleShow(itemNumber string) {
if strings.TrimSpace(itemNumber) == "" { if strings.TrimSpace(itemNumber) == "" {
sendMessage("please specify an item number. see !help") sendMessage("please specify an item number. see !help")
return return
} }
if !isValidItemNumber(itemNumber) {
return
}
itemPath := recordDir + "/" + itemNumber + ".txt" itemPath := recordDir + "/" + itemNumber + ".txt"
if !fileExists(itemPath) { if !fileExists(itemPath) {
sendMessage("item specified does not exist") sendMessage("item specified does not exist")
@ -546,30 +618,49 @@ func handleShow(itemNumber string) {
func handleComment(event *gomatrix.Event, itemNumber string, comment string, emoji string) { func handleComment(event *gomatrix.Event, itemNumber string, comment string, emoji string) {
if strings.TrimSpace(itemNumber) == "" { if strings.TrimSpace(itemNumber) == "" {
sendMessage("please specify an item number. see !help") err := sendMessage("please specify an item number. see !help")
if err != nil {
log.Println(fmt.Sprintf("Error (from handleComment) sending messag: %v", err))
}
return return
} }
if strings.TrimSpace(comment) == "" { if strings.TrimSpace(comment) == "" {
sendMessage("please specify a comment. see !help") err := sendMessage("please specify a comment. see !help")
if err != nil {
log.Println(fmt.Sprintf("Error (from handleComment) sending messag: %v", err))
}
return
}
if !isValidItemNumber(itemNumber) {
return return
} }
itemPath := recordDir + "/" + itemNumber + ".txt" itemPath := recordDir + "/" + itemNumber + ".txt"
if !fileExists(itemPath) { if !fileExists(itemPath) {
sendMessage("item specified does not exist") err := sendMessage("item specified does not exist")
if err != nil {
log.Println(fmt.Sprintf("Error (from handleComment) sending messag: %v", err))
}
return return
} }
item, err := loadItemByPath(itemPath) item, err := loadItemByPath(itemPath)
if err != nil { if err != nil {
sendMessage("item not found: " + err.Error()) err = sendMessage("item not found: " + err.Error())
if err != nil {
log.Println(fmt.Sprintf("Error (from handleComment) sending messag: %v", err))
}
return return
} }
if !item.isActive() { if !item.isActive() {
sendMessage(fmt.Sprintf("item %s (%s) is not active and may not be commented on", item.number, item.name)) err = sendMessage(fmt.Sprintf("item %s (%s) is not active and may not be commented on", item.number, item.name))
if err != nil {
log.Println(fmt.Sprintf("Error (from handleComment) sending messag: %v", err))
}
return return
} }