<commit_before>// Package goawk is an implementation of AWK written in Go.
//
// You can use the command-line "goawk" command or run AWK from your
// Go programs using the "interp" package. The command-line program
// has the same interface as regular awk:
//
//     goawk [-F fs] [-v var=value] [-f progfile | 'prog'] [file ...]
//
// The -F flag specifies the field separator (the default is to split
// on whitespace). The -v flag allows you to set a variable to a
// given value (multiple -v flags allowed). The -f flag allows you to
// read AWK source from a file instead of the 'prog' command-line
// argument. The rest of the arguments are input filenames (default
// is to read from stdin).
//
// A simple example (prints the sum of the numbers in the file's
// second column):
//
//     $ echo 'foo 12
//     > bar 34
//     > baz 56' >file.txt
//     $ goawk '{ sum += $2 } END { print sum }' file.txt
//     102
//
// To use GoAWK in your Go programs, see README.md or the "interp"
// docs.
//
package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"runtime/pprof"
	"strings"
	"unicode/utf8"

	"github.com/benhoyt/goawk/interp"
	"github.com/benhoyt/goawk/lexer"
	"github.com/benhoyt/goawk/parser"
)

const (
	version    = "v1.9.2"
	copyright  = "GoAWK " + version + " - Copyright (c) 2021 Ben Hoyt"
	shortUsage = "usage: goawk [-F fs] [-v var=value] [-f progfile | 'prog'] [file ...]"
	longUsage  = `Standard AWK arguments:
  -F separator
        field separator (default " ")
  -v assignment
        name=value variable assignment (multiple allowed)
  -f progfile
        load AWK source from progfile (multiple allowed)

Additional GoAWK arguments:
  -cpuprofile file
        write CPU profile to file
  -d    debug mode (print parsed AST to stderr)
  -dt   show variable types debug info
  -h    show this usage message
  -version
        show GoAWK version and exit
`
)

func main() {
	// Parse command line arguments manually rather than using the
	// "flag" package, so we can support flags with no space between
	// flag and argument, like '-F:' (allowed by POSIX)
	var progFiles []string
	var vars []string
	fieldSep := " "
	cpuprofile := ""
	debug := false
	debugTypes := false
	memprofile := ""

	var i int
	for i = 1; i < len(os.Args); i++ {
		// Stop on explicit end of args or first arg not prefixed with "-"
		arg := os.Args[i]
		if arg == "--" {
			i++
			break
		}
		if arg == "-" || !strings.HasPrefix(arg, "-") {
			break
		}

		switch arg {
		case "-F":
			if i+1 >= len(os.Args) {
				errorExitf("flag needs an argument: -F")
			}
			i++
			fieldSep = os.Args[i]
		case "-f":
			if i+1 >= len(os.Args) {
				errorExitf("flag needs an argument: -f")
			}
			i++
			progFiles = append(progFiles, os.Args[i])
		case "-v":
			if i+1 >= len(os.Args) {
				errorExitf("flag needs an argument: -v")
			}
			i++
			vars = append(vars, os.Args[i])
		case "-cpuprofile":
			if i+1 >= len(os.Args) {
				errorExitf("flag needs an argument: -cpuprofile")
			}
			i++
			cpuprofile = os.Args[i]
		case "-d":
			debug = true
		case "-dt":
			debugTypes = true
		case "-h", "--help":
			fmt.Printf("%s\n\n%s\n\n%s", copyright, shortUsage, longUsage)
			os.Exit(0)
		case "-memprofile":
			if i+1 >= len(os.Args) {
				errorExitf("flag needs an argument: -memprofile")
			}
			i++
			memprofile = os.Args[i]
		case "-version", "--version":
			fmt.Println(version)
			os.Exit(0)
		default:
			switch {
			case strings.HasPrefix(arg, "-F"):
				fieldSep = arg[2:]
			case strings.HasPrefix(arg, "-f"):
				progFiles = append(progFiles, arg[2:])
			case strings.HasPrefix(arg, "-v"):
				vars = append(vars, arg[2:])
			case strings.HasPrefix(arg, "-cpuprofile="):
				cpuprofile = arg[12:]
			case strings.HasPrefix(arg, "-memprofile="):
				memprofile = arg[12:]
			default:
				errorExitf("flag provided but not defined: %s", arg)
			}
		}
	}

	// Any remaining args are program and input files
	args := os.Args[i:]

	var src []byte
	var stdinBytes []byte // used if there's a parse error
	if len(progFiles) > 0 {
		// Read source: the concatenation of all source files specified
		buf := &bytes.Buffer{}
		progFiles = expandWildcardsOnWindows(progFiles)
		for _, progFile := range progFiles {
			if progFile == "-" {
				b, err := ioutil.ReadAll(os.Stdin)
				if err != nil {
					errorExit(err)
				}
				stdinBytes = b
				_, _ = buf.Write(b)
			} else {
				f, err := os.Open(progFile)
				if err != nil {
					errorExit(err)
				}
				_, err = buf.ReadFrom(f)
				if err != nil {
					_ = f.Close()
					errorExit(err)
				}
				_ = f.Close()
			}
			// Append newline to file in case it doesn't end with one
			_ = buf.WriteByte('\n')
		}
		src = buf.Bytes()
	} else {
		if len(args) < 1 {
			errorExitf(shortUsage)
		}
		src = []byte(args[0])
		args = args[1:]
	}

	// Parse source code and setup interpreter
	parserConfig := &parser.ParserConfig{
		DebugTypes:  debugTypes,
		DebugWriter: os.Stderr,
	}
	prog, err := parser.ParseProgram(src, parserConfig)
	if err != nil {
		if err, ok := err.(*parser.ParseError); ok {
			name, line := errorFileLine(progFiles, stdinBytes, err.Position.Line)
			fmt.Fprintf(os.Stderr, "%s:%d:%d: %s\n",
				name, line, err.Position.Column, err.Message)
			showSourceLine(src, err.Position)
			os.Exit(1)
		}
		errorExitf("%s", err)
	}
	if debug {
		fmt.Fprintln(os.Stderr, prog)
	}
	config := &interp.Config{
		Argv0: filepath.Base(os.Args[0]),
		Args:  expandWildcardsOnWindows(args),
		Vars:  []string{"FS", fieldSep},
	}
	for _, v := range vars {
		parts := strings.SplitN(v, "=", 2)
		if len(parts) != 2 {
			errorExitf("-v flag must be in format name=value")
		}
		config.Vars = append(config.Vars, parts[0], parts[1])
	}

	if cpuprofile != "" {
		f, err := os.Create(cpuprofile)
		if err != nil {
			errorExitf("could not create CPU profile: %v", err)
		}
		if err := pprof.StartCPUProfile(f); err != nil {
			errorExitf("could not start CPU profile: %v", err)
		}
	}

	// Run the program!
	status, err := interp.ExecProgram(prog, config)
	if err != nil {
		errorExit(err)
	}

	if cpuprofile != "" {
		pprof.StopCPUProfile()
	}
	if memprofile != "" {
		f, err := os.Create(memprofile)
		if err != nil {
			errorExitf("could not create memory profile: %v", err)
		}
		runtime.GC() // get up-to-date statistics
		if err := pprof.WriteHeapProfile(f); err != nil {
			errorExitf("could not write memory profile: %v", err)
		}
		_ = f.Close()
	}

	os.Exit(status)
}

// Show source line and position of error, for example:
//
// BEGIN { x*; }
//           ^
func showSourceLine(src []byte, pos lexer.Position) {
	lines := bytes.Split(src, []byte{'\n'})
	srcLine := string(lines[pos.Line-1])
	numTabs := strings.Count(srcLine[:pos.Column-1], "\t")
	runeColumn := utf8.RuneCountInString(srcLine[:pos.Column-1])
	fmt.Fprintln(os.Stderr, strings.Replace(srcLine, "\t", "    ", -1))
	fmt.Fprintln(os.Stderr, strings.Repeat(" ", runeColumn)+strings.Repeat("   ", numTabs)+"^")
}

// Determine which filename and line number to display for the overall
// error line number.
func errorFileLine(progFiles []string, stdinBytes []byte, errorLine int) (string, int) {
	if len(progFiles) == 0 {
		return "<cmdline>", errorLine
	}
	startLine := 1
	for _, progFile := range progFiles {
		var content []byte
		if progFile == "-" {
			progFile = "<stdin>"
			content = stdinBytes
		} else {
			b, err := ioutil.ReadFile(progFile)
			if err != nil {
				return "<unknown>", errorLine
			}
			content = b
		}
		content = append(content, '\n')

		numLines := bytes.Count(content, []byte{'\n'})
		if errorLine >= startLine && errorLine < startLine+numLines {
			return progFile, errorLine - startLine + 1
		}
		startLine += numLines
	}
	return "<unknown>", errorLine
}

func errorExit(err error) {
	pathErr, ok := err.(*os.PathError)
	if ok && os.IsNotExist(err) {
		errorExitf("file %q not found", pathErr.Path)
	}
	errorExitf("%s", err)
}

func errorExitf(format string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, format+"\n", args...)
	os.Exit(1)
}

func expandWildcardsOnWindows(args []string) []string {
	if runtime.GOOS != "windows" {
		return args
	}
	return expandWildcards(args)
}

// Originally from https://github.com/mattn/getwild (compatible LICENSE).
func expandWildcards(args []string) []string {
	result := make([]string, 0, len(args))
	for _, arg := range args {
		matches, err := filepath.Glob(arg)
		if err == nil && len(matches) > 0 {
			result = append(result, matches...)
		} else {
			result = append(result, arg)
		}
	}
	return result
}
<commit_msg>Bump up version number for release (before adding bytes mode)<commit_after>// Package goawk is an implementation of AWK written in Go.
//
// You can use the command-line "goawk" command or run AWK from your
// Go programs using the "interp" package. The command-line program
// has the same interface as regular awk:
//
//     goawk [-F fs] [-v var=value] [-f progfile | 'prog'] [file ...]
//
// The -F flag specifies the field separator (the default is to split
// on whitespace). The -v flag allows you to set a variable to a
// given value (multiple -v flags allowed). The -f flag allows you to
// read AWK source from a file instead of the 'prog' command-line
// argument. The rest of the arguments are input filenames (default
// is to read from stdin).
//
// A simple example (prints the sum of the numbers in the file's
// second column):
//
//     $ echo 'foo 12
//     > bar 34
//     > baz 56' >file.txt
//     $ goawk '{ sum += $2 } END { print sum }' file.txt
//     102
//
// To use GoAWK in your Go programs, see README.md or the "interp"
// docs.
//
package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"runtime/pprof"
	"strings"
	"unicode/utf8"

	"github.com/benhoyt/goawk/interp"
	"github.com/benhoyt/goawk/lexer"
	"github.com/benhoyt/goawk/parser"
)

const (
	version    = "v1.10.0"
	copyright  = "GoAWK " + version + " - Copyright (c) 2021 Ben Hoyt"
	shortUsage = "usage: goawk [-F fs] [-v var=value] [-f progfile | 'prog'] [file ...]"
	longUsage  = `Standard AWK arguments:
  -F separator
        field separator (default " ")
  -v assignment
        name=value variable assignment (multiple allowed)
  -f progfile
        load AWK source from progfile (multiple allowed)

Additional GoAWK arguments:
  -cpuprofile file
        write CPU profile to file
  -d    debug mode (print parsed AST to stderr)
  -dt   show variable types debug info
  -h    show this usage message
  -version
        show GoAWK version and exit
`
)

func main() {
	// Parse command line arguments manually rather than using the
	// "flag" package, so we can support flags with no space between
	// flag and argument, like '-F:' (allowed by POSIX)
	var progFiles []string
	var vars []string
	fieldSep := " "
	cpuprofile := ""
	debug := false
	debugTypes := false
	memprofile := ""

	var i int
	for i = 1; i < len(os.Args); i++ {
		// Stop on explicit end of args or first arg not prefixed with "-"
		arg := os.Args[i]
		if arg == "--" {
			i++
			break
		}
		if arg == "-" || !strings.HasPrefix(arg, "-") {
			break
		}

		switch arg {
		case "-F":
			if i+1 >= len(os.Args) {
				errorExitf("flag needs an argument: -F")
			}
			i++
			fieldSep = os.Args[i]
		case "-f":
			if i+1 >= len(os.Args) {
				errorExitf("flag needs an argument: -f")
			}
			i++
			progFiles = append(progFiles, os.Args[i])
		case "-v":
			if i+1 >= len(os.Args) {
				errorExitf("flag needs an argument: -v")
			}
			i++
			vars = append(vars, os.Args[i])
		case "-cpuprofile":
			if i+1 >= len(os.Args) {
				errorExitf("flag needs an argument: -cpuprofile")
			}
			i++
			cpuprofile = os.Args[i]
		case "-d":
			debug = true
		case "-dt":
			debugTypes = true
		case "-h", "--help":
			fmt.Printf("%s\n\n%s\n\n%s", copyright, shortUsage, longUsage)
			os.Exit(0)
		case "-memprofile":
			if i+1 >= len(os.Args) {
				errorExitf("flag needs an argument: -memprofile")
			}
			i++
			memprofile = os.Args[i]
		case "-version", "--version":
			fmt.Println(version)
			os.Exit(0)
		default:
			switch {
			case strings.HasPrefix(arg, "-F"):
				fieldSep = arg[2:]
			case strings.HasPrefix(arg, "-f"):
				progFiles = append(progFiles, arg[2:])
			case strings.HasPrefix(arg, "-v"):
				vars = append(vars, arg[2:])
			case strings.HasPrefix(arg, "-cpuprofile="):
				cpuprofile = arg[12:]
			case strings.HasPrefix(arg, "-memprofile="):
				memprofile = arg[12:]
			default:
				errorExitf("flag provided but not defined: %s", arg)
			}
		}
	}

	// Any remaining args are program and input files
	args := os.Args[i:]

	var src []byte
	var stdinBytes []byte // used if there's a parse error
	if len(progFiles) > 0 {
		// Read source: the concatenation of all source files specified
		buf := &bytes.Buffer{}
		progFiles = expandWildcardsOnWindows(progFiles)
		for _, progFile := range progFiles {
			if progFile == "-" {
				b, err := ioutil.ReadAll(os.Stdin)
				if err != nil {
					errorExit(err)
				}
				stdinBytes = b
				_, _ = buf.Write(b)
			} else {
				f, err := os.Open(progFile)
				if err != nil {
					errorExit(err)
				}
				_, err = buf.ReadFrom(f)
				if err != nil {
					_ = f.Close()
					errorExit(err)
				}
				_ = f.Close()
			}
			// Append newline to file in case it doesn't end with one
			_ = buf.WriteByte('\n')
		}
		src = buf.Bytes()
	} else {
		if len(args) < 1 {
			errorExitf(shortUsage)
		}
		src = []byte(args[0])
		args = args[1:]
	}

	// Parse source code and setup interpreter
	parserConfig := &parser.ParserConfig{
		DebugTypes:  debugTypes,
		DebugWriter: os.Stderr,
	}
	prog, err := parser.ParseProgram(src, parserConfig)
	if err != nil {
		if err, ok := err.(*parser.ParseError); ok {
			name, line := errorFileLine(progFiles, stdinBytes, err.Position.Line)
			fmt.Fprintf(os.Stderr, "%s:%d:%d: %s\n",
				name, line, err.Position.Column, err.Message)
			showSourceLine(src, err.Position)
			os.Exit(1)
		}
		errorExitf("%s", err)
	}
	if debug {
		fmt.Fprintln(os.Stderr, prog)
	}
	config := &interp.Config{
		Argv0: filepath.Base(os.Args[0]),
		Args:  expandWildcardsOnWindows(args),
		Vars:  []string{"FS", fieldSep},
	}
	for _, v := range vars {
		parts := strings.SplitN(v, "=", 2)
		if len(parts) != 2 {
			errorExitf("-v flag must be in format name=value")
		}
		config.Vars = append(config.Vars, parts[0], parts[1])
	}

	if cpuprofile != "" {
		f, err := os.Create(cpuprofile)
		if err != nil {
			errorExitf("could not create CPU profile: %v", err)
		}
		if err := pprof.StartCPUProfile(f); err != nil {
			errorExitf("could not start CPU profile: %v", err)
		}
	}

	// Run the program!
	status, err := interp.ExecProgram(prog, config)
	if err != nil {
		errorExit(err)
	}

	if cpuprofile != "" {
		pprof.StopCPUProfile()
	}
	if memprofile != "" {
		f, err := os.Create(memprofile)
		if err != nil {
			errorExitf("could not create memory profile: %v", err)
		}
		runtime.GC() // get up-to-date statistics
		if err := pprof.WriteHeapProfile(f); err != nil {
			errorExitf("could not write memory profile: %v", err)
		}
		_ = f.Close()
	}

	os.Exit(status)
}

// Show source line and position of error, for example:
//
// BEGIN { x*; }
//           ^
func showSourceLine(src []byte, pos lexer.Position) {
	lines := bytes.Split(src, []byte{'\n'})
	srcLine := string(lines[pos.Line-1])
	numTabs := strings.Count(srcLine[:pos.Column-1], "\t")
	runeColumn := utf8.RuneCountInString(srcLine[:pos.Column-1])
	fmt.Fprintln(os.Stderr, strings.Replace(srcLine, "\t", "    ", -1))
	fmt.Fprintln(os.Stderr, strings.Repeat(" ", runeColumn)+strings.Repeat("   ", numTabs)+"^")
}

// Determine which filename and line number to display for the overall
// error line number.
func errorFileLine(progFiles []string, stdinBytes []byte, errorLine int) (string, int) {
	if len(progFiles) == 0 {
		return "<cmdline>", errorLine
	}
	startLine := 1
	for _, progFile := range progFiles {
		var content []byte
		if progFile == "-" {
			progFile = "<stdin>"
			content = stdinBytes
		} else {
			b, err := ioutil.ReadFile(progFile)
			if err != nil {
				return "<unknown>", errorLine
			}
			content = b
		}
		content = append(content, '\n')

		numLines := bytes.Count(content, []byte{'\n'})
		if errorLine >= startLine && errorLine < startLine+numLines {
			return progFile, errorLine - startLine + 1
		}
		startLine += numLines
	}
	return "<unknown>", errorLine
}

func errorExit(err error) {
	pathErr, ok := err.(*os.PathError)
	if ok && os.IsNotExist(err) {
		errorExitf("file %q not found", pathErr.Path)
	}
	errorExitf("%s", err)
}

func errorExitf(format string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, format+"\n", args...)
	os.Exit(1)
}

func expandWildcardsOnWindows(args []string) []string {
	if runtime.GOOS != "windows" {
		return args
	}
	return expandWildcards(args)
}

// Originally from https://github.com/mattn/getwild (compatible LICENSE).
func expandWildcards(args []string) []string {
	result := make([]string, 0, len(args))
	for _, arg := range args {
		matches, err := filepath.Glob(arg)
		if err == nil && len(matches) > 0 {
			result = append(result, matches...)
		} else {
			result = append(result, arg)
		}
	}
	return result
}
<|endoftext|>


<commit_before>// Package xkcd allows access to metadata for xkcd comics.
package xkcd

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
)

// Comic is a struct that contains infomation about an xkcd comic.
type Comic struct {
	Num        int    `json:"num"`
	Title      string `json:"title"`
	SafeTitle  string `json:"safe_title"`
	Img        string `json:"img"`
	Alt        string `json:"alt"`
	Year       string `json:"year"`
	Month      string `json:"month"`
	Day        string `json:"day"`
	News       string `json:"news"`
	Link       string `json:"link"`
	Transcript string `json:"transcript"`
}

// New reads from an io.Reader and returns a *Comic struct.
func New(r io.Reader) (*Comic, error) {
	d := json.NewDecoder(r)
	c := new(Comic)
	err := d.Decode(c)
	return c, err
}

const (
	currentURL  = "http://xkcd.com/info.0.json"
	templateURL = "http://xkcd.com/%v/info.0.json"
)

// Get fetches information about the xkcd comic number `n'.
func Get(n int) (*Comic, error) {
	url := fmt.Sprintf(templateURL, n)
	return getByURL(url)
}

// GetCurrent fetches information for the newest xkcd comic.
func GetCurrent() (*Comic, error) {
	return getByURL(currentURL)
}

const ErrNotFound = errors.New("Error retrieving comic")

// getByURL returns infomation downloaded from `url'.
func getByURL(url string) (*Comic, error) {
	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode >= 400 {
		return nil, ErrNotFound
	}
	return New(resp.Body)
}
<commit_msg>Fixed error with new type.<commit_after>// Package xkcd allows access to metadata for xkcd comics.
package xkcd

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
)

// Comic is a struct that contains infomation about an xkcd comic.
type Comic struct {
	Num        int    `json:"num"`
	Title      string `json:"title"`
	SafeTitle  string `json:"safe_title"`
	Img        string `json:"img"`
	Alt        string `json:"alt"`
	Year       string `json:"year"`
	Month      string `json:"month"`
	Day        string `json:"day"`
	News       string `json:"news"`
	Link       string `json:"link"`
	Transcript string `json:"transcript"`
}

// New reads from an io.Reader and returns a *Comic struct.
func New(r io.Reader) (*Comic, error) {
	d := json.NewDecoder(r)
	c := new(Comic)
	err := d.Decode(c)
	return c, err
}

const (
	currentURL  = "http://xkcd.com/info.0.json"
	templateURL = "http://xkcd.com/%v/info.0.json"
)

// Get fetches information about the xkcd comic number `n'.
func Get(n int) (*Comic, error) {
	url := fmt.Sprintf(templateURL, n)
	return getByURL(url)
}

// GetCurrent fetches information for the newest xkcd comic.
func GetCurrent() (*Comic, error) {
	return getByURL(currentURL)
}

var ErrNotFound = errors.New("Error retrieving comic")

// getByURL returns infomation downloaded from `url'.
func getByURL(url string) (*Comic, error) {
	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode >= 400 {
		return nil, ErrNotFound
	}
	return New(resp.Body)
}
<|endoftext|>


<commit_before>package slackapi

import (
	"encoding/json"
)

// AccessLogArgs defines the data to send to the API service.
type AccessLogArgs struct {
	Before string `json:"before"`
	Count  int    `json:"count"`
	Page   int    `json:"page"`
}

// ResponseTeamAccessLogs defines the JSON-encoded output for TeamAccessLogs.
type ResponseTeamAccessLogs struct {
	Response
	AccessLogs []AccessLog `json:"logins"`
}

// ResponseTeamInfo defines the JSON-encoded output for TeamInfo.
type ResponseTeamInfo struct {
	Response
	Team Team `json:"team"`
}

// ResponseTeamProfile defines the JSON-encoded output for TeamProfile.
type ResponseTeamProfile struct {
	Response
	Profile TeamProfile `json:"profile"`
}

// ResponseBillableInfo defines the JSON-encoded output for BillableInfo.
type ResponseBillableInfo struct {
	Response
	BillableInfo map[string]BillableInfo `json:"billable_info"`
}

// AccessLog defines the expected data from the JSON-encoded API response.
type AccessLog struct {
	UserID    string      `json:"user_id"`
	Username  string      `json:"username"`
	DateFirst json.Number `json:"date_first"`
	DateLast  json.Number `json:"date_last"`
	Count     int         `json:"count"`
	IP        string      `json:"ip"`
	UserAgent string      `json:"user_agent"`
	ISP       string      `json:"isp"`
	Country   string      `json:"country"`
	Region    string      `json:"region"`
}

// Team defines the expected data from the JSON-encoded API response.
type Team struct {
	Domain      string   `json:"domain"`
	EmailDomain string   `json:"email_domain"`
	Icon        TeamIcon `json:"icon"`
	ID          string   `json:"id"`
	Name        string   `json:"name"`
}

// TeamProfile defines the expected data from the JSON-encoded API response.
type TeamProfile struct {
	Fields []TeamProfileField `json:"fields"`
}

// TeamProfileField defines the expected data from the JSON-encoded API response.
type TeamProfileField struct {
	ID             string      `json:"id"`
	Ordering       int         `json:"ordering"`
	FieldName      string      `json:"field_name"`
	Label          string      `json:"label"`
	Hint           string      `json:"hint"`
	Type           string      `json:"type"`
	PossibleValues interface{} `json:"possible_values"`
	Options        interface{} `json:"options"`
	IsHidden       bool        `json:"is_hidden"`
}

// TeamIcon defines the expected data from the JSON-encoded API response.
type TeamIcon struct {
	Image102      string `json:"image_102"`
	Image132      string `json:"image_132"`
	Image34       string `json:"image_34"`
	Image44       string `json:"image_44"`
	Image68       string `json:"image_68"`
	Image88       string `json:"image_88"`
	ImageOriginal string `json:"image_original"`
}

// BillableInfo defines the expected data from the JSON-encoded API response.
type BillableInfo struct {
	BillingActive bool `json:"billing_active"`
}

// TeamAccessLogs gets the access logs for the current team.
func (s *SlackAPI) TeamAccessLogs(data AccessLogArgs) ResponseTeamAccessLogs {
	var response ResponseTeamAccessLogs
	s.getRequest(&response, "team.accessLogs", data)
	return response
}

// TeamBillableInfo gets billable users information for the current team.
func (s *SlackAPI) TeamBillableInfo(user string) ResponseBillableInfo {
	var response ResponseBillableInfo
	s.getRequest(&response, "team.billableInfo", struct {
		User string `json:"user"`
	}{user})
	return response
}

// TeamInfo gets information about the current team.
func (s *SlackAPI) TeamInfo() ResponseTeamInfo {
	var response ResponseTeamInfo
	s.getRequest(&response, "team.info", nil)
	return response
}

// TeamProfileGet retrieve a team's profile.
func (s *SlackAPI) TeamProfileGet() ResponseTeamProfile {
	var response ResponseTeamProfile
	s.getRequest(&response, "team.profile.get", nil)
	return response
}
<commit_msg>Organize team.accessLogs method implementation with new API<commit_after>package slackapi

import (
	"encoding/json"
	"net/url"
	"strconv"
)

type TeamAccessLogsInput struct {
	// End of time range of logs to include in results (inclusive).
	Before string `json:"before"`
	// Number of items to return per page.
	Count int `json:"count"`
	// Page number of results to return.
	Page int `json:"page"`
	// Encoded team id to get logs from, required if org token is used.
	TeamID string `json:"team_id"`
}

// TeamAccessLogsResponse defines the JSON-encoded output for TeamAccessLogs.
type TeamAccessLogsResponse struct {
	Response
	Logins []AccessLog `json:"logins"`
}

// AccessLog defines the expected data from the JSON-encoded API response.
type AccessLog struct {
	UserID    string      `json:"user_id"`
	Username  string      `json:"username"`
	DateFirst json.Number `json:"date_first"`
	DateLast  json.Number `json:"date_last"`
	Count     int         `json:"count"`
	IP        string      `json:"ip"`
	UserAgent string      `json:"user_agent"`
	ISP       string      `json:"isp"`
	Country   string      `json:"country"`
	Region    string      `json:"region"`
}

// TeamAccessLogs is https://api.slack.com/methods/team.accessLogs
func (s *SlackAPI) TeamAccessLogs(input TeamAccessLogsInput) TeamAccessLogsResponse {
	in := url.Values{}

	if input.Before != "" {
		in.Add("before", input.Before)
	}

	if input.Count > 0 {
		in.Add("count", strconv.Itoa(input.Count))
	} else {
		in.Add("count", "100")
	}

	if input.Page > 0 {
		in.Add("page", strconv.Itoa(input.Page))
	}

	if input.TeamID != "" {
		in.Add("team_id", input.TeamID)
	}

	var out TeamAccessLogsResponse
	if err := s.baseGET("/api/team.accessLogs", in, &out); err != nil {
		return TeamAccessLogsResponse{Response: Response{Error: err.Error()}}
	}
	return out
}

// ResponseTeamInfo defines the JSON-encoded output for TeamInfo.
type ResponseTeamInfo struct {
	Response
	Team Team `json:"team"`
}

// ResponseTeamProfile defines the JSON-encoded output for TeamProfile.
type ResponseTeamProfile struct {
	Response
	Profile TeamProfile `json:"profile"`
}

// ResponseBillableInfo defines the JSON-encoded output for BillableInfo.
type ResponseBillableInfo struct {
	Response
	BillableInfo map[string]BillableInfo `json:"billable_info"`
}

// Team defines the expected data from the JSON-encoded API response.
type Team struct {
	Domain      string   `json:"domain"`
	EmailDomain string   `json:"email_domain"`
	Icon        TeamIcon `json:"icon"`
	ID          string   `json:"id"`
	Name        string   `json:"name"`
}

// TeamProfile defines the expected data from the JSON-encoded API response.
type TeamProfile struct {
	Fields []TeamProfileField `json:"fields"`
}

// TeamProfileField defines the expected data from the JSON-encoded API response.
type TeamProfileField struct {
	ID             string      `json:"id"`
	Ordering       int         `json:"ordering"`
	FieldName      string      `json:"field_name"`
	Label          string      `json:"label"`
	Hint           string      `json:"hint"`
	Type           string      `json:"type"`
	PossibleValues interface{} `json:"possible_values"`
	Options        interface{} `json:"options"`
	IsHidden       bool        `json:"is_hidden"`
}

// TeamIcon defines the expected data from the JSON-encoded API response.
type TeamIcon struct {
	Image102      string `json:"image_102"`
	Image132      string `json:"image_132"`
	Image34       string `json:"image_34"`
	Image44       string `json:"image_44"`
	Image68       string `json:"image_68"`
	Image88       string `json:"image_88"`
	ImageOriginal string `json:"image_original"`
}

// BillableInfo defines the expected data from the JSON-encoded API response.
type BillableInfo struct {
	BillingActive bool `json:"billing_active"`
}

// TeamBillableInfo gets billable users information for the current team.
func (s *SlackAPI) TeamBillableInfo(user string) ResponseBillableInfo {
	var response ResponseBillableInfo
	s.getRequest(&response, "team.billableInfo", struct {
		User string `json:"user"`
	}{user})
	return response
}

// TeamInfo gets information about the current team.
func (s *SlackAPI) TeamInfo() ResponseTeamInfo {
	var response ResponseTeamInfo
	s.getRequest(&response, "team.info", nil)
	return response
}

// TeamProfileGet retrieve a team's profile.
func (s *SlackAPI) TeamProfileGet() ResponseTeamProfile {
	var response ResponseTeamProfile
	s.getRequest(&response, "team.profile.get", nil)
	return response
}
<|endoftext|>


<commit_before>package goics

import (
	"time"
)

// Package goics implements an ical encoder and decoder.
// First release will include decode and encoder of Event types
// Will try to add more features as needed.

type Calendar struct {
	Uid    string
	Events []*Event
	Extra  map[string]string
}

// http://www.kanzaki.com/docs/ical/vevent.html
type Event struct {
	Start        time.Time
	End          time.Time
	LastModified time.Time
	Dtstamp      time.Time
	Created		time.Time
	Uid          string
	Summary      string
	Description  string
	Location     string
	Status       string
	Transp		 string
	Params       map[string]string
	//Alarms []*Alarm
}

// http://www.kanzaki.com/docs/ical/valarm.html
//type Alarm struct {
//
//}<commit_msg>new properties struct event<commit_after>package goics

import (
	"time"
)

// Package goics implements an ical encoder and decoder.
// First release will include decode and encoder of Event types
// Will try to add more features as needed.

type Calendar struct {
	Uid    string
	Events []*Event
	Extra  map[string]string
}

// http://www.kanzaki.com/docs/ical/vevent.html
type Event struct {
	Start        time.Time
	End          time.Time
	LastModified time.Time
	Dtstamp      time.Time
	Created      time.Time
	Uid          string
	Summary      string
	Description  string
	Location     string
	Status       string
	Transp       string
	Params       map[string]string
	//Alarms []*Alarm
}

// http://www.kanzaki.com/docs/ical/valarm.html
//type Alarm struct {
//
//}
<|endoftext|>


<commit_before>package gorai

import (
	"crypto/tls"
	"github.com/facebookgo/grace/gracehttp"
	"github.com/go51/auth551"
	"github.com/go51/container551"
	"github.com/go51/cookie551"
	"github.com/go51/log551"
	"github.com/go51/memcache551"
	"github.com/go51/model551"
	"github.com/go51/mysql551"
	"github.com/go51/response551"
	"github.com/go51/router551"
	"github.com/go51/secure551"
	"net/http"
	"os"
	"time"
)

type gorai struct {
	config       *Config
	logger       *log551.Log551
	router       *router551.Router
	modelManager *model551.Model
	auth         *auth551.Auth
}

var goraiInstance *gorai = nil

func Load() *gorai {
	if goraiInstance != nil {
		return goraiInstance
	}

	goraiInstance = &gorai{}

	goraiInstance.initialize()

	return goraiInstance
}

func (g *gorai) initialize() {
	g.config = loadConfig()

	// Logger
	if isConsole() {
		g.logger = log551.New(&g.config.Framework.CommandLog)
	} else {
		g.logger = log551.New(&g.config.Framework.SystemLog)
	}
	g.logger.Open()
	defer g.logger.Close()

	// Router
	g.router = router551.Load()

	// ModelManager
	g.modelManager = model551.Load()

	// Add Auth Model
	g.modelManager.Add(auth551.NewUserModel, auth551.NewUserModelPointer)
	g.modelManager.Add(auth551.NewUserTokenModel, auth551.NewUserTokenModelPointer)

	// Auth
	g.auth = auth551.Load(&g.config.Framework.Auth)
}

func (g *gorai) Run() {
	if isConsole() {
		consoleHandler()
		return
	}

	cer, err := tls.LoadX509KeyPair(g.config.Framework.WebServerSSL.CtrFile, g.config.Framework.WebServerSSL.KeyFile)
	if err != nil {
		panic(err)
	}

	config := &tls.Config{Certificates: []tls.Certificate{cer}}

	server := &http.Server{
		Addr:         g.config.Framework.WebServer.Host + ":" + g.config.Framework.WebServer.Port,
		Handler:      webReWriteHandler(),
		ReadTimeout:  g.config.Framework.WebServer.ReadTimeout * time.Second,
		WriteTimeout: g.config.Framework.WebServer.WriteTimeout * time.Second,
	}
	serverSSL := &http.Server{
		Addr:         g.config.Framework.WebServerSSL.Host + ":" + g.config.Framework.WebServerSSL.Port,
		Handler:      webHandler(),
		ReadTimeout:  g.config.Framework.WebServerSSL.ReadTimeout * time.Second,
		WriteTimeout: g.config.Framework.WebServerSSL.WriteTimeout * time.Second,
		TLSConfig:    config,
	}

	gracehttp.Serve(server, serverSSL)
}

func isConsole() bool {
	return len(os.Args) > 1
}

func consoleHandler() {

	g := Load()

	l := log551.New(&g.config.Framework.CommandLog)
	l.Open()
	defer l.Close()

	sid := secure551.Hash()

	// Routing
	name := os.Args[1]
	l.Debugf("%s [ Command ] %s", sid[:10], name)
	route := g.router.FindRouteByName(router551.COMMAND.String(), name)
	if route == nil {
		l.Errorf("%s %s", sid[:10], "Action not found...")
		return
	}

	// Options
	optionArgs := os.Args[2:]
	if len(optionArgs)%2 == 1 {
		l.Errorf("%s %s", sid[:10], "Missing options.")
	}
	options := make(map[string]string, len(optionArgs)/2)
	for i := 0; i < len(optionArgs); i += 2 {
		options[optionArgs[i][1:]] = optionArgs[i+1]
	}

	mysql := mysql551.New(&g.config.Framework.Database)
	mysql.Open()
	defer mysql.Close()

	session := memcache551.New(&g.config.Framework.Session.Server, sid)

	c := container551.New()
	c.SetSID(sid)
	c.SetResponseWriter(nil)
	c.SetRequest(nil)
	c.SetLogger(l)
	c.SetCookie(nil)
	c.SetDb(mysql)
	c.SetSession(session)
	c.SetModel(g.modelManager)
	c.SetCommandOptions(options)
	if g.config.Framework.WebServerSSL.Port == "443" {
		c.SetBaseURL("https://" + g.config.Framework.WebServerSSL.Host)
	} else {
		c.SetBaseURL("https://" + g.config.Framework.WebServerSSL.Host + ":" + g.config.Framework.WebServerSSL.Port)
	}

	action := route.Action()
	action(c)

}

func webHandler() http.Handler {
	mux := http.NewServeMux()

	mux.HandleFunc("/static/", staticResource)
	mux.HandleFunc("/", rootFunc)

	return mux
}

func webReWriteHandler() http.Handler {
	mux := http.NewServeMux()

	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {

		g := Load()

		http.Redirect(w, r, "https://"+g.config.Framework.WebServerSSL.Host+":"+g.config.Framework.WebServerSSL.Port+r.URL.Path, http.StatusMovedPermanently)
	})

	return mux
}

func staticResource(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Cache-Control-Max-Age", "10")
	http.ServeFile(w, r, r.URL.Path[1:])

}

func rootFunc(w http.ResponseWriter, r *http.Request) {

	g := Load()

	l := log551.New(&g.config.Framework.SystemLog)
	l.Open()
	defer l.Close()

	cookie := cookie551.New(w, r)

	sid := g.sid(cookie)
	l.Debugf("%s SID: %s", sid[:10], sid)
	session := memcache551.New(&g.config.Framework.Session.Server, sid)

	route := g.router.FindRouteByPathMatch(r.Method, r.URL.Path)
	response551.UrlFunction = g.router.Url

	var data interface{} = nil
	if route != nil {
		mysql := mysql551.New(&g.config.Framework.Database)
		mysql.Open()
		defer mysql.Close()

		l.Debugf("%s --[ Routing ]--", sid[:10])
		l.Debugf("%s Path: %s", sid[:10], r.URL.Path)
		l.Debugf("%s Neme: %s", sid[:10], route.Name())

		c := container551.New()
		c.SetSID(sid)
		c.SetResponseWriter(w)
		c.SetRequest(r)
		c.SetLogger(l)
		c.SetCookie(cookie)
		c.SetDb(mysql)
		c.SetSession(session)
		c.SetModel(g.modelManager)
		c.SetAuth(g.auth)
		c.SetUrlFunc(g.router.Url)
		if g.config.Framework.WebServerSSL.Port == "443" {
			c.SetBaseURL("https://" + g.config.Framework.WebServerSSL.Host)
		} else {
			c.SetBaseURL("https://" + g.config.Framework.WebServerSSL.Host + ":" + g.config.Framework.WebServerSSL.Port)
		}

		action := route.Action()
		data = action(c)
		response551.Response(w, r, data, route.PackageName(), route.Name(), c.User(), g.config.Application)
	} else {
		l.Errorf("%s --[ Routing ]--", sid[:10])
		l.Errorf("%s Path: %s", sid[:10], r.URL.Path)
		l.Errorf("%s Neme: Route not found.", sid[:10])
		data = response551.Error(404, "Route not found.")
		response551.Response(w, r, data, "", "", nil, g.config.Application)
	}

}

func (g *gorai) Config() *Config {
	return g.config
}

func (g *gorai) Logger() *log551.Log551 {
	return g.logger
}

func (g *gorai) Router() *router551.Router {
	return g.router
}

func (g *gorai) ModelManager() *model551.Model {
	return g.modelManager
}

func (g *gorai) sid(cookie *cookie551.Cookie) string {
	sid, err := cookie.Get(g.config.Framework.Session.CookieKeyName)
	if err == nil {
		return sid
	}

	sid = secure551.Hash()

	cookie.Set(g.config.Framework.Session.CookieKeyName, sid, 60*60*24*365)

	return sid

}
<commit_msg>refs #54 ルートが見つからなかった場合に UserAgent とリクエスト Method をログに出力する<commit_after>package gorai

import (
	"crypto/tls"
	"github.com/facebookgo/grace/gracehttp"
	"github.com/go51/auth551"
	"github.com/go51/container551"
	"github.com/go51/cookie551"
	"github.com/go51/log551"
	"github.com/go51/memcache551"
	"github.com/go51/model551"
	"github.com/go51/mysql551"
	"github.com/go51/response551"
	"github.com/go51/router551"
	"github.com/go51/secure551"
	"net/http"
	"os"
	"time"
)

type gorai struct {
	config       *Config
	logger       *log551.Log551
	router       *router551.Router
	modelManager *model551.Model
	auth         *auth551.Auth
}

var goraiInstance *gorai = nil

func Load() *gorai {
	if goraiInstance != nil {
		return goraiInstance
	}

	goraiInstance = &gorai{}

	goraiInstance.initialize()

	return goraiInstance
}

func (g *gorai) initialize() {
	g.config = loadConfig()

	// Logger
	if isConsole() {
		g.logger = log551.New(&g.config.Framework.CommandLog)
	} else {
		g.logger = log551.New(&g.config.Framework.SystemLog)
	}
	g.logger.Open()
	defer g.logger.Close()

	// Router
	g.router = router551.Load()

	// ModelManager
	g.modelManager = model551.Load()

	// Add Auth Model
	g.modelManager.Add(auth551.NewUserModel, auth551.NewUserModelPointer)
	g.modelManager.Add(auth551.NewUserTokenModel, auth551.NewUserTokenModelPointer)

	// Auth
	g.auth = auth551.Load(&g.config.Framework.Auth)
}

func (g *gorai) Run() {
	if isConsole() {
		consoleHandler()
		return
	}

	cer, err := tls.LoadX509KeyPair(g.config.Framework.WebServerSSL.CtrFile, g.config.Framework.WebServerSSL.KeyFile)
	if err != nil {
		panic(err)
	}

	config := &tls.Config{Certificates: []tls.Certificate{cer}}

	server := &http.Server{
		Addr:         g.config.Framework.WebServer.Host + ":" + g.config.Framework.WebServer.Port,
		Handler:      webReWriteHandler(),
		ReadTimeout:  g.config.Framework.WebServer.ReadTimeout * time.Second,
		WriteTimeout: g.config.Framework.WebServer.WriteTimeout * time.Second,
	}
	serverSSL := &http.Server{
		Addr:         g.config.Framework.WebServerSSL.Host + ":" + g.config.Framework.WebServerSSL.Port,
		Handler:      webHandler(),
		ReadTimeout:  g.config.Framework.WebServerSSL.ReadTimeout * time.Second,
		WriteTimeout: g.config.Framework.WebServerSSL.WriteTimeout * time.Second,
		TLSConfig:    config,
	}

	gracehttp.Serve(server, serverSSL)
}

func isConsole() bool {
	return len(os.Args) > 1
}

func consoleHandler() {

	g := Load()

	l := log551.New(&g.config.Framework.CommandLog)
	l.Open()
	defer l.Close()

	sid := secure551.Hash()

	// Routing
	name := os.Args[1]
	l.Debugf("%s [ Command ] %s", sid[:10], name)
	route := g.router.FindRouteByName(router551.COMMAND.String(), name)
	if route == nil {
		l.Errorf("%s %s", sid[:10], "Action not found...")
		return
	}

	// Options
	optionArgs := os.Args[2:]
	if len(optionArgs)%2 == 1 {
		l.Errorf("%s %s", sid[:10], "Missing options.")
	}
	options := make(map[string]string, len(optionArgs)/2)
	for i := 0; i < len(optionArgs); i += 2 {
		options[optionArgs[i][1:]] = optionArgs[i+1]
	}

	mysql := mysql551.New(&g.config.Framework.Database)
	mysql.Open()
	defer mysql.Close()

	session := memcache551.New(&g.config.Framework.Session.Server, sid)

	c := container551.New()
	c.SetSID(sid)
	c.SetResponseWriter(nil)
	c.SetRequest(nil)
	c.SetLogger(l)
	c.SetCookie(nil)
	c.SetDb(mysql)
	c.SetSession(session)
	c.SetModel(g.modelManager)
	c.SetCommandOptions(options)
	if g.config.Framework.WebServerSSL.Port == "443" {
		c.SetBaseURL("https://" + g.config.Framework.WebServerSSL.Host)
	} else {
		c.SetBaseURL("https://" + g.config.Framework.WebServerSSL.Host + ":" + g.config.Framework.WebServerSSL.Port)
	}

	action := route.Action()
	action(c)

}

func webHandler() http.Handler {
	mux := http.NewServeMux()

	mux.HandleFunc("/static/", staticResource)
	mux.HandleFunc("/", rootFunc)

	return mux
}

func webReWriteHandler() http.Handler {
	mux := http.NewServeMux()

	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {

		g := Load()

		http.Redirect(w, r, "https://"+g.config.Framework.WebServerSSL.Host+":"+g.config.Framework.WebServerSSL.Port+r.URL.Path, http.StatusMovedPermanently)
	})

	return mux
}

func staticResource(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Cache-Control-Max-Age", "10")
	http.ServeFile(w, r, r.URL.Path[1:])

}

func rootFunc(w http.ResponseWriter, r *http.Request) {

	g := Load()

	l := log551.New(&g.config.Framework.SystemLog)
	l.Open()
	defer l.Close()

	cookie := cookie551.New(w, r)

	sid := g.sid(cookie)
	l.Debugf("%s SID: %s", sid[:10], sid)
	session := memcache551.New(&g.config.Framework.Session.Server, sid)

	route := g.router.FindRouteByPathMatch(r.Method, r.URL.Path)
	response551.UrlFunction = g.router.Url

	var data interface{} = nil
	if route != nil {
		mysql := mysql551.New(&g.config.Framework.Database)
		mysql.Open()
		defer mysql.Close()

		l.Debugf("%s --[ Routing ]--", sid[:10])
		l.Debugf("%s Path: %s", sid[:10], r.URL.Path)
		l.Debugf("%s Neme: %s", sid[:10], route.Name())

		c := container551.New()
		c.SetSID(sid)
		c.SetResponseWriter(w)
		c.SetRequest(r)
		c.SetLogger(l)
		c.SetCookie(cookie)
		c.SetDb(mysql)
		c.SetSession(session)
		c.SetModel(g.modelManager)
		c.SetAuth(g.auth)
		c.SetUrlFunc(g.router.Url)
		if g.config.Framework.WebServerSSL.Port == "443" {
			c.SetBaseURL("https://" + g.config.Framework.WebServerSSL.Host)
		} else {
			c.SetBaseURL("https://" + g.config.Framework.WebServerSSL.Host + ":" + g.config.Framework.WebServerSSL.Port)
		}

		action := route.Action()
		data = action(c)
		response551.Response(w, r, data, route.PackageName(), route.Name(), c.User(), g.config.Application)
	} else {
		l.Errorf("%s --[ Route not found ]--", sid[:10])
		l.Errorf("%s UA:     %s", sid[:10], r.UserAgent())
		l.Errorf("%s Method: %s", sid[:10], r.Method)
		l.Errorf("%s Path:   %s", sid[:10], r.URL.Path)
		l.Errorf("%s --[/Route not found ]--", sid[:10])
		data = response551.Error(404, "Route not found.")
		response551.Response(w, r, data, "", "", nil, g.config.Application)
	}

}

func (g *gorai) Config() *Config {
	return g.config
}

func (g *gorai) Logger() *log551.Log551 {
	return g.logger
}

func (g *gorai) Router() *router551.Router {
	return g.router
}

func (g *gorai) ModelManager() *model551.Model {
	return g.modelManager
}

func (g *gorai) sid(cookie *cookie551.Cookie) string {
	sid, err := cookie.Get(g.config.Framework.Session.CookieKeyName)
	if err == nil {
		return sid
	}

	sid = secure551.Hash()

	cookie.Set(g.config.Framework.Session.CookieKeyName, sid, 60*60*24*365)

	return sid

}
<|endoftext|>


<commit_before>// Gone Time Tracker -or- Where has my time gone?
package main

import (
	"bytes"
	"errors"
	"log"
	"os"
	"time"

	"github.com/BurntSushi/xgb"
	"github.com/BurntSushi/xgb/screensaver"
	"github.com/BurntSushi/xgb/xproto"
)

type Xorg struct {
	conn        *xgb.Conn
	root        xproto.Window
	activeAtom  *xproto.InternAtomReply
	netNameAtom *xproto.InternAtomReply
	nameAtom    *xproto.InternAtomReply
	classAtom   *xproto.InternAtomReply
	event       chan xgb.Event
}

func (x Xorg) atom(aname string) *xproto.InternAtomReply {
	a, err := xproto.InternAtom(x.conn, true, uint16(len(aname)), aname).Reply()
	if err != nil {
		log.Fatal("atom: ", err)
	}
	return a
}

func (x Xorg) property(w xproto.Window, a *xproto.InternAtomReply) (*xproto.GetPropertyReply, error) {
	return xproto.GetProperty(x.conn, false, w, a.Atom,
		xproto.GetPropertyTypeAny, 0, (1<<32)-1).Reply()
}

func (x Xorg) active() xproto.Window {
	p, err := x.property(x.root, x.activeAtom)
	if err != nil {
		return x.root
	}
	return xproto.Window(xgb.Get32(p.Value))
}

func (x Xorg) name(w xproto.Window) (string, error) {
	name, err := x.property(w, x.netNameAtom)
	if err != nil {
		return "", err
	}
	if string(name.Value) == "" {
		name, err = x.property(w, x.nameAtom)
		if err != nil {
			return "", err
		}
		if string(name.Value) == "" {
			return "", errors.New("empty value")
		}
	}
	return string(name.Value), nil
}

func (x Xorg) class(w xproto.Window) (string, error) {
	class, err := x.property(w, x.classAtom)
	if err != nil {
		return "", err
	}
	zero := []byte{0}
	s := bytes.Split(bytes.TrimSuffix(class.Value, zero), zero)
	if l := len(s); l > 0 && len(s[l-1]) != 0 {
		return string(s[l-1]), nil
	}
	return "", errors.New("empty class")
}

func (x Xorg) window() (Window, bool) {
	id := x.active()
	/* skip invalid window id */
	if id == 0 {
		return Window{}, false
	}
	class, err := x.class(id)
	if err != nil {
		return Window{}, false
	}
	name, err := x.name(id)
	if err != nil {
		return Window{}, false
	}
	x.spy(id)
	return Window{
		Class: class,
		Name:  name,
	}, true
}

func (x Xorg) spy(w xproto.Window) {
	xproto.ChangeWindowAttributes(x.conn, w, xproto.CwEventMask,
		[]uint32{xproto.EventMaskPropertyChange})
}

func (x Xorg) Close() {
	x.conn.Close()
}

func Connect() Xorg {
	var x Xorg
	var err error

	display := os.Getenv("DISPLAY")
	if display == "" {
		display = ":0"
	}
	x.conn, err = xgb.NewConnDisplay(display)
	if err != nil {
		log.Fatal("xgb: ", err)
	}

	err = screensaver.Init(x.conn)
	if err != nil {
		log.Fatal("screensaver: ", err)
	}

	setup := xproto.Setup(x.conn)
	x.root = setup.DefaultScreen(x.conn).Root

	drw := xproto.Drawable(x.root)
	screensaver.SelectInput(x.conn, drw, screensaver.EventNotifyMask)

	x.activeAtom = x.atom("_NET_ACTIVE_WINDOW")
	x.netNameAtom = x.atom("_NET_WM_NAME")
	x.nameAtom = x.atom("WM_NAME")
	x.classAtom = x.atom("WM_CLASS")
	x.event = make(chan xgb.Event)

	x.spy(x.root)

	return x
}

func (x Xorg) WaitForEvent() {
	ev, err := x.conn.WaitForEvent()
	if err != nil {
		log.Println("wait for event:", err)
		x.event <- nil
		return
	}
	x.event <- ev
}

func (x Xorg) QueryIdle() time.Duration {
	info, err := screensaver.QueryInfo(x.conn,
		xproto.Drawable(x.root)).Reply()
	if err != nil {
		log.Println("query idle:", err)
		return 0
	}
	return time.Duration(info.MsSinceUserInput) * time.Millisecond
}

func (x Xorg) Collect(t Tracker) {
	if win, ok := x.window(); ok {
		t.Update(win)
	}
	for {
		go x.WaitForEvent()

		select {
		case event := <-x.event:
			switch e := event.(type) {
			case xproto.PropertyNotifyEvent:
				t.Wakeup()
				if win, ok := x.window(); ok {
					t.Update(win)
				}
			case screensaver.NotifyEvent:
				switch e.State {
				case screensaver.StateOn:
					idle := x.QueryIdle()
					t.Snooze(idle)
				default:
					t.Wakeup()
				}
			}
		case <-time.After(time.Minute):
			idle := x.QueryIdle()
			if idle > 5*time.Minute {
				t.Snooze(idle)
			}
		}
	}
}
<commit_msg>wakeup only if we have a valid window<commit_after>// Gone Time Tracker -or- Where has my time gone?
package main

import (
	"bytes"
	"errors"
	"log"
	"os"
	"time"

	"github.com/BurntSushi/xgb"
	"github.com/BurntSushi/xgb/screensaver"
	"github.com/BurntSushi/xgb/xproto"
)

type Xorg struct {
	conn        *xgb.Conn
	root        xproto.Window
	activeAtom  *xproto.InternAtomReply
	netNameAtom *xproto.InternAtomReply
	nameAtom    *xproto.InternAtomReply
	classAtom   *xproto.InternAtomReply
	event       chan xgb.Event
}

func (x Xorg) atom(aname string) *xproto.InternAtomReply {
	a, err := xproto.InternAtom(x.conn, true, uint16(len(aname)), aname).Reply()
	if err != nil {
		log.Fatal("atom: ", err)
	}
	return a
}

func (x Xorg) property(w xproto.Window, a *xproto.InternAtomReply) (*xproto.GetPropertyReply, error) {
	return xproto.GetProperty(x.conn, false, w, a.Atom,
		xproto.GetPropertyTypeAny, 0, (1<<32)-1).Reply()
}

func (x Xorg) active() xproto.Window {
	p, err := x.property(x.root, x.activeAtom)
	if err != nil {
		return x.root
	}
	return xproto.Window(xgb.Get32(p.Value))
}

func (x Xorg) name(w xproto.Window) (string, error) {
	name, err := x.property(w, x.netNameAtom)
	if err != nil {
		return "", err
	}
	if string(name.Value) == "" {
		name, err = x.property(w, x.nameAtom)
		if err != nil {
			return "", err
		}
		if string(name.Value) == "" {
			return "", errors.New("empty value")
		}
	}
	return string(name.Value), nil
}

func (x Xorg) class(w xproto.Window) (string, error) {
	class, err := x.property(w, x.classAtom)
	if err != nil {
		return "", err
	}
	zero := []byte{0}
	s := bytes.Split(bytes.TrimSuffix(class.Value, zero), zero)
	if l := len(s); l > 0 && len(s[l-1]) != 0 {
		return string(s[l-1]), nil
	}
	return "", errors.New("empty class")
}

func (x Xorg) window() (Window, bool) {
	id := x.active()
	/* skip invalid window id */
	if id == 0 {
		return Window{}, false
	}
	class, err := x.class(id)
	if err != nil {
		return Window{}, false
	}
	name, err := x.name(id)
	if err != nil {
		return Window{}, false
	}
	x.spy(id)
	return Window{
		Class: class,
		Name:  name,
	}, true
}

func (x Xorg) spy(w xproto.Window) {
	xproto.ChangeWindowAttributes(x.conn, w, xproto.CwEventMask,
		[]uint32{xproto.EventMaskPropertyChange})
}

func (x Xorg) Close() {
	x.conn.Close()
}

func Connect() Xorg {
	var x Xorg
	var err error

	display := os.Getenv("DISPLAY")
	if display == "" {
		display = ":0"
	}
	x.conn, err = xgb.NewConnDisplay(display)
	if err != nil {
		log.Fatal("xgb: ", err)
	}

	err = screensaver.Init(x.conn)
	if err != nil {
		log.Fatal("screensaver: ", err)
	}

	setup := xproto.Setup(x.conn)
	x.root = setup.DefaultScreen(x.conn).Root

	drw := xproto.Drawable(x.root)
	screensaver.SelectInput(x.conn, drw, screensaver.EventNotifyMask)

	x.activeAtom = x.atom("_NET_ACTIVE_WINDOW")
	x.netNameAtom = x.atom("_NET_WM_NAME")
	x.nameAtom = x.atom("WM_NAME")
	x.classAtom = x.atom("WM_CLASS")
	x.event = make(chan xgb.Event)

	x.spy(x.root)

	return x
}

func (x Xorg) WaitForEvent() {
	ev, err := x.conn.WaitForEvent()
	if err != nil {
		log.Println("wait for event:", err)
		x.event <- nil
		return
	}
	x.event <- ev
}

func (x Xorg) QueryIdle() time.Duration {
	info, err := screensaver.QueryInfo(x.conn,
		xproto.Drawable(x.root)).Reply()
	if err != nil {
		log.Println("query idle:", err)
		return 0
	}
	return time.Duration(info.MsSinceUserInput) * time.Millisecond
}

func (x Xorg) Collect(t Tracker) {
	if win, ok := x.window(); ok {
		t.Update(win)
	}
	for {
		go x.WaitForEvent()

		select {
		case event := <-x.event:
			switch e := event.(type) {
			case xproto.PropertyNotifyEvent:
				if win, ok := x.window(); ok {
					t.Wakeup()
					t.Update(win)
				}
			case screensaver.NotifyEvent:
				switch e.State {
				case screensaver.StateOn:
					idle := x.QueryIdle()
					t.Snooze(idle)
				default:
					t.Wakeup()
				}
			}
		case <-time.After(time.Minute):
			idle := x.QueryIdle()
			if idle > 5*time.Minute {
				t.Snooze(idle)
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import "fmt"
import "log"
import "os"
import "errors"
import "strings"
import "github.com/nlopes/slack"
import "github.com/tadgh/go-toggl"
import "encoding/gob"
import "time"
import "runtime"

const file = "./test.gob"

func Check(err error) {
	if err != nil {
		_, file, line, _ := runtime.Caller(1)
		fmt.Println(line, "\t", file, "\n", err)
		os.Exit(1)
	}
}

func pingTogglApi(apiKey string) error {
	ts := toggl.OpenSession(apiKey)
	_, err := ts.GetAccount()
	if err != nil {
		fmt.Println(os.Stderr, "Error: %s\n", err)
		return err
	}
	return nil
}

func Save(path string, object interface{}) error {
	file, err := os.Create(path)
	if err == nil {
		encoder := gob.NewEncoder(file)
		encoder.Encode(object)
	}
	file.Close()
	return err
}

func Load(path string, object interface{}) error {
	file, err := os.Open(path)
	if err == nil {
		decoder := gob.NewDecoder(file)
		err = decoder.Decode(object)
	}
	file.Close()
	return err
}

func createTimeEntry(apiKey, description string, start time.Time, duration time.Duration, pid, tid int) *toggl.TimeEntry {
	ts := toggl.OpenSession(apiKey)
	te, err := ts.CreateTimeEntry(pid, 0, start, duration, description)
	if err != nil {
		log.Fatal("Error uploading time entry! %v", err)
	}
	return &te
}

func stopTimer(apiKey string) (*toggl.TimeEntry, error) {
	ts := toggl.OpenSession(apiKey)
	te, err := ts.GetActiveTimeEntry()
	if err != nil {
		return nil, err
	}
	if te.ID <= 0 {
		return nil, errors.New("No timer is currently running!")
	}
	te, err = ts.StopTimeEntry(te)
	if err != nil {
		return nil, err
	}
	return &te, nil
}

func startTimer(apiKey, description string, pid int) *toggl.TimeEntry {
	ts := toggl.OpenSession(apiKey)
	te, err := ts.StartTimeEntryForProject(description, pid)

	if err != nil {
		fmt.Println(err)
	}
	sessionMap[apiKey] = te
	return &te
}

func getProjectWithName(apiKey, projectName string) int {
	ts := toggl.OpenSession(apiKey)
	acc, err := ts.GetAccount()
	var retVal int
	if err != nil {
		fmt.Println(err)
	}
	for _, project := range acc.Data.Projects {
		//case insensitive string comparison
		if strings.EqualFold(project.Name, projectName) {
			retVal = project.ID
		}
	}
	return retVal
}

type BotCommand struct {
	Channel string
	Event   *slack.MessageEvent
	UserId  string
}

type ReplyChannel struct {
	Channel      string
	Attachment   *slack.Attachment
	DisplayTitle string
}

var (
	api               *slack.Client
	botCommandChannel chan *BotCommand
	botReplyChannel   chan ReplyChannel
	botId             string
	userMap           map[string]string
	sessionMap        map[string]toggl.TimeEntry
)

func handleBotCommands(replyChannel chan ReplyChannel) {
	commands := map[string]string{
		"register": "Register yourself with togglbot. `@togglbot register MY_TOGGL_API_KEY`",
		"start":    "Start a timer for a given project and description. `@togglbot start <PROJECT_NAME> <EVERYTHING_ELSE_IS_DESCRIPTION>`",
		"stop":     "Stops any current timer session. `@togglbot stop`",
		"track":    "adds a toggl entry to a project for a given time range. `@togglbot track icancope 9am-5pm`",
	}

	for {
		incomingCommand := <-botCommandChannel
		commandArray := strings.Fields(incomingCommand.Event.Text)
		if commandArray[0] == "<@"+botId+">" {
			commandArray = commandArray[1:]
		}
		var reply ReplyChannel
		reply.Channel = incomingCommand.Channel

		switch commandArray[0] {
		case "help":
			reply.DisplayTitle = "Help!"
			fields := make([]slack.AttachmentField, 0)
			for k, v := range commands {
				fields = append(fields, slack.AttachmentField{
					Title: "<bot> " + k,
					Value: v,
				})
			}
			attachment := &slack.Attachment{
				Pretext:    "TogglBot Command List",
				Color:      "#B733FF",
				Fields:     fields,
				MarkdownIn: []string{"fields"},
			}
			reply.Attachment = attachment
			replyChannel <- reply
		case "register":
			togglApiKey := commandArray[1]
			err := pingTogglApi(togglApiKey)
			if err != nil {
				reply.DisplayTitle = "Failed to register. Bad api key?"
			} else {
				userMap[incomingCommand.Event.User] = togglApiKey
				err := Save(file, userMap)
				Check(err)
				reply.DisplayTitle = "Successfully registered!"
			}
			replyChannel <- reply
		}

		if reply.DisplayTitle != "" {
			continue
		}
		togglApiKey, ok := userMap[incomingCommand.Event.User]
		if !ok {
			reply.DisplayTitle = "You have not registered with togglbot yet. Try @togglbot register API_KEY_HERE"
			replyChannel <- reply
			continue
		}

		switch commandArray[0] {
		case "start":
			if len(commandArray) <= 2 {
				reply.DisplayTitle = "Please provide a project name and description! `@togglbot start PROJECT_NAME DESCRIPTION`"
				replyChannel <- reply
				break
			}
			project := commandArray[1]
			description := strings.Join(commandArray[2:], " ")
			pid := getProjectWithName(togglApiKey, project)
			fmt.Printf("%v", pid)
			startTimer(togglApiKey, description, pid)
			reply.DisplayTitle = "Timer started! *get back to work peon*"
			replyChannel <- reply
		case "stop":
			te, err := stopTimer(togglApiKey)
			if err != nil {
				reply.DisplayTitle = "couldn't stop timer: " + err.Error()
				replyChannel <- reply
				break
			}
			dur, err := time.ParseDuration(fmt.Sprintf("%vs", te.Duration))
			if err != nil {
				log.Fatal("Unparseable duration! %v", te.Duration)
			}
			reply.DisplayTitle = fmt.Sprintf("Timer Stopped. Worked for %v.", dur.String())
			replyChannel <- reply
		case "track":
			if len(commandArray) < 3 {
				reply.DisplayTitle = "Sorry, I don't have enough information to make an event for you. try `@togglbot track PROJECT_NAME 9:00AM-5:00PM TASK_DESCRIPTION`"
				replyChannel <- reply
				break
			}
			projectName := commandArray[1]
			pid := getProjectWithName(togglApiKey, projectName)
			timeRange := commandArray[2]
			description := "no description provided"
			if len(commandArray) > 3 {
				description = strings.Join(commandArray[3:], " ")
			}

			startTime, duration, err := parseTimeRange(timeRange)
			if err != nil {
				reply.DisplayTitle = err.Error()
				replyChannel <- reply
				break
			}
			createTimeEntry(togglApiKey, description, *startTime, *duration, pid, 0)
			reply.DisplayTitle = "Time entry created!"
			replyChannel <- reply
		default:
			reply.DisplayTitle = "Sorry, i don't understand that command. Try `@Togglbot help`"
			replyChannel <- reply
		}
	}
}

func parseTimeRange(timeRange string) (*time.Time, *time.Duration, error) {
	fields := strings.Split(timeRange, "-")
	if len(fields) != 2 {
		return nil, nil, errors.New("Your date range is incorrectly formatted! Try something like: 9:00AM-5:00PM")
	}

	startTime, err := time.Parse(time.Kitchen, fields[0])
	if err != nil {
		return nil, nil, errors.New("Your date range is incorrectly formatted! Try something like: 9:00AM-5:00PM")
	}

	endTime, err := time.Parse(time.Kitchen, fields[1])
	if err != nil {
		return nil, nil, errors.New("Your date range is incorrectly formatted! Try something like: 9:00AM-5:00PM")
	}

	duration := endTime.Sub(startTime)

	// Kitchen time has only hours and PM/AM. Drop in today's date.
	now := time.Now()
	startTime = startTime.AddDate(now.Year(), int(now.Month())-1, now.Day()-1)
	return &startTime, &duration, nil
}

func handleBotReplies() {
	for {
		reply := <-botReplyChannel
		params := slack.PostMessageParameters{}
		params.AsUser = true
		if reply.Attachment != nil {
			params.Attachments = []slack.Attachment{*reply.Attachment}
		}
		_, _, err := api.PostMessage(reply.Channel, reply.DisplayTitle, params)
		if err != nil {
			fmt.Println("FATAL SHIT")
			log.Fatal(err)
		}
	}
}

func main() {

	if len(os.Args) != 2 {
		fmt.Printf("usage: togglbot slack-bot-token")
		os.Exit(1)
	}

	//First attempt to deserialize a saved registration file, otherwise
	//create new
	err := Load(file, &userMap)
	if err != nil {
		fmt.Println("ERROR WAS DETECTED")
		log.Fatal(err)
		userMap = make(map[string]string)
	}

	sessionMap = make(map[string]toggl.TimeEntry)
	token := os.Args[1]
	api = slack.New(token)
	rtm := api.NewRTM()
	botCommandChannel = make(chan *BotCommand)
	botReplyChannel = make(chan ReplyChannel)
	go rtm.ManageConnection()
	go handleBotCommands(botReplyChannel)
	go handleBotReplies()
	go fmt.Println("TogglBot ready, ^C exits")
Loop:
	for {
		select {
		case msg := <-rtm.IncomingEvents:
			switch event := msg.Data.(type) {
			case *slack.ConnectedEvent:
				botId = event.Info.User.ID
			case *slack.MessageEvent:

				botCommand := &BotCommand{
					Channel: event.Channel,
					Event:   event,
					UserId:  event.User,
				}

				if isValidMessageEvent(event) {
					fmt.Println("Received event: ", event)
					botCommandChannel <- botCommand
				}
			case *slack.RTMError:
				fmt.Printf("ERROR: %s\n", event.Error())
			case *slack.InvalidAuthEvent:
				fmt.Printf("Invalid credentials")
				break Loop
			}
		}
	}

}

func isValidMessageEvent(event *slack.MessageEvent) bool {
	if event.Type != "message" {
		return false
	}
	if event.User == botId {
		return false
	}
	if strings.HasPrefix(event.Text, "<@"+botId+">") {
		return true
	}
	if strings.HasPrefix(event.Channel, "D") {
		return true
	}
	return false
}
<commit_msg>closes #1, and fixes minor bug with time tracking<commit_after>package main

import "fmt"
import "log"
import "os"
import "errors"
import "strings"
import "github.com/nlopes/slack"
import "github.com/tadgh/go-toggl"
import "encoding/gob"
import "time"
import "runtime"

const file = "./test.gob"

func Check(err error) {
	if err != nil {
		_, file, line, _ := runtime.Caller(1)
		fmt.Println(line, "\t", file, "\n", err)
		os.Exit(1)
	}
}

func pingTogglApi(apiKey string) error {
	ts := toggl.OpenSession(apiKey)
	_, err := ts.GetAccount()
	if err != nil {
		fmt.Println(os.Stderr, "Error: %s\n", err)
		return err
	}
	return nil
}

func Save(path string, object interface{}) error {
	file, err := os.Create(path)
	if err == nil {
		encoder := gob.NewEncoder(file)
		encoder.Encode(object)
	}
	file.Close()
	return err
}

func Load(path string, object interface{}) error {
	file, err := os.Open(path)
	if err == nil {
		decoder := gob.NewDecoder(file)
		err = decoder.Decode(object)
	}
	file.Close()
	return err
}

func createTimeEntry(apiKey, description string, start time.Time, duration time.Duration, pid, tid int) *toggl.TimeEntry {
	ts := toggl.OpenSession(apiKey)
	te, err := ts.CreateTimeEntry(pid, 0, start, duration, description)
	if err != nil {
		log.Fatal("Error uploading time entry! %v", err)
	}
	return &te
}

func stopTimer(apiKey string) (*toggl.TimeEntry, error) {
	ts := toggl.OpenSession(apiKey)
	te, err := ts.GetActiveTimeEntry()
	if err != nil {
		return nil, err
	}
	if te.ID <= 0 {
		return nil, errors.New("No timer is currently running!")
	}
	te, err = ts.StopTimeEntry(te)
	if err != nil {
		return nil, err
	}
	return &te, nil
}

func startTimer(apiKey, description string, pid int) *toggl.TimeEntry {
	ts := toggl.OpenSession(apiKey)
	te, err := ts.StartTimeEntryForProject(description, pid)

	if err != nil {
		fmt.Println(err)
	}
	sessionMap[apiKey] = te
	return &te
}

func getProjectWithName(apiKey, projectName string) int {
	ts := toggl.OpenSession(apiKey)
	acc, err := ts.GetAccount()
	var retVal int
	if err != nil {
		fmt.Println(err)
	}
	for _, project := range acc.Data.Projects {
		//case insensitive string comparison
		if strings.EqualFold(project.Name, projectName) {
			retVal = project.ID
		}
	}
	return retVal
}

type BotCommand struct {
	Channel string
	Event   *slack.MessageEvent
	UserId  string
}

type ReplyChannel struct {
	Channel      string
	Attachment   *slack.Attachment
	DisplayTitle string
}

var (
	api               *slack.Client
	botCommandChannel chan *BotCommand
	botReplyChannel   chan ReplyChannel
	botId             string
	userMap           map[string]string
	sessionMap        map[string]toggl.TimeEntry
)

func handleBotCommands(replyChannel chan ReplyChannel) {
	commands := map[string]string{
		"register": "Register yourself with togglbot. `@togglbot register MY_TOGGL_API_KEY`",
		"start":    "Start a timer for a given project and description. `@togglbot start <PROJECT_NAME> <EVERYTHING_ELSE_IS_DESCRIPTION>`",
		"stop":     "Stops any current timer session. `@togglbot stop`",
		"track":    "adds a toggl entry to a project for a given time range. `@togglbot track icancope 9am-5pm`",
	}

	for {
		incomingCommand := <-botCommandChannel

		commandArray := strings.Fields(strings.ToLower(incomingCommand.Event.Text))
		if strings.EqualFold(commandArray[0], "<@"+botId+">") {
			commandArray = commandArray[1:]
		}
		var reply ReplyChannel
		reply.Channel = incomingCommand.Channel

		switch commandArray[0] {
		case "help":
			reply.DisplayTitle = "Help!"
			fields := make([]slack.AttachmentField, 0)
			for k, v := range commands {
				fields = append(fields, slack.AttachmentField{
					Title: "<bot> " + k,
					Value: v,
				})
			}
			attachment := &slack.Attachment{
				Pretext:    "TogglBot Command List",
				Color:      "#B733FF",
				Fields:     fields,
				MarkdownIn: []string{"fields"},
			}
			reply.Attachment = attachment
			replyChannel <- reply
		case "register":
			togglApiKey := commandArray[1]
			err := pingTogglApi(togglApiKey)
			if err != nil {
				reply.DisplayTitle = "Failed to register. Bad api key?"
			} else {
				userMap[incomingCommand.Event.User] = togglApiKey
				err := Save(file, userMap)
				Check(err)
				reply.DisplayTitle = "Successfully registered!"
			}
			replyChannel <- reply
		}

		if reply.DisplayTitle != "" {
			continue
		}
		togglApiKey, ok := userMap[incomingCommand.Event.User]
		if !ok {
			reply.DisplayTitle = "You have not registered with togglbot yet. Try @togglbot register API_KEY_HERE"
			replyChannel <- reply
			continue
		}

		switch commandArray[0] {
		case "start":
			if len(commandArray) <= 2 {
				reply.DisplayTitle = "Please provide a project name and description! `@togglbot start PROJECT_NAME DESCRIPTION`"
				replyChannel <- reply
				break
			}
			project := commandArray[1]
			description := strings.Join(commandArray[2:], " ")
			pid := getProjectWithName(togglApiKey, project)
			fmt.Printf("%v", pid)
			startTimer(togglApiKey, description, pid)
			reply.DisplayTitle = "Timer started! *get back to work peon*"
			replyChannel <- reply
		case "stop":
			te, err := stopTimer(togglApiKey)
			if err != nil {
				reply.DisplayTitle = "couldn't stop timer: " + err.Error()
				replyChannel <- reply
				break
			}
			dur, err := time.ParseDuration(fmt.Sprintf("%vs", te.Duration))
			if err != nil {
				log.Fatal("Unparseable duration! %v", te.Duration)
			}
			reply.DisplayTitle = fmt.Sprintf("Timer Stopped. Worked for %v.", dur.String())
			replyChannel <- reply
		case "track":
			if len(commandArray) < 3 {
				reply.DisplayTitle = "Sorry, I don't have enough information to make an event for you. try `@togglbot track PROJECT_NAME 9:00AM-5:00PM TASK_DESCRIPTION`"
				replyChannel <- reply
				break
			}
			projectName := commandArray[1]
			pid := getProjectWithName(togglApiKey, projectName)
			timeRange := commandArray[2]
			description := "no description provided"
			if len(commandArray) > 3 {
				description = strings.Join(commandArray[3:], " ")
			}

			startTime, duration, err := parseTimeRange(timeRange)
			if err != nil {
				reply.DisplayTitle = err.Error()
				replyChannel <- reply
				break
			}
			createTimeEntry(togglApiKey, description, *startTime, *duration, pid, 0)
			reply.DisplayTitle = "Time entry created!"
			replyChannel <- reply
		default:
			reply.DisplayTitle = "Sorry, i don't understand that command. Try `@Togglbot help`"
			replyChannel <- reply
		}
	}
}

func parseTimeRange(timeRange string) (*time.Time, *time.Duration, error) {
	timeRange = strings.ToUpper(timeRange)
	fields := strings.Split(timeRange, "-")
	if len(fields) != 2 {
		return nil, nil, errors.New("Your date range is incorrectly formatted! Try something like: 9:00AM-5:00PM")
	}

	startTime, err := time.Parse(time.Kitchen, fields[0])
	if err != nil {
		return nil, nil, errors.New("Your date range is incorrectly formatted! Try something like: 9:00AM-5:00PM")
	}

	endTime, err := time.Parse(time.Kitchen, fields[1])
	if err != nil {
		return nil, nil, errors.New("Your date range is incorrectly formatted! Try something like: 9:00AM-5:00PM")
	}

	duration := endTime.Sub(startTime)

	// Kitchen time has only hours and PM/AM. Drop in today's date.
	now := time.Now()
	fmt.Println("NOW:\t", now)
	startTime = time.Date(now.Year(), now.Month(), now.Day(), startTime.Hour(), startTime.Minute(), startTime.Second(), startTime.Nanosecond(), now.Location())
	fmt.Println("START TIME:\t", startTime)
	return &startTime, &duration, nil
}

func handleBotReplies() {
	for {
		reply := <-botReplyChannel
		params := slack.PostMessageParameters{}
		params.AsUser = true
		if reply.Attachment != nil {
			params.Attachments = []slack.Attachment{*reply.Attachment}
		}
		_, _, err := api.PostMessage(reply.Channel, reply.DisplayTitle, params)
		if err != nil {
			fmt.Println("FATAL SHIT")
			log.Fatal(err)
		}
	}
}

func main() {

	if len(os.Args) != 2 {
		fmt.Printf("usage: togglbot slack-bot-token")
		os.Exit(1)
	}

	//First attempt to deserialize a saved registration file, otherwise
	//create new
	err := Load(file, &userMap)
	if err != nil {
		fmt.Println("ERROR WAS DETECTED")
		log.Fatal(err)
		userMap = make(map[string]string)
	}

	sessionMap = make(map[string]toggl.TimeEntry)
	token := os.Args[1]
	api = slack.New(token)
	rtm := api.NewRTM()
	botCommandChannel = make(chan *BotCommand)
	botReplyChannel = make(chan ReplyChannel)
	go rtm.ManageConnection()
	go handleBotCommands(botReplyChannel)
	go handleBotReplies()
	go fmt.Println("TogglBot ready, ^C exits")
Loop:
	for {
		select {
		case msg := <-rtm.IncomingEvents:
			switch event := msg.Data.(type) {
			case *slack.ConnectedEvent:
				botId = event.Info.User.ID
			case *slack.MessageEvent:

				botCommand := &BotCommand{
					Channel: event.Channel,
					Event:   event,
					UserId:  event.User,
				}

				if isValidMessageEvent(event) {
					fmt.Println("Received event: ", event)
					botCommandChannel <- botCommand
				}
			case *slack.RTMError:
				fmt.Printf("ERROR: %s\n", event.Error())
			case *slack.InvalidAuthEvent:
				fmt.Printf("Invalid credentials")
				break Loop
			}
		}
	}

}

func isValidMessageEvent(event *slack.MessageEvent) bool {
	if event.Type != "message" {
		return false
	}
	if event.User == botId {
		return false
	}
	if strings.HasPrefix(event.Text, "<@"+botId+">") {
		return true
	}
	if strings.HasPrefix(event.Channel, "D") {
		return true
	}
	return false
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"go/build"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"

	"golang.org/x/tools/go/loader"

	"sourcegraph.com/sourcegraph/srclib-go/gog"
	"sourcegraph.com/sourcegraph/srclib-go/gog/definfo"
	defpkg "sourcegraph.com/sourcegraph/srclib-go/golang_def"
	"sourcegraph.com/sourcegraph/srclib/graph"
	"sourcegraph.com/sourcegraph/srclib/unit"
)

func init() {
	_, err := parser.AddCommand("graph",
		"graph a Go package",
		"Graph a Go package, producing all defs, refs, and docs.",
		&graphCmd,
	)
	if err != nil {
		log.Fatal(err)
	}

	// Check that we have the '-i' flag.
	cmd := exec.Command("go", "help", "build")
	o, err := cmd.Output()
	if err != nil {
		log.Fatal(err)
	}
	usage := strings.Split(string(o), "\n")[0] // The usage is on the first line.
	matched, err := regexp.MatchString("-i", usage)
	if err != nil {
		log.Fatal(err)
	}
	if !matched {
		log.Fatal("'go build' does not have the '-i' flag. Please upgrade to go1.3+.")
	}
}

type GraphCmd struct{}

var graphCmd GraphCmd

// allowErrorsInGoGet is whether the grapher should continue after
// if `go get` fails.
var allowErrorsInGoGet = true

func (c *GraphCmd) Execute(args []string) error {
	inputBytes, err := ioutil.ReadAll(os.Stdin)
	if err != nil {
		return err
	}
	var units unit.SourceUnits
	if err := json.NewDecoder(bytes.NewReader(inputBytes)).Decode(&units); err != nil {
		// Legacy API: try parsing input as a single source unit
		var u *unit.SourceUnit
		if err := json.NewDecoder(bytes.NewReader(inputBytes)).Decode(&u); err != nil {
			return err
		}
		units = unit.SourceUnits{u}
	}
	if err := os.Stdin.Close(); err != nil {
		return err
	}

	if len(units) == 0 {
		log.Fatal("Input contains no source unit data.")
	}

	// HACK: fix this. Is this required? We only seem to be setting
	// GOROOT and GOPATH
	if err := unmarshalTypedConfig(units[0].Config); err != nil {
		return err
	}
	if err := config.apply(); err != nil {
		return err
	}

	out, err := Graph(units)
	if err != nil {
		return err
	}

	// Make paths relative to repo.
	for _, gs := range out.Defs {
		if gs.File == "" {
			log.Printf("no file %+v", gs)
		}
		if gs.File != "" {
			gs.File = relPath(cwd, gs.File)
		}
	}
	for _, gr := range out.Refs {
		if gr.File != "" {
			gr.File = relPath(cwd, gr.File)
		}
	}
	for _, gd := range out.Docs {
		if gd.File != "" {
			gd.File = relPath(cwd, gd.File)
		}
	}

	if err := json.NewEncoder(os.Stdout).Encode(out); err != nil {
		return err
	}
	return nil
}

func relPath(base, path string) string {
	rp, err := filepath.Rel(evalSymlinks(base), evalSymlinks(path))
	if err != nil {
		log.Fatalf("Failed to make path %q relative to %q: %s", path, base, err)
	}
	return filepath.ToSlash(rp)
}

func Graph(units unit.SourceUnits) (*graph.Output, error) {
	var pkgs []*build.Package
	for _, u := range units {
		pkg, err := UnitDataAsBuildPackage(u)
		if err != nil {
			log.Printf("Ignoring unit %q due to error in converting to build pkg: %s.", u.Name, err)
			continue
		}
		pkgs = append(pkgs, pkg)
	}

	o, err := doGraph(pkgs)
	if err != nil {
		return nil, err
	}

	o2 := graph.Output{}

	for _, gs := range o.Defs {
		d, err := convertGoDef(gs)
		if err != nil {
			log.Printf("Ignoring def %v due to error in converting to GoDef: %s.", gs, err)
			continue
		}
		if d != nil {
			o2.Defs = append(o2.Defs, d)
		}
	}
	for _, gr := range o.Refs {
		r, err := convertGoRef(gr)
		if err != nil {
			log.Printf("Ignoring ref %v due to error in converting to GoRef: %s.", gr, err)
			continue
		}
		if r != nil {
			o2.Refs = append(o2.Refs, r)
		}
	}
	for _, gd := range o.Docs {
		d, err := convertGoDoc(gd)
		if err != nil {
			log.Printf("Ignoring doc %v due to error in converting to GoDoc: %s.", gd, err)
			continue
		}
		if d != nil {
			o2.Docs = append(o2.Docs, d)
		}
	}

	return &o2, nil
}

func convertGoDef(gs *gog.Def) (*graph.Def, error) {
	resolvedTarget, err := ResolveDep(gs.DefKey.PackageImportPath)
	if err != nil {
		return nil, err
	}
	path := filepath.ToSlash(pathOrDot(filepath.Join(gs.Path...)))
	treePath := treePath(strings.Replace(string(path), ".go", "", -1))
	if !graph.IsValidTreePath(treePath) {
		return nil, fmt.Errorf("'%s' is not a valid tree-path", treePath)
	}

	def := &graph.Def{
		DefKey: graph.DefKey{
			Unit:     resolvedTarget.ToUnit,
			UnitType: resolvedTarget.ToUnitType,
			Path:     path,
		},
		TreePath: treePath,

		Name: gs.Name,
		Kind: definfo.GeneralKindMap[gs.Kind],

		File:     filepath.ToSlash(gs.File),
		DefStart: gs.DeclSpan[0],
		DefEnd:   gs.DeclSpan[1],

		Exported: gs.DefInfo.Exported,
		Local:    !gs.DefInfo.Exported && !gs.DefInfo.PkgScope,
		Test:     strings.HasSuffix(gs.File, "_test.go"),
	}

	d := defpkg.DefData{
		PackageImportPath: gs.DefKey.PackageImportPath,
		DefInfo:           gs.DefInfo,
	}
	def.Data, err = json.Marshal(d)
	if err != nil {
		return nil, err
	}

	if def.File == "" {
		// some cgo defs have empty File; omit them
		return nil, nil
	}

	return def, nil
}

func convertGoRef(gr *gog.Ref) (*graph.Ref, error) {
	resolvedTarget, err := ResolveDep(gr.Def.PackageImportPath)
	if err != nil {
		return nil, err
	}
	if resolvedTarget == nil {
		return nil, nil
	}

	resolvedRefUnit, err := ResolveDep(gr.Unit)
	if err != nil {
		return nil, err
	}
	if resolvedRefUnit == nil {
		return nil, nil
	}

	return &graph.Ref{
		DefRepo:     filepath.ToSlash(uriOrEmpty(resolvedTarget.ToRepoCloneURL)),
		DefPath:     filepath.ToSlash(pathOrDot(filepath.Join(gr.Def.Path...))),
		DefUnit:     resolvedTarget.ToUnit,
		DefUnitType: resolvedTarget.ToUnitType,
		Def:         gr.IsDef,
		Unit:        resolvedRefUnit.ToUnit,
		File:        filepath.ToSlash(gr.File),
		Start:       gr.Span[0],
		End:         gr.Span[1],
	}, nil
}

func convertGoDoc(gd *gog.Doc) (*graph.Doc, error) {
	var key graph.DefKey
	if gd.DefKey != nil {
		resolvedTarget, err := ResolveDep(gd.PackageImportPath)
		if err != nil {
			return nil, err
		}
		key = graph.DefKey{
			Path:     filepath.ToSlash(pathOrDot(filepath.Join(gd.Path...))),
			Unit:     resolvedTarget.ToUnit,
			UnitType: resolvedTarget.ToUnitType,
		}
	}

	resolvedDocUnit, err := ResolveDep(gd.Unit)
	if err != nil {
		return nil, err
	}
	if resolvedDocUnit == nil {
		return nil, nil
	}

	return &graph.Doc{
		DefKey:  key,
		Format:  gd.Format,
		Data:    gd.Data,
		File:    filepath.ToSlash(gd.File),
		Start:   gd.Span[0],
		End:     gd.Span[1],
		DocUnit: resolvedDocUnit.ToUnit,
	}, nil
}

func uriOrEmpty(cloneURL string) string {
	if cloneURL == "" {
		return ""
	}
	return graph.MakeURI(cloneURL)
}

func pathOrDot(path string) string {
	if path == "" {
		return "."
	}
	return path
}

func treePath(path string) string {
	if path == "" || path == "." {
		return string(".")
	}
	return "./" + path
}

// allowErrorsInGraph is whether the grapher should continue after
// encountering "reasonably common" errors (such as compile errors).
var allowErrorsInGraph = true

func doGraph(pkgs []*build.Package) (*gog.Output, error) {
	// Special-case: if this is a Cgo package, treat the CgoFiles as GoFiles or
	// else the character offsets will be junk.
	//
	// See https://codereview.appspot.com/86140043.
	loaderConfig.Build.CgoEnabled = false
	build.Default = *loaderConfig.Build

	for _, pkg := range pkgs {
		importPath := pkg.ImportPath
		importUnsafe := importPath == "unsafe"

		if len(pkg.CgoFiles) > 0 {
			var allGoFiles []string
			allGoFiles = append(allGoFiles, pkg.GoFiles...)
			allGoFiles = append(allGoFiles, pkg.CgoFiles...)
			allGoFiles = append(allGoFiles, pkg.TestGoFiles...)
			for i, f := range allGoFiles {
				allGoFiles[i] = filepath.Join(cwd, pkg.Dir, f)
			}
			loaderConfig.CreateFromFilenames(pkg.ImportPath, allGoFiles...)
		} else {
			// Normal import
			loaderConfig.ImportWithTests(importPath)
		}

		if importUnsafe {
			// Special-case "unsafe" because go/loader does not let you load it
			// directly.
			if loaderConfig.ImportPkgs == nil {
				loaderConfig.ImportPkgs = make(map[string]bool)
			}
			loaderConfig.ImportPkgs["unsafe"] = true
		}
	}

	prog, err := loaderConfig.Load()
	if err != nil {
		log.Println("XXX", err)
		return nil, err
	}

	g := gog.New(prog)

	var pkgInfos []*loader.PackageInfo
	for _, pkg := range prog.Created {
		if strings.HasSuffix(pkg.Pkg.Name(), "_test") {
			// ignore xtest packages
			continue
		}
		pkgInfos = append(pkgInfos, pkg)
	}
	for _, pkg := range prog.Imported {
		pkgInfos = append(pkgInfos, pkg)
	}

	for _, pkg := range pkgInfos {
		if err := g.Graph(pkg); err != nil {
			log.Printf("Ignoring pkg %q due to error in gog.Graph: %s.", pkg.Pkg.Name(), err)
		}
	}

	return &g.Output, nil
}
<commit_msg>remove unused global variable<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"go/build"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"

	"golang.org/x/tools/go/loader"

	"sourcegraph.com/sourcegraph/srclib-go/gog"
	"sourcegraph.com/sourcegraph/srclib-go/gog/definfo"
	defpkg "sourcegraph.com/sourcegraph/srclib-go/golang_def"
	"sourcegraph.com/sourcegraph/srclib/graph"
	"sourcegraph.com/sourcegraph/srclib/unit"
)

func init() {
	_, err := parser.AddCommand("graph",
		"graph a Go package",
		"Graph a Go package, producing all defs, refs, and docs.",
		&graphCmd,
	)
	if err != nil {
		log.Fatal(err)
	}

	// Check that we have the '-i' flag.
	cmd := exec.Command("go", "help", "build")
	o, err := cmd.Output()
	if err != nil {
		log.Fatal(err)
	}
	usage := strings.Split(string(o), "\n")[0] // The usage is on the first line.
	matched, err := regexp.MatchString("-i", usage)
	if err != nil {
		log.Fatal(err)
	}
	if !matched {
		log.Fatal("'go build' does not have the '-i' flag. Please upgrade to go1.3+.")
	}
}

type GraphCmd struct{}

var graphCmd GraphCmd

// allowErrorsInGoGet is whether the grapher should continue after
// if `go get` fails.
var allowErrorsInGoGet = true

func (c *GraphCmd) Execute(args []string) error {
	inputBytes, err := ioutil.ReadAll(os.Stdin)
	if err != nil {
		return err
	}
	var units unit.SourceUnits
	if err := json.NewDecoder(bytes.NewReader(inputBytes)).Decode(&units); err != nil {
		// Legacy API: try parsing input as a single source unit
		var u *unit.SourceUnit
		if err := json.NewDecoder(bytes.NewReader(inputBytes)).Decode(&u); err != nil {
			return err
		}
		units = unit.SourceUnits{u}
	}
	if err := os.Stdin.Close(); err != nil {
		return err
	}

	if len(units) == 0 {
		log.Fatal("Input contains no source unit data.")
	}

	// HACK: fix this. Is this required? We only seem to be setting
	// GOROOT and GOPATH
	if err := unmarshalTypedConfig(units[0].Config); err != nil {
		return err
	}
	if err := config.apply(); err != nil {
		return err
	}

	out, err := Graph(units)
	if err != nil {
		return err
	}

	// Make paths relative to repo.
	for _, gs := range out.Defs {
		if gs.File == "" {
			log.Printf("no file %+v", gs)
		}
		if gs.File != "" {
			gs.File = relPath(cwd, gs.File)
		}
	}
	for _, gr := range out.Refs {
		if gr.File != "" {
			gr.File = relPath(cwd, gr.File)
		}
	}
	for _, gd := range out.Docs {
		if gd.File != "" {
			gd.File = relPath(cwd, gd.File)
		}
	}

	if err := json.NewEncoder(os.Stdout).Encode(out); err != nil {
		return err
	}
	return nil
}

func relPath(base, path string) string {
	rp, err := filepath.Rel(evalSymlinks(base), evalSymlinks(path))
	if err != nil {
		log.Fatalf("Failed to make path %q relative to %q: %s", path, base, err)
	}
	return filepath.ToSlash(rp)
}

func Graph(units unit.SourceUnits) (*graph.Output, error) {
	var pkgs []*build.Package
	for _, u := range units {
		pkg, err := UnitDataAsBuildPackage(u)
		if err != nil {
			log.Printf("Ignoring unit %q due to error in converting to build pkg: %s.", u.Name, err)
			continue
		}
		pkgs = append(pkgs, pkg)
	}

	o, err := doGraph(pkgs)
	if err != nil {
		return nil, err
	}

	o2 := graph.Output{}

	for _, gs := range o.Defs {
		d, err := convertGoDef(gs)
		if err != nil {
			log.Printf("Ignoring def %v due to error in converting to GoDef: %s.", gs, err)
			continue
		}
		if d != nil {
			o2.Defs = append(o2.Defs, d)
		}
	}
	for _, gr := range o.Refs {
		r, err := convertGoRef(gr)
		if err != nil {
			log.Printf("Ignoring ref %v due to error in converting to GoRef: %s.", gr, err)
			continue
		}
		if r != nil {
			o2.Refs = append(o2.Refs, r)
		}
	}
	for _, gd := range o.Docs {
		d, err := convertGoDoc(gd)
		if err != nil {
			log.Printf("Ignoring doc %v due to error in converting to GoDoc: %s.", gd, err)
			continue
		}
		if d != nil {
			o2.Docs = append(o2.Docs, d)
		}
	}

	return &o2, nil
}

func convertGoDef(gs *gog.Def) (*graph.Def, error) {
	resolvedTarget, err := ResolveDep(gs.DefKey.PackageImportPath)
	if err != nil {
		return nil, err
	}
	path := filepath.ToSlash(pathOrDot(filepath.Join(gs.Path...)))
	treePath := treePath(strings.Replace(string(path), ".go", "", -1))
	if !graph.IsValidTreePath(treePath) {
		return nil, fmt.Errorf("'%s' is not a valid tree-path", treePath)
	}

	def := &graph.Def{
		DefKey: graph.DefKey{
			Unit:     resolvedTarget.ToUnit,
			UnitType: resolvedTarget.ToUnitType,
			Path:     path,
		},
		TreePath: treePath,

		Name: gs.Name,
		Kind: definfo.GeneralKindMap[gs.Kind],

		File:     filepath.ToSlash(gs.File),
		DefStart: gs.DeclSpan[0],
		DefEnd:   gs.DeclSpan[1],

		Exported: gs.DefInfo.Exported,
		Local:    !gs.DefInfo.Exported && !gs.DefInfo.PkgScope,
		Test:     strings.HasSuffix(gs.File, "_test.go"),
	}

	d := defpkg.DefData{
		PackageImportPath: gs.DefKey.PackageImportPath,
		DefInfo:           gs.DefInfo,
	}
	def.Data, err = json.Marshal(d)
	if err != nil {
		return nil, err
	}

	if def.File == "" {
		// some cgo defs have empty File; omit them
		return nil, nil
	}

	return def, nil
}

func convertGoRef(gr *gog.Ref) (*graph.Ref, error) {
	resolvedTarget, err := ResolveDep(gr.Def.PackageImportPath)
	if err != nil {
		return nil, err
	}
	if resolvedTarget == nil {
		return nil, nil
	}

	resolvedRefUnit, err := ResolveDep(gr.Unit)
	if err != nil {
		return nil, err
	}
	if resolvedRefUnit == nil {
		return nil, nil
	}

	return &graph.Ref{
		DefRepo:     filepath.ToSlash(uriOrEmpty(resolvedTarget.ToRepoCloneURL)),
		DefPath:     filepath.ToSlash(pathOrDot(filepath.Join(gr.Def.Path...))),
		DefUnit:     resolvedTarget.ToUnit,
		DefUnitType: resolvedTarget.ToUnitType,
		Def:         gr.IsDef,
		Unit:        resolvedRefUnit.ToUnit,
		File:        filepath.ToSlash(gr.File),
		Start:       gr.Span[0],
		End:         gr.Span[1],
	}, nil
}

func convertGoDoc(gd *gog.Doc) (*graph.Doc, error) {
	var key graph.DefKey
	if gd.DefKey != nil {
		resolvedTarget, err := ResolveDep(gd.PackageImportPath)
		if err != nil {
			return nil, err
		}
		key = graph.DefKey{
			Path:     filepath.ToSlash(pathOrDot(filepath.Join(gd.Path...))),
			Unit:     resolvedTarget.ToUnit,
			UnitType: resolvedTarget.ToUnitType,
		}
	}

	resolvedDocUnit, err := ResolveDep(gd.Unit)
	if err != nil {
		return nil, err
	}
	if resolvedDocUnit == nil {
		return nil, nil
	}

	return &graph.Doc{
		DefKey:  key,
		Format:  gd.Format,
		Data:    gd.Data,
		File:    filepath.ToSlash(gd.File),
		Start:   gd.Span[0],
		End:     gd.Span[1],
		DocUnit: resolvedDocUnit.ToUnit,
	}, nil
}

func uriOrEmpty(cloneURL string) string {
	if cloneURL == "" {
		return ""
	}
	return graph.MakeURI(cloneURL)
}

func pathOrDot(path string) string {
	if path == "" {
		return "."
	}
	return path
}

func treePath(path string) string {
	if path == "" || path == "." {
		return string(".")
	}
	return "./" + path
}

func doGraph(pkgs []*build.Package) (*gog.Output, error) {
	// Special-case: if this is a Cgo package, treat the CgoFiles as GoFiles or
	// else the character offsets will be junk.
	//
	// See https://codereview.appspot.com/86140043.
	loaderConfig.Build.CgoEnabled = false
	build.Default = *loaderConfig.Build

	for _, pkg := range pkgs {
		importPath := pkg.ImportPath
		importUnsafe := importPath == "unsafe"

		if len(pkg.CgoFiles) > 0 {
			var allGoFiles []string
			allGoFiles = append(allGoFiles, pkg.GoFiles...)
			allGoFiles = append(allGoFiles, pkg.CgoFiles...)
			allGoFiles = append(allGoFiles, pkg.TestGoFiles...)
			for i, f := range allGoFiles {
				allGoFiles[i] = filepath.Join(cwd, pkg.Dir, f)
			}
			loaderConfig.CreateFromFilenames(pkg.ImportPath, allGoFiles...)
		} else {
			// Normal import
			loaderConfig.ImportWithTests(importPath)
		}

		if importUnsafe {
			// Special-case "unsafe" because go/loader does not let you load it
			// directly.
			if loaderConfig.ImportPkgs == nil {
				loaderConfig.ImportPkgs = make(map[string]bool)
			}
			loaderConfig.ImportPkgs["unsafe"] = true
		}
	}

	prog, err := loaderConfig.Load()
	if err != nil {
		log.Println("XXX", err)
		return nil, err
	}

	g := gog.New(prog)

	var pkgInfos []*loader.PackageInfo
	for _, pkg := range prog.Created {
		if strings.HasSuffix(pkg.Pkg.Name(), "_test") {
			// ignore xtest packages
			continue
		}
		pkgInfos = append(pkgInfos, pkg)
	}
	for _, pkg := range prog.Imported {
		pkgInfos = append(pkgInfos, pkg)
	}

	for _, pkg := range pkgInfos {
		if err := g.Graph(pkg); err != nil {
			log.Printf("Ignoring pkg %q due to error in gog.Graph: %s.", pkg.Pkg.Name(), err)
		}
	}

	return &g.Output, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	t "github.com/bytbox/termbox-go"
	. "github.com/bytbox/go-mail"
	"log"
)

var (
	width  int
	height int
)

var chActions = map[rune]func(){
	'u': updateMessages,
}

var keyActions = map[uint16]func(){}

func unknownAction() {
	panic("unrecognized keystroke")
}

func act(a func()) {
	defer func() {
		if er := recover(); er != nil {
			if e, ok := er.(string); ok {
				message <- e
			} else if e, ok := er.(error); ok {
				message <- e.Error()
			} else {
				message <- "unknown error"
			}
		}
	}()
	a()
}

func updateSize() {
	width = t.Width()
	height = t.Height()
}

// Channels for signaling the main routine
var (
	escape = make(chan interface{})  // exits safely
	updates = make(chan interface{}) // updates the screen
	message = make(chan string)      // updates the message displayed
)

type Display struct {
	message  string
	selected int // this is the row of the currently selected message
}

func lim(s string, i int) string {
	if len(s) > i {
		return s[:i]
	}
	return s
}

func updateMessages() {
	// TODO do this in a way that doesn't risk race conditions
	message <- "updating..."
	mc := UpdateAll()
	i := 0
	for m := range mc {
		store.Add(m)
		store.messageList = append(store.messageList, m)
		i++
	}
	message <- fmt.Sprintf("read %d messages", i)
}

func lookup(m Message, k string) string {
	// TODO this is a hack
	for _, h := range m.RawHeaders {
		if k == h.Key {
			return h.Value
		}
	}
	return "---"
}

func display(d Display) {
	t.Clear()

	// Calculate tab-stops
	tabs := []int{
		1,
		2 * width / 8,
		4 * width / 8,
		7 * width / 8,
		width,
	}

	headers := []string{
		"From",
		"To",
		"Subject",
	}

	// Headers
	for i, h := range headers {
		t.WriteAt(tabs[i], 0, h, t.GREEN, t.BLACK)
	}

	// Message
	for i, m := range store.messageList {
		var fg = uint16(t.WHITE)
		var bg = uint16(t.BLACK)
		if i == d.selected {
			bg = t.RED
		}
		for x := 0; x < width; x++ {
			t.ChangeCell(x, i+1, ' ', fg, bg)
		}
		for j, h := range headers {
			c := lim(lookup(m, h), tabs[j+1] - tabs[j] - 1)
			t.WriteAt(tabs[j], i+1, c, fg, bg)
		}
	}

	// Write the error message or otherwise
	messageWriter := t.Writer(0, height-1, t.WHITE, t.BLACK)
	fmt.Fprint(messageWriter, d.message)

	t.Present()
}

func UIMain() {
	t.Init()
	defer func() {
		t.Shutdown()
	}()
	updateSize()

	events := make(chan t.Event)

	go func() {
		defer func() {
			escape <- nil
		}()
		for {
			e := t.Event{}
			e.Poll()
			events <- e
		}
	}()

	d := Display{}

	for {
		display(d)
		select {
		case e := <-events:
			switch e.Type {
			case t.EVENT_KEY:
				if e.Ch == 'q' {
					goto Exit
				}
				a, ok := chActions[e.Ch]
				if !ok {
					a, ok = keyActions[e.Key]
				}
				if ok {
					go act(a)
				} else {
					go act(unknownAction)
				}
			case t.EVENT_RESIZE:
				updateSize()
			default:
				log.Print("warning: unknown event type")
			}
		case <-updates:
		case m := <-message:
			d.message = m
		case <-escape:
			goto Exit
		}
	}

Exit:
}
<commit_msg>Moving the cursor via the arrow keys works<commit_after>package main

import (
	"fmt"
	t "github.com/bytbox/termbox-go"
	. "github.com/bytbox/go-mail"
	"log"
)

var (
	width  int
	height int
)

var chActions = map[rune]func(){
	'u': updateMessages,
}

func unknownAction() {
	panic("unrecognized keystroke")
}

func act(a func()) {
	defer func() {
		if er := recover(); er != nil {
			if e, ok := er.(string); ok {
				message <- e
			} else if e, ok := er.(error); ok {
				message <- e.Error()
			} else {
				message <- "unknown error"
			}
		}
	}()
	a()
}

func updateSize() {
	width = t.Width()
	height = t.Height()
}

// Channels for signaling the main routine
var (
	escape = make(chan interface{})  // exits safely
	updates = make(chan interface{}) // updates the screen
	message = make(chan string)      // updates the message displayed
)

type Display struct {
	message  string
	selected int // this is the row of the currently selected message
}

func lim(s string, i int) string {
	if len(s) > i {
		return s[:i]
	}
	return s
}

func updateMessages() {
	// TODO do this in a way that doesn't risk race conditions
	message <- "updating..."
	mc := UpdateAll()
	i := 0
	for m := range mc {
		store.Add(m)
		store.messageList = append(store.messageList, m)
		i++
	}
	message <- fmt.Sprintf("read %d messages", i)
}

func lookup(m Message, k string) string {
	// TODO this is a hack
	for _, h := range m.RawHeaders {
		if k == h.Key {
			return h.Value
		}
	}
	return "---"
}

func display(d Display) {
	t.Clear()

	// Calculate tab-stops
	tabs := []int{
		1,
		2 * width / 8,
		4 * width / 8,
		7 * width / 8,
		width,
	}

	headers := []string{
		"From",
		"To",
		"Subject",
	}

	// Headers
	for i, h := range headers {
		t.WriteAt(tabs[i], 0, h, t.GREEN, t.BLACK)
	}

	// Message
	for i, m := range store.messageList {
		var fg = uint16(t.WHITE)
		var bg = uint16(t.BLACK)
		if i == d.selected {
			bg = t.RED
		}
		for x := 0; x < width; x++ {
			t.ChangeCell(x, i+1, ' ', fg, bg)
		}
		for j, h := range headers {
			c := lim(lookup(m, h), tabs[j+1] - tabs[j] - 1)
			t.WriteAt(tabs[j], i+1, c, fg, bg)
		}
	}

	// Write the error message or otherwise
	messageWriter := t.Writer(0, height-1, t.WHITE, t.BLACK)
	fmt.Fprint(messageWriter, d.message)

	t.Present()
}

func UIMain() {
	t.Init()
	defer func() {
		t.Shutdown()
	}()
	updateSize()

	events := make(chan t.Event)

	go func() {
		defer func() {
			escape <- nil
		}()
		for {
			e := t.Event{}
			e.Poll()
			events <- e
		}
	}()

	d := Display{}

	for {
		display(d)
		select {
		case e := <-events:
			switch e.Type {
			case t.EVENT_KEY:
				if e.Ch == 'q' {
					goto Exit
				}
				if e.Key == t.KEY_ARROW_UP {
					if d.selected > 0 {
						d.selected--
					}
				} else if e.Key == t.KEY_ARROW_DOWN {
					if d.selected < len(store.messageList)-1 {
						d.selected++
					}
				} else {
					a, ok := chActions[e.Ch]
					if ok {
						go act(a)
					} else {
						go act(unknownAction)
					}
				}
			case t.EVENT_RESIZE:
				updateSize()
			default:
				log.Print("warning: unknown event type")
			}
		case <-updates:
		case m := <-message:
			d.message = m
		case <-escape:
			goto Exit
		}
	}

Exit:
}
<|endoftext|>


<commit_before>package sexp

import (
	"sys_info/old_rt"
)

// IsStmt returns true for statement form.
func IsStmt(form Form) bool {
	return form.Type() == typVoid
}

func IsThrow(form Form) bool {
	switch form := form.(type) {
	case *Call:
		return old_rt.ThrowingFuncs[form.Fn.Name]
	case *LispCall:
		return old_rt.ThrowingFuncs[form.Fn.Sym]

	default:
		return false
	}
}

// IsReturning returns true for forms that unconditionally
// return from function.
func IsReturning(form Form) bool {
	switch form := form.(type) {
	case *Return:
		return true

	case *Block:
		for _, form := range form.Forms {
			if IsReturning(form) {
				return true
			}
		}

	case *If:
		// If both branches return, whole statement returns.
		return IsReturning(form.Then) && IsReturning(form.Else)

	case *ExprStmt:
		return IsReturning(form.Expr)

	case *Call:
		return old_rt.ThrowingFuncs[form.Fn.Name]
	case *LispCall:
		return old_rt.ThrowingFuncs[form.Fn.Sym]
	}

	return false
}
<commit_msg>fix compilation (typo)<commit_after>package sexp

import (
	"sys_info/old_rt"
	"xtypes"
)

// IsStmt returns true for statement form.
func IsStmt(form Form) bool {
	return form.Type() == xtypes.TypVoid
}

func IsThrow(form Form) bool {
	switch form := form.(type) {
	case *Call:
		return old_rt.ThrowingFuncs[form.Fn.Name]
	case *LispCall:
		return old_rt.ThrowingFuncs[form.Fn.Sym]

	default:
		return false
	}
}

// IsReturning returns true for forms that unconditionally
// return from function.
func IsReturning(form Form) bool {
	switch form := form.(type) {
	case *Return:
		return true

	case *Block:
		for _, form := range form.Forms {
			if IsReturning(form) {
				return true
			}
		}

	case *If:
		// If both branches return, whole statement returns.
		return IsReturning(form.Then) && IsReturning(form.Else)

	case *ExprStmt:
		return IsReturning(form.Expr)

	case *Call:
		return old_rt.ThrowingFuncs[form.Fn.Name]
	case *LispCall:
		return old_rt.ThrowingFuncs[form.Fn.Sym]
	}

	return false
}
<|endoftext|>