source: code/trunk/db.go@ 785

Last change on this file since 785 was 781, checked in by delthas, 3 years ago

Add support for the wip soju.im/read capability and READ command

READ lets downstream clients share information between each other about
what messages have been read by other downstreams.

Each target/entity has an optional corresponding read receipt, which is
stored as a timestamp.

  • When a downstream sends: READ #chan timestamp=2020-01-01T01:23:45.000Z the read receipt for that target is set to that date
  • soju sends READ to downstreams:
    • on JOIN, if the client uses the soju.im/read capability
    • when the read receipt timestamp is set by any downstream

The read receipt date is clamped by the previous receipt date and the
current time.

File size: 4.0 KB
RevLine 
[98]1package soju
[77]2
3import (
[652]4 "context"
[148]5 "fmt"
[457]6 "net/url"
[263]7 "strings"
[434]8 "time"
[712]9
10 "github.com/prometheus/client_golang/prometheus"
[77]11)
12
[531]13type Database interface {
14 Close() error
[652]15 Stats(ctx context.Context) (*DatabaseStats, error)
[531]16
[652]17 ListUsers(ctx context.Context) ([]User, error)
18 GetUser(ctx context.Context, username string) (*User, error)
19 StoreUser(ctx context.Context, user *User) error
20 DeleteUser(ctx context.Context, id int64) error
[531]21
[652]22 ListNetworks(ctx context.Context, userID int64) ([]Network, error)
23 StoreNetwork(ctx context.Context, userID int64, network *Network) error
24 DeleteNetwork(ctx context.Context, id int64) error
25 ListChannels(ctx context.Context, networkID int64) ([]Channel, error)
26 StoreChannel(ctx context.Context, networKID int64, ch *Channel) error
27 DeleteChannel(ctx context.Context, id int64) error
[531]28
[652]29 ListDeliveryReceipts(ctx context.Context, networkID int64) ([]DeliveryReceipt, error)
30 StoreClientDeliveryReceipts(ctx context.Context, networkID int64, client string, receipts []DeliveryReceipt) error
[781]31
32 GetReadReceipt(ctx context.Context, networkID int64, name string) (*ReadReceipt, error)
33 StoreReadReceipt(ctx context.Context, networkID int64, receipt *ReadReceipt) error
[531]34}
35
[712]36type MetricsCollectorDatabase interface {
37 Database
38 MetricsCollector() prometheus.Collector
39}
40
[620]41func OpenDB(driver, source string) (Database, error) {
42 switch driver {
43 case "sqlite3":
44 return OpenSqliteDB(source)
45 case "postgres":
46 return OpenPostgresDB(source)
47 default:
48 return nil, fmt.Errorf("unsupported database driver: %q", driver)
49 }
50}
51
[607]52type DatabaseStats struct {
53 Users int64
54 Networks int64
55 Channels int64
56}
57
[77]58type User struct {
[382]59 ID int64
[77]60 Username string
[85]61 Password string // hashed
[568]62 Realname string
[327]63 Admin bool
[77]64}
65
[95]66type SASL struct {
67 Mechanism string
68
69 Plain struct {
70 Username string
71 Password string
72 }
[307]73
74 // TLS client certificate authentication.
75 External struct {
76 // X.509 certificate in DER form.
77 CertBlob []byte
78 // PKCS#8 private key in DER form.
79 PrivKeyBlob []byte
80 }
[95]81}
82
[77]83type Network struct {
[263]84 ID int64
85 Name string
86 Addr string
87 Nick string
88 Username string
89 Realname string
90 Pass string
91 ConnectCommands []string
92 SASL SASL
[542]93 Enabled bool
[77]94}
95
[149]96func (net *Network) GetName() string {
97 if net.Name != "" {
98 return net.Name
99 }
100 return net.Addr
101}
102
[457]103func (net *Network) URL() (*url.URL, error) {
104 s := net.Addr
105 if !strings.Contains(s, "://") {
106 // This is a raw domain name, make it an URL with the default scheme
107 s = "ircs://" + s
108 }
109
110 u, err := url.Parse(s)
111 if err != nil {
112 return nil, fmt.Errorf("failed to parse upstream server URL: %v", err)
113 }
114
115 return u, nil
116}
117
[664]118func GetNick(user *User, net *Network) string {
119 if net.Nick != "" {
120 return net.Nick
121 }
122 return user.Username
123}
124
[674]125func GetUsername(user *User, net *Network) string {
126 if net.Username != "" {
127 return net.Username
128 }
129 return GetNick(user, net)
130}
131
[568]132func GetRealname(user *User, net *Network) string {
[457]133 if net.Realname != "" {
134 return net.Realname
135 }
[568]136 if user.Realname != "" {
137 return user.Realname
138 }
[664]139 return GetNick(user, net)
[457]140}
141
[434]142type MessageFilter int
143
144const (
145 // TODO: use customizable user defaults for FilterDefault
146 FilterDefault MessageFilter = iota
147 FilterNone
148 FilterHighlight
149 FilterMessage
150)
151
152func parseFilter(filter string) (MessageFilter, error) {
153 switch filter {
154 case "default":
155 return FilterDefault, nil
156 case "none":
157 return FilterNone, nil
158 case "highlight":
159 return FilterHighlight, nil
160 case "message":
161 return FilterMessage, nil
162 }
163 return 0, fmt.Errorf("unknown filter: %q", filter)
164}
165
[77]166type Channel struct {
[497]167 ID int64
168 Name string
169 Key string
[434]170
[497]171 Detached bool
172 DetachedInternalMsgID string
173
[434]174 RelayDetached MessageFilter
175 ReattachOn MessageFilter
176 DetachAfter time.Duration
177 DetachOn MessageFilter
[77]178}
179
[489]180type DeliveryReceipt struct {
181 ID int64
182 Target string // channel or nick
183 Client string
184 InternalMsgID string
185}
[781]186
187type ReadReceipt struct {
188 ID int64
189 Target string // channel or nick
190 Timestamp time.Time
191}
Note: See TracBrowser for help on using the repository browser.