soju/msgstore/msgstore.go

158 lines
4.5 KiB
Go
Raw Normal View History

2022-05-09 07:25:57 -07:00
package msgstore
import (
"bytes"
"context"
"encoding/base64"
"fmt"
"time"
"git.sr.ht/~sircmpwn/go-bare"
2022-11-14 03:06:58 -08:00
"gopkg.in/irc.v4"
2022-05-09 03:34:43 -07:00
"git.sr.ht/~emersion/soju/database"
)
2022-05-09 07:25:57 -07:00
type LoadMessageOptions struct {
Network *database.Network
Entity string
Limit int
Events bool
}
2022-05-09 07:25:57 -07:00
// Store is a per-user store for IRC messages.
type Store 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.
2022-05-09 03:34:43 -07:00
LastMsgID(network *database.Network, entity string, t time.Time) (string, error)
2021-10-08 15:13:16 -07: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.
2022-05-09 07:25:57 -07:00
LoadLatestID(ctx context.Context, id string, options *LoadMessageOptions) ([]*irc.Message, error)
2022-05-09 03:34:43 -07:00
Append(network *database.Network, entity string, msg *irc.Message) (id string, err error)
}
2022-05-09 07:25:57 -07:00
type ChatHistoryTarget struct {
Name string
LatestMessage time.Time
}
2022-05-09 07:25:57 -07:00
// ChatHistoryStore is a message store that supports chat history operations.
type ChatHistoryStore interface {
Store
// 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 15:13:16 -07:00
// If events is false, only PRIVMSG/NOTICE messages are considered.
2022-05-09 07:25:57 -07:00
ListTargets(ctx context.Context, network *database.Network, start, end time.Time, limit int, events bool) ([]ChatHistoryTarget, error)
2021-03-27 05:08:31 -07: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 15:13:16 -07:00
// If events is false, only PRIVMSG/NOTICE messages are considered.
2022-05-09 07:25:57 -07:00
LoadBeforeTime(ctx context.Context, start, end time.Time, options *LoadMessageOptions) ([]*irc.Message, error)
// LoadAfterTime loads up to limit messages after start up to end. The
2021-03-27 05:08:31 -07:00
// returned messages must be between and excluding the provided bounds.
// end is after start.
2021-10-08 15:13:16 -07:00
// If events is false, only PRIVMSG/NOTICE messages are considered.
2022-05-09 07:25:57 -07:00
LoadAfterTime(ctx context.Context, start, end time.Time, options *LoadMessageOptions) ([]*irc.Message, error)
}
2022-05-09 07:25:57 -07:00
type SearchMessageOptions struct {
Start time.Time
End time.Time
Limit int
From string
In string
Text string
2022-02-21 10:44:56 -08:00
}
2022-05-09 07:25:57 -07:00
// SearchStore is a message store that supports server-side search operations.
type SearchStore interface {
Store
2022-02-21 10:44:56 -08:00
// Search returns messages matching the specified options.
2022-05-09 07:25:57 -07:00
Search(ctx context.Context, network *database.Network, options *SearchMessageOptions) ([]*irc.Message, error)
}
// RenameNetworkStore is a message store which needs to be notified of network
// name changes.
type RenameNetworkStore interface {
Store
RenameNetwork(oldNet, newNet *database.Network) error
2022-02-21 10:44:56 -08:00
}
type msgIDType uint
const (
msgIDNone msgIDType = iota
msgIDMemory
msgIDFS
msgIDDB
)
const msgIDVersion uint = 0
type msgIDHeader struct {
Version uint
Network bare.Int
Target string
Type msgIDType
}
type msgIDBody interface {
msgIDType() msgIDType
}
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)
}
if err := bare.MarshalWriter(w, body); err != nil {
panic(err)
}
return base64.RawURLEncoding.EncodeToString(buf.Bytes())
}
2022-05-09 07:25:57 -07:00
func ParseMsgID(s string, body msgIDBody) (netID int64, target string, err error) {
b, err := base64.RawURLEncoding.DecodeString(s)
if err != nil {
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)
}
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
}