Files
api/services/parsers/irccloud.go
T
2020-08-20 20:58:16 +02:00

162 lines
3.4 KiB
Go
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package parsers
import (
"errors"
"fmt"
"git.aiterp.net/rpdata/api/models"
"strings"
"time"
)
var ErrSkip = errors.New("parsers: skip this post")
func IRCCloudLogs(data string, location *time.Location, threshold time.Duration) ([]ParsedLog, error) {
lines := strings.Split(data, "\n")
pos := 0
results := make([]ParsedLog, 0, 8)
for pos < len(lines) {
log, n, err := IRCCloudLog(lines[pos:], location, threshold)
if err != nil {
if err == ErrEmptyLog {
pos += n
continue
}
return nil, err
}
pos += n
results = append(results, *log)
}
return results, nil
}
// IRCCloudLog parses the log and returns the things that can be gleamed from them.
func IRCCloudLog(lines []string, location *time.Location, threshold time.Duration) (*ParsedLog, int, error) {
posts := make([]*models.Post, 0, len(lines))
prev := (*models.Post)(nil)
amount := 0
for _, line := range lines {
line = strings.Trim(line, "\r\t  ")
if len(line) < 1 {
amount += 1
continue
}
post, err := IRCCloudPost(line, location)
if err == ErrSkip {
amount += 1
continue
} else if err != nil {
return nil, -1, err
}
if prev != nil {
if post.Time.Sub(prev.Time) >= threshold {
break
}
post.Position = prev.Position + 1
} else {
post.Position = 1
}
posts = append(posts, &post)
prev = &post
amount += 1
}
if len(posts) == 0 {
return nil, amount, ErrEmptyLog
}
log := models.Log{
Date: posts[0].Time,
}
return &ParsedLog{log, posts}, amount, nil
}
// IRCCloudPost parses a post from a mirc-like line. If the previous post is included (it can be empty), it will be used
// to determine whether midnight has passed.
func IRCCloudPost(line string, tz *time.Location) (models.Post, error) {
// Do basic validation
line = strings.Trim(line, "  \t\n\r")
if len(line) == 0 || !strings.HasPrefix(line, "[") {
return models.Post{}, &ParseError{
Line: line,
Problem: "no timestamp",
}
}
// Parse timestamp
tsEndIndex := strings.IndexByte(line, ']')
if tsEndIndex == -1 || len(line) < tsEndIndex+5 {
return models.Post{}, &ParseError{
Line: line,
Problem: "incomplete timestamp",
}
}
tsStr := line[1:tsEndIndex]
ts, err := time.ParseInLocation("2006-01-02 15:04:05", tsStr, tz)
if err != nil {
return models.Post{}, &ParseError{
Line: line,
Problem: fmt.Sprintf("Could not parse date: %s", err.Error()),
}
}
if strings.HasPrefix(line[tsEndIndex+2:], "—") {
split := strings.SplitN(line[tsEndIndex+6:], " ", 2)
if len(split) == 1 {
return models.Post{}, &ParseError{
Line: line,
Problem: "post is empty",
}
}
post := models.Post{
ID: "UNASSIGNED",
LogID: "UNASSIGNED",
Time: ts,
Kind: "action",
Nick: strings.Trim(strings.TrimLeft(split[0], "+@!~"), "\u001F"),
Text: split[1],
}
if strings.HasPrefix(post.Nick, "=") {
post.Kind = "scene"
}
return post, nil
} else if line[tsEndIndex+2] == '<' {
split := strings.SplitN(line[tsEndIndex+2:], " ", 2)
if len(split) == 1 {
return models.Post{}, &ParseError{
Line: line,
Problem: "post is empty",
}
}
post := models.Post{
ID: "UNASSIGNED",
LogID: "UNASSIGNED",
Time: ts,
Kind: "text",
Nick: strings.Trim(strings.TrimLeft(split[0][1:len(split[0])-1], "+@!~"), "\u001F"),
Text: split[1],
}
if strings.HasPrefix(post.Nick, "=") {
post.Kind = "scene"
}
return post, nil
} else {
return models.Post{}, ErrSkip
}
}