<commit_before>/*
Copyright 2015 Home Office All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
	"fmt"
	"os"
	"os/signal"
	"sync"
	"syscall"

	"github.com/golang/glog"
)

var (
	prog    = "vault-sidekick"
	release = "v0.3.2"
	gitsha  = ""
)

func main() {
	version := fmt.Sprintf("%s (git+sha %s)", release, gitsha)
	// step: parse and validate the command line / environment options
	if err := parseOptions(); err != nil {
		showUsage("invalid options, %s", err)
	}
	if options.showVersion {
		fmt.Printf("%s %s\n", prog, version)
		return
	}
	glog.Infof("starting the %s, %s", prog, version)

	if options.oneShot {
		glog.Infof("running in one-shot mode")
	}

	// step: create a client to vault
	vault, err := NewVaultService(options.vaultURL)
	if err != nil {
		showUsage("unable to create the vault client: %s", err)
	}
	// step: create a channel to receive events upon and add our resources for renewal
	updates := make(chan VaultEvent, 10)
	vault.AddListener(updates)

	// step: setup the termination signals
	signalChannel := make(chan os.Signal)
	signal.Notify(signalChannel, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)

	// step: add each of the resources to the service processor
	for _, rn := range options.resources.items {
		if err := rn.IsValid(); err != nil {
			showUsage("%s", err)
		}
		vault.Watch(rn)
	}

	toProcess := options.resources.items
	toProcessLock := &sync.Mutex{}
	failedResource := false
	if options.oneShot && len(toProcess) == 0 {
		glog.Infof("nothing to retrieve from vault. exiting...")
		os.Exit(0)
	}
	// step: we simply wait for events i.e. secrets from vault and write them to the output directory
	for {
		select {
		case evt := <-updates:
			glog.V(10).Infof("recieved an update from the resource: %s", evt.Resource)
			go func(r VaultEvent) {
				toProcessLock.Lock()
				defer toProcessLock.Unlock()
				switch r.Type {
				case EventTypeSuccess:
					if err := processResource(evt.Resource, evt.Secret); err != nil {
						glog.Errorf("failed to write out the update, error: %s", err)
					}
					if options.oneShot {
						for i, r := range toProcess {
							if evt.Resource == r {
								toProcess = append(toProcess[:i], toProcess[i+1:]...)
							}
						}
					}
				case EventTypeFailure:
					if evt.Resource.maxRetries > 0 && evt.Resource.maxRetries < evt.Resource.retries {
						for i, r := range toProcess {
							if evt.Resource == r {
								toProcess = append(toProcess[:i], toProcess[i+1:]...)
								failedResource = true
							}
						}
					}
				}
				if len(toProcess) == 0 {
					glog.Infof("no resources left to process. exiting...")
					if failedResource {
						os.Exit(1)
					} else {
						os.Exit(0)
					}
				}
			}(evt)
		case <-signalChannel:
			glog.Infof("recieved a termination signal, shutting down the service")
			os.Exit(0)
		}
	}
}
<commit_msg>increment version<commit_after>/*
Copyright 2015 Home Office All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
	"fmt"
	"os"
	"os/signal"
	"sync"
	"syscall"

	"github.com/golang/glog"
)

var (
	prog    = "vault-sidekick"
	release = "v0.3.3"
	gitsha  = ""
)

func main() {
	version := fmt.Sprintf("%s (git+sha %s)", release, gitsha)
	// step: parse and validate the command line / environment options
	if err := parseOptions(); err != nil {
		showUsage("invalid options, %s", err)
	}
	if options.showVersion {
		fmt.Printf("%s %s\n", prog, version)
		return
	}
	glog.Infof("starting the %s, %s", prog, version)

	if options.oneShot {
		glog.Infof("running in one-shot mode")
	}

	// step: create a client to vault
	vault, err := NewVaultService(options.vaultURL)
	if err != nil {
		showUsage("unable to create the vault client: %s", err)
	}
	// step: create a channel to receive events upon and add our resources for renewal
	updates := make(chan VaultEvent, 10)
	vault.AddListener(updates)

	// step: setup the termination signals
	signalChannel := make(chan os.Signal)
	signal.Notify(signalChannel, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)

	// step: add each of the resources to the service processor
	for _, rn := range options.resources.items {
		if err := rn.IsValid(); err != nil {
			showUsage("%s", err)
		}
		vault.Watch(rn)
	}

	toProcess := options.resources.items
	toProcessLock := &sync.Mutex{}
	failedResource := false
	if options.oneShot && len(toProcess) == 0 {
		glog.Infof("nothing to retrieve from vault. exiting...")
		os.Exit(0)
	}
	// step: we simply wait for events i.e. secrets from vault and write them to the output directory
	for {
		select {
		case evt := <-updates:
			glog.V(10).Infof("recieved an update from the resource: %s", evt.Resource)
			go func(r VaultEvent) {
				toProcessLock.Lock()
				defer toProcessLock.Unlock()
				switch r.Type {
				case EventTypeSuccess:
					if err := processResource(evt.Resource, evt.Secret); err != nil {
						glog.Errorf("failed to write out the update, error: %s", err)
					}
					if options.oneShot {
						for i, r := range toProcess {
							if evt.Resource == r {
								toProcess = append(toProcess[:i], toProcess[i+1:]...)
							}
						}
					}
				case EventTypeFailure:
					if evt.Resource.maxRetries > 0 && evt.Resource.maxRetries < evt.Resource.retries {
						for i, r := range toProcess {
							if evt.Resource == r {
								toProcess = append(toProcess[:i], toProcess[i+1:]...)
								failedResource = true
							}
						}
					}
				}
				if len(toProcess) == 0 {
					glog.Infof("no resources left to process. exiting...")
					if failedResource {
						os.Exit(1)
					} else {
						os.Exit(0)
					}
				}
			}(evt)
		case <-signalChannel:
			glog.Infof("recieved a termination signal, shutting down the service")
			os.Exit(0)
		}
	}
}
<|endoftext|>


<commit_before>package main

import "fmt"

func Test() {
	fmt.Println("Test")
}

func main() {
	fmt.Println("Hello Artifacts!")

	fmt.Println("Commit =", Commit)
	fmt.Println("Tag =", Tag)
	fmt.Println("Branch =", Branch)
	fmt.Println("BuildNumber =", BuildNumber)
}
<commit_msg>Import UUID package for testing.<commit_after>package main

import (
	"fmt"

	"github.com/mendersoftware/services/utils/uuid"
)

func Test() {
	fmt.Println("Test")
}

func main() {
	fmt.Println("Hello Artifacts!")

	fmt.Println("Commit =", Commit)
	fmt.Println("Tag =", Tag)
	fmt.Println("Branch =", Branch)
	fmt.Println("BuildNumber =", BuildNumber)

	id, _ := uuid.MakeUUID()
	fmt.Println("UUID=", id)
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
)

var (
	ConfigsPath string
)

func init() {
	flag.StringVar(&ConfigsPath, "c", "", "Path to all configs")
	flag.Parse()

	if ConfigsPath == "" {
		log.Fatalln("Please specify path to configs directory")
	}
}

func main() {
	file, err := os.Open(ConfigsPath)
	if err != nil {
		log.Fatalln(err)
	}

	stat, err := file.Stat()
	if err != nil {
		log.Fatalln(err)
	}

	if !stat.IsDir() {
		log.Fatalln("Path is not a directory")
	}

	files, err := ioutil.ReadDir(ConfigsPath)
	if err != nil {
		log.Fatalln(err)
	}

	for _, file := range files {
		if filepath.Ext(file.Name()) != ".yml" {
			continue
		}

		config, err := ReadConfig(ConfigsPath + "/" + file.Name())
		if err != nil {
			log.Fatalln(err)
		}

		RunConfig(config)
	}
}
<commit_msg>Add version<commit_after>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
)

const (
	VERSION = "0.1.0"
)

var (
	ConfigsPath string
	ShowVersion bool
)

func init() {
	flag.StringVar(&ConfigsPath, "c", "", "Path to all configs")
	flag.BoolVar(&ShowVersion, "v", false, "Show version")
	flag.Parse()

	if ShowVersion {
		fmt.Println("Version:", VERSION)
		os.Exit(0)
	}

	if ConfigsPath == "" {
		log.Fatalln("Please specify path to configs directory")
	}
}

func main() {
	file, err := os.Open(ConfigsPath)
	if err != nil {
		log.Fatalln(err)
	}

	stat, err := file.Stat()
	if err != nil {
		log.Fatalln(err)
	}

	if !stat.IsDir() {
		log.Fatalln("Path is not a directory")
	}

	files, err := ioutil.ReadDir(ConfigsPath)
	if err != nil {
		log.Fatalln(err)
	}

	for _, file := range files {
		if filepath.Ext(file.Name()) != ".yml" {
			continue
		}

		config, err := ReadConfig(ConfigsPath + "/" + file.Name())
		if err != nil {
			log.Fatalln(err)
		}

		RunConfig(config)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"os"
	"os/signal"
	"sync"
	"syscall"

	"github.com/Shopify/sarama"
)

func main() {

	config := InitConfig()

	config.GetConfig()

	consumerBrokers := config.srcBrokers
	producerBrokers := config.dstBrokers

	logFile, err := os.OpenFile(config.firehoseLog, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
	if err != nil {
		log.Fatalln("Failed to open log file:", err)
	}
	defer logFile.Close()

	log.SetOutput(logFile)

	metricsFile, err := os.OpenFile(config.metricsLog, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
	if err != nil {
		log.Fatalln("Failed to open log file:", err)
	}
	defer metricsFile.Close()

	log.Println("Getting the Kafka consumer")
	consumer, err := GetKafkaConsumer(consumerBrokers, metricsFile)
	if err != nil {
		log.Fatalln("Unable to create consumer", err)
	}

	log.Println("Getting the Kafka producer")
	producer, err := GetKafkaProducer(producerBrokers, metricsFile)
	if err != nil {
		log.Fatalln("Unable to create producer", err)
	}

	defer CloseConsumer(&consumer)
	defer CloseProducer(&producer)

	signals := make(chan os.Signal, 1)
	signal.Notify(signals, syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT)

	var wg sync.WaitGroup

	topic := config.topic

	transferChan := make(chan sarama.ProducerMessage, 100000)
	for partition := 0; partition < 24; partition++ {

		partitionConsumer, err := consumer.ConsumePartition(topic, int32(partition), sarama.OffsetNewest)
		if err != nil {
			log.Fatalln("Unable to create partition consumer", err)
		}

		wg.Add(1)

		go PullFromTopic(partitionConsumer, transferChan, signals, &wg)
		log.Println("Started consumer for partition ", partition)

		wg.Add(2)
		go PushToTopic(producer, transferChan, signals, &wg)
		log.Println("Started producer")
		go PushToTopic(producer, transferChan, signals, &wg)
		log.Println("Started producer")
	}

	go MonitorChan(transferChan, signals, &wg)

	wg.Wait()

	log.Println("All threads done, closing clients and ending")
}
<commit_msg>No need to set topic var, just use config<commit_after>package main

import (
	"log"
	"os"
	"os/signal"
	"sync"
	"syscall"

	"github.com/Shopify/sarama"
)

func main() {

	config := InitConfig()

	config.GetConfig()

	consumerBrokers := config.srcBrokers
	producerBrokers := config.dstBrokers

	logFile, err := os.OpenFile(config.firehoseLog, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
	if err != nil {
		log.Fatalln("Failed to open log file:", err)
	}
	defer logFile.Close()

	log.SetOutput(logFile)

	metricsFile, err := os.OpenFile(config.metricsLog, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
	if err != nil {
		log.Fatalln("Failed to open log file:", err)
	}
	defer metricsFile.Close()

	log.Println("Getting the Kafka consumer")
	consumer, err := GetKafkaConsumer(consumerBrokers, metricsFile)
	if err != nil {
		log.Fatalln("Unable to create consumer", err)
	}

	log.Println("Getting the Kafka producer")
	producer, err := GetKafkaProducer(producerBrokers, metricsFile)
	if err != nil {
		log.Fatalln("Unable to create producer", err)
	}

	defer CloseConsumer(&consumer)
	defer CloseProducer(&producer)

	signals := make(chan os.Signal, 1)
	signal.Notify(signals, syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT)

	var wg sync.WaitGroup

	transferChan := make(chan sarama.ProducerMessage, 100000)
	for partition := 0; partition < 24; partition++ {

		partitionConsumer, err := consumer.ConsumePartition(config.topic, int32(partition), sarama.OffsetNewest)
		if err != nil {
			log.Fatalln("Unable to create partition consumer", err)
		}

		wg.Add(1)

		go PullFromTopic(partitionConsumer, transferChan, signals, &wg)
		log.Println("Started consumer for partition ", partition)

		wg.Add(2)
		go PushToTopic(producer, transferChan, signals, &wg)
		log.Println("Started producer")
		go PushToTopic(producer, transferChan, signals, &wg)
		log.Println("Started producer")
	}

	go MonitorChan(transferChan, signals, &wg)

	wg.Wait()

	log.Println("All threads done, closing clients and ending")
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/go-redis/redis"
	"net/http"
	"html/template"
	"strconv"
	"time"
	"fmt"
	"log"
)

// Change these settings as needed
var client = redis.NewClient(&redis.Options{
          Addr:     "localhost:6379",
          Password: "",
          DB:       0,
})

// Load template into RAM; only one request to logs.html is necessary per server
var logTemplate = template.Must(template.ParseFiles("logs.html"))

// Main handler; displays web page.
func logsHandler(w http.ResponseWriter, r *http.Request) {
	// Execute values into template
	err := logTemplate.Execute(w, nil)
	// If the server can't access the template, bail
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

// I don't like that there are four handlers doing essentially the same thing;
// Could at least use just 1 function.
// Clean-up later.
func nuiHandler(w http.ResponseWriter, r *http.Request) {
	str, err := client.Get("nui").Result()
	if err != nil {
		log.Fatal(err)
	}
	// Shouldn't be vulnerable to a format string attack since the input
	// is sanitized before getting to this point, anyway
	fmt.Fprint(w, str)
}

func forestHandler(w http.ResponseWriter, r *http.Request) {
	str, err := client.Get("forest").Result()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Fprint(w, str)
}

func atcHandler(w http.ResponseWriter, r *http.Request) {
	str, err := client.Get("atc").Result()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Fprint(w, str)
}

func btcHandler(w http.ResponseWriter, r *http.Request) {
	str, err := client.Get("btc").Result()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Fprint(w, str)
}

func countDown(key string) {
	// Get the value of key
	str, err := client.Get(key).Result()
	if err != nil {
		log.Fatal(err)
	}
	// Convert key's value to an integer, so it can be
	// decremented and compared to other ints.
	val, err := strconv.Atoi(str)
	// If an error occurs (if the string is not a number),
	// return. Should never happen, since the value has
	// already been parsed by the save function
	if err != nil {
		return
	}
	for ; val >= 0; val-- {
		// Save the value to the database every interval of 5
		// Can cause some weird behaviour, but saves insignificant
		// amounts of stress on server :^)
		if val % 5 == 0 || val == 0 {
			// Error value is ignored for now.
			err := client.Set(key, val, 0).Err()
			if err != nil {
				log.Fatal(err)
			}
		}
		// Slightly inaccurate if I remember correctly
		// For our purposes, should work fine
		time.Sleep(time.Second)
	}
	return
}

func saveHandler(w http.ResponseWriter, r *http.Request) {
	// Initializes r to be parsed
	err := r.ParseForm()
	if err != nil {
		log.Fatal(err)
	}
	var key string
	// There has to be a better way
	for key, _ = range r.Form {
		;
	}
	val := r.FormValue(key)
	// This should, as a side effect, protect against most exploits, like
	// XSS attacks etc. since only numbers are allowed.
	// If something like <script> is inserted, it'll just
	// return an error.
	newVal, err := strconv.Atoi(val)
	seconds := newVal*60
	// If the value is not able to be converted to an int
	// (if it's not a number)
	// return.
	// Error is already handed client side; anyone entering
	// invalid input is bypassing said error
	if err != nil {
		return
	}
	// If no value is entered
	// Should also be illegal through normal methods
	if newVal == 0 {
		// Redirect back to main page
		http.Redirect(w, r, "/logs/", http.StatusFound)
		return
	}
	// If value is greater than 1 hour 30 minutes,
	// redirect and return
	// To ensure times greater than the maximum value cannot be entered
	if seconds > 5400 {
		http.Redirect(w, r, "/logs/", http.StatusFound)
		return
	}
	// Save the value in the database, converting minutes to seconds
	err = client.Set(key, (newVal*60), 0).Err()
	if err != nil {
		log.Fatal(err)
	}
	// Start countdown for newly saved value
	go countDown(key)
	http.Redirect(w, r, "/logs/", http.StatusFound)
}

func main() {
	http.HandleFunc("/logs/", logsHandler)
	http.HandleFunc("/save/", saveHandler)
	http.HandleFunc("/nuitimer/", nuiHandler)
	http.HandleFunc("/foresttimer/", forestHandler)
	http.HandleFunc("/atctimer/", atcHandler)
	http.HandleFunc("/btctimer/", btcHandler)
	// FileServer handler
	http.Handle("/img/", http.StripPrefix("/img/", http.FileServer(http.Dir("./img/"))))
	// Starts the countdown for all four functions on server start
	// these will self-terminate if unnecessary
	go countDown("nui")
	go countDown("forest")
	go countDown("atc")
	go countDown("btc")
	// Start listening on port 80 (default port for http), logging
	// Fatal errors (and closing upon error)
	log.Fatal(http.ListenAndServe(":80", nil))
}
<commit_msg>Remove unnecessary values<commit_after>package main

import (
	"github.com/go-redis/redis"
	"net/http"
	"html/template"
	"strconv"
	"time"
	"fmt"
	"log"
)

// Change these settings as needed
var client = redis.NewClient(&redis.Options{
          Addr:     "localhost:6379",
          Password: "",
          DB:       0,
})

// Load template into RAM; only one request to logs.html is necessary per server
var logTemplate = template.Must(template.ParseFiles("logs.html"))

// Main handler; displays web page.
func logsHandler(w http.ResponseWriter, r *http.Request) {
	// Execute values into template
	err := logTemplate.Execute(w, nil)
	// If the server can't access the template, bail
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

// I don't like that there are four handlers doing essentially the same thing;
// Could at least use just 1 function.
// Clean-up later.
func nuiHandler(w http.ResponseWriter, r *http.Request) {
	str, err := client.Get("nui").Result()
	if err != nil {
		log.Fatal(err)
	}
	// Shouldn't be vulnerable to a format string attack since the input
	// is sanitized before getting to this point, anyway
	fmt.Fprint(w, str)
}

func forestHandler(w http.ResponseWriter, r *http.Request) {
	str, err := client.Get("forest").Result()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Fprint(w, str)
}

func atcHandler(w http.ResponseWriter, r *http.Request) {
	str, err := client.Get("atc").Result()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Fprint(w, str)
}

func btcHandler(w http.ResponseWriter, r *http.Request) {
	str, err := client.Get("btc").Result()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Fprint(w, str)
}

func countDown(key string) {
	// Get the value of key
	str, err := client.Get(key).Result()
	if err != nil {
		log.Fatal(err)
	}
	// Convert key's value to an integer, so it can be
	// decremented and compared to other ints.
	val, err := strconv.Atoi(str)
	// If an error occurs (if the string is not a number),
	// return. Should never happen, since the value has
	// already been parsed by the save function
	if err != nil {
		return
	}
	for ; val >= 0; val-- {
		// Save the value to the database every interval of 5
		// Can cause some weird behaviour, but saves insignificant
		// amounts of stress on server :^)
		if val % 5 == 0 || val == 0 {
			// Error value is ignored for now.
			err := client.Set(key, val, 0).Err()
			if err != nil {
				log.Fatal(err)
			}
		}
		// Slightly inaccurate if I remember correctly
		// For our purposes, should work fine
		time.Sleep(time.Second)
	}
	return
}

func saveHandler(w http.ResponseWriter, r *http.Request) {
	// Initializes r to be parsed
	err := r.ParseForm()
	if err != nil {
		log.Fatal(err)
	}
	var key string
	// There has to be a better way
	for key = range r.Form {
		;
	}
	val := r.FormValue(key)
	// This should, as a side effect, protect against most exploits, like
	// XSS attacks etc. since only numbers are allowed.
	// If something like <script> is inserted, it'll just
	// return an error.
	newVal, err := strconv.Atoi(val)
	seconds := newVal*60
	// If the value is not able to be converted to an int
	// (if it's not a number)
	// return.
	// Error is already handed client side; anyone entering
	// invalid input is bypassing said error
	if err != nil {
		http.Redirect(w, r, "/logs/", http.StatusFound)
		return
	}
	// If no value is entered
	// Should also be illegal through normal methods
	if newVal == 0 {
		// Redirect back to main page
		http.Redirect(w, r, "/logs/", http.StatusFound)
		return
	}
	// If value is greater than 1 hour 30 minutes,
	// redirect and return
	// To ensure times greater than the maximum value cannot be entered
	if seconds > 5400 {
		http.Redirect(w, r, "/logs/", http.StatusFound)
		return
	}
	// Save the value in the database, converting minutes to seconds
	err = client.Set(key, (newVal*60), 0).Err()
	if err != nil {
		log.Fatal(err)
	}
	// Start countdown for newly saved value
	go countDown(key)
	http.Redirect(w, r, "/logs/", http.StatusFound)
}

func main() {
	http.HandleFunc("/logs/", logsHandler)
	http.HandleFunc("/save/", saveHandler)
	http.HandleFunc("/nuitimer/", nuiHandler)
	http.HandleFunc("/foresttimer/", forestHandler)
	http.HandleFunc("/atctimer/", atcHandler)
	http.HandleFunc("/btctimer/", btcHandler)
	// FileServer handler
	http.Handle("/img/", http.StripPrefix("/img/", http.FileServer(http.Dir("./img/"))))
	// Starts the countdown for all four functions on server start
	// these will self-terminate if unnecessary
	go countDown("nui")
	go countDown("forest")
	go countDown("atc")
	go countDown("btc")
	// Start listening on port 80 (default port for http), logging
	// Fatal errors (and closing upon error)
	log.Fatal(http.ListenAndServe(":80", nil))
}
<|endoftext|>


<commit_before>package main

func main() {
	println("Hello Server World!!!")
}
<commit_msg>One last commit test<commit_after>package main

func main() {
	println("Hello Server World!!!!!!!")
}
<|endoftext|>


<commit_before>// An app that draws and moves triangles on the screen and pushes them on to
// other screens.
//
// See https://github.com/asimshankar/triangles
package main

import (
	"github.com/asimshankar/triangles/spec"
	"golang.org/x/mobile/app"
	"golang.org/x/mobile/event/lifecycle"
	"golang.org/x/mobile/event/paint"
	"golang.org/x/mobile/event/size"
	"golang.org/x/mobile/event/touch"
	"golang.org/x/mobile/gl"
	"log"
	"math/rand"
)

func main() {
	app.Main(func(a app.App) {
		var (
			myGL        *GL
			myTriangles []*spec.Triangle
			sz          size.Event

			touchCount int // Number of touch events before the touch stopped
			touchStart touch.Event

			chMyScreen      = make(chan *spec.Triangle) // New triangles to draw on my screen
			leftScreen      = newOtherScreen(nil, chMyScreen)
			rightScreen     = newOtherScreen(nil, chMyScreen)
			networkChannels = SetupNetwork(chMyScreen)

			myR, myG, myB = randomColor()
			spawnTriangle = func() {
				myTriangles = append(myTriangles, &spec.Triangle{R: myR, G: myG, B: myB})
			}
		)
		spawnTriangle()
		for {
			select {
			case err := <-networkChannels.Ready:
				if err != nil {
					log.Panic(err)
				}
				networkChannels.Ready = nil // To stop this select clause from being hit.
			case ch := <-networkChannels.NewLeftScreen:
				leftScreen.close()
				leftScreen = newOtherScreen(ch, chMyScreen)
			case ch := <-networkChannels.NewRightScreen:
				rightScreen.close()
				rightScreen = newOtherScreen(ch, chMyScreen)
			case t := <-chMyScreen:
				myTriangles = append(myTriangles, t)
			case e := <-a.Events():
				switch e := a.Filter(e).(type) {
				case lifecycle.Event:
					switch e.Crosses(lifecycle.StageVisible) {
					case lifecycle.CrossOn:
						glctx, _ := e.DrawContext.(gl.Context)
						var err error
						if myGL, err = NewGL(glctx); err != nil {
							log.Panic(err)
						}
						a.Send(paint.Event{})
					case lifecycle.CrossOff:
						if exitOnLifecycleCrossOff() {
							return
						}
						myGL.Release()
						myGL = nil
					}
				case paint.Event:
					if e.External {
						continue
					}
					var mine, left, right []*spec.Triangle
					for _, t := range myTriangles {
						moveTriangle(t)
						switch {
						case t.X <= -1:
							left = append(left, t)
						case t.X >= 1:
							right = append(right, t)
						default:
							mine = append(mine, t)
						}
					}
					if len(left) > 0 {
						go leftScreen.send(left)
					}
					if len(right) > 0 {
						go rightScreen.send(right)
					}
					myTriangles = mine
					myGL.Paint(myTriangles)
					a.Publish()
					a.Send(paint.Event{})
				case size.Event:
					sz = e
				case touch.Event:
					switch e.Type {
					case touch.TypeBegin:
						touchCount = 1
						touchStart = e
					case touch.TypeMove:
						touchCount++
					case touch.TypeEnd:
						if touchCount > maxTouchCount {
							log.Printf("Ignoring long touch (%d > %d)", touchCount, maxTouchCount)
							touchCount = 0
							break
						}
						touchCount = 0
						// Find the closest triangle to the touch start and adjust its velocity.
						var (
							// Normalize the touch coordinates to the triangle coordinates ([-1,1])
							x, y          = touch2coords(touchStart, sz)
							closestT      *spec.Triangle
							minDistanceSq float32
						)
						for idx, t := range myTriangles {
							if d := (x-t.X)*(x-t.X) + (y-t.Y)*(y-t.Y); d < minDistanceSq || idx == 0 {
								minDistanceSq = d
								closestT = t
							}
						}
						if closestT != nil {
							closestT.Dx += (e.X - touchStart.X) / float32(sz.WidthPx)
							closestT.Dy += (e.Y - touchStart.Y) / float32(sz.HeightPx)
						}
					}
				}
			}
		}
	})
}

type otherScreen struct {
	chTriangles chan<- *spec.Triangle
	chLost      chan struct{}
	chSelf      chan<- *spec.Triangle
}

func newOtherScreen(other, self chan<- *spec.Triangle) *otherScreen {
	return &otherScreen{
		chTriangles: other,
		chLost:      make(chan struct{}),
		chSelf:      self,
	}
}

func (s *otherScreen) close() {
	close(s.chLost)
}

func (s *otherScreen) send(triangles []*spec.Triangle) {
	if s.chTriangles == nil {
		for _, t := range triangles {
			returnTriangle(t, s.chSelf)
		}
	}
	for i, t := range triangles {
		select {
		case <-s.chLost:
			// Lost the other screen, so reflect the remaining triangles back onto my screen.
			for i < len(triangles) {
				returnTriangle(t, s.chSelf)
				i++
			}
			return
		case s.chTriangles <- t:
		}
	}
}

// touch2coords transforms coordinates from the touch.Event coordinate system
// to the GL and Triangles coordinate system.
func touch2coords(t touch.Event, sz size.Event) (x, y float32) {
	return 2*t.X/float32(sz.WidthPx) - 1, 2*t.Y/float32(sz.HeightPx) - 1
}

func moveTriangle(t *spec.Triangle) {
	t.X = t.X + t.Dx*timeBetweenPaints
	t.Y = t.Y + (t.Dy-gravity)*timeBetweenPaints
	if t.Y <= -1 {
		t.Dy = -1 * t.Dy
		t.Y = -1
	} else if t.Y >= 1-triangleHeight {
		t.Dy = -1 * t.Dy
		t.Y = 1 - triangleHeight
	}
}

func returnTriangle(t *spec.Triangle, myScreen chan<- *spec.Triangle) {
	t.Dx = -1 * t.Dx
	moveTriangle(t)
	myScreen <- t
}

func randomColor() (r, g, b float32) {
	random := func() float32 {
		// Pick a number between [30, 255] and normalize it to [0, 1]
		return float32(rand.Intn(255-29)+30) / 255.0
	}
	return random(), random(), random()
}

const (
	maxTouchCount     = 30
	gravity           = 0.1
	timeBetweenPaints = 0.1
)
<commit_msg>There can be multiple simultaneously active touch events, and touch.Event.Sequence is provided for that, use it to avoid multiple finger taps from messing up the velocity calculations.<commit_after>// An app that draws and moves triangles on the screen and pushes them on to
// other screens.
//
// See https://github.com/asimshankar/triangles
package main

import (
	"github.com/asimshankar/triangles/spec"
	"golang.org/x/mobile/app"
	"golang.org/x/mobile/event/lifecycle"
	"golang.org/x/mobile/event/paint"
	"golang.org/x/mobile/event/size"
	"golang.org/x/mobile/event/touch"
	"golang.org/x/mobile/gl"
	"log"
	"math/rand"
)

func main() {
	app.Main(func(a app.App) {
		var (
			myGL        *GL
			myTriangles []*spec.Triangle
			sz          size.Event
			touches     = make(map[touch.Sequence]*touchEvents) // Active touch events

			chMyScreen      = make(chan *spec.Triangle) // New triangles to draw on my screen
			leftScreen      = newOtherScreen(nil, chMyScreen)
			rightScreen     = newOtherScreen(nil, chMyScreen)
			networkChannels = SetupNetwork(chMyScreen)

			myR, myG, myB = randomColor()
			spawnTriangle = func() {
				myTriangles = append(myTriangles, &spec.Triangle{R: myR, G: myG, B: myB})
			}
		)
		spawnTriangle()
		for {
			select {
			case err := <-networkChannels.Ready:
				if err != nil {
					log.Panic(err)
				}
				networkChannels.Ready = nil // To stop this select clause from being hit.
			case ch := <-networkChannels.NewLeftScreen:
				leftScreen.close()
				leftScreen = newOtherScreen(ch, chMyScreen)
			case ch := <-networkChannels.NewRightScreen:
				rightScreen.close()
				rightScreen = newOtherScreen(ch, chMyScreen)
			case t := <-chMyScreen:
				myTriangles = append(myTriangles, t)
			case e := <-a.Events():
				switch e := a.Filter(e).(type) {
				case lifecycle.Event:
					switch e.Crosses(lifecycle.StageVisible) {
					case lifecycle.CrossOn:
						glctx, _ := e.DrawContext.(gl.Context)
						var err error
						if myGL, err = NewGL(glctx); err != nil {
							log.Panic(err)
						}
						a.Send(paint.Event{})
					case lifecycle.CrossOff:
						if exitOnLifecycleCrossOff() {
							return
						}
						myGL.Release()
						myGL = nil
					}
				case paint.Event:
					if e.External {
						continue
					}
					var mine, left, right []*spec.Triangle
					for _, t := range myTriangles {
						moveTriangle(t)
						switch {
						case t.X <= -1:
							left = append(left, t)
						case t.X >= 1:
							right = append(right, t)
						default:
							mine = append(mine, t)
						}
					}
					if len(left) > 0 {
						go leftScreen.send(left)
					}
					if len(right) > 0 {
						go rightScreen.send(right)
					}
					myTriangles = mine
					myGL.Paint(myTriangles)
					a.Publish()
					a.Send(paint.Event{})
				case size.Event:
					sz = e
				case touch.Event:
					switch e.Type {
					case touch.TypeBegin:
						touches[e.Sequence] = &touchEvents{Start: e}
					case touch.TypeMove:
						touches[e.Sequence].Count++
					case touch.TypeEnd:
						tch := touches[e.Sequence]
						delete(touches, e.Sequence)
						if c := tch.Count; c > maxTouchCount {
							log.Printf("Ignoring long touch (%d > %d)", c, maxTouchCount)
							break
						}
						// Find the closest triangle to the touch start and adjust its velocity.
						var (
							// Normalize the touch coordinates to the triangle coordinates ([-1,1])
							x, y          = touch2coords(tch.Start, sz)
							closestT      *spec.Triangle
							minDistanceSq float32
						)
						for idx, t := range myTriangles {
							if d := (x-t.X)*(x-t.X) + (y-t.Y)*(y-t.Y); d < minDistanceSq || idx == 0 {
								minDistanceSq = d
								closestT = t
							}
						}
						if closestT != nil {
							closestT.Dx += (e.X - tch.Start.X) / float32(sz.WidthPx)
							closestT.Dy += (e.Y - tch.Start.Y) / float32(sz.HeightPx)
						}
					}
				}
			}
		}
	})
}

type touchEvents struct {
	Start touch.Event // Starting event
	Count int         // Number of moves before the end event
}

type otherScreen struct {
	chTriangles chan<- *spec.Triangle
	chLost      chan struct{}
	chSelf      chan<- *spec.Triangle
}

func newOtherScreen(other, self chan<- *spec.Triangle) *otherScreen {
	return &otherScreen{
		chTriangles: other,
		chLost:      make(chan struct{}),
		chSelf:      self,
	}
}

func (s *otherScreen) close() {
	close(s.chLost)
}

func (s *otherScreen) send(triangles []*spec.Triangle) {
	if s.chTriangles == nil {
		for _, t := range triangles {
			returnTriangle(t, s.chSelf)
		}
	}
	for i, t := range triangles {
		select {
		case <-s.chLost:
			// Lost the other screen, so reflect the remaining triangles back onto my screen.
			for i < len(triangles) {
				returnTriangle(t, s.chSelf)
				i++
			}
			return
		case s.chTriangles <- t:
		}
	}
}

// touch2coords transforms coordinates from the touch.Event coordinate system
// to the GL and Triangles coordinate system.
func touch2coords(t touch.Event, sz size.Event) (x, y float32) {
	return 2*t.X/float32(sz.WidthPx) - 1, 2*t.Y/float32(sz.HeightPx) - 1
}

func moveTriangle(t *spec.Triangle) {
	t.X = t.X + t.Dx*timeBetweenPaints
	t.Y = t.Y + (t.Dy-gravity)*timeBetweenPaints
	if t.Y <= -1 {
		t.Dy = -1 * t.Dy
		t.Y = -1
	} else if t.Y >= 1-triangleHeight {
		t.Dy = -1 * t.Dy
		t.Y = 1 - triangleHeight
	}
}

func returnTriangle(t *spec.Triangle, myScreen chan<- *spec.Triangle) {
	t.Dx = -1 * t.Dx
	moveTriangle(t)
	myScreen <- t
}

func randomColor() (r, g, b float32) {
	random := func() float32 {
		// Pick a number between [30, 255] and normalize it to [0, 1]
		return float32(rand.Intn(255-29)+30) / 255.0
	}
	return random(), random(), random()
}

const (
	maxTouchCount     = 30
	gravity           = 0.1
	timeBetweenPaints = 0.1
)
<|endoftext|>


<commit_before>package main

import (

	//	"github.com/go-fsnotify/fsnotify"
	"bytes"
	"fmt"
	"github.com/coreos/etcd/client"
	"github.com/rjeczalik/notify"
	"github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"
	"time"
)

const MARK_FILE_NAME = ".ETCDIR_MARK_FILE_HUGSDBDND" // Name of lock-file for prevent bad things
const DEFAULT_DIRMODE = 0700
const DEFAULT_FILEMODE = 0600
const EVENT_CHANNEL_LEN = 1000
const LOCK_INTERVAL = time.Second // Wait since previous touch to can get lock directory once more.

type fileChangeEvent struct {
	Path      string
	IsDir     bool
	IsRemoved bool
	Content   []byte
}

/*
Monitoring changes in etcd server.
It designed for run in separate goroutine.
 */
func etcdMon(config client.Config, bus chan fileChangeEvent, startIndex uint64) {
	c, err := client.New(config)
	if err != nil {
		panic(err)
	}
	kapi := client.NewKeysAPI(c)
	var nextEvent uint64 = startIndex
	for {
		response, err := kapi.Watcher("/", &client.WatcherOptions{AfterIndex: nextEvent, Recursive: true}).Next(context.Background())
		if err != nil {
			log.Println(err)
			time.Sleep(time.Second)
			continue
		}
		nextEvent = response.Index
		if response.Action == "delete" {
			bus <- fileChangeEvent{Path: response.Node.Key, IsRemoved: true, IsDir: response.Node.Dir}
			continue
		}
		if response.Node.Dir {
			bus <- fileChangeEvent{Path: response.Node.Key, IsDir: response.Node.Dir}
			continue
		}
		bus <- fileChangeEvent{Path: response.Node.Key, Content: []byte(response.Node.Value)}
	}
}

/*
Monitoring changes in file system.
It designed for run in separate goroutine.
 */
func fileMon(path string, bus chan fileChangeEvent) {
	// Make the channel buffered to ensure no event is dropped. Notify will drop
	// an event if the receiver is not able to keep up the sending pace.
	c := make(chan notify.EventInfo, 1)

	// Set up a watchpoint listening on events within current working directory.
	// Dispatch each create and remove events separately to c.
	if err := notify.Watch(path+"/...", c, notify.All); err != nil {
		log.Fatal(err)
	}
	defer notify.Stop(c)

	// Block until an event is received.
	for {
		event := <-c
		fstat, err := os.Lstat(event.Path())
		if os.IsNotExist(err) {
			bus <- fileChangeEvent{Path: event.Path(), IsRemoved: true}
			continue
		}

		if err != nil {
			log.Println(err)
			continue
		}

		if fstat.IsDir() {
			bus <- fileChangeEvent{Path: event.Path(), IsDir: true}
			continue
		}

		content, err := ioutil.ReadFile(event.Path())
		if err != nil {
			log.Println(err)
		}
		bus <- fileChangeEvent{Path: event.Path(), Content: content}
	}
}

/*
Clear dir and dump content of etcd to the dir.
ATTENTION: the function REMOVE ALL CONTENT of the dir.
 */
func firstSyncEtcDir(etcdConfig client.Config, dir string) (etcdIndex uint64) {
	dirFile, err := os.Open(dir)
	if err != nil {
		panic(err)
	}
	dirNames, err := dirFile.Readdirnames(-1)
	if err != nil {
		panic(err)
	}
	for _, item := range dirNames {
		err = os.RemoveAll(filepath.Join(dir, item))
		if err != nil {
			log.Println("I can't remove: ", filepath.Join(dir, item))
			panic(err)
		}
	}
	err = ioutil.WriteFile(filepath.Join(dir, MARK_FILE_NAME), []byte{}, DEFAULT_FILEMODE)
	if err != nil {
		log.Println("I can't create touchfile: ", filepath.Join(dir, MARK_FILE_NAME))
		return
	}

	etcdClient, err := client.New(etcdConfig)
	if err != nil {
		log.Println("Can't create etcdClient: ", err)
		panic(err)
	}

	kapi := client.NewKeysAPI(etcdClient)
	response, err := kapi.Get(context.Background(), "/", &client.GetOptions{Recursive: true, Quorum: true})
	if err != nil {
		fmt.Println("I can't get initial etcd state: ", err)
		panic(err)
	}
	writeNodeToDir(dir, response.Node)
	return response.Index
}

/*
Check if dir can be locked and lock it.
Return true if lock succesfully.
 */
func lock(dir string)bool{
	lockFile := filepath.Join(dir, MARK_FILE_NAME)
	stat, err := os.Stat(lockFile)
	if err != nil {
		log.Println("Can't stat lock file: ", lockFile, err)
		return false
	}
	if time.Now().Sub(stat.ModTime()) <= LOCK_INTERVAL {
		return false
	}

	pid := os.Getpid()
	go func(){
		for {
			mess := fmt.Sprint("PID: ", pid, "\nLAST TIME: ", time.Now().String())
			ioutil.WriteFile(lockFile, []byte(mess), DEFAULT_FILEMODE)
			time.Sleep(LOCK_INTERVAL / 3)
		}
	}()
	return true
}

func main() {
	if len(os.Args) != 2 {
		printUsage()
		return
	}

	dir, err := filepath.Abs(os.Args[1])
	if err != nil {
		panic(err)
	}
	dirStat, err := os.Stat(dir)
	if err != nil {
		panic(err)
	}
	if !dirStat.IsDir() {
		fmt.Printf("'%v' is not dir.\n", dir)
		return
	}

	_, err = os.Stat(filepath.Join(dir, MARK_FILE_NAME))
	if os.IsNotExist(err) {
		fmt.Printf(`You have to create file '%[1]v' before usage dir as syncdir. You can do it by command:
echo > %[1]v
`, filepath.Join(dir, MARK_FILE_NAME))
		return
	}

	if !lock(dir){
		log.Println("Can't get lock. May be another instance work with the dir")
		return
	}

	etcdConfig := client.Config{Endpoints: []string{"http://127.0.0.1:4001"}}
	etcdStartFrom := firstSyncEtcDir(etcdConfig, dir)

	etcdChan := make(chan fileChangeEvent, EVENT_CHANNEL_LEN)
	fsChan := make(chan fileChangeEvent, EVENT_CHANNEL_LEN)

	go fileMon(dir, fsChan)
	go etcdMon(etcdConfig, etcdChan, etcdStartFrom)

	syncProcess(dir, etcdConfig, etcdChan, fsChan)
}

func printUsage() {
	fmt.Printf(`%v <syncdir>
syncdir - directory for show etcd content. ALL CURRENT CONTENT WILL BE LOST.
you have to create file '{1}' in syncdir before can use it.
`, os.Args[0], MARK_FILE_NAME)
}

/*
function for replicate changes between etcd and file system.
It is never returned function.
It can be run in separate goroutine or call it as last function in main()
 */
func syncProcess(dir string, etcdConfig client.Config, etcdChan, fsChan <-chan fileChangeEvent) {
	etcdClient, err := client.New(etcdConfig)
	if err != nil {
		panic(err)
	}
	kapi := client.NewKeysAPI(etcdClient)
	ctx := context.Background()
	fsMarkFile := filepath.Join(dir, MARK_FILE_NAME)
	for {
		var event fileChangeEvent
		select {
		case event = <-etcdChan:
			fsPath := filepath.Join(dir, event.Path)
			if fsPath == fsMarkFile {
				continue
			}
			switch {
			case event.IsRemoved:
				err := os.RemoveAll(fsPath)
				if err != nil {
					log.Println("Can't remove: ", fsPath, err)
				}
			case event.IsDir:
				err := os.Mkdir(fsPath, DEFAULT_DIRMODE)
				if err != nil && !os.IsExist(err){
					log.Println("Can't make dir: ", fsPath, err)
				}
			default:
				if content, err := ioutil.ReadFile(fsPath); err == nil {
					if bytes.Equal(content, event.Content) {
						// Skip if contents are equals
						continue
					}
				}
				err := ioutil.WriteFile(fsPath, event.Content, DEFAULT_FILEMODE)
				if err != nil {
					log.Println("Can't write file: ", fsPath, err)
				}
			}
		case event = <-fsChan:
			if event.Path == fsMarkFile {
				continue
			}
			etcdPath := event.Path[len(dir):]
			etcdPath = strings.Replace(etcdPath, "\\", "/", -1)
			switch {
			case event.IsRemoved:
				_, err := kapi.Delete(ctx, etcdPath, &client.DeleteOptions{Recursive: true})
				if err != nil {
					log.Println("Can't remove etcd: "+etcdPath, err)
				}
			case event.IsDir:
				_, err := kapi.Set(ctx, etcdPath, "", &client.SetOptions{Dir: true})
				if err != nil {
					log.Println("Can't create etcd dir: ", etcdPath, err)
				}
			default:
				if resp, err := kapi.Get(ctx, etcdPath, &client.GetOptions{Quorum: true}); err == nil {
					if bytes.Equal([]byte(resp.Node.Value), event.Content) {
						// Skip equal contents
						continue
					}
				}
				_, err := kapi.Set(ctx, etcdPath, string(event.Content), nil)
				if err != nil {
					log.Println("Can't set etcd value: ", etcdPath, err)
				}
			}
		}
	}
}

func writeNodeToDir(dir string, node *client.Node) {
	nodePath := filepath.Join(dir, node.Key)
	if node.Dir {
		err := os.Mkdir(nodePath, DEFAULT_DIRMODE)
		if err != nil && !os.IsExist(err) {
			log.Println("I can't create dir: ", nodePath)
			panic(err)
		}
		for _, item := range node.Nodes {
			writeNodeToDir(dir, item)
		}
	} else {
		err := ioutil.WriteFile(nodePath, []byte(node.Value), DEFAULT_FILEMODE)
		if err != nil {
			log.Println("I can't create file: ", nodePath)
			panic(err)
		}
	}
}
<commit_msg>add addr param<commit_after>package main

import (

	//	"github.com/go-fsnotify/fsnotify"
	"bytes"
	"fmt"
	"github.com/coreos/etcd/client"
	"github.com/rjeczalik/notify"
	"github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"
	"time"
)

const MARK_FILE_NAME = ".ETCDIR_MARK_FILE_HUGSDBDND" // Name of lock-file for prevent bad things
const DEFAULT_DIRMODE = 0777
const DEFAULT_FILEMODE = 0777
const EVENT_CHANNEL_LEN = 1000
const LOCK_INTERVAL = time.Second // Wait since previous touch to can get lock directory once more.

type fileChangeEvent struct {
	Path      string
	IsDir     bool
	IsRemoved bool
	Content   []byte
}

/*
Monitoring changes in etcd server.
It designed for run in separate goroutine.
 */
func etcdMon(config client.Config, bus chan fileChangeEvent, startIndex uint64) {
	c, err := client.New(config)
	if err != nil {
		panic(err)
	}
	kapi := client.NewKeysAPI(c)
	var nextEvent uint64 = startIndex
	for {
		response, err := kapi.Watcher("/", &client.WatcherOptions{AfterIndex: nextEvent, Recursive: true}).Next(context.Background())
		if err != nil {
			log.Println(err)
			time.Sleep(time.Second)
			continue
		}
		nextEvent = response.Index
		if response.Action == "delete" {
			bus <- fileChangeEvent{Path: response.Node.Key, IsRemoved: true, IsDir: response.Node.Dir}
			continue
		}
		if response.Node.Dir {
			bus <- fileChangeEvent{Path: response.Node.Key, IsDir: response.Node.Dir}
			continue
		}
		bus <- fileChangeEvent{Path: response.Node.Key, Content: []byte(response.Node.Value)}
	}
}

/*
Monitoring changes in file system.
It designed for run in separate goroutine.
 */
func fileMon(path string, bus chan fileChangeEvent) {
	// Make the channel buffered to ensure no event is dropped. Notify will drop
	// an event if the receiver is not able to keep up the sending pace.
	c := make(chan notify.EventInfo, 1)

	// Set up a watchpoint listening on events within current working directory.
	// Dispatch each create and remove events separately to c.
	if err := notify.Watch(path+"/...", c, notify.All); err != nil {
		log.Fatal(err)
	}
	defer notify.Stop(c)

	// Block until an event is received.
	for {
		event := <-c
		fstat, err := os.Lstat(event.Path())
		if os.IsNotExist(err) {
			bus <- fileChangeEvent{Path: event.Path(), IsRemoved: true}
			continue
		}

		if err != nil {
			log.Println(err)
			continue
		}

		if fstat.IsDir() {
			bus <- fileChangeEvent{Path: event.Path(), IsDir: true}
			continue
		}

		content, err := ioutil.ReadFile(event.Path())
		if err != nil {
			log.Println(err)
		}
		bus <- fileChangeEvent{Path: event.Path(), Content: content}
	}
}

/*
Clear dir and dump content of etcd to the dir.
ATTENTION: the function REMOVE ALL CONTENT of the dir.
 */
func firstSyncEtcDir(etcdConfig client.Config, dir string) (etcdIndex uint64) {
	dirFile, err := os.Open(dir)
	if err != nil {
		panic(err)
	}
	dirNames, err := dirFile.Readdirnames(-1)
	if err != nil {
		panic(err)
	}
	for _, item := range dirNames {
		err = os.RemoveAll(filepath.Join(dir, item))
		if err != nil {
			log.Println("I can't remove: ", filepath.Join(dir, item))
			panic(err)
		}
	}
	err = ioutil.WriteFile(filepath.Join(dir, MARK_FILE_NAME), []byte{}, DEFAULT_FILEMODE)
	if err != nil {
		log.Println("I can't create touchfile: ", filepath.Join(dir, MARK_FILE_NAME))
		return
	}

	etcdClient, err := client.New(etcdConfig)
	if err != nil {
		log.Println("Can't create etcdClient: ", err)
		panic(err)
	}

	kapi := client.NewKeysAPI(etcdClient)
	response, err := kapi.Get(context.Background(), "/", &client.GetOptions{Recursive: true, Quorum: true})
	if err != nil {
		fmt.Println("I can't get initial etcd state: ", err)
		panic(err)
	}
	writeNodeToDir(dir, response.Node)
	return response.Index
}

/*
Check if dir can be locked and lock it.
Return true if lock succesfully.
 */
func lock(dir string)bool{
	lockFile := filepath.Join(dir, MARK_FILE_NAME)
	stat, err := os.Stat(lockFile)
	if err != nil {
		log.Println("Can't stat lock file: ", lockFile, err)
		return false
	}
	if time.Now().Sub(stat.ModTime()) <= LOCK_INTERVAL {
		return false
	}

	pid := os.Getpid()
	go func(){
		for {
			mess := fmt.Sprint("PID: ", pid, "\nLAST TIME: ", time.Now().String())
			ioutil.WriteFile(lockFile, []byte(mess), DEFAULT_FILEMODE)
			time.Sleep(LOCK_INTERVAL / 3)
		}
	}()
	return true
}

func main() {
	if len(os.Args) < 2 {
		printUsage()
		return
	}

	dir, err := filepath.Abs(os.Args[1])
	if err != nil {
		panic(err)
	}
	dirStat, err := os.Stat(dir)
	if err != nil {
		panic(err)
	}
	if !dirStat.IsDir() {
		fmt.Printf("'%v' is not dir.\n", dir)
		return
	}

	_, err = os.Stat(filepath.Join(dir, MARK_FILE_NAME))
	if os.IsNotExist(err) {
		fmt.Printf(`You have to create file '%[1]v' before usage dir as syncdir. You can do it by command:
echo > %[1]v
`, filepath.Join(dir, MARK_FILE_NAME))
		return
	}

	if !lock(dir){
		log.Println("Can't get lock. May be another instance work with the dir")
		return
	}

	addr := "http://127.0.0.1:4001"
	if len(os.Args) > 2 {
		addr = os.Args[2]
	}

	fmt.Println(addr)
	etcdConfig := client.Config{Endpoints: []string{addr}}
	fmt.Println(etcdConfig)
	etcdStartFrom := firstSyncEtcDir(etcdConfig, dir)

	etcdChan := make(chan fileChangeEvent, EVENT_CHANNEL_LEN)
	fsChan := make(chan fileChangeEvent, EVENT_CHANNEL_LEN)

	go fileMon(dir, fsChan)
	go etcdMon(etcdConfig, etcdChan, etcdStartFrom)

	syncProcess(dir, etcdConfig, etcdChan, fsChan)
}

func printUsage() {
	fmt.Printf(`%v <syncdir>
syncdir - directory for show etcd content. ALL CURRENT CONTENT WILL BE LOST.
you have to create file '{1}' in syncdir before can use it.
`, os.Args[0], MARK_FILE_NAME)
}

/*
function for replicate changes between etcd and file system.
It is never returned function.
It can be run in separate goroutine or call it as last function in main()
 */
func syncProcess(dir string, etcdConfig client.Config, etcdChan, fsChan <-chan fileChangeEvent) {
	etcdClient, err := client.New(etcdConfig)
	if err != nil {
		panic(err)
	}
	kapi := client.NewKeysAPI(etcdClient)
	ctx := context.Background()
	fsMarkFile := filepath.Join(dir, MARK_FILE_NAME)
	for {
		var event fileChangeEvent
		select {
		case event = <-etcdChan:
			fsPath := filepath.Join(dir, event.Path)
			if fsPath == fsMarkFile {
				continue
			}
			switch {
			case event.IsRemoved:
				err := os.RemoveAll(fsPath)
				if err != nil {
					log.Println("Can't remove: ", fsPath, err)
				}
			case event.IsDir:
				err := os.Mkdir(fsPath, DEFAULT_DIRMODE)
				if err != nil && !os.IsExist(err){
					log.Println("Can't make dir: ", fsPath, err)
				}
			default:
				if content, err := ioutil.ReadFile(fsPath); err == nil {
					if bytes.Equal(content, event.Content) {
						// Skip if contents are equals
						continue
					}
				}
				err := ioutil.WriteFile(fsPath, event.Content, DEFAULT_FILEMODE)
				if err != nil {
					log.Println("Can't write file: ", fsPath, err)
				}
			}
		case event = <-fsChan:
			if event.Path == fsMarkFile {
				continue
			}
			etcdPath := event.Path[len(dir):]
			etcdPath = strings.Replace(etcdPath, "\\", "/", -1)
			switch {
			case event.IsRemoved:
				_, err := kapi.Delete(ctx, etcdPath, &client.DeleteOptions{Recursive: true})
				if err != nil {
					log.Println("Can't remove etcd: "+etcdPath, err)
				}
			case event.IsDir:
				_, err := kapi.Set(ctx, etcdPath, "", &client.SetOptions{Dir: true})
				if err != nil {
					log.Println("Can't create etcd dir: ", etcdPath, err)
				}
			default:
				if resp, err := kapi.Get(ctx, etcdPath, &client.GetOptions{Quorum: true}); err == nil {
					if bytes.Equal([]byte(resp.Node.Value), event.Content) {
						// Skip equal contents
						continue
					}
				}
				_, err := kapi.Set(ctx, etcdPath, string(event.Content), nil)
				if err != nil {
					log.Println("Can't set etcd value: ", etcdPath, err)
				}
			}
		}
	}
}

func writeNodeToDir(dir string, node *client.Node) {
	nodePath := filepath.Join(dir, node.Key)
	if node.Dir {
		err := os.Mkdir(nodePath, DEFAULT_DIRMODE)
		if err != nil && !os.IsExist(err) {
			log.Println("I can't create dir: ", nodePath)
			panic(err)
		}
		for _, item := range node.Nodes {
			writeNodeToDir(dir, item)
		}
	} else {
		err := ioutil.WriteFile(nodePath, []byte(node.Value), DEFAULT_FILEMODE)
		if err != nil {
			log.Println("I can't create file: ", nodePath)
			panic(err)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"os"

	"gopkg.in/alecthomas/kingpin.v2"
)

var (
	app            = kingpin.New("scm", "ssh config manager.")
	dump           = app.Command("dump", "")
	list           = app.Command("list", "List all Host.")
	show           = app.Command("show", "Dumps one section.")
	showHost       = show.Arg("host", "Name for a host").Required().String()
	add            = app.Command("add", "Add host")
	addHost        = add.Arg("host", "Name for a host").Required().String()
	addHostName    = add.Arg("hostname", "HostName of the specified host").Required().String()
	addUser        = add.Flag("user", "").Short('u').String()
	addPort        = add.Flag("port", "").Short('p').String()
	addIdentify    = add.Flag("identify", "").Short('i').String()
	addParams      = add.Flag("params", "").Short('P').StringMap()
	update         = app.Command("update", "")
	updateHost     = update.Arg("host", "").Required().String()
	updateHostName = update.Flag("hostname", "HostName of the specified host").Short('h').String()
	updateUser     = update.Flag("user", "").Short('u').String()
	updatePort     = update.Flag("port", "").Short('p').String()
	updateIdentify = update.Flag("identify", "").Short('i').String()
	updateParams   = update.Flag("params", "").Short('P').StringMap()
	move           = app.Command("mv", "Rename Host")
	moveOldHost    = move.Arg("old_host", "").Required().String()
	moveNewHost    = move.Arg("new_host", "").Required().String()
	cp             = app.Command("cp", "")
	cpOldHost      = cp.Arg("old_host", "").Required().String()
	cpNewHost      = cp.Arg("new_host", "").Required().String()
	remove         = app.Command("remove", "")
	removeHost     = remove.Arg("host", "").Required().String()
)

var (
	hosts           Hosts
	ssh_config_file = os.ExpandEnv("$HOME/.ssh/config")
)

func addCommand(name, ip, user, port, identify string, params map[string]string) {
	if hosts = hosts.addHost(name, ip, user, port, identify, params); hosts != nil {
		hosts.saveConfig(ssh_config_file)
	} else {
		log.Fatalf("host %s is already exist\n", name)
	}
}

func updateCommand(name, ip, user, port, identify string, params map[string]string) {
	if hosts = hosts.updateHost(name, ip, user, port, identify, params); hosts != nil {
		hosts.saveConfig(ssh_config_file)
	} else {
		log.Fatalf("host %s is not found.\n", name)
	}
}

func listCommand() {
	for _, v := range hosts.listHost() {
		fmt.Println(v)
	}
}

func showCommand(name string) {
	_, host := hosts.GetHost(name)
	fmt.Println(host)
}

func moveCommand(old_host, new_host string) {
	if hosts = hosts.moveHost(old_host, new_host); hosts != nil {
		hosts.saveConfig(ssh_config_file)
	} else {
		log.Fatalf("host %s is not found.\n", old_host)
	}
}

func cpCommand(old_host, new_host string) {
	if hosts = hosts.copyHost(old_host, new_host); hosts != nil {
		hosts.saveConfig(ssh_config_file)
	} else {
		log.Fatalf("host %s is not found.\n", old_host)
	}
}

func removeCommand(name string) {
	if hosts = hosts.removeHost(name); hosts != nil {
		hosts.saveConfig(ssh_config_file)
	} else {
		log.Fatalf("host %s is not found.\n", name)
	}
}

func main() {
	file, err := os.Open(ssh_config_file)
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	hosts, err = Parse(file)
	if err != nil {
		log.Fatal(err)
	}

	switch kingpin.MustParse(app.Parse(os.Args[1:])) {
	case dump.FullCommand():
		fmt.Print(hosts)
	case show.FullCommand():
		showCommand(*showHost)
	case add.FullCommand():
		addCommand(*addHost, *addHostName, *addUser, *addPort, *addIdentify, *addParams)
	case list.FullCommand():
		listCommand()
	case update.FullCommand():
		updateCommand(*updateHost, *updateHostName, *updateUser, *updatePort, *updateIdentify, *updateParams)
	case move.FullCommand():
		moveCommand(*moveOldHost, *moveNewHost)
	case cp.FullCommand():
		cpCommand(*cpOldHost, *cpNewHost)
	case remove.FullCommand():
		removeCommand(*removeHost)
	}
}
<commit_msg>add place holder to params flag<commit_after>package main

import (
	"fmt"
	"log"
	"os"

	"gopkg.in/alecthomas/kingpin.v2"
)

var (
	app            = kingpin.New("scm", "ssh config manager.")
	dump           = app.Command("dump", "")
	list           = app.Command("list", "List all Host.")
	show           = app.Command("show", "Dumps one section.")
	showHost       = show.Arg("host", "Name for a host").Required().String()
	add            = app.Command("add", "Add host")
	addHost        = add.Arg("host", "Name for a host").Required().String()
	addHostName    = add.Arg("hostname", "HostName of the specified host").Required().String()
	addUser        = add.Flag("user", "").Short('u').String()
	addPort        = add.Flag("port", "").Short('p').String()
	addIdentify    = add.Flag("identify", "").Short('i').String()
	addParams      = add.Flag("params", "").PlaceHolder("KEY:VALUE").Short('P').StringMap()
	update         = app.Command("update", "")
	updateHost     = update.Arg("host", "").Required().String()
	updateHostName = update.Flag("hostname", "HostName of the specified host").Short('h').String()
	updateUser     = update.Flag("user", "").Short('u').String()
	updatePort     = update.Flag("port", "").Short('p').String()
	updateIdentify = update.Flag("identify", "").Short('i').String()
	updateParams   = update.Flag("params", "").PlaceHolder("KEY:VALUE").Short('P').StringMap()
	move           = app.Command("mv", "Rename Host")
	moveOldHost    = move.Arg("old_host", "").Required().String()
	moveNewHost    = move.Arg("new_host", "").Required().String()
	cp             = app.Command("cp", "")
	cpOldHost      = cp.Arg("old_host", "").Required().String()
	cpNewHost      = cp.Arg("new_host", "").Required().String()
	remove         = app.Command("remove", "")
	removeHost     = remove.Arg("host", "").Required().String()
)

var (
	hosts           Hosts
	ssh_config_file = os.ExpandEnv("$HOME/.ssh/config")
)

func addCommand(name, ip, user, port, identify string, params map[string]string) {
	if hosts = hosts.addHost(name, ip, user, port, identify, params); hosts != nil {
		hosts.saveConfig(ssh_config_file)
	} else {
		log.Fatalf("host %s is already exist\n", name)
	}
}

func updateCommand(name, ip, user, port, identify string, params map[string]string) {
	if hosts = hosts.updateHost(name, ip, user, port, identify, params); hosts != nil {
		hosts.saveConfig(ssh_config_file)
	} else {
		log.Fatalf("host %s is not found.\n", name)
	}
}

func listCommand() {
	for _, v := range hosts.listHost() {
		fmt.Println(v)
	}
}

func showCommand(name string) {
	_, host := hosts.GetHost(name)
	fmt.Println(host)
}

func moveCommand(old_host, new_host string) {
	if hosts = hosts.moveHost(old_host, new_host); hosts != nil {
		hosts.saveConfig(ssh_config_file)
	} else {
		log.Fatalf("host %s is not found.\n", old_host)
	}
}

func cpCommand(old_host, new_host string) {
	if hosts = hosts.copyHost(old_host, new_host); hosts != nil {
		hosts.saveConfig(ssh_config_file)
	} else {
		log.Fatalf("host %s is not found.\n", old_host)
	}
}

func removeCommand(name string) {
	if hosts = hosts.removeHost(name); hosts != nil {
		hosts.saveConfig(ssh_config_file)
	} else {
		log.Fatalf("host %s is not found.\n", name)
	}
}

func main() {
	file, err := os.Open(ssh_config_file)
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	hosts, err = Parse(file)
	if err != nil {
		log.Fatal(err)
	}

	switch kingpin.MustParse(app.Parse(os.Args[1:])) {
	case dump.FullCommand():
		fmt.Print(hosts)
	case show.FullCommand():
		showCommand(*showHost)
	case add.FullCommand():
		addCommand(*addHost, *addHostName, *addUser, *addPort, *addIdentify, *addParams)
	case list.FullCommand():
		listCommand()
	case update.FullCommand():
		updateCommand(*updateHost, *updateHostName, *updateUser, *updatePort, *updateIdentify, *updateParams)
	case move.FullCommand():
		moveCommand(*moveOldHost, *moveNewHost)
	case cp.FullCommand():
		cpCommand(*cpOldHost, *cpNewHost)
	case remove.FullCommand():
		removeCommand(*removeHost)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/user"
	"path"
	"path/filepath"
	"strings"
	"text/template"
)

const (
	templatesExtension = ".tpl"
	templatesFolder    = ".plate"
)

type plate struct {
	srcPath string
	outPath string
}

func newPlate(srcPath, outPath string) *plate {
	return &plate{
		srcPath: srcPath,
		outPath: outPath,
	}
}

func (p *plate) buildTemplatePath(name string) string {
	filename := fmt.Sprintf("%s%s", name, templatesExtension)
	return path.Join(p.srcPath, filename)
}

func (p *plate) buildOutPath(filepath string) string {
	return path.Join(p.outPath, filepath)
}

func (p *plate) openTemplate(name string) (*template.Template, error) {
	t := template.New("")
	f, err := os.Open(p.buildTemplatePath(name))
	if err != nil {
		return t, err
	}
	defer f.Close()

	content, err := ioutil.ReadAll(f)
	if err != nil {
		return t, err
	}

	return t.Parse(string(content))
}

func (p *plate) execute(name string) error {
	t, err := p.openTemplate(name)
	if err != nil {
		return err
	}

	for _, tpl := range t.Templates() {
		name := tpl.Name()
		if name != "" {
			path := p.buildOutPath(name)
			dir := filepath.Dir(path)
			err := os.MkdirAll(dir, 0777)
			if err != nil {
				return err
			}

			f, err := os.Create(path)
			if err != nil {
				return err
			}

			buf := bytes.NewBuffer([]byte{})
			tpl.Execute(buf, nil)
			tplContent := strings.TrimSpace(buf.String())
			io.WriteString(f, tplContent)
		}
	}

	return nil
}

func main() {
	var tplName string

	flag.StringVar(&tplName, "t", "", "template name")
	flag.Parse()

	fmt.Printf(" --- %v\n", tplName)

	usr, err := user.Current()
	if err != nil {
		log.Fatal(err)
	}

	templatesPath := path.Join(usr.HomeDir, templatesFolder)

	args := os.Args

	fmt.Printf("%v", args)

	p := newPlate(templatesPath, args[1])
	err = p.execute(tplName)
	if err != nil {
		log.Fatal(err)
	}
}
<commit_msg>shows all available templates<commit_after>package main

import (
	"bytes"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/user"
	"path"
	"path/filepath"
	"strings"
	"text/template"
)

const (
	templatesExtension = ".tpl"
	templatesFolder    = ".plate"
)

type plate struct {
	srcPath string
	outPath string
}

func newPlate(srcPath, outPath string) *plate {
	return &plate{
		srcPath: srcPath,
		outPath: outPath,
	}
}

func (p *plate) buildTemplatePath(name string) string {
	filename := fmt.Sprintf("%s%s", name, templatesExtension)
	return path.Join(p.srcPath, filename)
}

func (p *plate) buildOutPath(filepath string) string {
	return path.Join(p.outPath, filepath)
}

func (p *plate) openTemplate(name string) (*template.Template, error) {
	t := template.New("")
	f, err := os.Open(p.buildTemplatePath(name))
	if err != nil {
		return t, err
	}
	defer f.Close()

	content, err := ioutil.ReadAll(f)
	if err != nil {
		return t, err
	}

	return t.Parse(string(content))
}

func (p *plate) availableTemplates() []string {
	pattern := path.Join(p.srcPath, fmt.Sprintf("*%s", templatesExtension))
	paths, err := filepath.Glob(pattern)
	if err != nil {
		log.Fatal(err)
	}

	var names []string

	for _, path := range paths {
		name, err := filepath.Rel(p.srcPath, path)
		if err != nil {
			log.Fatal(err)
		}

		names = append(names, name[0:len(name)-len(templatesExtension)])
	}

	return names
}

func (p *plate) execute(name string) error {
	t, err := p.openTemplate(name)
	if err != nil {
		return err
	}

	for _, tpl := range t.Templates() {
		name := tpl.Name()
		if name != "" {
			path := p.buildOutPath(name)
			dir := filepath.Dir(path)
			err := os.MkdirAll(dir, 0777)
			if err != nil {
				return err
			}

			f, err := os.Create(path)
			if err != nil {
				return err
			}

			buf := bytes.NewBuffer([]byte{})
			tpl.Execute(buf, nil)
			tplContent := strings.TrimSpace(buf.String())
			io.WriteString(f, tplContent)
		}
	}

	return nil
}

func chooseTemplate(p *plate) string {
	templates := p.availableTemplates()
	fmt.Printf("Available templates:\n\n")
	for i, path := range templates {
		fmt.Printf("  %d - %v\n", i+1, path)
	}

	fmt.Printf("\nChoose your template [1-%d]: ", len(templates))

	var i int
	fmt.Scanf("%d", &i)

	if i < 1 || i > len(templates) {
		return chooseTemplate(p)
	}

	return templates[i-1]
}

func main() {
	var tplName string

	flag.StringVar(&tplName, "t", "", "template name")
	flag.Parse()

	usr, err := user.Current()
	if err != nil {
		log.Fatal(err)
	}

	templatesPath := path.Join(usr.HomeDir, templatesFolder)

	args := os.Args

	if len(args) != 2 {
		fmt.Printf("Usage:\n  %s PROJECT_PATH\n", args[0])
		os.Exit(1)
	}

	p := newPlate(templatesPath, args[1])
	name := chooseTemplate(p)
	p.execute(name)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"runtime"
	"syscall"
	"time"

	sdees "github.com/schollz/gitsdees/src"
	"github.com/urfave/cli"
)

var (
	Version, BuildTime, Build string
	Debug                     bool
	DontEncrypt               bool
	DeleteDocument            string
	ResetConfig               bool
	ImportOldFile, ImportFile string
)

func main() {
	// Delete temp files upon exit
	defer sdees.CleanUp()

	// Handle Ctl+C for cleanUp
	// from http://stackoverflow.com/questions/11268943/golang-is-it-possible-to-capture-a-ctrlc-signal-and-run-a-cleanup-function-in
	c := make(chan os.Signal, 2)
	signal.Notify(c, os.Interrupt, syscall.SIGTERM)
	go func() {
		<-c
		sdees.CleanUp()
		os.Exit(1)
	}()

	// App information
	app := cli.NewApp()
	app.Name = "sdees"
	if len(Build) == 0 {
		Build = "dev"
		out, err := exec.Command("git", []string{"rev-parse", "HEAD"}...).Output()
		if err != nil {
			log.Fatal(err)
		}
		bString := string(out)
		Build = bString[0:7]
		Version = "dev"
		BuildTime = time.Now().String()
	} else {
		Build = Build[0:7]
	}
	app.Version = Version + " " + Build + " " + BuildTime
	app.Usage = `SDEES Does Editing, Encryption, and Synchronization

	 https://github.com/schollz/sdees

EXAMPLE USAGE:
   sdees new.txt # edit a new document, new.txt
   sdees --summary -n 5 # list a summary of last five entries
   sdees --search "dogs cats" # find entries that mention 'dogs' or 'cats'`

	app.Action = func(c *cli.Context) error {
		// Set the log level
		if Debug {
			sdees.DebugMode()
		}

		workingFile := c.Args().Get(0)
		if len(workingFile) > 0 {
			sdees.CurrentDocument = workingFile
		}

		// Check if its Windows
		if runtime.GOOS == "windows" {
			sdees.Extension = ".exe"
		} else {
			sdees.Extension = ""
		}

		sdees.Encrypt = !DontEncrypt

		// Load configuration
		sdees.LoadConfiguration()

		// Process some flags
		if len(DeleteDocument) > 0 {
			return sdees.DeleteAndPush(DeleteDocument)
		} else if ResetConfig {
			sdees.SetupConfig()
		} else if len(ImportOldFile) > 0 {
			fmt.Println("Importing old file")
			sdees.ImportOld(ImportOldFile)
		} else if len(ImportFile) > 0 {
			fmt.Println("Importing file")
			sdees.Import(ImportFile)
		} else {
			sdees.Run()
		}
		return nil
	}
	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:        "debug",
			Usage:       "Turn on debug mode",
			Destination: &Debug,
		},
		cli.StringFlag{
			Name:        "importold",
			Usage:       "Import an oldstyle file",
			Destination: &ImportOldFile,
		},
		cli.StringFlag{
			Name:        "import",
			Usage:       "Import an file",
			Destination: &ImportFile,
		},
		cli.BoolFlag{
			Name:        "export",
			Usage:       "Export the document",
			Destination: &sdees.Export,
		},
		cli.BoolFlag{
			Name:        "config",
			Usage:       "Configure",
			Destination: &ResetConfig,
		},
		cli.BoolFlag{
			Name:        "all, a",
			Usage:       "Edit all of the document",
			Destination: &sdees.All,
		},
		cli.StringFlag{
			Name:        "delete",
			Usage:       "Delete `document`",
			Destination: &DeleteDocument,
		},
		cli.BoolFlag{
			Name:        "plaintext",
			Usage:       "Turns off encryption",
			Destination: &DontEncrypt,
		},
	}
	app.Run(os.Args)
}
<commit_msg>Updated main with some prints<commit_after>package main

import (
	"fmt"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"runtime"
	"syscall"
	"time"

	sdees "github.com/schollz/gitsdees/src"
	"github.com/urfave/cli"
)

var (
	Version, BuildTime, Build string
	Debug                     bool
	DontEncrypt               bool
	DeleteDocument            string
	ResetConfig               bool
	ImportOldFile, ImportFile string
)

func main() {
	// Delete temp files upon exit
	defer sdees.CleanUp()

	// Handle Ctl+C for cleanUp
	// from http://stackoverflow.com/questions/11268943/golang-is-it-possible-to-capture-a-ctrlc-signal-and-run-a-cleanup-function-in
	c := make(chan os.Signal, 2)
	signal.Notify(c, os.Interrupt, syscall.SIGTERM)
	go func() {
		<-c
		sdees.CleanUp()
		os.Exit(1)
	}()

	// App information
	app := cli.NewApp()
	app.Name = "sdees"
	if len(Build) == 0 {
		Build = "dev"
		out, err := exec.Command("git", []string{"rev-parse", "HEAD"}...).Output()
		if err != nil {
			log.Fatal(err)
		}
		bString := string(out)
		Build = bString[0:7]
		Version = "dev"
		BuildTime = time.Now().String()
	} else {
		Build = Build[0:7]
	}
	app.Version = Version + " " + Build + " " + BuildTime
	app.Usage = `SDEES Does Editing, Encryption, and Synchronization

	 https://github.com/schollz/sdees

EXAMPLE USAGE:
   sdees new.txt # edit a new document, new.txt
   sdees --summary -n 5 # list a summary of last five entries
   sdees --search "dogs cats" # find entries that mention 'dogs' or 'cats'`

	app.Action = func(c *cli.Context) error {
		// Set the log level
		if Debug {
			sdees.DebugMode()
		}

		workingFile := c.Args().Get(0)
		if len(workingFile) > 0 {
			sdees.CurrentDocument = workingFile
		}

		// Check if its Windows
		if runtime.GOOS == "windows" {
			sdees.Extension = ".exe"
		} else {
			sdees.Extension = ""
		}

		sdees.Encrypt = !DontEncrypt

		// Load configuration
		sdees.LoadConfiguration()

		// Process some flags
		if len(DeleteDocument) > 0 {
			fmt.Printf("Deleting branch %s\n", DeleteDocument)
			return sdees.DeleteAndPush(DeleteDocument)
		} else if ResetConfig {
			sdees.SetupConfig()
		} else if len(ImportOldFile) > 0 {
			fmt.Printf("Importing %s using deprecated import file\n", ImportOldFile)
			sdees.ImportOld(ImportOldFile)
		} else if len(ImportFile) > 0 {
			fmt.Printf("Importing %s\n", ImportFile)
			sdees.Import(ImportFile)
		} else {
			sdees.Run()
		}
		return nil
	}
	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:        "debug",
			Usage:       "Turn on debug mode",
			Destination: &Debug,
		},
		cli.StringFlag{
			Name:        "importold",
			Usage:       "Import an oldstyle file",
			Destination: &ImportOldFile,
		},
		cli.StringFlag{
			Name:        "import",
			Usage:       "Import an file",
			Destination: &ImportFile,
		},
		cli.BoolFlag{
			Name:        "export",
			Usage:       "Export the document",
			Destination: &sdees.Export,
		},
		cli.BoolFlag{
			Name:        "config",
			Usage:       "Configure",
			Destination: &ResetConfig,
		},
		cli.BoolFlag{
			Name:        "all, a",
			Usage:       "Edit all of the document",
			Destination: &sdees.All,
		},
		cli.StringFlag{
			Name:        "delete",
			Usage:       "Delete `document`",
			Destination: &DeleteDocument,
		},
		cli.BoolFlag{
			Name:        "plaintext",
			Usage:       "Turns off encryption",
			Destination: &DontEncrypt,
		},
	}
	app.Run(os.Args)
}
<|endoftext|>


<commit_before>package nnmc

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"regexp"
	"strconv"
	"strings"

	"golang.org/x/net/html/charset"
)

// NNMc values:
// client http.Client with cookie
type NNMc struct {
	client http.Client
}

// Topic from forum
type Topic struct {
	Href    string
	Name    string
	Year    string
	Quality string
}

// Film all values
// ID            id
// Name          Название
// EngName       Английское название
// Href          Ссылка
// Year          Год
// Genre         Жанр
// Country       Производство
// Director      Режиссер
// Producer      Продюсер
// Actors        Актеры
// Description   Описание
// Age           Возраст
// ReleaseDate   Дата мировой премьеры
// RussianDate   Дата премьеры в России
// Duration      Продолжительность
// Quality       Качество видео
// Translation   Перевод
// SubtitlesType Вид субтитров
// Subtitles     Субтитры
// Video         Видео
// Audio         Аудио
// Kinopoisk     Рейтинг кинопоиска
// Imdb          Рейтинг IMDb
// NNM           Рейтинг nnm-club
// Sound         Звук
// Size          Размер
// DateCreate    Дата создания раздачи
// Torrent       Ссылка на torrent
// Poster        Ссылка на постер
// Hide          Скрывать в общем списке
type Film struct {
	ID            int64   `gorm:"column:id" sql:"AUTO_INCREMENT"`
	Name          string  `gorm:"column:name"`
	EngName       string  `gorm:"column:eng_name"`
	Href          string  `gorm:"column:href"`
	Year          int64   `gorm:"column:year"`
	Genre         string  `gorm:"column:genre"`
	Country       string  `gorm:"column:country"`
	Director      string  `gorm:"column:director"`
	Producer      string  `gorm:"column:producer"`
	Actors        string  `gorm:"column:actors"`
	Description   string  `gorm:"column:description"`
	Age           string  `gorm:"column:age"`
	ReleaseDate   string  `gorm:"column:release_date"`
	RussianDate   string  `gorm:"column:russian_date"`
	Duration      int64   `gorm:"column:duration"`
	Quality       string  `gorm:"column:quality"`
	Translation   string  `gorm:"column:translation"`
	SubtitlesType string  `gorm:"column:subtitles_type"`
	Subtitles     string  `gorm:"column:subtitles"`
	Video         string  `gorm:"column:video"`
	Audio         string  `gorm:"column:audio"`
	Kinopoisk     float64 `gorm:"column:kinopoisk"`
	IMDb          float64 `gorm:"column:imdb"`
	NNM           float64 `gorm:"column:nnm"`
	Sound         string  `gorm:"column:sound"`
	Size          int64   `gorm:"column:size"`
	DateCreate    string  `gorm:"column:date_create"`
	Torrent       string  `gorm:"column:torrent"`
	Poster        string  `gorm:"column:poster"`
	Hide          bool    `gorm:"column:hide" sql:"default:0"`
}

// Init nnmc with login password
func Init(login string, password string) (*NNMc, error) {
	var client http.Client
	cookieJar, _ := cookiejar.New(nil)
	client.Jar = cookieJar
	urlPost := "http://nnm-club.me/forum/login.php"
	form := url.Values{}
	form.Set("username", login)
	form.Add("password", password)
	form.Add("redirect", "")
	form.Add("login", "âõîä")
	req, _ := http.NewRequest("POST", urlPost, bytes.NewBufferString(form.Encode()))
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Add("Content-Length", strconv.Itoa(len(form.Encode())))
	_, err := client.Do(req)
	return &NNMc{client: client}, err
}

// getHTML get body from url
func getHTML(url string, n *NNMc) ([]byte, error) {
	resp, err := n.client.Get(url)
	if err != nil {
		log.Println("client Get error:", err)
		return nil, err
	}
	defer resp.Body.Close()
	utf8body, err := charset.NewReader(resp.Body, resp.Header.Get("Content-Type"))
	if err != nil {
		log.Println("Encoding error:", err)
		return nil, err
	}
	doc, err := ioutil.ReadAll(utf8body)
	if err != nil {
		log.Println("ioutil.ReadAll error:", err)
	}
	doc = replaceAll(doc, " ", " ")
	doc = replaceAll(doc, "&", "&")
	return doc, nil
}

// ParseForumTree get topics from forumTree
func (n *NNMc) ParseForumTree(url string) ([]Topic, error) {
	var (
		topics []Topic
		reTree = regexp.MustCompile(`<a href="(viewtopic.php\?t=\d+)"class="topictitle">(.+?)\s\((\d{4})\)\s(.+?)</a>`)
	)
	body, err := getHTML(url, n)
	if err != nil {
		return topics, err
	}
	if reTree.Match(body) == false {
		return topics, fmt.Errorf("No topic in body")
	}
	findResult := reTree.FindAllSubmatch(body, -1)
	for _, v := range findResult {
		var t Topic
		t.Href = "http://nnm-club.me/forum/" + string(v[1])
		t.Name = string(v[2])
		t.Year = string(v[3])
		t.Quality = string(v[4])
		topics = append(topics, t)
	}

	return topics, nil
}

// ParseTopic get film from topic
func (n *NNMc) ParseTopic(topic Topic) (Film, error) {
	var (
		film     Film
		reTopic  = regexp.MustCompile(`(Производство|Жанр|Режиссер|Продюсер|Актеры|Описание|Возраст|Дата мировой премьеры|Дата премьеры в России|Дата Российской премьеры|Дата российской премьеры|Продолжительность|Качество видео|Качество|Перевод|Вид субтитров|Субтитры|Видео|Аудио):<\/span>(.+?)<`)
		reDate   = regexp.MustCompile(`> (\d{1,2} .{3} \d{4}).{9}<`)
		reSize   = regexp.MustCompile(`Размер блока: \d{1,2} MB"> (\d{1,2},\d{1,2}|\d{3,4})`)
		reRating = regexp.MustCompile(`>(\d,\d)<\/span`)
		reDl     = regexp.MustCompile(`<a href="download\.php\?id=(\d{5,7})" rel="nofollow">Скачать<`)
	)
	name := strings.Split(topic.Name, "/")
	switch len(name) {
	case 1:
		film.Name = strings.Trim(name[0], " ")
	case 2:
		film.Name = strings.Trim(name[0], " ")
		film.EngName = strings.Trim(name[1], " ")
	case 3:
		film.Name = strings.Trim(name[0], " ")
		film.EngName = strings.Trim(name[1], " ")
	}
	film.Href = topic.Href
	if year64, err := strconv.ParseInt(topic.Year, 10, 64); err == nil {
		film.Year = year64
	}
	body, err := getHTML(film.Href, n)
	if err != nil {
		return film, err
	}
	if reTopic.Match(body) == false {
		return film, fmt.Errorf("No topic in body")
	}
	findAttrs := reTopic.FindAllSubmatch(body, -1)
	for _, v := range findAttrs {
		one := strings.Trim(string(v[1]), " ")
		two := strings.Replace(string(v[2]), "
", "", -1)
		two = strings.Trim(two, " ")
		switch one {
		case "Производство":
			film.Country = two
		case "Жанр":
			film.Genre = strings.ToLower(two)
		case "Режиссер":
			film.Director = two
		case "Продюсер":
			film.Producer = two
		case "Актеры":
			film.Actors = two
		case "Описание":
			film.Description = two
		case "Возраст":
			film.Age = two
		case "Дата мировой премьеры":
			film.ReleaseDate = two
		case "Дата премьеры в России", "Дата российской премьеры", "Дата Российской премьеры":
			film.RussianDate = two
		case "Продолжительность":
			if i64, err := strconv.ParseInt(two, 10, 64); err == nil {
				film.Duration = i64
			}
		case "Качество видео", "Качество":
			film.Quality = two
		case "Перевод":
			film.Translation = two
		case "Вид субтитров":
			film.SubtitlesType = two
		case "Субтитры":
			film.Subtitles = two
		case "Видео":
			film.Video = two
		case "Аудио":
			film.Audio = two
		}
	}
	if reDate.Match(body) == true {
		film.DateCreate = string(reDate.FindSubmatch(body)[1])
	}
	if reSize.Match(body) == true {
		size := string(reSize.FindSubmatch(body)[1])
		size = strings.Replace(size, ",", ".", -1)
		if s64, err := strconv.ParseFloat(size, 64); err == nil {
			if s64 < 100 {
				s64 = s64 * 1000
			}
			film.Size = int64(s64)
		}
	}
	if reRating.Match(body) == true {
		rating := string(reRating.FindSubmatch(body)[1])
		rating = strings.Replace(rating, ",", ".", -1)
		if r64, err := strconv.ParseFloat(rating, 64); err == nil {
			film.NNM = r64
		}
	}
	if reDl.Match(body) == false {
		return film, fmt.Errorf("No torrent url in body")
	}
	findDl := reDl.FindAllSubmatch(body, -1)
	film.Torrent = "http://nnm-club.me/forum/download.php?id=" + string(findDl[0][1])
	return film, nil
}

func replaceAll(body []byte, from string, to string) []byte {
	var reStr = regexp.MustCompile(from)
	result := reStr.ReplaceAll(body, []byte(to))
	return result
}
<commit_msg>fix search NNM rating<commit_after>package nnmc

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"regexp"
	"strconv"
	"strings"

	"golang.org/x/net/html/charset"
)

// NNMc values:
// client http.Client with cookie
type NNMc struct {
	client http.Client
}

// Topic from forum
type Topic struct {
	Href    string
	Name    string
	Year    string
	Quality string
}

// Film all values
// ID            id
// Name          Название
// EngName       Английское название
// Href          Ссылка
// Year          Год
// Genre         Жанр
// Country       Производство
// Director      Режиссер
// Producer      Продюсер
// Actors        Актеры
// Description   Описание
// Age           Возраст
// ReleaseDate   Дата мировой премьеры
// RussianDate   Дата премьеры в России
// Duration      Продолжительность
// Quality       Качество видео
// Translation   Перевод
// SubtitlesType Вид субтитров
// Subtitles     Субтитры
// Video         Видео
// Audio         Аудио
// Kinopoisk     Рейтинг кинопоиска
// Imdb          Рейтинг IMDb
// NNM           Рейтинг nnm-club
// Sound         Звук
// Size          Размер
// DateCreate    Дата создания раздачи
// Torrent       Ссылка на torrent
// Poster        Ссылка на постер
// Hide          Скрывать в общем списке
type Film struct {
	ID            int64   `gorm:"column:id" sql:"AUTO_INCREMENT"`
	Name          string  `gorm:"column:name"`
	EngName       string  `gorm:"column:eng_name"`
	Href          string  `gorm:"column:href"`
	Year          int64   `gorm:"column:year"`
	Genre         string  `gorm:"column:genre"`
	Country       string  `gorm:"column:country"`
	Director      string  `gorm:"column:director"`
	Producer      string  `gorm:"column:producer"`
	Actors        string  `gorm:"column:actors"`
	Description   string  `gorm:"column:description"`
	Age           string  `gorm:"column:age"`
	ReleaseDate   string  `gorm:"column:release_date"`
	RussianDate   string  `gorm:"column:russian_date"`
	Duration      int64   `gorm:"column:duration"`
	Quality       string  `gorm:"column:quality"`
	Translation   string  `gorm:"column:translation"`
	SubtitlesType string  `gorm:"column:subtitles_type"`
	Subtitles     string  `gorm:"column:subtitles"`
	Video         string  `gorm:"column:video"`
	Audio         string  `gorm:"column:audio"`
	Kinopoisk     float64 `gorm:"column:kinopoisk"`
	IMDb          float64 `gorm:"column:imdb"`
	NNM           float64 `gorm:"column:nnm"`
	Sound         string  `gorm:"column:sound"`
	Size          int64   `gorm:"column:size"`
	DateCreate    string  `gorm:"column:date_create"`
	Torrent       string  `gorm:"column:torrent"`
	Poster        string  `gorm:"column:poster"`
	Hide          bool    `gorm:"column:hide" sql:"default:0"`
}

// Init nnmc with login password
func Init(login string, password string) (*NNMc, error) {
	var client http.Client
	cookieJar, _ := cookiejar.New(nil)
	client.Jar = cookieJar
	urlPost := "http://nnm-club.me/forum/login.php"
	form := url.Values{}
	form.Set("username", login)
	form.Add("password", password)
	form.Add("redirect", "")
	form.Add("login", "âõîä")
	req, _ := http.NewRequest("POST", urlPost, bytes.NewBufferString(form.Encode()))
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Add("Content-Length", strconv.Itoa(len(form.Encode())))
	_, err := client.Do(req)
	return &NNMc{client: client}, err
}

// getHTML get body from url
func getHTML(url string, n *NNMc) ([]byte, error) {
	resp, err := n.client.Get(url)
	if err != nil {
		log.Println("client Get error:", err)
		return nil, err
	}
	defer resp.Body.Close()
	utf8body, err := charset.NewReader(resp.Body, resp.Header.Get("Content-Type"))
	if err != nil {
		log.Println("Encoding error:", err)
		return nil, err
	}
	doc, err := ioutil.ReadAll(utf8body)
	if err != nil {
		log.Println("ioutil.ReadAll error:", err)
	}
	doc = replaceAll(doc, " ", " ")
	doc = replaceAll(doc, "&", "&")
	return doc, nil
}

// ParseForumTree get topics from forumTree
func (n *NNMc) ParseForumTree(url string) ([]Topic, error) {
	var (
		topics []Topic
		reTree = regexp.MustCompile(`<a href="(viewtopic.php\?t=\d+)"class="topictitle">(.+?)\s\((\d{4})\)\s(.+?)</a>`)
	)
	body, err := getHTML(url, n)
	if err != nil {
		return topics, err
	}
	if reTree.Match(body) == false {
		return topics, fmt.Errorf("No topic in body")
	}
	findResult := reTree.FindAllSubmatch(body, -1)
	for _, v := range findResult {
		var t Topic
		t.Href = "http://nnm-club.me/forum/" + string(v[1])
		t.Name = string(v[2])
		t.Year = string(v[3])
		t.Quality = string(v[4])
		topics = append(topics, t)
	}

	return topics, nil
}

// ParseTopic get film from topic
func (n *NNMc) ParseTopic(topic Topic) (Film, error) {
	var (
		film     Film
		reTopic  = regexp.MustCompile(`(Производство|Жанр|Режиссер|Продюсер|Актеры|Описание|Возраст|Дата мировой премьеры|Дата премьеры в России|Дата Российской премьеры|Дата российской премьеры|Продолжительность|Качество видео|Качество|Перевод|Вид субтитров|Субтитры|Видео|Аудио):<\/span>(.+?)<`)
		reDate   = regexp.MustCompile(`> (\d{1,2} .{3} \d{4}).{9}<`)
		reSize   = regexp.MustCompile(`Размер блока: \d{1,2} MB"> (\d{1,2},\d{1,2}|\d{3,4})`)
		reRating = regexp.MustCompile(`>(\d,\d|\d)<\/span>.+?\(Голосов:`)
		reDl     = regexp.MustCompile(`<a href="download\.php\?id=(\d{5,7})" rel="nofollow">Скачать<`)
	)
	name := strings.Split(topic.Name, "/")
	switch len(name) {
	case 1:
		film.Name = strings.Trim(name[0], " ")
	case 2:
		film.Name = strings.Trim(name[0], " ")
		film.EngName = strings.Trim(name[1], " ")
	case 3:
		film.Name = strings.Trim(name[0], " ")
		film.EngName = strings.Trim(name[1], " ")
	}
	film.Href = topic.Href
	if year64, err := strconv.ParseInt(topic.Year, 10, 64); err == nil {
		film.Year = year64
	}
	body, err := getHTML(film.Href, n)
	if err != nil {
		return film, err
	}
	if reTopic.Match(body) == false {
		return film, fmt.Errorf("No topic in body")
	}
	findAttrs := reTopic.FindAllSubmatch(body, -1)
	for _, v := range findAttrs {
		one := strings.Trim(string(v[1]), " ")
		two := strings.Replace(string(v[2]), "
", "", -1)
		two = strings.Trim(two, " ")
		switch one {
		case "Производство":
			film.Country = two
		case "Жанр":
			film.Genre = strings.ToLower(two)
		case "Режиссер":
			film.Director = two
		case "Продюсер":
			film.Producer = two
		case "Актеры":
			film.Actors = two
		case "Описание":
			film.Description = two
		case "Возраст":
			film.Age = two
		case "Дата мировой премьеры":
			film.ReleaseDate = two
		case "Дата премьеры в России", "Дата российской премьеры", "Дата Российской премьеры":
			film.RussianDate = two
		case "Продолжительность":
			if i64, err := strconv.ParseInt(two, 10, 64); err == nil {
				film.Duration = i64
			}
		case "Качество видео", "Качество":
			film.Quality = two
		case "Перевод":
			film.Translation = two
		case "Вид субтитров":
			film.SubtitlesType = two
		case "Субтитры":
			film.Subtitles = two
		case "Видео":
			film.Video = two
		case "Аудио":
			film.Audio = two
		}
	}
	if reDate.Match(body) == true {
		film.DateCreate = string(reDate.FindSubmatch(body)[1])
	}
	if reSize.Match(body) == true {
		size := string(reSize.FindSubmatch(body)[1])
		size = strings.Replace(size, ",", ".", -1)
		if s64, err := strconv.ParseFloat(size, 64); err == nil {
			if s64 < 100 {
				s64 = s64 * 1000
			}
			film.Size = int64(s64)
		}
	}
	if reRating.Match(body) == true {
		rating := string(reRating.FindSubmatch(body)[1])
		rating = strings.Replace(rating, ",", ".", -1)
		if r64, err := strconv.ParseFloat(rating, 64); err == nil {
			film.NNM = r64
		}
	}
	if reDl.Match(body) == false {
		return film, fmt.Errorf("No torrent url in body")
	}
	findDl := reDl.FindAllSubmatch(body, -1)
	film.Torrent = "http://nnm-club.me/forum/download.php?id=" + string(findDl[0][1])
	return film, nil
}

func replaceAll(body []byte, from string, to string) []byte {
	var reStr = regexp.MustCompile(from)
	result := reStr.ReplaceAll(body, []byte(to))
	return result
}
<|endoftext|>


<commit_before>package main

// Main entry point for the app. Handles command-line options, starts the web
// listener and any import, etc

import (
	"log"
	"math/rand"
	"os"
	"time"
)

var (
	httpPort       string
	httpHost       string
	numWords       int
	prefixLen      int
	stateFile      string
	responseChance int
	botUsername    string

	markovChain *Chain
)

func init() {
	rand.Seed(time.Now().UnixNano()) // Seed the random number generator.
}

func main() {
	// // Parse command-line options
	// flag.Usage = func() {
	// 	fmt.Fprintf(os.Stderr, "usage: ./slack-markov\n")
	// 	flag.PrintDefaults()
	// }

	// flag.IntVar(&httpPort, "port", 8000, "The HTTP port on which to listen")
	// flag.IntVar(&numWords, "words", 100, "Maximum number of words in the output")
	// flag.IntVar(&prefixLen, "prefix", 2, "Prefix length in words")
	// flag.IntVar(&responseChance, "responseChance", 10, "Percent chance to generate a response on each request")
	// flag.StringVar(&stateFile, "stateFile", "state", "File to use for maintaining our markov chain state")
	// flag.StringVar(&botUsername, "botUsername", "markov-bot", "The name of the bot when it speaks")

	// flag.StringVar(&twitterConsumerKey, "twitterConsumerKey", "", "Twitter API key")
	// flag.StringVar(&twitterConsumerSecret, "twitterConsumerSecret", "", "Twitter API key secret")
	// flag.StringVar(&twitterAccessToken, "twitterAccessToken", "", "Twitter access token")
	// flag.StringVar(&twitterAccessTokenSecret, "twitterAccessTokenSecret", "", "Twitter access token secret")

	// var importDir = flag.String("importDir", "", "The directory of a Slack export")
	// var importChan = flag.String("importChan", "", "Optional channel to limit the import to")

	// flag.Parse()

	// if httpPort == 0 {
	// 	flag.Usage()
	// 	os.Exit(2)
	// }

	httpPort = os.Getenv("PORT")
	httpHost = os.Getenv("HOST")
	//stateFile = os.Getenv("BRAIN")
	stateFile = "state"
	//botUsername = os.Getenv("NAME")
	botUsername = "Brainard"
	responseChance = 10

	markovChain = NewChain(prefixLen) // Initialize a new Chain.

	// Import into the chain
	err := markovChain.Load(stateFile)
	if err != nil {
		//log.Fatal(err)
		log.Printf("Could not load from '%s'. This may be expected.", stateFile)
	} else {
		log.Printf("Loaded previous state from '%s' (%d suffixes).", stateFile, len(markovChain.Chain))
	}

	// Start the webserver
	StartServer(httpHost+":"+httpPort)
}
<commit_msg>Fix runtime crash<commit_after>package main

// Main entry point for the app. Handles command-line options, starts the web
// listener and any import, etc

import (
	"log"
	"math/rand"
	"os"
	"time"
)

var (
	httpPort       string
	httpHost       string
	numWords       int
	prefixLen      int
	stateFile      string
	responseChance int
	botUsername    string

	markovChain *Chain
)

func init() {
	rand.Seed(time.Now().UnixNano()) // Seed the random number generator.
}

func main() {
	// // Parse command-line options
	// flag.Usage = func() {
	// 	fmt.Fprintf(os.Stderr, "usage: ./slack-markov\n")
	// 	flag.PrintDefaults()
	// }

	// flag.IntVar(&httpPort, "port", 8000, "The HTTP port on which to listen")
	// flag.IntVar(&numWords, "words", 100, "Maximum number of words in the output")
	// flag.IntVar(&prefixLen, "prefix", 2, "Prefix length in words")
	// flag.IntVar(&responseChance, "responseChance", 10, "Percent chance to generate a response on each request")
	// flag.StringVar(&stateFile, "stateFile", "state", "File to use for maintaining our markov chain state")
	// flag.StringVar(&botUsername, "botUsername", "markov-bot", "The name of the bot when it speaks")

	// flag.StringVar(&twitterConsumerKey, "twitterConsumerKey", "", "Twitter API key")
	// flag.StringVar(&twitterConsumerSecret, "twitterConsumerSecret", "", "Twitter API key secret")
	// flag.StringVar(&twitterAccessToken, "twitterAccessToken", "", "Twitter access token")
	// flag.StringVar(&twitterAccessTokenSecret, "twitterAccessTokenSecret", "", "Twitter access token secret")

	// var importDir = flag.String("importDir", "", "The directory of a Slack export")
	// var importChan = flag.String("importChan", "", "Optional channel to limit the import to")

	// flag.Parse()

	// if httpPort == 0 {
	// 	flag.Usage()
	// 	os.Exit(2)
	// }

	httpPort = os.Getenv("PORT")
	httpHost = os.Getenv("HOST")
	//stateFile = os.Getenv("BRAIN")
	stateFile = "../data/state"
	//botUsername = os.Getenv("NAME")
	botUsername = "Brainard"
	responseChance = 10
	prefixLen = 2
	numWords = 100

	markovChain = NewChain(prefixLen) // Initialize a new Chain.

	// Import into the chain
	err := markovChain.Load(stateFile)
	if err != nil {
		//log.Fatal(err)
		log.Printf("Could not load from '%s'. This may be expected.", stateFile)
	} else {
		log.Printf("Loaded previous state from '%s' (%d suffixes).", stateFile, len(markovChain.Chain))
	}

	// Start the webserver
	StartServer(httpHost+":"+httpPort)
}
<|endoftext|>