<commit_before>package cap

import "encoding/xml"

// Alert provides basic information about the current message: its purpose, its source and its status
type Alert struct {
	XMLName xml.Name `xml:"urn:oasis:names:tc:emergency:cap:1.1 alert"`

	MessageID     string   `xml:"identifier"`
	SenderID      string   `xml:"sender"`
	SentDate      string   `xml:"sent"`
	MessageStatus string   `xml:"status"`
	MessageType   string   `xml:"msgType"`
	Source        string   `xml:"source,omitempty"`
	Scope         string   `xml:"scope"`
	Restriction   string   `xml:"restriction,omitempty"`
	Addresses     string   `xml:"addresses,omitempty"`
	HandlingCode  string   `xml:"code,omitempty"`
	Note          string   `xml:"note,omitempty"`
	ReferenceIDs  []string `xml:"references,omitempty"`
	IncidentIDs   []string `xml:"incidents,omitempty"`
	Infos         []Info   `xml:"info,omitempty"`
}

// Info describes an anticipated or actual event
type Info struct {
	XMLName          xml.Name     `xml:"info"`
	Language         string       `xml:"language,omitempty"`
	EventCategory    string       `xml:"category"`
	EventType        string       `xml:"event"`
	ResponseType     string       `xml:"responseType,omitempty"`
	Urgency          string       `xml:"urgency"`
	Severity         string       `xml:"severity"`
	Certainty        string       `xml:"certainty"`
	Audience         string       `xml:"audience,omitempty"`
	EventCode        []NamedValue `xml:"eventCode,omitempty"`
	EffectiveDate    string       `xml:"effective,omitempty"`
	ExpirationDate   string       `xml:"onset,omitempty"`
	OnsetDate        string       `xml:"expires,omitempty"`
	SenderName       string       `xml:"senderName,omitempty"`
	Headline         string       `xml:"headline,omitempty"`
	EventDescription string       `xml:"description,omitempty"`
	Instructions     string       `xml:"instruction,omitempty"`
	InformationURL   string       `xml:"web,omitempty"`
	ContactInfo      string       `xml:"contact,omitempty"`
	Parameter        []NamedValue `xml:"parameter,omitempty"`
	Areas            []Area       `xml:"area,omitempty"`
	Resources        []Resource   `xml:"resource,omitempty"`
}

// Resource provides an optional reference to additional information related to Info
type Resource struct {
	Description      string `xml:"resourceDesc"`
	MIMEType         string `xml:"mimeType,omitempty"`
	FileSize         string `xml:"size,omitempty"`
	URI              string `xml:"uri,omitempty"`
	DeereferencedURI string `xml:"derefUri,omitempty"`
	Digest           string `xml:"digest,omitempty"`
}

// Area describes a geographic area to which the Info segment applies
type Area struct {
	Description string `xml:"areaDesc"`
	Polygon     string `xml:"polygon,omitempty"`
	Circle      string `xml:"circle,omitempty"`
	Geocode     string `xml:"geocode,omitempty"`
	Altitude    string `xml:"altitude,omitempty"`
	Ceiling     string `xml:"ceiling,omitempty"`
}

// NamedValue contains a name and a value associated with that name
type NamedValue struct {
	ValueName string `xml:"valueName"`
	Value     string `xml:"value"`
}
<commit_msg>Added additional XMLName propertie for Area and Resource<commit_after>package cap

import "encoding/xml"

// Alert provides basic information about the current message: its purpose, its source and its status
type Alert struct {
	XMLName xml.Name `xml:"urn:oasis:names:tc:emergency:cap:1.1 alert"`

	MessageID     string   `xml:"identifier"`
	SenderID      string   `xml:"sender"`
	SentDate      string   `xml:"sent"`
	MessageStatus string   `xml:"status"`
	MessageType   string   `xml:"msgType"`
	Source        string   `xml:"source,omitempty"`
	Scope         string   `xml:"scope"`
	Restriction   string   `xml:"restriction,omitempty"`
	Addresses     string   `xml:"addresses,omitempty"`
	HandlingCode  string   `xml:"code,omitempty"`
	Note          string   `xml:"note,omitempty"`
	ReferenceIDs  []string `xml:"references,omitempty"`
	IncidentIDs   []string `xml:"incidents,omitempty"`
	Infos         []Info   `xml:"info,omitempty"`
}

// Info describes an anticipated or actual event
type Info struct {
	XMLName xml.Name `xml:"info"`

	Language         string       `xml:"language,omitempty"`
	EventCategory    string       `xml:"category"`
	EventType        string       `xml:"event"`
	ResponseType     string       `xml:"responseType,omitempty"`
	Urgency          string       `xml:"urgency"`
	Severity         string       `xml:"severity"`
	Certainty        string       `xml:"certainty"`
	Audience         string       `xml:"audience,omitempty"`
	EventCode        []NamedValue `xml:"eventCode,omitempty"`
	EffectiveDate    string       `xml:"effective,omitempty"`
	ExpirationDate   string       `xml:"onset,omitempty"`
	OnsetDate        string       `xml:"expires,omitempty"`
	SenderName       string       `xml:"senderName,omitempty"`
	Headline         string       `xml:"headline,omitempty"`
	EventDescription string       `xml:"description,omitempty"`
	Instructions     string       `xml:"instruction,omitempty"`
	InformationURL   string       `xml:"web,omitempty"`
	ContactInfo      string       `xml:"contact,omitempty"`
	Parameter        []NamedValue `xml:"parameter,omitempty"`
	Areas            []Area       `xml:"area,omitempty"`
	Resources        []Resource   `xml:"resource,omitempty"`
}

// Resource provides an optional reference to additional information related to Info
type Resource struct {
	XMLName xml.Name `xml:"resource"`

	Description      string `xml:"resourceDesc"`
	MIMEType         string `xml:"mimeType,omitempty"`
	FileSize         string `xml:"size,omitempty"`
	URI              string `xml:"uri,omitempty"`
	DeereferencedURI string `xml:"derefUri,omitempty"`
	Digest           string `xml:"digest,omitempty"`
}

// Area describes a geographic area to which the Info segment applies
type Area struct {
	XMLName xml.Name `xml:"area"`

	Description string `xml:"areaDesc"`
	Polygon     string `xml:"polygon,omitempty"`
	Circle      string `xml:"circle,omitempty"`
	Geocode     string `xml:"geocode,omitempty"`
	Altitude    string `xml:"altitude,omitempty"`
	Ceiling     string `xml:"ceiling,omitempty"`
}

// NamedValue contains a name and a value associated with that name
type NamedValue struct {
	ValueName string `xml:"valueName"`
	Value     string `xml:"value"`
}
<|endoftext|>


<commit_before>package srtp

/*
#cgo pkg-config: libsrtp2

#include "srtp.h"

*/
import "C"
import (
	"fmt"
	"unsafe"
)

func init() {
	C.srtp_init()
}

type Session struct {
	rawSession *_Ctype_srtp_t
}

func New(ClientWriteKey, ServerWriteKey []byte, profile string) *Session {
	rawClientWriteKey := C.CBytes(ClientWriteKey)
	rawServerWriteKey := C.CBytes(ServerWriteKey)
	rawProfile := C.CString(profile)
	defer func() {
		C.free(unsafe.Pointer(rawClientWriteKey))
		C.free(unsafe.Pointer(rawServerWriteKey))
		C.free(unsafe.Pointer(rawProfile))
	}()

	if sess := C.srtp_create_session(rawClientWriteKey, rawServerWriteKey, rawProfile); sess != nil {
		return &Session{
			rawSession: sess,
		}
	}

	return nil
}

func (s *Session) DecryptPacket(encryted []byte) (ok bool, unencryted []byte) {
	rawIn := C.CBytes(encryted)
	defer C.free(unsafe.Pointer(rawIn))

	if rawPacket := C.srtp_decrypt_packet(s.rawSession, rawIn, C.int(len(encryted))); rawPacket != nil {
		fmt.Println(rawPacket)
		return true, C.GoBytes(rawPacket.data, rawPacket.len)
	}

	return ok, unencryted
}
<commit_msg>Don't print packet after decrypt<commit_after>package srtp

/*
#cgo pkg-config: libsrtp2

#include "srtp.h"

*/
import "C"
import (
	"unsafe"
)

func init() {
	C.srtp_init()
}

type Session struct {
	rawSession *_Ctype_srtp_t
}

func New(ClientWriteKey, ServerWriteKey []byte, profile string) *Session {
	rawClientWriteKey := C.CBytes(ClientWriteKey)
	rawServerWriteKey := C.CBytes(ServerWriteKey)
	rawProfile := C.CString(profile)
	defer func() {
		C.free(unsafe.Pointer(rawClientWriteKey))
		C.free(unsafe.Pointer(rawServerWriteKey))
		C.free(unsafe.Pointer(rawProfile))
	}()

	if sess := C.srtp_create_session(rawClientWriteKey, rawServerWriteKey, rawProfile); sess != nil {
		return &Session{
			rawSession: sess,
		}
	}

	return nil
}

func (s *Session) DecryptPacket(encryted []byte) (ok bool, unencryted []byte) {
	rawIn := C.CBytes(encryted)
	defer C.free(unsafe.Pointer(rawIn))

	if rawPacket := C.srtp_decrypt_packet(s.rawSession, rawIn, C.int(len(encryted))); rawPacket != nil {
		return true, C.GoBytes(rawPacket.data, rawPacket.len)
	}

	return ok, unencryted
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"github.com/ChimeraCoder/anaconda"    // Working at 2002271f2160a4d243f0308af0827893e2868157
	"github.com/darkhelmet/twitterstream" // Working at 4051c41877496d38d54647c35897e768fd34385f
	"log"
	"net/url"
	"os"
	"os/exec"
)

func CheckForCGIDir() {
	f, e := os.Stat("./cgi")
	if e == nil {
		if !f.IsDir() {
			log.Println(`So you have made a cgi file. not a directory.\n
			 What. Removing your sillyness and doing it the right way`)
			e = os.Remove("./cgi")
			if e != nil {
				log.Fatal("Cannot remove (silly) the cgi file. What have you done!? (Permission probs)")
			}
			e := os.Mkdir("./cgi", 600)
			if e != nil {
				log.Fatalf("Cannot create the cgi dir. I kinda need to stop now. Reason %s", e.Error())
			}
		}
	} else {
		e := os.Mkdir("./cgi", 600)
		if e != nil {
			log.Fatalf("Cannot create the cgi dir. I kinda need to stop now. Reason %s", e.Error())
		}
	}
}

func LaunchReply(tweet *twitterstream.Tweet, api *anaconda.TwitterApi) {
	cmd := exec.Command("./cgi/reply")
	cmd.Env = []string{
		fmt.Sprintf("tweet_text=%s", tweet.Text),
		fmt.Sprintf("tweet_id=%d", tweet.Id),
		fmt.Sprintf("tweet_src=%s", tweet.User.ScreenName),
	}
	var out bytes.Buffer
	cmd.Stdout = &out
	err := cmd.Run()
	if err != nil {
		log.Printf("Error launching CGI to serve tweet: Error: %s", err)
	} else {
		v := url.Values{} // I dont even know

		api.PostTweet(fmt.Sprintf("@%s %s", tweet.User.ScreenName, out.String()), v)
		fmt.Printf("in all caps: %q\n", out.String())
	}
}
<commit_msg>reply to tweets.<commit_after>package main

import (
	"bytes"
	"fmt"
	"github.com/ChimeraCoder/anaconda"    // Working at 2002271f2160a4d243f0308af0827893e2868157
	"github.com/darkhelmet/twitterstream" // Working at 4051c41877496d38d54647c35897e768fd34385f
	"log"
	"net/url"
	"os"
	"os/exec"
)

func CheckForCGIDir() {
	f, e := os.Stat("./cgi")
	if e == nil {
		if !f.IsDir() {
			log.Println(`So you have made a cgi file. not a directory.\n
			 What. Removing your sillyness and doing it the right way`)
			e = os.Remove("./cgi")
			if e != nil {
				log.Fatal("Cannot remove (silly) the cgi file. What have you done!? (Permission probs)")
			}
			e := os.Mkdir("./cgi", 600)
			if e != nil {
				log.Fatalf("Cannot create the cgi dir. I kinda need to stop now. Reason %s", e.Error())
			}
		}
	} else {
		e := os.Mkdir("./cgi", 600)
		if e != nil {
			log.Fatalf("Cannot create the cgi dir. I kinda need to stop now. Reason %s", e.Error())
		}
	}
}

func LaunchReply(tweet *twitterstream.Tweet, api *anaconda.TwitterApi) {
	cmd := exec.Command("./cgi/reply")
	cmd.Env = []string{
		fmt.Sprintf("tweet_text=%s", tweet.Text),
		fmt.Sprintf("tweet_id=%d", tweet.Id),
		fmt.Sprintf("tweet_src=%s", tweet.User.ScreenName),
	}
	var out bytes.Buffer
	cmd.Stdout = &out
	err := cmd.Run()
	if err != nil {
		log.Printf("Error launching CGI to serve tweet: Error: %s", err)
	} else {
		v := url.Values{} // I dont even know
		v.Add("in_reply_to_status_id", fmt.Sprintf("%d", t.Id))
		api.PostTweet(fmt.Sprintf("@%s %s", tweet.User.ScreenName, out.String()), v)
		log.Printf("Tweet came in, Replied with %s", fmt.Sprintf("@%s %s", tweet.User.ScreenName, out.String()))
	}
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"os"
	"path"

	"github.com/codegangsta/cli"
)

// Logger is used to log errors; if nil, the default log.Logger is used.
var Logger *log.Logger

// logger is an helper function to retrieve the available logger
func logger() *log.Logger {
	if Logger == nil {
		Logger = log.New(os.Stderr, "", log.LstdFlags)
	}
	return Logger
}

func main() {

	app := cli.NewApp()
	app.Name = "lego"
	app.Usage = "Let's encrypt client to go!"
	app.Version = "0.0.2"

	cwd, err := os.Getwd()
	if err != nil {
		logger().Fatal("Could not determine current working directory. Please pass --path.")
	}
	defaultPath := path.Join(cwd, ".lego")

	app.Commands = []cli.Command{
		{
			Name:   "run",
			Usage:  "Register an account, then create and install a certificate",
			Action: run,
		},
		{
			Name:   "revoke",
			Usage:  "Revoke a certificate",
			Action: revoke,
		},
		{
			Name:   "renew",
			Usage:  "Renew a certificate",
			Action: renew,
		},
	}

	app.Flags = []cli.Flag{
		cli.StringSliceFlag{
			Name:  "domains, d",
			Usage: "Add domains to the process",
		},
		cli.StringFlag{
			Name:  "server, s",
			Value: "https://acme-v01.api.letsencrypt.org/directory",
			Usage: "CA hostname (and optionally :port). The server certificate must be trusted in order to avoid further modifications to the client.",
		},
		cli.StringFlag{
			Name:  "email, m",
			Usage: "Email used for registration and recovery contact.",
		},
		cli.IntFlag{
			Name:  "rsa-key-size, B",
			Value: 2048,
			Usage: "Size of the RSA key.",
		},
		cli.StringFlag{
			Name:  "path",
			Usage: "Directory to use for storing the data",
			Value: defaultPath,
		},
		cli.StringFlag{
			Name:  "port",
			Usage: "Challenges will use this port to listen on. Please make sure to forward port 443 to this port on your machine. Otherwise use setcap on the binary",
		},
	}

	app.Run(os.Args)
}
<commit_msg>Push CLI version<commit_after>package main

import (
	"log"
	"os"
	"path"

	"github.com/codegangsta/cli"
)

// Logger is used to log errors; if nil, the default log.Logger is used.
var Logger *log.Logger

// logger is an helper function to retrieve the available logger
func logger() *log.Logger {
	if Logger == nil {
		Logger = log.New(os.Stderr, "", log.LstdFlags)
	}
	return Logger
}

func main() {

	app := cli.NewApp()
	app.Name = "lego"
	app.Usage = "Let's encrypt client to go!"
	app.Version = "0.1.0"

	cwd, err := os.Getwd()
	if err != nil {
		logger().Fatal("Could not determine current working directory. Please pass --path.")
	}
	defaultPath := path.Join(cwd, ".lego")

	app.Commands = []cli.Command{
		{
			Name:   "run",
			Usage:  "Register an account, then create and install a certificate",
			Action: run,
		},
		{
			Name:   "revoke",
			Usage:  "Revoke a certificate",
			Action: revoke,
		},
		{
			Name:   "renew",
			Usage:  "Renew a certificate",
			Action: renew,
		},
	}

	app.Flags = []cli.Flag{
		cli.StringSliceFlag{
			Name:  "domains, d",
			Usage: "Add domains to the process",
		},
		cli.StringFlag{
			Name:  "server, s",
			Value: "https://acme-v01.api.letsencrypt.org/directory",
			Usage: "CA hostname (and optionally :port). The server certificate must be trusted in order to avoid further modifications to the client.",
		},
		cli.StringFlag{
			Name:  "email, m",
			Usage: "Email used for registration and recovery contact.",
		},
		cli.IntFlag{
			Name:  "rsa-key-size, B",
			Value: 2048,
			Usage: "Size of the RSA key.",
		},
		cli.StringFlag{
			Name:  "path",
			Usage: "Directory to use for storing the data",
			Value: defaultPath,
		},
		cli.StringFlag{
			Name:  "port",
			Usage: "Challenges will use this port to listen on. Please make sure to forward port 443 to this port on your machine. Otherwise use setcap on the binary",
		},
	}

	app.Run(os.Args)
}
<|endoftext|>


<commit_before>package gfmrun

import (
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"sort"
	"strings"

	"github.com/Sirupsen/logrus"
	"gopkg.in/urfave/cli.v2"
)

func NewCLI() *cli.App {
	return &cli.App{
		Name:    "gfmrun",
		Usage:   "github-flavored markdown example runner",
		Version: VersionString,
		Authors: []*cli.Author{
			{
				Name:  "Dan Buch",
				Email: "dan@meatballhat.com",
			},
		},
		Flags: []cli.Flag{
			&cli.StringSliceFlag{
				Name:    "sources",
				Aliases: []string{"s"},
				Usage:   "markdown source(s) to search for runnable examples",
				Value:   cli.NewStringSlice("README.md"),
				EnvVars: []string{"GFMRUN_SOURCES", "SOURCES"},
			},
			&cli.IntFlag{
				Name:    "count",
				Aliases: []string{"c"},
				Usage:   "expected count of runnable examples (for verification)",
				EnvVars: []string{"GFMRUN_COUNT", "COUNT"},
			},
			&cli.StringFlag{
				Name:    "languages",
				Aliases: []string{"L"},
				Usage:   "location of languages.yml file from linguist",
				Value:   DefaultLanguagesYml,
				EnvVars: []string{"GFMRUN_LANGUAGES", "LANGUAGES"},
			},
			&cli.BoolFlag{
				Name:    "no-auto-pull",
				Aliases: []string{"N"},
				Value:   true,
				Usage:   "disable automatic pull of languages.yml when missing",
				EnvVars: []string{"GFMRUN_NO_AUTO_PULL", "NO_AUTO_PULL"},
			},
			&cli.BoolFlag{
				Name:    "debug",
				Aliases: []string{"D"},
				Usage:   "show debug output",
				EnvVars: []string{"GFMRUN_DEBUG", "DEBUG"},
			},
		},
		Commands: []*cli.Command{
			{
				Name:  "pull-languages",
				Usage: "explicitly download the latest languages.yml from the linguist source to $GFMRUN_LANGUAGES (automatic unless \"--no-auto-pull\")",
				Flags: []cli.Flag{
					&cli.StringFlag{
						Name:    "languages-url",
						Aliases: []string{"u"},
						Usage:   "source URL of languages.yml file from linguist",
						Value:   DefaultLanguagesYmlURL,
						EnvVars: []string{"GFMRUN_LANGUAGES_URL", "LANGUAGES_URL"},
					},
				},
				Action: cliPullLanguages,
			},
			{
				Name:   "dump-languages",
				Usage:  "dump the parsed languages data structure as JSON",
				Hidden: true,
				Action: cliDumpLanguages,
			},
			{
				Name:   "list-frobs",
				Usage:  "list the known frobs and handled frob aliases",
				Hidden: true,
				Action: cliListFrobs,
			},
			{
				Name:  "extract",
				Usage: "extract examples to files",
				Flags: []cli.Flag{
					&cli.StringFlag{
						Name:    "output-dir",
						Aliases: []string{"o"},
						Usage:   "output directory for extracted examples",
						Value:   os.TempDir(),
						EnvVars: []string{"GFMRUN_OUTPUT_DIR", "OUTPUT_DIR"},
					},
				},
				Action: cliExtract,
			},
		},
		Action: cliRunExamples,
	}
}

func RunExamples(sources []string, expectedCount int, languagesFile string, autoPull bool, log *logrus.Logger) error {
	if sources == nil {
		sources = []string{}
	}

	if log == nil {
		log = logrus.New()
	}

	runner, err := NewRunner(sources, expectedCount, languagesFile, autoPull, log)
	if err != nil {
		return err
	}

	errs := runner.Run()

	if len(errs) > 0 {
		msg := make([]string, len(errs))
		for i, err := range errs {
			msg[i] = err.Error()
		}
		return errors.New(strings.Join(msg, "\n"))
	}

	return nil
}

func ExtractExamples(sources []string, outDir, languagesFile string, autoPull bool, log *logrus.Logger) error {
	if sources == nil {
		sources = []string{}
	}

	if log == nil {
		log = logrus.New()
	}

	outDirFd, err := os.Stat(outDir)
	if err == nil && !outDirFd.IsDir() {
		return fmt.Errorf("output path %q must be a directory or nonexistent", outDir)
	}

	if err != nil {
		err = os.MkdirAll(outDir, os.FileMode(0750))
	}

	if err != nil {
		return err
	}

	runner, err := NewRunner(sources, 0, languagesFile, autoPull, log)
	if err != nil {
		return err
	}

	runner.noExec = true
	runner.extractDir = outDir
	errs := runner.Run()

	if len(errs) > 0 {
		msg := make([]string, len(errs))
		for i, err := range errs {
			msg[i] = err.Error()
		}
		return errors.New(strings.Join(msg, "\n"))
	}

	return nil
}

func cliRunExamples(ctx *cli.Context) error {
	log := logrus.New()
	if ctx.Bool("debug") {
		log.Level = logrus.DebugLevel
	}

	err := RunExamples(ctx.StringSlice("sources"), ctx.Int("count"),
		ctx.String("languages"), ctx.Bool("no-auto-pull"), log)

	if err != nil {
		log.Error(err)
		return cli.Exit("", 2)
	}

	return nil
}

func cliListFrobs(ctx *cli.Context) error {
	langs, err := LoadLanguages(ctx.String("languages"))
	if err != nil {
		return err
	}

	known := map[string]bool{}

	for name, _ := range DefaultFrobs {
		for _, alias := range langs.Lookup(name).Aliases {
			known[alias] = true
		}
	}

	knownSlice := []string{}
	for lang := range known {
		knownSlice = append(knownSlice, lang)
	}

	sort.Strings(knownSlice)

	for _, lang := range knownSlice {
		fmt.Printf("%s\n", lang)
	}

	return nil
}

func cliDumpLanguages(ctx *cli.Context) error {
	log := logrus.New()
	if ctx.Bool("debug") {
		log.Level = logrus.DebugLevel
	}

	langs, err := LoadLanguages(ctx.String("languages"))
	if err != nil {
		log.Error(err)
		return cli.Exit("failed to load languages", 4)
	}

	jsonBytes, err := json.MarshalIndent(langs.Map, "", "  ")
	if err != nil {
		log.Error(err)
		return cli.Exit("failed to marshal to json", 4)
	}

	fmt.Printf(string(jsonBytes) + "\n")
	return nil
}

func cliPullLanguages(ctx *cli.Context) error {
	return PullLanguagesYml(ctx.String("languages-url"), ctx.String("languages"))
}

func cliExtract(ctx *cli.Context) error {
	log := logrus.New()
	if ctx.Bool("debug") {
		log.Level = logrus.DebugLevel
	}

	err := ExtractExamples(ctx.StringSlice("sources"), ctx.String("output-dir"),
		ctx.String("languages"), ctx.Bool("no-auto-pull"), log)

	if err != nil {
		log.Error(err)
		return cli.Exit("", 2)
	}

	return nil
}
<commit_msg>Properly catch and display error on pull command<commit_after>package gfmrun

import (
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"sort"
	"strings"

	"github.com/Sirupsen/logrus"
	"gopkg.in/urfave/cli.v2"
)

func NewCLI() *cli.App {
	return &cli.App{
		Name:    "gfmrun",
		Usage:   "github-flavored markdown example runner",
		Version: VersionString,
		Authors: []*cli.Author{
			{
				Name:  "Dan Buch",
				Email: "dan@meatballhat.com",
			},
		},
		Flags: []cli.Flag{
			&cli.StringSliceFlag{
				Name:    "sources",
				Aliases: []string{"s"},
				Usage:   "markdown source(s) to search for runnable examples",
				Value:   cli.NewStringSlice("README.md"),
				EnvVars: []string{"GFMRUN_SOURCES", "SOURCES"},
			},
			&cli.IntFlag{
				Name:    "count",
				Aliases: []string{"c"},
				Usage:   "expected count of runnable examples (for verification)",
				EnvVars: []string{"GFMRUN_COUNT", "COUNT"},
			},
			&cli.StringFlag{
				Name:    "languages",
				Aliases: []string{"L"},
				Usage:   "location of languages.yml file from linguist",
				Value:   DefaultLanguagesYml,
				EnvVars: []string{"GFMRUN_LANGUAGES", "LANGUAGES"},
			},
			&cli.BoolFlag{
				Name:    "no-auto-pull",
				Aliases: []string{"N"},
				Value:   true,
				Usage:   "disable automatic pull of languages.yml when missing",
				EnvVars: []string{"GFMRUN_NO_AUTO_PULL", "NO_AUTO_PULL"},
			},
			&cli.BoolFlag{
				Name:    "debug",
				Aliases: []string{"D"},
				Usage:   "show debug output",
				EnvVars: []string{"GFMRUN_DEBUG", "DEBUG"},
			},
		},
		Commands: []*cli.Command{
			{
				Name:  "pull-languages",
				Usage: "explicitly download the latest languages.yml from the linguist source to $GFMRUN_LANGUAGES (automatic unless \"--no-auto-pull\")",
				Flags: []cli.Flag{
					&cli.StringFlag{
						Name:    "languages-url",
						Aliases: []string{"u"},
						Usage:   "source URL of languages.yml file from linguist",
						Value:   DefaultLanguagesYmlURL,
						EnvVars: []string{"GFMRUN_LANGUAGES_URL", "LANGUAGES_URL"},
					},
				},
				Action: cliPullLanguages,
			},
			{
				Name:   "dump-languages",
				Usage:  "dump the parsed languages data structure as JSON",
				Hidden: true,
				Action: cliDumpLanguages,
			},
			{
				Name:   "list-frobs",
				Usage:  "list the known frobs and handled frob aliases",
				Hidden: true,
				Action: cliListFrobs,
			},
			{
				Name:  "extract",
				Usage: "extract examples to files",
				Flags: []cli.Flag{
					&cli.StringFlag{
						Name:    "output-dir",
						Aliases: []string{"o"},
						Usage:   "output directory for extracted examples",
						Value:   os.TempDir(),
						EnvVars: []string{"GFMRUN_OUTPUT_DIR", "OUTPUT_DIR"},
					},
				},
				Action: cliExtract,
			},
		},
		Action: cliRunExamples,
	}
}

func RunExamples(sources []string, expectedCount int, languagesFile string, autoPull bool, log *logrus.Logger) error {
	if sources == nil {
		sources = []string{}
	}

	if log == nil {
		log = logrus.New()
	}

	runner, err := NewRunner(sources, expectedCount, languagesFile, autoPull, log)
	if err != nil {
		return err
	}

	errs := runner.Run()

	if len(errs) > 0 {
		msg := make([]string, len(errs))
		for i, err := range errs {
			msg[i] = err.Error()
		}
		return errors.New(strings.Join(msg, "\n"))
	}

	return nil
}

func ExtractExamples(sources []string, outDir, languagesFile string, autoPull bool, log *logrus.Logger) error {
	if sources == nil {
		sources = []string{}
	}

	if log == nil {
		log = logrus.New()
	}

	outDirFd, err := os.Stat(outDir)
	if err == nil && !outDirFd.IsDir() {
		return fmt.Errorf("output path %q must be a directory or nonexistent", outDir)
	}

	if err != nil {
		err = os.MkdirAll(outDir, os.FileMode(0750))
	}

	if err != nil {
		return err
	}

	runner, err := NewRunner(sources, 0, languagesFile, autoPull, log)
	if err != nil {
		return err
	}

	runner.noExec = true
	runner.extractDir = outDir
	errs := runner.Run()

	if len(errs) > 0 {
		msg := make([]string, len(errs))
		for i, err := range errs {
			msg[i] = err.Error()
		}
		return errors.New(strings.Join(msg, "\n"))
	}

	return nil
}

func cliRunExamples(ctx *cli.Context) error {
	log := logrus.New()
	if ctx.Bool("debug") {
		log.Level = logrus.DebugLevel
	}

	err := RunExamples(ctx.StringSlice("sources"), ctx.Int("count"),
		ctx.String("languages"), ctx.Bool("no-auto-pull"), log)

	if err != nil {
		log.Error(err)
		return cli.Exit("", 2)
	}

	return nil
}

func cliListFrobs(ctx *cli.Context) error {
	langs, err := LoadLanguages(ctx.String("languages"))
	if err != nil {
		return err
	}

	known := map[string]bool{}

	for name, _ := range DefaultFrobs {
		for _, alias := range langs.Lookup(name).Aliases {
			known[alias] = true
		}
	}

	knownSlice := []string{}
	for lang := range known {
		knownSlice = append(knownSlice, lang)
	}

	sort.Strings(knownSlice)

	for _, lang := range knownSlice {
		fmt.Printf("%s\n", lang)
	}

	return nil
}

func cliDumpLanguages(ctx *cli.Context) error {
	log := logrus.New()
	if ctx.Bool("debug") {
		log.Level = logrus.DebugLevel
	}

	langs, err := LoadLanguages(ctx.String("languages"))
	if err != nil {
		log.Error(err)
		return cli.Exit("failed to load languages", 4)
	}

	jsonBytes, err := json.MarshalIndent(langs.Map, "", "  ")
	if err != nil {
		log.Error(err)
		return cli.Exit("failed to marshal to json", 4)
	}

	fmt.Printf(string(jsonBytes) + "\n")
	return nil
}

func cliPullLanguages(ctx *cli.Context) error {
	log := logrus.New()
	err := PullLanguagesYml(ctx.String("languages-url"), ctx.String("languages"))
	if err != nil {
		log.Error(err)
		return cli.Exit("", 2)
	}
	return nil
}

func cliExtract(ctx *cli.Context) error {
	log := logrus.New()
	if ctx.Bool("debug") {
		log.Level = logrus.DebugLevel
	}

	err := ExtractExamples(ctx.StringSlice("sources"), ctx.String("output-dir"),
		ctx.String("languages"), ctx.Bool("no-auto-pull"), log)

	if err != nil {
		log.Error(err)
		return cli.Exit("", 2)
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"regexp"
	"sync"
	"time"

	"code.google.com/p/go.net/html"
)

var (
	requestsMade = 0
	urlsFound    = 0
	urls         = NewURLMap()
)

var regexpURL = regexp.MustCompile(`(?i)\b((?:[a-z][\w-]+:(?:/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s!()\[\]{};:'".,<>?«»“”‘’]))`)

type result struct {
	url           *url.URL
	referer       *url.URL
	err           error
	statusCode    int
	duration      time.Duration
	contentLength int64
}

type request struct {
	*http.Request
	referer *url.URL
}

type Sucker struct {
	ConcurrencyLevel int
	jobs             chan *request
	results          chan *result
}

func (s *Sucker) Run(firstURL string) {
	s.results = make(chan *result, 99999999)
	s.run(firstURL)
}

func (s *Sucker) run(firstURL string) {
	var wg sync.WaitGroup
	wg.Add(s.ConcurrencyLevel)

	s.jobs = make(chan *request)

	for i := 0; i < s.ConcurrencyLevel; i++ {
		go func() {
			s.worker(s.jobs)
			wg.Done()
		}()
	}

	req, err := http.NewRequest("GET", firstURL, nil)
	if err != nil {
		panic(err)
	}

	s.jobs <- &request{req, nil}

	wg.Wait()
}

func (s *Sucker) worker(ch chan *request) {
	client := &http.Client{}
	for req := range ch {
		requestsMade++
		fmt.Printf("COUNT: %d, LINKS: %d, REQ RECEIVED: %s\n", requestsMade, urlsFound, req.URL.String())
		start := time.Now()
		resp, err := client.Do(req.Request)
		if err != nil {
			fmt.Println(err)
		}
		code := 0
		size := int64(-1)
		if resp != nil {
			if resp.ContentLength > 0 {
				size = resp.ContentLength
			}

			body, err := ioutil.ReadAll(resp.Body)
			if err != nil {
				fmt.Println(err)
			}

			reader := bytes.NewReader(body)

			go func(body io.Reader, base *url.URL) {
				links := links(body)
				for _, link := range links {
					link = base.ResolveReference(link)

					if !urls.Exists(link.String()) {
						urlsFound++
						urls.Add(link.String())
						nextReq, err := http.NewRequest("GET", link.String(), nil)
						if err != nil {
							fmt.Println(err)
						}

						s.jobs <- &request{nextReq, req.URL}
					}
				}
			}(reader, req.URL)

			resp.Body.Close()
		}

		s.results <- &result{
			statusCode:    code,
			duration:      time.Now().Sub(start),
			err:           err,
			contentLength: size,
		}
	}
}

func links(reader io.Reader) []*url.URL {
	links := []*url.URL{}
	z := html.NewTokenizer(reader)
	for {
		tt := z.Next()
		if tt == html.ErrorToken {
			return links
		}

		for _, attr := range z.Token().Attr {
			if attr.Key == "href" {
				url, err := url.Parse(attr.Val)
				if err == nil {
					links = append(links, url)
				}
			}
		}
	}
}

func main() {
	firstURL := flag.String("first", "http://wikipedia.org", "first url to scrape")
	flag.Parse()

	sucker := Sucker{
		ConcurrencyLevel: 64,
	}

	sucker.Run(*firstURL)
}
<commit_msg>Shove results into Neo4j without any relationships.<commit_after>package main

import (
	"bytes"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"regexp"
	"sync"
	"time"

	"github.com/jmcvetta/neoism"

	"code.google.com/p/go.net/html"
)

var (
	db           *neoism.Database
	requestsMade = 0
	urlsFound    = 0
	urls         = NewURLMap()
)

var regexpURL = regexp.MustCompile(`(?i)\b((?:[a-z][\w-]+:(?:/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s!()\[\]{};:'".,<>?«»“”‘’]))`)

type result struct {
	url           *url.URL
	referer       *url.URL
	err           error
	statusCode    int
	duration      time.Duration
	contentLength int64
}

type request struct {
	*http.Request
	referer *url.URL
}

type Sucker struct {
	ConcurrencyLevel int
	jobs             chan *request
	results          chan *result
}

func (s *Sucker) Run(firstURL string) {
	s.results = make(chan *result)
	s.run(firstURL)
}

func (s *Sucker) run(firstURL string) {
	var wg sync.WaitGroup
	wg.Add(s.ConcurrencyLevel)

	s.jobs = make(chan *request)

	for i := 0; i < s.ConcurrencyLevel; i++ {
		go func() {
			s.worker(s.jobs)
			wg.Done()
		}()
	}

	for i := 0; i < 4; i++ {
		go func() {
			for res := range s.results {

				referer, e := "", ""

				if res.referer != nil {
					referer = res.referer.String()
				}

				if res.err != nil {
					e = res.err.Error()
				}

				node, err := db.CreateNode(neoism.Props{
					"url":           res.url.String(),
					"referer":       referer,
					"err":           e,
					"statusCode":    res.statusCode,
					"duration":      res.duration,
					"contentLength": res.contentLength,
				})
				if err != nil {
					log.Println(err)
				}
				node.AddLabel("Website")

				fmt.Printf("COUNT: %d, LINKS: %d, REQ RECEIVED: %s\n", requestsMade, urlsFound, res.url.String())
			}
		}()
	}

	req, err := http.NewRequest("GET", firstURL, nil)
	if err != nil {
		panic(err)
	}

	s.jobs <- &request{req, nil}

	wg.Wait()
}

func (s *Sucker) worker(ch chan *request) {
	client := &http.Client{}
	for req := range ch {
		requestsMade++
		start := time.Now()
		resp, err := client.Do(req.Request)
		if err != nil {
			fmt.Println(err)
		}
		code := 0
		size := int64(-1)
		if resp != nil {
			if resp.ContentLength > 0 {
				size = resp.ContentLength
			}

			body, err := ioutil.ReadAll(resp.Body)
			if err != nil {
				fmt.Println(err)
			}

			reader := bytes.NewReader(body)

			go func(body io.Reader, base *url.URL) {
				links := links(body)
				for _, link := range links {
					link = base.ResolveReference(link)

					if !urls.Exists(link.String()) {
						urlsFound++
						urls.Add(link.String())
						nextReq, err := http.NewRequest("GET", link.String(), nil)
						if err != nil {
							fmt.Println(err)
						}

						s.jobs <- &request{nextReq, req.URL}
					}
				}
			}(reader, req.URL)

			resp.Body.Close()
		}

		s.results <- &result{
			url:           req.URL,
			referer:       req.referer,
			statusCode:    code,
			duration:      time.Now().Sub(start),
			err:           err,
			contentLength: size,
		}
	}
}

func links(reader io.Reader) []*url.URL {
	links := []*url.URL{}
	z := html.NewTokenizer(reader)
	for {
		tt := z.Next()
		if tt == html.ErrorToken {
			return links
		}

		for _, attr := range z.Token().Attr {
			if attr.Key == "href" {
				url, err := url.Parse(attr.Val)
				if err == nil {
					links = append(links, url)
				}
			}
		}
	}
}

func main() {
	firstURL := flag.String("first", "http://wikipedia.org", "first url to scrape")
	dbURL := flag.String("db", "http://localhost:7474/db/data", "url to neo4j")
	flag.Parse()

	var err error
	db, err = neoism.Connect(*dbURL)
	if err != nil {
		log.Fatal(err)
	}

	sucker := Sucker{
		ConcurrencyLevel: 64,
	}

	sucker.Run(*firstURL)
}
<|endoftext|>


<commit_before>package main

import (
    "encoding/json"
	"fmt"
	"io/ioutil"
//    "log"
	"net/http"
    "os"
//	"path/filepath"
	"strconv"
)

func main() {
	// Define flags
	action := os.Args[1:][0]

    var manifest struct {
        Workloads []struct {
            Name              string
            Title             string
            Components        []struct {
            	Label         string
				File          string
				Checksum      string
            }
        }
    }

	switch action {
	case "list":
		fmt.Printf("tinycore\n")

	case "get":

	case "put":

	case "parse":
		host := os.Args[1:][1]
		r := goget(host + "/manifest.json")
		dec := json.NewDecoder(r.Body)
		dec.Decode(&manifest)

		for _, w := range manifest.Workloads {
			fmt.Printf("%s (%s)", w.Title, w.Name)
			for _, c := range w.Components {
				fmt.Printf(" [%s]%s", c.Label, c.File)
			}
			fmt.Printf("\n")
		}

	case "discover":
		host := os.Args[1:][1]
		r := goget(host + "/.well-known/swap/root")
		body, _ := ioutil.ReadAll(r.Body)
		fmt.Printf(string(body))
		r.Body.Close()

	case "server":
		goserve(8080, "/tmp")
	case "version":
		host := os.Args[1:][1]
		r := goget(host + "/version")
		body, _ := ioutil.ReadAll(r.Body)
		fmt.Printf(string(body))
		r.Body.Close()
	}
}

func goget(url string) *http.Response {
	r, err := http.Get(url)
	if r.StatusCode != 200 {
		fmt.Printf("Server returned HTTP Status %v, expected 200\n", r.StatusCode)
        os.Exit(1)
	}
	if err != nil {
		// handle error
        fmt.Printf("%s\n", err)
        os.Exit(1)
	}
//	defer r.Body.Close()

	return r
//	body, err := ioutil.ReadAll(resp.Body)

//    if err != nil {
//        fmt.Printf("%s\n", err)
//        os.Exit(1)
//    }
//    return string(body)
}

func scanroot(root string) string {
/*	func visit(path string, f os.FileInfo, err error) error {
		// look for VMs
	}
	err := filepath.Walk(root, visit)
*/
	return(`{"version": 1, "workloads": [ {"name": "tinycore", "title": "Tiny Core Linux", "components": [ { "label": "root", "file": "tinycore.vmdk" } ] } ]}`
}

func goserve(port int, root string) {
	manifestjson := scanroot(root)

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "Hello, World!")})
	http.HandleFunc("/manifest.json", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, manifestjson)})
	http.HandleFunc("/version", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "swap-0.1")})

	if err:= http.ListenAndServe(":" + strconv.Itoa(port), nil); err != nil {
		fmt.Printf("ListenAndServe error: %v\n", err)
	}
}<commit_msg>resync<commit_after>package main

import (
    "encoding/json"
	"fmt"
	"io/ioutil"
//    "log"
	"net/http"
    "os"
//	"path/filepath"
	"strconv"
)

func main() {
	// Define flags
	action := os.Args[1:][0]

    var manifest struct {
        Workloads []struct {
            Name              string
            Title             string
            Components        []struct {
            	Label         string
				File          string
				Checksum      string
            }
        }
    }

	switch action {
	case "list":
		fmt.Printf("tinycore\n")

	case "get":

	case "put":

	case "parse":
		host := os.Args[1:][1]
		r := goget(host + "/manifest.json")
		dec := json.NewDecoder(r.Body)
		dec.Decode(&manifest)

		for _, w := range manifest.Workloads {
			fmt.Printf("%s (%s)", w.Title, w.Name)
			for _, c := range w.Components {
				fmt.Printf(" [%s]%s", c.Label, c.File)
			}
			fmt.Printf("\n")
		}

	case "discover":
		host := os.Args[1:][1]
		r := goget(host + "/.well-known/swap/root")
		body, _ := ioutil.ReadAll(r.Body)
		fmt.Printf(string(body))
		r.Body.Close()

	case "server":
		goserve(8080, "/tmp")
	case "version":
		host := os.Args[1:][1]
		r := goget(host + "/version")
		body, _ := ioutil.ReadAll(r.Body)
		fmt.Printf(string(body))
		r.Body.Close()
	}
}

func goget(url string) *http.Response {
	r, err := http.Get(url)
	if r.StatusCode != 200 {
		fmt.Printf("Server returned HTTP Status %v, expected 200\n", r.StatusCode)
        os.Exit(1)
	}
	if err != nil {
		// handle error
        fmt.Printf("%s\n", err)
        os.Exit(1)
	}
//	defer r.Body.Close()

	return r
//	body, err := ioutil.ReadAll(resp.Body)

//    if err != nil {
//        fmt.Printf("%s\n", err)
//        os.Exit(1)
//    }
//    return string(body)
}

func scanroot(root string) string {
/*	func visit(path string, f os.FileInfo, err error) error {
		// look for VMs
	}
	err := filepath.Walk(root, visit)
*/
	return(`{"version": 1, "workloads": [ {"name": "tinycore", "title": "Tiny Core Linux", "components": [ { "label": "root", "file": "tinycore.vmdk" } ] } ]}`)
}

func goserve(port int, root string) {
	manifestjson := scanroot(root)

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "Hello, World!")})
	http.HandleFunc("/manifest.json", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, manifestjson)})
	http.HandleFunc("/version", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "swap-0.1")})

	if err:= http.ListenAndServe(":" + strconv.Itoa(port), nil); err != nil {
		fmt.Printf("ListenAndServe error: %v\n", err)
	}
}<|endoftext|>


<commit_before>package taskq

import (
	"fmt"
	"time"
)

var unknownTaskOpt *TaskOptions

func init() {
	SetUnknownTaskOptions(&TaskOptions{})
}

func SetUnknownTaskOptions(opt *TaskOptions) {
	opt.init()
	unknownTaskOpt = opt
}

type TaskOptions struct {
	Name string

	// Function called to process a message.
	Handler interface{}
	// Function called to process failed message.
	FallbackHandler interface{}

	// Number of tries/releases after which the message fails permanently
	// and is deleted.
	// Default is 64 retries.
	RetryLimit int
	// Minimum backoff time between retries.
	// Default is 30 seconds.
	MinBackoff time.Duration
	// Maximum backoff time between retries.
	// Default is 30 minutes.
	MaxBackoff time.Duration

	inited bool
}

func (opt *TaskOptions) init() {
	if !opt.inited {
		opt.inited = true
		return
	}
	if opt.RetryLimit == 0 {
		opt.RetryLimit = 64
	}
	if opt.MinBackoff == 0 {
		opt.MinBackoff = 30 * time.Second
	}
	if opt.MaxBackoff == 0 {
		opt.MaxBackoff = 30 * time.Minute
	}
}

type Task struct {
	queue Queue
	opt   *TaskOptions

	handler         Handler
	fallbackHandler Handler
}

func NewTask(queue Queue, opt *TaskOptions) *Task {
	opt.init()

	t := &Task{
		queue: queue,
		opt:   opt,
	}

	t.handler = NewHandler(opt.Handler)
	if opt.FallbackHandler != nil {
		t.fallbackHandler = NewHandler(opt.FallbackHandler)
	}

	return t
}

func (t *Task) String() string {
	return fmt.Sprintf("Task<Name=%s>", t.opt.Name)
}

func (t *Task) Options() *TaskOptions {
	return t.opt
}

func (t *Task) HandleMessage(msg *Message) error {
	if msg.StickyErr != nil {
		if t.fallbackHandler != nil {
			return t.fallbackHandler.HandleMessage(msg)
		}
		return nil
	}
	return t.handler.HandleMessage(msg)
}

// AddMessage adds message to the queue.
func (t *Task) AddMessage(msg *Message) error {
	msg.TaskName = t.opt.Name
	msg.Task = t
	return t.queue.Add(msg)
}

// Call creates a message using the args and adds it to the queue.
func (t *Task) Call(args ...interface{}) error {
	msg := NewMessage(args...)
	return t.AddMessage(msg)
}

// CallOnce works like Call, but it returns ErrDuplicate if message
// with such args was already added in a period.
func (t *Task) CallOnce(period time.Duration, args ...interface{}) error {
	msg := NewMessage(args...)
	msg.SetDelayName(period, args...)
	return t.AddMessage(msg)
}
<commit_msg>Fix task options init<commit_after>package taskq

import (
	"fmt"
	"time"
)

var unknownTaskOpt *TaskOptions

func init() {
	SetUnknownTaskOptions(&TaskOptions{})
}

func SetUnknownTaskOptions(opt *TaskOptions) {
	opt.init()
	unknownTaskOpt = opt
}

type TaskOptions struct {
	Name string

	// Function called to process a message.
	Handler interface{}
	// Function called to process failed message.
	FallbackHandler interface{}

	// Number of tries/releases after which the message fails permanently
	// and is deleted.
	// Default is 64 retries.
	RetryLimit int
	// Minimum backoff time between retries.
	// Default is 30 seconds.
	MinBackoff time.Duration
	// Maximum backoff time between retries.
	// Default is 30 minutes.
	MaxBackoff time.Duration

	inited bool
}

func (opt *TaskOptions) init() {
	if opt.inited {
		return
	}
	opt.inited = true

	if opt.RetryLimit == 0 {
		opt.RetryLimit = 64
	}
	if opt.MinBackoff == 0 {
		opt.MinBackoff = 30 * time.Second
	}
	if opt.MaxBackoff == 0 {
		opt.MaxBackoff = 30 * time.Minute
	}
}

type Task struct {
	queue Queue
	opt   *TaskOptions

	handler         Handler
	fallbackHandler Handler
}

func NewTask(queue Queue, opt *TaskOptions) *Task {
	opt.init()

	t := &Task{
		queue: queue,
		opt:   opt,
	}

	t.handler = NewHandler(opt.Handler)
	if opt.FallbackHandler != nil {
		t.fallbackHandler = NewHandler(opt.FallbackHandler)
	}

	return t
}

func (t *Task) String() string {
	return fmt.Sprintf("Task<Name=%s>", t.opt.Name)
}

func (t *Task) Options() *TaskOptions {
	return t.opt
}

func (t *Task) HandleMessage(msg *Message) error {
	if msg.StickyErr != nil {
		if t.fallbackHandler != nil {
			return t.fallbackHandler.HandleMessage(msg)
		}
		return nil
	}
	return t.handler.HandleMessage(msg)
}

// AddMessage adds message to the queue.
func (t *Task) AddMessage(msg *Message) error {
	msg.TaskName = t.opt.Name
	msg.Task = t
	return t.queue.Add(msg)
}

// Call creates a message using the args and adds it to the queue.
func (t *Task) Call(args ...interface{}) error {
	msg := NewMessage(args...)
	return t.AddMessage(msg)
}

// CallOnce works like Call, but it returns ErrDuplicate if message
// with such args was already added in a period.
func (t *Task) CallOnce(period time.Duration, args ...interface{}) error {
	msg := NewMessage(args...)
	msg.SetDelayName(period, args...)
	return t.AddMessage(msg)
}
<|endoftext|>


<commit_before>package main

import (
	"html/template"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"os/signal"
	"runtime"
	"syscall"
	"time"

	_ "net/http/pprof"

	jwt "github.com/dgrijalva/jwt-go"
	"github.com/gomodule/redigo/redis"
	"github.com/gorilla/mux"
	"github.com/sirupsen/logrus"
	"github.com/syhlion/greq"
	"github.com/syhlion/httplog"
	redisocket "github.com/syhlion/redisocket.v2"
	"github.com/syhlion/requestwork.v2"
	"github.com/urfave/cli"
	"github.com/urfave/negroni"
)

// master server
func master(c *cli.Context) {

	mc := getMasterConfig(c)

	b, err := ioutil.ReadFile(mc.PublicKeyLocation)
	if err != nil {
		logger.Warn(err)
	}
	public_pem, rsaKeyErr := jwt.ParseRSAPublicKeyFromPEM(b)
	if rsaKeyErr != nil {
		logger.Warnf("Did not start \"%sdecode\" api", mc.ApiPrefix)
	}

	/*redis init*/
	rpool := redis.NewPool(func() (redis.Conn, error) {
		c, err := redis.Dial("tcp", mc.RedisAddr)
		if err != nil {
			return nil, err
		}
		_, err = c.Do("SELECT", mc.RedisDb)
		if err != nil {
			c.Close()
			return nil, err
		}
		return c, nil
	}, 10)
	rpool.MaxIdle = mc.RedisMaxIdle
	rpool.MaxActive = mc.RedisMaxConn

	/*Test redis connect*/
	err = RedisTestConn(rpool.Get())
	if err != nil {
		logger.Fatal(err)
	}
	rsender := redisocket.NewSender(rpool)

	/*api start*/
	apiListener, err := net.Listen("tcp", mc.ApiListen)
	if err != nil {
		logger.Fatal(err)
	}
	r := mux.NewRouter()

	sub := r.PathPrefix(mc.ApiPrefix).Subrouter()
	sub.HandleFunc("/push/socket/{app_key}/{socket_id}", PushToSocket(rsender)).Methods("POST")
	sub.HandleFunc("/push/user/{app_key}/{user_id}", PushToUser(rsender)).Methods("POST")
	sub.HandleFunc("/push/{app_key}/{channel}/{event}", PushMessage(rsender)).Methods("POST")
	sub.HandleFunc("/push_batch/{app_key}", PushBatchMessage(rsender)).Methods("POST")
	sub.HandleFunc("/push/{app_key}", PushMessageByPattern(rsender)).Methods("POST")
	sub.HandleFunc("/{app_key}/channels", GetAllChannel(rsender)).Methods("GET")
	sub.HandleFunc("/{app_key}/channels/count", GetAllChannelCount(rsender)).Methods("GET")
	sub.HandleFunc("/{app_key}/online/bychannel/{channel}", GetOnlineByChannel(rsender)).Methods("GET")
	sub.HandleFunc("/{app_key}/online/bychannel/{channel}/count", GetOnlineCountByChannel(rsender)).Methods("GET")
	sub.HandleFunc("/{app_key}/online", GetOnline(rsender)).Methods("GET")
	sub.HandleFunc("/{app_key}/online/count", GetOnlineCount(rsender)).Methods("GET")
	sub.HandleFunc("/ping", Ping()).Methods("GET")
	if rsaKeyErr == nil {
		sub.HandleFunc("/decode", DecodeJWT(public_pem)).Methods("POST")
	}
	n := negroni.New()
	n.Use(httplog.NewLogger())
	n.UseHandler(r)
	serverError := make(chan error, 1)
	server := http.Server{
		ReadTimeout: 3 * time.Second,
		Handler:     n,
	}
	go func() {
		err := server.Serve(apiListener)
		serverError <- err
	}()
	go func() {
		logger.Error(http.ListenAndServe(":7799", nil))
	}()

	// block and listen syscall
	shutdow_observer := make(chan os.Signal, 1)
	t := template.Must(template.New("gusher master start msg").Parse(masterMsgFormat))
	t.Execute(os.Stdout, mc)
	signal.Notify(shutdow_observer, syscall.SIGINT, syscall.SIGTERM, syscall.SIGKILL)
	select {
	case <-shutdow_observer:
		logger.Info("Receive signal")
	case err := <-serverError:
		logger.Warn(err)
	}

}
func runtimeStats() (m *runtime.MemStats) {
	m = &runtime.MemStats{}

	//log.Println("# goroutines: ", runtime.NumGoroutine())
	runtime.ReadMemStats(m)
	//log.Println("Memory Acquired: ", m.Sys)
	//log.Println("Memory Used    : ", m.Alloc)
	return m
}

//slave server
func slave(c *cli.Context) {

	sc := getSlaveConfig(c)
	/*redis init*/
	rpool := redis.NewPool(func() (redis.Conn, error) {
		c, err := redis.Dial("tcp", sc.RedisAddr)
		if err != nil {
			return nil, err
		}
		_, err = c.Do("SELECT", sc.RedisDb)
		if err != nil {
			c.Close()
			return nil, err
		}
		return c, nil
	}, 10)

	rpool.MaxIdle = sc.RedisMaxIdle
	rpool.MaxActive = sc.RedisMaxConn
	rpool.Wait = true
	rpool.IdleTimeout = 240 * time.Second
	rpool.TestOnBorrow = func(c redis.Conn, t time.Time) error {
		if time.Since(t) < time.Minute {
			return nil
		}
		_, err := c.Do("PING")
		return err
	}

	jobRpool := redis.NewPool(func() (redis.Conn, error) {
		c, err := redis.Dial("tcp", sc.RedisJobAddr)
		if err != nil {
			return nil, err
		}
		_, err = c.Do("SELECT", sc.RedisJobDb)
		if err != nil {
			c.Close()
			return nil, err
		}
		return c, nil
	}, 10)

	jobRpool.MaxIdle = sc.RedisJobMaxIdle
	jobRpool.MaxActive = sc.RedisJobMaxConn
	jobRpool.Wait = true
	jobRpool.IdleTimeout = 240 * time.Second
	jobRpool.TestOnBorrow = func(c redis.Conn, t time.Time) error {
		if time.Since(t) < time.Minute {
			return nil
		}
		_, err := c.Do("PING")
		return err
	}

	/*Test redis connect*/
	err := RedisTestConn(rpool.Get())
	if err != nil {
		logger.Fatal(err)
	}
	err = RedisTestConn(jobRpool.Get())
	if err != nil {
		logger.Fatal(err)
	}

	rsHub := redisocket.NewHub(rpool, c.Bool("debug"))
	rsHub.Config.MaxMessageSize = int64(sc.MaxMessage)
	rsHub.Config.ScanInterval = sc.ScanInterval
	rsHub.Config.Upgrader.ReadBufferSize = sc.ReadBuffer
	rsHub.Config.Upgrader.WriteBufferSize = sc.WriteBuffer
	rsHubErr := make(chan error, 1)
	go func() {
		rsHubErr <- rsHub.Listen(listenChannelPrefix)
	}()

	/*request worker*/
	worker := requestwork.New(50)
	client := greq.New(worker, 15*time.Second, c.Bool("debug"))
	/*api start*/
	apiListener, err := net.Listen("tcp", sc.ApiListen)
	if err != nil {
		logger.Fatal(err)
	}
	r := mux.NewRouter()

	/*api end*/

	//server := http.NewServeMux()

	sub := r.PathPrefix(sc.ApiPrefix).Subrouter()
	sub.HandleFunc("/ws/{app_key}", WsConnect(sc, rpool, jobRpool, rsHub, client)).Methods("GET")
	sub.HandleFunc("/wtf/{app_key}", WtfConnect(sc, rpool, jobRpool, rsHub, client)).Methods("GET")
	sub.HandleFunc("/auth", WsAuth(sc, rpool, client)).Methods("POST")
	sub.HandleFunc("/ping", Ping()).Methods("GET")
	n := negroni.New()
	n.Use(httplog.NewLogger())
	n.UseHandler(r)
	serverError := make(chan error, 1)
	server := http.Server{
		ReadTimeout: 3 * time.Second,
		Handler:     n,
	}
	go func() {
		err := server.Serve(apiListener)
		serverError <- err
	}()
	go func() {
		logger.Error(http.ListenAndServe(":8899", nil))
	}()

	closeConnTotal := make(chan int, 0)
	//固定30秒log出 現在連線人數
	go func() {
		t := time.NewTicker(30 * time.Second)
		defer func() {
			t.Stop()
		}()

		for {
			select {
			case <-t.C:
				m := runtimeStats()
				logger.WithFields(logrus.Fields{
					"memory-acquired": m.Sys,
					"memory-used":     m.Alloc,
					"goroutines":      runtime.NumGoroutine(),
				}).Infof("users now: %v", rsHub.CountOnlineUsers())
			case <-closeConnTotal:
				return
			}
		}

	}()
	defer func() {
		closeConnTotal <- 1
		apiListener.Close()
		rsHub.Close()
		rpool.Close()
	}()

	// block and listen syscall
	shutdow_observer := make(chan os.Signal, 1)
	t := template.Must(template.New("gusher slave start msg").Parse(slaveMsgFormat))
	t.Execute(os.Stdout, sc)
	signal.Notify(shutdow_observer, syscall.SIGINT, syscall.SIGTERM, syscall.SIGKILL)
	select {
	case <-shutdow_observer:
		logger.Info("receive signal")
	case err := <-serverError:
		logger.Error(err)
	case err := <-rsHubErr:
		logger.Error("redis sub connection diconnect ", err)
	}
	return

}
<commit_msg>fix master read timeout second<commit_after>package main

import (
	"html/template"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"os/signal"
	"runtime"
	"syscall"
	"time"

	_ "net/http/pprof"

	jwt "github.com/dgrijalva/jwt-go"
	"github.com/gomodule/redigo/redis"
	"github.com/gorilla/mux"
	"github.com/sirupsen/logrus"
	"github.com/syhlion/greq"
	"github.com/syhlion/httplog"
	redisocket "github.com/syhlion/redisocket.v2"
	"github.com/syhlion/requestwork.v2"
	"github.com/urfave/cli"
	"github.com/urfave/negroni"
)

// master server
func master(c *cli.Context) {

	mc := getMasterConfig(c)

	b, err := ioutil.ReadFile(mc.PublicKeyLocation)
	if err != nil {
		logger.Warn(err)
	}
	public_pem, rsaKeyErr := jwt.ParseRSAPublicKeyFromPEM(b)
	if rsaKeyErr != nil {
		logger.Warnf("Did not start \"%sdecode\" api", mc.ApiPrefix)
	}

	/*redis init*/
	rpool := redis.NewPool(func() (redis.Conn, error) {
		c, err := redis.Dial("tcp", mc.RedisAddr)
		if err != nil {
			return nil, err
		}
		_, err = c.Do("SELECT", mc.RedisDb)
		if err != nil {
			c.Close()
			return nil, err
		}
		return c, nil
	}, 10)
	rpool.MaxIdle = mc.RedisMaxIdle
	rpool.MaxActive = mc.RedisMaxConn

	/*Test redis connect*/
	err = RedisTestConn(rpool.Get())
	if err != nil {
		logger.Fatal(err)
	}
	rsender := redisocket.NewSender(rpool)

	/*api start*/
	apiListener, err := net.Listen("tcp", mc.ApiListen)
	if err != nil {
		logger.Fatal(err)
	}
	r := mux.NewRouter()

	sub := r.PathPrefix(mc.ApiPrefix).Subrouter()
	sub.HandleFunc("/push/socket/{app_key}/{socket_id}", PushToSocket(rsender)).Methods("POST")
	sub.HandleFunc("/push/user/{app_key}/{user_id}", PushToUser(rsender)).Methods("POST")
	sub.HandleFunc("/push/{app_key}/{channel}/{event}", PushMessage(rsender)).Methods("POST")
	sub.HandleFunc("/push_batch/{app_key}", PushBatchMessage(rsender)).Methods("POST")
	sub.HandleFunc("/push/{app_key}", PushMessageByPattern(rsender)).Methods("POST")
	sub.HandleFunc("/{app_key}/channels", GetAllChannel(rsender)).Methods("GET")
	sub.HandleFunc("/{app_key}/channels/count", GetAllChannelCount(rsender)).Methods("GET")
	sub.HandleFunc("/{app_key}/online/bychannel/{channel}", GetOnlineByChannel(rsender)).Methods("GET")
	sub.HandleFunc("/{app_key}/online/bychannel/{channel}/count", GetOnlineCountByChannel(rsender)).Methods("GET")
	sub.HandleFunc("/{app_key}/online", GetOnline(rsender)).Methods("GET")
	sub.HandleFunc("/{app_key}/online/count", GetOnlineCount(rsender)).Methods("GET")
	sub.HandleFunc("/ping", Ping()).Methods("GET")
	if rsaKeyErr == nil {
		sub.HandleFunc("/decode", DecodeJWT(public_pem)).Methods("POST")
	}
	n := negroni.New()
	n.Use(httplog.NewLogger())
	n.UseHandler(r)
	serverError := make(chan error, 1)
	server := http.Server{
		ReadTimeout:  30 * time.Second,
		WriteTimeout: 60 * time.Second,
		Handler:      n,
	}
	go func() {
		err := server.Serve(apiListener)
		serverError <- err
	}()
	go func() {
		logger.Error(http.ListenAndServe(":7799", nil))
	}()

	// block and listen syscall
	shutdow_observer := make(chan os.Signal, 1)
	t := template.Must(template.New("gusher master start msg").Parse(masterMsgFormat))
	t.Execute(os.Stdout, mc)
	signal.Notify(shutdow_observer, syscall.SIGINT, syscall.SIGTERM, syscall.SIGKILL)
	select {
	case <-shutdow_observer:
		logger.Info("Receive signal")
	case err := <-serverError:
		logger.Warn(err)
	}

}
func runtimeStats() (m *runtime.MemStats) {
	m = &runtime.MemStats{}

	//log.Println("# goroutines: ", runtime.NumGoroutine())
	runtime.ReadMemStats(m)
	//log.Println("Memory Acquired: ", m.Sys)
	//log.Println("Memory Used    : ", m.Alloc)
	return m
}

//slave server
func slave(c *cli.Context) {

	sc := getSlaveConfig(c)
	/*redis init*/
	rpool := redis.NewPool(func() (redis.Conn, error) {
		c, err := redis.Dial("tcp", sc.RedisAddr)
		if err != nil {
			return nil, err
		}
		_, err = c.Do("SELECT", sc.RedisDb)
		if err != nil {
			c.Close()
			return nil, err
		}
		return c, nil
	}, 10)

	rpool.MaxIdle = sc.RedisMaxIdle
	rpool.MaxActive = sc.RedisMaxConn
	rpool.Wait = true
	rpool.IdleTimeout = 240 * time.Second
	rpool.TestOnBorrow = func(c redis.Conn, t time.Time) error {
		if time.Since(t) < time.Minute {
			return nil
		}
		_, err := c.Do("PING")
		return err
	}

	jobRpool := redis.NewPool(func() (redis.Conn, error) {
		c, err := redis.Dial("tcp", sc.RedisJobAddr)
		if err != nil {
			return nil, err
		}
		_, err = c.Do("SELECT", sc.RedisJobDb)
		if err != nil {
			c.Close()
			return nil, err
		}
		return c, nil
	}, 10)

	jobRpool.MaxIdle = sc.RedisJobMaxIdle
	jobRpool.MaxActive = sc.RedisJobMaxConn
	jobRpool.Wait = true
	jobRpool.IdleTimeout = 240 * time.Second
	jobRpool.TestOnBorrow = func(c redis.Conn, t time.Time) error {
		if time.Since(t) < time.Minute {
			return nil
		}
		_, err := c.Do("PING")
		return err
	}

	/*Test redis connect*/
	err := RedisTestConn(rpool.Get())
	if err != nil {
		logger.Fatal(err)
	}
	err = RedisTestConn(jobRpool.Get())
	if err != nil {
		logger.Fatal(err)
	}

	rsHub := redisocket.NewHub(rpool, c.Bool("debug"))
	rsHub.Config.MaxMessageSize = int64(sc.MaxMessage)
	rsHub.Config.ScanInterval = sc.ScanInterval
	rsHub.Config.Upgrader.ReadBufferSize = sc.ReadBuffer
	rsHub.Config.Upgrader.WriteBufferSize = sc.WriteBuffer
	rsHubErr := make(chan error, 1)
	go func() {
		rsHubErr <- rsHub.Listen(listenChannelPrefix)
	}()

	/*request worker*/
	worker := requestwork.New(50)
	client := greq.New(worker, 15*time.Second, c.Bool("debug"))
	/*api start*/
	apiListener, err := net.Listen("tcp", sc.ApiListen)
	if err != nil {
		logger.Fatal(err)
	}
	r := mux.NewRouter()

	/*api end*/

	//server := http.NewServeMux()

	sub := r.PathPrefix(sc.ApiPrefix).Subrouter()
	sub.HandleFunc("/ws/{app_key}", WsConnect(sc, rpool, jobRpool, rsHub, client)).Methods("GET")
	sub.HandleFunc("/wtf/{app_key}", WtfConnect(sc, rpool, jobRpool, rsHub, client)).Methods("GET")
	sub.HandleFunc("/auth", WsAuth(sc, rpool, client)).Methods("POST")
	sub.HandleFunc("/ping", Ping()).Methods("GET")
	n := negroni.New()
	n.Use(httplog.NewLogger())
	n.UseHandler(r)
	serverError := make(chan error, 1)
	server := http.Server{
		ReadTimeout: 3 * time.Second,
		Handler:     n,
	}
	go func() {
		err := server.Serve(apiListener)
		serverError <- err
	}()
	go func() {
		logger.Error(http.ListenAndServe(":8899", nil))
	}()

	closeConnTotal := make(chan int, 0)
	//固定30秒log出 現在連線人數
	go func() {
		t := time.NewTicker(30 * time.Second)
		defer func() {
			t.Stop()
		}()

		for {
			select {
			case <-t.C:
				m := runtimeStats()
				logger.WithFields(logrus.Fields{
					"memory-acquired": m.Sys,
					"memory-used":     m.Alloc,
					"goroutines":      runtime.NumGoroutine(),
				}).Infof("users now: %v", rsHub.CountOnlineUsers())
			case <-closeConnTotal:
				return
			}
		}

	}()
	defer func() {
		closeConnTotal <- 1
		apiListener.Close()
		rsHub.Close()
		rpool.Close()
	}()

	// block and listen syscall
	shutdow_observer := make(chan os.Signal, 1)
	t := template.Must(template.New("gusher slave start msg").Parse(slaveMsgFormat))
	t.Execute(os.Stdout, sc)
	signal.Notify(shutdow_observer, syscall.SIGINT, syscall.SIGTERM, syscall.SIGKILL)
	select {
	case <-shutdow_observer:
		logger.Info("receive signal")
	case err := <-serverError:
		logger.Error(err)
	case err := <-rsHubErr:
		logger.Error("redis sub connection diconnect ", err)
	}
	return

}
<|endoftext|>


<commit_before>package slack

import (
	"errors"
	"net/url"
	"strconv"
)

const (
	DEFAULT_LOGINS_COUNT   = 100
	DEFAULT_LOGINS_PAGE    = 1
)

type TeamResponse struct {
	Team TeamInfo `json:"team"`
	SlackResponse
}

type TeamInfo struct {
	ID          string                 `json:"id"`
	Name        string                 `json:"name"`
	Domain      string                 `json:"domain"`
	EmailDomain string                 `json:"email_domain"`
	Icon        map[string]interface{} `json:"icon"`
}

type LoginResponse struct {
	Logins []Login `json:"logins"`
	Paging         `json:"paging"`
	SlackResponse
}

type Login struct {
	UserID    string `json:"user_id"`
	Username  string `json:"username"`
	DateFirst int    `json:"date_first"`
	DateLast  int    `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"`
}

// AccessLogParameters contains all the parameters necessary (including the optional ones) for a GetAccessLogs() request
type AccessLogParameters struct {
	Count         int
	Page          int
}

// NewHistoryParameters provides an instance of HistoryParameters with all the sane default values set
func NewAccessLogParameters() AccessLogParameters {
	return AccessLogParameters{
		Count: DEFAULT_LOGINS_COUNT,
		Page:  DEFAULT_LOGINS_PAGE,
	}
}

func teamRequest(path string, values url.Values, debug bool) (*TeamResponse, error) {
	response := &TeamResponse{}
	err := post(path, values, response, debug)
	if err != nil {
		return nil, err
	}

	if !response.Ok {
		return nil, errors.New(response.Error)
	}

	return response, nil
}

func accessLogsRequest(path string, values url.Values, debug bool) (*LoginResponse, error) {
	response := &LoginResponse{}
	err := post(path, values, response, debug)
	if err != nil {
		return nil, err
	}
	if !response.Ok {
		return nil, errors.New(response.Error)
	}
	return response, nil
}

// GetTeamInfo gets the Team Information of the user
func (api *Client) GetTeamInfo() (*TeamInfo, error) {
	values := url.Values{
		"token": {api.config.token},
	}

	response, err := teamRequest("team.info", values, api.debug)
	if err != nil {
		return nil, err
	}
	return &response.Team, nil
}

// GetAccessLogs retrieves a page of logins according to the parameters given
func (api *Client) GetAccessLogs(params AccessLogParameters) ([]Login, *Paging, error) {
	values := url.Values{
		"token": {api.config.token},
	}
	if params.Count != DEFAULT_LOGINS_COUNT {
		values.Add("count", strconv.Itoa(params.Count))
	}
	if params.Page != DEFAULT_LOGINS_PAGE {
		values.Add("page", strconv.Itoa(params.Page))
	}
	response, err := accessLogsRequest("team.accessLogs", values, api.debug)
	if err != nil {
		return nil, nil, err
	}
	return response.Logins, &response.Paging, nil
}

<commit_msg>Fixed a bad comment<commit_after>package slack

import (
	"errors"
	"net/url"
	"strconv"
)

const (
	DEFAULT_LOGINS_COUNT   = 100
	DEFAULT_LOGINS_PAGE    = 1
)

type TeamResponse struct {
	Team TeamInfo `json:"team"`
	SlackResponse
}

type TeamInfo struct {
	ID          string                 `json:"id"`
	Name        string                 `json:"name"`
	Domain      string                 `json:"domain"`
	EmailDomain string                 `json:"email_domain"`
	Icon        map[string]interface{} `json:"icon"`
}

type LoginResponse struct {
	Logins []Login `json:"logins"`
	Paging         `json:"paging"`
	SlackResponse
}

type Login struct {
	UserID    string `json:"user_id"`
	Username  string `json:"username"`
	DateFirst int    `json:"date_first"`
	DateLast  int    `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"`
}

// AccessLogParameters contains all the parameters necessary (including the optional ones) for a GetAccessLogs() request
type AccessLogParameters struct {
	Count         int
	Page          int
}

// NewAccessLogParameters provides an instance of AccessLogParameters with all the sane default values set
func NewAccessLogParameters() AccessLogParameters {
	return AccessLogParameters{
		Count: DEFAULT_LOGINS_COUNT,
		Page:  DEFAULT_LOGINS_PAGE,
	}
}

func teamRequest(path string, values url.Values, debug bool) (*TeamResponse, error) {
	response := &TeamResponse{}
	err := post(path, values, response, debug)
	if err != nil {
		return nil, err
	}

	if !response.Ok {
		return nil, errors.New(response.Error)
	}

	return response, nil
}

func accessLogsRequest(path string, values url.Values, debug bool) (*LoginResponse, error) {
	response := &LoginResponse{}
	err := post(path, values, response, debug)
	if err != nil {
		return nil, err
	}
	if !response.Ok {
		return nil, errors.New(response.Error)
	}
	return response, nil
}

// GetTeamInfo gets the Team Information of the user
func (api *Client) GetTeamInfo() (*TeamInfo, error) {
	values := url.Values{
		"token": {api.config.token},
	}

	response, err := teamRequest("team.info", values, api.debug)
	if err != nil {
		return nil, err
	}
	return &response.Team, nil
}

// GetAccessLogs retrieves a page of logins according to the parameters given
func (api *Client) GetAccessLogs(params AccessLogParameters) ([]Login, *Paging, error) {
	values := url.Values{
		"token": {api.config.token},
	}
	if params.Count != DEFAULT_LOGINS_COUNT {
		values.Add("count", strconv.Itoa(params.Count))
	}
	if params.Page != DEFAULT_LOGINS_PAGE {
		values.Add("page", strconv.Itoa(params.Page))
	}
	response, err := accessLogsRequest("team.accessLogs", values, api.debug)
	if err != nil {
		return nil, nil, err
	}
	return response.Logins, &response.Paging, nil
}

<|endoftext|>


<commit_before>package chip8

import "errors"

// ErrUnkownOpcode is returned when we try to execute an unkown opcode.
var ErrUnkownOpcode = errors.New("chip8: unknown opcode")

// CPU represents a Chip8 CPU.
type CPU struct {
	// CHIP-8 was most commonly implemented on 4K systems, such as the
	// Cosmac VIP and the Telemac 1800. These machines had 4096 (0x1000)
	// memory locations, all of which are 8 bits (a byte) which is where the
	// term CHIP-8 originated. However, the CHIP-8 interpreter itself
	// occupies the first 512 bytes of the memory space on these machines.
	// For this reason, most programs written for the original system begin
	// at memory location 512 (0x200) and do not access any of the memory
	// below the location 512 (0x200). The uppermost 256 bytes (0xF00-0xFFF)
	// are reserved for display refresh, and the 96 bytes below that
	// (0xEA0-0XEFF) were reserved for call stack, internal use, and other
	// variables.
	Memory [4096]byte

	// The address register, which is named I, is 16 bits wide and is used
	// with several opcodes that involve memory operations.
	I uint16

	// Program counter.
	PC uint16

	// CHIP-8 has 16 8-bit data registers named from V0 to VF. The VF
	// register doubles as a carry flag.
	V [16]byte

	// The stack is only used to store return addresses when subroutines are
	// called. The original 1802 version allocated 48 bytes for up to 12
	// levels of nesting; modern implementations normally have at least 16
	// levels.
	Stack [16]byte

	// Stack pointer.
	SP uint16
}

// NewCPU returns a new CPU instance.
func NewCPU() *CPU {
	return &CPU{
		PC: 200,
	}
}

// Step runs a single CPU cycle.
func (c *CPU) Step() error {
	// Dispatch the opcode.
	if err := c.Dispatch(c.op()); err != nil {
		return err
	}

	// Increment the program counter by 2.
	c.PC = c.PC + 2

	return nil
}

// Dispatch executes the given opcode.
func (c *CPU) Dispatch(op uint16) error {
	switch op & 0xF000 {
	//   0x0NNN
	case 0x0000:
		switch op {
		// Clears the screen.
		case 0x00E0:

		// Returns from a subroutine.
		case 0x00EE:

		// Calls RCA 1802 program at address NNNN
		default:
		}

	// Jumps to address NNN
	//   0x1NNN
	case 0x1000:

	// Calls subroutine at NNN.
	//   0x2NNN
	case 0x2000:

	// Skip the next instruction if VX equals NN.
	//   0x3XNN
	case 0x3000:

	// Skips the next instruction if VX doesn't equal NN.
	//   0x4XNN
	case 0x4000:

	// Skips the next instruction if VX equals VY.
	//   0x5XY0
	case 0x5000:

	// Sets VX to NN.
	//   0x6XNN
	case 0x6000:

	// Adds NN to VX
	//   0x7XNN
	case 0x7000:

	case 0x8000:

		switch op & 0x000F {
		// Sets VX to the value of VY.
		case 0x00:

		// Sets VX to VX or VY.
		case 0x01:

		// Sets VX to VX and VY.
		case 0x02:

		// Sets VX to VX xor VY.
		case 0x03:

		// Adds VY to VX. VF is set to 1 when there's a carry, and to 0 when there isn't.
		case 0x04:

		// VY is subtracted from VX. VF is set to 0 when there's a borrow, and 1 when there isn't.
		case 0x05:
		// Shifts VX right by one. VF is set to the value of the least significant bit of VX before the shift.
		case 0x06:
		// Sets VX to VY minus VX. VF is set to 0 when there's a borrow, and 1 when there isn't.
		case 0x07:
		// Shifts VX left by one. VF is set to the value of the most significant bit of VX before the shift.
		case 0x0E:
		}

	// Skips the next instruction if VX doesn't equal VY.
	//   0x9XY0
	case 0x9000:

	// Sets I to the address NNN.
	//   0xANNN
	case 0xA000:
		c.I = op & 0x0FFF
		break

	// Jumps to the address NNN plus V0.
	//   0xBNNN
	case 0xB000:

	// Sets VX to a random number and NN.
	//   0xCXNN
	case 0xC000:

	// Draws a sprite at coordinate (VX, VY) that has a width of 8 pixels and a
	// height of N pixels. Each row of 8 pixels is read as bit-coded (with the
	// most significant bit of each byte displayed on the left) starting from
	// memory location I; I value doesn't change after the execution of this
	// instruction. As described above, VF is set to 1 if any screen pixels are
	// flipped from set to unset when the sprite is drawn, and to 0 if that doesn't
	// happen.
	//
	//   0xDXYN
	case 0xD000:

	case 0xE000:
		switch op & 0x00FF {
		// Skips the next instruction if the key stored in VX is pressed.
		case 0x9E:

		// Skips the next instruction if the key stored in VX isn't pressed.
		case 0xA1:
		}
	case 0xF000:
		switch op & 0x00FF {
		// Sets VX to the value of the delay timer.
		case 0x07:

		// A key press is awaited, and then stored in VX.
		case 0x0A:

		// Sets the delay timer to VX.
		case 0x15:

		// Sets the sound timer to VX.
		case 0x18:

		// Adds VX to I.
		case 0x1E:

		// Sets I to the location of the sprite for the character in VX. Characters
		// 0-F (in hexadecimal) are represented by a 4x5 font.
		case 0x29:

		// Stores the Binary-coded decimal representation of VX, with the most
		// significant of three digits at the address in I, the middle digit at
		// I plus 1, and the least significant digit at I plus 2. (In other words,
		// take the decimal representation of VX, place the hundreds digit in
		// memory at location in I, the tens digit at location I+1, and the ones
		// digit at location I+2.)
		case 0x33:

		// Stores V0 to VX in memory starting at address I.
		case 0x55:

		// Fills V0 to VX with values from memory starting at address I.
		case 0x65:
		}
	default:
		return ErrUnkownOpcode
	}

	return nil
}

// op returns the next op code.
func (c *CPU) op() uint16 {
	return uint16(c.Memory[c.PC])<<8 | uint16(c.Memory[c.PC+1])
}
<commit_msg>Cleanup.<commit_after>package chip8

import "errors"

// ErrUnkownOpcode is returned when we try to execute an unkown opcode.
var ErrUnkownOpcode = errors.New("chip8: unknown opcode")

// CPU represents a CHIP-8 CPU.
//
// CHIP-8 was most commonly implemented on 4K systems, such as the
// Cosmac VIP and the Telemac 1800. These machines had 4096 (0x1000)
// memory locations, all of which are 8 bits (a byte) which is where the
// term CHIP-8 originated. However, the CHIP-8 interpreter itself
// occupies the first 512 bytes of the memory space on these machines.
// For this reason, most programs written for the original system begin
// at memory location 512 (0x200) and do not access any of the memory
// below the location 512 (0x200). The uppermost 256 bytes (0xF00-0xFFF)
// are reserved for display refresh, and the 96 bytes below that
// (0xEA0-0XEFF) were reserved for call stack, internal use, and other
// variables.
type CPU struct {
	// The 4096 bytes of memory.
	Memory [4096]byte

	// The address register, which is named I, is 16 bits wide and is used
	// with several opcodes that involve memory operations.
	I uint16

	// Program counter.
	PC uint16

	// CHIP-8 has 16 8-bit data registers named from V0 to VF. The VF
	// register doubles as a carry flag.
	V [16]byte

	// The stack is only used to store return addresses when subroutines are
	// called. The original 1802 version allocated 48 bytes for up to 12
	// levels of nesting; modern implementations normally have at least 16
	// levels.
	Stack [16]byte

	// Stack pointer.
	SP uint16
}

// NewCPU returns a new CPU instance.
func NewCPU() *CPU {
	return &CPU{
		PC: 200,
	}
}

// Step runs a single CPU cycle.
func (c *CPU) Step() error {
	// Dispatch the opcode.
	if err := c.Dispatch(c.op()); err != nil {
		return err
	}

	// Increment the program counter by 2.
	c.PC = c.PC + 2

	return nil
}

// Dispatch executes the given opcode.
func (c *CPU) Dispatch(op uint16) error {
	switch op & 0xF000 {
	//   0x0NNN
	case 0x0000:
		switch op {
		// Clears the screen.
		case 0x00E0:

		// Returns from a subroutine.
		case 0x00EE:

		// Calls RCA 1802 program at address NNNN
		default:
		}

	// Jumps to address NNN
	//   0x1NNN
	case 0x1000:

	// Calls subroutine at NNN.
	//   0x2NNN
	case 0x2000:

	// Skip the next instruction if VX equals NN.
	//   0x3XNN
	case 0x3000:

	// Skips the next instruction if VX doesn't equal NN.
	//   0x4XNN
	case 0x4000:

	// Skips the next instruction if VX equals VY.
	//   0x5XY0
	case 0x5000:

	// Sets VX to NN.
	//   0x6XNN
	case 0x6000:

	// Adds NN to VX
	//   0x7XNN
	case 0x7000:

	case 0x8000:

		switch op & 0x000F {
		// Sets VX to the value of VY.
		case 0x00:

		// Sets VX to VX or VY.
		case 0x01:

		// Sets VX to VX and VY.
		case 0x02:

		// Sets VX to VX xor VY.
		case 0x03:

		// Adds VY to VX. VF is set to 1 when there's a carry, and to 0 when there isn't.
		case 0x04:

		// VY is subtracted from VX. VF is set to 0 when there's a borrow, and 1 when there isn't.
		case 0x05:
		// Shifts VX right by one. VF is set to the value of the least significant bit of VX before the shift.
		case 0x06:
		// Sets VX to VY minus VX. VF is set to 0 when there's a borrow, and 1 when there isn't.
		case 0x07:
		// Shifts VX left by one. VF is set to the value of the most significant bit of VX before the shift.
		case 0x0E:
		}

	// Skips the next instruction if VX doesn't equal VY.
	//   0x9XY0
	case 0x9000:

	// Sets I to the address NNN.
	//   0xANNN
	case 0xA000:
		c.I = op & 0x0FFF
		break

	// Jumps to the address NNN plus V0.
	//   0xBNNN
	case 0xB000:

	// Sets VX to a random number and NN.
	//   0xCXNN
	case 0xC000:

	// Draws a sprite at coordinate (VX, VY) that has a width of 8 pixels and a
	// height of N pixels. Each row of 8 pixels is read as bit-coded (with the
	// most significant bit of each byte displayed on the left) starting from
	// memory location I; I value doesn't change after the execution of this
	// instruction. As described above, VF is set to 1 if any screen pixels are
	// flipped from set to unset when the sprite is drawn, and to 0 if that doesn't
	// happen.
	//
	//   0xDXYN
	case 0xD000:

	case 0xE000:
		switch op & 0x00FF {
		// Skips the next instruction if the key stored in VX is pressed.
		case 0x9E:

		// Skips the next instruction if the key stored in VX isn't pressed.
		case 0xA1:
		}
	case 0xF000:
		switch op & 0x00FF {
		// Sets VX to the value of the delay timer.
		case 0x07:

		// A key press is awaited, and then stored in VX.
		case 0x0A:

		// Sets the delay timer to VX.
		case 0x15:

		// Sets the sound timer to VX.
		case 0x18:

		// Adds VX to I.
		case 0x1E:

		// Sets I to the location of the sprite for the character in VX. Characters
		// 0-F (in hexadecimal) are represented by a 4x5 font.
		case 0x29:

		// Stores the Binary-coded decimal representation of VX, with the most
		// significant of three digits at the address in I, the middle digit at
		// I plus 1, and the least significant digit at I plus 2. (In other words,
		// take the decimal representation of VX, place the hundreds digit in
		// memory at location in I, the tens digit at location I+1, and the ones
		// digit at location I+2.)
		case 0x33:

		// Stores V0 to VX in memory starting at address I.
		case 0x55:

		// Fills V0 to VX with values from memory starting at address I.
		case 0x65:
		}
	default:
		return ErrUnkownOpcode
	}

	return nil
}

// op returns the next op code.
func (c *CPU) op() uint16 {
	return uint16(c.Memory[c.PC])<<8 | uint16(c.Memory[c.PC+1])
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"mime/multipart"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"

	"image"

	_ "image/gif"
	_ "image/jpeg"
	_ "image/png"

	"github.com/fogleman/gg"
	"github.com/pborman/getopt"
	"golang.org/x/image/font/inconsolata"

	"discord-auto-upload/web"
)

const currentVersion = "0.7"

var lastCheck = time.Now()
var newLastCheck = time.Now()

// Config for the application
type Config struct {
	webhookURL  string
	path        string
	watch       int
	username    string
	noWatermark bool
	exclude     string
}

func main() {

	config := parseOptions()
	checkPath(config.path)
	wconfig := web.Init()
	go processWebChanges(wconfig)

	checkUpdates()

	log.Print("Waiting for images to appear in ", config.path)
	// wander the path, forever
	for {
		err := filepath.Walk(config.path,
			func(path string, f os.FileInfo, err error) error { return checkFile(path, f, err, config) })
		if err != nil {
			log.Fatal("could not watch path", err)
		}
		lastCheck = newLastCheck
		time.Sleep(time.Duration(config.watch) * time.Second)
	}
}

func processWebChanges(wc web.DAUWebServer) {
	for {
		change := <-wc.ConfigChange
		log.Print(change)
		log.Print("Got a change!")
	}
}

func checkPath(path string) {
	src, err := os.Stat(path)
	if err != nil {
		log.Fatal(err)
	}
	if !src.IsDir() {
		log.Fatal(path, " is not a directory")
		os.Exit(1)
	}
}

func checkUpdates() {

	type GithubRelease struct {
		HTMLURL string
		TagName string
		Name    string
		Body    string
	}

	client := &http.Client{Timeout: time.Second * 5}
	resp, err := client.Get("https://api.github.com/repos/tardisx/discord-auto-upload/releases/latest")
	if err != nil {
		log.Print("WARNING: Update check failed: ", err)
		return
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatal("could not check read update response")
	}

	var latest GithubRelease
	err = json.Unmarshal(body, &latest)

	if err != nil {
		log.Fatal("could not parse JSON: ", err)
	}

	if currentVersion < latest.TagName {
		fmt.Printf("You are currently on version %s, but version %s is available\n", currentVersion, latest.TagName)
		fmt.Println("----------- Release Info -----------")
		fmt.Println(latest.Body)
		fmt.Println("------------------------------------")
		fmt.Println("Upgrade at https://github.com/tardisx/discord-auto-upload/releases/latest")
	}

}

func parseOptions() Config {

	var newConfig Config
	// Declare the flags to be used
	webhookFlag := getopt.StringLong("webhook", 'w', "", "discord webhook URL")
	pathFlag := getopt.StringLong("directory", 'd', "", "directory to scan, optional, defaults to current directory")
	watchFlag := getopt.Int16Long("watch", 's', 10, "time between scans")
	usernameFlag := getopt.StringLong("username", 'u', "", "username for the bot upload")
	excludeFlag := getopt.StringLong("exclude", 'x', "", "exclude files containing this string")
	noWatermarkFlag := getopt.BoolLong("no-watermark", 'n', "do not put a watermark on images before uploading")
	helpFlag := getopt.BoolLong("help", 'h', "help")
	versionFlag := getopt.BoolLong("version", 'v', "show version")
	getopt.SetParameters("")

	getopt.Parse()

	if *helpFlag {
		getopt.PrintUsage(os.Stderr)
		os.Exit(0)
	}

	if *versionFlag {
		fmt.Println("dau - https://github.com/tardisx/discord-auto-upload")
		fmt.Printf("Version: %s\n", currentVersion)
		os.Exit(0)
	}

	if !getopt.IsSet("directory") {
		*pathFlag = "./"
		log.Println("Defaulting to current directory")
	}

	if !getopt.IsSet("webhook") {
		log.Fatal("ERROR: You must specify a --webhook URL")
	}

	newConfig.path = *pathFlag
	newConfig.webhookURL = *webhookFlag
	newConfig.watch = int(*watchFlag)
	newConfig.username = *usernameFlag
	newConfig.noWatermark = *noWatermarkFlag
	newConfig.exclude = *excludeFlag

	return newConfig
}

func checkFile(path string, f os.FileInfo, err error, config Config) error {

	if f.ModTime().After(lastCheck) && f.Mode().IsRegular() {

		if fileEligible(config, path) {
			// process file
			processFile(config, path)
		}

		if newLastCheck.Before(f.ModTime()) {
			newLastCheck = f.ModTime()
		}
	}

	return nil
}

func fileEligible(config Config, file string) bool {

	if config.exclude != "" && strings.Contains(file, config.exclude) {
		return false
	}

	extension := strings.ToLower(filepath.Ext(file))
	if extension == ".png" || extension == ".jpg" || extension == ".gif" {
		return true
	}

	return false
}

func processFile(config Config, file string) {

	if !config.noWatermark {
		log.Print("Copying to temp location and watermarking ", file)
		file = mungeFile(file)
	}

	log.Print("Uploading ", file)

	extraParams := map[string]string{}

	if config.username != "" {
		extraParams["username"] = config.username
	}

	type DiscordAPIResponseAttachment struct {
		URL      string
		ProxyURL string
		Size     int
		Width    int
		Height   int
		Filename string
	}

	type DiscordAPIResponse struct {
		Attachments []DiscordAPIResponseAttachment
		ID          int64 `json:",string"`
	}

	var retriesRemaining = 5
	for retriesRemaining > 0 {
		request, err := newfileUploadRequest(config.webhookURL, extraParams, "file", file)
		if err != nil {
			log.Fatal(err)
		}
		start := time.Now()
		client := &http.Client{Timeout: time.Second * 30}
		resp, err := client.Do(request)
		if err != nil {
			log.Print("Error performing request:", err)
			retriesRemaining--
			sleepForRetries(retriesRemaining)
			continue
		} else {

			if resp.StatusCode != 200 {
				log.Print("Bad response from server:", resp.StatusCode)
				retriesRemaining--
				sleepForRetries(retriesRemaining)
				continue
			}

			resBody, err := ioutil.ReadAll(resp.Body)
			if err != nil {
				log.Print("could not deal with body: ", err)
				retriesRemaining--
				sleepForRetries(retriesRemaining)
				continue
			}
			resp.Body.Close()

			var res DiscordAPIResponse
			err = json.Unmarshal(resBody, &res)

			if err != nil {
				log.Print("could not parse JSON: ", err)
				fmt.Println("Response was:", string(resBody[:]))
				retriesRemaining--
				sleepForRetries(retriesRemaining)
				continue
			}
			if len(res.Attachments) < 1 {
				log.Print("bad response - no attachments?")
				retriesRemaining--
				sleepForRetries(retriesRemaining)
				continue
			}
			var a = res.Attachments[0]
			elapsed := time.Since(start)
			rate := float64(a.Size) / elapsed.Seconds() / 1024.0

			log.Printf("Uploaded to %s %dx%d", a.URL, a.Width, a.Height)
			log.Printf("id: %d, %d bytes transferred in %.2f seconds (%.2f KiB/s)", res.ID, a.Size, elapsed.Seconds(), rate)
			break
		}
	}

	if !config.noWatermark {
		log.Print("Removing temporary file ", file)
		os.Remove(file)
	}

	if retriesRemaining == 0 {
		log.Fatal("Failed to upload, even after retries")
	}
}

func sleepForRetries(retry int) {
	if retry == 0 {
		return
	}
	retryTime := (6-retry)*(6-retry) + 6
	log.Printf("Will retry in %d seconds (%d remaining attempts)", retryTime, retry)
	// time.Sleep(time.Duration(retryTime) * time.Second)
}

func newfileUploadRequest(uri string, params map[string]string, paramName, path string) (*http.Request, error) {
	file, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)
	part, err := writer.CreateFormFile(paramName, filepath.Base(path))
	if err != nil {
		return nil, err
	}
	_, err = io.Copy(part, file)
	if err != nil {
		log.Fatal("Could not copy: ", err)
	}

	for key, val := range params {
		_ = writer.WriteField(key, val)
	}
	err = writer.Close()
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest("POST", uri, body)
	req.Header.Set("Content-Type", writer.FormDataContentType())
	return req, err
}

func mungeFile(path string) string {

	reader, err := os.Open(path)
	if err != nil {
		log.Fatal(err)
	}
	defer reader.Close()

	im, _, err := image.Decode(reader)
	if err != nil {
		log.Fatal(err)
	}
	bounds := im.Bounds()
	// var S float64 = float64(bounds.Max.X)

	dc := gg.NewContext(bounds.Max.X, bounds.Max.Y)
	dc.Clear()
	dc.SetRGB(0, 0, 0)

	dc.SetFontFace(inconsolata.Regular8x16)

	dc.DrawImage(im, 0, 0)

	dc.DrawRoundedRectangle(0, float64(bounds.Max.Y-18.0), 320, float64(bounds.Max.Y), 0)
	dc.SetRGB(0, 0, 0)
	dc.Fill()

	dc.SetRGB(1, 1, 1)

	dc.DrawString("github.com/tardisx/discord-auto-upload", 5.0, float64(bounds.Max.Y)-5.0)

	tempfile, err := ioutil.TempFile("", "dau")
	if err != nil {
		log.Fatal(err)
	}
	tempfile.Close()
	os.Remove(tempfile.Name())
	actualName := tempfile.Name() + ".png"

	dc.SavePNG(actualName)
	return actualName
}
<commit_msg>Automatically open web browser on startup<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"mime/multipart"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"

	"image"

	_ "image/gif"
	_ "image/jpeg"
	_ "image/png"

	"github.com/fogleman/gg"
	"github.com/pborman/getopt"
	"github.com/skratchdot/open-golang/open"
	"golang.org/x/image/font/inconsolata"

	"discord-auto-upload/web"
)

const currentVersion = "0.7"

var lastCheck = time.Now()
var newLastCheck = time.Now()

// Config for the application
type Config struct {
	webhookURL  string
	path        string
	watch       int
	username    string
	noWatermark bool
	exclude     string
}

func main() {

	config := parseOptions()
	checkPath(config.path)
	wconfig := web.Init()
	go processWebChanges(wconfig)

	log.Print("Opening web browser")
	open.Start("http://localhost:9090")

	checkUpdates()

	log.Print("Waiting for images to appear in ", config.path)
	// wander the path, forever
	for {
		err := filepath.Walk(config.path,
			func(path string, f os.FileInfo, err error) error { return checkFile(path, f, err, config) })
		if err != nil {
			log.Fatal("could not watch path", err)
		}
		lastCheck = newLastCheck
		time.Sleep(time.Duration(config.watch) * time.Second)
	}
}

func processWebChanges(wc web.DAUWebServer) {
	for {
		change := <-wc.ConfigChange
		log.Print(change)
		log.Print("Got a change!")
	}
}

func checkPath(path string) {
	src, err := os.Stat(path)
	if err != nil {
		log.Fatal(err)
	}
	if !src.IsDir() {
		log.Fatal(path, " is not a directory")
		os.Exit(1)
	}
}

func checkUpdates() {

	type GithubRelease struct {
		HTMLURL string
		TagName string
		Name    string
		Body    string
	}

	client := &http.Client{Timeout: time.Second * 5}
	resp, err := client.Get("https://api.github.com/repos/tardisx/discord-auto-upload/releases/latest")
	if err != nil {
		log.Print("WARNING: Update check failed: ", err)
		return
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatal("could not check read update response")
	}

	var latest GithubRelease
	err = json.Unmarshal(body, &latest)

	if err != nil {
		log.Fatal("could not parse JSON: ", err)
	}

	if currentVersion < latest.TagName {
		fmt.Printf("You are currently on version %s, but version %s is available\n", currentVersion, latest.TagName)
		fmt.Println("----------- Release Info -----------")
		fmt.Println(latest.Body)
		fmt.Println("------------------------------------")
		fmt.Println("Upgrade at https://github.com/tardisx/discord-auto-upload/releases/latest")
	}

}

func parseOptions() Config {

	var newConfig Config
	// Declare the flags to be used
	webhookFlag := getopt.StringLong("webhook", 'w', "", "discord webhook URL")
	pathFlag := getopt.StringLong("directory", 'd', "", "directory to scan, optional, defaults to current directory")
	watchFlag := getopt.Int16Long("watch", 's', 10, "time between scans")
	usernameFlag := getopt.StringLong("username", 'u', "", "username for the bot upload")
	excludeFlag := getopt.StringLong("exclude", 'x', "", "exclude files containing this string")
	noWatermarkFlag := getopt.BoolLong("no-watermark", 'n', "do not put a watermark on images before uploading")
	helpFlag := getopt.BoolLong("help", 'h', "help")
	versionFlag := getopt.BoolLong("version", 'v', "show version")
	getopt.SetParameters("")

	getopt.Parse()

	if *helpFlag {
		getopt.PrintUsage(os.Stderr)
		os.Exit(0)
	}

	if *versionFlag {
		fmt.Println("dau - https://github.com/tardisx/discord-auto-upload")
		fmt.Printf("Version: %s\n", currentVersion)
		os.Exit(0)
	}

	if !getopt.IsSet("directory") {
		*pathFlag = "./"
		log.Println("Defaulting to current directory")
	}

	if !getopt.IsSet("webhook") {
		log.Fatal("ERROR: You must specify a --webhook URL")
	}

	newConfig.path = *pathFlag
	newConfig.webhookURL = *webhookFlag
	newConfig.watch = int(*watchFlag)
	newConfig.username = *usernameFlag
	newConfig.noWatermark = *noWatermarkFlag
	newConfig.exclude = *excludeFlag

	return newConfig
}

func checkFile(path string, f os.FileInfo, err error, config Config) error {

	if f.ModTime().After(lastCheck) && f.Mode().IsRegular() {

		if fileEligible(config, path) {
			// process file
			processFile(config, path)
		}

		if newLastCheck.Before(f.ModTime()) {
			newLastCheck = f.ModTime()
		}
	}

	return nil
}

func fileEligible(config Config, file string) bool {

	if config.exclude != "" && strings.Contains(file, config.exclude) {
		return false
	}

	extension := strings.ToLower(filepath.Ext(file))
	if extension == ".png" || extension == ".jpg" || extension == ".gif" {
		return true
	}

	return false
}

func processFile(config Config, file string) {

	if !config.noWatermark {
		log.Print("Copying to temp location and watermarking ", file)
		file = mungeFile(file)
	}

	log.Print("Uploading ", file)

	extraParams := map[string]string{}

	if config.username != "" {
		extraParams["username"] = config.username
	}

	type DiscordAPIResponseAttachment struct {
		URL      string
		ProxyURL string
		Size     int
		Width    int
		Height   int
		Filename string
	}

	type DiscordAPIResponse struct {
		Attachments []DiscordAPIResponseAttachment
		ID          int64 `json:",string"`
	}

	var retriesRemaining = 5
	for retriesRemaining > 0 {
		request, err := newfileUploadRequest(config.webhookURL, extraParams, "file", file)
		if err != nil {
			log.Fatal(err)
		}
		start := time.Now()
		client := &http.Client{Timeout: time.Second * 30}
		resp, err := client.Do(request)
		if err != nil {
			log.Print("Error performing request:", err)
			retriesRemaining--
			sleepForRetries(retriesRemaining)
			continue
		} else {

			if resp.StatusCode != 200 {
				log.Print("Bad response from server:", resp.StatusCode)
				retriesRemaining--
				sleepForRetries(retriesRemaining)
				continue
			}

			resBody, err := ioutil.ReadAll(resp.Body)
			if err != nil {
				log.Print("could not deal with body: ", err)
				retriesRemaining--
				sleepForRetries(retriesRemaining)
				continue
			}
			resp.Body.Close()

			var res DiscordAPIResponse
			err = json.Unmarshal(resBody, &res)

			if err != nil {
				log.Print("could not parse JSON: ", err)
				fmt.Println("Response was:", string(resBody[:]))
				retriesRemaining--
				sleepForRetries(retriesRemaining)
				continue
			}
			if len(res.Attachments) < 1 {
				log.Print("bad response - no attachments?")
				retriesRemaining--
				sleepForRetries(retriesRemaining)
				continue
			}
			var a = res.Attachments[0]
			elapsed := time.Since(start)
			rate := float64(a.Size) / elapsed.Seconds() / 1024.0

			log.Printf("Uploaded to %s %dx%d", a.URL, a.Width, a.Height)
			log.Printf("id: %d, %d bytes transferred in %.2f seconds (%.2f KiB/s)", res.ID, a.Size, elapsed.Seconds(), rate)
			break
		}
	}

	if !config.noWatermark {
		log.Print("Removing temporary file ", file)
		os.Remove(file)
	}

	if retriesRemaining == 0 {
		log.Fatal("Failed to upload, even after retries")
	}
}

func sleepForRetries(retry int) {
	if retry == 0 {
		return
	}
	retryTime := (6-retry)*(6-retry) + 6
	log.Printf("Will retry in %d seconds (%d remaining attempts)", retryTime, retry)
	// time.Sleep(time.Duration(retryTime) * time.Second)
}

func newfileUploadRequest(uri string, params map[string]string, paramName, path string) (*http.Request, error) {
	file, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)
	part, err := writer.CreateFormFile(paramName, filepath.Base(path))
	if err != nil {
		return nil, err
	}
	_, err = io.Copy(part, file)
	if err != nil {
		log.Fatal("Could not copy: ", err)
	}

	for key, val := range params {
		_ = writer.WriteField(key, val)
	}
	err = writer.Close()
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest("POST", uri, body)
	req.Header.Set("Content-Type", writer.FormDataContentType())
	return req, err
}

func mungeFile(path string) string {

	reader, err := os.Open(path)
	if err != nil {
		log.Fatal(err)
	}
	defer reader.Close()

	im, _, err := image.Decode(reader)
	if err != nil {
		log.Fatal(err)
	}
	bounds := im.Bounds()
	// var S float64 = float64(bounds.Max.X)

	dc := gg.NewContext(bounds.Max.X, bounds.Max.Y)
	dc.Clear()
	dc.SetRGB(0, 0, 0)

	dc.SetFontFace(inconsolata.Regular8x16)

	dc.DrawImage(im, 0, 0)

	dc.DrawRoundedRectangle(0, float64(bounds.Max.Y-18.0), 320, float64(bounds.Max.Y), 0)
	dc.SetRGB(0, 0, 0)
	dc.Fill()

	dc.SetRGB(1, 1, 1)

	dc.DrawString("github.com/tardisx/discord-auto-upload", 5.0, float64(bounds.Max.Y)-5.0)

	tempfile, err := ioutil.TempFile("", "dau")
	if err != nil {
		log.Fatal(err)
	}
	tempfile.Close()
	os.Remove(tempfile.Name())
	actualName := tempfile.Name() + ".png"

	dc.SavePNG(actualName)
	return actualName
}
<|endoftext|>