use raw logs and parse at read time
This commit is contained in:
parent
9bc350834f
commit
bd73490f45
11 changed files with 437 additions and 223 deletions
|
@ -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
|
|
@ -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"
|
||||
]
|
||||
}
|
272
api/logs.go
272
api/logs.go
|
@ -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
146
api/main.go
Normal 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
|
||||
}
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
{
|
||||
"admin": "gempir",
|
||||
"logsDirectory": "./logs",
|
||||
"username": "gempbot",
|
||||
"oauth": "cg74a9xb4le868tctmelk7eidqtrra",
|
||||
"channels": ["gempir", "pajlada"]
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
1
helix/user.go
Normal file
|
@ -0,0 +1 @@
|
|||
package helix
|
|
@ -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
44
main.go
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue