Compare commits
7 Commits
06c3b47f47
...
3950f8b672
Author | SHA1 | Date |
---|---|---|
Martyn | 3950f8b672 | |
Martyn | 6e7c52879c | |
Martyn | fad3079132 | |
Martyn | e989b8454b | |
Martyn | 408516ecea | |
Martyn | 4d3b8a33e5 | |
Martyn | ba1c9438f3 |
|
@ -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 .
|
|
@ -2,9 +2,8 @@ FROM golang@sha256:cee6f4b901543e8e3f20da3a4f7caac6ea643fd5a46201c3c2387183a332d
|
||||||
RUN apk update && apk add --no-cache git make ca-certificates && update-ca-certificates
|
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 main.go /go/src/git.martyn.berlin/martyn/karaokards/
|
||||||
COPY internal/ /go/src/git.martyn.berlin/martyn/karaokards/internal/
|
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 .
|
COPY Makefile /go/src/git.martyn.berlin/martyn/karaokards/
|
||||||
#RUN ls /go/src/github.com/karaokards/ -l
|
RUN cd /go/src/git.martyn.berlin/martyn/karaokards/; make deps ; make static
|
||||||
|
|
||||||
|
|
||||||
FROM scratch
|
FROM scratch
|
||||||
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
{
|
{
|
||||||
"channels": ["iMartynOnTwitch"]
|
"channels": ["iMartynOnTwitch"],
|
||||||
|
"externalUrl": "karaokards.ing.martyn.berlin"
|
||||||
}
|
}
|
|
@ -1,12 +1,12 @@
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
data:
|
data:
|
||||||
strings.json: "{\r\n \"strings\": [\r\n\t \"Let Pineboy choose!\",\r\n\t \"They're
|
config.json: |
|
||||||
from the North\",\r\n \"Refers to food\"\r\n ]\r\n}"
|
{
|
||||||
|
"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
|
kind: ConfigMap
|
||||||
metadata:
|
metadata:
|
||||||
creationTimestamp: "2020-01-27T20:04:58Z"
|
name: kardconfig
|
||||||
name: extracards
|
|
||||||
namespace: karaokards
|
|
||||||
resourceVersion: "48537355"
|
|
||||||
selfLink: /api/v1/namespaces/karaokards/configmaps/extracards
|
|
||||||
uid: 4b1d73b0-4140-11ea-94d8-9cb6540931b5
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ spec:
|
||||||
run: kardbot
|
run: kardbot
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- image: imartyn/karaokardbot:0.0.3-linux-amd64
|
- image: imartyn/karaokardbot:devel
|
||||||
imagePullPolicy: IfNotPresent
|
imagePullPolicy: IfNotPresent
|
||||||
name: kardbot
|
name: kardbot
|
||||||
ports:
|
ports:
|
||||||
|
@ -39,6 +39,9 @@ spec:
|
||||||
- mountPath: /app/strings.json
|
- mountPath: /app/strings.json
|
||||||
name: extracards
|
name: extracards
|
||||||
subPath: strings.json
|
subPath: strings.json
|
||||||
|
- mountPath: /app/config.json
|
||||||
|
name: config
|
||||||
|
subPath: config.json
|
||||||
- mountPath: /data
|
- mountPath: /data
|
||||||
name: data
|
name: data
|
||||||
dnsPolicy: ClusterFirst
|
dnsPolicy: ClusterFirst
|
||||||
|
@ -56,8 +59,15 @@ spec:
|
||||||
items:
|
items:
|
||||||
- key: strings.json
|
- key: strings.json
|
||||||
path: strings.json
|
path: strings.json
|
||||||
name: extracards
|
name: kardconfig
|
||||||
name: extracards
|
name: extracards
|
||||||
|
- configMap:
|
||||||
|
defaultMode: 420
|
||||||
|
items:
|
||||||
|
- key: config.json
|
||||||
|
path: config.json
|
||||||
|
name: kardconfig
|
||||||
|
name: config
|
||||||
- name: data
|
- name: data
|
||||||
persistentVolumeClaim:
|
persistentVolumeClaim:
|
||||||
claimName: kkard-data
|
claimName: kkard-data
|
|
@ -3,6 +3,7 @@ package irc
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"encoding/base64"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
@ -15,6 +16,8 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
rgb "github.com/foresthoffman/rgblog"
|
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"
|
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.
|
// The developer application client ID. Used for API calls to Twitch.
|
||||||
ClientID string `json:"client_id,omitempty"`
|
ClientID string `json:"client_id,omitempty"`
|
||||||
|
|
||||||
// List of Channels to join
|
|
||||||
Channels []string `json:"channels,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type KardBot struct {
|
type KardBot struct {
|
||||||
|
@ -54,6 +54,16 @@ type KardBot struct {
|
||||||
Server string
|
Server string
|
||||||
startTime time.Time
|
startTime time.Time
|
||||||
Prompts []string
|
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
|
// 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
|
// channel-owner specific commands
|
||||||
if userName == bb.Channel {
|
if userName == channel {
|
||||||
switch cmd {
|
switch cmd {
|
||||||
case "tbdown":
|
case "tbdown":
|
||||||
rgb.CPrintf(
|
rgb.CPrintf(
|
||||||
|
@ -144,6 +154,14 @@ func (bb *KardBot) HandleChat() error {
|
||||||
|
|
||||||
bb.Disconnect()
|
bb.Disconnect()
|
||||||
return nil
|
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:
|
default:
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
|
@ -232,15 +250,24 @@ func (bb *KardBot) Start() {
|
||||||
err := bb.ReadCredentials()
|
err := bb.ReadCredentials()
|
||||||
if nil != err {
|
if nil != err {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
fmt.Println("Aborting...")
|
fmt.Println("Aborting!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = bb.readChannelData()
|
||||||
|
if nil != err {
|
||||||
|
fmt.Println(err)
|
||||||
|
fmt.Println("Aborting!")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
bb.Connect()
|
bb.Connect()
|
||||||
bb.Login()
|
bb.Login()
|
||||||
if len(bb.Credentials.Channels) > 0 {
|
if len(bb.channelData) > 0 {
|
||||||
bb.JoinChannel(bb.Credentials.Channels...)
|
for channelName := range(bb.channelData) {
|
||||||
|
bb.JoinChannel(channelName)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
bb.JoinChannel()
|
bb.JoinChannel()
|
||||||
}
|
}
|
||||||
|
@ -257,6 +284,63 @@ 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 {
|
func TimeStamp() string {
|
||||||
return TimeStampFmt(PSTFormat)
|
return TimeStampFmt(PSTFormat)
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ func HealthHandler(response http.ResponseWriter, request *http.Request) {
|
||||||
|
|
||||||
func NotFoundHandler(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.Header().Add("X-Template-File", "html"+request.URL.Path)
|
||||||
|
response.WriteHeader(404)
|
||||||
tmpl := template.Must(template.ParseFiles("web/404.html"))
|
tmpl := template.Must(template.ParseFiles("web/404.html"))
|
||||||
tmpl.Execute(response, nil)
|
tmpl.Execute(response, nil)
|
||||||
}
|
}
|
||||||
|
@ -72,7 +73,7 @@ func TemplateHandler(response http.ResponseWriter, request *http.Request) {
|
||||||
// NotFoundHandler(response, request)
|
// NotFoundHandler(response, request)
|
||||||
// return
|
// 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)
|
err = tmpl.Execute(response, td)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(response, err.Error(), http.StatusInternalServerError)
|
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) {
|
func HandleHTTP(passedIrcBot irc.KardBot) {
|
||||||
ircBot = passedIrcBot
|
ircBot = passedIrcBot
|
||||||
r := mux.NewRouter()
|
r := mux.NewRouter()
|
||||||
|
@ -89,6 +95,7 @@ func HandleHTTP(passedIrcBot irc.KardBot) {
|
||||||
r.HandleFunc("/healthz", HealthHandler)
|
r.HandleFunc("/healthz", HealthHandler)
|
||||||
r.HandleFunc("/example/{.*}", TemplateHandler)
|
r.HandleFunc("/example/{.*}", TemplateHandler)
|
||||||
r.HandleFunc("/cover.css", CSSHandler)
|
r.HandleFunc("/cover.css", CSSHandler)
|
||||||
|
r.HandleFunc("/admin/{channel}/{key}", AdminHandler)
|
||||||
http.Handle("/", r)
|
http.Handle("/", r)
|
||||||
srv := &http.Server{
|
srv := &http.Server{
|
||||||
Handler: loggedRouter,
|
Handler: loggedRouter,
|
||||||
|
|
15
main.go
15
main.go
|
@ -70,7 +70,7 @@ func readConfig() {
|
||||||
rgb.RPrintf("[%s] Could not unmarshal `%s`. Unmarshal error: %s\n", irc.TimeStamp(), configFile, err)
|
rgb.RPrintf("[%s] Could not unmarshal `%s`. Unmarshal error: %s\n", irc.TimeStamp(), configFile, err)
|
||||||
os.Exit(1)
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,7 +147,10 @@ func readBonusStrings() []string {
|
||||||
return customStrings.Strings
|
return customStrings.Strings
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var buildDate string
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
rgb.YPrintf("[%s] starting karaokard bot build %s\n", irc.TimeStamp(), buildDate)
|
||||||
readConfig()
|
readConfig()
|
||||||
rand.Seed(time.Now().UnixNano())
|
rand.Seed(time.Now().UnixNano())
|
||||||
for _, val := range builtins.Karaokards {
|
for _, val := range builtins.Karaokards {
|
||||||
|
@ -156,6 +159,13 @@ func main() {
|
||||||
for _, val := range readBonusStrings() {
|
for _, val := range readBonusStrings() {
|
||||||
selectablePrompts = append(selectablePrompts, val)
|
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))
|
rgb.YPrintf("[%s] %d prompts available.\n", irc.TimeStamp(), len(selectablePrompts))
|
||||||
oauthPath := ""
|
oauthPath := ""
|
||||||
if config.OAuthPath == "" {
|
if config.OAuthPath == "" {
|
||||||
|
@ -183,8 +193,6 @@ func main() {
|
||||||
}
|
}
|
||||||
oauthPath = config.OAuthPath
|
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
|
// Replace the channel name, bot name, and the path to the private directory with your respective
|
||||||
// values.
|
// values.
|
||||||
|
@ -196,6 +204,7 @@ func main() {
|
||||||
PrivatePath: oauthPath,
|
PrivatePath: oauthPath,
|
||||||
Server: "irc.chat.twitch.tv",
|
Server: "irc.chat.twitch.tv",
|
||||||
Prompts: selectablePrompts,
|
Prompts: selectablePrompts,
|
||||||
|
Database: *persistentData,
|
||||||
}
|
}
|
||||||
go func() {
|
go func() {
|
||||||
rgb.YPrintf("[%s] Starting webserver on port %s\n", irc.TimeStamp(), "5353")
|
rgb.YPrintf("[%s] Starting webserver on port %s\n", irc.TimeStamp(), "5353")
|
||||||
|
|
Loading…
Reference in New Issue