2020-02-22 13:08:01 +00:00
package main
import (
"encoding/json"
"io/ioutil"
"math/rand"
"os"
"path/filepath"
"time"
builtins "git.martyn.berlin/martyn/karaokards/internal/builtins"
irc "git.martyn.berlin/martyn/karaokards/internal/irc"
webserver "git.martyn.berlin/martyn/karaokards/internal/webserver"
rgb "github.com/foresthoffman/rgblog"
scribble "github.com/nanobox-io/golang-scribble"
)
type customStringsStruct struct {
Strings [ ] string ` json:"strings,omitempty" `
}
var selectablePrompts [ ] string
var customStrings customStringsStruct
2020-03-07 19:42:16 +00:00
var config irc . ConfigStruct
2020-02-22 13:08:01 +00:00
func readConfig ( ) {
var data [ ] byte
var err error
configFile := ""
if os . Getenv ( "KARAOKARDS_CONFIGFILE" ) != "" {
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" ) )
os . Exit ( 1 )
}
configFile = os . Getenv ( "KARAOKARDS_CONFIGFILE" )
} else {
ex , err := os . Executable ( )
if err != nil {
rgb . YPrintf ( "[%s] Warning, KARAOKARDS_CONFIGFILE env var unset and cannot find executable!\n" , irc . TimeStamp ( ) )
}
exPath := filepath . Dir ( ex )
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 ( ) )
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" )
os . Exit ( 1 )
} else {
configFile = "/etc/karaokards/config.json"
}
} else {
configFile = exPath + "/config.json"
}
}
data , err = ioutil . ReadFile ( configFile )
if err != nil {
rgb . RPrintf ( "[%s] Could not read `%s`. File reading error: %s\n" , irc . TimeStamp ( ) , configFile , err )
os . Exit ( 1 )
}
2020-03-07 19:42:16 +00:00
err = json . Unmarshal ( data , & config )
2020-02-22 13:08:01 +00:00
if err != nil {
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 )
2020-03-07 19:42:16 +00:00
rgb . YPrintf ( "[%s] config %v\n" , irc . TimeStamp ( ) , config )
2020-02-22 13:08:01 +00:00
return
}
//openDatabase "database" in this sense being a scribble db
func openDatabase ( ) * scribble . Driver {
dataPath := ""
if config . DataPath == "" {
if os . Getenv ( "KARAOKARDS_DATA_FOLDER" ) != "" {
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" ) )
os . Exit ( 1 )
}
dataPath = os . Getenv ( "KARAOKARDS_DATA_FOLDER" )
} else {
ex , err := os . Executable ( )
if err != nil {
rgb . RPrintf ( "[%s] Error, KARAOKARDS_DATA_FOLDER env var unset and cannot find executable!\n" , irc . TimeStamp ( ) )
os . Exit ( 1 )
}
exPath := filepath . Dir ( ex )
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" )
err = os . Mkdir ( exPath + "/data" , 0770 )
if err != nil {
rgb . RPrintf ( "[%s] Error cannot create %s: %s!\n" , irc . TimeStamp ( ) , exPath + "/data" , err )
os . Exit ( 1 )
}
}
dataPath = exPath + "/data"
}
} else {
2020-03-07 19:42:16 +00:00
if _ , err := os . Stat ( config . DataPath ) ; os . IsNotExist ( err ) {
rgb . RPrintf ( "[%s] Error, config-specified path '%s' doesn't exist!\n" , irc . TimeStamp ( ) , config . DataPath )
2020-02-22 13:08:01 +00:00
os . Exit ( 1 )
}
dataPath = config . DataPath
}
db , err := scribble . New ( dataPath , nil )
if err != nil {
rgb . RPrintf ( "[%s] Error opening database in '%s' : %s\n" , irc . TimeStamp ( ) , dataPath , err )
os . Exit ( 1 )
}
return db
}
func readBonusStrings ( ) [ ] string {
var data [ ] byte
var err error
if config . StringPath == "" {
ex , err := os . Executable ( )
if err != nil {
rgb . YPrintf ( "[%s] Could not read `strings.json`, will only have builtin prompts. File reading error: %s\n" , irc . TimeStamp ( ) , err )
return [ ] string { }
}
exPath := filepath . Dir ( ex )
data , err = ioutil . ReadFile ( exPath + "/strings.json" )
if err != nil {
rgb . YPrintf ( "[%s] Could not read `strings.json`, will only have builtin prompts. File reading error: %s\n" , irc . TimeStamp ( ) , err )
return [ ] string { }
}
} else {
data , err = ioutil . ReadFile ( config . StringPath )
if err != nil {
rgb . YPrintf ( "[%s] Could not read `strings.json`, will only have builtin prompts. File reading error: %s\n" , irc . TimeStamp ( ) , err )
return [ ] string { }
}
}
err = json . Unmarshal ( data , & customStrings )
if err != nil {
rgb . YPrintf ( "[%s] Could not unmarshal `strings.json`, will only have builtin prompts. Unmarshal error: %s\n" , irc . TimeStamp ( ) , err )
return [ ] string { }
}
rgb . YPrintf ( "[%s] Read %d prompts from `strings.json`\n" , irc . TimeStamp ( ) , len ( customStrings . Strings ) )
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 {
selectablePrompts = append ( selectablePrompts , val )
}
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 ) )
2020-03-07 19:42:16 +00:00
ircOauthPath := ""
if config . IrcOAuthPath == "" {
if os . Getenv ( "TWITCH_OAUTH_JSON" ) != "" {
if _ , err := os . Stat ( os . Getenv ( "TWITCH_OAUTH_JSON" ) ) ; os . IsNotExist ( err ) {
os . Exit ( 1 )
}
ircOauthPath = os . Getenv ( "TWITCH_OAUTH_JSON" )
} else {
if _ , err := os . Stat ( os . Getenv ( "HOME" ) + "/.twitch/ircoauth.json" ) ; os . IsNotExist ( err ) {
rgb . YPrintf ( "[%s] Warning %s doesn't exist, trying %s next!\n" , irc . TimeStamp ( ) , os . Getenv ( "HOME" ) + "/.twitch/ircoauth.json" , "/etc/twitch/ircoauth.json" )
if _ , err := os . Stat ( "/etc/twitch/ircoauth.json" ) ; os . IsNotExist ( err ) {
rgb . YPrintf ( "[%s] Error %s doesn't exist either, bailing!\n" , irc . TimeStamp ( ) , "/etc/twitch/ircoauth.json" )
os . Exit ( 1 )
}
ircOauthPath = "/etc/twitch/ircoauth.json"
} else {
ircOauthPath = os . Getenv ( "HOME" ) + "/.twitch/ircoauth.json"
}
}
} else {
if _ , err := os . Stat ( config . IrcOAuthPath ) ; os . IsNotExist ( err ) {
rgb . YPrintf ( "[%s] Error config-specified oauth file %s doesn't exist, bailing!\n" , irc . TimeStamp ( ) , config . IrcOAuthPath )
os . Exit ( 1 )
}
ircOauthPath = config . IrcOAuthPath
}
appOauthPath := ""
if config . AppOAuthPath == "" {
2020-02-22 13:08:01 +00:00
if os . Getenv ( "TWITCH_OAUTH_JSON" ) != "" {
if _ , err := os . Stat ( os . Getenv ( "TWITCH_OAUTH_JSON" ) ) ; os . IsNotExist ( err ) {
os . Exit ( 1 )
}
2020-03-07 19:42:16 +00:00
appOauthPath = os . Getenv ( "TWITCH_OAUTH_JSON" )
2020-02-22 13:08:01 +00:00
} else {
2020-03-07 19:42:16 +00:00
if _ , err := os . Stat ( os . Getenv ( "HOME" ) + "/.twitch/appoauth.json" ) ; os . IsNotExist ( err ) {
rgb . YPrintf ( "[%s] Warning %s doesn't exist, trying %s next!\n" , irc . TimeStamp ( ) , os . Getenv ( "HOME" ) + "/.twitch/appoauth.json" , "/etc/twitch/appoauth.json" )
if _ , err := os . Stat ( "/etc/twitch/appoauth.json" ) ; os . IsNotExist ( err ) {
rgb . YPrintf ( "[%s] Error %s doesn't exist either, bailing!\n" , irc . TimeStamp ( ) , "/etc/twitch/appoauth.json" )
2020-02-22 13:08:01 +00:00
os . Exit ( 1 )
}
2020-03-07 19:42:16 +00:00
appOauthPath = "/etc/twitch/appoauth.json"
2020-02-22 13:08:01 +00:00
} else {
2020-03-07 19:42:16 +00:00
appOauthPath = os . Getenv ( "HOME" ) + "/.twitch/appoauth.json"
2020-02-22 13:08:01 +00:00
}
}
} else {
2020-03-07 19:42:16 +00:00
if _ , err := os . Stat ( config . AppOAuthPath ) ; os . IsNotExist ( err ) {
rgb . YPrintf ( "[%s] Error config-specified oauth file %s doesn't exist, bailing!\n" , irc . TimeStamp ( ) , config . AppOAuthPath )
2020-02-22 13:08:01 +00:00
os . Exit ( 1 )
}
2020-03-07 19:42:16 +00:00
appOauthPath = config . AppOAuthPath
2020-02-22 13:08:01 +00:00
}
// Replace the channel name, bot name, and the path to the private directory with your respective
// values.
myBot := irc . KardBot {
2020-03-07 19:42:16 +00:00
Channel : "karaokards" ,
2020-02-22 13:08:01 +00:00
MsgRate : time . Duration ( 20 / 30 ) * time . Millisecond ,
Name : "Karaokards" ,
Port : "6667" ,
2020-03-07 19:42:16 +00:00
IrcPrivatePath : ircOauthPath ,
AppPrivatePath : appOauthPath ,
2020-02-22 13:08:01 +00:00
Server : "irc.chat.twitch.tv" ,
Prompts : selectablePrompts ,
Database : * persistentData ,
2020-03-07 19:42:16 +00:00
Config : config ,
2020-02-22 13:08:01 +00:00
}
go func ( ) {
rgb . YPrintf ( "[%s] Starting webserver on port %s\n" , irc . TimeStamp ( ) , "5353" )
webserver . HandleHTTP ( & myBot )
} ( )
myBot . Start ( )
}