source: code/trunk/msgstore.go@ 666

Last change on this file since 666 was 666, checked in by contact, 4 years ago

msgstore: take Network as arg instead of network

The message stores don't need to access the internal network
struct, they just need network metadata such as ID and name.

This can ease moving message stores into a separate package in the
future.

File size: 3.7 KB
RevLine 
[423]1package soju
2
3import (
[488]4 "bytes"
5 "encoding/base64"
[440]6 "fmt"
[423]7 "time"
8
[488]9 "git.sr.ht/~sircmpwn/go-bare"
[423]10 "gopkg.in/irc.v3"
11)
12
13// messageStore is a per-user store for IRC messages.
[439]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.
[666]19 LastMsgID(network *Network, entity string, t time.Time) (string, error)
[665]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.
[666]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)
[423]24}
[440]25
[549]26type chatHistoryTarget struct {
27 Name string
28 LatestMessage time.Time
29}
30
[441]31// chatHistoryMessageStore is a message store that supports chat history
32// operations.
33type chatHistoryMessageStore interface {
34 messageStore
35
[549]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.
[665]39 // If events is false, only PRIVMSG/NOTICE messages are considered.
[666]40 ListTargets(network *Network, start, end time.Time, limit int, events bool) ([]chatHistoryTarget, error)
[516]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.
[665]44 // If events is false, only PRIVMSG/NOTICE messages are considered.
[666]45 LoadBeforeTime(network *Network, entity string, start, end time.Time, limit int, events bool) ([]*irc.Message, error)
[516]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.
[665]49 // If events is false, only PRIVMSG/NOTICE messages are considered.
[666]50 LoadAfterTime(network *Network, entity string, start, end time.Time, limit int, events bool) ([]*irc.Message, error)
[441]51}
52
[488]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
[440]68}
69
[488]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(),
[440]83 }
[488]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)
[440]95 if err != nil {
[488]96 return 0, "", fmt.Errorf("invalid internal message ID: %v", err)
[440]97 }
[488]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
[440]122}
Note: See TracBrowser for help on using the repository browser.