// MediNET - Session repository and community portal for Meditation Assistant // https://code.rocket9labs.com/tslocum/medinet // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . package main import ( "flag" "fmt" "io/ioutil" "log" "math/rand" "os" "regexp" "time" "gopkg.in/yaml.v2" ) type configuration struct { TimeZone string DBDriver string DBSource string Om string Web string } type statistics struct { AccountsCreated int ActiveAccounts []int SessionsPosted int TopStreak int } var ( db *database config *configuration printDebug bool stats *statistics serverLocation *time.Location regexpNumbers = regexp.MustCompile("[0-9]+") regexpMarket = regexp.MustCompile(".*?([a-zA-Z]+)$") ) func logDebug(message string) { if printDebug { log.Println(message) } } func logDebugf(format string, a ...interface{}) { logDebug(fmt.Sprintf(format, a...)) } func atWindowStart(t time.Time, streakBuffer int) time.Time { year, month, day := t.Date() return time.Date(year, month, day, streakBuffer/3600, (streakBuffer%3600)/60, streakBuffer%60, 0, t.Location()) } func beforeWindowStart(t time.Time, streakBuffer int) bool { return t.Before(atWindowStart(t, streakBuffer)) } func failOnError(err error) { if err != nil { log.Fatal(err) } } func trackActiveAccount(accountID int) { active := false for _, aID := range stats.ActiveAccounts { if aID == accountID { active = true } } if !active { stats.ActiveAccounts = append(stats.ActiveAccounts, accountID) } err := db.updateLastActive(accountID) failOnError(err) } func printStatistics() { for { stats.ActiveAccounts = nil stats.AccountsCreated, stats.SessionsPosted, stats.TopStreak = 0, 0, 0 now := time.Now().In(serverLocation) fourAM := time.Date(now.Year(), now.Month(), now.Day(), 4, 0, 0, 0, serverLocation) if !fourAM.After(now) { fourAM = fourAM.AddDate(0, 0, 1) } time.Sleep(time.Until(fourAM)) log.Printf("%d accounts (%d new) posted %d sessions (top streak %d)", len(stats.ActiveAccounts), stats.AccountsCreated, stats.SessionsPosted, stats.TopStreak) } } func main() { var opts struct { ConfigFile string Debug bool } flag.StringVar(&opts.ConfigFile, "config", "", "Configuration file") flag.BoolVar(&opts.Debug, "debug", false, "Print debug information") flag.Parse() rand.Seed(time.Now().UTC().UnixNano()) if opts.ConfigFile == "" { log.Fatal("Please specify configuration file with: medinet -c ") } _, err := os.Stat(opts.ConfigFile) if err != nil { log.Fatalf("Configuration file %s does not exist: %s", opts.ConfigFile, err) } configData, err := ioutil.ReadFile(opts.ConfigFile) if err != nil { log.Fatalf("Failed to read %s: %v", opts.ConfigFile, err) } config = new(configuration) err = yaml.Unmarshal(configData, config) if err != nil { log.Fatalf("Failed to read %s: %v", opts.ConfigFile, err) } else if config.DBDriver == "" { log.Fatal("Specify database driver in configuration file") } else if config.Om == "" { log.Fatal("Specify Om host:port in configuration file") } else if config.Web == "" { log.Fatal("Specify Web directory in configuration file") } tz := "UTC" if config.TimeZone != "" { tz = config.TimeZone } loc, err := time.LoadLocation(tz) failOnError(err) serverLocation = loc printDebug = opts.Debug stats = new(statistics) go printStatistics() db, err = connect(config.DBDriver, config.DBSource) failOnError(err) initWeb() listenWeb() log.Printf("Listening on %+v", config.Om) }