2020-02-12 15:50:45 +00:00
package main
import (
2020-02-13 15:39:57 +00:00
"encoding/json"
"io/ioutil"
2020-02-12 15:50:45 +00:00
"math/rand"
"os"
2020-02-13 15:39:57 +00:00
"path/filepath"
2020-02-13 15:18:44 +00:00
"time"
2020-02-12 15:50:45 +00:00
2020-02-13 15:39:57 +00:00
builtins "git.martyn.berlin/martyn/karaokards/internal/builtins"
2020-02-13 15:18:44 +00:00
irc "git.martyn.berlin/martyn/karaokards/internal/irc"
2020-02-14 10:42:38 +00:00
webserver "git.martyn.berlin/martyn/karaokards/internal/webserver"
2020-02-13 15:39:57 +00:00
rgb "github.com/foresthoffman/rgblog"
2020-02-15 11:56:17 +00:00
scribble "github.com/nanobox-io/golang-scribble"
2020-02-12 15:50:45 +00:00
)
2020-02-15 11:56:17 +00:00
type configStruct struct {
InitialChannels [ ] string ` json:"channels" `
OAuthPath string ` json:"oauthpath,omitempty" `
StringPath string ` json:"authpath,omitempty" `
DataPath string ` json:"datapath,omitempty" `
}
2020-02-13 15:39:57 +00:00
type customStringsStruct struct {
Strings [ ] string ` json:"strings,omitempty" `
}
var selectablePrompts [ ] string
var customStrings customStringsStruct
2020-02-15 11:56:17 +00:00
var config configStruct
2020-02-13 15:39:57 +00:00
2020-02-15 11:56:17 +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 )
2020-02-13 15:39:57 +00:00
if err != nil {
2020-02-15 11:56:17 +00:00
rgb . RPrintf ( "[%s] Could not read `%s`. File reading error: %s\n" , irc . TimeStamp ( ) , configFile , err )
os . Exit ( 1 )
2020-02-13 15:39:57 +00:00
}
2020-02-15 11:56:17 +00:00
err = json . Unmarshal ( data , & customStrings )
2020-02-13 15:39:57 +00:00
if err != nil {
2020-02-15 11:56:17 +00:00
rgb . RPrintf ( "[%s] Could not unmarshal `%s`. Unmarshal error: %s\n" , irc . TimeStamp ( ) , configFile , err )
os . Exit ( 1 )
}
2020-02-21 19:55:19 +00:00
rgb . YPrintf ( "[%s] Read config file from `%s`\n" , irc . TimeStamp ( ) , configFile )
2020-02-15 11:56:17 +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 {
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" ) )
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 { }
}
2020-02-13 15:39:57 +00:00
}
err = json . Unmarshal ( data , & customStrings )
if err != nil {
2020-02-15 11:56:17 +00:00
rgb . YPrintf ( "[%s] Could not unmarshal `strings.json`, will only have builtin prompts. Unmarshal error: %s\n" , irc . TimeStamp ( ) , err )
2020-02-13 15:39:57 +00:00
return [ ] string { }
}
2020-02-15 11:56:17 +00:00
rgb . YPrintf ( "[%s] Read %d prompts from `strings.json`\n" , irc . TimeStamp ( ) , len ( customStrings . Strings ) )
2020-02-13 15:39:57 +00:00
return customStrings . Strings
}
2020-02-21 19:55:19 +00:00
var buildDate string
2020-02-12 15:50:45 +00:00
func main ( ) {
2020-02-21 19:55:19 +00:00
rgb . YPrintf ( "[%s] starting karaokard bot build %s\n" , irc . TimeStamp ( ) , buildDate )
2020-02-15 11:56:17 +00:00
readConfig ( )
2020-02-12 15:50:45 +00:00
rand . Seed ( time . Now ( ) . UnixNano ( ) )
2020-02-13 15:39:57 +00:00
for _ , val := range builtins . Karaokards {
2020-02-13 15:18:44 +00:00
selectablePrompts = append ( selectablePrompts , val )
2020-02-12 15:50:45 +00:00
}
for _ , val := range readBonusStrings ( ) {
2020-02-13 15:18:44 +00:00
selectablePrompts = append ( selectablePrompts , val )
2020-02-12 15:50:45 +00:00
}
2020-02-21 19:55:19 +00:00
persistentData := openDatabase ( )
var dbGlobalPrompts [ ] string
if err := persistentData . Read ( "prompts" , "global" , & dbGlobalPrompts ) ; err != nil {
persistentData . Write ( "prompts" , "common" , dbGlobalPrompts )
}
selectablePrompts := append ( selectablePrompts , dbGlobalPrompts ... )
2020-02-15 11:56:17 +00:00
rgb . YPrintf ( "[%s] %d prompts available.\n" , irc . TimeStamp ( ) , len ( selectablePrompts ) )
2020-02-13 15:18:44 +00:00
oauthPath := ""
2020-02-15 11:56:17 +00:00
if config . OAuthPath == "" {
if os . Getenv ( "TWITCH_OAUTH_JSON" ) != "" {
if _ , err := os . Stat ( os . Getenv ( "TWITCH_OAUTH_JSON" ) ) ; os . IsNotExist ( err ) {
2020-02-13 15:18:44 +00:00
os . Exit ( 1 )
2020-02-12 15:50:45 +00:00
}
2020-02-15 11:56:17 +00:00
oauthPath = os . Getenv ( "TWITCH_OAUTH_JSON" )
2020-02-12 15:50:45 +00:00
} else {
2020-02-15 11:56:17 +00:00
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" )
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" )
os . Exit ( 1 )
}
oauthPath = "/etc/twitch/oauth.json"
} else {
oauthPath = os . Getenv ( "HOME" ) + "/.twitch/oauth.json"
}
2020-02-12 15:50:45 +00:00
}
2020-02-15 11:56:17 +00:00
} else {
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 )
os . Exit ( 1 )
}
oauthPath = config . OAuthPath
2020-02-12 15:50:45 +00:00
}
2020-02-15 11:56:17 +00:00
2020-02-12 15:50:45 +00:00
// Replace the channel name, bot name, and the path to the private directory with your respective
// values.
2020-02-13 15:18:44 +00:00
myBot := irc . KardBot {
2020-02-12 15:50:45 +00:00
Channel : "imartynontwitch" ,
MsgRate : time . Duration ( 20 / 30 ) * time . Millisecond ,
Name : "Karaokards" ,
Port : "6667" ,
PrivatePath : oauthPath ,
Server : "irc.chat.twitch.tv" ,
2020-02-13 15:39:57 +00:00
Prompts : selectablePrompts ,
2020-02-21 19:55:19 +00:00
Database : * persistentData ,
2020-02-12 15:50:45 +00:00
}
2020-02-14 10:42:38 +00:00
go func ( ) {
rgb . YPrintf ( "[%s] Starting webserver on port %s\n" , irc . TimeStamp ( ) , "5353" )
2020-02-14 14:56:10 +00:00
webserver . HandleHTTP ( myBot )
2020-02-14 10:42:38 +00:00
} ( )
2020-02-12 15:50:45 +00:00
myBot . Start ( )
2020-02-13 15:18:44 +00:00
}