From b6f15c3e3cfe26f00c1269c1efc52b52d3617e81 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 19 Aug 2020 13:17:32 +0200 Subject: [PATCH] Introduce loadHistoryLatestID This loads latest messages from logs up to a given message ID. This is similar to the IRCv3 CHATHISTORY LATEST command [1]. [1]: https://github.com/ircv3/ircv3-specifications/blob/0c271a5f1df4f93b5ae4c7597422821c40a8cfeb/extensions/chathistory.md#latest --- logger.go | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/logger.go b/logger.go index 47b3e8a..86381a3 100644 --- a/logger.go +++ b/logger.go @@ -233,7 +233,7 @@ func parseMessage(line, entity string, ref time.Time) (*irc.Message, time.Time, return msg, t, nil } -func parseMessagesBefore(network *network, entity string, ref time.Time, limit int) ([]*irc.Message, error) { +func parseMessagesBefore(network *network, entity string, ref time.Time, limit int, afterOffset int64) ([]*irc.Message, error) { path := logPath(network, entity, ref) f, err := os.Open(path) if err != nil { @@ -248,6 +248,14 @@ func parseMessagesBefore(network *network, entity string, ref time.Time, limit i cur := 0 sc := bufio.NewScanner(f) + + if afterOffset >= 0 { + if _, err := f.Seek(afterOffset, io.SeekStart); err != nil { + return nil, nil + } + sc.Scan() // skip till next newline + } + for sc.Scan() { msg, t, err := parseMessage(sc.Text(), entity, ref) if err != nil { @@ -316,7 +324,7 @@ func loadHistoryBeforeTime(network *network, entity string, t time.Time, limit i remaining := limit tries := 0 for remaining > 0 && tries < messageLoggerMaxTries { - buf, err := parseMessagesBefore(network, entity, t, remaining) + buf, err := parseMessagesBefore(network, entity, t, remaining, -1) if err != nil { return nil, err } @@ -356,3 +364,51 @@ func loadHistoryAfterTime(network *network, entity string, t time.Time, limit in } return history, nil } + +func truncateDay(t time.Time) time.Time { + year, month, day := t.Date() + return time.Date(year, month, day, 0, 0, 0, 0, t.Location()) +} + +func loadHistoryLatestID(network *network, entity, id string, limit int) ([]*irc.Message, error) { + var afterTime time.Time + var afterOffset int64 + if id != "" { + var idNet, idEntity string + var err error + idNet, idEntity, afterTime, afterOffset, err = parseMsgID(id) + if err != nil { + return nil, err + } + if idNet != network.GetName() || idEntity != entity { + return nil, fmt.Errorf("cannot find message ID: message ID doesn't match network/entity") + } + } + + history := make([]*irc.Message, limit) + t := time.Now() + remaining := limit + tries := 0 + for remaining > 0 && tries < messageLoggerMaxTries && !truncateDay(t).Before(afterTime) { + var offset int64 = -1 + if afterOffset >= 0 && truncateDay(t).Equal(afterTime) { + offset = afterOffset + } + + buf, err := parseMessagesBefore(network, entity, t, remaining, offset) + if err != nil { + return nil, err + } + if len(buf) == 0 { + tries++ + } else { + tries = 0 + } + copy(history[remaining-len(buf):], buf) + remaining -= len(buf) + year, month, day := t.Date() + t = time.Date(year, month, day, 0, 0, 0, 0, t.Location()).Add(-1) + } + + return history[remaining:], nil +}