From 06c3b47f47c54265f91e3959863dbc4d031b7ba8 Mon Sep 17 00:00:00 2001 From: Martyn Ranyard Date: Thu, 20 Feb 2020 19:05:40 +0100 Subject: [PATCH 1/7] Oi. windows, no. Yaml not executable. Signed-off-by: Martyn Ranyard --- deployments/kubernetes/pvc.yaml | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 deployments/kubernetes/pvc.yaml diff --git a/deployments/kubernetes/pvc.yaml b/deployments/kubernetes/pvc.yaml old mode 100755 new mode 100644 From ba1c9438f38b3d1b50ed00486abbe82a132ed1e5 Mon Sep 17 00:00:00 2001 From: Martyn Ranyard Date: Fri, 21 Feb 2020 19:07:08 +0100 Subject: [PATCH 2/7] Makefile for build date Signed-off-by: Martyn Ranyard --- Makefile | 14 ++++++++++++++ build/package/Dockerfile | 5 ++--- 2 files changed, 16 insertions(+), 3 deletions(-) create mode 100755 Makefile diff --git a/Makefile b/Makefile new file mode 100755 index 0000000..11fad2d --- /dev/null +++ b/Makefile @@ -0,0 +1,14 @@ +BUILD=`date +%FT%T%z` + +LDFLAGS=-ldflags "-X main.buildDate=${BUILD}" + +.PHONY: build deps static + +build: + go build ${LDFLAGS} + +deps: + go get + +static: + CGO_ENABLED=0 GOOS=linux go build ${LDFLAGS} -a -installsuffix cgo -o karaokards . \ No newline at end of file diff --git a/build/package/Dockerfile b/build/package/Dockerfile index 11fe7de..c97f727 100755 --- a/build/package/Dockerfile +++ b/build/package/Dockerfile @@ -2,9 +2,8 @@ FROM golang@sha256:cee6f4b901543e8e3f20da3a4f7caac6ea643fd5a46201c3c2387183a332d RUN apk update && apk add --no-cache git make ca-certificates && update-ca-certificates COPY main.go /go/src/git.martyn.berlin/martyn/karaokards/ COPY internal/ /go/src/git.martyn.berlin/martyn/karaokards/internal/ -RUN cd /go/src/git.martyn.berlin/martyn/karaokards/; go get; CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o karaokards . -#RUN ls /go/src/github.com/karaokards/ -l - +COPY Makefile /go/src/git.martyn.berlin/martyn/karaokards/ +RUN cd /go/src/git.martyn.berlin/martyn/karaokards/; make deps ; make static FROM scratch COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ From 4d3b8a33e5a986da5cf878f4ec5ce558e45c0aa1 Mon Sep 17 00:00:00 2001 From: Martyn Ranyard Date: Fri, 21 Feb 2020 19:28:11 +0100 Subject: [PATCH 3/7] All the deployment updates Signed-off-by: Martyn Ranyard --- deployments/kubernetes/configmap.yaml | 16 ++++++++-------- deployments/kubernetes/deploy.yaml | 14 ++++++++++++-- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/deployments/kubernetes/configmap.yaml b/deployments/kubernetes/configmap.yaml index 6ac7f75..95286a6 100644 --- a/deployments/kubernetes/configmap.yaml +++ b/deployments/kubernetes/configmap.yaml @@ -1,12 +1,12 @@ apiVersion: v1 data: - strings.json: "{\r\n \"strings\": [\r\n\t \"Let Pineboy choose!\",\r\n\t \"They're - from the North\",\r\n \"Refers to food\"\r\n ]\r\n}" + config.json: | + { + "channels": ["iMartynOnTwitch"], + "externalUrl": "karaokards.ing.martyn.berlin" + } + strings.json: "{\r\n \"strings\": [\r\n\t \"They're from the North\",\r\n + \ \"Refers to food\"\r\n ]\r\n}\n" kind: ConfigMap metadata: - creationTimestamp: "2020-01-27T20:04:58Z" - name: extracards - namespace: karaokards - resourceVersion: "48537355" - selfLink: /api/v1/namespaces/karaokards/configmaps/extracards - uid: 4b1d73b0-4140-11ea-94d8-9cb6540931b5 + name: kardconfig diff --git a/deployments/kubernetes/deploy.yaml b/deployments/kubernetes/deploy.yaml index 952c447..9ed33ba 100644 --- a/deployments/kubernetes/deploy.yaml +++ b/deployments/kubernetes/deploy.yaml @@ -24,7 +24,7 @@ spec: run: kardbot spec: containers: - - image: imartyn/karaokardbot:0.0.3-linux-amd64 + - image: imartyn/karaokardbot:devel imagePullPolicy: IfNotPresent name: kardbot ports: @@ -39,6 +39,9 @@ spec: - mountPath: /app/strings.json name: extracards subPath: strings.json + - mountPath: /app/config.json + name: config + subPath: config.json - mountPath: /data name: data dnsPolicy: ClusterFirst @@ -56,8 +59,15 @@ spec: items: - key: strings.json path: strings.json - name: extracards + name: kardconfig name: extracards + - configMap: + defaultMode: 420 + items: + - key: config.json + path: config.json + name: kardconfig + name: config - name: data persistentVolumeClaim: claimName: kkard-data \ No newline at end of file From 408516eceac313f7272b9113c726384a5521f2d4 Mon Sep 17 00:00:00 2001 From: Martyn Ranyard Date: Fri, 21 Feb 2020 20:51:17 +0100 Subject: [PATCH 4/7] External url Signed-off-by: Martyn Ranyard --- configs/config.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/configs/config.json b/configs/config.json index a248a48..80e56cc 100755 --- a/configs/config.json +++ b/configs/config.json @@ -1,3 +1,4 @@ { - "channels": ["iMartynOnTwitch"] + "channels": ["iMartynOnTwitch"], + "externalUrl": "karaokards.ing.martyn.berlin" } \ No newline at end of file From e989b8454b65f76094d74f80f3e11e8c819b5239 Mon Sep 17 00:00:00 2001 From: Martyn Ranyard Date: Fri, 21 Feb 2020 20:52:19 +0100 Subject: [PATCH 5/7] 404s, admin panel route and removal of old data structure Signed-off-by: Martyn Ranyard --- internal/webserver/webserver.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/internal/webserver/webserver.go b/internal/webserver/webserver.go index d0e710a..89ea4e1 100755 --- a/internal/webserver/webserver.go +++ b/internal/webserver/webserver.go @@ -26,6 +26,7 @@ func HealthHandler(response http.ResponseWriter, request *http.Request) { func NotFoundHandler(response http.ResponseWriter, request *http.Request) { response.Header().Add("X-Template-File", "html"+request.URL.Path) + response.WriteHeader(404) tmpl := template.Must(template.ParseFiles("web/404.html")) tmpl.Execute(response, nil) } @@ -72,7 +73,7 @@ func TemplateHandler(response http.ResponseWriter, request *http.Request) { // NotFoundHandler(response, request) // return } - var td = TemplateData{ircBot.Prompts[rand.Intn(len(ircBot.Prompts))], len(ircBot.Prompts), len(ircBot.Credentials.Channels), 0} + var td = TemplateData{ircBot.Prompts[rand.Intn(len(ircBot.Prompts))], len(ircBot.Prompts), 0, 0} err = tmpl.Execute(response, td) if err != nil { http.Error(response, err.Error(), http.StatusInternalServerError) @@ -80,6 +81,11 @@ func TemplateHandler(response http.ResponseWriter, request *http.Request) { } } +func AdminHandler(response http.ResponseWriter, request *http.Request) { + request.URL.Path = "/index.html" + TemplateHandler(response, request) +} + func HandleHTTP(passedIrcBot irc.KardBot) { ircBot = passedIrcBot r := mux.NewRouter() @@ -89,6 +95,7 @@ func HandleHTTP(passedIrcBot irc.KardBot) { r.HandleFunc("/healthz", HealthHandler) r.HandleFunc("/example/{.*}", TemplateHandler) r.HandleFunc("/cover.css", CSSHandler) + r.HandleFunc("/admin/{channel}/{key}", AdminHandler) http.Handle("/", r) srv := &http.Server{ Handler: loggedRouter, From fad307913226a18985f9db295d039504569566ac Mon Sep 17 00:00:00 2001 From: Martyn Ranyard Date: Fri, 21 Feb 2020 20:54:27 +0100 Subject: [PATCH 6/7] All the magic, database and admin stuff Signed-off-by: Martyn Ranyard --- internal/irc/irc.go | 100 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 92 insertions(+), 8 deletions(-) diff --git a/internal/irc/irc.go b/internal/irc/irc.go index 62c2c94..310916c 100644 --- a/internal/irc/irc.go +++ b/internal/irc/irc.go @@ -3,6 +3,7 @@ package irc import ( "bufio" "encoding/json" + "encoding/base64" "errors" "fmt" "io" @@ -15,6 +16,8 @@ import ( "time" rgb "github.com/foresthoffman/rgblog" + scribble "github.com/nanobox-io/golang-scribble" + uuid "github.com/google/uuid" ) const PSTFormat = "Jan 2 15:04:05 PST" @@ -38,9 +41,6 @@ type OAuthCred struct { // The developer application client ID. Used for API calls to Twitch. ClientID string `json:"client_id,omitempty"` - - // List of Channels to join - Channels []string `json:"channels,omitempty"` } type KardBot struct { @@ -54,6 +54,16 @@ type KardBot struct { Server string startTime time.Time Prompts []string + Database scribble.Driver + channelData map[string]ChannelData +} + +type ChannelData struct { + Name string `json:"name"` + AdminKey string `json:"value,omitempty"` + CustomCommand string `json:"customcommand,omitempty"` + ExtraStrings string `json:"extrastrings,omitempty"` + JoinTime time.Time `json:"jointime"` } // Connects the bot to the Twitch IRC server. The bot will continue to try to connect until it @@ -134,7 +144,7 @@ func (bb *KardBot) HandleChat() error { } // channel-owner specific commands - if userName == bb.Channel { + if userName == channel { switch cmd { case "tbdown": rgb.CPrintf( @@ -144,6 +154,14 @@ func (bb *KardBot) HandleChat() error { bb.Disconnect() return nil + case "wat": + magicCode := bb.readOrCreateChannelKey(channel) + rgb.CPrintf( + "[%s] Magic code is %s - https://karaokards.ing.martyn.berlin/admin/%s/%s\n", + TimeStamp(), + magicCode, userName, magicCode, + ) + bb.Say("Ack.") default: // do nothing } @@ -232,15 +250,24 @@ func (bb *KardBot) Start() { err := bb.ReadCredentials() if nil != err { fmt.Println(err) - fmt.Println("Aborting...") + fmt.Println("Aborting!") + return + } + + err = bb.readChannelData() + if nil != err { + fmt.Println(err) + fmt.Println("Aborting!") return } for { bb.Connect() bb.Login() - if len(bb.Credentials.Channels) > 0 { - bb.JoinChannel(bb.Credentials.Channels...) + if len(bb.channelData) > 0 { + for channelName := range(bb.channelData) { + bb.JoinChannel(channelName) + } } else { bb.JoinChannel() } @@ -257,10 +284,67 @@ func (bb *KardBot) Start() { } } +func (bb *KardBot) readChannelData() error { + records, err := bb.Database.ReadAll("channelData") + if err != nil { + // no db? initialise one? + record := ChannelData{Name: bb.Channel, JoinTime: time.Now()} + rgb.YPrintf("[%s] No channel data for #%s exists, creating...\n", TimeStamp(), bb.Channel) + if err := bb.Database.Write("channelData", bb.Channel, record); err != nil { + return err + } + bb.channelData = make(map[string]ChannelData) + bb.channelData[bb.Channel] = record; + } else { + bb.channelData = make(map[string]ChannelData) + } + for _, data := range records { + record := ChannelData{} + err := json.Unmarshal([]byte(data), &record); + if err != nil { + return err + } + bb.channelData[record.Name] = record + } + return nil +} + +func (bb *KardBot) readOrCreateChannelKey(channel string) string { + magicCode := "" + var err error + var record ChannelData + if record, ok := bb.channelData[channel]; !ok { + rgb.YPrintf("[%s] No channel data for #%s exists, creating\n", TimeStamp(), channel) + err = bb.Database.Read("channelData", channel, &record); + if err == nil { + bb.channelData[channel] = record + } + } + record = bb.channelData[channel] + if err != nil || record.AdminKey == "" { + rgb.YPrintf("[%s] No channel key for #%s exists, creating one\n", TimeStamp(), channel) + newuu, _ := uuid.NewRandom() + magicCode = base64.StdEncoding.EncodeToString([]byte(newuu.String())) + record.AdminKey = magicCode + if record.Name == "" { + record.Name = channel + } + if err := bb.Database.Write("channelData", channel, record); err != nil { + rgb.RPrintf("[%s] Error writing channel data for #%s\n", TimeStamp(), channel) + } + bb.channelData[record.Name] = record + rgb.YPrintf("[%s] Cached channel key for #%s\n", TimeStamp(), record.Name) + } else { + magicCode = record.AdminKey + rgb.YPrintf("[%s] Loaded data for #%s\n", TimeStamp(), channel) + } + return magicCode +} + func TimeStamp() string { return TimeStampFmt(PSTFormat) } func TimeStampFmt(format string) string { return time.Now().Format(format) -} +} \ No newline at end of file From 6e7c52879cdf7d99bffacbaefabcbd882e0bfaa5 Mon Sep 17 00:00:00 2001 From: Martyn Ranyard Date: Fri, 21 Feb 2020 20:55:19 +0100 Subject: [PATCH 7/7] database and build date Signed-off-by: Martyn Ranyard --- main.go | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/main.go b/main.go index bc47778..89c4225 100755 --- a/main.go +++ b/main.go @@ -70,7 +70,7 @@ func readConfig() { rgb.RPrintf("[%s] Could not unmarshal `%s`. Unmarshal error: %s\n", irc.TimeStamp(), configFile, err) os.Exit(1) } - rgb.YPrintf("[%s] Read config file from `%s\n`", irc.TimeStamp(), configFile) + rgb.YPrintf("[%s] Read config file from `%s`\n", irc.TimeStamp(), configFile) return } @@ -147,7 +147,10 @@ func readBonusStrings() []string { return customStrings.Strings } +var buildDate string + func main() { + rgb.YPrintf("[%s] starting karaokard bot build %s\n", irc.TimeStamp(), buildDate) readConfig() rand.Seed(time.Now().UnixNano()) for _, val := range builtins.Karaokards { @@ -156,6 +159,13 @@ func main() { for _, val := range readBonusStrings() { selectablePrompts = append(selectablePrompts, val) } + persistentData := openDatabase() + var dbGlobalPrompts []string + if err := persistentData.Read("prompts", "global", &dbGlobalPrompts); err != nil { + persistentData.Write("prompts", "common", dbGlobalPrompts) + } + selectablePrompts := append(selectablePrompts, dbGlobalPrompts...) + rgb.YPrintf("[%s] %d prompts available.\n", irc.TimeStamp(), len(selectablePrompts)) oauthPath := "" if config.OAuthPath == "" { @@ -183,8 +193,6 @@ func main() { } oauthPath = config.OAuthPath } - persistentData := openDatabase() - persistentData.Write("prompts", "common", selectablePrompts) // Replace the channel name, bot name, and the path to the private directory with your respective // values. @@ -196,6 +204,7 @@ func main() { PrivatePath: oauthPath, Server: "irc.chat.twitch.tv", Prompts: selectablePrompts, + Database: *persistentData, } go func() { rgb.YPrintf("[%s] Starting webserver on port %s\n", irc.TimeStamp(), "5353")