2020-04-03 16:59:17 +00:00
|
|
|
package soju
|
|
|
|
|
|
|
|
import (
|
2021-03-31 09:59:13 +00:00
|
|
|
"bytes"
|
2021-11-03 17:18:04 +00:00
|
|
|
"context"
|
2021-03-31 09:59:13 +00:00
|
|
|
"encoding/base64"
|
2021-01-04 15:26:30 +00:00
|
|
|
"fmt"
|
2020-04-03 16:59:17 +00:00
|
|
|
"time"
|
|
|
|
|
2021-03-31 09:59:13 +00:00
|
|
|
"git.sr.ht/~sircmpwn/go-bare"
|
2020-04-03 16:59:17 +00:00
|
|
|
"gopkg.in/irc.v3"
|
|
|
|
)
|
|
|
|
|
2020-10-25 10:13:51 +00:00
|
|
|
// messageStore is a per-user store for IRC messages.
|
2021-01-04 13:24:00 +00:00
|
|
|
type messageStore interface {
|
|
|
|
Close() error
|
|
|
|
// LastMsgID queries the last message ID for the given network, entity and
|
|
|
|
// date. The message ID returned may not refer to a valid message, but can be
|
|
|
|
// used in history queries.
|
2021-11-03 15:37:01 +00:00
|
|
|
LastMsgID(network *Network, entity string, t time.Time) (string, error)
|
2021-10-08 22:13:16 +00:00
|
|
|
// LoadLatestID queries the latest non-event messages for the given network,
|
|
|
|
// entity and date, up to a count of limit messages, sorted from oldest to newest.
|
2021-11-03 17:18:04 +00:00
|
|
|
LoadLatestID(ctx context.Context, network *Network, entity, id string, limit int) ([]*irc.Message, error)
|
2021-11-03 15:37:01 +00:00
|
|
|
Append(network *Network, entity string, msg *irc.Message) (id string, err error)
|
2020-08-19 11:17:32 +00:00
|
|
|
}
|
2021-01-04 15:26:30 +00:00
|
|
|
|
2021-05-18 14:50:19 +00:00
|
|
|
type chatHistoryTarget struct {
|
|
|
|
Name string
|
|
|
|
LatestMessage time.Time
|
|
|
|
}
|
|
|
|
|
2021-01-04 16:17:35 +00:00
|
|
|
// chatHistoryMessageStore is a message store that supports chat history
|
|
|
|
// operations.
|
|
|
|
type chatHistoryMessageStore interface {
|
|
|
|
messageStore
|
|
|
|
|
2021-05-18 14:50:19 +00:00
|
|
|
// ListTargets lists channels and nicknames by time of the latest message.
|
|
|
|
// It returns up to limit targets, starting from start and ending on end,
|
|
|
|
// both excluded. end may be before or after start.
|
2021-10-08 22:13:16 +00:00
|
|
|
// If events is false, only PRIVMSG/NOTICE messages are considered.
|
2021-11-03 17:18:04 +00:00
|
|
|
ListTargets(ctx context.Context, network *Network, start, end time.Time, limit int, events bool) ([]chatHistoryTarget, error)
|
2021-03-27 12:08:31 +00:00
|
|
|
// 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.
|
2021-10-08 22:13:16 +00:00
|
|
|
// If events is false, only PRIVMSG/NOTICE messages are considered.
|
2021-11-03 17:18:04 +00:00
|
|
|
LoadBeforeTime(ctx context.Context, network *Network, entity string, start, end time.Time, limit int, events bool) ([]*irc.Message, error)
|
2021-03-27 12:08:31 +00:00
|
|
|
// 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.
|
2021-10-08 22:13:16 +00:00
|
|
|
// If events is false, only PRIVMSG/NOTICE messages are considered.
|
2021-11-03 17:18:04 +00:00
|
|
|
LoadAfterTime(ctx context.Context, network *Network, entity string, start, end time.Time, limit int, events bool) ([]*irc.Message, error)
|
2021-01-04 16:17:35 +00:00
|
|
|
}
|
|
|
|
|
2022-02-21 18:44:56 +00:00
|
|
|
type searchOptions struct {
|
|
|
|
start time.Time
|
|
|
|
end time.Time
|
|
|
|
limit int
|
|
|
|
from string
|
|
|
|
in string
|
|
|
|
text string
|
|
|
|
}
|
|
|
|
|
|
|
|
// searchMessageStore is a message store that supports server-side search
|
|
|
|
// operations.
|
|
|
|
type searchMessageStore interface {
|
|
|
|
messageStore
|
|
|
|
|
|
|
|
// Search returns messages matching the specified options.
|
|
|
|
Search(ctx context.Context, network *Network, search searchOptions) ([]*irc.Message, error)
|
|
|
|
}
|
|
|
|
|
2021-03-31 09:59:13 +00:00
|
|
|
type msgIDType uint
|
|
|
|
|
|
|
|
const (
|
|
|
|
msgIDNone msgIDType = iota
|
|
|
|
msgIDMemory
|
|
|
|
msgIDFS
|
|
|
|
)
|
|
|
|
|
|
|
|
const msgIDVersion uint = 0
|
|
|
|
|
|
|
|
type msgIDHeader struct {
|
|
|
|
Version uint
|
|
|
|
Network bare.Int
|
|
|
|
Target string
|
|
|
|
Type msgIDType
|
|
|
|
}
|
|
|
|
|
|
|
|
type msgIDBody interface {
|
|
|
|
msgIDType() msgIDType
|
2021-01-04 15:26:30 +00:00
|
|
|
}
|
|
|
|
|
2021-03-31 09:59:13 +00:00
|
|
|
func formatMsgID(netID int64, target string, body msgIDBody) string {
|
|
|
|
var buf bytes.Buffer
|
|
|
|
w := bare.NewWriter(&buf)
|
|
|
|
|
|
|
|
header := msgIDHeader{
|
|
|
|
Version: msgIDVersion,
|
|
|
|
Network: bare.Int(netID),
|
|
|
|
Target: target,
|
|
|
|
Type: body.msgIDType(),
|
|
|
|
}
|
|
|
|
if err := bare.MarshalWriter(w, &header); err != nil {
|
|
|
|
panic(err)
|
2021-01-04 15:26:30 +00:00
|
|
|
}
|
2021-03-31 09:59:13 +00:00
|
|
|
if err := bare.MarshalWriter(w, body); err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
return base64.RawURLEncoding.EncodeToString(buf.Bytes())
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseMsgID(s string, body msgIDBody) (netID int64, target string, err error) {
|
|
|
|
b, err := base64.RawURLEncoding.DecodeString(s)
|
2021-01-04 15:26:30 +00:00
|
|
|
if err != nil {
|
2021-03-31 09:59:13 +00:00
|
|
|
return 0, "", fmt.Errorf("invalid internal message ID: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
r := bare.NewReader(bytes.NewReader(b))
|
|
|
|
|
|
|
|
var header msgIDHeader
|
|
|
|
if err := bare.UnmarshalBareReader(r, &header); err != nil {
|
|
|
|
return 0, "", fmt.Errorf("invalid internal message ID: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if header.Version != msgIDVersion {
|
|
|
|
return 0, "", fmt.Errorf("invalid internal message ID: got version %v, want %v", header.Version, msgIDVersion)
|
2021-01-04 15:26:30 +00:00
|
|
|
}
|
2021-03-31 09:59:13 +00:00
|
|
|
|
|
|
|
if body != nil {
|
|
|
|
typ := body.msgIDType()
|
|
|
|
if header.Type != typ {
|
|
|
|
return 0, "", fmt.Errorf("invalid internal message ID: got type %v, want %v", header.Type, typ)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := bare.UnmarshalBareReader(r, body); err != nil {
|
|
|
|
return 0, "", fmt.Errorf("invalid internal message ID: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return int64(header.Network), header.Target, nil
|
2021-01-04 15:26:30 +00:00
|
|
|
}
|