-
Notifications
You must be signed in to change notification settings - Fork 15
/
config.go
298 lines (272 loc) · 12.8 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
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
package main
import (
"encoding/json"
"errors"
"fmt"
"io"
"net"
"os"
"path/filepath"
"regexp"
"strconv"
)
type endpoint struct {
ListenPath string `json:"listen_path"` // the path excluding domain to listen to, the good choice is "/your-telegram-bot-token"
StatPath string `json:"stat_path"` // the path for statistics
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 set up a webhook and to set up this HTTP server
BotToken string `json:"bot_token"` // your Telegram bot token
Translation []string `json:"translation"` // translation files
Ads []string `json:"ads"` // ads files
}
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
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 statusConfirmationSeconds struct {
Offline int `json:"offline"`
Online int `json:"online"`
NotFound int `json:"not_found"`
Denied int `json:"denied"`
}
type config struct {
Debug bool `json:"debug"` // debug mode
CheckGID bool `json:"check_gid"` // check goroutines ids
ListenAddress string `json:"listen_address"` // the address to listen to
Website string `json:"website"` // one of the following strings: "bongacams", "stripchat", "chaturbate", "livejasmin", "flirt4free", "streamate"
WebsiteLink string `json:"website_link"` // affiliate link to website
PeriodSeconds int `json:"period_seconds"` // the period of querying models statuses
CleaningPeriodSeconds int `json:"cleaning_period_seconds"` // the cleaning period
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
BlockThreshold int `json:"block_threshold"` // do not send a message to the user after being blocked by him this number of times
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 a 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
StatusConfirmationSeconds statusConfirmationSeconds `json:"status_confirmation_seconds"` // a status is confirmed only if it lasts for at least this number of seconds
OfflineNotifications bool `json:"offline_notifications"` // enable offline notifications
SQLPrelude []string `json:"sql_prelude"` // run these SQL commands before any other
EnableWeek bool `json:"enable_week"` // enable week command
AffiliateLink string `json:"affiliate_link"` // affiliate link template
SpecificConfig map[string]string `json:"specific_config"` // the config for specific website
TelegramTimeoutSeconds int `json:"telegram_timeout_seconds"` // the timeout for Telegram queries
MaxSubscriptionsForPics int `json:"max_subscriptions_for_pics"` // the maximum amount of subscriptions for pics in a group chat
KeepStatusesForDays int `json:"keep_statuses_for_days"` // keep statuses for this number of days
MaxCleanSeconds int `json:"max_clean_seconds"` // maximum number of seconds to clean
SubsConfirmationPeriodSeconds int `json:"subs_confirmation_period_seconds"` // subscriptions confirmation period
NotificationsReadyPeriodSeconds int `json:"notifications_ready_period_seconds"` // notifications ready check period
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.ListenPath == "" {
return errors.New("configure listen_path")
}
if x.BotToken == "" {
return errors.New("configure bot_token")
}
if len(x.Translation) == 0 {
return errors.New("configure translation")
}
}
if cfg.ListenAddress == "" {
return errors.New("configure listen_address")
}
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.CleaningPeriodSeconds == 0 {
return errors.New("configure cleaning_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.BlockThreshold == 0 {
return errors.New("configure block_threshold")
}
if cfg.Website == "" {
return errors.New("configure website")
}
if cfg.WebsiteLink == "" {
return errors.New("configure website_link")
}
if cfg.Website == "livejasmin" {
if cfg.SpecificConfig["ps_id"] == "" {
return errors.New("configure specific_config/ps_id")
}
if cfg.SpecificConfig["access_key"] == "" {
return errors.New("configure specific_config/access_key")
}
}
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 cfg.AffiliateLink == "" {
cfg.AffiliateLink = "{{ . }}"
}
if cfg.TelegramTimeoutSeconds == 0 {
return errors.New("configure telegram_timeout_seconds")
}
if cfg.MaxSubscriptionsForPics == 0 {
return errors.New("configure max_subscriptions_for_pics")
}
if cfg.KeepStatusesForDays == 0 {
return errors.New("configure keep_statuses_for_days")
}
if cfg.MaxCleanSeconds == 0 {
return errors.New("configure max_clean_seconds")
}
if cfg.SubsConfirmationPeriodSeconds == 0 {
return errors.New("configure subs_confirmation_period_seconds")
}
if cfg.NotificationsReadyPeriodSeconds == 0 {
return errors.New("configure notifications_ready_period_seconds")
}
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.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")
}
return nil
}