Implement CHATHISTORY BETWEEN
This commit is contained in:
parent
bede274f32
commit
b078ccaf7a
@ -1801,11 +1801,22 @@ func (dc *downstreamConn) handleMessageRegistered(msg *irc.Message) error {
|
||||
if err := parseMessageParams(msg, &subcommand); err != nil {
|
||||
return err
|
||||
}
|
||||
var target, criteria, limitStr string
|
||||
if err := parseMessageParams(msg, nil, &target, &criteria, &limitStr); err != nil {
|
||||
var target, limitStr string
|
||||
var boundsStr [2]string
|
||||
switch subcommand {
|
||||
case "AFTER", "BEFORE":
|
||||
if err := parseMessageParams(msg, nil, &target, &boundsStr[0], &limitStr); err != nil {
|
||||
return err
|
||||
}
|
||||
case "BETWEEN":
|
||||
if err := parseMessageParams(msg, nil, &target, &boundsStr[0], &boundsStr[1], &limitStr); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
// TODO: support LATEST, AROUND
|
||||
return ircError{&irc.Message{
|
||||
Command: "FAIL",
|
||||
Params: []string{"CHATHISTORY", "NEED_MORE_PARAMS", subcommand, "Missing parameters"},
|
||||
Params: []string{"CHATHISTORY", "INVALID_PARAMS", subcommand, "Unknown command"},
|
||||
}}
|
||||
}
|
||||
|
||||
@ -1824,21 +1835,24 @@ func (dc *downstreamConn) handleMessageRegistered(msg *irc.Message) error {
|
||||
entity = uc.network.casemap(entity)
|
||||
|
||||
// TODO: support msgid criteria
|
||||
criteriaParts := strings.SplitN(criteria, "=", 2)
|
||||
if len(criteriaParts) != 2 || criteriaParts[0] != "timestamp" {
|
||||
var bounds [2]time.Time
|
||||
bounds[0] = parseChatHistoryBound(boundsStr[0])
|
||||
if bounds[0].IsZero() {
|
||||
return ircError{&irc.Message{
|
||||
Command: "FAIL",
|
||||
Params: []string{"CHATHISTORY", "INVALID_PARAMS", subcommand, criteria, "Unknown criteria"},
|
||||
Params: []string{"CHATHISTORY", "INVALID_PARAMS", subcommand, boundsStr[0], "Invalid first bound"},
|
||||
}}
|
||||
}
|
||||
|
||||
timestamp, err := time.Parse(serverTimeLayout, criteriaParts[1])
|
||||
if err != nil {
|
||||
if boundsStr[1] != "" {
|
||||
bounds[1] = parseChatHistoryBound(boundsStr[1])
|
||||
if bounds[1].IsZero() {
|
||||
return ircError{&irc.Message{
|
||||
Command: "FAIL",
|
||||
Params: []string{"CHATHISTORY", "INVALID_PARAMS", subcommand, criteria, "Invalid criteria"},
|
||||
Params: []string{"CHATHISTORY", "INVALID_PARAMS", subcommand, boundsStr[1], "Invalid second bound"},
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
limit, err := strconv.Atoi(limitStr)
|
||||
if err != nil || limit < 0 || limit > dc.srv.HistoryLimit {
|
||||
@ -1851,15 +1865,15 @@ func (dc *downstreamConn) handleMessageRegistered(msg *irc.Message) error {
|
||||
var history []*irc.Message
|
||||
switch subcommand {
|
||||
case "BEFORE":
|
||||
history, err = store.LoadBeforeTime(uc.network, entity, timestamp, limit)
|
||||
history, err = store.LoadBeforeTime(uc.network, entity, bounds[0], time.Time{}, limit)
|
||||
case "AFTER":
|
||||
history, err = store.LoadAfterTime(uc.network, entity, timestamp, limit)
|
||||
default:
|
||||
// TODO: support LATEST, BETWEEN
|
||||
return ircError{&irc.Message{
|
||||
Command: "FAIL",
|
||||
Params: []string{"CHATHISTORY", "UNKNOWN_COMMAND", subcommand, "Unknown command"},
|
||||
}}
|
||||
history, err = store.LoadAfterTime(uc.network, entity, bounds[0], time.Now(), limit)
|
||||
case "BETWEEN":
|
||||
if bounds[0].Before(bounds[1]) {
|
||||
history, err = store.LoadAfterTime(uc.network, entity, bounds[0], bounds[1], limit)
|
||||
} else {
|
||||
history, err = store.LoadBeforeTime(uc.network, entity, bounds[0], bounds[1], limit)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
dc.logger.Printf("failed fetching %q messages for chathistory: %v", target, err)
|
||||
|
20
irc.go
20
irc.go
@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
@ -637,3 +638,22 @@ func isHighlight(text, nick string) bool {
|
||||
text = text[i+len(nick):]
|
||||
}
|
||||
}
|
||||
|
||||
// parseChatHistoryBound parses the given CHATHISTORY parameter as a bound.
|
||||
// The zero time is returned on error.
|
||||
func parseChatHistoryBound(param string) time.Time {
|
||||
parts := strings.SplitN(param, "=", 2)
|
||||
if len(parts) != 2 {
|
||||
return time.Time{}
|
||||
}
|
||||
switch parts[0] {
|
||||
case "timestamp":
|
||||
timestamp, err := time.Parse(serverTimeLayout, parts[1])
|
||||
if err != nil {
|
||||
return time.Time{}
|
||||
}
|
||||
return timestamp
|
||||
default:
|
||||
return time.Time{}
|
||||
}
|
||||
}
|
||||
|
10
msgstore.go
10
msgstore.go
@ -26,8 +26,14 @@ type messageStore interface {
|
||||
type chatHistoryMessageStore interface {
|
||||
messageStore
|
||||
|
||||
LoadBeforeTime(network *network, entity string, t time.Time, limit int) ([]*irc.Message, error)
|
||||
LoadAfterTime(network *network, entity string, t time.Time, limit int) ([]*irc.Message, error)
|
||||
// LoadBeforeTime loads up to limit messages before start down to end. The
|
||||
// returned messages must be between and excluding the provided bounds.
|
||||
// end is before start.
|
||||
LoadBeforeTime(network *network, entity string, start, end time.Time, limit int) ([]*irc.Message, error)
|
||||
// LoadBeforeTime loads up to limit messages after start up to end. The
|
||||
// returned messages must be between and excluding the provided bounds.
|
||||
// end is after start.
|
||||
LoadAfterTime(network *network, entity string, start, end time.Time, limit int) ([]*irc.Message, error)
|
||||
}
|
||||
|
||||
type msgIDType uint
|
||||
|
@ -257,7 +257,7 @@ func parseMessage(line, entity string, ref time.Time) (*irc.Message, time.Time,
|
||||
return msg, t, nil
|
||||
}
|
||||
|
||||
func (ms *fsMessageStore) parseMessagesBefore(network *network, entity string, ref time.Time, limit int, afterOffset int64) ([]*irc.Message, error) {
|
||||
func (ms *fsMessageStore) parseMessagesBefore(network *network, entity string, ref time.Time, end time.Time, limit int, afterOffset int64) ([]*irc.Message, error) {
|
||||
path := ms.logPath(network, entity, ref)
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
@ -284,7 +284,7 @@ func (ms *fsMessageStore) parseMessagesBefore(network *network, entity string, r
|
||||
msg, t, err := parseMessage(sc.Text(), entity, ref)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if msg == nil {
|
||||
} else if msg == nil || !t.After(end) {
|
||||
continue
|
||||
} else if !t.Before(ref) {
|
||||
break
|
||||
@ -313,7 +313,7 @@ func (ms *fsMessageStore) parseMessagesBefore(network *network, entity string, r
|
||||
}
|
||||
}
|
||||
|
||||
func (ms *fsMessageStore) parseMessagesAfter(network *network, entity string, ref time.Time, limit int) ([]*irc.Message, error) {
|
||||
func (ms *fsMessageStore) parseMessagesAfter(network *network, entity string, ref time.Time, end time.Time, limit int) ([]*irc.Message, error) {
|
||||
path := ms.logPath(network, entity, ref)
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
@ -332,6 +332,8 @@ func (ms *fsMessageStore) parseMessagesAfter(network *network, entity string, re
|
||||
return nil, err
|
||||
} else if msg == nil || !t.After(ref) {
|
||||
continue
|
||||
} else if !t.Before(end) {
|
||||
break
|
||||
}
|
||||
|
||||
history = append(history, msg)
|
||||
@ -343,12 +345,12 @@ func (ms *fsMessageStore) parseMessagesAfter(network *network, entity string, re
|
||||
return history, nil
|
||||
}
|
||||
|
||||
func (ms *fsMessageStore) LoadBeforeTime(network *network, entity string, t time.Time, limit int) ([]*irc.Message, error) {
|
||||
func (ms *fsMessageStore) LoadBeforeTime(network *network, entity string, start time.Time, end time.Time, limit int) ([]*irc.Message, error) {
|
||||
history := make([]*irc.Message, limit)
|
||||
remaining := limit
|
||||
tries := 0
|
||||
for remaining > 0 && tries < fsMessageStoreMaxTries {
|
||||
buf, err := ms.parseMessagesBefore(network, entity, t, remaining, -1)
|
||||
for remaining > 0 && tries < fsMessageStoreMaxTries && end.Before(start) {
|
||||
buf, err := ms.parseMessagesBefore(network, entity, start, end, remaining, -1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -359,20 +361,19 @@ func (ms *fsMessageStore) LoadBeforeTime(network *network, entity string, t time
|
||||
}
|
||||
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)
|
||||
year, month, day := start.Date()
|
||||
start = time.Date(year, month, day, 0, 0, 0, 0, start.Location()).Add(-1)
|
||||
}
|
||||
|
||||
return history[remaining:], nil
|
||||
}
|
||||
|
||||
func (ms *fsMessageStore) LoadAfterTime(network *network, entity string, t time.Time, limit int) ([]*irc.Message, error) {
|
||||
func (ms *fsMessageStore) LoadAfterTime(network *network, entity string, start time.Time, end time.Time, limit int) ([]*irc.Message, error) {
|
||||
var history []*irc.Message
|
||||
remaining := limit
|
||||
tries := 0
|
||||
now := time.Now()
|
||||
for remaining > 0 && tries < fsMessageStoreMaxTries && t.Before(now) {
|
||||
buf, err := ms.parseMessagesAfter(network, entity, t, remaining)
|
||||
for remaining > 0 && tries < fsMessageStoreMaxTries && start.Before(end) {
|
||||
buf, err := ms.parseMessagesAfter(network, entity, start, end, remaining)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -383,8 +384,8 @@ func (ms *fsMessageStore) LoadAfterTime(network *network, entity string, t time.
|
||||
}
|
||||
history = append(history, buf...)
|
||||
remaining -= len(buf)
|
||||
year, month, day := t.Date()
|
||||
t = time.Date(year, month, day+1, 0, 0, 0, 0, t.Location())
|
||||
year, month, day := start.Date()
|
||||
start = time.Date(year, month, day+1, 0, 0, 0, 0, start.Location())
|
||||
}
|
||||
return history, nil
|
||||
}
|
||||
@ -420,7 +421,7 @@ func (ms *fsMessageStore) LoadLatestID(network *network, entity, id string, limi
|
||||
offset = afterOffset
|
||||
}
|
||||
|
||||
buf, err := ms.parseMessagesBefore(network, entity, t, remaining, offset)
|
||||
buf, err := ms.parseMessagesBefore(network, entity, t, time.Time{}, remaining, offset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user