use raw logs and parse at read time

This commit is contained in:
gempir 2018-12-02 14:53:01 +01:00
parent 9bc350834f
commit bd73490f45
11 changed files with 437 additions and 223 deletions

View file

@ -7,5 +7,11 @@ email: !vault |
35666538386231343533333231646131383734663834663039636235313237636366343535376366
3438353430666537630a323338663565366439353163646435633738653563393330613064363163
66356363623063373532626262343361663933623132653162633034396131623038
admin: gempir
channels: "gempir,pajlada"
username: gempbot
oauth: !vault |
$ANSIBLE_VAULT;1.1;AES256
34306435393365666330346435616335386537653565393433656436383131343063356666623930
6137316537306234353665333065636636663363383038640a653735356262613362666561383033
33343936646165393262353239633335653563373438353765636463373439653235643535333633
6163656464313337350a323039656238346538666132646530386261643336613238356264363132
36623964383333333965343132626662623832626663656631366563613139373832

View file

@ -1,5 +1,22 @@
{
"admin": "{{ admin }}",
"admin": "gempir",
"logsDirectory": "/var/justlog",
"channels": ["gempir", "pajlada"]
"username": "{{ username }}",
"oauth": "{{ oauth }}",
"channels": [
"gempir",
"pajlada",
"gempbot",
"forsen",
"jaxerie",
"nymn",
"nani",
"trizze",
"chancu",
"tearon",
"thiccanimegrili",
"razq",
"krakenbul",
"infinitegachi"
]
}

View file

@ -8,24 +8,15 @@ import (
"math/rand"
"net/http"
"os"
"regexp"
"strconv"
"strings"
"time"
"github.com/gempir/go-twitch-irc"
"github.com/labstack/echo"
"github.com/labstack/gommon/log"
)
var (
logReg = regexp.MustCompile(`\[(.*)\]\s(.*?):\s(.*)`)
)
// ErrorJSON simple json for default error response
type ErrorJSON struct {
Error string `json:"Error"`
}
// RandomQuoteJSON simple json to output rq
type RandomQuoteJSON struct {
Channel string `json:"channel"`
Username string `json:"username"`
@ -33,27 +24,18 @@ type RandomQuoteJSON struct {
Timestamp string `json:"timestamp"`
}
// AllChannelsJSON api response
type AllChannelsJSON struct {
Channels []string `json:"channels"`
}
// LogMessage for json
type LogMessage struct {
Username string `json:"username"`
Message string `json:"message"`
Timestamp string `json:"timestamp"`
}
func (s *Server) getCurrentUserLogs(c echo.Context) error {
channel := strings.ToLower(c.Param("channel"))
channel = strings.TrimSpace(channel)
year := time.Now().Year()
month := time.Now().Month().String()
username := c.Param("username")
username = strings.ToLower(strings.TrimSpace(username))
channelID := c.Param("channelid")
userID := c.Param("userid")
redirectURL := fmt.Sprintf("/channel/%s/user/%s/%d/%s", channel, username, year, month)
year := time.Now().Year()
month := int(time.Now().Month())
redirectURL := fmt.Sprintf("/channelid/%s/userid/%s/%d/%d", channelID, userID, year, month)
return c.Redirect(303, redirectURL)
}
@ -65,145 +47,16 @@ func (s *Server) getAllChannels(c echo.Context) error {
}
func (s *Server) getCurrentChannelLogs(c echo.Context) error {
channel := strings.ToLower(c.Param("channel"))
channel = strings.TrimSpace(channel)
channelID := c.Param("channelid")
year := time.Now().Year()
month := time.Now().Month().String()
month := int(time.Now().Month())
day := time.Now().Day()
redirectURL := fmt.Sprintf("/channel/%s/%d/%s/%d", channel, year, month, day)
redirectURL := fmt.Sprintf("/channelid/%s/%d/%d/%d", channelID, year, month, day)
return c.Redirect(http.StatusSeeOther, redirectURL)
}
func (s *Server) getDatedChannelLogs(c echo.Context) error {
channel := strings.ToLower(c.Param("channel"))
channel = strings.TrimSpace(channel)
year := c.Param("year")
month := strings.Title(c.Param("month"))
day := c.Param("day")
if year == "" || month == "" {
year = strconv.Itoa(time.Now().Year())
month = time.Now().Month().String()
}
content := ""
file := fmt.Sprintf(s.logPath+"/%s/%s/%s/%s/channel.txt", channel, year, month, day)
if _, err := os.Stat(file + ".gz"); err == nil {
file = file + ".gz"
f, err := os.Open(file)
if err != nil {
errJSON := new(ErrorJSON)
errJSON.Error = "error finding logs"
return c.JSON(http.StatusNotFound, errJSON)
}
gz, err := gzip.NewReader(f)
scanner := bufio.NewScanner(gz)
if err != nil {
errJSON := new(ErrorJSON)
errJSON.Error = "error finding logs"
return c.JSON(http.StatusNotFound, errJSON)
}
for scanner.Scan() {
line := scanner.Text()
content += line + "\r\n"
}
return c.String(http.StatusOK, content)
}
return c.File(file)
}
func (s *Server) getDatedUserLogs(c echo.Context) error {
channel := strings.ToLower(c.Param("channel"))
channel = strings.TrimSpace(channel)
year := c.Param("year")
month := strings.Title(c.Param("month"))
username := c.Param("username")
username = strings.ToLower(strings.TrimSpace(username))
if year == "" || month == "" {
year = strconv.Itoa(time.Now().Year())
month = time.Now().Month().String()
}
content := ""
file := fmt.Sprintf(s.logPath+"/%s/%s/%s/%s.txt", channel, year, month, username)
if _, err := os.Stat(file + ".gz"); err == nil {
file = file + ".gz"
f, err := os.Open(file)
if err != nil {
errJSON := new(ErrorJSON)
errJSON.Error = "error finding logs"
return c.JSON(http.StatusNotFound, errJSON)
}
gz, err := gzip.NewReader(f)
scanner := bufio.NewScanner(gz)
if err != nil {
errJSON := new(ErrorJSON)
errJSON.Error = "error finding logs"
return c.JSON(http.StatusNotFound, errJSON)
}
if c.Request().Header.Get("Content-Type") == "application/json" {
messages := []LogMessage{}
for scanner.Scan() {
line := scanner.Text()
result := logReg.FindStringSubmatch(line)
message := LogMessage{Username: result[2], Message: result[3], Timestamp: result[1]}
messages = append(messages, message)
}
return c.JSON(http.StatusOK, messages)
}
for scanner.Scan() {
line := scanner.Text()
content += line + "\r\n"
}
return c.String(http.StatusOK, content)
}
if c.Request().Header.Get("Content-Type") == "application/json" {
openFile, err := os.Open(file)
if err != nil {
errJSON := new(ErrorJSON)
errJSON.Error = "error finding logs"
return c.JSON(http.StatusNotFound, errJSON)
}
defer openFile.Close()
scanner := bufio.NewScanner(openFile)
messages := []LogMessage{}
for scanner.Scan() {
line := scanner.Text()
result := logReg.FindStringSubmatch(line)
message := LogMessage{Username: result[2], Message: result[3], Timestamp: result[1]}
messages = append(messages, message)
}
return c.JSON(http.StatusOK, messages)
}
return c.File(file)
}
func (s *Server) getRandomQuote(c echo.Context) error {
errJSON := new(ErrorJSON)
errJSON.Error = "error finding logs"
username := c.Param("username")
username = strings.ToLower(strings.TrimSpace(username))
channel := strings.ToLower(c.Param("channel"))
@ -227,9 +80,7 @@ func (s *Server) getRandomQuote(c echo.Context) error {
}
}
if len(userLogs) < 1 {
errJSON := new(ErrorJSON)
errJSON.Error = "error finding logs"
return c.JSON(http.StatusNotFound, errJSON)
return c.JSON(http.StatusNotFound, "error finding logs")
}
for _, logFile := range userLogs {
@ -267,3 +118,102 @@ func (s *Server) getRandomQuote(c echo.Context) error {
return c.String(http.StatusOK, lineSplit[1])
}
func (s *Server) getUserLogs(c echo.Context) error {
channelID := c.Param("channelid")
userID := c.Param("userid")
yearStr := c.Param("year")
monthStr := c.Param("month")
year, err := strconv.Atoi(yearStr)
if err != nil {
log.Error(err)
return c.JSON(http.StatusInternalServerError, "Invalid year")
}
month, err := strconv.Atoi(monthStr)
if err != nil {
log.Error(err)
return c.JSON(http.StatusInternalServerError, "Invalid month")
}
logMessages, err := s.fileLogger.ReadLogForUser(channelID, userID, year, month)
if err != nil {
log.Error(err)
return c.JSON(http.StatusInternalServerError, "Failure reading log")
}
var logResult chatLog
for _, rawMessage := range logMessages {
channel, user, parsedMessage := twitch.ParseMessage(rawMessage)
message := chatMessage{
Timestamp: timestamp{parsedMessage.Time},
Username: user.Username,
Text: parsedMessage.Text,
Type: parsedMessage.Type,
Channel: channel,
}
logResult.Messages = append(logResult.Messages, message)
}
if c.Request().Header.Get("Content-Type") == "application/json" || c.QueryParam("type") == "json" {
return writeJSONResponse(c, &logResult)
}
return writeTextResponse(c, &logResult)
}
func (s *Server) getChannelLogs(c echo.Context) error {
channelID := c.Param("channelid")
yearStr := c.Param("year")
monthStr := c.Param("month")
dayStr := c.Param("day")
year, err := strconv.Atoi(yearStr)
if err != nil {
log.Error(err)
return c.JSON(http.StatusInternalServerError, "Invalid year")
}
month, err := strconv.Atoi(monthStr)
if err != nil {
log.Error(err)
return c.JSON(http.StatusInternalServerError, "Invalid month")
}
day, err := strconv.Atoi(dayStr)
if err != nil {
log.Error(err)
return c.JSON(http.StatusInternalServerError, "Invalid day")
}
logMessages, err := s.fileLogger.ReadLogForChannel(channelID, year, month, day)
if err != nil {
log.Error(err)
return c.JSON(http.StatusInternalServerError, "Failure reading log")
}
var logResult chatLog
for _, rawMessage := range logMessages {
channel, user, parsedMessage := twitch.ParseMessage(rawMessage)
message := chatMessage{
Timestamp: timestamp{parsedMessage.Time},
Username: user.Username,
Text: parsedMessage.Text,
Type: parsedMessage.Type,
Channel: channel,
}
logResult.Messages = append(logResult.Messages, message)
}
if c.Request().Header.Get("Content-Type") == "application/json" || c.QueryParam("type") == "json" {
return writeJSONResponse(c, &logResult)
}
return writeTextResponse(c, &logResult)
}

146
api/main.go Normal file
View file

@ -0,0 +1,146 @@
package api
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"strconv"
"strings"
"time"
twitch "github.com/gempir/go-twitch-irc"
jsoniter "github.com/json-iterator/go"
"github.com/labstack/echo"
)
type order string
var (
orderDesc order = "DESC"
orderAsc order = "ASC"
)
type chatLog struct {
Messages []chatMessage `json:"messages"`
}
type chatMessage struct {
Text string `json:"text"`
Username string `json:"username"`
Channel string `json:"channel"`
Timestamp timestamp `json:"timestamp"`
Type twitch.MessageType `json:"type"`
}
type timestamp struct {
time.Time
}
func (t timestamp) MarshalJSON() ([]byte, error) {
return []byte("\"" + t.UTC().Format(time.RFC3339) + "\""), nil
}
func (t *timestamp) UnmarshalJSON(data []byte) error {
goTime, err := time.Parse(time.RFC3339, strings.TrimSuffix(strings.TrimPrefix(string(data[:]), "\""), "\""))
if err != nil {
return err
}
*t = timestamp{
goTime,
}
return nil
}
func parseFromTo(from, to string, limit float64) (time.Time, time.Time, error) {
var fromTime time.Time
var toTime time.Time
if from == "" && to == "" {
fromTime = time.Now().AddDate(0, -1, 0)
toTime = time.Now()
} else if from == "" && to != "" {
var err error
toTime, err = parseTimestamp(to)
if err != nil {
return fromTime, toTime, fmt.Errorf("Can't parse to timestamp: %s", err)
}
fromTime = toTime.AddDate(0, -1, 0)
} else if from != "" && to == "" {
var err error
fromTime, err = parseTimestamp(from)
if err != nil {
return fromTime, toTime, fmt.Errorf("Can't parse from timestamp: %s", err)
}
toTime = fromTime.AddDate(0, 1, 0)
} else {
var err error
fromTime, err = parseTimestamp(from)
if err != nil {
return fromTime, toTime, fmt.Errorf("Can't parse from timestamp: %s", err)
}
toTime, err = parseTimestamp(to)
if err != nil {
return fromTime, toTime, fmt.Errorf("Can't parse to timestamp: %s", err)
}
if toTime.Sub(fromTime).Hours() > limit {
return fromTime, toTime, errors.New("Timespan too big")
}
}
return fromTime, toTime, nil
}
func writeTextResponse(c echo.Context, cLog *chatLog) error {
c.Response().WriteHeader(http.StatusOK)
for _, cMessage := range cLog.Messages {
switch cMessage.Type {
case twitch.PRIVMSG:
c.Response().Write([]byte(fmt.Sprintf("[%s] #%s %s: %s\r\n", cMessage.Timestamp.Format("2006-01-2 15:04:05"), cMessage.Channel, cMessage.Username, cMessage.Text)))
case twitch.CLEARCHAT:
c.Response().Write([]byte(fmt.Sprintf("[%s] #%s %s\r\n", cMessage.Timestamp.Format("2006-01-2 15:04:05"), cMessage.Channel, cMessage.Text)))
}
}
return nil
}
func writeJSONResponse(c echo.Context, logResult *chatLog) error {
_, stream := c.QueryParams()["stream"]
if stream {
c.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationJSONCharsetUTF8)
c.Response().WriteHeader(http.StatusOK)
return json.NewEncoder(c.Response()).Encode(logResult)
}
json := jsoniter.ConfigCompatibleWithStandardLibrary
data, err := json.Marshal(logResult)
if err != nil {
return c.JSON(http.StatusInternalServerError, err.Error())
}
return c.Blob(http.StatusOK, echo.MIMEApplicationJSONCharsetUTF8, data)
}
func parseTimestamp(timestamp string) (time.Time, error) {
i, err := strconv.ParseInt(timestamp, 10, 64)
if err != nil {
return time.Now(), err
}
return time.Unix(i, 0), nil
}
func buildOrder(c echo.Context) order {
dataOrder := orderAsc
_, reverse := c.QueryParams()["reverse"]
if reverse {
dataOrder = orderDesc
}
return dataOrder
}

View file

@ -1,34 +1,33 @@
package api
import (
"fmt"
"net/http"
"github.com/gempir/justlog/filelog"
"github.com/labstack/echo"
"github.com/labstack/echo/middleware"
)
// Server api server
type Server struct {
port string
logPath string
channels []string
listenAddress string
logPath string
fileLogger *filelog.Logger
channels []string
}
// NewServer create Server
func NewServer(logPath string) Server {
func NewServer(logPath string, listenAddress string, fileLogger *filelog.Logger) Server {
return Server{
logPath: logPath,
channels: []string{},
listenAddress: listenAddress,
logPath: logPath,
fileLogger: fileLogger,
channels: []string{},
}
}
// AddChannel to in-memory store to serve joined channels
func (s *Server) AddChannel(channel string) {
s.channels = append(s.channels, channel)
}
// Init api server
func (s *Server) Init() {
e := echo.New()
@ -44,13 +43,12 @@ func (s *Server) Init() {
e.GET("/", func(c echo.Context) error {
return c.String(http.StatusOK, "Hello, World!")
})
e.GET("/channel/:channel/user/:username", s.getCurrentUserLogs)
e.GET("/channel", s.getAllChannels)
e.GET("/channel/:channel", s.getCurrentChannelLogs)
e.GET("/channel/:channel/:year/:month/:day", s.getDatedChannelLogs)
e.GET("/channel/:channel/user/:username/:year/:month", s.getDatedUserLogs)
e.GET("/channel/:channel/user/:username/random", s.getRandomQuote)
e.GET("/channelid/:channelid/user/:userid", s.getCurrentUserLogs)
e.GET("/channelid", s.getAllChannels)
e.GET("/channelid/:channelid", s.getCurrentChannelLogs)
e.GET("/channelid/:channelid/:year/:month/:day", s.getChannelLogs)
e.GET("/channelid/:channelid/userid/:userid/:year/:month", s.getUserLogs)
e.GET("/channelid/:channelid/userid/:userid/random", s.getRandomQuote)
fmt.Println("starting API on port :8025")
e.Logger.Fatal(e.Start(":8025"))
e.Logger.Fatal(e.Start(s.listenAddress))
}

View file

@ -1,5 +1,7 @@
{
"admin": "gempir",
"logsDirectory": "./logs",
"username": "gempbot",
"oauth": "cg74a9xb4le868tctmelk7eidqtrra",
"channels": ["gempir", "pajlada"]
}

View file

@ -1,33 +1,76 @@
package filelog
import (
"bufio"
"compress/gzip"
"errors"
"fmt"
"io"
"os"
"strings"
"github.com/gempir/go-twitch-irc"
)
// LogMessageForChannel file log
func (l *Logger) LogMessageForChannel(channel string, user twitch.User, message twitch.Message) error {
year := message.Time.Year()
month := message.Time.Month()
month := int(message.Time.Month())
day := message.Time.Day()
err := os.MkdirAll(fmt.Sprintf(l.logPath+"/%s/%d/%s/%d", channel, year, month, day), 0755)
err := os.MkdirAll(fmt.Sprintf(l.logPath+"/%s/%d/%d/%d", message.Tags["room-id"], year, month, day), 0740)
if err != nil {
return err
}
filename := fmt.Sprintf(l.logPath+"/%s/%d/%s/%d/channel.txt", channel, year, month, day)
filename := fmt.Sprintf(l.logPath+"/%s/%d/%d/%d/channel.txt", message.Tags["room-id"], year, month, day)
file, err := os.OpenFile(filename, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0755)
file, err := os.OpenFile(filename, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0640)
if err != nil {
return err
}
defer file.Close()
contents := fmt.Sprintf("[%s] %s: %s\r\n", message.Time.Format("2006-01-2 15:04:05"), user.Username, message.Text)
if _, err = file.WriteString(contents); err != nil {
if _, err = file.WriteString(message.Raw + "\r\n"); err != nil {
return err
}
return nil
}
func (l *Logger) ReadLogForChannel(channelID string, year int, month int, day int) ([]string, error) {
filename := fmt.Sprintf(l.logPath+"/%s/%d/%d/%d/channel.txt", channelID, year, month, day)
if _, err := os.Stat(filename); err != nil {
filename = filename + ".gz"
}
f, err := os.Open(filename)
if err != nil {
return []string{}, errors.New("file not found")
}
defer f.Close()
var reader io.Reader
if strings.HasSuffix(filename, ".gz") {
gz, err := gzip.NewReader(f)
if err != nil {
return []string{}, errors.New("file gzip not readable")
}
reader = gz
} else {
reader = f
}
scanner := bufio.NewScanner(reader)
if err != nil {
return []string{}, errors.New("file not readable")
}
content := []string{}
for scanner.Scan() {
line := scanner.Text()
content = append(content, line)
}
return content, nil
}

View file

@ -1,43 +1,84 @@
package filelog
import (
"bufio"
"compress/gzip"
"errors"
"fmt"
"io"
"os"
"strings"
"github.com/gempir/go-twitch-irc"
)
// Logger logger struct
type Logger struct {
logPath string
}
// NewFileLogger create file logger
func NewFileLogger(logPath string) Logger {
return Logger{
logPath: logPath,
}
}
// LogMessageForUser log in file
func (l *Logger) LogMessageForUser(channel string, user twitch.User, message twitch.Message) error {
year := message.Time.Year()
month := message.Time.Month()
err := os.MkdirAll(fmt.Sprintf(l.logPath+"/%s/%d/%s/", channel, year, month), 0755)
month := int(message.Time.Month())
err := os.MkdirAll(fmt.Sprintf(l.logPath+"/%s/%d/%d/", message.Tags["room-id"], year, month), 0740)
if err != nil {
return err
}
filename := fmt.Sprintf(l.logPath+"/%s/%d/%s/%s.txt", channel, year, month, user.Username)
filename := fmt.Sprintf(l.logPath+"/%s/%d/%d/%s.txt", message.Tags["room-id"], year, month, user.UserID)
file, err := os.OpenFile(filename, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0755)
file, err := os.OpenFile(filename, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0640)
if err != nil {
return err
}
defer file.Close()
contents := fmt.Sprintf("[%s] %s: %s\r\n", message.Time.Format("2006-01-2 15:04:05"), user.Username, message.Text)
if _, err = file.WriteString(contents); err != nil {
if _, err = file.WriteString(message.Raw + "\r\n"); err != nil {
return err
}
return nil
}
func (l *Logger) ReadLogForUser(channelID string, userID string, year int, month int) ([]string, error) {
filename := fmt.Sprintf(l.logPath+"/%s/%d/%d/%s.txt", channelID, year, month, userID)
if _, err := os.Stat(filename); err != nil {
filename = filename + ".gz"
}
f, err := os.Open(filename)
if err != nil {
return []string{}, errors.New("file not found")
}
defer f.Close()
var reader io.Reader
if strings.HasSuffix(filename, ".gz") {
gz, err := gzip.NewReader(f)
if err != nil {
return []string{}, errors.New("file gzip not readable")
}
reader = gz
} else {
reader = f
}
scanner := bufio.NewScanner(reader)
if err != nil {
return []string{}, errors.New("file not readable")
}
content := []string{}
for scanner.Scan() {
line := scanner.Text()
content = append(content, line)
}
return content, nil
}

1
helix/user.go Normal file
View file

@ -0,0 +1 @@
package helix

View file

@ -16,60 +16,48 @@ func formatDiff(years, months, days, hours, mins, secs int) string {
switch years {
case 1:
since += fmt.Sprintf("%d year ", years)
break
default:
since += fmt.Sprintf("%d years ", years)
break
}
}
if months > 0 {
switch months {
case 1:
since += fmt.Sprintf("%d month ", months)
break
default:
since += fmt.Sprintf("%d months ", months)
break
}
}
if days > 0 {
switch days {
case 1:
since += fmt.Sprintf("%d day ", days)
break
default:
since += fmt.Sprintf("%d days ", days)
break
}
}
if hours > 0 {
switch hours {
case 1:
since += fmt.Sprintf("%d hour ", hours)
break
default:
since += fmt.Sprintf("%d hours ", hours)
break
}
}
if mins > 0 && days == 0 && months == 0 && years == 0 {
switch mins {
case 1:
since += fmt.Sprintf("%d min ", mins)
break
default:
since += fmt.Sprintf("%d mins ", mins)
break
}
}
if secs > 0 && days == 0 && months == 0 && years == 0 && hours == 0 {
switch secs {
case 1:
since += fmt.Sprintf("%d sec ", secs)
break
default:
since += fmt.Sprintf("%d secs ", secs)
break
}
}
return strings.TrimSpace(since)

44
main.go
View file

@ -3,7 +3,6 @@ package main
import (
"encoding/json"
"flag"
"fmt"
"os"
"time"
@ -19,6 +18,9 @@ import (
type config struct {
LogsDirectory string `json:"logsDirectory"`
Username string `json:"username"`
OAuth string `json:"oauth"`
ListenAddress string `json:"listenAddress"`
Admin string `json:"admin"`
Channels []string `json:"channels"`
}
@ -34,14 +36,20 @@ func main() {
flag.Parse()
cfg = loadConfiguration(*configFile)
apiServer := api.NewServer(cfg.LogsDirectory)
go apiServer.Init()
twitchClient := twitch.NewClient("justinfan123123", "oauth:123123123")
twitchClient := twitch.NewClient(cfg.Username, "oauth:"+cfg.OAuth)
fileLogger := filelog.NewFileLogger(cfg.LogsDirectory)
if strings.HasPrefix(cfg.Username, "justinfan") {
log.Info("Bot joining anonymous")
} else {
log.Info("Bot joining as user " + cfg.Username)
}
apiServer := api.NewServer(cfg.LogsDirectory, cfg.ListenAddress, &fileLogger)
go apiServer.Init()
for _, channel := range cfg.Channels {
fmt.Println("Joining " + channel)
log.Info("Joining " + channel)
twitchClient.Join(channel)
apiServer.AddChannel(channel)
}
@ -85,22 +93,36 @@ func main() {
}()
})
twitchClient.Connect()
log.Fatal(twitchClient.Connect())
}
func loadConfiguration(file string) config {
log.Info("Loading config from " + file)
var cfg config
// setup defaults
cfg := config{
LogsDirectory: "./logs",
ListenAddress: "127.0.0.1:8025",
Username: "justinfan777777",
OAuth: "oauth:777777777",
Channels: []string{},
Admin: "gempir",
}
configFile, err := os.Open(file)
if err != nil {
log.Fatal(err)
}
defer configFile.Close()
jsonParser := json.NewDecoder(configFile)
err = jsonParser.Decode(&cfg)
if err != nil {
log.Fatal(err)
}
jsonParser := json.NewDecoder(configFile)
jsonParser.Decode(&cfg)
cfg.LogsDirectory = strings.TrimSuffix(cfg.LogsDirectory, "/")
cfg.OAuth = strings.TrimPrefix(cfg.OAuth, "oauth:")
return cfg
}