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