Compare commits
2 Commits
4803122aad
...
8dbe61da21
Author | SHA1 | Date |
---|---|---|
Martyn | 8dbe61da21 | |
Martyn | 11c96d20d0 |
|
@ -2,8 +2,8 @@ package irc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"encoding/json"
|
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
@ -16,17 +16,23 @@ 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"
|
uuid "github.com/google/uuid"
|
||||||
|
scribble "github.com/nanobox-io/golang-scribble"
|
||||||
)
|
)
|
||||||
|
|
||||||
const PSTFormat = "Jan 2 15:04:05 PST"
|
const PSTFormat = "Jan 2 15:04:05 PST"
|
||||||
|
|
||||||
|
// Regex for parsing connection messages
|
||||||
|
//
|
||||||
|
// First matched group is our real username - twitch doesn't complain at using NICK command but doesn't honor it.
|
||||||
|
var ConnectRegex *regexp.Regexp = regexp.MustCompile(`^:tmi.twitch.tv 001 ([^ ]+) .*`)
|
||||||
|
|
||||||
// Regex for parsing PRIVMSG strings.
|
// Regex for parsing PRIVMSG strings.
|
||||||
//
|
//
|
||||||
// First matched group is the user's name, second is the channel? and the third matched group is the content of the
|
// First matched group is the user's name, second is the channel? and the third matched group is the content of the
|
||||||
// user's message.
|
// user's message.
|
||||||
var MsgRegex *regexp.Regexp = regexp.MustCompile(`^:(\w+)!\w+@\w+\.tmi\.twitch\.tv (PRIVMSG) #(\w+)(?: :(.*))?$`)
|
var MsgRegex *regexp.Regexp = regexp.MustCompile(`^:(\w+)!\w+@\w+\.tmi\.twitch\.tv (PRIVMSG) #(\w+)(?: :(.*))?$`)
|
||||||
|
var DirectMsgRegex *regexp.Regexp = regexp.MustCompile(`^:(\w+)!\w+@\w+\.tmi\.twitch\.tv (PRIVMSG) (\w+)(?: :(.*))?$`)
|
||||||
|
|
||||||
// Regex for parsing user commands, from already parsed PRIVMSG strings.
|
// Regex for parsing user commands, from already parsed PRIVMSG strings.
|
||||||
//
|
//
|
||||||
|
@ -54,16 +60,17 @@ type KardBot struct {
|
||||||
Server string
|
Server string
|
||||||
startTime time.Time
|
startTime time.Time
|
||||||
Prompts []string
|
Prompts []string
|
||||||
Database scribble.Driver
|
Database scribble.Driver
|
||||||
ChannelData map[string]ChannelData
|
ChannelData map[string]ChannelData
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChannelData struct {
|
type ChannelData struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
AdminKey string `json:"value,omitempty"`
|
AdminKey string `json:"value,omitempty"`
|
||||||
Command string `json:"customcommand,omitempty"`
|
Command string `json:"customcommand,omitempty"`
|
||||||
ExtraStrings string `json:"extrastrings,omitempty"`
|
ExtraStrings string `json:"extrastrings,omitempty"`
|
||||||
JoinTime time.Time `json:"jointime"`
|
JoinTime time.Time `json:"jointime"`
|
||||||
|
ControlChannel bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
|
@ -118,9 +125,29 @@ func (bb *KardBot) HandleChat() error {
|
||||||
bb.conn.Write([]byte("PONG :tmi.twitch.tv\r\n"))
|
bb.conn.Write([]byte("PONG :tmi.twitch.tv\r\n"))
|
||||||
continue
|
continue
|
||||||
} else {
|
} else {
|
||||||
|
matches := ConnectRegex.FindStringSubmatch(line)
|
||||||
|
if nil != matches {
|
||||||
|
realUserName := matches[1]
|
||||||
|
if bb.ChannelData[realUserName].Name == "" {
|
||||||
|
record := ChannelData{Name: realUserName, JoinTime: time.Now(), Command: "card", ControlChannel: true}
|
||||||
|
bb.Database.Write("channelData", realUserName, record)
|
||||||
|
bb.ChannelData[realUserName] = record
|
||||||
|
}
|
||||||
|
bb.JoinChannel(realUserName)
|
||||||
|
}
|
||||||
|
|
||||||
|
matches = DirectMsgRegex.FindStringSubmatch(line)
|
||||||
|
if nil != matches {
|
||||||
|
userName := matches[1]
|
||||||
|
// msgType := matches[2]
|
||||||
|
// channel := matches[3]
|
||||||
|
msg := matches[4]
|
||||||
|
rgb.GPrintf("[%s] Direct message %s: %s\n", TimeStamp(), userName, msg)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// handle a PRIVMSG message
|
// handle a PRIVMSG message
|
||||||
matches := MsgRegex.FindStringSubmatch(line)
|
matches = MsgRegex.FindStringSubmatch(line)
|
||||||
if nil != matches {
|
if nil != matches {
|
||||||
userName := matches[1]
|
userName := matches[1]
|
||||||
msgType := matches[2]
|
msgType := matches[2]
|
||||||
|
@ -142,6 +169,16 @@ func (bb *KardBot) HandleChat() error {
|
||||||
rgb.CPrintf("[%s] Card asked for by %s on %s' channel!\n", TimeStamp(), userName, channel)
|
rgb.CPrintf("[%s] Card asked for by %s on %s' channel!\n", TimeStamp(), userName, channel)
|
||||||
|
|
||||||
bb.Say("Your prompt is : "+bb.Prompts[rand.Intn(len(bb.Prompts))], channel)
|
bb.Say("Your prompt is : "+bb.Prompts[rand.Intn(len(bb.Prompts))], channel)
|
||||||
|
case "join":
|
||||||
|
if bb.ChannelData[channel].ControlChannel {
|
||||||
|
rgb.CPrintf("[%s] Join asked for by %s on %s' channel!\n", TimeStamp(), userName, channel)
|
||||||
|
if bb.ChannelData[userName].Name == "" {
|
||||||
|
record := ChannelData{Name: userName, JoinTime: time.Now(), Command: "card", ControlChannel: true}
|
||||||
|
bb.Database.Write("channelData", userName, record)
|
||||||
|
bb.ChannelData[userName] = record
|
||||||
|
}
|
||||||
|
bb.JoinChannel(userName)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// channel-owner specific commands
|
// channel-owner specific commands
|
||||||
|
@ -155,13 +192,17 @@ func (bb *KardBot) HandleChat() error {
|
||||||
|
|
||||||
bb.Disconnect()
|
bb.Disconnect()
|
||||||
return nil
|
return nil
|
||||||
case "wat":
|
case "kcardadmin":
|
||||||
magicCode := bb.readOrCreateChannelKey(channel)
|
magicCode := bb.readOrCreateChannelKey(channel)
|
||||||
rgb.CPrintf(
|
rgb.CPrintf(
|
||||||
"[%s] Magic code is %s - https://karaokards.ing.martyn.berlin/admin/%s/%s\n",
|
"[%s] Magic code is %s - https://karaokards.ing.martyn.berlin/admin/%s/%s\n",
|
||||||
TimeStamp(),
|
TimeStamp(),
|
||||||
magicCode, userName, magicCode,
|
magicCode, userName, magicCode,
|
||||||
)
|
)
|
||||||
|
err := bb.Msg("Welcome to Karaokards, your admin panel is https://karaokards.ing.martyn.berlin/admin/"+userName+"/"+magicCode, userName)
|
||||||
|
if err != nil {
|
||||||
|
rgb.RPrintf("[%s] ERROR %s\n",err)
|
||||||
|
}
|
||||||
bb.Say("Ack.")
|
bb.Say("Ack.")
|
||||||
default:
|
default:
|
||||||
// do nothing
|
// do nothing
|
||||||
|
@ -185,7 +226,6 @@ func (bb *KardBot) Login() {
|
||||||
bb.conn.Write([]byte("NICK " + bb.Name + "\r\n"))
|
bb.conn.Write([]byte("NICK " + bb.Name + "\r\n"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (bb *KardBot) LeaveChannel(channels ...string) {
|
func (bb *KardBot) LeaveChannel(channels ...string) {
|
||||||
for _, channel := range channels {
|
for _, channel := range channels {
|
||||||
rgb.YPrintf("[%s] Leaving #%s...\n", TimeStamp(), channel)
|
rgb.YPrintf("[%s] Leaving #%s...\n", TimeStamp(), channel)
|
||||||
|
@ -227,6 +267,31 @@ func (bb *KardBot) ReadCredentials() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (bb *KardBot) Msg(msg string, users ...string) error {
|
||||||
|
if "" == msg {
|
||||||
|
return errors.New("BasicBot.Say: msg was empty.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if message is too large for IRC
|
||||||
|
if len(msg) > 512 {
|
||||||
|
return errors.New("BasicBot.Say: msg exceeded 512 bytes")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(users) == 0 {
|
||||||
|
return errors.New("BasicBot.Say: users was empty.")
|
||||||
|
}
|
||||||
|
|
||||||
|
rgb.YPrintf("[%s] sending %s to users %v as @%s!\n", TimeStamp(), msg, users, bb.Name)
|
||||||
|
for _, channel := range users {
|
||||||
|
_, err := bb.conn.Write([]byte(fmt.Sprintf("PRIVMSG %s :%s\r\n", channel, msg)))
|
||||||
|
rgb.YPrintf("[%s] PRIVMSG %s :%s\r\n", TimeStamp(), channel, msg)
|
||||||
|
if nil != err {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Makes the bot send a message to the chat channel.
|
// Makes the bot send a message to the chat channel.
|
||||||
func (bb *KardBot) Say(msg string, channels ...string) error {
|
func (bb *KardBot) Say(msg string, channels ...string) error {
|
||||||
if "" == msg {
|
if "" == msg {
|
||||||
|
@ -263,7 +328,7 @@ func (bb *KardBot) Start() {
|
||||||
fmt.Println("Aborting!")
|
fmt.Println("Aborting!")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = bb.readChannelData()
|
err = bb.readChannelData()
|
||||||
if nil != err {
|
if nil != err {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
|
@ -275,7 +340,7 @@ func (bb *KardBot) Start() {
|
||||||
bb.Connect()
|
bb.Connect()
|
||||||
bb.Login()
|
bb.Login()
|
||||||
if len(bb.ChannelData) > 0 {
|
if len(bb.ChannelData) > 0 {
|
||||||
for channelName := range(bb.ChannelData) {
|
for channelName := range bb.ChannelData {
|
||||||
bb.JoinChannel(channelName)
|
bb.JoinChannel(channelName)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -304,20 +369,20 @@ func (bb *KardBot) readChannelData() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
bb.ChannelData = make(map[string]ChannelData)
|
bb.ChannelData = make(map[string]ChannelData)
|
||||||
bb.ChannelData[bb.Channel] = record;
|
bb.ChannelData[bb.Channel] = record
|
||||||
} else {
|
} else {
|
||||||
bb.ChannelData = make(map[string]ChannelData)
|
bb.ChannelData = make(map[string]ChannelData)
|
||||||
}
|
}
|
||||||
for _, data := range records {
|
for _, data := range records {
|
||||||
record := ChannelData{}
|
record := ChannelData{}
|
||||||
err := json.Unmarshal([]byte(data), &record);
|
err := json.Unmarshal([]byte(data), &record)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if record.Name != "" {
|
if record.Name != "" {
|
||||||
if record.Command == "" {
|
if record.Command == "" {
|
||||||
record.Command = "card"
|
record.Command = "card"
|
||||||
|
|
||||||
rgb.YPrintf("[%s] Rewriting data for #%s...\n", TimeStamp(), bb.Channel)
|
rgb.YPrintf("[%s] Rewriting data for #%s...\n", TimeStamp(), bb.Channel)
|
||||||
if err := bb.Database.Write("channelData", record.Name, record); err != nil {
|
if err := bb.Database.Write("channelData", record.Name, record); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -346,7 +411,7 @@ func (bb *KardBot) readOrCreateChannelKey(channel string) string {
|
||||||
var record ChannelData
|
var record ChannelData
|
||||||
if record, ok := bb.ChannelData[channel]; !ok {
|
if record, ok := bb.ChannelData[channel]; !ok {
|
||||||
rgb.YPrintf("[%s] No channel data for #%s exists, creating\n", TimeStamp(), channel)
|
rgb.YPrintf("[%s] No channel data for #%s exists, creating\n", TimeStamp(), channel)
|
||||||
err = bb.Database.Read("channelData", channel, &record);
|
err = bb.Database.Read("channelData", channel, &record)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
bb.ChannelData[channel] = record
|
bb.ChannelData[channel] = record
|
||||||
}
|
}
|
||||||
|
@ -378,4 +443,4 @@ func TimeStamp() string {
|
||||||
|
|
||||||
func TimeStampFmt(format string) string {
|
func TimeStampFmt(format string) string {
|
||||||
return time.Now().Format(format)
|
return time.Now().Format(format)
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,40 +91,40 @@ func AdminHandler(response http.ResponseWriter, request *http.Request) {
|
||||||
if vars["key"] != ircBot.ChannelData[vars["channel"]].AdminKey {
|
if vars["key"] != ircBot.ChannelData[vars["channel"]].AdminKey {
|
||||||
UnauthorizedHandler(response, request)
|
UnauthorizedHandler(response, request)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
type TemplateData struct {
|
type TemplateData struct {
|
||||||
Channel string
|
Channel string
|
||||||
Command string
|
Command string
|
||||||
ExtraStrings string
|
ExtraStrings string
|
||||||
SinceTime time.Time
|
SinceTime time.Time
|
||||||
Leaving bool
|
Leaving bool
|
||||||
}
|
}
|
||||||
channelData := ircBot.ChannelData[vars["channel"]]
|
channelData := ircBot.ChannelData[vars["channel"]]
|
||||||
var td = TemplateData{channelData.Name, channelData.Command, channelData.ExtraStrings, channelData.JoinTime, false}
|
var td = TemplateData{channelData.Name, channelData.Command, channelData.ExtraStrings, channelData.JoinTime, false}
|
||||||
|
|
||||||
if request.Method == "POST" {
|
if request.Method == "POST" {
|
||||||
request.ParseForm()
|
request.ParseForm()
|
||||||
if strings.Join(request.PostForm["leave"],",") == "Leave twitch channel" {
|
if strings.Join(request.PostForm["leave"], ",") == "Leave twitch channel" {
|
||||||
td.Leaving = true
|
td.Leaving = true
|
||||||
} else if strings.Join(request.PostForm["reallyleave"],",") == "Really leave twitch channel" {
|
} else if strings.Join(request.PostForm["reallyleave"], ",") == "Really leave twitch channel" {
|
||||||
delete(ircBot.ChannelData,vars["channel"])
|
delete(ircBot.ChannelData, vars["channel"])
|
||||||
ircBot.Database.Delete("channelData", vars["channel"])
|
ircBot.Database.Delete("channelData", vars["channel"])
|
||||||
ircBot.LeaveChannel(vars["channel"])
|
ircBot.LeaveChannel(vars["channel"])
|
||||||
LeaveHandler(response, request)
|
LeaveHandler(response, request)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
sourceData := ircBot.ChannelData[vars["channel"]]
|
sourceData := ircBot.ChannelData[vars["channel"]]
|
||||||
if strings.Join(request.PostForm["Command"],",") != "" {
|
if strings.Join(request.PostForm["Command"], ",") != "" {
|
||||||
sourceData.Command = strings.Join(request.PostForm["Command"],",")
|
sourceData.Command = strings.Join(request.PostForm["Command"], ",")
|
||||||
td.Command = sourceData.Command
|
td.Command = sourceData.Command
|
||||||
ircBot.ChannelData[vars["channel"]] = sourceData
|
ircBot.ChannelData[vars["channel"]] = sourceData
|
||||||
}
|
}
|
||||||
if strings.Join(request.PostForm["ExtraStrings"],",") != sourceData.ExtraStrings {
|
if strings.Join(request.PostForm["ExtraStrings"], ",") != sourceData.ExtraStrings {
|
||||||
sourceData.ExtraStrings = strings.Join(request.PostForm["ExtraStrings"],",")
|
sourceData.ExtraStrings = strings.Join(request.PostForm["ExtraStrings"], ",")
|
||||||
td.ExtraStrings = sourceData.ExtraStrings
|
td.ExtraStrings = sourceData.ExtraStrings
|
||||||
ircBot.ChannelData[vars["channel"]] = sourceData
|
ircBot.ChannelData[vars["channel"]] = sourceData
|
||||||
}
|
}
|
||||||
ircBot.Database.Write("channelData", vars["channel"], sourceData);
|
ircBot.Database.Write("channelData", vars["channel"], sourceData)
|
||||||
}
|
}
|
||||||
tmpl := template.Must(template.ParseFiles("web/admin.html"))
|
tmpl := template.Must(template.ParseFiles("web/admin.html"))
|
||||||
tmpl.Execute(response, td)
|
tmpl.Execute(response, td)
|
||||||
|
|
428
main.go
428
main.go
|
@ -1,214 +1,214 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
builtins "git.martyn.berlin/martyn/karaokards/internal/builtins"
|
builtins "git.martyn.berlin/martyn/karaokards/internal/builtins"
|
||||||
irc "git.martyn.berlin/martyn/karaokards/internal/irc"
|
irc "git.martyn.berlin/martyn/karaokards/internal/irc"
|
||||||
webserver "git.martyn.berlin/martyn/karaokards/internal/webserver"
|
webserver "git.martyn.berlin/martyn/karaokards/internal/webserver"
|
||||||
rgb "github.com/foresthoffman/rgblog"
|
rgb "github.com/foresthoffman/rgblog"
|
||||||
scribble "github.com/nanobox-io/golang-scribble"
|
scribble "github.com/nanobox-io/golang-scribble"
|
||||||
)
|
)
|
||||||
|
|
||||||
type configStruct struct {
|
type configStruct struct {
|
||||||
InitialChannels []string `json:"channels"`
|
InitialChannels []string `json:"channels"`
|
||||||
OAuthPath string `json:"oauthpath,omitempty"`
|
OAuthPath string `json:"oauthpath,omitempty"`
|
||||||
StringPath string `json:"authpath,omitempty"`
|
StringPath string `json:"authpath,omitempty"`
|
||||||
DataPath string `json:"datapath,omitempty"`
|
DataPath string `json:"datapath,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type customStringsStruct struct {
|
type customStringsStruct struct {
|
||||||
Strings []string `json:"strings,omitempty"`
|
Strings []string `json:"strings,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var selectablePrompts []string
|
var selectablePrompts []string
|
||||||
|
|
||||||
var customStrings customStringsStruct
|
var customStrings customStringsStruct
|
||||||
|
|
||||||
var config configStruct
|
var config configStruct
|
||||||
|
|
||||||
func readConfig() {
|
func readConfig() {
|
||||||
var data []byte
|
var data []byte
|
||||||
var err error
|
var err error
|
||||||
configFile := ""
|
configFile := ""
|
||||||
if os.Getenv("KARAOKARDS_CONFIGFILE") != "" {
|
if os.Getenv("KARAOKARDS_CONFIGFILE") != "" {
|
||||||
if _, err := os.Stat(os.Getenv("KARAOKARDS_CONFIGFILE")); os.IsNotExist(err) {
|
if _, err := os.Stat(os.Getenv("KARAOKARDS_CONFIGFILE")); os.IsNotExist(err) {
|
||||||
rgb.RPrintf("[%s] Error, KARAOKARDS_CONFIGFILE env var set and '%s' doesn't exist!\n", irc.TimeStamp(), os.Getenv("KARAOKARDS_CONFIGFILE"))
|
rgb.RPrintf("[%s] Error, KARAOKARDS_CONFIGFILE env var set and '%s' doesn't exist!\n", irc.TimeStamp(), os.Getenv("KARAOKARDS_CONFIGFILE"))
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
configFile = os.Getenv("KARAOKARDS_CONFIGFILE")
|
configFile = os.Getenv("KARAOKARDS_CONFIGFILE")
|
||||||
} else {
|
} else {
|
||||||
ex, err := os.Executable()
|
ex, err := os.Executable()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rgb.YPrintf("[%s] Warning, KARAOKARDS_CONFIGFILE env var unset and cannot find executable!\n", irc.TimeStamp())
|
rgb.YPrintf("[%s] Warning, KARAOKARDS_CONFIGFILE env var unset and cannot find executable!\n", irc.TimeStamp())
|
||||||
}
|
}
|
||||||
exPath := filepath.Dir(ex)
|
exPath := filepath.Dir(ex)
|
||||||
if _, err := os.Stat(exPath + "/config.json"); os.IsNotExist(err) {
|
if _, err := os.Stat(exPath + "/config.json"); os.IsNotExist(err) {
|
||||||
rgb.YPrintf("[%s] Warning, KARAOKARDS_CONFIGFILE env var unset and `config.json` not alongside executable!\n", irc.TimeStamp())
|
rgb.YPrintf("[%s] Warning, KARAOKARDS_CONFIGFILE env var unset and `config.json` not alongside executable!\n", irc.TimeStamp())
|
||||||
if _, err := os.Stat("/etc/karaokards/config.json"); os.IsNotExist(err) {
|
if _, err := os.Stat("/etc/karaokards/config.json"); os.IsNotExist(err) {
|
||||||
rgb.RPrintf("[%s] Error, KARAOKARDS_CONFIGFILE env var unset and neither '%s' nor '%s' exist!\n", irc.TimeStamp(), exPath + "/config.json", "/etc/karaokards/config.json")
|
rgb.RPrintf("[%s] Error, KARAOKARDS_CONFIGFILE env var unset and neither '%s' nor '%s' exist!\n", irc.TimeStamp(), exPath+"/config.json", "/etc/karaokards/config.json")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
} else {
|
} else {
|
||||||
configFile = "/etc/karaokards/config.json"
|
configFile = "/etc/karaokards/config.json"
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
configFile = exPath + "/config.json"
|
configFile = exPath + "/config.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
data, err = ioutil.ReadFile(configFile)
|
data, err = ioutil.ReadFile(configFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rgb.RPrintf("[%s] Could not read `%s`. File reading error: %s\n", irc.TimeStamp(), configFile, err)
|
rgb.RPrintf("[%s] Could not read `%s`. File reading error: %s\n", irc.TimeStamp(), configFile, err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
err = json.Unmarshal(data, &customStrings)
|
err = json.Unmarshal(data, &customStrings)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
//openDatabase "database" in this sense being a scribble db
|
//openDatabase "database" in this sense being a scribble db
|
||||||
func openDatabase() *scribble.Driver {
|
func openDatabase() *scribble.Driver {
|
||||||
dataPath := ""
|
dataPath := ""
|
||||||
if config.DataPath == "" {
|
if config.DataPath == "" {
|
||||||
if os.Getenv("KARAOKARDS_DATA_FOLDER") != "" {
|
if os.Getenv("KARAOKARDS_DATA_FOLDER") != "" {
|
||||||
if _, err := os.Stat(os.Getenv("KARAOKARDS_DATA_FOLDER")); os.IsNotExist(err) {
|
if _, err := os.Stat(os.Getenv("KARAOKARDS_DATA_FOLDER")); os.IsNotExist(err) {
|
||||||
rgb.RPrintf("[%s] Error, KARAOKARDS_DATA_FOLDER env var set and '%s' doesn't exist!\n", irc.TimeStamp(), os.Getenv("KARAOKARDS_DATA_FOLDER"))
|
rgb.RPrintf("[%s] Error, KARAOKARDS_DATA_FOLDER env var set and '%s' doesn't exist!\n", irc.TimeStamp(), os.Getenv("KARAOKARDS_DATA_FOLDER"))
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
dataPath = os.Getenv("KARAOKARDS_DATA_FOLDER")
|
dataPath = os.Getenv("KARAOKARDS_DATA_FOLDER")
|
||||||
} else {
|
} else {
|
||||||
ex, err := os.Executable()
|
ex, err := os.Executable()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rgb.RPrintf("[%s] Error, KARAOKARDS_DATA_FOLDER env var unset and cannot find executable!\n", irc.TimeStamp())
|
rgb.RPrintf("[%s] Error, KARAOKARDS_DATA_FOLDER env var unset and cannot find executable!\n", irc.TimeStamp())
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
exPath := filepath.Dir(ex)
|
exPath := filepath.Dir(ex)
|
||||||
if _, err := os.Stat(exPath + "/data"); os.IsNotExist(err) {
|
if _, err := os.Stat(exPath + "/data"); os.IsNotExist(err) {
|
||||||
rgb.YPrintf("[%s] Warning %s doesn't exist, trying to create it.\n", irc.TimeStamp(), exPath + "/data")
|
rgb.YPrintf("[%s] Warning %s doesn't exist, trying to create it.\n", irc.TimeStamp(), exPath+"/data")
|
||||||
err = os.Mkdir(exPath + "/data", 0770)
|
err = os.Mkdir(exPath+"/data", 0770)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rgb.RPrintf("[%s] Error cannot create %s: %s!\n", irc.TimeStamp(), exPath + "/data", err)
|
rgb.RPrintf("[%s] Error cannot create %s: %s!\n", irc.TimeStamp(), exPath+"/data", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dataPath = exPath + "/data"
|
dataPath = exPath + "/data"
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if _, err := os.Stat(config.OAuthPath); os.IsNotExist(err) {
|
if _, err := os.Stat(config.OAuthPath); os.IsNotExist(err) {
|
||||||
rgb.RPrintf("[%s] Error, config-specified path '%s' doesn't exist!\n", irc.TimeStamp(), os.Getenv("KARAOKARDS_DATA_FOLDER"))
|
rgb.RPrintf("[%s] Error, config-specified path '%s' doesn't exist!\n", irc.TimeStamp(), os.Getenv("KARAOKARDS_DATA_FOLDER"))
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
dataPath = config.DataPath
|
dataPath = config.DataPath
|
||||||
}
|
}
|
||||||
db, err := scribble.New(dataPath, nil)
|
db, err := scribble.New(dataPath, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rgb.RPrintf("[%s] Error opening database in '%s' : %s\n", irc.TimeStamp(), dataPath, err)
|
rgb.RPrintf("[%s] Error opening database in '%s' : %s\n", irc.TimeStamp(), dataPath, err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
return db
|
return db
|
||||||
}
|
}
|
||||||
|
|
||||||
func readBonusStrings() []string {
|
func readBonusStrings() []string {
|
||||||
var data []byte
|
var data []byte
|
||||||
var err error
|
var err error
|
||||||
if config.StringPath == "" {
|
if config.StringPath == "" {
|
||||||
ex, err := os.Executable()
|
ex, err := os.Executable()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rgb.YPrintf("[%s] Could not read `strings.json`, will only have builtin prompts. File reading error: %s\n", irc.TimeStamp(), err)
|
rgb.YPrintf("[%s] Could not read `strings.json`, will only have builtin prompts. File reading error: %s\n", irc.TimeStamp(), err)
|
||||||
return []string{}
|
return []string{}
|
||||||
}
|
}
|
||||||
exPath := filepath.Dir(ex)
|
exPath := filepath.Dir(ex)
|
||||||
data, err = ioutil.ReadFile(exPath + "/strings.json")
|
data, err = ioutil.ReadFile(exPath + "/strings.json")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rgb.YPrintf("[%s] Could not read `strings.json`, will only have builtin prompts. File reading error: %s\n", irc.TimeStamp(), err)
|
rgb.YPrintf("[%s] Could not read `strings.json`, will only have builtin prompts. File reading error: %s\n", irc.TimeStamp(), err)
|
||||||
return []string{}
|
return []string{}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
data, err = ioutil.ReadFile(config.StringPath)
|
data, err = ioutil.ReadFile(config.StringPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rgb.YPrintf("[%s] Could not read `strings.json`, will only have builtin prompts. File reading error: %s\n", irc.TimeStamp(), err)
|
rgb.YPrintf("[%s] Could not read `strings.json`, will only have builtin prompts. File reading error: %s\n", irc.TimeStamp(), err)
|
||||||
return []string{}
|
return []string{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err = json.Unmarshal(data, &customStrings)
|
err = json.Unmarshal(data, &customStrings)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rgb.YPrintf("[%s] Could not unmarshal `strings.json`, will only have builtin prompts. Unmarshal error: %s\n", irc.TimeStamp(), err)
|
rgb.YPrintf("[%s] Could not unmarshal `strings.json`, will only have builtin prompts. Unmarshal error: %s\n", irc.TimeStamp(), err)
|
||||||
return []string{}
|
return []string{}
|
||||||
}
|
}
|
||||||
rgb.YPrintf("[%s] Read %d prompts from `strings.json`\n", irc.TimeStamp(), len(customStrings.Strings))
|
rgb.YPrintf("[%s] Read %d prompts from `strings.json`\n", irc.TimeStamp(), len(customStrings.Strings))
|
||||||
return customStrings.Strings
|
return customStrings.Strings
|
||||||
}
|
}
|
||||||
|
|
||||||
var buildDate string
|
var buildDate string
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
rgb.YPrintf("[%s] starting karaokard bot build %s\n", irc.TimeStamp(), buildDate)
|
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 {
|
||||||
selectablePrompts = append(selectablePrompts, val)
|
selectablePrompts = append(selectablePrompts, val)
|
||||||
}
|
}
|
||||||
for _, val := range readBonusStrings() {
|
for _, val := range readBonusStrings() {
|
||||||
selectablePrompts = append(selectablePrompts, val)
|
selectablePrompts = append(selectablePrompts, val)
|
||||||
}
|
}
|
||||||
persistentData := openDatabase()
|
persistentData := openDatabase()
|
||||||
var dbGlobalPrompts []string
|
var dbGlobalPrompts []string
|
||||||
if err := persistentData.Read("prompts", "global", &dbGlobalPrompts); err != nil {
|
if err := persistentData.Read("prompts", "global", &dbGlobalPrompts); err != nil {
|
||||||
persistentData.Write("prompts", "common", dbGlobalPrompts)
|
persistentData.Write("prompts", "common", dbGlobalPrompts)
|
||||||
}
|
}
|
||||||
selectablePrompts := append(selectablePrompts, 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 == "" {
|
||||||
if os.Getenv("TWITCH_OAUTH_JSON") != "" {
|
if os.Getenv("TWITCH_OAUTH_JSON") != "" {
|
||||||
if _, err := os.Stat(os.Getenv("TWITCH_OAUTH_JSON")); os.IsNotExist(err) {
|
if _, err := os.Stat(os.Getenv("TWITCH_OAUTH_JSON")); os.IsNotExist(err) {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
oauthPath = os.Getenv("TWITCH_OAUTH_JSON")
|
oauthPath = os.Getenv("TWITCH_OAUTH_JSON")
|
||||||
} else {
|
} else {
|
||||||
if _, err := os.Stat(os.Getenv("HOME") + "/.twitch/oauth.json"); os.IsNotExist(err) {
|
if _, err := os.Stat(os.Getenv("HOME") + "/.twitch/oauth.json"); os.IsNotExist(err) {
|
||||||
rgb.YPrintf("[%s] Warning %s doesn't exist, trying %s next!\n", irc.TimeStamp(), os.Getenv("HOME")+"/.twitch/oauth.json", "/etc/twitch/oauth.json")
|
rgb.YPrintf("[%s] Warning %s doesn't exist, trying %s next!\n", irc.TimeStamp(), os.Getenv("HOME")+"/.twitch/oauth.json", "/etc/twitch/oauth.json")
|
||||||
if _, err := os.Stat("/etc/twitch/oauth.json"); os.IsNotExist(err) {
|
if _, err := os.Stat("/etc/twitch/oauth.json"); os.IsNotExist(err) {
|
||||||
rgb.YPrintf("[%s] Error %s doesn't exist either, bailing!\n", irc.TimeStamp(), "/etc/twitch/oauth.json")
|
rgb.YPrintf("[%s] Error %s doesn't exist either, bailing!\n", irc.TimeStamp(), "/etc/twitch/oauth.json")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
oauthPath = "/etc/twitch/oauth.json"
|
oauthPath = "/etc/twitch/oauth.json"
|
||||||
} else {
|
} else {
|
||||||
oauthPath = os.Getenv("HOME") + "/.twitch/oauth.json"
|
oauthPath = os.Getenv("HOME") + "/.twitch/oauth.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if _, err := os.Stat(config.OAuthPath); os.IsNotExist(err) {
|
if _, err := os.Stat(config.OAuthPath); os.IsNotExist(err) {
|
||||||
rgb.YPrintf("[%s] Error config-specified oauth file %s doesn't exist, bailing!\n", irc.TimeStamp(), config.OAuthPath)
|
rgb.YPrintf("[%s] Error config-specified oauth file %s doesn't exist, bailing!\n", irc.TimeStamp(), config.OAuthPath)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
oauthPath = config.OAuthPath
|
oauthPath = config.OAuthPath
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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.
|
||||||
myBot := irc.KardBot{
|
myBot := irc.KardBot{
|
||||||
Channel: "imartynontwitch",
|
Channel: "imartynontwitch",
|
||||||
MsgRate: time.Duration(20/30) * time.Millisecond,
|
MsgRate: time.Duration(20/30) * time.Millisecond,
|
||||||
Name: "Karaokards",
|
Name: "Karaokards",
|
||||||
Port: "6667",
|
Port: "6667",
|
||||||
PrivatePath: oauthPath,
|
PrivatePath: oauthPath,
|
||||||
Server: "irc.chat.twitch.tv",
|
Server: "irc.chat.twitch.tv",
|
||||||
Prompts: selectablePrompts,
|
Prompts: selectablePrompts,
|
||||||
Database: *persistentData,
|
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")
|
||||||
webserver.HandleHTTP(&myBot)
|
webserver.HandleHTTP(&myBot)
|
||||||
}()
|
}()
|
||||||
myBot.Start()
|
myBot.Start()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue