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