<commit_before>package main

import (
	"fmt"
	"os"
	"os/user"
	"path/filepath"
	"strconv"

	"time"

	"strings"

	"syscall"

	"os/signal"

	"runtime"

	"github.com/claudetech/loggo"
	. "github.com/claudetech/loggo/default"
	"github.com/dweidenfeld/plexdrive/chunk"
	"github.com/dweidenfeld/plexdrive/config"
	"github.com/dweidenfeld/plexdrive/drive"
	"github.com/dweidenfeld/plexdrive/mount"
	flag "github.com/ogier/pflag"
	"golang.org/x/sys/unix"
)

func main() {
	// get the users home dir
	user, err := user.Current()
	if nil != err {
		panic(fmt.Sprintf("Could not read users homedir %v\n", err))
	}

	// parse the command line arguments
	argLogLevel := flag.IntP("verbosity", "v", 0, "Set the log level (0 = error, 1 = warn, 2 = info, 3 = debug, 4 = trace)")
	argRootNodeID := flag.String("root-node-id", "root", "The ID of the root node to mount (use this for only mount a sub directory)")
	argConfigPath := flag.StringP("config", "c", filepath.Join(user.HomeDir, ".plexdrive"), "The path to the configuration directory")
	argCacheFile := flag.String("cache-file", filepath.Join(user.HomeDir, ".plexdrive", "cache.bolt"), "Path the the cache file")
	argChunkSize := flag.String("chunk-size", "10M", "The size of each chunk that is downloaded (units: B, K, M, G)")
	argChunkLoadThreads := flag.Int("chunk-load-threads", runtime.NumCPU()/2, "The number of threads to use for downloading chunks")
	argChunkCheckThreads := flag.Int("chunk-check-threads", runtime.NumCPU()/2, "The number of threads to use for checking chunk existence")
	argChunkLoadAhead := flag.Int("chunk-load-ahead", runtime.NumCPU()-1, "The number of chunks that should be read ahead")
	argMaxChunks := flag.Int("max-chunks", runtime.NumCPU()*2, "The maximum number of chunks to be stored on disk")
	argRefreshInterval := flag.Duration("refresh-interval", 1*time.Minute, "The time to wait till checking for changes")
	argMountOptions := flag.StringP("fuse-options", "o", "", "Fuse mount options (e.g. -fuse-options allow_other,...)")
	argVersion := flag.Bool("version", false, "Displays program's version information")
	argUID := flag.Int64("uid", -1, "Set the mounts UID (-1 = default permissions)")
	argGID := flag.Int64("gid", -1, "Set the mounts GID (-1 = default permissions)")
	argUmask := flag.Uint32("umask", 0, "Override the default file permissions")
	// argDownloadSpeedLimit := flag.String("speed-limit", "", "This value limits the download speed, e.g. 5M = 5MB/s per chunk (units: B, K, M, G)")
	flag.Parse()

	// display version information
	if *argVersion {
		fmt.Println("%VERSION%")
		return
	}

	// check if mountpoint is specified
	argMountPoint := flag.Arg(0)
	if "" == argMountPoint {
		flag.Usage()
		fmt.Println()
		panic(fmt.Errorf("Mountpoint not specified"))
	}

	// calculate uid / gid
	uid := uint32(unix.Geteuid())
	gid := uint32(unix.Getegid())
	if *argUID > -1 {
		uid = uint32(*argUID)
	}
	if *argGID > -1 {
		gid = uint32(*argGID)
	}

	// parse filemode
	umask := os.FileMode(*argUmask)

	// parse the mount options
	var mountOptions []string
	if "" != *argMountOptions {
		mountOptions = strings.Split(*argMountOptions, ",")
	}

	// initialize the logger with the specific log level
	var logLevel loggo.Level
	switch *argLogLevel {
	case 0:
		logLevel = loggo.Error
	case 1:
		logLevel = loggo.Warning
	case 2:
		logLevel = loggo.Info
	case 3:
		logLevel = loggo.Debug
	case 4:
		logLevel = loggo.Trace
	default:
		logLevel = loggo.Warning
	}
	Log.SetLevel(logLevel)

	// debug all given parameters
	Log.Debugf("verbosity            : %v", logLevel)
	Log.Debugf("root-node-id         : %v", *argRootNodeID)
	Log.Debugf("config               : %v", *argConfigPath)
	Log.Debugf("cache-file           : %v", *argCacheFile)
	Log.Debugf("chunk-size           : %v", *argChunkSize)
	Log.Debugf("chunk-load-threads   : %v", *argChunkLoadThreads)
	Log.Debugf("chunk-check-threads  : %v", *argChunkCheckThreads)
	Log.Debugf("chunk-load-ahead     : %v", *argChunkLoadAhead)
	Log.Debugf("max-chunks           : %v", *argMaxChunks)
	Log.Debugf("refresh-interval     : %v", *argRefreshInterval)
	Log.Debugf("fuse-options         : %v", *argMountOptions)
	Log.Debugf("UID                  : %v", uid)
	Log.Debugf("GID                  : %v", gid)
	Log.Debugf("umask                : %v", umask)
	// Log.Debugf("speed-limit          : %v", *argDownloadSpeedLimit)
	// version missing here

	// create all directories
	if err := os.MkdirAll(*argConfigPath, 0766); nil != err {
		Log.Errorf("Could not create configuration directory")
		Log.Debugf("%v", err)
		os.Exit(1)
	}
	if err := os.MkdirAll(filepath.Dir(*argCacheFile), 0766); nil != err {
		Log.Errorf("Could not create cache file directory")
		Log.Debugf("%v", err)
		os.Exit(1)
	}

	// set the global buffer configuration
	chunkSize, err := parseSizeArg(*argChunkSize)
	if nil != err {
		Log.Errorf("%v", err)
		os.Exit(2)
	}

	// read the configuration
	configPath := filepath.Join(*argConfigPath, "config.json")
	cfg, err := config.Read(configPath)
	if nil != err {
		cfg, err = config.Create(configPath)
		if nil != err {
			Log.Errorf("Could not read configuration")
			Log.Debugf("%v", err)
			os.Exit(3)
		}
	}

	cache, err := drive.NewCache(*argCacheFile, *argConfigPath, *argLogLevel > 3)
	if nil != err {
		Log.Errorf("%v", err)
		os.Exit(4)
	}
	defer cache.Close()

	client, err := drive.NewClient(cfg, cache, *argRefreshInterval, *argRootNodeID)
	if nil != err {
		Log.Errorf("%v", err)
		os.Exit(4)
	}

	chunkManager, err := chunk.NewManager(
		chunkSize,
		*argChunkLoadAhead,
		*argChunkCheckThreads,
		*argChunkLoadThreads,
		client,
		*argMaxChunks)
	if nil != err {
		Log.Errorf("%v", err)
		os.Exit(4)
	}

	// check os signals like SIGINT/TERM
	checkOsSignals(argMountPoint)
	if err := mount.Mount(client, chunkManager, argMountPoint, mountOptions, uid, gid, umask); nil != err {
		Log.Debugf("%v", err)
		os.Exit(5)
	}
}

func checkOsSignals(mountpoint string) {
	signals := make(chan os.Signal, 1)
	signal.Notify(signals, syscall.SIGINT)

	go func() {
		for sig := range signals {
			if sig == syscall.SIGINT {
				if err := mount.Unmount(mountpoint, false); nil != err {
					Log.Warningf("%v", err)
				}
			}
		}
	}()
}

func parseSizeArg(input string) (int64, error) {
	if "" == input {
		return 0, nil
	}

	suffix := input[len(input)-1]
	suffixLen := 1
	var multiplier float64
	switch suffix {
	case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.':
		suffixLen = 0
	case 'b', 'B':
		multiplier = 1
	case 'k', 'K':
		multiplier = 1024
	case 'm', 'M':
		multiplier = 1024 * 1024
	case 'g', 'G':
		multiplier = 1024 * 1024 * 1024
	default:
		return 0, fmt.Errorf("Invalid unit %v for %v", suffix, input)
	}
	input = input[:len(input)-suffixLen]
	value, err := strconv.ParseFloat(input, 64)
	if nil != err {
		Log.Debugf("%v", err)
		return 0, fmt.Errorf("Could not parse numeric value %v", input)
	}
	if value < 0 {
		return 0, fmt.Errorf("Numeric value must not be negative %v", input)
	}
	value *= multiplier
	return int64(value), nil
}
<commit_msg>preparation for multiple commands<commit_after>package main

import (
	"fmt"
	"os"
	"os/user"
	"path/filepath"
	"strconv"

	"time"

	"strings"

	"syscall"

	"os/signal"

	"runtime"

	"github.com/claudetech/loggo"
	. "github.com/claudetech/loggo/default"
	"github.com/dweidenfeld/plexdrive/chunk"
	"github.com/dweidenfeld/plexdrive/config"
	"github.com/dweidenfeld/plexdrive/drive"
	"github.com/dweidenfeld/plexdrive/mount"
	flag "github.com/ogier/pflag"
	"golang.org/x/sys/unix"
)

func main() {
	// get the users home dir
	user, err := user.Current()
	if nil != err {
		panic(fmt.Sprintf("Could not read users homedir %v\n", err))
	}

	// parse the command line arguments
	argLogLevel := flag.IntP("verbosity", "v", 0, "Set the log level (0 = error, 1 = warn, 2 = info, 3 = debug, 4 = trace)")
	argRootNodeID := flag.String("root-node-id", "root", "The ID of the root node to mount (use this for only mount a sub directory)")
	argConfigPath := flag.StringP("config", "c", filepath.Join(user.HomeDir, ".plexdrive"), "The path to the configuration directory")
	argCacheFile := flag.String("cache-file", filepath.Join(user.HomeDir, ".plexdrive", "cache.bolt"), "Path the the cache file")
	argChunkSize := flag.String("chunk-size", "10M", "The size of each chunk that is downloaded (units: B, K, M, G)")
	argChunkLoadThreads := flag.Int("chunk-load-threads", runtime.NumCPU()/2, "The number of threads to use for downloading chunks")
	argChunkCheckThreads := flag.Int("chunk-check-threads", runtime.NumCPU()/2, "The number of threads to use for checking chunk existence")
	argChunkLoadAhead := flag.Int("chunk-load-ahead", runtime.NumCPU()-1, "The number of chunks that should be read ahead")
	argMaxChunks := flag.Int("max-chunks", runtime.NumCPU()*2, "The maximum number of chunks to be stored on disk")
	argRefreshInterval := flag.Duration("refresh-interval", 1*time.Minute, "The time to wait till checking for changes")
	argMountOptions := flag.StringP("fuse-options", "o", "", "Fuse mount options (e.g. -fuse-options allow_other,...)")
	argVersion := flag.Bool("version", false, "Displays program's version information")
	argUID := flag.Int64("uid", -1, "Set the mounts UID (-1 = default permissions)")
	argGID := flag.Int64("gid", -1, "Set the mounts GID (-1 = default permissions)")
	argUmask := flag.Uint32("umask", 0, "Override the default file permissions")
	// argDownloadSpeedLimit := flag.String("speed-limit", "", "This value limits the download speed, e.g. 5M = 5MB/s per chunk (units: B, K, M, G)")
	flag.Parse()

	// display version information
	if *argVersion {
		fmt.Println("%VERSION%")
		return
	}

	argCommand := flag.Arg(0)

	if argCommand == "mount" {
		// check if mountpoint is specified
		argMountPoint := flag.Arg(1)
		if "" == argMountPoint {
			flag.Usage()
			fmt.Println()
			panic(fmt.Errorf("Mountpoint not specified"))
		}

		// calculate uid / gid
		uid := uint32(unix.Geteuid())
		gid := uint32(unix.Getegid())
		if *argUID > -1 {
			uid = uint32(*argUID)
		}
		if *argGID > -1 {
			gid = uint32(*argGID)
		}

		// parse filemode
		umask := os.FileMode(*argUmask)

		// parse the mount options
		var mountOptions []string
		if "" != *argMountOptions {
			mountOptions = strings.Split(*argMountOptions, ",")
		}

		// initialize the logger with the specific log level
		var logLevel loggo.Level
		switch *argLogLevel {
		case 0:
			logLevel = loggo.Error
		case 1:
			logLevel = loggo.Warning
		case 2:
			logLevel = loggo.Info
		case 3:
			logLevel = loggo.Debug
		case 4:
			logLevel = loggo.Trace
		default:
			logLevel = loggo.Warning
		}
		Log.SetLevel(logLevel)

		// debug all given parameters
		Log.Debugf("verbosity            : %v", logLevel)
		Log.Debugf("root-node-id         : %v", *argRootNodeID)
		Log.Debugf("config               : %v", *argConfigPath)
		Log.Debugf("cache-file           : %v", *argCacheFile)
		Log.Debugf("chunk-size           : %v", *argChunkSize)
		Log.Debugf("chunk-load-threads   : %v", *argChunkLoadThreads)
		Log.Debugf("chunk-check-threads  : %v", *argChunkCheckThreads)
		Log.Debugf("chunk-load-ahead     : %v", *argChunkLoadAhead)
		Log.Debugf("max-chunks           : %v", *argMaxChunks)
		Log.Debugf("refresh-interval     : %v", *argRefreshInterval)
		Log.Debugf("fuse-options         : %v", *argMountOptions)
		Log.Debugf("UID                  : %v", uid)
		Log.Debugf("GID                  : %v", gid)
		Log.Debugf("umask                : %v", umask)
		// Log.Debugf("speed-limit          : %v", *argDownloadSpeedLimit)
		// version missing here

		// create all directories
		if err := os.MkdirAll(*argConfigPath, 0766); nil != err {
			Log.Errorf("Could not create configuration directory")
			Log.Debugf("%v", err)
			os.Exit(1)
		}
		if err := os.MkdirAll(filepath.Dir(*argCacheFile), 0766); nil != err {
			Log.Errorf("Could not create cache file directory")
			Log.Debugf("%v", err)
			os.Exit(1)
		}

		// set the global buffer configuration
		chunkSize, err := parseSizeArg(*argChunkSize)
		if nil != err {
			Log.Errorf("%v", err)
			os.Exit(2)
		}

		// read the configuration
		configPath := filepath.Join(*argConfigPath, "config.json")
		cfg, err := config.Read(configPath)
		if nil != err {
			cfg, err = config.Create(configPath)
			if nil != err {
				Log.Errorf("Could not read configuration")
				Log.Debugf("%v", err)
				os.Exit(3)
			}
		}

		cache, err := drive.NewCache(*argCacheFile, *argConfigPath, *argLogLevel > 3)
		if nil != err {
			Log.Errorf("%v", err)
			os.Exit(4)
		}
		defer cache.Close()

		client, err := drive.NewClient(cfg, cache, *argRefreshInterval, *argRootNodeID)
		if nil != err {
			Log.Errorf("%v", err)
			os.Exit(4)
		}

		chunkManager, err := chunk.NewManager(
			chunkSize,
			*argChunkLoadAhead,
			*argChunkCheckThreads,
			*argChunkLoadThreads,
			client,
			*argMaxChunks)
		if nil != err {
			Log.Errorf("%v", err)
			os.Exit(4)
		}

		// check os signals like SIGINT/TERM
		checkOsSignals(argMountPoint)
		if err := mount.Mount(client, chunkManager, argMountPoint, mountOptions, uid, gid, umask); nil != err {
			Log.Debugf("%v", err)
			os.Exit(5)
		}
	} else {
		Log.Errorf("Command %v not found", argCommand)
	}
}

func checkOsSignals(mountpoint string) {
	signals := make(chan os.Signal, 1)
	signal.Notify(signals, syscall.SIGINT)

	go func() {
		for sig := range signals {
			if sig == syscall.SIGINT {
				if err := mount.Unmount(mountpoint, false); nil != err {
					Log.Warningf("%v", err)
				}
			}
		}
	}()
}

func parseSizeArg(input string) (int64, error) {
	if "" == input {
		return 0, nil
	}

	suffix := input[len(input)-1]
	suffixLen := 1
	var multiplier float64
	switch suffix {
	case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.':
		suffixLen = 0
	case 'b', 'B':
		multiplier = 1
	case 'k', 'K':
		multiplier = 1024
	case 'm', 'M':
		multiplier = 1024 * 1024
	case 'g', 'G':
		multiplier = 1024 * 1024 * 1024
	default:
		return 0, fmt.Errorf("Invalid unit %v for %v", suffix, input)
	}
	input = input[:len(input)-suffixLen]
	value, err := strconv.ParseFloat(input, 64)
	if nil != err {
		Log.Debugf("%v", err)
		return 0, fmt.Errorf("Could not parse numeric value %v", input)
	}
	if value < 0 {
		return 0, fmt.Errorf("Numeric value must not be negative %v", input)
	}
	value *= multiplier
	return int64(value), nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/astaxie/beego"
	"github.com/robxu9/kahinah/controllers"
	"github.com/robxu9/kahinah/integration"
	"github.com/robxu9/kahinah/util"
	"html/template"
	"net/http"
	"strings"
	"time"
)

func main() {
	beego.SessionOn = true

	beego.Router("/", &controllers.MainController{})

	// testing
	beego.Router("/testing", &controllers.TestingController{}) // lists testing updates
	//beego.Router("/testing/:buildid:int", &controllers.TestingPkgController{}) // shows specific testing update
	// ^ now use below BuildSpecificController

	// published
	beego.Router("/published", &controllers.PublishedController{}) // lists years

	// below: TODO in future update
	//beego.Router("/published/:year:int", &controllers.PublishedListController{})        // lists updates in said years
	//beego.Router("/published/:year:int/:id:int", &controllers.PublishedPkgController{}) // shows specific update
	// ^ redirect to below build specific controller

	// rejected
	beego.Router("/rejected", &controllers.RejectedController{}) // lists all rejected updates
	//beego.Router("/rejected/:before:int", &controllers.RejectedController{}) // list all rejected updates before this date

	// builds
	beego.Router("/builds", &controllers.BuildsController{}) // show all testing, published, rejected (all sorted by date, linking respectively to above)
	beego.Router("/builds/:id:int", &controllers.BuildController{})

	// platform
	//beego.Router("/platforms", &controllers.PlatformsController{})
	//beego.Router("/platforms/:platform:string/", &controllers.PlatformSpecificController{}) // show by platform

	// about
	//beego.Router("/about", &controllers.AboutController{})

	// ping target (for integration)
	//beego.Router("/ping", &controllers.PingController{})

	// persona
	beego.Router("/auth/check", &util.PersonaCheckController{})
	beego.Router("/auth/login", &util.PersonaLoginController{})
	beego.Router("/auth/logout", &util.PersonaLogoutController{})

	// admin
	beego.Router("/admin", &controllers.AdminController{})

	// templating
	beego.AddFuncMap("since", func(t time.Time) string {
		hrs := time.Since(t).Hours()
		return fmt.Sprintf("%dd %2dhrs", int(hrs)/24, int(hrs)%24)
	})

	beego.AddFuncMap("emailat", func(s string) string {
		return strings.Replace(s, "@", " [@T] ", -1)
	})

	// error handling
	beego.Errorhandler("550", func(rw http.ResponseWriter, r *http.Request) {
		t, _ := template.New("beegoerrortemp").ParseFiles(beego.ViewsPath + "/permissions_error.tpl")
		data := make(map[string]interface{})
		data["Permission"] = r.Form.Get("permission")
		t.Execute(rw, data)
	})

	// integration
	stop := make(chan bool)

	integration.Integrate(integration.ABF_HANDLER_NAME, integration.ABF(1))

	go func() {
		timeout := make(chan bool)
		go func() {
			for {
				timeout <- true
				time.Sleep(1 * time.Hour)
			}
		}()
		for {
			select {
			case <-stop:
				return
			case <-timeout:
				integration.Ping()
			}
		}
	}()

	beego.Run()
	<-stop
}
<commit_msg>bloody zero slipped out: fix printf for hours in main<commit_after>package main

import (
	"fmt"
	"github.com/astaxie/beego"
	"github.com/robxu9/kahinah/controllers"
	"github.com/robxu9/kahinah/integration"
	"github.com/robxu9/kahinah/util"
	"html/template"
	"net/http"
	"strings"
	"time"
)

func main() {
	beego.SessionOn = true

	beego.Router("/", &controllers.MainController{})

	// testing
	beego.Router("/testing", &controllers.TestingController{}) // lists testing updates
	//beego.Router("/testing/:buildid:int", &controllers.TestingPkgController{}) // shows specific testing update
	// ^ now use below BuildSpecificController

	// published
	beego.Router("/published", &controllers.PublishedController{}) // lists years

	// below: TODO in future update
	//beego.Router("/published/:year:int", &controllers.PublishedListController{})        // lists updates in said years
	//beego.Router("/published/:year:int/:id:int", &controllers.PublishedPkgController{}) // shows specific update
	// ^ redirect to below build specific controller

	// rejected
	beego.Router("/rejected", &controllers.RejectedController{}) // lists all rejected updates
	//beego.Router("/rejected/:before:int", &controllers.RejectedController{}) // list all rejected updates before this date

	// builds
	beego.Router("/builds", &controllers.BuildsController{}) // show all testing, published, rejected (all sorted by date, linking respectively to above)
	beego.Router("/builds/:id:int", &controllers.BuildController{})

	// platform
	//beego.Router("/platforms", &controllers.PlatformsController{})
	//beego.Router("/platforms/:platform:string/", &controllers.PlatformSpecificController{}) // show by platform

	// about
	//beego.Router("/about", &controllers.AboutController{})

	// ping target (for integration)
	//beego.Router("/ping", &controllers.PingController{})

	// persona
	beego.Router("/auth/check", &util.PersonaCheckController{})
	beego.Router("/auth/login", &util.PersonaLoginController{})
	beego.Router("/auth/logout", &util.PersonaLogoutController{})

	// admin
	beego.Router("/admin", &controllers.AdminController{})

	// templating
	beego.AddFuncMap("since", func(t time.Time) string {
		hrs := time.Since(t).Hours()
		return fmt.Sprintf("%dd %02dhrs", int(hrs)/24, int(hrs)%24)
	})

	beego.AddFuncMap("emailat", func(s string) string {
		return strings.Replace(s, "@", " [@T] ", -1)
	})

	// error handling
	beego.Errorhandler("550", func(rw http.ResponseWriter, r *http.Request) {
		t, _ := template.New("beegoerrortemp").ParseFiles(beego.ViewsPath + "/permissions_error.tpl")
		data := make(map[string]interface{})
		data["Permission"] = r.Form.Get("permission")
		t.Execute(rw, data)
	})

	// integration
	stop := make(chan bool)

	integration.Integrate(integration.ABF_HANDLER_NAME, integration.ABF(1))

	go func() {
		timeout := make(chan bool)
		go func() {
			for {
				timeout <- true
				time.Sleep(1 * time.Hour)
			}
		}()
		for {
			select {
			case <-stop:
				return
			case <-timeout:
				integration.Ping()
			}
		}
	}()

	beego.Run()
	<-stop
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"
	"regexp"
	"sort"
	"time"

	"github.com/Sirupsen/logrus"
	humanize "github.com/dustin/go-humanize"
	"github.com/nlopes/slack"
)

const extractMsgGroupName = "msg"

// extract suffixes of Slack messages starting with @bot-name
var extractMsgPattern = `(?m)^\s*<@%s>:?\s*(?P<` + extractMsgGroupName + `>.*)$`

// A standupMsg is a Slack message directed to the arriba bot (i.e. with a @botname prefix)
type standupMsg struct {
	ts   time.Time
	text string
}

// channelStandup contains the latest standup message of each user in a Slack channel.
type channelStandup map[string]standupMsg

// sortableChannelStandup is a channelStandup, sortable by the timestamp of its messages
// sortableChannelStandup implements sort.Interface to sort the keys of the channelStandup
type sortableChannelStandup struct {
	keys []string
	cs   channelStandup
}

func (s sortableChannelStandup) Swap(i, j int) { s.keys[i], s.keys[j] = s.keys[j], s.keys[i] }
func (s sortableChannelStandup) Len() int      { return len(s.keys) }
func (s sortableChannelStandup) Less(i, j int) bool {
	return s.cs[s.keys[i]].ts.After(s.cs[s.keys[j]].ts)
}

// getKeysByTimestamp returns the userIDs of the standup ordered by their message timestamp (newer first).
func (cs channelStandup) getKeysByTimestamp() []string {
	keys := make([]string, 0, len(cs))
	for k := range cs {
		keys = append(keys, k)
	}
	scs := sortableChannelStandup{
		cs:   cs,
		keys: keys,
	}
	sort.Sort(scs)
	return scs.keys
}

// standups contains the channelStandup of all Slack channels known to the bot.
type standups map[string]channelStandup

// conversation is a generic way to access the IDs, Names and history of both
// slack.Channel and slack.Group. Unfortunately nlopes/slack doesn't expose the
// underlying common type (groupConversation) and we cannot define methods for
// non-local types, which would allow to make things much cleaner ...
type conversation interface {
	getID() string
	getName() string
	getHistory(*slack.RTM, slack.HistoryParameters) (*slack.History, error)
}

type channel slack.Channel

func (c channel) getID() string   { return c.ID }
func (c channel) getName() string { return c.Name }
func (c channel) getHistory(rtm *slack.RTM, params slack.HistoryParameters) (*slack.History, error) {
	return rtm.GetChannelHistory(c.getID(), params)
}

type group slack.Group

func (g group) getID() string   { return g.ID }
func (g group) getName() string { return g.Name }
func (g group) getHistory(rtm *slack.RTM, params slack.HistoryParameters) (*slack.History, error) {
	return rtm.GetGroupHistory(g.getID(), params)
}

type arriba struct {
	rtm              *slack.RTM
	botID            string
	botName          string
	extractMsgRE     *regexp.Regexp
	historyDaysLimit int
	standups         standups
}

func newArriba(rtm *slack.RTM, historyDaysLimit int) arriba {
	return arriba{
		rtm:              rtm,
		historyDaysLimit: historyDaysLimit,
		standups:         make(standups),
	}
}

func parseSlackTimeStamp(ts string) (time.Time, error) {
	var seconds, milliseconds int64
	_, err := fmt.Sscanf(ts, "%d.%d", &seconds, &milliseconds)
	if err != nil {
		logrus.Warn("Can't parse timestamp ", ts)
		return time.Now(), err
	}
	return time.Unix(seconds, milliseconds*1000), nil
}

// extractStandupMsg parses Slack messages starting with @bot-name
func (a arriba) extractChannelStandupMsg(msg slack.Msg) (standupMsg, bool) {
	if msg.Type != "message" || msg.SubType != "" {
		return standupMsg{}, false
	}
	standupText := a.extractMsgRE.ReplaceAllString(msg.Text, "$"+extractMsgGroupName)
	if len(standupText) == len(msg.Text) {
		// Nothing was extracted
		return standupMsg{}, false
	}
	ts, err := parseSlackTimeStamp(msg.Timestamp)
	if err != nil {
		return standupMsg{}, false
	}
	return standupMsg{ts, standupText}, true
}

func (a arriba) retrieveChannelStandup(c conversation) (channelStandup, error) {
	params := slack.NewHistoryParameters()
	params.Count = 1000
	now := time.Now().UTC()
	params.Latest = fmt.Sprintf("%d", now.Unix())
	params.Oldest = fmt.Sprintf("%d", now.AddDate(0, 0, -a.historyDaysLimit).Unix())

	// It would be way more efficient to use slack.SearchMsgs instead
	// of traversing the whole history, but that's not allowed for bots :(
	cstandup := make(channelStandup)
	for {
		history, error := c.getHistory(a.rtm, params)
		if error != nil || history == nil || len(history.Messages) == 0 {
			return cstandup, error
		}

		logrus.Debugf(
			"Got history chunk (from %s to %s, latest %s) for conversation %s",
			history.Messages[len(history.Messages)-1].Msg.Timestamp,
			history.Messages[0].Msg.Timestamp, history.Latest, c.getID())

		for _, msg := range history.Messages {
			if _, ok := cstandup[msg.User]; ok {
				// we already have the latest standup message for this user
				continue
			}
			standupMsg, ok := a.extractChannelStandupMsg(msg.Msg)
			if ok && standupMsg.text != "" {
				cstandup[msg.User] = standupMsg
			}
		}

		if !history.HasMore {
			break
		}
		latestMsg := history.Messages[len(history.Messages)-1]
		params.Latest = latestMsg.Timestamp
		params.Inclusive = false
	}
	return cstandup, nil
}

func (a arriba) retrieveStandups(conversations []conversation) {
	for _, c := range conversations {
		logrus.Infof("Retrieveing standup for conversation #%s (%s)", c.getName(), c.getID())
		cstandup, err := a.retrieveChannelStandup(c)
		if err != nil {
			logrus.Errorf("Can't retrieve channel standup for conversation #%s: %s", c.getName(), err)
		}
		a.standups[c.getID()] = cstandup
		logrus.Infof("Standup for conversation #%s (%s) updated to %#v", c.getName(), c.getID(), cstandup)
	}
}

func (a arriba) getUserName(userID string) string {
	info, err := a.rtm.GetUserInfo(userID)
	userName := "id" + userID
	if err != nil {
		logrus.Errorf("Couldn't get user information for user %s: %s", userID, err)
	} else {
		userName = info.Name
	}
	return userName
}

func (a arriba) removeOldMessages(channelID string) {
	cstandup, ok := a.standups[channelID]
	if !ok {
		return
	}
	oldestAllowed := time.Now().UTC().AddDate(0, 0, -a.historyDaysLimit)
	for userID, msg := range cstandup {
		if msg.ts.Before(oldestAllowed) {
			delete(cstandup, userID)
		}
	}
}

func (a arriba) prettyPrintChannelStandup(cstandup channelStandup) string {
	text := "¡Ándale! ¡Ándale! here's the standup status :tada:\n"
	for _, userID := range cstandup.getKeysByTimestamp() {
		standupMsg := cstandup[userID]
		humanTime := humanize.Time(standupMsg.ts)
		userName := a.getUserName(userID)
		// Inject zero-width unicode character in username to avoid notifying users
		if len(userName) > 1 {
			userName = string(userName[0]) + "\ufeff" + string(userName[1:])
		}
		text += fmt.Sprintf("*%s*: %s _(%s)_\n", userName, standupMsg.text, humanTime)
	}
	return text
}

func (a arriba) sendStatus(channelID string) {
	var statusText string
	if cstandup, ok := a.standups[channelID]; ok && len(cstandup) > 0 {
		statusText = a.prettyPrintChannelStandup(cstandup)
	} else {
		statusText = fmt.Sprintf("No standup messages found\nType a message starting with *@%s* to record your standup message", a.botName)
	}
	a.rtm.SendMessage(a.rtm.NewOutgoingMessage(statusText, channelID))

}

func (a arriba) updateLastStandup(channelID, userID string, msg standupMsg) {
	if _, ok := a.standups[channelID]; !ok {
		a.standups[channelID] = make(channelStandup)
	}
	a.standups[channelID][userID] = msg
	confirmationText := fmt.Sprintf("<@%s>: ¡Yeppa! standup status recorded :taco:", userID)
	a.rtm.SendMessage(a.rtm.NewOutgoingMessage(confirmationText, channelID))
}

func (a *arriba) handleConnectedEvent(ev *slack.ConnectedEvent) {
	if a.botID != "" {
		logrus.Warn("Received unexpected Connected event")
		return
	}
	logrus.Infof(
		"Connected as user %s (%s) to team %s (%s)",
		ev.Info.User.Name,
		ev.Info.User.ID,
		ev.Info.Team.Name,
		ev.Info.Team.ID,
	)
	a.botID = ev.Info.User.ID
	a.botName = ev.Info.User.Name
	a.extractMsgRE = regexp.MustCompile(fmt.Sprintf(extractMsgPattern, a.botID))

	// Retrieve standups for public channels and private groups
	var conversations []conversation
	for _, c := range ev.Info.Channels {
		if c.IsMember {
			conversations = append(conversations, channel(c))
		}
	}
	for _, g := range ev.Info.Groups {
		conversations = append(conversations, group(g))
	}
	a.retrieveStandups(conversations)
}

func (a arriba) handleMessageEvent(ev *slack.MessageEvent) {
	logrus.Debugf("Message received %+v", ev)
	if a.botID == "" {
		logrus.Warn("Received message event before finishing initialization")
		return
	}
	if ev.Channel == "" {
		logrus.Warn("Received message with empty channel")
		return
	}
	switch ev.Channel[0] {
	case 'C', 'G':
		// Public and private (group) channels
		smsg, ok := a.extractChannelStandupMsg(ev.Msg)
		if !ok {
			return
		}
		logrus.Infof("Received standup message in channel %s: %+v", ev.Channel, smsg)
		// Garbage-collect old messages
		a.removeOldMessages(ev.Msg.Channel)
		if smsg.text == "" {
			a.sendStatus(ev.Msg.Channel)
		} else {
			a.updateLastStandup(ev.Msg.Channel, ev.Msg.User, smsg)
		}

	case 'D':
		// Direct messages are not supported yet
	}
}

func (a arriba) run() {
	go a.rtm.ManageConnection()

	for {
		select {
		case msg := <-a.rtm.IncomingEvents:
			switch ev := msg.Data.(type) {
			case *slack.ConnectedEvent:
				a.handleConnectedEvent(ev)
			case *slack.MessageEvent:
				a.handleMessageEvent(ev)
			case *slack.RTMError:
				logrus.Error("Invalid credentials", ev.Error())
			case *slack.InvalidAuthEvent:
				logrus.Error("Invalid credentials")
				os.Exit(1)
			}
		}
	}
}

func usage() {
	fmt.Fprintf(os.Stderr, "usage: %s [ flags ] <SlackAPItoken>\n", os.Args[0])
	flag.PrintDefaults()
	fmt.Fprintf(os.Stderr, "You can obtain <SlackAPItoken> from https://<yourteam>.slack.com/services/new/bot\n")
}

func main() {
	var (
		debug            bool
		historyDaysLimit int
	)

	flag.Usage = usage
	flag.BoolVar(&debug, "debug", false, "Print debug information")
	flag.IntVar(&historyDaysLimit, "history-limit", 7, "History limit (in days)")
	flag.Parse()
	if len(flag.Args()) < 1 || historyDaysLimit < 1 {
		usage()
		os.Exit(1)
	}

	logrus.SetOutput(os.Stderr)
	if debug {
		logrus.SetLevel(logrus.DebugLevel)
	}

	api := slack.New(flag.Arg(0))
	api.SetDebug(debug)

	newArriba(api.NewRTM(), historyDaysLimit).run()
}
<commit_msg>Add extra debugging messages<commit_after>package main

import (
	"flag"
	"fmt"
	"os"
	"regexp"
	"sort"
	"time"

	"github.com/Sirupsen/logrus"
	humanize "github.com/dustin/go-humanize"
	"github.com/nlopes/slack"
)

const extractMsgGroupName = "msg"

// extract suffixes of Slack messages starting with @bot-name
var extractMsgPattern = `(?m)^\s*<@%s>:?\s*(?P<` + extractMsgGroupName + `>.*)$`

// A standupMsg is a Slack message directed to the arriba bot (i.e. with a @botname prefix)
type standupMsg struct {
	ts   time.Time
	text string
}

// channelStandup contains the latest standup message of each user in a Slack channel.
type channelStandup map[string]standupMsg

// sortableChannelStandup is a channelStandup, sortable by the timestamp of its messages
// sortableChannelStandup implements sort.Interface to sort the keys of the channelStandup
type sortableChannelStandup struct {
	keys []string
	cs   channelStandup
}

func (s sortableChannelStandup) Swap(i, j int) { s.keys[i], s.keys[j] = s.keys[j], s.keys[i] }
func (s sortableChannelStandup) Len() int      { return len(s.keys) }
func (s sortableChannelStandup) Less(i, j int) bool {
	return s.cs[s.keys[i]].ts.After(s.cs[s.keys[j]].ts)
}

// getKeysByTimestamp returns the userIDs of the standup ordered by their message timestamp (newer first).
func (cs channelStandup) getKeysByTimestamp() []string {
	keys := make([]string, 0, len(cs))
	for k := range cs {
		keys = append(keys, k)
	}
	scs := sortableChannelStandup{
		cs:   cs,
		keys: keys,
	}
	sort.Sort(scs)
	return scs.keys
}

// standups contains the channelStandup of all Slack channels known to the bot.
type standups map[string]channelStandup

// conversation is a generic way to access the IDs, Names and history of both
// slack.Channel and slack.Group. Unfortunately nlopes/slack doesn't expose the
// underlying common type (groupConversation) and we cannot define methods for
// non-local types, which would allow to make things much cleaner ...
type conversation interface {
	getID() string
	getName() string
	getHistory(*slack.RTM, slack.HistoryParameters) (*slack.History, error)
}

type channel slack.Channel

func (c channel) getID() string   { return c.ID }
func (c channel) getName() string { return c.Name }
func (c channel) getHistory(rtm *slack.RTM, params slack.HistoryParameters) (*slack.History, error) {
	return rtm.GetChannelHistory(c.getID(), params)
}

type group slack.Group

func (g group) getID() string   { return g.ID }
func (g group) getName() string { return g.Name }
func (g group) getHistory(rtm *slack.RTM, params slack.HistoryParameters) (*slack.History, error) {
	return rtm.GetGroupHistory(g.getID(), params)
}

type arriba struct {
	rtm              *slack.RTM
	botID            string
	botName          string
	extractMsgRE     *regexp.Regexp
	historyDaysLimit int
	standups         standups
}

func newArriba(rtm *slack.RTM, historyDaysLimit int) arriba {
	return arriba{
		rtm:              rtm,
		historyDaysLimit: historyDaysLimit,
		standups:         make(standups),
	}
}

func parseSlackTimeStamp(ts string) (time.Time, error) {
	var seconds, milliseconds int64
	_, err := fmt.Sscanf(ts, "%d.%d", &seconds, &milliseconds)
	if err != nil {
		logrus.Warn("Can't parse timestamp ", ts)
		return time.Now(), err
	}
	return time.Unix(seconds, milliseconds*1000), nil
}

// extractStandupMsg parses Slack messages starting with @bot-name
func (a arriba) extractChannelStandupMsg(msg slack.Msg) (standupMsg, bool) {
	if msg.Type != "message" || msg.SubType != "" {
		return standupMsg{}, false
	}
	standupText := a.extractMsgRE.ReplaceAllString(msg.Text, "$"+extractMsgGroupName)
	if len(standupText) == len(msg.Text) {
		// Nothing was extracted
		return standupMsg{}, false
	}
	ts, err := parseSlackTimeStamp(msg.Timestamp)
	if err != nil {
		return standupMsg{}, false
	}
	return standupMsg{ts, standupText}, true
}

func (a arriba) retrieveChannelStandup(c conversation) (channelStandup, error) {
	params := slack.NewHistoryParameters()
	params.Count = 1000
	now := time.Now().UTC()
	params.Latest = fmt.Sprintf("%d", now.Unix())
	params.Oldest = fmt.Sprintf("%d", now.AddDate(0, 0, -a.historyDaysLimit).Unix())

	// It would be way more efficient to use slack.SearchMsgs instead
	// of traversing the whole history, but that's not allowed for bots :(
	cstandup := make(channelStandup)
	for {
		logrus.Debugf(
			"Requesting history for conversation %s with parameters %#v",
			c.getID(),
			params)

		history, error := c.getHistory(a.rtm, params)
		if error != nil || history == nil || len(history.Messages) == 0 {
			return cstandup, error
		}

		logrus.Debugf(
			"Got history chunk (from %s to %s, latest %s) for conversation %s",
			history.Messages[len(history.Messages)-1].Msg.Timestamp,
			history.Messages[0].Msg.Timestamp, history.Latest, c.getID())

		for _, msg := range history.Messages {
			if _, ok := cstandup[msg.User]; ok {
				// we already have the latest standup message for this user
				continue
			}
			standupMsg, ok := a.extractChannelStandupMsg(msg.Msg)
			if ok && standupMsg.text != "" {
				cstandup[msg.User] = standupMsg
			}
		}

		if !history.HasMore {
			break
		}
		latestMsg := history.Messages[len(history.Messages)-1]
		params.Latest = latestMsg.Timestamp
		params.Inclusive = false
	}
	return cstandup, nil
}

func (a arriba) retrieveStandups(conversations []conversation) {
	for _, c := range conversations {
		logrus.Infof("Retrieveing standup for conversation #%s (%s)", c.getName(), c.getID())
		cstandup, err := a.retrieveChannelStandup(c)
		if err != nil {
			logrus.Errorf("Can't retrieve channel standup for conversation #%s: %s", c.getName(), err)
		}
		a.standups[c.getID()] = cstandup
		logrus.Infof("Standup for conversation #%s (%s) updated to %#v", c.getName(), c.getID(), cstandup)
	}
}

func (a arriba) getUserName(userID string) string {
	info, err := a.rtm.GetUserInfo(userID)
	userName := "id" + userID
	if err != nil {
		logrus.Errorf("Couldn't get user information for user %s: %s", userID, err)
	} else {
		userName = info.Name
	}
	return userName
}

func (a arriba) removeOldMessages(channelID string) {
	cstandup, ok := a.standups[channelID]
	if !ok {
		return
	}
	oldestAllowed := time.Now().UTC().AddDate(0, 0, -a.historyDaysLimit)
	for userID, msg := range cstandup {
		if msg.ts.Before(oldestAllowed) {
			delete(cstandup, userID)
		}
	}
}

func (a arriba) prettyPrintChannelStandup(cstandup channelStandup) string {
	text := "¡Ándale! ¡Ándale! here's the standup status :tada:\n"
	for _, userID := range cstandup.getKeysByTimestamp() {
		standupMsg := cstandup[userID]
		humanTime := humanize.Time(standupMsg.ts)
		userName := a.getUserName(userID)
		// Inject zero-width unicode character in username to avoid notifying users
		if len(userName) > 1 {
			userName = string(userName[0]) + "\ufeff" + string(userName[1:])
		}
		text += fmt.Sprintf("*%s*: %s _(%s)_\n", userName, standupMsg.text, humanTime)
	}
	return text
}

func (a arriba) sendStatus(channelID string) {
	var statusText string
	if cstandup, ok := a.standups[channelID]; ok && len(cstandup) > 0 {
		statusText = a.prettyPrintChannelStandup(cstandup)
	} else {
		statusText = fmt.Sprintf("No standup messages found\nType a message starting with *@%s* to record your standup message", a.botName)
	}
	a.rtm.SendMessage(a.rtm.NewOutgoingMessage(statusText, channelID))

}

func (a arriba) updateLastStandup(channelID, userID string, msg standupMsg) {
	if _, ok := a.standups[channelID]; !ok {
		a.standups[channelID] = make(channelStandup)
	}
	a.standups[channelID][userID] = msg
	confirmationText := fmt.Sprintf("<@%s>: ¡Yeppa! standup status recorded :taco:", userID)
	a.rtm.SendMessage(a.rtm.NewOutgoingMessage(confirmationText, channelID))
}

func (a *arriba) handleConnectedEvent(ev *slack.ConnectedEvent) {
	if a.botID != "" {
		logrus.Warn("Received unexpected Connected event")
		return
	}
	logrus.Infof(
		"Connected as user %s (%s) to team %s (%s)",
		ev.Info.User.Name,
		ev.Info.User.ID,
		ev.Info.Team.Name,
		ev.Info.Team.ID,
	)
	a.botID = ev.Info.User.ID
	a.botName = ev.Info.User.Name
	a.extractMsgRE = regexp.MustCompile(fmt.Sprintf(extractMsgPattern, a.botID))

	// Retrieve standups for public channels and private groups
	var conversations []conversation
	for _, c := range ev.Info.Channels {
		if c.IsMember {
			conversations = append(conversations, channel(c))
		}
	}
	for _, g := range ev.Info.Groups {
		conversations = append(conversations, group(g))
	}
	a.retrieveStandups(conversations)
}

func (a arriba) handleMessageEvent(ev *slack.MessageEvent) {
	logrus.Debugf("Message received %+v", ev)
	if a.botID == "" {
		logrus.Warn("Received message event before finishing initialization")
		return
	}
	if ev.Channel == "" {
		logrus.Warn("Received message with empty channel")
		return
	}
	switch ev.Channel[0] {
	case 'C', 'G':
		// Public and private (group) channels
		smsg, ok := a.extractChannelStandupMsg(ev.Msg)
		if !ok {
			return
		}
		logrus.Infof("Received standup message in channel %s: %+v", ev.Channel, smsg)
		// Garbage-collect old messages
		a.removeOldMessages(ev.Msg.Channel)
		if smsg.text == "" {
			a.sendStatus(ev.Msg.Channel)
		} else {
			a.updateLastStandup(ev.Msg.Channel, ev.Msg.User, smsg)
		}

	case 'D':
		// Direct messages are not supported yet
	}
}

func (a arriba) run() {
	go a.rtm.ManageConnection()

	for {
		select {
		case msg := <-a.rtm.IncomingEvents:
			switch ev := msg.Data.(type) {
			case *slack.ConnectedEvent:
				a.handleConnectedEvent(ev)
			case *slack.MessageEvent:
				a.handleMessageEvent(ev)
			case *slack.RTMError:
				logrus.Error("Invalid credentials", ev.Error())
			case *slack.InvalidAuthEvent:
				logrus.Error("Invalid credentials")
				os.Exit(1)
			}
		}
	}
}

func usage() {
	fmt.Fprintf(os.Stderr, "usage: %s [ flags ] <SlackAPItoken>\n", os.Args[0])
	flag.PrintDefaults()
	fmt.Fprintf(os.Stderr, "You can obtain <SlackAPItoken> from https://<yourteam>.slack.com/services/new/bot\n")
}

func main() {
	var (
		debug            bool
		historyDaysLimit int
	)

	flag.Usage = usage
	flag.BoolVar(&debug, "debug", false, "Print debug information")
	flag.IntVar(&historyDaysLimit, "history-limit", 7, "History limit (in days)")
	flag.Parse()
	if len(flag.Args()) < 1 || historyDaysLimit < 1 {
		usage()
		os.Exit(1)
	}

	logrus.SetOutput(os.Stderr)
	if debug {
		logrus.SetLevel(logrus.DebugLevel)
	}

	api := slack.New(flag.Arg(0))
	api.SetDebug(debug)

	newArriba(api.NewRTM(), historyDaysLimit).run()
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"

	"github.com/disiqueira/tindergo"
)

func main() {
	token := flag.String("token", "", "Your Facebook Token.")

	flag.Parse()

	if *token == "" {
		fmt.Println("You must provide a valid Facebook Token.")
		os.Exit(2)
	}

	t := tindergo.New()

	err := t.Authenticate(*token)
	checkError(err)

	profile, err := t.Profile()
	checkError(err)

	fmt.Println("Your Profile:")
	fmt.Println("Name: " + profile.Name)
	fmt.Println("")

	var allRecs []tindergo.RecsCoreUser

	for j := 0; j <= 3; j++ {
		recs, err := t.RecsCore()
		checkError(err)
		allRecs = append(allRecs, recs)
	}
}

func analizeRecs(recs []tindergo.RecsCoreUser) {
	for _, elem := range recs {
		fmt.Println(elem.Name)
	}
}

// checkError Panic application if has an error returned.
func checkError(err error) {
	if err != nil {
		panic(err)
	}
}
<commit_msg>Counting Recs repetitions<commit_after>package main

import (
	"flag"
	"fmt"
	"os"

	"github.com/disiqueira/tindergo"
)

func main() {
	token := flag.String("token", "", "Your Facebook Token.")

	flag.Parse()

	if *token == "" {
		fmt.Println("You must provide a valid Facebook Token.")
		os.Exit(2)
	}

	t := tindergo.New()

	err := t.Authenticate(*token)
	checkError(err)

	profile, err := t.Profile()
	checkError(err)

	fmt.Println("Your Profile:")
	fmt.Println("Name: " + profile.Name)
	fmt.Println("")

	var allRecs map[string]tindergo.RecsCoreUser
	var countRecs map[string]int

	for j := 0; j <= 3; j++ {
		recs, err := t.RecsCore()
		checkError(err)

		for _, elem := range recs {
			_, exist := allRecs[elem.ID]
			if exist {
				countRecs[elem.ID] = countRecs[elem.ID] + 1
			}
			allRecs[elem.ID] = elem
		}

	}
}

func analizeRecs(recs []tindergo.RecsCoreUser) {
	for _, elem := range recs {
		fmt.Println(elem.Name)
	}
}

// checkError Panic application if has an error returned.
func checkError(err error) {
	if err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"strconv"
	"strings"
)

const (
	Black  = `0`
	Red    = `1`
	Green  = `2`
	Brown  = `3`
	Blue   = `4`
	Purple = `5`
	Cyan   = `6`
	Gray   = `7`
)

// Color returns the ANSI colour code for the given background and foreground
// Note Bold is usually interpeted as 'light' these days. E.g. 'light blue.'
func Color(bg string, fg string, bold bool) string {
	var boldstr string
	if bold {
		boldstr = `1`
	} else {
		boldstr = `0`
	}
	return "\033[4" + bg + `;` + boldstr + `;3` + fg + `m`

}

func InverseColor() string {
	return "\033[7m"
}

func ResetColor() string {
	return "\033[0m"
}

// TerminalSize returns the width and height of the tty, respectively
func TerminalSize() (int, int, error) {
	cmd := exec.Command("stty", "size")
	cmd.Stdin = os.Stdin
	out, err := cmd.Output()
	if err != nil {
		return 0, 0, err
	}
	outarr := strings.Split(string(out), " ")
	if len(outarr) != 2 {
		return 0, 0, errors.New("Terminal size split error, stty size return unexpected: " + string(out))
	}

	height, err := strconv.Atoi(outarr[0])
	if err != nil {
		return 0, 0, errors.New("Termianl size height error, stty size return unexpected: " + string(outarr[0]))
	}

	width, err := strconv.Atoi(strings.Trim(outarr[1], " \n"))
	if err != nil {
		return 0, 0, errors.New("Terminal size width error, stty size return unexpected: " + string(outarr[1]))
	}

	return width, height, nil
}

type JsonHeading struct {
	Title    string   `json:"title"`
	Commands []string `json:"commands"`
}

type JsonHeadings struct {
	Headings []JsonHeading `json:"headings"`
}

func (h JsonHeading) Width() int {
	max := len(h.Title)
	for _, v := range h.Commands {
		if len(v) > max {
			max = len(v)
		}
	}
	return max
}

func (hs JsonHeadings) HeadingWidth() int {
	max := 0
	for _, v := range hs.Headings {
		if v.Width() > max {
			max = v.Width()
		}
	}
	return max
}

func (hs JsonHeadings) CommandsHeight() int {
	max := 0
	for _, v := range hs.Headings {
		if len(v.Commands) > max {
			max = len(v.Commands)
		}
	}
	return max
}

func (hs JsonHeadings) RowHasCommands(start, end, row int) bool {
	for i := start; i < end; i++ {
		heading := hs.Headings[i]
		if len(heading.Commands) >= (row + 1) {
			return true
		}
	}
	return false
}

func (hs *JsonHeadings) PrintHeadings(start, end, width int) string {
	if end > len(hs.Headings) {
		end = len(hs.Headings)
	}

	var s string
	for i := start; i != end; i++ {
		heading := hs.Headings[i]
		s += InverseColor()
		s += heading.Title + strings.Repeat(" ", width-len(heading.Title)-1)
		s += ResetColor()
		s += ` `
	}

	commandsHeight := hs.CommandsHeight()
	for j := 0; j != commandsHeight; j++ {
		if !hs.RowHasCommands(start, end, j) {
			continue
		}
		s += "\n"
		for i := start; i < end; i++ {
			heading := hs.Headings[i]
			if len(heading.Commands) <= j {
				s += strings.Repeat(" ", width)
				continue
			}
			command := heading.Commands[j]
			s += command + strings.Repeat(" ", width-len(command))
		}
	}

	return s
}

func (hs *JsonHeadings) PrintString(width int) string {
	var s string

	headingWidth := hs.HeadingWidth() + 1
	headingsPerLine := width / headingWidth // +1 because headings are separated
	for i := 0; i < len(hs.Headings); i += headingsPerLine {
		s += hs.PrintHeadings(i, i+headingsPerLine, headingWidth) + "\n"
	}
	return s
}

func main() {
	if len(os.Args) < 2 {
		fmt.Println(`Usage: cman <program>`)
		return
	}

	filename := os.Args[1] + `.json`

	if _, err := os.Stat(filename); os.IsNotExist(err) {
		fmt.Println(`Cheatsheet Man does not exist for ` + os.Args[1])
		return
	}

	file, err := ioutil.ReadFile(filename)
	if err != nil {
		fmt.Printf("File error: %v\n", err)
		return
	}

	var jsonHeadings JsonHeadings
	err = json.Unmarshal(file, &jsonHeadings)
	if err != nil {
		fmt.Printf("JSON unmarshal error: %v\n", err)
		return
	}

	width, _, err := TerminalSize()
	if err != nil {
		fmt.Printf("Terminal size error: %v\n", err)
		return
	}

	fmt.Println(jsonHeadings.PrintString(width))
}
<commit_msg>Added godoc comments<commit_after>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"strconv"
	"strings"
)

const (
	Black  = `0`
	Red    = `1`
	Green  = `2`
	Brown  = `3`
	Blue   = `4`
	Purple = `5`
	Cyan   = `6`
	Gray   = `7`
)

// Color returns the ANSI colour code for the given background and foreground.
// Note Bold is usually interpeted as 'light' these days. E.g. 'light blue.'
func Color(bg string, fg string, bold bool) string {
	var boldstr string
	if bold {
		boldstr = `1`
	} else {
		boldstr = `0`
	}
	return "\033[4" + bg + `;` + boldstr + `;3` + fg + `m`

}

// InverseColor returns the inverse ANSI colour code, which, when sent to the terminal, will invert the current colours.
func InverseColor() string {
	return "\033[7m"
}

// ResetColor returns the reset ANSI colour code, which will reset the terminal colours to their default
func ResetColor() string {
	return "\033[0m"
}

// TerminalSize returns the width and height of the tty, respectively
func TerminalSize() (int, int, error) {
	cmd := exec.Command("stty", "size")
	cmd.Stdin = os.Stdin
	out, err := cmd.Output()
	if err != nil {
		return 0, 0, err
	}
	outarr := strings.Split(string(out), " ")
	if len(outarr) != 2 {
		return 0, 0, errors.New("Terminal size split error, stty size return unexpected: " + string(out))
	}

	height, err := strconv.Atoi(outarr[0])
	if err != nil {
		return 0, 0, errors.New("Termianl size height error, stty size return unexpected: " + string(outarr[0]))
	}

	width, err := strconv.Atoi(strings.Trim(outarr[1], " \n"))
	if err != nil {
		return 0, 0, errors.New("Terminal size width error, stty size return unexpected: " + string(outarr[1]))
	}

	return width, height, nil
}

// JsonHeading represents a cheatsheet heading definition. This is designed to be parsed from JSON.
type JsonHeading struct {
	Title    string   `json:"title"`
	Commands []string `json:"commands"`
}

// JsonHeading represents a cheatsheet definition, of all headings. This is designed to be parsed from JSON.
type JsonHeadings struct {
	Headings []JsonHeading `json:"headings"`
}

// Width returns the width of this JsonHeading, which is the width of the longest command.
func (h JsonHeading) Width() int {
	max := len(h.Title)
	for _, v := range h.Commands {
		if len(v) > max {
			max = len(v)
		}
	}
	return max
}

// HeadingWidth returns the width which headings should be, which is the width of the widest heading
func (hs JsonHeadings) HeadingWidth() int {
	max := 0
	for _, v := range hs.Headings {
		if v.Width() > max {
			max = v.Width()
		}
	}
	return max
}

// CommandsHeight is the height which commands should be, which is the number of commands in the heading with the most commands.
func (hs JsonHeadings) CommandsHeight() int {
	max := 0
	for _, v := range hs.Headings {
		if len(v.Commands) > max {
			max = len(v.Commands)
		}
	}
	return max
}

// RowHasCommands returns whether a given row has any commands.
// This is used to not display blank lines, if all the headings in a given row have fewer than the tallest row.
func (hs JsonHeadings) RowHasCommands(start, end, row int) bool {
	for i := start; i < end; i++ {
		heading := hs.Headings[i]
		if len(heading.Commands) >= (row + 1) {
			return true
		}
	}
	return false
}

// PrintHeadings returns the heading string to be printed to the console, including ANSI color codes
func (hs *JsonHeadings) PrintHeadings(start, end, width int) string {
	if end > len(hs.Headings) {
		end = len(hs.Headings)
	}

	var s string
	for i := start; i != end; i++ {
		heading := hs.Headings[i]
		s += InverseColor()
		s += heading.Title + strings.Repeat(" ", width-len(heading.Title)-1)
		s += ResetColor()
		s += ` `
	}

	commandsHeight := hs.CommandsHeight()
	for j := 0; j != commandsHeight; j++ {
		if !hs.RowHasCommands(start, end, j) {
			continue
		}
		s += "\n"
		for i := start; i < end; i++ {
			heading := hs.Headings[i]
			if len(heading.Commands) <= j {
				s += strings.Repeat(" ", width)
				continue
			}
			command := heading.Commands[j]
			s += command + strings.Repeat(" ", width-len(command))
		}
	}

	return s
}

// PrintString returns a string for printing, of the headings, including ANSI color codes, formatted to the given width.
func (hs *JsonHeadings) PrintString(width int) string {
	var s string

	headingWidth := hs.HeadingWidth() + 1
	headingsPerLine := width / headingWidth // +1 because headings are separated
	for i := 0; i < len(hs.Headings); i += headingsPerLine {
		s += hs.PrintHeadings(i, i+headingsPerLine, headingWidth) + "\n"
	}
	return s
}

func main() {
	if len(os.Args) < 2 {
		fmt.Println(`Usage: cman <program>`)
		return
	}

	filename := os.Args[1] + `.json`

	if _, err := os.Stat(filename); os.IsNotExist(err) {
		fmt.Println(`Cheatsheet Man does not exist for ` + os.Args[1])
		return
	}

	file, err := ioutil.ReadFile(filename)
	if err != nil {
		fmt.Printf("File error: %v\n", err)
		return
	}

	var jsonHeadings JsonHeadings
	err = json.Unmarshal(file, &jsonHeadings)
	if err != nil {
		fmt.Printf("JSON unmarshal error: %v\n", err)
		return
	}

	width, _, err := TerminalSize()
	if err != nil {
		fmt.Printf("Terminal size error: %v\n", err)
		return
	}

	fmt.Println(jsonHeadings.PrintString(width))
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/codegangsta/negroni"
	"github.com/gorilla/mux"
	"github.com/luismesas/goPi/piface"
	"github.com/luismesas/goPi/spi"
	"github.com/unrolled/render"
	"net/http"
	"strconv"
	"time"
)

var progress int
var running bool

func main() {
	// Render engine
	r := render.New(render.Options{
		Layout: "layout",
	})

	// Handlers
	router := mux.NewRouter()
	router.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		r.HTML(w, http.StatusOK, "home", progress)
	})
	router.HandleFunc("/about", func(w http.ResponseWriter, req *http.Request) {
		r.HTML(w, http.StatusOK, "about", nil)
	})
	router.HandleFunc("/start", func(w http.ResponseWriter, req *http.Request) {
		time := req.FormValue("time")
		stepsPerMinute, _ := strconv.Atoi(req.FormValue("steps"))
		var hours, minutes, seconds int
		fmt.Sscanf(time, "%d:%d:%d", &hours, &minutes, &seconds)
		fmt.Println(hours, minutes, seconds)
		go startLapse((hours*3600)+(minutes*60)+seconds, stepsPerMinute)
		r.HTML(w, http.StatusOK, "home", progress)
	}).Methods("POST")

	// HTTP Server
	n := negroni.Classic()
	n.UseHandler(router)
	n.Run(":3000")
}

func startLapse(seconds int, stepsPerMinute int) {
	go spin(stepsPerMinute)
	time.Sleep(time.Second * time.Duration(secods))
	running = false
}

func spin(stepsPerMinute int) {
	var pauseTime float64
	pauseTime = float64(stepsPerMinute) / float64(60)
	pfd := piface.NewPiFaceDigital(spi.DEFAULT_HARDWARE_ADDR, spi.DEFAULT_BUS, spi.DEFAULT_CHIP)

	// initializes pifacedigital board
	err := pfd.InitBoard()
	if err != nil {
		fmt.Printf("Error on init board: %s", err)
		return
	}

	pfd.OutputPins[2].SetValue(0)
	running = true
	for running {
		pfd.OutputPins[2].Toggle()
		time.Sleep(time.Second / 10)
		pfd.OutputPins[2].Toggle()
		time.Sleep(time.Duration(float64(time.Second) * pauseTime))
	}
}

func testMotor() {
	// creates a new pifacedigital instance
	pfd := piface.NewPiFaceDigital(spi.DEFAULT_HARDWARE_ADDR, spi.DEFAULT_BUS, spi.DEFAULT_CHIP)

	// initializes pifacedigital board
	err := pfd.InitBoard()
	if err != nil {
		fmt.Printf("Error on init board: %s", err)
		return
	}
	pfd.OutputPins[0].Toggle()
	time.Sleep(time.Second / 10)
	pfd.OutputPins[0].Toggle()
}
<commit_msg>reverted time calculation change<commit_after>package main

import (
	"fmt"
	"github.com/codegangsta/negroni"
	"github.com/gorilla/mux"
	"github.com/luismesas/goPi/piface"
	"github.com/luismesas/goPi/spi"
	"github.com/unrolled/render"
	"net/http"
	"strconv"
	"time"
)

var progress int
var running bool

func main() {
	// Render engine
	r := render.New(render.Options{
		Layout: "layout",
	})

	// Handlers
	router := mux.NewRouter()
	router.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		r.HTML(w, http.StatusOK, "home", progress)
	})
	router.HandleFunc("/about", func(w http.ResponseWriter, req *http.Request) {
		r.HTML(w, http.StatusOK, "about", nil)
	})
	router.HandleFunc("/start", func(w http.ResponseWriter, req *http.Request) {
		time := req.FormValue("time")
		stepsPerMinute, _ := strconv.Atoi(req.FormValue("steps"))
		var hours, minutes, seconds int
		fmt.Sscanf(time, "%d:%d:%d", &hours, &minutes, &seconds)
		fmt.Println(hours, minutes, seconds)
		go startLapse((hours*3600)+(minutes*60)+seconds, stepsPerMinute)
		r.HTML(w, http.StatusOK, "home", progress)
	}).Methods("POST")

	// HTTP Server
	n := negroni.Classic()
	n.UseHandler(router)
	n.Run(":3000")
}

func startLapse(seconds int, stepsPerMinute int) {
	go spin(stepsPerMinute)
	time.Sleep(time.Second * time.Duration(seconds))
	running = false
}

func spin(stepsPerMinute int) {
	var pauseTime float64
	pauseTime = float64(60) / float64(stepsPerMinute)
	pfd := piface.NewPiFaceDigital(spi.DEFAULT_HARDWARE_ADDR, spi.DEFAULT_BUS, spi.DEFAULT_CHIP)

	// initializes pifacedigital board
	err := pfd.InitBoard()
	if err != nil {
		fmt.Printf("Error on init board: %s", err)
		return
	}

	pfd.OutputPins[2].SetValue(0)
	running = true
	for running {
		pfd.OutputPins[2].Toggle()
		time.Sleep(time.Second / 10)
		pfd.OutputPins[2].Toggle()
		time.Sleep(time.Duration(float64(time.Second) * pauseTime))
	}
}

func testMotor() {
	// creates a new pifacedigital instance
	pfd := piface.NewPiFaceDigital(spi.DEFAULT_HARDWARE_ADDR, spi.DEFAULT_BUS, spi.DEFAULT_CHIP)

	// initializes pifacedigital board
	err := pfd.InitBoard()
	if err != nil {
		fmt.Printf("Error on init board: %s", err)
		return
	}
	pfd.OutputPins[0].Toggle()
	time.Sleep(time.Second / 10)
	pfd.OutputPins[0].Toggle()
}
<|endoftext|>


<commit_before>package marionette_client

import (
	"testing"
	"time"
)

const (
	TARGET_URL        = "http://www.abola.pt/"
	ID_SELECTOR       = "clubes-hp"
	CSS_SELECTOR_LI   = "li"
	ID_SELECTOR_INPUT = "topo_txtPesquisa"
)

var client *Client

func init() {
	client = NewClient()
	client.Transport(&MarionetteTransport{})
	RunningInDebugMode = true
}

func TestNewSession(t *testing.T) {
	err := client.Connect("", 0)
	if err != nil {
		t.Error(err)
	}

	r, err := client.NewSession("", nil)
	if err != nil {
		t.Error(err)
	}

	t.Log(r.Value)
}

// working
func TestGetSessionID(t *testing.T) {
	if client.SessionId != client.SessionID() {
		t.Fatalf("SessionId differ...")
	}

	t.Log("session is : ", client.SessionId)
}

func TestGetPage(t *testing.T) {
	r, err := client.Navigate(TARGET_URL)
	if err != nil {
		t.Fatalf("%#v", err)
	}

	t.Log(r.Value)
}

func TestCurrentUrl(t *testing.T) {
	url, err := client.CurrentUrl()
	if err != nil {
		t.Fatalf("%#v", err)
	}

	if url != TARGET_URL {
		t.Fatalf("Current Url %v not equal to target url %v", url, TARGET_URL)
	}

}

func TestGetCookies(t *testing.T) {
	r, err := client.Cookies()
	if err != nil {
		t.Fatalf("%#v", err)
	}

	t.Log(r.Value)
}

func TestGetCookie(t *testing.T) {
	r, err := client.Cookie("abolaCookie")
	if err != nil {
		t.Fatalf("%#v", err)
	}

	t.Log(r.Value)
}

//func TestConnectWithActiveConnection(t *testing.T) {
//	err := client.Connect("", 0)
//	if err == nil {
//		t.Fatalf("%#v", err)
//	}
//
//	t.Log("No Error..")
//}

// working
func TestGetSessionCapabilities(t *testing.T) {
	r, err := client.Capabilities()
	if err != nil {
		t.Fatalf("%#v", err)
	}

	t.Log(r.BrowserName)
}

// working
func TestScreenshot(t *testing.T) {
	_, err := client.Screenshot()
	if err != nil {
		t.Fatal(err)
	}

	//this print ise a problem for travis builds, since it can surpass the 4 MB of log size.
	// don't print the base64 encoded image.
	//println(base64encoded)
}

// working
func TestLog(t *testing.T) {
	r, err := client.Log("message testing", "warning")
	if err != nil {
		t.Fatalf("%#v", err)
	}

	t.Log(r.Value)
}

// working
func TestGetLogs(t *testing.T) {
	r, err := client.Logs()
	if err != nil {
		t.Fatalf("%#v", err)
	}

	t.Log(r.Value)
}

func TestSetContext(t *testing.T) {
	r, err := client.SetContext(Context(CHROME))
	if err != nil {
		t.Fatalf("%#v", err)
	}

	t.Log(r.Value)

	r, err = client.SetContext(Context(CONTENT))
	if err != nil {
		t.Fatalf("%#v", err)
	}

	t.Log(r.Value)
}

func TestGetContext(t *testing.T) {
	r, err := client.Context()
	if err != nil {
		t.Fatalf("%#v", err)
	}

	t.Log(r.Value)
}

func TestGetPageSource(t *testing.T) {
	r, err := client.SetContext(Context(CHROME))
	if err != nil {
		t.Fatalf("%#v", err)
	}

	t.Log(r.Value)

	r, err = client.SetContext(Context(CONTENT))
	if err != nil {
		t.Fatalf("%#v", err)
	}

	t.Log(r.Value)

}

func TestSetScriptTimout(t *testing.T) {
	r, err := client.SetScriptTimeout(1000)
	if err != nil {
		t.Fatalf("%#v", err)
	}

	t.Log(r.Value)
}

func TestSetPageTimout(t *testing.T) {
	r, err := client.SetPageTimeout(1000)
	if err != nil {
		t.Fatalf("%#v", err)
	}

	t.Log(r.Value)
}

func TestSetSearchTimout(t *testing.T) {
	r, err := client.SetSearchTimeout(1000)
	if err != nil {
		t.Fatalf("%#v", err)
	}

	t.Log(r.Value)
}

func TestExecuteScriptWithoutFunction(t *testing.T) {
	script := "return (document.readyState == 'complete');"
	args := []interface{}{}
	r, err := client.ExecuteScript(script, args, 1000, false)
	if err != nil {
		t.Fatalf("%#v", err)
	}

	t.Log(r.Value)
}

func TestExecuteScript(t *testing.T) {
	script := "function testMyGoMarionetteClient() { return 'yes'; } return testMyGoMarionetteClient();"
	args := []interface{}{}
	r, err := client.ExecuteScript(script, args, 1000, false)
	if err != nil {
		t.Fatalf("%#v", err)
	}

	t.Log(r.Value)
}

func TestExecuteScriptWithArgs(t *testing.T) {
	script := "function testMyGoMarionetteClientArgs(a, b) { return a + b; }; return testMyGoMarionetteClientArgs(arguments[0], arguments[1]);"
	args := []interface{}{1, 3}
	r, err := client.ExecuteScript(script, args, 1000, false)
	if err != nil {
		t.Fatalf("%#v", err)
	}

	t.Log(r.Value)
}

func TestGetTitle(t *testing.T) {
	title, err := client.Title()
	if err != nil {
		t.Fatalf("%#v", err)
	}

	t.Log(title)

}
func TestFindElement(t *testing.T) {
	element, err := client.FindElement(By(ID), ID_SELECTOR)
	if err != nil {
		t.Fatalf("%#v", err)
	}

	t.Log(element.Id())
	t.Log(element.Enabled())
	t.Log(element.Selected())
	t.Log(element.Displayed())
	t.Log(element.TagName())
	t.Log(element.Text())
	t.Log(element.Attribute("id"))
	t.Log(element.CssValue("text-decoration"))
	t.Log(element.Rect())

	collection, err := element.FindElements(By(CSS_SELECTOR), CSS_SELECTOR_LI)
	if 18 != len(collection) {
		t.FailNow()
	}

	t.Logf("%T %#V", collection, collection)
}

func TestSendKeys(t *testing.T) {
	e, err := client.FindElement(By(ID), ID_SELECTOR_INPUT)
	if err != nil {
		t.Fatalf("%#v", err)
	}

	e.SendKeys("teste")
}

func TestFindElements(t *testing.T) {
	elements, err := client.FindElements(By(CSS_SELECTOR), CSS_SELECTOR_LI)
	if err != nil {
		t.Fatalf("%#v", err)
	}

	t.Log(len(elements))
}

func TestCurrentChromeWindowHandle(t *testing.T) {
	r, err := client.CurrentChromeWindowHandle()
	if err != nil {
		t.Fatalf("%#v", err)
	}

	t.Log(r.Value)
}

func TestWindowHandles(t *testing.T) {
	w, err := client.CurrentWindowHandle()
	if err != nil {
		t.Fatalf("%#v", err)
	}

	t.Log(w)

	r, err := client.WindowHandles()
	if err != nil {
		t.Fatalf("%#v", err)
	}

	for _, w := range r {
		err := client.SwitchToWindow(w)
		if err != nil {
			t.Fatalf("%#v", err)
		}

		time.Sleep(time.Duration(time.Second))
	}

	// return to original window.
	client.SwitchToWindow(w)
}

func TestNavigatorMethods(t *testing.T) {
	client.SetContext(Context(CONTENT))
	url1 := "http://www.google.com/"
	url2 := "http://www.bing.com/"

	client.Navigate(url1)
	sleep := time.Duration(2) * time.Second
	time.Sleep(sleep)

	client.Navigate(url2)
	time.Sleep(sleep)

	client.Back()
	client.Refresh()
	firstUrl, err := client.CurrentUrl()
	if err != nil {
		t.Fatalf("%#v", err)
	}

	if firstUrl != url1 {
		t.Fatalf("Expected url %v - received url %v", url1, firstUrl)
	}

	client.Forward()
	secondUrl, err := client.CurrentUrl()
	if err != nil {
		t.Fatalf("%#v", err)
	}

	if secondUrl != url2 {
		t.Fatalf("Expected url %v - received url %v", url2, secondUrl)
	}
}

// working - if called before other tests all hell will break loose
//func TestCloseWindow(t *testing.T) {
//	r, err := client.CloseWindow()
//	if err != nil {
//		t.Fatalf("%#v", err)
//	}
//
//	t.Log(r.Value)
//}

// working
//func TestQuitApplication(t *testing.T) {
//	r, err := client.QuitApplication()
//	if err != nil {
//		t.Fatalf("%#v", err)
//	}
//
//	t.Log(r.ResponseError)
//}
<commit_msg>added time.Sleep() after Refresh on Navigator interface test.<commit_after>package marionette_client

import (
	"testing"
	"time"
)

const (
	TARGET_URL        = "http://www.abola.pt/"
	ID_SELECTOR       = "clubes-hp"
	CSS_SELECTOR_LI   = "li"
	ID_SELECTOR_INPUT = "topo_txtPesquisa"
)

var client *Client

func init() {
	client = NewClient()
	client.Transport(&MarionetteTransport{})
	RunningInDebugMode = true
}

func TestNewSession(t *testing.T) {
	err := client.Connect("", 0)
	if err != nil {
		t.Error(err)
	}

	r, err := client.NewSession("", nil)
	if err != nil {
		t.Error(err)
	}

	t.Log(r.Value)
}

// working
func TestGetSessionID(t *testing.T) {
	if client.SessionId != client.SessionID() {
		t.Fatalf("SessionId differ...")
	}

	t.Log("session is : ", client.SessionId)
}

func TestGetPage(t *testing.T) {
	r, err := client.Navigate(TARGET_URL)
	if err != nil {
		t.Fatalf("%#v", err)
	}

	t.Log(r.Value)
}

func TestCurrentUrl(t *testing.T) {
	url, err := client.CurrentUrl()
	if err != nil {
		t.Fatalf("%#v", err)
	}

	if url != TARGET_URL {
		t.Fatalf("Current Url %v not equal to target url %v", url, TARGET_URL)
	}

}

func TestGetCookies(t *testing.T) {
	r, err := client.Cookies()
	if err != nil {
		t.Fatalf("%#v", err)
	}

	t.Log(r.Value)
}

func TestGetCookie(t *testing.T) {
	r, err := client.Cookie("abolaCookie")
	if err != nil {
		t.Fatalf("%#v", err)
	}

	t.Log(r.Value)
}

//func TestConnectWithActiveConnection(t *testing.T) {
//	err := client.Connect("", 0)
//	if err == nil {
//		t.Fatalf("%#v", err)
//	}
//
//	t.Log("No Error..")
//}

// working
func TestGetSessionCapabilities(t *testing.T) {
	r, err := client.Capabilities()
	if err != nil {
		t.Fatalf("%#v", err)
	}

	t.Log(r.BrowserName)
}

// working
func TestScreenshot(t *testing.T) {
	_, err := client.Screenshot()
	if err != nil {
		t.Fatal(err)
	}

	//this print ise a problem for travis builds, since it can surpass the 4 MB of log size.
	// don't print the base64 encoded image.
	//println(base64encoded)
}

// working
func TestLog(t *testing.T) {
	r, err := client.Log("message testing", "warning")
	if err != nil {
		t.Fatalf("%#v", err)
	}

	t.Log(r.Value)
}

// working
func TestGetLogs(t *testing.T) {
	r, err := client.Logs()
	if err != nil {
		t.Fatalf("%#v", err)
	}

	t.Log(r.Value)
}

func TestSetContext(t *testing.T) {
	r, err := client.SetContext(Context(CHROME))
	if err != nil {
		t.Fatalf("%#v", err)
	}

	t.Log(r.Value)

	r, err = client.SetContext(Context(CONTENT))
	if err != nil {
		t.Fatalf("%#v", err)
	}

	t.Log(r.Value)
}

func TestGetContext(t *testing.T) {
	r, err := client.Context()
	if err != nil {
		t.Fatalf("%#v", err)
	}

	t.Log(r.Value)
}

func TestGetPageSource(t *testing.T) {
	r, err := client.SetContext(Context(CHROME))
	if err != nil {
		t.Fatalf("%#v", err)
	}

	t.Log(r.Value)

	r, err = client.SetContext(Context(CONTENT))
	if err != nil {
		t.Fatalf("%#v", err)
	}

	t.Log(r.Value)

}

func TestSetScriptTimout(t *testing.T) {
	r, err := client.SetScriptTimeout(1000)
	if err != nil {
		t.Fatalf("%#v", err)
	}

	t.Log(r.Value)
}

func TestSetPageTimout(t *testing.T) {
	r, err := client.SetPageTimeout(1000)
	if err != nil {
		t.Fatalf("%#v", err)
	}

	t.Log(r.Value)
}

func TestSetSearchTimout(t *testing.T) {
	r, err := client.SetSearchTimeout(1000)
	if err != nil {
		t.Fatalf("%#v", err)
	}

	t.Log(r.Value)
}

func TestExecuteScriptWithoutFunction(t *testing.T) {
	script := "return (document.readyState == 'complete');"
	args := []interface{}{}
	r, err := client.ExecuteScript(script, args, 1000, false)
	if err != nil {
		t.Fatalf("%#v", err)
	}

	t.Log(r.Value)
}

func TestExecuteScript(t *testing.T) {
	script := "function testMyGoMarionetteClient() { return 'yes'; } return testMyGoMarionetteClient();"
	args := []interface{}{}
	r, err := client.ExecuteScript(script, args, 1000, false)
	if err != nil {
		t.Fatalf("%#v", err)
	}

	t.Log(r.Value)
}

func TestExecuteScriptWithArgs(t *testing.T) {
	script := "function testMyGoMarionetteClientArgs(a, b) { return a + b; }; return testMyGoMarionetteClientArgs(arguments[0], arguments[1]);"
	args := []interface{}{1, 3}
	r, err := client.ExecuteScript(script, args, 1000, false)
	if err != nil {
		t.Fatalf("%#v", err)
	}

	t.Log(r.Value)
}

func TestGetTitle(t *testing.T) {
	title, err := client.Title()
	if err != nil {
		t.Fatalf("%#v", err)
	}

	t.Log(title)

}
func TestFindElement(t *testing.T) {
	element, err := client.FindElement(By(ID), ID_SELECTOR)
	if err != nil {
		t.Fatalf("%#v", err)
	}

	t.Log(element.Id())
	t.Log(element.Enabled())
	t.Log(element.Selected())
	t.Log(element.Displayed())
	t.Log(element.TagName())
	t.Log(element.Text())
	t.Log(element.Attribute("id"))
	t.Log(element.CssValue("text-decoration"))
	t.Log(element.Rect())

	collection, err := element.FindElements(By(CSS_SELECTOR), CSS_SELECTOR_LI)
	if 18 != len(collection) {
		t.FailNow()
	}

	t.Logf("%T %#V", collection, collection)
}

func TestSendKeys(t *testing.T) {
	e, err := client.FindElement(By(ID), ID_SELECTOR_INPUT)
	if err != nil {
		t.Fatalf("%#v", err)
	}

	e.SendKeys("teste")
}

func TestFindElements(t *testing.T) {
	elements, err := client.FindElements(By(CSS_SELECTOR), CSS_SELECTOR_LI)
	if err != nil {
		t.Fatalf("%#v", err)
	}

	t.Log(len(elements))
}

func TestCurrentChromeWindowHandle(t *testing.T) {
	r, err := client.CurrentChromeWindowHandle()
	if err != nil {
		t.Fatalf("%#v", err)
	}

	t.Log(r.Value)
}

func TestWindowHandles(t *testing.T) {
	w, err := client.CurrentWindowHandle()
	if err != nil {
		t.Fatalf("%#v", err)
	}

	t.Log(w)

	r, err := client.WindowHandles()
	if err != nil {
		t.Fatalf("%#v", err)
	}

	for _, w := range r {
		err := client.SwitchToWindow(w)
		if err != nil {
			t.Fatalf("%#v", err)
		}

		time.Sleep(time.Duration(time.Second))
	}

	// return to original window.
	client.SwitchToWindow(w)
}

func TestNavigatorMethods(t *testing.T) {
	client.SetContext(Context(CONTENT))
	url1 := "http://www.google.com/"
	url2 := "http://www.bing.com/"

	client.Navigate(url1)
	sleep := time.Duration(2) * time.Second
	time.Sleep(sleep)

	client.Navigate(url2)
	time.Sleep(sleep)

	client.Back()
	client.Refresh()
	time.Sleep(sleep)

	firstUrl, err := client.CurrentUrl()
	if err != nil {
		t.Fatalf("%#v", err)
	}

	if firstUrl != url1 {
		t.Fatalf("Expected url %v - received url %v", url1, firstUrl)
	}

	client.Forward()
	secondUrl, err := client.CurrentUrl()
	if err != nil {
		t.Fatalf("%#v", err)
	}

	if secondUrl != url2 {
		t.Fatalf("Expected url %v - received url %v", url2, secondUrl)
	}
}

// working - if called before other tests all hell will break loose
//func TestCloseWindow(t *testing.T) {
//	r, err := client.CloseWindow()
//	if err != nil {
//		t.Fatalf("%#v", err)
//	}
//
//	t.Log(r.Value)
//}

// working
//func TestQuitApplication(t *testing.T) {
//	r, err := client.QuitApplication()
//	if err != nil {
//		t.Fatalf("%#v", err)
//	}
//
//	t.Log(r.ResponseError)
//}
<|endoftext|>


<commit_before>// Copyright 2016 The front Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package main

import (
	"github.com/repejota/logger"
)

// CLI entrypoint.
func main() {
	l := logger.New("default")

	// Start server
	err := Start()
	if err != nil {
		l.Errorf(err.Error())
	}
}
<commit_msg>Using panic on main, avoid log for error<commit_after>// Copyright 2016 The front Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package main

func main() {
	err := Start()
	if err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>/*
 *
 * k6 - a next-generation load testing tool
 * Copyright (C) 2016 Load Impact
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package main

import (
	log "github.com/Sirupsen/logrus"
	"gopkg.in/urfave/cli.v1"
	"os"
)

func main() {
	// This won't be needed in cli v2
	cli.VersionFlag.Name = "version"
	cli.HelpFlag.Name = "help"
	cli.HelpFlag.Hidden = true

	app := cli.NewApp()
	app.Name = "k6"
	app.Usage = "a next generation load generator"
	app.Version = "0.4.5"
	app.Commands = []cli.Command{
		commandRun,
		commandInspect,
		commandStatus,
		commandStats,
		commandScale,
		commandPause,
		commandResume,
	}
	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:  "verbose, v",
			Usage: "show debug messages",
		},
		cli.StringFlag{
			Name:  "address, a",
			Usage: "address for the API",
			Value: "127.0.0.1:6565",
		},
	}
	app.Before = func(cc *cli.Context) error {
		if cc.Bool("verbose") {
			log.SetLevel(log.DebugLevel)
		}

		return nil
	}
	if err := app.Run(os.Args); err != nil {
		os.Exit(1)
	}
}
<commit_msg>v0.5.0<commit_after>/*
 *
 * k6 - a next-generation load testing tool
 * Copyright (C) 2016 Load Impact
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package main

import (
	log "github.com/Sirupsen/logrus"
	"gopkg.in/urfave/cli.v1"
	"os"
)

func main() {
	// This won't be needed in cli v2
	cli.VersionFlag.Name = "version"
	cli.HelpFlag.Name = "help"
	cli.HelpFlag.Hidden = true

	app := cli.NewApp()
	app.Name = "k6"
	app.Usage = "a next generation load generator"
	app.Version = "0.5.0"
	app.Commands = []cli.Command{
		commandRun,
		commandInspect,
		commandStatus,
		commandStats,
		commandScale,
		commandPause,
		commandResume,
	}
	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:  "verbose, v",
			Usage: "show debug messages",
		},
		cli.StringFlag{
			Name:  "address, a",
			Usage: "address for the API",
			Value: "127.0.0.1:6565",
		},
	}
	app.Before = func(cc *cli.Context) error {
		if cc.Bool("verbose") {
			log.SetLevel(log.DebugLevel)
		}

		return nil
	}
	if err := app.Run(os.Args); err != nil {
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package tachyon

import (
	"fmt"
	"github.com/jessevdk/go-flags"
	"os"
	"path/filepath"
)

type Options struct {
	Vars        map[string]string `short:"s" long:"set" description:"Set a variable"`
	ShowOutput  bool              `short:"o" long:"output" description:"Show command output"`
	Host        string            `short:"t" long:"host" description:"Run the playbook on another host"`
	Development bool              `long:"dev" description:"Use a dev version of tachyon"`
	CleanHost   bool              `long:"clean-host" description:"Clean the host cache before using"`
	Debug       bool              `short:"d" long:"debug" description:"Show all information about commands"`
	Release     string            `long:"release" description:"The release to use when remotely invoking tachyon"`
	JSON        bool              `long:"json" description:"Output the run details in chunked json"`
}

var Release string = "dev"
var Arg0 string

func Main(args []string) int {
	var opts Options

	abs, err := filepath.Abs(args[0])
	if err != nil {
		panic(err)
	}

	Arg0 = abs

	parser := flags.NewParser(&opts, flags.Default)

	for _, o := range parser.Command.Group.Groups()[0].Options() {
		if o.LongName == "release" {
			o.Default = []string{Release}
		}
	}
	args, err = parser.ParseArgs(args)

	if err != nil {
		if serr, ok := err.(*flags.Error); ok {
			if serr.Type == flags.ErrHelp {
				return 2
			}
		}

		fmt.Printf("Error parsing options: %s", err)
		return 1
	}

	if len(args) != 2 {
		fmt.Printf("Usage: tachyon [options] <playbook>\n")
		return 1
	}

	if opts.Host != "" {
		return runOnHost(&opts, args)
	}

	cfg := &Config{ShowCommandOutput: opts.ShowOutput}

	ns := NewNestedScope(nil)

	for k, v := range opts.Vars {
		ns.Set(k, v)
	}

	env := NewEnv(ns, cfg)
	defer env.Cleanup()

	if opts.JSON {
		env.ReportJSON()
	}

	playbook, err := NewPlaybook(env, args[1])
	if err != nil {
		fmt.Printf("Error loading plays: %s\n", err)
		return 1
	}

	cur, err := os.Getwd()
	if err != nil {
		fmt.Printf("Unable to figure out the current directory: %s\n", err)
		return 1
	}

	defer os.Chdir(cur)
	os.Chdir(playbook.baseDir)

	runner := NewRunner(env, playbook.Plays)
	err = runner.Run(env)

	if err != nil {
		fmt.Printf("Error running playbook: %s\n", err)
		return 1
	}

	return 0
}

func runOnHost(opts *Options, args []string) int {
	fmt.Printf("=== Executing playbook on %s\n", opts.Host)

	t := &Tachyon{
		Target:   opts.Host,
		Debug:    opts.Debug,
		Clean:    opts.CleanHost,
		Dev:      opts.Development,
		Playbook: args[1],
		Release:  opts.Release,
	}

	_, err := RunAdhocCommand(t, "")
	if err != nil {
		fmt.Printf("Error: %s\n", err)
		return 1
	}

	return 0
}
<commit_msg>Report error on stderr to not blow up remote invocation<commit_after>package tachyon

import (
	"fmt"
	"github.com/jessevdk/go-flags"
	"os"
	"path/filepath"
)

type Options struct {
	Vars        map[string]string `short:"s" long:"set" description:"Set a variable"`
	ShowOutput  bool              `short:"o" long:"output" description:"Show command output"`
	Host        string            `short:"t" long:"host" description:"Run the playbook on another host"`
	Development bool              `long:"dev" description:"Use a dev version of tachyon"`
	CleanHost   bool              `long:"clean-host" description:"Clean the host cache before using"`
	Debug       bool              `short:"d" long:"debug" description:"Show all information about commands"`
	Release     string            `long:"release" description:"The release to use when remotely invoking tachyon"`
	JSON        bool              `long:"json" description:"Output the run details in chunked json"`
}

var Release string = "dev"
var Arg0 string

func Main(args []string) int {
	var opts Options

	abs, err := filepath.Abs(args[0])
	if err != nil {
		panic(err)
	}

	Arg0 = abs

	parser := flags.NewParser(&opts, flags.Default)

	for _, o := range parser.Command.Group.Groups()[0].Options() {
		if o.LongName == "release" {
			o.Default = []string{Release}
		}
	}
	args, err = parser.ParseArgs(args)

	if err != nil {
		if serr, ok := err.(*flags.Error); ok {
			if serr.Type == flags.ErrHelp {
				return 2
			}
		}

		fmt.Printf("Error parsing options: %s", err)
		return 1
	}

	if len(args) != 2 {
		fmt.Printf("Usage: tachyon [options] <playbook>\n")
		return 1
	}

	if opts.Host != "" {
		return runOnHost(&opts, args)
	}

	cfg := &Config{ShowCommandOutput: opts.ShowOutput}

	ns := NewNestedScope(nil)

	for k, v := range opts.Vars {
		ns.Set(k, v)
	}

	env := NewEnv(ns, cfg)
	defer env.Cleanup()

	if opts.JSON {
		env.ReportJSON()
	}

	playbook, err := NewPlaybook(env, args[1])
	if err != nil {
		fmt.Printf("Error loading plays: %s\n", err)
		return 1
	}

	cur, err := os.Getwd()
	if err != nil {
		fmt.Printf("Unable to figure out the current directory: %s\n", err)
		return 1
	}

	defer os.Chdir(cur)
	os.Chdir(playbook.baseDir)

	runner := NewRunner(env, playbook.Plays)
	err = runner.Run(env)

	if err != nil {
		fmt.Fprintf(os.Stderr, "Error running playbook: %s\n", err)
		return 1
	}

	return 0
}

func runOnHost(opts *Options, args []string) int {
	fmt.Printf("=== Executing playbook on %s\n", opts.Host)

	t := &Tachyon{
		Target:   opts.Host,
		Debug:    opts.Debug,
		Clean:    opts.CleanHost,
		Dev:      opts.Development,
		Playbook: args[1],
		Release:  opts.Release,
	}

	_, err := RunAdhocCommand(t, "")
	if err != nil {
		fmt.Printf("Error: %s\n", err)
		return 1
	}

	return 0
}
<|endoftext|>