Gisle Aune
6 years ago
3 changed files with 205 additions and 0 deletions
@ -0,0 +1,78 @@ |
|||||
|
package main |
||||
|
|
||||
|
import ( |
||||
|
"flag" |
||||
|
"log" |
||||
|
"os" |
||||
|
"time" |
||||
|
|
||||
|
"git.aiterp.net/rpdata/api/internal/store" |
||||
|
"git.aiterp.net/rpdata/api/models/posts" |
||||
|
|
||||
|
"git.aiterp.net/rpdata/api/models/logs" |
||||
|
) |
||||
|
|
||||
|
var dateStr string |
||||
|
var tzStr string |
||||
|
var channel string |
||||
|
var fileName string |
||||
|
|
||||
|
func main() { |
||||
|
var inputPosts []Post |
||||
|
|
||||
|
flag.StringVar(&dateStr, "date", "2013-04-04", "Date in YYYY-MM-DD form.") |
||||
|
flag.StringVar(&tzStr, "tz", "Europe/Oslo", "Time zone for the date.") |
||||
|
flag.StringVar(&channel, "channel", "#RedrockAgency", "Channel it's set in.") |
||||
|
flag.StringVar(&fileName, "file", "-", "File to read from (- = stdin).") |
||||
|
flag.Parse() |
||||
|
|
||||
|
tz, err := time.LoadLocation(tzStr) |
||||
|
if err != nil { |
||||
|
log.Fatalln("Parse timezone:", err) |
||||
|
} |
||||
|
|
||||
|
date, err := time.ParseInLocation("2006-01-02", dateStr, tz) |
||||
|
if err != nil { |
||||
|
log.Fatalln("Parse date:", err) |
||||
|
} |
||||
|
|
||||
|
err = store.Init() |
||||
|
if err != nil { |
||||
|
log.Fatalln("Init store:", err) |
||||
|
} |
||||
|
|
||||
|
if fileName != "-" { |
||||
|
file, fileErr := os.Open(fileName) |
||||
|
if fileErr != nil { |
||||
|
log.Fatalln("Open file:", fileErr) |
||||
|
} |
||||
|
|
||||
|
inputPosts, err = parsePosts(file, date) |
||||
|
file.Close() |
||||
|
} else { |
||||
|
inputPosts, err = parsePosts(os.Stdin, date) |
||||
|
} |
||||
|
if err != nil { |
||||
|
log.Fatalln("Read inputPosts:", err) |
||||
|
} |
||||
|
|
||||
|
if len(inputPosts) == 0 { |
||||
|
log.Fatalln("Input file contained no posts.") |
||||
|
} |
||||
|
|
||||
|
l, err := logs.Add(inputPosts[0].Time, channel, "", "", "", false) |
||||
|
if err != nil { |
||||
|
log.Fatalln("Add log:", err) |
||||
|
} |
||||
|
|
||||
|
log.Println("Added log", l.ID) |
||||
|
|
||||
|
for _, inputPost := range inputPosts { |
||||
|
p, err := posts.Add(l, inputPost.Time, inputPost.Kind, inputPost.Nick, inputPost.Text) |
||||
|
if err != nil { |
||||
|
log.Fatalln("Add post:", err) |
||||
|
} |
||||
|
|
||||
|
log.Println("Added post", p.ID) |
||||
|
} |
||||
|
} |
@ -0,0 +1,126 @@ |
|||||
|
package main |
||||
|
|
||||
|
import ( |
||||
|
"bufio" |
||||
|
"errors" |
||||
|
"io" |
||||
|
"strconv" |
||||
|
"strings" |
||||
|
"time" |
||||
|
) |
||||
|
|
||||
|
// ErrNotPost is returned by parsePost if the line is empty or not a post.
|
||||
|
var ErrNotPost = errors.New("not a post") |
||||
|
|
||||
|
// ErrTooShort is returned by parsePost if the line too short to be a post.
|
||||
|
var ErrTooShort = errors.New("post too short") |
||||
|
|
||||
|
// ErrInvalidTimestamp is returned by parsePost if the line is empty or not a post.
|
||||
|
var ErrInvalidTimestamp = errors.New("invalid timestamp") |
||||
|
|
||||
|
// A Post is a part of a log.
|
||||
|
type Post struct { |
||||
|
Time time.Time |
||||
|
Kind string |
||||
|
Nick string |
||||
|
Text string |
||||
|
} |
||||
|
|
||||
|
func parsePosts(reader io.Reader, date time.Time) ([]Post, error) { |
||||
|
prev := Post{} |
||||
|
posts := make([]Post, 0, 8) |
||||
|
bufReader := bufio.NewReader(reader) |
||||
|
|
||||
|
for { |
||||
|
line, err := bufReader.ReadString('\n') |
||||
|
if err != nil && err != io.EOF { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
if len(line) > 8 { |
||||
|
post, err := parsePost(strings.Trim(line, " \n\r"), date, prev) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
posts = append(posts, post) |
||||
|
prev = post |
||||
|
} |
||||
|
|
||||
|
if err == io.EOF { |
||||
|
break |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return posts, nil |
||||
|
} |
||||
|
|
||||
|
func parsePost(line string, date time.Time, prev Post) (Post, error) { |
||||
|
// Do basic validation
|
||||
|
line = strings.Trim(line, " \t\n\r") |
||||
|
if len(line) == 0 || !strings.HasPrefix(line, "[") { |
||||
|
return Post{}, ErrNotPost |
||||
|
} |
||||
|
|
||||
|
// Parse timestamp
|
||||
|
tsEndIndex := strings.IndexByte(line, ']') |
||||
|
if tsEndIndex == -1 || len(line) < tsEndIndex+5 { |
||||
|
return Post{}, ErrNotPost |
||||
|
} |
||||
|
tsStr := line[1:tsEndIndex] |
||||
|
tsSplit := strings.Split(tsStr, ":") |
||||
|
tsUnits := make([]int, len(tsSplit)) |
||||
|
if len(tsSplit) < 2 { |
||||
|
return Post{}, ErrNotPost |
||||
|
} |
||||
|
for i := range tsSplit { |
||||
|
n, err := strconv.Atoi(tsSplit[i]) |
||||
|
if err != nil { |
||||
|
return Post{}, ErrNotPost |
||||
|
} |
||||
|
|
||||
|
tsUnits[i] = n |
||||
|
} |
||||
|
if len(tsUnits) == 2 { |
||||
|
tsUnits = append(tsUnits, 0) |
||||
|
} |
||||
|
|
||||
|
ts := time.Date(date.Year(), date.Month(), date.Day(), tsUnits[0], tsUnits[1], tsUnits[2], 0, date.Location()) |
||||
|
if !prev.Time.IsZero() && prev.Time.After(ts) { |
||||
|
ts = ts.AddDate(0, 0, 1) |
||||
|
} |
||||
|
|
||||
|
if line[tsEndIndex+2] == '*' { |
||||
|
split := strings.SplitN(line[tsEndIndex+4:], " ", 2) |
||||
|
|
||||
|
post := Post{ |
||||
|
Time: ts, |
||||
|
Kind: "action", |
||||
|
Nick: strings.TrimLeft(split[0], "+@!~"), |
||||
|
Text: split[1], |
||||
|
} |
||||
|
|
||||
|
if post.Nick[0] == '=' { |
||||
|
post.Kind = "scene" |
||||
|
} |
||||
|
|
||||
|
return post, nil |
||||
|
} else if line[tsEndIndex+2] == '<' { |
||||
|
split := strings.SplitN(line[tsEndIndex+2:], " ", 2) |
||||
|
|
||||
|
post := Post{ |
||||
|
Time: ts, |
||||
|
Kind: "text", |
||||
|
Nick: strings.TrimLeft(split[0][1:len(split[0])-1], "+@!~"), |
||||
|
Text: split[1], |
||||
|
} |
||||
|
|
||||
|
if post.Nick[0] == '=' { |
||||
|
post.Kind = "scene" |
||||
|
} |
||||
|
|
||||
|
return post, nil |
||||
|
} else { |
||||
|
return Post{}, ErrNotPost |
||||
|
} |
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue