justlog/filelog/userlog.go

378 lines
9.9 KiB
Go
Raw Permalink Normal View History

2017-03-08 20:08:44 +01:00
package filelog
import (
2018-12-02 14:53:01 +01:00
"bufio"
"compress/gzip"
"errors"
2017-03-11 21:51:08 +01:00
"fmt"
2018-12-02 14:53:01 +01:00
"io"
"io/ioutil"
"math/rand"
"os"
"sort"
"strconv"
2018-12-02 14:53:01 +01:00
"strings"
2017-09-11 19:51:29 +02:00
2022-01-12 19:44:06 +01:00
"github.com/gempir/go-twitch-irc/v3"
2018-12-02 19:23:54 +01:00
log "github.com/sirupsen/logrus"
2017-03-08 20:08:44 +01:00
)
2022-10-20 20:06:39 +02:00
type Logger interface {
LogPrivateMessageForUser(user twitch.User, message twitch.PrivateMessage) error
LogClearchatMessageForUser(userID string, message twitch.ClearChatMessage) error
LogUserNoticeMessageForUser(userID string, message twitch.UserNoticeMessage) error
GetLastLogYearAndMonthForUser(channelID, userID string) (int, int, error)
GetAvailableLogsForUser(channelID, userID string) ([]UserLogFile, error)
ReadLogForUser(channelID, userID string, year string, month string) ([]string, error)
ReadRandomMessageForUser(channelID, userID string) (string, error)
LogPrivateMessageForChannel(message twitch.PrivateMessage) error
LogClearchatMessageForChannel(message twitch.ClearChatMessage) error
LogUserNoticeMessageForChannel(message twitch.UserNoticeMessage) error
ReadLogForChannel(channelID string, year int, month int, day int) ([]string, error)
ReadRandomMessageForChannel(channelID string) (string, error)
GetAvailableLogsForChannel(channelID string) ([]ChannelLogFile, error)
}
type FileLogger struct {
2017-03-08 20:08:44 +01:00
logPath string
}
2022-10-20 20:06:39 +02:00
func NewFileLogger(logPath string) FileLogger {
return FileLogger{
2018-11-29 22:20:57 +01:00
logPath: logPath,
2017-03-08 20:08:44 +01:00
}
}
2022-10-20 20:06:39 +02:00
func (l *FileLogger) LogPrivateMessageForUser(user twitch.User, message twitch.PrivateMessage) error {
year := message.Time.Year()
2018-12-02 14:53:01 +01:00
month := int(message.Time.Month())
2019-03-11 21:41:12 +01:00
err := os.MkdirAll(fmt.Sprintf(l.logPath+"/%s/%d/%d/", message.RoomID, year, month), 0750)
2017-03-08 20:08:44 +01:00
if err != nil {
return err
2017-03-08 20:08:44 +01:00
}
2019-03-11 21:41:12 +01:00
filename := fmt.Sprintf(l.logPath+"/%s/%d/%d/%s.txt", message.RoomID, year, month, user.ID)
file, err := os.OpenFile(filename, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0640)
if err != nil {
return err
}
defer file.Close()
if _, err = file.WriteString(message.Raw + "\n"); err != nil {
return err
}
return nil
}
2022-10-20 20:06:39 +02:00
func (l *FileLogger) LogClearchatMessageForUser(userID string, message twitch.ClearChatMessage) error {
year := message.Time.Year()
month := int(message.Time.Month())
err := os.MkdirAll(fmt.Sprintf(l.logPath+"/%s/%d/%d/", message.RoomID, year, month), 0750)
if err != nil {
return err
}
filename := fmt.Sprintf(l.logPath+"/%s/%d/%d/%s.txt", message.RoomID, year, month, userID)
file, err := os.OpenFile(filename, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0640)
if err != nil {
return err
}
defer file.Close()
if _, err = file.WriteString(message.Raw + "\n"); err != nil {
return err
}
return nil
}
2022-10-20 20:06:39 +02:00
func (l *FileLogger) LogUserNoticeMessageForUser(userID string, message twitch.UserNoticeMessage) error {
2019-03-11 21:41:12 +01:00
year := message.Time.Year()
month := int(message.Time.Month())
err := os.MkdirAll(fmt.Sprintf(l.logPath+"/%s/%d/%d/", message.RoomID, year, month), 0750)
if err != nil {
return err
}
filename := fmt.Sprintf(l.logPath+"/%s/%d/%d/%s.txt", message.RoomID, year, month, userID)
2017-03-08 20:08:44 +01:00
2018-12-02 14:53:01 +01:00
file, err := os.OpenFile(filename, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0640)
2017-03-08 20:08:44 +01:00
if err != nil {
return err
2017-03-08 20:08:44 +01:00
}
defer file.Close()
2018-12-05 22:41:31 +01:00
if _, err = file.WriteString(message.Raw + "\n"); err != nil {
return err
2017-03-08 20:08:44 +01:00
}
return nil
2017-03-11 11:06:50 +01:00
}
2018-12-02 14:53:01 +01:00
2020-05-31 14:58:32 +02:00
type UserLogFile struct {
path string
2020-05-31 14:58:32 +02:00
Year string `json:"year"`
Month string `json:"month"`
}
2022-10-20 20:06:39 +02:00
func (l *FileLogger) GetLastLogYearAndMonthForUser(channelID, userID string) (int, int, error) {
if channelID == "" || userID == "" {
return 0, 0, fmt.Errorf("Invalid channelID: %s or userID: %s", channelID, userID)
}
2020-05-31 14:58:32 +02:00
logFiles := []UserLogFile{}
years, _ := ioutil.ReadDir(l.logPath + "/" + channelID)
for _, yearDir := range years {
year := yearDir.Name()
months, _ := ioutil.ReadDir(l.logPath + "/" + channelID + "/" + year + "/")
for _, monthDir := range months {
month := monthDir.Name()
path := fmt.Sprintf("%s/%s/%s/%s/%s.txt", l.logPath, channelID, year, month, userID)
if _, err := os.Stat(path); err == nil {
2020-05-31 14:58:32 +02:00
logFile := UserLogFile{path, year, month}
logFiles = append(logFiles, logFile)
} else if _, err := os.Stat(path + ".gz"); err == nil {
2020-05-31 14:58:32 +02:00
logFile := UserLogFile{path + ".gz", year, month}
logFiles = append(logFiles, logFile)
}
}
}
sort.Slice(logFiles, func(i, j int) bool {
2020-05-31 14:58:32 +02:00
yearA, _ := strconv.Atoi(logFiles[i].Year)
yearB, _ := strconv.Atoi(logFiles[j].Year)
monthA, _ := strconv.Atoi(logFiles[i].Month)
monthB, _ := strconv.Atoi(logFiles[j].Month)
if yearA == yearB {
return monthA > monthB
}
2020-05-31 14:58:32 +02:00
return yearA > yearB
})
if len(logFiles) > 0 {
2020-05-31 14:58:32 +02:00
year, _ := strconv.Atoi(logFiles[0].Year)
month, _ := strconv.Atoi(logFiles[0].Month)
return year, month, nil
}
return 0, 0, errors.New("No logs file")
}
2022-10-20 20:06:39 +02:00
func (l *FileLogger) GetAvailableLogsForUser(channelID, userID string) ([]UserLogFile, error) {
2020-05-31 14:58:32 +02:00
if channelID == "" || userID == "" {
return []UserLogFile{}, fmt.Errorf("Invalid channelID: %s or userID: %s", channelID, userID)
}
logFiles := []UserLogFile{}
years, _ := ioutil.ReadDir(l.logPath + "/" + channelID)
for _, yearDir := range years {
year := yearDir.Name()
months, _ := ioutil.ReadDir(l.logPath + "/" + channelID + "/" + year + "/")
for _, monthDir := range months {
month := monthDir.Name()
path := fmt.Sprintf("%s/%s/%s/%s/%s.txt", l.logPath, channelID, year, month, userID)
if _, err := os.Stat(path); err == nil {
logFile := UserLogFile{path, year, month}
logFiles = append(logFiles, logFile)
} else if _, err := os.Stat(path + ".gz"); err == nil {
logFile := UserLogFile{path + ".gz", year, month}
logFiles = append(logFiles, logFile)
}
}
}
sort.Slice(logFiles, func(i, j int) bool {
yearA, _ := strconv.Atoi(logFiles[i].Year)
yearB, _ := strconv.Atoi(logFiles[j].Year)
monthA, _ := strconv.Atoi(logFiles[i].Month)
monthB, _ := strconv.Atoi(logFiles[j].Month)
if yearA == yearB {
return monthA > monthB
}
return yearA > yearB
})
if len(logFiles) > 0 {
return logFiles, nil
}
return logFiles, errors.New("No logs file")
}
type ChannelLogFile struct {
path string
Year string `json:"year"`
Month string `json:"month"`
Day string `json:"day"`
}
2022-10-20 20:06:39 +02:00
func (l *FileLogger) GetAvailableLogsForChannel(channelID string) ([]ChannelLogFile, error) {
if channelID == "" {
return []ChannelLogFile{}, fmt.Errorf("Invalid channelID: %s", channelID)
}
logFiles := []ChannelLogFile{}
years, _ := ioutil.ReadDir(l.logPath + "/" + channelID)
for _, yearDir := range years {
year := yearDir.Name()
months, _ := ioutil.ReadDir(l.logPath + "/" + channelID + "/" + year + "/")
for _, monthDir := range months {
month := monthDir.Name()
days, _ := ioutil.ReadDir(l.logPath + "/" + channelID + "/" + year + "/" + month + "/")
for _, dayDir := range days {
day := dayDir.Name()
path := fmt.Sprintf("%s/%s/%s/%s/%s/channel.txt", l.logPath, channelID, year, month, day)
if _, err := os.Stat(path); err == nil {
logFile := ChannelLogFile{path, year, month, day}
logFiles = append(logFiles, logFile)
} else if _, err := os.Stat(path + ".gz"); err == nil {
logFile := ChannelLogFile{path + ".gz", year, month, day}
logFiles = append(logFiles, logFile)
}
}
}
}
sort.Slice(logFiles, func(i, j int) bool {
yearA, _ := strconv.Atoi(logFiles[i].Year)
yearB, _ := strconv.Atoi(logFiles[j].Year)
monthA, _ := strconv.Atoi(logFiles[i].Month)
monthB, _ := strconv.Atoi(logFiles[j].Month)
dayA, _ := strconv.Atoi(logFiles[j].Day)
dayB, _ := strconv.Atoi(logFiles[j].Day)
if yearA == yearB {
if monthA == monthB {
return dayA > dayB
}
return monthA > monthB
}
if monthA == monthB {
return dayA > dayB
}
return yearA > yearB
})
if len(logFiles) > 0 {
return logFiles, nil
}
return logFiles, errors.New("No logs file")
}
2020-02-25 12:36:10 +01:00
// ReadLogForUser fetch logs
2022-10-20 20:06:39 +02:00
func (l *FileLogger) ReadLogForUser(channelID, userID string, year string, month string) ([]string, error) {
2018-12-08 15:41:40 +01:00
if channelID == "" || userID == "" {
return []string{}, fmt.Errorf("Invalid channelID: %s or userID: %s", channelID, userID)
}
2020-02-25 12:36:10 +01:00
filename := fmt.Sprintf(l.logPath+"/%s/%s/%s/%s.txt", channelID, year, month, userID)
2018-12-02 14:53:01 +01:00
if _, err := os.Stat(filename); err != nil {
filename = filename + ".gz"
}
2018-12-02 19:23:54 +01:00
log.Debug("Opening " + filename)
2018-12-02 14:53:01 +01:00
f, err := os.Open(filename)
if err != nil {
2018-12-02 19:23:54 +01:00
return []string{}, errors.New("file not found: " + filename)
2018-12-02 14:53:01 +01:00
}
defer f.Close()
var reader io.Reader
if strings.HasSuffix(filename, ".gz") {
gz, err := gzip.NewReader(f)
if err != nil {
log.Error(err)
2018-12-02 14:53:01 +01:00
return []string{}, errors.New("file gzip not readable")
}
reader = gz
} else {
reader = f
}
scanner := bufio.NewScanner(reader)
if err != nil {
log.Error(err)
2018-12-02 14:53:01 +01:00
return []string{}, errors.New("file not readable")
}
content := []string{}
for scanner.Scan() {
line := scanner.Text()
content = append(content, line)
}
return content, nil
}
2022-10-20 20:06:39 +02:00
func (l *FileLogger) ReadRandomMessageForUser(channelID, userID string) (string, error) {
var userLogs []string
var lines []string
if channelID == "" || userID == "" {
return "", errors.New("missing channelID or userID")
}
years, _ := ioutil.ReadDir(l.logPath + "/" + channelID)
for _, yearDir := range years {
year := yearDir.Name()
months, _ := ioutil.ReadDir(l.logPath + "/" + channelID + "/" + year + "/")
for _, monthDir := range months {
month := monthDir.Name()
path := fmt.Sprintf("%s/%s/%s/%s/%s.txt", l.logPath, channelID, year, month, userID)
if _, err := os.Stat(path); err == nil {
userLogs = append(userLogs, path)
} else if _, err := os.Stat(path + ".gz"); err == nil {
userLogs = append(userLogs, path+".gz")
}
}
}
if len(userLogs) < 1 {
return "", errors.New("no log found")
}
for _, logFile := range userLogs {
f, _ := os.Open(logFile)
scanner := bufio.NewScanner(f)
if strings.HasSuffix(logFile, ".gz") {
gz, _ := gzip.NewReader(f)
scanner = bufio.NewScanner(gz)
}
for scanner.Scan() {
line := scanner.Text()
lines = append(lines, line)
}
f.Close()
}
ranNum := rand.Intn(len(lines))
return lines[ranNum], nil
}