mirror of
https://github.com/Luzifer/twitch-bot.git
synced 2024-11-08 16:20:02 +00:00
[editor] Lint: Refactor code to simplify comprehension
Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
parent
dec24b7b92
commit
2a322e5803
5 changed files with 626 additions and 558 deletions
558
configEditor.go
558
configEditor.go
|
@ -5,18 +5,14 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"regexp"
|
|
||||||
"sort"
|
"sort"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Luzifer/twitch-bot/plugins"
|
"github.com/Luzifer/twitch-bot/plugins"
|
||||||
"github.com/Luzifer/twitch-bot/twitch"
|
"github.com/Luzifer/twitch-bot/twitch"
|
||||||
"github.com/gofrs/uuid/v3"
|
|
||||||
"github.com/gorilla/mux"
|
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const websocketPingInterval = 30 * time.Second
|
const websocketPingInterval = 30 * time.Second
|
||||||
|
@ -41,13 +37,6 @@ func registerActorDocumentation(doc plugins.ActionDocumentation) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
type (
|
|
||||||
configEditorGeneralConfig struct {
|
|
||||||
BotEditors []string `json:"bot_editors"`
|
|
||||||
Channels []string `json:"channels"`
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
registerEditorAutoMessageRoutes()
|
registerEditorAutoMessageRoutes()
|
||||||
registerEditorFrontend()
|
registerEditorFrontend()
|
||||||
|
@ -56,155 +45,6 @@ func init() {
|
||||||
registerEditorRulesRoutes()
|
registerEditorRulesRoutes()
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint:funlen // This is a logic unit and shall not be split up
|
|
||||||
func registerEditorAutoMessageRoutes() {
|
|
||||||
for _, rd := range []plugins.HTTPRouteRegistrationArgs{
|
|
||||||
{
|
|
||||||
Description: "Returns the current set of configured auto-messages in JSON format",
|
|
||||||
HandlerFunc: func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if err := json.NewEncoder(w).Encode(config.AutoMessages); err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Method: http.MethodGet,
|
|
||||||
Module: "config-editor",
|
|
||||||
Name: "Get current auto-messages",
|
|
||||||
Path: "/auto-messages",
|
|
||||||
RequiresEditorsAuth: true,
|
|
||||||
ResponseType: plugins.HTTPRouteResponseTypeJSON,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Description: "Adds a new Auto-Message",
|
|
||||||
HandlerFunc: func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
user, _, err := getAuthorizationFromRequest(r)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, errors.Wrap(err, "getting authorized user").Error(), http.StatusInternalServerError)
|
|
||||||
}
|
|
||||||
|
|
||||||
msg := &autoMessage{}
|
|
||||||
if err := json.NewDecoder(r.Body).Decode(msg); err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
msg.UUID = uuid.Must(uuid.NewV4()).String()
|
|
||||||
|
|
||||||
if err := patchConfig(cfg.Config, user, "", "Add auto-message", func(c *configFile) error {
|
|
||||||
c.AutoMessages = append(c.AutoMessages, msg)
|
|
||||||
return nil
|
|
||||||
}); err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
w.WriteHeader(http.StatusCreated)
|
|
||||||
},
|
|
||||||
Method: http.MethodPost,
|
|
||||||
Module: "config-editor",
|
|
||||||
Name: "Add Auto-Message",
|
|
||||||
Path: "/auto-messages",
|
|
||||||
RequiresEditorsAuth: true,
|
|
||||||
ResponseType: plugins.HTTPRouteResponseTypeTextPlain,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Description: "Deletes the given Auto-Message",
|
|
||||||
HandlerFunc: func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
user, _, err := getAuthorizationFromRequest(r)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, errors.Wrap(err, "getting authorized user").Error(), http.StatusInternalServerError)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := patchConfig(cfg.Config, user, "", "Delete auto-message", func(c *configFile) error {
|
|
||||||
var (
|
|
||||||
id = mux.Vars(r)["uuid"]
|
|
||||||
tmp []*autoMessage
|
|
||||||
)
|
|
||||||
|
|
||||||
for i := range c.AutoMessages {
|
|
||||||
if c.AutoMessages[i].ID() == id {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
tmp = append(tmp, c.AutoMessages[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
c.AutoMessages = tmp
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}); err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
w.WriteHeader(http.StatusNoContent)
|
|
||||||
},
|
|
||||||
Method: http.MethodDelete,
|
|
||||||
Module: "config-editor",
|
|
||||||
Name: "Delete Auto-Message",
|
|
||||||
Path: "/auto-messages/{uuid}",
|
|
||||||
RequiresEditorsAuth: true,
|
|
||||||
ResponseType: plugins.HTTPRouteResponseTypeTextPlain,
|
|
||||||
RouteParams: []plugins.HTTPRouteParamDocumentation{
|
|
||||||
{
|
|
||||||
Description: "UUID of the auto-message to delete",
|
|
||||||
Name: "uuid",
|
|
||||||
Required: false,
|
|
||||||
Type: "string",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Description: "Updates the given Auto-Message",
|
|
||||||
HandlerFunc: func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
user, _, err := getAuthorizationFromRequest(r)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, errors.Wrap(err, "getting authorized user").Error(), http.StatusInternalServerError)
|
|
||||||
}
|
|
||||||
|
|
||||||
msg := &autoMessage{}
|
|
||||||
if err := json.NewDecoder(r.Body).Decode(msg); err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := patchConfig(cfg.Config, user, "", "Update auto-message", func(c *configFile) error {
|
|
||||||
id := mux.Vars(r)["uuid"]
|
|
||||||
|
|
||||||
for i := range c.AutoMessages {
|
|
||||||
if c.AutoMessages[i].ID() == id {
|
|
||||||
c.AutoMessages[i] = msg
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}); err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
w.WriteHeader(http.StatusNoContent)
|
|
||||||
},
|
|
||||||
Method: http.MethodPut,
|
|
||||||
Module: "config-editor",
|
|
||||||
Name: "Update Auto-Message",
|
|
||||||
Path: "/auto-messages/{uuid}",
|
|
||||||
RequiresEditorsAuth: true,
|
|
||||||
ResponseType: plugins.HTTPRouteResponseTypeTextPlain,
|
|
||||||
RouteParams: []plugins.HTTPRouteParamDocumentation{
|
|
||||||
{
|
|
||||||
Description: "UUID of the auto-message to update",
|
|
||||||
Name: "uuid",
|
|
||||||
Required: false,
|
|
||||||
Type: "string",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} {
|
|
||||||
if err := registerRoute(rd); err != nil {
|
|
||||||
log.WithError(err).Fatal("Unable to register config editor route")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func registerEditorFrontend() {
|
func registerEditorFrontend() {
|
||||||
router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
f, err := configEditorFrontend.Open("editor/index.html")
|
f, err := configEditorFrontend.Open("editor/index.html")
|
||||||
|
@ -232,401 +72,3 @@ func registerEditorFrontend() {
|
||||||
|
|
||||||
router.PathPrefix("/editor").Handler(http.FileServer(http.FS(configEditorFrontend)))
|
router.PathPrefix("/editor").Handler(http.FileServer(http.FS(configEditorFrontend)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func registerEditorGeneralConfigRoutes() {
|
|
||||||
for _, rd := range []plugins.HTTPRouteRegistrationArgs{
|
|
||||||
{
|
|
||||||
Description: "Returns the current general config",
|
|
||||||
HandlerFunc: func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if err := json.NewEncoder(w).Encode(configEditorGeneralConfig{
|
|
||||||
BotEditors: config.BotEditors,
|
|
||||||
Channels: config.Channels,
|
|
||||||
}); err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Method: http.MethodGet,
|
|
||||||
Module: "config-editor",
|
|
||||||
Name: "Get general config",
|
|
||||||
Path: "/general",
|
|
||||||
RequiresEditorsAuth: true,
|
|
||||||
ResponseType: plugins.HTTPRouteResponseTypeJSON,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Description: "Updates the general config",
|
|
||||||
HandlerFunc: func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
user, _, err := getAuthorizationFromRequest(r)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, errors.Wrap(err, "getting authorized user").Error(), http.StatusInternalServerError)
|
|
||||||
}
|
|
||||||
|
|
||||||
var payload configEditorGeneralConfig
|
|
||||||
|
|
||||||
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := range payload.BotEditors {
|
|
||||||
usr, err := twitchClient.GetUserInformation(payload.BotEditors[i])
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, errors.Wrap(err, "getting bot editor profile").Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
payload.BotEditors[i] = usr.ID
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := patchConfig(cfg.Config, user, "", "Update general config", func(cfg *configFile) error {
|
|
||||||
cfg.Channels = payload.Channels
|
|
||||||
cfg.BotEditors = payload.BotEditors
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}); err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
w.WriteHeader(http.StatusNoContent)
|
|
||||||
},
|
|
||||||
Method: http.MethodPut,
|
|
||||||
Module: "config-editor",
|
|
||||||
Name: "Update general config",
|
|
||||||
Path: "/general",
|
|
||||||
RequiresEditorsAuth: true,
|
|
||||||
ResponseType: plugins.HTTPRouteResponseTypeTextPlain,
|
|
||||||
},
|
|
||||||
} {
|
|
||||||
if err := registerRoute(rd); err != nil {
|
|
||||||
log.WithError(err).Fatal("Unable to register config editor route")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//nolint:funlen,gocognit,gocyclo // This is a logic unit and shall not be split up
|
|
||||||
func registerEditorGlobalMethods() {
|
|
||||||
for _, rd := range []plugins.HTTPRouteRegistrationArgs{
|
|
||||||
{
|
|
||||||
Description: "Returns the documentation for available actions",
|
|
||||||
HandlerFunc: func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
availableActorDocsLock.Lock()
|
|
||||||
defer availableActorDocsLock.Unlock()
|
|
||||||
|
|
||||||
if err := json.NewEncoder(w).Encode(availableActorDocs); err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Method: http.MethodGet,
|
|
||||||
Module: "config-editor",
|
|
||||||
Name: "Get available actions",
|
|
||||||
Path: "/actions",
|
|
||||||
ResponseType: plugins.HTTPRouteResponseTypeJSON,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Description: "Returns information about a Twitch user to properly display bot editors",
|
|
||||||
HandlerFunc: func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
usr, err := twitchClient.GetUserInformation(r.FormValue("user"))
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := json.NewEncoder(w).Encode(usr); err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Method: http.MethodGet,
|
|
||||||
Module: "config-editor",
|
|
||||||
Name: "Get user information",
|
|
||||||
Path: "/user",
|
|
||||||
QueryParams: []plugins.HTTPRouteParamDocumentation{
|
|
||||||
{
|
|
||||||
Description: "The user to query the information for",
|
|
||||||
Name: "user",
|
|
||||||
Required: true,
|
|
||||||
Type: "string",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
RequiresEditorsAuth: true,
|
|
||||||
ResponseType: plugins.HTTPRouteResponseTypeJSON,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Description: "Subscribe for configuration changes",
|
|
||||||
HandlerFunc: func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
conn, err := upgrader.Upgrade(w, r, nil)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
var (
|
|
||||||
configReloadNotify = make(chan struct{}, 1)
|
|
||||||
pingTimer = time.NewTicker(websocketPingInterval)
|
|
||||||
unsubscribe = registerConfigReloadHook(func() { configReloadNotify <- struct{}{} })
|
|
||||||
)
|
|
||||||
defer unsubscribe()
|
|
||||||
|
|
||||||
type socketMsg struct {
|
|
||||||
MsgType string `json:"msg_type"`
|
|
||||||
}
|
|
||||||
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-configReloadNotify:
|
|
||||||
if err := conn.WriteJSON(socketMsg{
|
|
||||||
MsgType: "configReload",
|
|
||||||
}); err != nil {
|
|
||||||
log.WithError(err).Debug("Unable to send websocket notification")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
case <-pingTimer.C:
|
|
||||||
if err := conn.WriteJSON(socketMsg{
|
|
||||||
MsgType: "ping",
|
|
||||||
}); err != nil {
|
|
||||||
log.WithError(err).Debug("Unable to send websocket ping")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Method: http.MethodGet,
|
|
||||||
Module: "config-editor",
|
|
||||||
Name: "Websocket: Subscribe config changes",
|
|
||||||
Path: "/notify-config",
|
|
||||||
ResponseType: plugins.HTTPRouteResponseTypeTextPlain,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Description: "Validate a cron expression and return the next executions",
|
|
||||||
HandlerFunc: func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
sched, err := cronParser.Parse(r.FormValue("cron"))
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
lt = time.Now()
|
|
||||||
out []time.Time
|
|
||||||
)
|
|
||||||
|
|
||||||
if id := r.FormValue("uuid"); id != "" {
|
|
||||||
for _, a := range config.AutoMessages {
|
|
||||||
if a.ID() != id {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
lt = a.lastMessageSent
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < 3; i++ {
|
|
||||||
lt = sched.Next(lt)
|
|
||||||
out = append(out, lt)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := json.NewEncoder(w).Encode(out); err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Method: http.MethodPut,
|
|
||||||
Module: "config-editor",
|
|
||||||
Name: "Validate cron expression",
|
|
||||||
Path: "/validate-cron",
|
|
||||||
QueryParams: []plugins.HTTPRouteParamDocumentation{
|
|
||||||
{
|
|
||||||
Description: "The cron expression to test",
|
|
||||||
Name: "cron",
|
|
||||||
Required: true,
|
|
||||||
Type: "string",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Description: "Check cron with last execution of auto-message",
|
|
||||||
Name: "uuid",
|
|
||||||
Required: false,
|
|
||||||
Type: "string",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ResponseType: plugins.HTTPRouteResponseTypeJSON,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Description: "Validate a regular expression against the RE2 regex parser",
|
|
||||||
HandlerFunc: func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if _, err := regexp.Compile(r.FormValue("regexp")); err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
w.WriteHeader(http.StatusNoContent)
|
|
||||||
},
|
|
||||||
Method: http.MethodPut,
|
|
||||||
Module: "config-editor",
|
|
||||||
Name: "Validate regular expression",
|
|
||||||
Path: "/validate-regex",
|
|
||||||
QueryParams: []plugins.HTTPRouteParamDocumentation{
|
|
||||||
{
|
|
||||||
Description: "The regular expression to test",
|
|
||||||
Name: "regexp",
|
|
||||||
Required: true,
|
|
||||||
Type: "string",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ResponseType: plugins.HTTPRouteResponseTypeTextPlain,
|
|
||||||
},
|
|
||||||
} {
|
|
||||||
if err := registerRoute(rd); err != nil {
|
|
||||||
log.WithError(err).Fatal("Unable to register config editor route")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//nolint:funlen // This is a logic unit and shall not be split up
|
|
||||||
func registerEditorRulesRoutes() {
|
|
||||||
for _, rd := range []plugins.HTTPRouteRegistrationArgs{
|
|
||||||
{
|
|
||||||
Description: "Returns the current set of configured rules in JSON format",
|
|
||||||
HandlerFunc: func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if err := json.NewEncoder(w).Encode(config.Rules); err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Method: http.MethodGet,
|
|
||||||
Module: "config-editor",
|
|
||||||
Name: "Get current rules",
|
|
||||||
Path: "/rules",
|
|
||||||
RequiresEditorsAuth: true,
|
|
||||||
ResponseType: plugins.HTTPRouteResponseTypeJSON,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Description: "Adds a new Rule",
|
|
||||||
HandlerFunc: func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
user, _, err := getAuthorizationFromRequest(r)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, errors.Wrap(err, "getting authorized user").Error(), http.StatusInternalServerError)
|
|
||||||
}
|
|
||||||
|
|
||||||
msg := &plugins.Rule{}
|
|
||||||
if err := json.NewDecoder(r.Body).Decode(msg); err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
msg.UUID = uuid.Must(uuid.NewV4()).String()
|
|
||||||
|
|
||||||
if err := patchConfig(cfg.Config, user, "", "Add rule", func(c *configFile) error {
|
|
||||||
c.Rules = append(c.Rules, msg)
|
|
||||||
return nil
|
|
||||||
}); err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
w.WriteHeader(http.StatusCreated)
|
|
||||||
},
|
|
||||||
Method: http.MethodPost,
|
|
||||||
Module: "config-editor",
|
|
||||||
Name: "Add Rule",
|
|
||||||
Path: "/rules",
|
|
||||||
RequiresEditorsAuth: true,
|
|
||||||
ResponseType: plugins.HTTPRouteResponseTypeTextPlain,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Description: "Deletes the given Rule",
|
|
||||||
HandlerFunc: func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
user, _, err := getAuthorizationFromRequest(r)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, errors.Wrap(err, "getting authorized user").Error(), http.StatusInternalServerError)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := patchConfig(cfg.Config, user, "", "Delete rule", func(c *configFile) error {
|
|
||||||
var (
|
|
||||||
id = mux.Vars(r)["uuid"]
|
|
||||||
tmp []*plugins.Rule
|
|
||||||
)
|
|
||||||
|
|
||||||
for i := range c.Rules {
|
|
||||||
if c.Rules[i].MatcherID() == id {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
tmp = append(tmp, c.Rules[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
c.Rules = tmp
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}); err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
w.WriteHeader(http.StatusNoContent)
|
|
||||||
},
|
|
||||||
Method: http.MethodDelete,
|
|
||||||
Module: "config-editor",
|
|
||||||
Name: "Delete Rule",
|
|
||||||
Path: "/rules/{uuid}",
|
|
||||||
RequiresEditorsAuth: true,
|
|
||||||
ResponseType: plugins.HTTPRouteResponseTypeTextPlain,
|
|
||||||
RouteParams: []plugins.HTTPRouteParamDocumentation{
|
|
||||||
{
|
|
||||||
Description: "UUID of the rule to delete",
|
|
||||||
Name: "uuid",
|
|
||||||
Required: false,
|
|
||||||
Type: "string",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Description: "Updates the given Rule",
|
|
||||||
HandlerFunc: func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
user, _, err := getAuthorizationFromRequest(r)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, errors.Wrap(err, "getting authorized user").Error(), http.StatusInternalServerError)
|
|
||||||
}
|
|
||||||
|
|
||||||
msg := &plugins.Rule{}
|
|
||||||
if err := json.NewDecoder(r.Body).Decode(msg); err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := patchConfig(cfg.Config, user, "", "Update rule", func(c *configFile) error {
|
|
||||||
id := mux.Vars(r)["uuid"]
|
|
||||||
|
|
||||||
for i := range c.Rules {
|
|
||||||
if c.Rules[i].MatcherID() == id {
|
|
||||||
c.Rules[i] = msg
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}); err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
w.WriteHeader(http.StatusNoContent)
|
|
||||||
},
|
|
||||||
Method: http.MethodPut,
|
|
||||||
Module: "config-editor",
|
|
||||||
Name: "Update Rule",
|
|
||||||
Path: "/rules/{uuid}",
|
|
||||||
RequiresEditorsAuth: true,
|
|
||||||
ResponseType: plugins.HTTPRouteResponseTypeTextPlain,
|
|
||||||
RouteParams: []plugins.HTTPRouteParamDocumentation{
|
|
||||||
{
|
|
||||||
Description: "UUID of the rule to update",
|
|
||||||
Name: "uuid",
|
|
||||||
Required: false,
|
|
||||||
Type: "string",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} {
|
|
||||||
if err := registerRoute(rd); err != nil {
|
|
||||||
log.WithError(err).Fatal("Unable to register config editor route")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
168
configEditor_automessage.go
Normal file
168
configEditor_automessage.go
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/Luzifer/twitch-bot/plugins"
|
||||||
|
"github.com/gofrs/uuid/v3"
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func registerEditorAutoMessageRoutes() {
|
||||||
|
for _, rd := range []plugins.HTTPRouteRegistrationArgs{
|
||||||
|
{
|
||||||
|
Description: "Returns the current set of configured auto-messages in JSON format",
|
||||||
|
HandlerFunc: configEditorHandleAutoMessagesGet,
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Module: "config-editor",
|
||||||
|
Name: "Get current auto-messages",
|
||||||
|
Path: "/auto-messages",
|
||||||
|
RequiresEditorsAuth: true,
|
||||||
|
ResponseType: plugins.HTTPRouteResponseTypeJSON,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Description: "Adds a new Auto-Message",
|
||||||
|
HandlerFunc: configEditorHandleAutoMessageAdd,
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Module: "config-editor",
|
||||||
|
Name: "Add Auto-Message",
|
||||||
|
Path: "/auto-messages",
|
||||||
|
RequiresEditorsAuth: true,
|
||||||
|
ResponseType: plugins.HTTPRouteResponseTypeTextPlain,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Description: "Deletes the given Auto-Message",
|
||||||
|
HandlerFunc: configEditorHandleAutoMessageDelete,
|
||||||
|
Method: http.MethodDelete,
|
||||||
|
Module: "config-editor",
|
||||||
|
Name: "Delete Auto-Message",
|
||||||
|
Path: "/auto-messages/{uuid}",
|
||||||
|
RequiresEditorsAuth: true,
|
||||||
|
ResponseType: plugins.HTTPRouteResponseTypeTextPlain,
|
||||||
|
RouteParams: []plugins.HTTPRouteParamDocumentation{
|
||||||
|
{
|
||||||
|
Description: "UUID of the auto-message to delete",
|
||||||
|
Name: "uuid",
|
||||||
|
Required: false,
|
||||||
|
Type: "string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Description: "Updates the given Auto-Message",
|
||||||
|
HandlerFunc: configEditorHandleAutoMessageUpdate,
|
||||||
|
Method: http.MethodPut,
|
||||||
|
Module: "config-editor",
|
||||||
|
Name: "Update Auto-Message",
|
||||||
|
Path: "/auto-messages/{uuid}",
|
||||||
|
RequiresEditorsAuth: true,
|
||||||
|
ResponseType: plugins.HTTPRouteResponseTypeTextPlain,
|
||||||
|
RouteParams: []plugins.HTTPRouteParamDocumentation{
|
||||||
|
{
|
||||||
|
Description: "UUID of the auto-message to update",
|
||||||
|
Name: "uuid",
|
||||||
|
Required: false,
|
||||||
|
Type: "string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
if err := registerRoute(rd); err != nil {
|
||||||
|
log.WithError(err).Fatal("Unable to register config editor route")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func configEditorHandleAutoMessageAdd(w http.ResponseWriter, r *http.Request) {
|
||||||
|
user, _, err := getAuthorizationFromRequest(r)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, errors.Wrap(err, "getting authorized user").Error(), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := &autoMessage{}
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(msg); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.UUID = uuid.Must(uuid.NewV4()).String()
|
||||||
|
|
||||||
|
if err := patchConfig(cfg.Config, user, "", "Add auto-message", func(c *configFile) error {
|
||||||
|
c.AutoMessages = append(c.AutoMessages, msg)
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusCreated)
|
||||||
|
}
|
||||||
|
|
||||||
|
func configEditorHandleAutoMessageDelete(w http.ResponseWriter, r *http.Request) {
|
||||||
|
user, _, err := getAuthorizationFromRequest(r)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, errors.Wrap(err, "getting authorized user").Error(), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := patchConfig(cfg.Config, user, "", "Delete auto-message", func(c *configFile) error {
|
||||||
|
var (
|
||||||
|
id = mux.Vars(r)["uuid"]
|
||||||
|
tmp []*autoMessage
|
||||||
|
)
|
||||||
|
|
||||||
|
for i := range c.AutoMessages {
|
||||||
|
if c.AutoMessages[i].ID() == id {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
tmp = append(tmp, c.AutoMessages[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
c.AutoMessages = tmp
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusNoContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
func configEditorHandleAutoMessagesGet(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if err := json.NewEncoder(w).Encode(config.AutoMessages); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func configEditorHandleAutoMessageUpdate(w http.ResponseWriter, r *http.Request) {
|
||||||
|
user, _, err := getAuthorizationFromRequest(r)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, errors.Wrap(err, "getting authorized user").Error(), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := &autoMessage{}
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(msg); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := patchConfig(cfg.Config, user, "", "Update auto-message", func(c *configFile) error {
|
||||||
|
id := mux.Vars(r)["uuid"]
|
||||||
|
|
||||||
|
for i := range c.AutoMessages {
|
||||||
|
if c.AutoMessages[i].ID() == id {
|
||||||
|
c.AutoMessages[i] = msg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusNoContent)
|
||||||
|
}
|
91
configEditor_general.go
Normal file
91
configEditor_general.go
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/Luzifer/twitch-bot/plugins"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
configEditorGeneralConfig struct {
|
||||||
|
BotEditors []string `json:"bot_editors"`
|
||||||
|
Channels []string `json:"channels"`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func registerEditorGeneralConfigRoutes() {
|
||||||
|
for _, rd := range []plugins.HTTPRouteRegistrationArgs{
|
||||||
|
{
|
||||||
|
Description: "Returns the current general config",
|
||||||
|
HandlerFunc: configEditorHandleGeneralGet,
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Module: "config-editor",
|
||||||
|
Name: "Get general config",
|
||||||
|
Path: "/general",
|
||||||
|
RequiresEditorsAuth: true,
|
||||||
|
ResponseType: plugins.HTTPRouteResponseTypeJSON,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Description: "Updates the general config",
|
||||||
|
HandlerFunc: configEditorHandleGeneralUpdate,
|
||||||
|
Method: http.MethodPut,
|
||||||
|
Module: "config-editor",
|
||||||
|
Name: "Update general config",
|
||||||
|
Path: "/general",
|
||||||
|
RequiresEditorsAuth: true,
|
||||||
|
ResponseType: plugins.HTTPRouteResponseTypeTextPlain,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
if err := registerRoute(rd); err != nil {
|
||||||
|
log.WithError(err).Fatal("Unable to register config editor route")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func configEditorHandleGeneralGet(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if err := json.NewEncoder(w).Encode(configEditorGeneralConfig{
|
||||||
|
BotEditors: config.BotEditors,
|
||||||
|
Channels: config.Channels,
|
||||||
|
}); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func configEditorHandleGeneralUpdate(w http.ResponseWriter, r *http.Request) {
|
||||||
|
user, _, err := getAuthorizationFromRequest(r)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, errors.Wrap(err, "getting authorized user").Error(), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
|
var payload configEditorGeneralConfig
|
||||||
|
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range payload.BotEditors {
|
||||||
|
usr, err := twitchClient.GetUserInformation(payload.BotEditors[i])
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, errors.Wrap(err, "getting bot editor profile").Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
payload.BotEditors[i] = usr.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := patchConfig(cfg.Config, user, "", "Update general config", func(cfg *configFile) error {
|
||||||
|
cfg.Channels = payload.Channels
|
||||||
|
cfg.BotEditors = payload.BotEditors
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusNoContent)
|
||||||
|
}
|
199
configEditor_global.go
Normal file
199
configEditor_global.go
Normal file
|
@ -0,0 +1,199 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"regexp"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Luzifer/twitch-bot/plugins"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func registerEditorGlobalMethods() {
|
||||||
|
for _, rd := range []plugins.HTTPRouteRegistrationArgs{
|
||||||
|
{
|
||||||
|
Description: "Returns the documentation for available actions",
|
||||||
|
HandlerFunc: configEditorGlobalGetActions,
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Module: "config-editor",
|
||||||
|
Name: "Get available actions",
|
||||||
|
Path: "/actions",
|
||||||
|
ResponseType: plugins.HTTPRouteResponseTypeJSON,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Description: "Returns information about a Twitch user to properly display bot editors",
|
||||||
|
HandlerFunc: configEditorGlobalGetUser,
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Module: "config-editor",
|
||||||
|
Name: "Get user information",
|
||||||
|
Path: "/user",
|
||||||
|
QueryParams: []plugins.HTTPRouteParamDocumentation{
|
||||||
|
{
|
||||||
|
Description: "The user to query the information for",
|
||||||
|
Name: "user",
|
||||||
|
Required: true,
|
||||||
|
Type: "string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
RequiresEditorsAuth: true,
|
||||||
|
ResponseType: plugins.HTTPRouteResponseTypeJSON,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Description: "Subscribe for configuration changes",
|
||||||
|
HandlerFunc: configEditorGlobalSubscribe,
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Module: "config-editor",
|
||||||
|
Name: "Websocket: Subscribe config changes",
|
||||||
|
Path: "/notify-config",
|
||||||
|
ResponseType: plugins.HTTPRouteResponseTypeTextPlain,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Description: "Validate a cron expression and return the next executions",
|
||||||
|
HandlerFunc: configEditorGlobalValidateCron,
|
||||||
|
Method: http.MethodPut,
|
||||||
|
Module: "config-editor",
|
||||||
|
Name: "Validate cron expression",
|
||||||
|
Path: "/validate-cron",
|
||||||
|
QueryParams: []plugins.HTTPRouteParamDocumentation{
|
||||||
|
{
|
||||||
|
Description: "The cron expression to test",
|
||||||
|
Name: "cron",
|
||||||
|
Required: true,
|
||||||
|
Type: "string",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Description: "Check cron with last execution of auto-message",
|
||||||
|
Name: "uuid",
|
||||||
|
Required: false,
|
||||||
|
Type: "string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ResponseType: plugins.HTTPRouteResponseTypeJSON,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Description: "Validate a regular expression against the RE2 regex parser",
|
||||||
|
HandlerFunc: configEditorGlobalValidateRegex,
|
||||||
|
Method: http.MethodPut,
|
||||||
|
Module: "config-editor",
|
||||||
|
Name: "Validate regular expression",
|
||||||
|
Path: "/validate-regex",
|
||||||
|
QueryParams: []plugins.HTTPRouteParamDocumentation{
|
||||||
|
{
|
||||||
|
Description: "The regular expression to test",
|
||||||
|
Name: "regexp",
|
||||||
|
Required: true,
|
||||||
|
Type: "string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ResponseType: plugins.HTTPRouteResponseTypeTextPlain,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
if err := registerRoute(rd); err != nil {
|
||||||
|
log.WithError(err).Fatal("Unable to register config editor route")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func configEditorGlobalGetActions(w http.ResponseWriter, r *http.Request) {
|
||||||
|
availableActorDocsLock.Lock()
|
||||||
|
defer availableActorDocsLock.Unlock()
|
||||||
|
|
||||||
|
if err := json.NewEncoder(w).Encode(availableActorDocs); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func configEditorGlobalGetUser(w http.ResponseWriter, r *http.Request) {
|
||||||
|
usr, err := twitchClient.GetUserInformation(r.FormValue("user"))
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.NewEncoder(w).Encode(usr); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func configEditorGlobalSubscribe(w http.ResponseWriter, r *http.Request) {
|
||||||
|
conn, err := upgrader.Upgrade(w, r, nil)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
var (
|
||||||
|
configReloadNotify = make(chan struct{}, 1)
|
||||||
|
pingTimer = time.NewTicker(websocketPingInterval)
|
||||||
|
unsubscribe = registerConfigReloadHook(func() { configReloadNotify <- struct{}{} })
|
||||||
|
)
|
||||||
|
defer unsubscribe()
|
||||||
|
|
||||||
|
type socketMsg struct {
|
||||||
|
MsgType string `json:"msg_type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-configReloadNotify:
|
||||||
|
if err := conn.WriteJSON(socketMsg{
|
||||||
|
MsgType: "configReload",
|
||||||
|
}); err != nil {
|
||||||
|
log.WithError(err).Debug("Unable to send websocket notification")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
case <-pingTimer.C:
|
||||||
|
if err := conn.WriteJSON(socketMsg{
|
||||||
|
MsgType: "ping",
|
||||||
|
}); err != nil {
|
||||||
|
log.WithError(err).Debug("Unable to send websocket ping")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func configEditorGlobalValidateCron(w http.ResponseWriter, r *http.Request) {
|
||||||
|
sched, err := cronParser.Parse(r.FormValue("cron"))
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
lt = time.Now()
|
||||||
|
out []time.Time
|
||||||
|
)
|
||||||
|
|
||||||
|
if id := r.FormValue("uuid"); id != "" {
|
||||||
|
for _, a := range config.AutoMessages {
|
||||||
|
if a.ID() != id {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
lt = a.lastMessageSent
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
lt = sched.Next(lt)
|
||||||
|
out = append(out, lt)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.NewEncoder(w).Encode(out); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func configEditorGlobalValidateRegex(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if _, err := regexp.Compile(r.FormValue("regexp")); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusNoContent)
|
||||||
|
}
|
168
configEditor_rules.go
Normal file
168
configEditor_rules.go
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/Luzifer/twitch-bot/plugins"
|
||||||
|
"github.com/gofrs/uuid/v3"
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func registerEditorRulesRoutes() {
|
||||||
|
for _, rd := range []plugins.HTTPRouteRegistrationArgs{
|
||||||
|
{
|
||||||
|
Description: "Returns the current set of configured rules in JSON format",
|
||||||
|
HandlerFunc: configEditorRulesGet,
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Module: "config-editor",
|
||||||
|
Name: "Get current rules",
|
||||||
|
Path: "/rules",
|
||||||
|
RequiresEditorsAuth: true,
|
||||||
|
ResponseType: plugins.HTTPRouteResponseTypeJSON,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Description: "Adds a new Rule",
|
||||||
|
HandlerFunc: configEditorRulesAdd,
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Module: "config-editor",
|
||||||
|
Name: "Add Rule",
|
||||||
|
Path: "/rules",
|
||||||
|
RequiresEditorsAuth: true,
|
||||||
|
ResponseType: plugins.HTTPRouteResponseTypeTextPlain,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Description: "Deletes the given Rule",
|
||||||
|
HandlerFunc: configEditorRulesDelete,
|
||||||
|
Method: http.MethodDelete,
|
||||||
|
Module: "config-editor",
|
||||||
|
Name: "Delete Rule",
|
||||||
|
Path: "/rules/{uuid}",
|
||||||
|
RequiresEditorsAuth: true,
|
||||||
|
ResponseType: plugins.HTTPRouteResponseTypeTextPlain,
|
||||||
|
RouteParams: []plugins.HTTPRouteParamDocumentation{
|
||||||
|
{
|
||||||
|
Description: "UUID of the rule to delete",
|
||||||
|
Name: "uuid",
|
||||||
|
Required: false,
|
||||||
|
Type: "string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Description: "Updates the given Rule",
|
||||||
|
HandlerFunc: configEditorRulesUpdate,
|
||||||
|
Method: http.MethodPut,
|
||||||
|
Module: "config-editor",
|
||||||
|
Name: "Update Rule",
|
||||||
|
Path: "/rules/{uuid}",
|
||||||
|
RequiresEditorsAuth: true,
|
||||||
|
ResponseType: plugins.HTTPRouteResponseTypeTextPlain,
|
||||||
|
RouteParams: []plugins.HTTPRouteParamDocumentation{
|
||||||
|
{
|
||||||
|
Description: "UUID of the rule to update",
|
||||||
|
Name: "uuid",
|
||||||
|
Required: false,
|
||||||
|
Type: "string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
if err := registerRoute(rd); err != nil {
|
||||||
|
log.WithError(err).Fatal("Unable to register config editor route")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func configEditorRulesAdd(w http.ResponseWriter, r *http.Request) {
|
||||||
|
user, _, err := getAuthorizationFromRequest(r)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, errors.Wrap(err, "getting authorized user").Error(), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := &plugins.Rule{}
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(msg); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.UUID = uuid.Must(uuid.NewV4()).String()
|
||||||
|
|
||||||
|
if err := patchConfig(cfg.Config, user, "", "Add rule", func(c *configFile) error {
|
||||||
|
c.Rules = append(c.Rules, msg)
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusCreated)
|
||||||
|
}
|
||||||
|
|
||||||
|
func configEditorRulesDelete(w http.ResponseWriter, r *http.Request) {
|
||||||
|
user, _, err := getAuthorizationFromRequest(r)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, errors.Wrap(err, "getting authorized user").Error(), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := patchConfig(cfg.Config, user, "", "Delete rule", func(c *configFile) error {
|
||||||
|
var (
|
||||||
|
id = mux.Vars(r)["uuid"]
|
||||||
|
tmp []*plugins.Rule
|
||||||
|
)
|
||||||
|
|
||||||
|
for i := range c.Rules {
|
||||||
|
if c.Rules[i].MatcherID() == id {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
tmp = append(tmp, c.Rules[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Rules = tmp
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusNoContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
func configEditorRulesGet(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if err := json.NewEncoder(w).Encode(config.Rules); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func configEditorRulesUpdate(w http.ResponseWriter, r *http.Request) {
|
||||||
|
user, _, err := getAuthorizationFromRequest(r)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, errors.Wrap(err, "getting authorized user").Error(), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := &plugins.Rule{}
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(msg); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := patchConfig(cfg.Config, user, "", "Update rule", func(c *configFile) error {
|
||||||
|
id := mux.Vars(r)["uuid"]
|
||||||
|
|
||||||
|
for i := range c.Rules {
|
||||||
|
if c.Rules[i].MatcherID() == id {
|
||||||
|
c.Rules[i] = msg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusNoContent)
|
||||||
|
}
|
Loading…
Reference in a new issue