forked from bcmk/siren
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathconfig.go
260 lines (235 loc) · 10.1 KB
/
config.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
package main
import (
"encoding/json"
"errors"
"fmt"
"io"
"net"
"os"
"path/filepath"
"regexp"
"strconv"
)
type endpoint struct {
BotName string `json:"bot_name"` // the name of the bot
ListenPath string `json:"listen_path"` // the path excluding domain to listen to, the good choice is "/your-telegram-bot-token"
ListenAddress string `json:"listen_address"` // the address to listen to
WebhookDomain string `json:"webhook_domain"` // the domain listening to the webhook
CertificatePath string `json:"certificate_path"` // a path to your certificate, it is used to setup a webook and to setup this HTTP server
CertificateKeyPath string `json:"certificate_key_path"` // your certificate key, omit if under a proxy
BotToken string `json:"bot_token"` // your telegram bot token
Translation string `json:"translation"` // translation strings
}
type coinPaymentsConfig struct {
SubscriptionPacket string `json:"subscription_packet"` // subscription packet, format "15/10" meaning 15 USD for 10 models
Currencies []string `json:"currencies"` // CoinPayments currencies to buy a subscription with
PublicKey string `json:"public_key"` // CoinPayments public key
PrivateKey string `json:"private_key"` // CoinPayments private key
IPNListenURL string `json:"ipn_listen_url"` // CoinPayments IPN payment status notification listen URL
IPNListenAddress string `json:"ipn_listen_address"` // CoinPayments IPN payment status notification listen address
IPNSecret string `json:"ipn_secret"` // CoinPayments IPN secret
subscriptionPacketPrice int
subscriptionPacketModelNumber int
}
type mailConfig struct {
Host string `json:"host"` // the hostname for email
ListenAddress string `json:"listen_address"` // the address to listen to incoming mail
Certificate string `json:"certificate"` // certificate path for STARTTLS
CertificateKey string `json:"certificate_key"` // certificate key path for STARTTLS
}
type config struct {
Website string `json:"website"` // one of the following strings: "bongacams", "stripchat", "chaturbate"
PeriodSeconds int `json:"period_seconds"` // the period of querying models statuses
MaxModels int `json:"max_models"` // maximum models per user
TimeoutSeconds int `json:"timeout_seconds"` // HTTP timeout
AdminID int64 `json:"admin_id"` // admin telegram ID
AdminEndpoint string `json:"admin_endpoint"` // admin endpoint
DBPath string `json:"db_path"` // path to the database
NotFoundThreshold int `json:"not_found_threshold"` // remove a model after she is reported as not found this number of times
BlockThreshold int `json:"block_threshold"` // do not send a message to the user after being blocked by him this number of times
Debug bool `json:"debug"` // debug mode
IntervalMs int `json:"interval_ms"` // queries interval per IP address for rate limited access
SourceIPAddresses []string `json:"source_ip_addresses"` // source IP addresses for rate limited access
DangerousErrorRate string `json:"dangerous_error_rate"` // dangerous error rate, warn admin if it is reached, format "1000/10000"
EnableCookies bool `json:"enable_cookies"` // enable cookies, it can be useful to mitigate rate limits
Headers [][2]string `json:"headers"` // HTTP headers to make queries with
StatPassword string `json:"stat_password"` // password for statistics
ErrorReportingPeriodMinutes int `json:"error_reporting_period_minutes"` // the period of the error reports
Endpoints map[string]endpoint `json:"endpoints"` // the endpoints by simple name, used for the support of the bots in different languages accessing the same database
HeavyUserRemainder int `json:"heavy_user_remainder"` // the maximum remainder of models to treat an user as heavy
CoinPayments *coinPaymentsConfig `json:"coin_payments"` // CoinPayments integration
Mail *mailConfig `json:"mail"` // mail config
ReferralBonus int `json:"referral_bonus"` // number of emails for a referrer
FollowerBonus int `json:"follower_bonus"` // number of emails for a new user registered by a referral link
UsersOnlineEndpoint string `json:"users_online_endpoint"` // the endpoint to fetch online users
OfflineThresholdSeconds int `json:"offline_threshold_seconds"` // report online status only if model was offline longer than this threshold
OfflineNotifications bool `json:"offline_notifications"` // enable offline notifications
errorThreshold int
errorDenominator int
}
var fractionRegexp = regexp.MustCompile(`^(\d+)/(\d+)$`)
func readConfig(path string) *config {
file, err := os.Open(filepath.Clean(path))
checkErr(err)
defer func() { checkErr(file.Close()) }()
return parseConfig(file)
}
func parseConfig(r io.Reader) *config {
decoder := json.NewDecoder(r)
decoder.DisallowUnknownFields()
cfg := &config{}
err := decoder.Decode(cfg)
checkErr(err)
checkErr(checkConfig(cfg))
if len(cfg.SourceIPAddresses) == 0 {
cfg.SourceIPAddresses = append(cfg.SourceIPAddresses, "")
}
return cfg
}
func checkConfig(cfg *config) error {
for _, x := range cfg.SourceIPAddresses {
if net.ParseIP(x) == nil {
return fmt.Errorf("cannot parse sourece IP address %s", x)
}
}
for _, x := range cfg.Endpoints {
if x.BotName == "" {
return errors.New("configure bot_name")
}
if x.ListenAddress == "" {
return errors.New("configure listen_address")
}
if x.ListenPath == "" {
return errors.New("configure listen_path")
}
if x.BotToken == "" {
return errors.New("configure bot_token")
}
if x.Translation == "" {
return errors.New("configure translation")
}
}
if _, found := cfg.Endpoints[cfg.AdminEndpoint]; !found {
return errors.New("configure admin_endpoint")
}
if cfg.PeriodSeconds == 0 {
return errors.New("configure period_seconds")
}
if cfg.MaxModels == 0 {
return errors.New("configure max_models")
}
if cfg.TimeoutSeconds == 0 {
return errors.New("configure timeout_seconds")
}
if cfg.AdminID == 0 {
return errors.New("configure admin_id")
}
if cfg.DBPath == "" {
return errors.New("configure db_path")
}
if cfg.NotFoundThreshold == 0 {
return errors.New("configure not_found_threshold")
}
if cfg.BlockThreshold == 0 {
return errors.New("configure block_threshold")
}
if cfg.Website == "" {
return errors.New("configure website")
}
if cfg.StatPassword == "" {
return errors.New("configure stat_password")
}
if cfg.ErrorReportingPeriodMinutes == 0 {
return errors.New("configure error_reporting_period_minutes")
}
if cfg.HeavyUserRemainder == 0 {
return errors.New("configure heavy_user_remainder")
}
if cfg.ReferralBonus == 0 {
return errors.New("configure referral_bonus")
}
if cfg.FollowerBonus == 0 {
return errors.New("configure follower_bonus")
}
if m := fractionRegexp.FindStringSubmatch(cfg.DangerousErrorRate); len(m) == 3 {
errorThreshold, err := strconv.ParseInt(m[1], 10, 0)
if err != nil {
return err
}
errorDenominator, err := strconv.ParseInt(m[2], 10, 0)
if err != nil {
return err
}
if errorDenominator == 0 {
return errors.New(`configure dangerous_errors_rate as "x/y", where y > 0`)
}
cfg.errorThreshold = int(errorThreshold)
cfg.errorDenominator = int(errorDenominator)
} else {
return errors.New("configure dangerous_error_rate")
}
if cfg.CoinPayments != nil {
if err := checkCoinPaymentsConfig(cfg.CoinPayments); err != nil {
return err
}
}
if cfg.Mail != nil {
if err := checkMailConfig(cfg.Mail); err != nil {
return err
}
}
return nil
}
func checkCoinPaymentsConfig(cfg *coinPaymentsConfig) error {
if len(cfg.Currencies) == 0 {
return errors.New("configure currencies")
}
if cfg.PublicKey == "" {
return errors.New("configure public_key")
}
if cfg.PrivateKey == "" {
return errors.New("configure private_key")
}
if cfg.IPNListenURL == "" {
return errors.New("configure ipn_path")
}
if cfg.IPNListenAddress == "" {
return errors.New("configure ipn_path")
}
if cfg.IPNSecret == "" {
return errors.New("configure ipn_secret")
}
if m := fractionRegexp.FindStringSubmatch(cfg.SubscriptionPacket); len(m) == 3 {
subscriptionPacketModelNumber, err := strconv.ParseInt(m[1], 10, 0)
if err != nil {
return err
}
subscriptionPacketPrice, err := strconv.ParseInt(m[2], 10, 0)
if err != nil {
return err
}
if subscriptionPacketModelNumber == 0 || subscriptionPacketPrice == 0 {
return errors.New("invalid subscription packet")
}
cfg.subscriptionPacketPrice = int(subscriptionPacketPrice)
cfg.subscriptionPacketModelNumber = int(subscriptionPacketModelNumber)
} else {
return errors.New("configure subscription_packet")
}
return nil
}
func checkMailConfig(cfg *mailConfig) error {
if cfg.Host == "" {
return errors.New("configure host")
}
if cfg.ListenAddress == "" {
return errors.New("configure listen_address")
}
if cfg.Certificate == "" {
return errors.New("configure certificate")
}
if cfg.CertificateKey == "" {
return errors.New("configure certificate_key")
}
return nil
}