source: code/trunk/msgstore.go@ 665

Last change on this file since 665 was 665, checked in by delthas, 4 years ago

Add support for draft/event-playback

File size: 3.7 KB
Line 
1package soju
2
3import (
4 "bytes"
5 "encoding/base64"
6 "fmt"
7 "time"
8
9 "git.sr.ht/~sircmpwn/go-bare"
10 "gopkg.in/irc.v3"
11)
12
13// messageStore is a per-user store for IRC messages.
14type messageStore interface {
15 Close() error
16 // LastMsgID queries the last message ID for the given network, entity and
17 // date. The message ID returned may not refer to a valid message, but can be
18 // used in history queries.
19 LastMsgID(network *network, entity string, t time.Time) (string, error)
20 // LoadLatestID queries the latest non-event messages for the given network,
21 // entity and date, up to a count of limit messages, sorted from oldest to newest.
22 LoadLatestID(network *network, entity, id string, limit int) ([]*irc.Message, error)
23 Append(network *network, entity string, msg *irc.Message) (id string, err error)
24}
25
26type chatHistoryTarget struct {
27 Name string
28 LatestMessage time.Time
29}
30
31// chatHistoryMessageStore is a message store that supports chat history
32// operations.
33type chatHistoryMessageStore interface {
34 messageStore
35
36 // ListTargets lists channels and nicknames by time of the latest message.
37 // It returns up to limit targets, starting from start and ending on end,
38 // both excluded. end may be before or after start.
39 // If events is false, only PRIVMSG/NOTICE messages are considered.
40 ListTargets(network *network, start, end time.Time, limit int, events bool) ([]chatHistoryTarget, error)
41 // LoadBeforeTime loads up to limit messages before start down to end. The
42 // returned messages must be between and excluding the provided bounds.
43 // end is before start.
44 // If events is false, only PRIVMSG/NOTICE messages are considered.
45 LoadBeforeTime(network *network, entity string, start, end time.Time, limit int, events bool) ([]*irc.Message, error)
46 // LoadBeforeTime loads up to limit messages after start up to end. The
47 // returned messages must be between and excluding the provided bounds.
48 // end is after start.
49 // If events is false, only PRIVMSG/NOTICE messages are considered.
50 LoadAfterTime(network *network, entity string, start, end time.Time, limit int, events bool) ([]*irc.Message, error)
51}
52
53type msgIDType uint
54
55const (
56 msgIDNone msgIDType = iota
57 msgIDMemory
58 msgIDFS
59)
60
61const msgIDVersion uint = 0
62
63type msgIDHeader struct {
64 Version uint
65 Network bare.Int
66 Target string
67 Type msgIDType
68}
69
70type msgIDBody interface {
71 msgIDType() msgIDType
72}
73
74func formatMsgID(netID int64, target string, body msgIDBody) string {
75 var buf bytes.Buffer
76 w := bare.NewWriter(&buf)
77
78 header := msgIDHeader{
79 Version: msgIDVersion,
80 Network: bare.Int(netID),
81 Target: target,
82 Type: body.msgIDType(),
83 }
84 if err := bare.MarshalWriter(w, &header); err != nil {
85 panic(err)
86 }
87 if err := bare.MarshalWriter(w, body); err != nil {
88 panic(err)
89 }
90 return base64.RawURLEncoding.EncodeToString(buf.Bytes())
91}
92
93func parseMsgID(s string, body msgIDBody) (netID int64, target string, err error) {
94 b, err := base64.RawURLEncoding.DecodeString(s)
95 if err != nil {
96 return 0, "", fmt.Errorf("invalid internal message ID: %v", err)
97 }
98
99 r := bare.NewReader(bytes.NewReader(b))
100
101 var header msgIDHeader
102 if err := bare.UnmarshalBareReader(r, &header); err != nil {
103 return 0, "", fmt.Errorf("invalid internal message ID: %v", err)
104 }
105
106 if header.Version != msgIDVersion {
107 return 0, "", fmt.Errorf("invalid internal message ID: got version %v, want %v", header.Version, msgIDVersion)
108 }
109
110 if body != nil {
111 typ := body.msgIDType()
112 if header.Type != typ {
113 return 0, "", fmt.Errorf("invalid internal message ID: got type %v, want %v", header.Type, typ)
114 }
115
116 if err := bare.UnmarshalBareReader(r, body); err != nil {
117 return 0, "", fmt.Errorf("invalid internal message ID: %v", err)
118 }
119 }
120
121 return int64(header.Network), header.Target, nil
122}
Note: See TracBrowser for help on using the repository browser.