<commit_before>// Package server is the backend web server for reaction.pics
package server

import (
	"encoding/json"
	"fmt"
	"html/template"
	"net/http"
	"os"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"time"

	"github.com/albertyw/reaction-pics/tumblr"
	"github.com/ikeikeikeike/go-sitemap-generator/v2/stm"
	"github.com/pkg/errors"
	"github.com/rollbar/rollbar-go"
	"go.uber.org/zap"
)

const (
	maxResults = 20
)

type metaHeader struct {
	Property string
	Content  string
}

func relToAbsPath(path string) string {
	_, filename, _, ok := runtime.Caller(0)
	if !ok {
		filename = "."
	}
	absPath := filepath.Join(filepath.Dir(filename), path)
	return absPath
}

// indexHandler is an http handler that returns the index page HTML
func indexHandler(w http.ResponseWriter, r *http.Request, d handlerDeps) {
	if r.URL.Path != "/" && !strings.HasPrefix(r.URL.Path, "/post/") {
		err := fmt.Errorf("file not found: %s", r.URL.Path)
		d.logger.Warn(err)
		if !strings.HasPrefix(r.URL.Path, ".php") {
			// Ignore bots scanning for php vulnerabilities
			rollbar.RequestError(rollbar.WARN, r, err)
		}
		http.NotFound(w, r)
		return
	}
	indexHandlerWithHeaders(w, r, d, []metaHeader{})
}

func indexHandlerWithHeaders(w http.ResponseWriter, r *http.Request, d handlerDeps, headers []metaHeader) {
	path := relToAbsPath("static/index.htm")
	t, err := template.ParseFiles(path)
	if err != nil {
		err = errors.Wrap(err, "Cannot read post template")
		d.logger.Error(err)
		rollbar.RequestError(rollbar.ERR, r, err)
		http.Error(w, err.Error(), 500)
		return
	}
	templateData := struct {
		CacheString string
		MetaHeaders []metaHeader
	}{
		CacheString: d.appCacheString,
		MetaHeaders: headers,
	}
	err = t.Execute(w, templateData)
	if err != nil {
		err = errors.Wrap(err, "Cannot execute template")
		d.logger.Error(err)
		rollbar.RequestError(rollbar.ERR, r, err)
		http.Error(w, err.Error(), 500)
		return
	}
}

// searchHandler is an http handler to search data for keywords in json format
// It matches the query against post titles and then ranks posts by number of likes
func searchHandler(w http.ResponseWriter, r *http.Request, d handlerDeps) {
	query := r.URL.Query().Get("query")
	query = strings.TrimSpace(strings.ToLower(query))
	queries := strings.Split(query, " ")
	queriedBoard := d.board.FilterBoard(queries)
	if query == "" {
		queriedBoard.RandomizePosts()
	}
	offsetString := r.URL.Query().Get("offset")
	offset, err := strconv.Atoi(offsetString)
	if err != nil {
		offset = 0
	}
	data := map[string]interface{}{
		"offset":       offset,
		"totalResults": len(queriedBoard.Posts),
	}
	queriedBoard.LimitBoard(offset, maxResults)
	queriedBoard.SortPostsByLikes()
	data["data"] = queriedBoard.PostsToJSON()
	dataBytes, _ := json.Marshal(data)
	fmt.Fprint(w, string(dataBytes))
}

// postDataHandler is an http handler to return post data by ID in json format
func postDataHandler(w http.ResponseWriter, r *http.Request, d handlerDeps) {
	pathStrings := strings.Split(r.URL.Path, "/")
	postIDString := pathStrings[2]
	postID, err := strconv.ParseInt(postIDString, 10, 64)
	if err != nil {
		err = errors.Wrap(err, "Cannot parse post id")
		d.logger.Warn(err)
		rollbar.RequestError(rollbar.WARN, r, err)
		http.NotFound(w, r)
		return
	}
	post := d.board.GetPostByID(postID)
	if post == nil {
		err = errors.New("Cannot find post")
		d.logger.Warn(err)
		rollbar.RequestError(rollbar.WARN, r, err)
		http.NotFound(w, r)
		return
	}
	data := map[string]interface{}{
		"offset":       0,
		"totalResults": 1,
		"data":         []tumblr.PostJSON{post.ToJSONStruct()},
	}
	marshalledPost, _ := json.Marshal(data)
	fmt.Fprint(w, string(marshalledPost))
}

// postHandler is an http handler that validates the correctness of a post url
// and returns the index page html to render it correct
func postHandler(w http.ResponseWriter, r *http.Request, d handlerDeps) {
	pathStrings := strings.Split(r.URL.Path, "/")
	postIDString := pathStrings[2]
	postID, err := strconv.ParseInt(postIDString, 10, 64)
	if err != nil {
		err = errors.Wrap(err, "Cannot parse post id")
		d.logger.Warn(err)
		rollbar.RequestError(rollbar.WARN, r, err)
		http.NotFound(w, r)
		return
	}
	var post *tumblr.Post
	for _, p := range d.board.Posts {
		if p.ID == postID {
			post = &p
			break
		}
	}
	if post == nil {
		err = errors.New("Cannot find post")
		d.logger.Warn(err)
		rollbar.RequestError(rollbar.WARN, r, err)
		http.NotFound(w, r)
		return
	}

	headers := []metaHeader{
		{"og:title", post.Title},
		{"og:image", post.Image},
	}

	indexHandlerWithHeaders(w, r, d, headers)
}

// statsHandler returns internal stats about the reaction.pics DB as json
func statsHandler(w http.ResponseWriter, r *http.Request, d handlerDeps) {
	postCount := strconv.Itoa(len(d.board.Posts))
	data := map[string]interface{}{
		"postCount": postCount,
		"keywords":  d.board.Keywords(),
	}
	stats, _ := json.Marshal(data)
	fmt.Fprint(w, string(stats))
}

// sitemapHandler returns a sitemap of reaction.pics as an xml file
func sitemapHandler(w http.ResponseWriter, r *http.Request, d handlerDeps) {
	sm := stm.NewSitemap(0)
	sm.SetDefaultHost(os.Getenv("HOST"))

	sm.Create()
	sm.Add(stm.URL{{"loc", "/"}})
	for _, url := range d.board.URLs() {
		sm.Add(stm.URL{{"loc", url}})
	}
	w.Write(sm.XMLContent())
}

// staticHandler returns static files
func staticHandler(w http.ResponseWriter, r *http.Request, _ handlerDeps) {
	staticFS := rewriteFS(http.FileServer(http.Dir(relToAbsPath("static"))).ServeHTTP)
	staticFS(w, r)
}

func timeHandler(w http.ResponseWriter, r *http.Request, _ handlerDeps) {
	unixTime := int32(time.Now().Unix())
	data := map[string]interface{}{
		"unixtime": unixTime,
	}
	timeData, _ := json.Marshal(data)
	fmt.Fprint(w, string(timeData))
}

func faviconHandler(w http.ResponseWriter, r *http.Request, _ handlerDeps) {
	faviconPath := relToAbsPath("static/favicon/favicon.iso")
	http.ServeFile(w, r, faviconPath)
}

func robotsTxtHandler(w http.ResponseWriter, r *http.Request, _ handlerDeps) {
	fmt.Fprint(w, "")
}

// Run starts up the HTTP server
func Run(logger *zap.SugaredLogger) {
	board := tumblr.InitializeBoard()
	address := fmt.Sprintf(":%s", os.Getenv("PORT"))
	logger.Infof("server listening on %s", address)
	generator := newHandlerGenerator(board, logger)
	http.Handle("/", generator.newHandler(indexHandler))
	http.Handle("/favicon.ico", generator.newHandler(faviconHandler))
	http.Handle("/robots.txt", generator.newHandler(robotsTxtHandler))
	http.Handle("/search", generator.newHandler(searchHandler))
	http.Handle("/postdata/", generator.newHandler(postDataHandler))
	http.Handle("/post/", generator.newHandler(postHandler))
	http.Handle("/stats.json", generator.newHandler(statsHandler))
	http.Handle("/sitemap.xml", generator.newHandler(sitemapHandler))
	http.Handle("/static/", generator.newHandler(staticHandler))
	http.Handle("/time/", generator.newHandler(timeHandler))
	http.ListenAndServe(address, nil)
}
<commit_msg>Fix reference to favicon.ico<commit_after>// Package server is the backend web server for reaction.pics
package server

import (
	"encoding/json"
	"fmt"
	"html/template"
	"net/http"
	"os"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"time"

	"github.com/albertyw/reaction-pics/tumblr"
	"github.com/ikeikeikeike/go-sitemap-generator/v2/stm"
	"github.com/pkg/errors"
	"github.com/rollbar/rollbar-go"
	"go.uber.org/zap"
)

const (
	maxResults = 20
)

type metaHeader struct {
	Property string
	Content  string
}

func relToAbsPath(path string) string {
	_, filename, _, ok := runtime.Caller(0)
	if !ok {
		filename = "."
	}
	absPath := filepath.Join(filepath.Dir(filename), path)
	return absPath
}

// indexHandler is an http handler that returns the index page HTML
func indexHandler(w http.ResponseWriter, r *http.Request, d handlerDeps) {
	if r.URL.Path != "/" && !strings.HasPrefix(r.URL.Path, "/post/") {
		err := fmt.Errorf("file not found: %s", r.URL.Path)
		d.logger.Warn(err)
		if !strings.HasPrefix(r.URL.Path, ".php") {
			// Ignore bots scanning for php vulnerabilities
			rollbar.RequestError(rollbar.WARN, r, err)
		}
		http.NotFound(w, r)
		return
	}
	indexHandlerWithHeaders(w, r, d, []metaHeader{})
}

func indexHandlerWithHeaders(w http.ResponseWriter, r *http.Request, d handlerDeps, headers []metaHeader) {
	path := relToAbsPath("static/index.htm")
	t, err := template.ParseFiles(path)
	if err != nil {
		err = errors.Wrap(err, "Cannot read post template")
		d.logger.Error(err)
		rollbar.RequestError(rollbar.ERR, r, err)
		http.Error(w, err.Error(), 500)
		return
	}
	templateData := struct {
		CacheString string
		MetaHeaders []metaHeader
	}{
		CacheString: d.appCacheString,
		MetaHeaders: headers,
	}
	err = t.Execute(w, templateData)
	if err != nil {
		err = errors.Wrap(err, "Cannot execute template")
		d.logger.Error(err)
		rollbar.RequestError(rollbar.ERR, r, err)
		http.Error(w, err.Error(), 500)
		return
	}
}

// searchHandler is an http handler to search data for keywords in json format
// It matches the query against post titles and then ranks posts by number of likes
func searchHandler(w http.ResponseWriter, r *http.Request, d handlerDeps) {
	query := r.URL.Query().Get("query")
	query = strings.TrimSpace(strings.ToLower(query))
	queries := strings.Split(query, " ")
	queriedBoard := d.board.FilterBoard(queries)
	if query == "" {
		queriedBoard.RandomizePosts()
	}
	offsetString := r.URL.Query().Get("offset")
	offset, err := strconv.Atoi(offsetString)
	if err != nil {
		offset = 0
	}
	data := map[string]interface{}{
		"offset":       offset,
		"totalResults": len(queriedBoard.Posts),
	}
	queriedBoard.LimitBoard(offset, maxResults)
	queriedBoard.SortPostsByLikes()
	data["data"] = queriedBoard.PostsToJSON()
	dataBytes, _ := json.Marshal(data)
	fmt.Fprint(w, string(dataBytes))
}

// postDataHandler is an http handler to return post data by ID in json format
func postDataHandler(w http.ResponseWriter, r *http.Request, d handlerDeps) {
	pathStrings := strings.Split(r.URL.Path, "/")
	postIDString := pathStrings[2]
	postID, err := strconv.ParseInt(postIDString, 10, 64)
	if err != nil {
		err = errors.Wrap(err, "Cannot parse post id")
		d.logger.Warn(err)
		rollbar.RequestError(rollbar.WARN, r, err)
		http.NotFound(w, r)
		return
	}
	post := d.board.GetPostByID(postID)
	if post == nil {
		err = errors.New("Cannot find post")
		d.logger.Warn(err)
		rollbar.RequestError(rollbar.WARN, r, err)
		http.NotFound(w, r)
		return
	}
	data := map[string]interface{}{
		"offset":       0,
		"totalResults": 1,
		"data":         []tumblr.PostJSON{post.ToJSONStruct()},
	}
	marshalledPost, _ := json.Marshal(data)
	fmt.Fprint(w, string(marshalledPost))
}

// postHandler is an http handler that validates the correctness of a post url
// and returns the index page html to render it correct
func postHandler(w http.ResponseWriter, r *http.Request, d handlerDeps) {
	pathStrings := strings.Split(r.URL.Path, "/")
	postIDString := pathStrings[2]
	postID, err := strconv.ParseInt(postIDString, 10, 64)
	if err != nil {
		err = errors.Wrap(err, "Cannot parse post id")
		d.logger.Warn(err)
		rollbar.RequestError(rollbar.WARN, r, err)
		http.NotFound(w, r)
		return
	}
	var post *tumblr.Post
	for _, p := range d.board.Posts {
		if p.ID == postID {
			post = &p
			break
		}
	}
	if post == nil {
		err = errors.New("Cannot find post")
		d.logger.Warn(err)
		rollbar.RequestError(rollbar.WARN, r, err)
		http.NotFound(w, r)
		return
	}

	headers := []metaHeader{
		{"og:title", post.Title},
		{"og:image", post.Image},
	}

	indexHandlerWithHeaders(w, r, d, headers)
}

// statsHandler returns internal stats about the reaction.pics DB as json
func statsHandler(w http.ResponseWriter, r *http.Request, d handlerDeps) {
	postCount := strconv.Itoa(len(d.board.Posts))
	data := map[string]interface{}{
		"postCount": postCount,
		"keywords":  d.board.Keywords(),
	}
	stats, _ := json.Marshal(data)
	fmt.Fprint(w, string(stats))
}

// sitemapHandler returns a sitemap of reaction.pics as an xml file
func sitemapHandler(w http.ResponseWriter, r *http.Request, d handlerDeps) {
	sm := stm.NewSitemap(0)
	sm.SetDefaultHost(os.Getenv("HOST"))

	sm.Create()
	sm.Add(stm.URL{{"loc", "/"}})
	for _, url := range d.board.URLs() {
		sm.Add(stm.URL{{"loc", url}})
	}
	w.Write(sm.XMLContent())
}

// staticHandler returns static files
func staticHandler(w http.ResponseWriter, r *http.Request, _ handlerDeps) {
	staticFS := rewriteFS(http.FileServer(http.Dir(relToAbsPath("static"))).ServeHTTP)
	staticFS(w, r)
}

func timeHandler(w http.ResponseWriter, r *http.Request, _ handlerDeps) {
	unixTime := int32(time.Now().Unix())
	data := map[string]interface{}{
		"unixtime": unixTime,
	}
	timeData, _ := json.Marshal(data)
	fmt.Fprint(w, string(timeData))
}

func faviconHandler(w http.ResponseWriter, r *http.Request, _ handlerDeps) {
	faviconPath := relToAbsPath("static/favicon/favicon.ico")
	http.ServeFile(w, r, faviconPath)
}

func robotsTxtHandler(w http.ResponseWriter, r *http.Request, _ handlerDeps) {
	fmt.Fprint(w, "")
}

// Run starts up the HTTP server
func Run(logger *zap.SugaredLogger) {
	board := tumblr.InitializeBoard()
	address := fmt.Sprintf(":%s", os.Getenv("PORT"))
	logger.Infof("server listening on %s", address)
	generator := newHandlerGenerator(board, logger)
	http.Handle("/", generator.newHandler(indexHandler))
	http.Handle("/favicon.ico", generator.newHandler(faviconHandler))
	http.Handle("/robots.txt", generator.newHandler(robotsTxtHandler))
	http.Handle("/search", generator.newHandler(searchHandler))
	http.Handle("/postdata/", generator.newHandler(postDataHandler))
	http.Handle("/post/", generator.newHandler(postHandler))
	http.Handle("/stats.json", generator.newHandler(statsHandler))
	http.Handle("/sitemap.xml", generator.newHandler(sitemapHandler))
	http.Handle("/static/", generator.newHandler(staticHandler))
	http.Handle("/time/", generator.newHandler(timeHandler))
	http.ListenAndServe(address, nil)
}
<|endoftext|>


<commit_before>package main

//go:generate go run generate/assets.go
//go:generate go run generate/templates.go

import (
	"os"

	srv "github.com/ZoltanLajosKis/gmdd/server"
	kpn "github.com/alecthomas/kingpin"
	log "github.com/sirupsen/logrus"
)

var (
	version  string
	revision string

	root = kpn.Arg("rootDir", "Root directory to serve.").String()
	addr = kpn.Flag("addr", "Address to bind to.").Short('a').Default("").
		OverrideDefaultFromEnvar("GMDD_Addr").String()
	port = kpn.Flag("port", "Listen port.").Short('p').Default("8000").
		OverrideDefaultFromEnvar("GMDD_PORT").Int()
	logLevel = kpn.Flag("loglevel", "Log level").Short('l').Default("warn").
			OverrideDefaultFromEnvar("GMDD_LOGLEVEL").String()
)

func main() {
	kpn.Parse()

	lvl, err := log.ParseLevel(*logLevel)
	if err != nil {
		lvl = log.WarnLevel
		log.WithFields(log.Fields{
			"error": err,
		}).Warn("Invalid log level; using warn.")
	}
	log.SetLevel(lvl)

	if *root == "" {
		*root, err = os.Getwd()
		if err != nil {
			log.Panic(err)
		}
	}

	log.WithFields(log.Fields{
		"version":  version,
		"revision": revision,
	}).Debug("gmdd")

	srv.Start(*addr, *port, *root)
}
<commit_msg>Fix typo<commit_after>package main

//go:generate go run generate/assets.go
//go:generate go run generate/templates.go

import (
	"os"

	srv "github.com/ZoltanLajosKis/gmdd/server"
	kpn "github.com/alecthomas/kingpin"
	log "github.com/sirupsen/logrus"
)

var (
	version  string
	revision string

	root = kpn.Arg("rootDir", "Root directory to serve.").String()
	addr = kpn.Flag("addr", "Address to bind to.").Short('a').Default("").
		OverrideDefaultFromEnvar("GMDD_Addr").String()
	port = kpn.Flag("port", "Listen port.").Short('p').Default("8000").
		OverrideDefaultFromEnvar("GMDD_PORT").Int()
	logLevel = kpn.Flag("loglevel", "Log level.").Short('l').Default("warn").
			OverrideDefaultFromEnvar("GMDD_LOGLEVEL").String()
)

func main() {
	kpn.Parse()

	lvl, err := log.ParseLevel(*logLevel)
	if err != nil {
		lvl = log.WarnLevel
		log.WithFields(log.Fields{
			"error": err,
		}).Warn("Invalid log level; using warn.")
	}
	log.SetLevel(lvl)

	if *root == "" {
		*root, err = os.Getwd()
		if err != nil {
			log.Panic(err)
		}
	}

	log.WithFields(log.Fields{
		"version":  version,
		"revision": revision,
	}).Debug("gmdd")

	srv.Start(*addr, *port, *root)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"math/rand"
	"os"
	"time"

	"github.com/lucapette/fakedata/pkg/fakedata"
	flag "github.com/spf13/pflag"
)

var version = "master"

func getFormatter(format, table string) (f fakedata.Formatter, err error) {
	switch format {
	case "csv":
		f = fakedata.NewSeparatorFormatter(",")
	case "tab":
		f = fakedata.NewSeparatorFormatter("\t")
	case "sql":
		f = fakedata.NewSQLFormatter(table)
	case "":
		f = fakedata.NewSeparatorFormatter(" ")
	default:
		err = fmt.Errorf("unknown format: %s", format)
	}
	return f, err
}

func generatorsHelp(generators fakedata.Generators) string {
	max := 0
	for _, gen := range generators {
		if len(gen.Name) > max {
			max = len(gen.Name)
		}
	}

	buffer := &bytes.Buffer{}
	pattern := fmt.Sprintf("%%-%ds%%s\n", max+2) //+2 makes the output more readable
	for _, gen := range generators {
		fmt.Fprintf(buffer, pattern, gen.Name, gen.Desc)
	}

	return buffer.String()
}

func isPipe() bool {
	stat, err := os.Stdin.Stat()
	if err != nil {
		fmt.Printf("error checking shell pipe: %v", err)
	}
	// Check if template data is piped to fakedata
	return (stat.Mode() & os.ModeCharDevice) == 0
}

func findTemplate(path string) string {
	if path != "" {
		tp, err := ioutil.ReadFile(path)
		if err != nil {
			fmt.Printf("unable to read input: %s", err)
			os.Exit(1)
		}

		return string(tp)
	}

	if isPipe() {
		tp, err := ioutil.ReadAll(os.Stdin)
		if err != nil {
			fmt.Printf("unable to read input: %s", err)
			os.Exit(1)
		}

		return string(tp)
	}

	return ""
}

func main() {
	var (
		completionFlag  string
		generatorsFlag  = flag.BoolP("generators", "G", false, "lists available generators")
		generatorFlag   = flag.StringP("generator", "g", "", "show help for a specific generator")
		constraintsFlag = flag.BoolP("generators-with-constraints", "c", false, "lists available generators with constraints")
		limitFlag       = flag.IntP("limit", "l", 10, "limits rows up to n")
		formatFlag      = flag.StringP("format", "f", "", "generators rows in f format. Available formats: csv|tab|sql")
		versionFlag     = flag.BoolP("version", "v", false, "shows version information")
		tableFlag       = flag.StringP("table", "t", "TABLE", "table name of the sql format")
		templateFlag    = flag.StringP("template", "T", "", "Use template as input")
	)
	flag.StringVarP(&completionFlag, "completion", "C", "", "print bash/zsh completion function, pass shell as argument (\"bash\" or \"zsh\")")

	flag.Usage = func() {
		fmt.Fprintf(os.Stdout, "Usage: fakedata [option ...] field...\n\n")
		flag.PrintDefaults()
	}
	flag.Parse()

	if completionFlag != "" {
		completion, err := fakedata.PrintShellCompletionFunction(completionFlag)
		if err != nil {
			fmt.Println(err)
		}
		fmt.Printf("%s\n", completion)
		os.Exit(0)
	}

	if *versionFlag {
		fmt.Println(version)
		os.Exit(0)
	}

	generators := fakedata.NewGenerators()

	if *generatorsFlag {
		fmt.Print(generatorsHelp(generators))
		os.Exit(0)
	}

	if *generatorFlag != "" {
		if generator := generators.FindByName(*generatorFlag); generator != nil {
			fmt.Printf("Description: %s\n\nExample:\n\n", generator.Desc)
			for i := 0; i < 5; i++ {
				fmt.Println(generator.Func())
			}
		}
		os.Exit(0)
	}

	if *constraintsFlag {
		fmt.Print(generatorsHelp(generators.WithConstraints()))
		os.Exit(0)
	}

	rand.Seed(time.Now().UnixNano())

	if tmpl := findTemplate(*templateFlag); tmpl != "" {
		if err := fakedata.ExecuteTemplate(tmpl, *limitFlag); err != nil {
			fmt.Println(err)
			os.Exit(1)
		}

		os.Exit(0)
	}

	if len(flag.Args()) == 0 {
		flag.Usage()
		os.Exit(0)
	}

	columns, err := fakedata.NewColumns(flag.Args())
	if err != nil {
		fmt.Printf("%v\n\n", err)
		flag.Usage()
		os.Exit(1)
	}

	formatter, err := getFormatter(*formatFlag, *tableFlag)
	if err != nil {
		fmt.Printf("%v\n\n", err)
		flag.Usage()
		os.Exit(0)
	}

	for i := 0; i < *limitFlag; i++ {
		fmt.Println(columns.GenerateRow(formatter))
	}
}
<commit_msg>(shell): Switch from flag.StringVarP to flag.StringP<commit_after>package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"math/rand"
	"os"
	"time"

	"github.com/lucapette/fakedata/pkg/fakedata"
	flag "github.com/spf13/pflag"
)

var version = "master"

func getFormatter(format, table string) (f fakedata.Formatter, err error) {
	switch format {
	case "csv":
		f = fakedata.NewSeparatorFormatter(",")
	case "tab":
		f = fakedata.NewSeparatorFormatter("\t")
	case "sql":
		f = fakedata.NewSQLFormatter(table)
	case "":
		f = fakedata.NewSeparatorFormatter(" ")
	default:
		err = fmt.Errorf("unknown format: %s", format)
	}
	return f, err
}

func generatorsHelp(generators fakedata.Generators) string {
	max := 0
	for _, gen := range generators {
		if len(gen.Name) > max {
			max = len(gen.Name)
		}
	}

	buffer := &bytes.Buffer{}
	pattern := fmt.Sprintf("%%-%ds%%s\n", max+2) //+2 makes the output more readable
	for _, gen := range generators {
		fmt.Fprintf(buffer, pattern, gen.Name, gen.Desc)
	}

	return buffer.String()
}

func isPipe() bool {
	stat, err := os.Stdin.Stat()
	if err != nil {
		fmt.Printf("error checking shell pipe: %v", err)
	}
	// Check if template data is piped to fakedata
	return (stat.Mode() & os.ModeCharDevice) == 0
}

func findTemplate(path string) string {
	if path != "" {
		tp, err := ioutil.ReadFile(path)
		if err != nil {
			fmt.Printf("unable to read input: %s", err)
			os.Exit(1)
		}

		return string(tp)
	}

	if isPipe() {
		tp, err := ioutil.ReadAll(os.Stdin)
		if err != nil {
			fmt.Printf("unable to read input: %s", err)
			os.Exit(1)
		}

		return string(tp)
	}

	return ""
}

func main() {
	var (
		generatorsFlag  = flag.BoolP("generators", "G", false, "lists available generators")
		generatorFlag   = flag.StringP("generator", "g", "", "show help for a specific generator")
		constraintsFlag = flag.BoolP("generators-with-constraints", "c", false, "lists available generators with constraints")
		limitFlag       = flag.IntP("limit", "l", 10, "limits rows up to n")
		formatFlag      = flag.StringP("format", "f", "", "generators rows in f format. Available formats: csv|tab|sql")
		versionFlag     = flag.BoolP("version", "v", false, "shows version information")
		tableFlag       = flag.StringP("table", "t", "TABLE", "table name of the sql format")
		templateFlag    = flag.StringP("template", "T", "", "Use template as input")
		completionFlag  = flag.StringP("completion", "C", "", "print bash/zsh completion function, pass shell as argument (\"bash\" or \"zsh\")")
	)

	flag.Usage = func() {
		fmt.Fprintf(os.Stdout, "Usage: fakedata [option ...] field...\n\n")
		flag.PrintDefaults()
	}
	flag.Parse()

	if *completionFlag != "" {
		completion, err := fakedata.PrintShellCompletionFunction(*completionFlag)
		if err != nil {
			fmt.Println(err)
		}
		fmt.Printf("%s\n", completion)
		os.Exit(0)
	}

	if *versionFlag {
		fmt.Println(version)
		os.Exit(0)
	}

	generators := fakedata.NewGenerators()

	if *generatorsFlag {
		fmt.Print(generatorsHelp(generators))
		os.Exit(0)
	}

	if *generatorFlag != "" {
		if generator := generators.FindByName(*generatorFlag); generator != nil {
			fmt.Printf("Description: %s\n\nExample:\n\n", generator.Desc)
			for i := 0; i < 5; i++ {
				fmt.Println(generator.Func())
			}
		}
		os.Exit(0)
	}

	if *constraintsFlag {
		fmt.Print(generatorsHelp(generators.WithConstraints()))
		os.Exit(0)
	}

	rand.Seed(time.Now().UnixNano())

	if tmpl := findTemplate(*templateFlag); tmpl != "" {
		if err := fakedata.ExecuteTemplate(tmpl, *limitFlag); err != nil {
			fmt.Println(err)
			os.Exit(1)
		}

		os.Exit(0)
	}

	if len(flag.Args()) == 0 {
		flag.Usage()
		os.Exit(0)
	}

	columns, err := fakedata.NewColumns(flag.Args())
	if err != nil {
		fmt.Printf("%v\n\n", err)
		flag.Usage()
		os.Exit(1)
	}

	formatter, err := getFormatter(*formatFlag, *tableFlag)
	if err != nil {
		fmt.Printf("%v\n\n", err)
		flag.Usage()
		os.Exit(0)
	}

	for i := 0; i < *limitFlag; i++ {
		fmt.Println(columns.GenerateRow(formatter))
	}
}
<|endoftext|>


<commit_before>package main

import (
	"code.google.com/p/go.crypto/ssh"
	"code.google.com/p/go.crypto/ssh/terminal"
	"flag"
	"github.com/golang/glog"
	"io/ioutil"
	//"reflect"
	"runtime"
	"strconv"
	"time"
)

var rsaPrivKeyFile = flag.String("rsa_key", "id_rsa", "The private rsa key for the ssh-server")
var bumpEvery = flag.Int("bump_every", 3600, "How offten to print general process stats, in seconds")
var port = flag.Int("port", 2022, "The port to listen on")

func init() {
}

func Bumper() {
	ticker := time.NewTicker(time.Duration(*bumpEvery) * time.Second)
	var mstats runtime.MemStats
	for {
		runtime.ReadMemStats(&mstats)
		off := (int(mstats.NumGC) + len(mstats.PauseNs) - 1) % len(mstats.PauseNs)
		glog.Infof("GoRoutines: %v Alloc: %vBytes LastGCPause: %v AvgGCPause: %v",
			runtime.NumGoroutine(), mstats.Alloc, time.Duration(mstats.PauseNs[off]),
			time.Duration(mstats.PauseTotalNs/uint64(mstats.NumGC)))

		<-ticker.C

	}
}

func main() {
	flag.Parse()

	go Bumper()

	// An SSH server is represented by a ServerConfig, which holds
	// certificate details and handles authentication of ServerConns.
	config := &ssh.ServerConfig{
		NoClientAuth: false,
		PasswordCallback: func(conn *ssh.ServerConn, user, pass string) bool {
			glog.Warningf("Password auth - User=%q Addr=%v ClientVersion=%q", user, conn.RemoteAddr(), conn.ClientVersion)
			return true
		},
		PublicKeyCallback: func(conn *ssh.ServerConn, user, algo string, pubkey []byte) bool {
			glog.Warningf("Pubkey auth - User=%q Addr=%v ClientVersion=%q", user, conn.RemoteAddr(), conn.ClientVersion)
			return true
		},
		KeyboardInteractiveCallback: func(conn *ssh.ServerConn, user string, client ssh.ClientKeyboardInteractive) bool {
			glog.Warningf("Interactive auth - User=%q Addr=%v ClientVersion=%q", user, conn.RemoteAddr(), conn.ClientVersion)
			return true
		},
	}

	pemBytes, err := ioutil.ReadFile(*rsaPrivKeyFile)
	if err != nil {
		glog.Fatal("Failed to load private key: ", err)
	}
	if err = config.SetRSAPrivateKey(pemBytes); err != nil {
		glog.Fatal("Failed to parse private key: ", err)
	}

	// Once a ServerConfig has been configured, connections can be
	// accepted.
	listener, err := ssh.Listen("tcp", "0.0.0.0:"+strconv.Itoa(*port), config)
	if err != nil {
		glog.Fatalf("failed to listen for connection: %v", err)
	}
	for {
		sConn, err := listener.Accept()
		if err != nil {
			glog.Errorf("failed to accept incoming connection from %v with error: %v", sConn.RemoteAddr(), err)
			continue
		}
		if err := sConn.Handshake(); err != nil {
			glog.Errorf("failed to handshake from %v with error: %v", sConn.RemoteAddr(), err)
			continue
		}

		// A ServerConn multiplexes several channels, which must
		// themselves be Accepted.
		go func() {
			defer sConn.Close()
			defer glog.Infof("Broke Down connection with %v", sConn.RemoteAddr())

			sCloseChan := make(chan struct{})
			defer close(sCloseChan)

			for {
				// Accept reads from the connection, demultiplexes packets
				// to their corresponding channels and returns when a new
				// channel request is seen. Some goroutine must always be
				// calling Accept; otherwise no messages will be forwarded
				// to the channels.
				channel, err := sConn.Accept()
				if err != nil {
					glog.Errorf("error from Accept from %v with error %v: ", sConn.RemoteAddr(), err)
					break
				}

				// Channels have a type, depending on the application level
				// protocol intended. In the case of a shell, the type is
				// "session" and ServerShell may be used to present a simple
				// terminal interface.
				if channelType := channel.ChannelType(); channelType != "session" {
					glog.Warningf("Rejected Channel from %v because of unknown channel type %q", sConn.RemoteAddr(), channelType)
					channel.Reject(ssh.UnknownChannelType, "unknown channel type "+channelType)
					continue
				}
				go func() {
					channel.Accept()
					defer channel.Close()
					defer glog.Infof("Broke Down Channel from %v", sConn.RemoteAddr())

					cCloseChan := make(chan struct{})
					defer close(cCloseChan)

					glog.Warningf("Creating Channel from %v with Payload: %q", sConn.RemoteAddr(), channel.ExtraData())

					term := terminal.NewTerminal(channel, "> ")
					serverTerm := &ssh.ServerTerminal{
						Term:    term,
						Channel: channel,
					}
					/*
						go func() {
							for {
								var data []byte
								length, err := channel.Read(data)
								if errT:= reflect.TypeOf(err); errT.String() == "ssh.ChannelRequest" {
									request := err.(ssh.ChannelRequest)
									glog.Warningf("Got a channel request from %v of %v: ", sConn.RemoteAddr(), request)
								} else if err != nil {
									glog.Errorf("Got a werid channel read from %v with error: %v", sConn.RemoteAddr(), err)
									break
								} else {
									glog.Warningf("Got a channel read from %v with data %q: ", sConn.RemoteAddr(), data[:length])
								}
							}
						}()
					*/
					for {
						line, err := serverTerm.ReadLine()
						if err != nil {
							return
						}
						glog.Info(line)
						if line == "quit" {
							return
						}
					}

				}()
			}
		}()
	}

	glog.Fatal("How did I get here?")
}
<commit_msg>Move code around to make main function slightly more manageable<commit_after>package main

import (
	"code.google.com/p/go.crypto/ssh"
	"code.google.com/p/go.crypto/ssh/terminal"
	"flag"
	"github.com/golang/glog"
	"io/ioutil"
	//"reflect"
	"runtime"
	"strconv"
	"time"
)

var rsaPrivKeyFile = flag.String("rsa_key", "id_rsa", "The private rsa key for the ssh-server")
var bumpEvery = flag.Int("bump_every", 3600, "How offten to print general process stats, in seconds")
var port = flag.Int("port", 2022, "The port to listen on")

func init() {
}

func Bumper() {
	ticker := time.NewTicker(time.Duration(*bumpEvery) * time.Second)
	var mstats runtime.MemStats
	for {
		runtime.ReadMemStats(&mstats)
		off := (int(mstats.NumGC) + len(mstats.PauseNs) - 1) % len(mstats.PauseNs)
		glog.Infof("GoRoutines: %v Alloc: %vBytes LastGCPause: %v AvgGCPause: %v",
			runtime.NumGoroutine(), mstats.Alloc, time.Duration(mstats.PauseNs[off]),
			time.Duration(mstats.PauseTotalNs/uint64(mstats.NumGC)))

		<-ticker.C

	}
}

func ServeSSHConnection(sConn *ssh.ServerConn) {
	defer sConn.Close()
	defer glog.Infof("Broke Down connection with %v", sConn.RemoteAddr())

	sCloseChan := make(chan struct{})
	defer close(sCloseChan)

	for {
		// Accept reads from the connection, demultiplexes packets
		// to their corresponding channels and returns when a new
		// channel request is seen. Some goroutine must always be
		// calling Accept; otherwise no messages will be forwarded
		// to the channels.
		channel, err := sConn.Accept()
		if err != nil {
			glog.Errorf("error from Accept from %v with error %v: ", sConn.RemoteAddr(), err)
			break
		}

		// Channels have a type, depending on the application level
		// protocol intended. In the case of a shell, the type is
		// "session" and ServerShell may be used to present a simple
		// terminal interface.
		if channelType := channel.ChannelType(); channelType != "session" {
			glog.Warningf("Rejected Channel from %v because of unknown channel type %q", sConn.RemoteAddr(), channelType)
			channel.Reject(ssh.UnknownChannelType, "unknown channel type "+channelType)
			continue
		}
		go func() {
			channel.Accept()
			defer channel.Close()
			defer glog.Infof("Broke Down Channel from %v", sConn.RemoteAddr())

			cCloseChan := make(chan struct{})
			defer close(cCloseChan)

			glog.Warningf("Creating Channel from %v with Payload: %q", sConn.RemoteAddr(), channel.ExtraData())

			term := terminal.NewTerminal(channel, "> ")
			serverTerm := &ssh.ServerTerminal{
				Term:    term,
				Channel: channel,
			}
			/*
				go func() {
					for {
						var data []byte
						length, err := channel.Read(data)
						if errT:= reflect.TypeOf(err); errT.String() == "ssh.ChannelRequest" {
							request := err.(ssh.ChannelRequest)
							glog.Warningf("Got a channel request from %v of %v: ", sConn.RemoteAddr(), request)
						} else if err != nil {
							glog.Errorf("Got a werid channel read from %v with error: %v", sConn.RemoteAddr(), err)
							break
						} else {
							glog.Warningf("Got a channel read from %v with data %q: ", sConn.RemoteAddr(), data[:length])
						}
					}
				}()
			*/
			for {
				line, err := serverTerm.ReadLine()
				if err != nil {
					return
				}
				glog.Info(line)
				if line == "quit" {
					return
				}
			}

		}()
	}
}

func main() {
	flag.Parse()

	go Bumper()

	// An SSH server is represented by a ServerConfig, which holds
	// certificate details and handles authentication of ServerConns.
	config := &ssh.ServerConfig{
		NoClientAuth: false,
		PasswordCallback: func(conn *ssh.ServerConn, user, pass string) bool {
			glog.Warningf("Password auth - User=%q Addr=%v ClientVersion=%q", user, conn.RemoteAddr(), conn.ClientVersion)
			return true
		},
		PublicKeyCallback: func(conn *ssh.ServerConn, user, algo string, pubkey []byte) bool {
			glog.Warningf("Pubkey auth - User=%q Addr=%v ClientVersion=%q", user, conn.RemoteAddr(), conn.ClientVersion)
			return true
		},
		KeyboardInteractiveCallback: func(conn *ssh.ServerConn, user string, client ssh.ClientKeyboardInteractive) bool {
			glog.Warningf("Interactive auth - User=%q Addr=%v ClientVersion=%q", user, conn.RemoteAddr(), conn.ClientVersion)
			return true
		},
	}

	pemBytes, err := ioutil.ReadFile(*rsaPrivKeyFile)
	if err != nil {
		glog.Fatal("Failed to load private key: ", err)
	}
	if err = config.SetRSAPrivateKey(pemBytes); err != nil {
		glog.Fatal("Failed to parse private key: ", err)
	}

	// Once a ServerConfig has been configured, connections can be
	// accepted.
	listener, err := ssh.Listen("tcp", "0.0.0.0:"+strconv.Itoa(*port), config)
	if err != nil {
		glog.Fatalf("failed to listen for connection: %v", err)
	}

	for {
		sConn, err := listener.Accept()
		if err != nil {
			glog.Errorf("failed to accept incoming connection from %v with error: %v", sConn.RemoteAddr(), err)
			continue
		}
		if err := sConn.Handshake(); err != nil {
			glog.Errorf("failed to handshake from %v with error: %v", sConn.RemoteAddr(), err)
			continue
		}

		// A ServerConn multiplexes several channels, which must
		// themselves be Accepted.
		go ServeSSHConnection(sConn)
	}

	glog.Fatal("How did I get here?")
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"strings"
	"time"
	"net/http"

	"github.com/CiscoCloud/mesos-consul/config"
	"github.com/CiscoCloud/mesos-consul/consul"
	"github.com/CiscoCloud/mesos-consul/mesos"

	flag "github.com/ogier/pflag"
	log "github.com/sirupsen/logrus"
)

const Name = "mesos-consul"
const Version = "0.3"

func main() {
	c, err := parseFlags(os.Args[1:])
	if err != nil {
		log.Fatal(err)
	}

	if c.Healthcheck {
		go StartHealthcheckService(c)
	}

	log.Info("Using zookeeper: ", c.Zk)
	leader := mesos.New(c)

	ticker := time.NewTicker(c.Refresh)
	leader.Refresh()
	for _ = range ticker.C {
		leader.Refresh()
	}
}

func StartHealthcheckService(c *config.Config) {
	http.HandleFunc("/health", HealthHandler)
	log.Fatal(http.ListenAndServe(fmt.Sprintf("%s:%s", c.HealthcheckIp, c.HealthcheckPort), nil))
}

func HealthHandler(w http.ResponseWriter, r *http.Request) {
  fmt.Fprintln(w, "OK")
}

func parseFlags(args []string) (*config.Config, error) {
	var doHelp bool
	var doVersion bool
	var c = config.DefaultConfig()

	flags := flag.NewFlagSet("github.com/CiscoCloud/mesos-consul", flag.ContinueOnError)
	flags.Usage = func() {
		fmt.Println(Help())
	}

	flags.BoolVar(&doHelp, "help", false, "")
	flags.BoolVar(&doVersion, "version", false, "")
	flags.StringVar(&c.LogLevel, "log-level", "WARN", "")
	flags.DurationVar(&c.Refresh, "refresh", time.Minute, "")
	flags.StringVar(&c.Zk, "zk", "zk://127.0.0.1:2181/mesos", "")
	flags.StringVar(&c.MesosIpOrder, "mesos-ip-order", "netinfo,mesos,host", "")
	flags.BoolVar(&c.Healthcheck, "healthcheck", false, "")
	flags.StringVar(&c.HealthcheckIp, "healthcheck-ip", "127.0.0.1", "")
	flags.StringVar(&c.HealthcheckPort, "healthcheck-port", "24476", "")

	consul.AddCmdFlags(flags)

	if err := flags.Parse(args); err != nil {
		return nil, err
	}

	args = flags.Args()
	if len(args) > 0 {
		return nil, fmt.Errorf("extra argument(s): %q", args)
	}

	if doVersion {
		fmt.Printf("%s v%s\n", Name, Version)
		os.Exit(0)
	}
	if doHelp {
		flags.Usage()
		os.Exit(0)
	}

	l, err := log.ParseLevel(strings.ToLower(c.LogLevel))
	if err != nil {
		log.SetLevel(log.WarnLevel)
		log.Warnf("Invalid log level '%v'. Setting to WARN", c.LogLevel)
	} else {
		log.SetLevel(l)
	}

	return c, nil
}

func Help() string {
	helpText := `
Usage: github.com/CiscoCloud/mesos-consul [options]

Options:

  --version 			Print mesos-consul version
  --log-level=<log_level>	Set the Logging level to one of [ "DEBUG", "INFO", "WARN", "ERROR" ]
				(default "WARN")
  --refresh=<time>		Set the Mesos refresh rate (default 1m)
  --zk=<address>		Zookeeper path to Mesos (default zk://127.0.0.1:2181/mesos)
  --healthcheck 		Enables a http endpoint for health checks. When this
				flag is enabled, serves a service health status on 127.0.0.1:24476 (default not enabled)
  --healthcheck-ip=<ip> 	Health check interface ip (default 127.0.0.1)
  --healthcheck-port=<port>	Health check service port (default 24476)
  --mesos-ip-order		Comma separated list to control the order in
				which github.com/CiscoCloud/mesos-consul searches for the task IP
				address. Valid options are 'netinfo', 'mesos', 'docker' and 'host'
				(default netinfo,mesos,host)
` + consul.Help()

	return strings.TrimSpace(helpText)
}
<commit_msg>Fixed artifact name<commit_after>package main

import (
	"fmt"
	"os"
	"strings"
	"time"
	"net/http"

	"github.com/CiscoCloud/mesos-consul/config"
	"github.com/CiscoCloud/mesos-consul/consul"
	"github.com/CiscoCloud/mesos-consul/mesos"

	flag "github.com/ogier/pflag"
	log "github.com/sirupsen/logrus"
)

const Name = "mesos-consul"
const Version = "0.3"

func main() {
	c, err := parseFlags(os.Args[1:])
	if err != nil {
		log.Fatal(err)
	}

	if c.Healthcheck {
		go StartHealthcheckService(c)
	}

	log.Info("Using zookeeper: ", c.Zk)
	leader := mesos.New(c)

	ticker := time.NewTicker(c.Refresh)
	leader.Refresh()
	for _ = range ticker.C {
		leader.Refresh()
	}
}

func StartHealthcheckService(c *config.Config) {
	http.HandleFunc("/health", HealthHandler)
	log.Fatal(http.ListenAndServe(fmt.Sprintf("%s:%s", c.HealthcheckIp, c.HealthcheckPort), nil))
}

func HealthHandler(w http.ResponseWriter, r *http.Request) {
  fmt.Fprintln(w, "OK")
}

func parseFlags(args []string) (*config.Config, error) {
	var doHelp bool
	var doVersion bool
	var c = config.DefaultConfig()

	flags := flag.NewFlagSet("mesos-consul", flag.ContinueOnError)
	flags.Usage = func() {
		fmt.Println(Help())
	}

	flags.BoolVar(&doHelp, "help", false, "")
	flags.BoolVar(&doVersion, "version", false, "")
	flags.StringVar(&c.LogLevel, "log-level", "WARN", "")
	flags.DurationVar(&c.Refresh, "refresh", time.Minute, "")
	flags.StringVar(&c.Zk, "zk", "zk://127.0.0.1:2181/mesos", "")
	flags.StringVar(&c.MesosIpOrder, "mesos-ip-order", "netinfo,mesos,host", "")
	flags.BoolVar(&c.Healthcheck, "healthcheck", false, "")
	flags.StringVar(&c.HealthcheckIp, "healthcheck-ip", "127.0.0.1", "")
	flags.StringVar(&c.HealthcheckPort, "healthcheck-port", "24476", "")

	consul.AddCmdFlags(flags)

	if err := flags.Parse(args); err != nil {
		return nil, err
	}

	args = flags.Args()
	if len(args) > 0 {
		return nil, fmt.Errorf("extra argument(s): %q", args)
	}

	if doVersion {
		fmt.Printf("%s v%s\n", Name, Version)
		os.Exit(0)
	}
	if doHelp {
		flags.Usage()
		os.Exit(0)
	}

	l, err := log.ParseLevel(strings.ToLower(c.LogLevel))
	if err != nil {
		log.SetLevel(log.WarnLevel)
		log.Warnf("Invalid log level '%v'. Setting to WARN", c.LogLevel)
	} else {
		log.SetLevel(l)
	}

	return c, nil
}

func Help() string {
	helpText := `
Usage: mesos-consul [options]

Options:

  --version 			Print mesos-consul version
  --log-level=<log_level>	Set the Logging level to one of [ "DEBUG", "INFO", "WARN", "ERROR" ]
				(default "WARN")
  --refresh=<time>		Set the Mesos refresh rate (default 1m)
  --zk=<address>		Zookeeper path to Mesos (default zk://127.0.0.1:2181/mesos)
  --healthcheck 		Enables a http endpoint for health checks. When this
				flag is enabled, serves a service health status on 127.0.0.1:24476 (default not enabled)
  --healthcheck-ip=<ip> 	Health check interface ip (default 127.0.0.1)
  --healthcheck-port=<port>	Health check service port (default 24476)
  --mesos-ip-order		Comma separated list to control the order in
				which github.com/CiscoCloud/mesos-consul searches for the task IP
				address. Valid options are 'netinfo', 'mesos', 'docker' and 'host'
				(default netinfo,mesos,host)
` + consul.Help()

	return strings.TrimSpace(helpText)
}
<|endoftext|>


<commit_before>package main

import (
	crand "crypto/rand"
	"encoding/binary"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"math/rand"
	"os/exec"
	"path/filepath"
	"strings"
	"time"
	"unicode"

	"github.com/reusee/gobfile"
)

var (
	pt = fmt.Printf
)

func init() {
	var seed int64
	binary.Read(crand.Reader, binary.LittleEndian, &seed)
	rand.Seed(seed)

	flag.Parse()
}

func main() {
	dir := "."
	if args := flag.Args(); len(args) > 0 {
		dir = args[0]
	}

	// load words
	type Word struct {
		Text string
		Desc string
	}
	words := map[string]Word{}
	content, err := ioutil.ReadFile(filepath.Join(dir, "words"))
	checkErr("read words file", err)
	for _, line := range strings.Split(string(content), "\n") {
		if len(line) == 0 {
			continue
		}
		index := 0
		runes := []rune(line)
		for i, r := range runes {
			if unicode.IsSpace(r) {
				index = i
				break
			}
		}
		text := string(runes[:index])
		desc := string(runes[index+1:])
		if len(text) == 0 || len(desc) == 0 {
			log.Fatalf("invalid word entry %s\n", line)
		}
		words[text] = Word{
			Text: text,
			Desc: desc,
		}
	}

	// data file
	dataFilePath := filepath.Join(dir, "data")
	dataFileLockPath := filepath.Join(dir, ".data.lock")
	type HistoryEntry struct {
		Time time.Time
		What string
	}
	type Practice struct {
		Type string
		Text string
	}
	data := struct {
		History map[Practice][]HistoryEntry
	}{
		History: make(map[Practice][]HistoryEntry),
	}
	dataFile, err := gobfile.New(&data, dataFilePath, gobfile.NewFileLocker(dataFileLockPath))
	checkErr("open data file", err)
	defer dataFile.Close()
	defer dataFile.Save()

	// check new entry
	for _, word := range words {
		for _, t := range []string{"audio", "text", "usage"} {
			practice := Practice{
				Type: t,
				Text: word.Text,
			}
			if _, ok := data.History[practice]; !ok {
				pt("new practice: %s %s\n", t, word.Text)
				data.History[practice] = append(data.History[practice], HistoryEntry{
					Time: time.Now(),
					What: "ok",
				})
			}
		}
	}
	dataFile.Save()

	// review functions
	audioReview := func(word Word) bool {
		var reply string
		retry := 1
	play:
		pt("playing audio\n")
		err := exec.Command("mpv", filepath.Join(dir, fmt.Sprintf("%s.mp3", word.Text))).Run()
		checkErr("play audio", err)
	ask1:
		pt("'j' to show text, 'r' to replay\n")
		fmt.Scanf("%s", &reply)
		switch reply {
		case "j":
			pt("%s\n", word.Text)
		ask2:
			pt("'y' to level up, 'n' to keep\n")
			fmt.Scanf("%s", &reply)
			switch reply {
			case "y":
				return true
			case "n":
				return false
			default:
				goto ask2
			}
		case "r":
			if retry > 0 {
				retry--
				goto play
			} else {
				pt("no more replay\n")
				goto ask1
			}
		default:
			goto ask1
		}
		return false
	}

	textReview := func(word Word) bool {
		pt("showing text\n")
		pt("%s\n", word.Text)
	ask2:
		pt("'j' to play audio\n")
		var reply string
		fmt.Scanf("%s\n", &reply)
		switch reply {
		case "j":
			pt("playing audio\n")
			err := exec.Command("mpv", filepath.Join(dir, fmt.Sprintf("%s.mp3", word.Text))).Run()
			checkErr("play audio", err)
		ask:
			pt("'y' to level up, 'n' to keep\n")
			fmt.Scanf("%s\n", &reply)
			switch reply {
			case "y":
				return true
			case "n":
				return false
			default:
				goto ask
			}
		default:
			goto ask2
		}
		return false
	}

	usageReview := func(word Word) bool {
		pt("showing usage\n")
		pt("%s\n", word.Desc)
	ask:
		pt("'j' to show answer\n")
		var reply string
		fmt.Scanf("%s\n", &reply)
		switch reply {
		case "j":
			pt("playing audio\n")
			err := exec.Command("mpv", filepath.Join(dir, fmt.Sprintf("%s.mp3", word.Text))).Run()
			checkErr("play audio", err)
			pt("%s\n", word.Text)
		ask2:
			pt("'y' to level up, 'n' to keep\n")
			fmt.Scanf("%s\n", &reply)
			switch reply {
			case "y":
				return true
			case "n":
				return false
			default:
				goto ask2
			}
		default:
			goto ask
		}
		return false
	}

	reviewFuncs := map[string]func(Word) bool{
		"audio": audioReview,
		"text":  textReview,
		"usage": usageReview,
	}

	practicedWords := map[string]struct{}{}
	for practice, history := range data.History {
		// calculate fade and max
		last := history[len(history)-1]
		fade := time.Now().Sub(last.Time)
		var max time.Duration
		for i := 1; i < len(history); i++ {
			if history[i].What != "ok" {
				continue
			}
			if d := history[i].Time.Sub(history[i-1].Time); d > max {
				max = d
			}
		}
		// filter
		if fade < max {
			continue
		}
		if fade < time.Second*30 {
			continue
		}
		if _, ok := practicedWords[practice.Text]; ok {
			continue
		}
		pt("practice %s fade %v max %v\n", practice.Type, fade, max)
		// practice
		practicedWords[practice.Text] = struct{}{}
		var what string
		if reviewFuncs[practice.Type](words[practice.Text]) {
			what = "ok"
		} else {
			what = "fail"
		}
		data.History[practice] = append(data.History[practice], HistoryEntry{
			What: what,
			Time: time.Now(),
		})
		dataFile.Save()
	}
}

func checkErr(desc string, err error) {
	if err != nil {
		log.Fatalf("%s error %v", desc, err)
	}
}
<commit_msg>minor cleanups<commit_after>package main

import (
	crand "crypto/rand"
	"encoding/binary"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"math/rand"
	"os/exec"
	"path/filepath"
	"strings"
	"time"
	"unicode"

	"github.com/reusee/gobfile"
)

var (
	pt = fmt.Printf
)

func init() {
	var seed int64
	binary.Read(crand.Reader, binary.LittleEndian, &seed)
	rand.Seed(seed)

	flag.Parse()
}

func main() {
	dir := "."
	if args := flag.Args(); len(args) > 0 {
		dir = args[0]
	}

	// load words
	type Word struct {
		Text string
		Desc string
	}
	words := map[string]Word{}
	content, err := ioutil.ReadFile(filepath.Join(dir, "words"))
	checkErr("read words file", err)
	for _, line := range strings.Split(string(content), "\n") {
		if len(line) == 0 {
			continue
		}
		index := 0
		runes := []rune(line)
		for i, r := range runes {
			if unicode.IsSpace(r) {
				index = i
				break
			}
		}
		text := string(runes[:index])
		desc := string(runes[index+1:])
		if len(text) == 0 || len(desc) == 0 {
			log.Fatalf("invalid word entry %s\n", line)
		}
		words[text] = Word{
			Text: text,
			Desc: desc,
		}
	}

	// data file
	dataFilePath := filepath.Join(dir, "data")
	dataFileLockPath := filepath.Join(dir, ".data.lock")
	type HistoryEntry struct {
		Time time.Time
		What string
	}
	type Practice struct {
		Type string
		Text string
	}
	data := struct {
		History map[Practice][]HistoryEntry
	}{
		History: make(map[Practice][]HistoryEntry),
	}
	dataFile, err := gobfile.New(&data, dataFilePath, gobfile.NewFileLocker(dataFileLockPath))
	checkErr("open data file", err)
	defer dataFile.Close()
	defer dataFile.Save()

	// check new entry
	for _, word := range words {
		for _, t := range []string{"audio", "text", "usage"} {
			practice := Practice{
				Type: t,
				Text: word.Text,
			}
			if _, ok := data.History[practice]; !ok {
				pt("new practice: %s %s\n", t, word.Text)
				data.History[practice] = append(data.History[practice], HistoryEntry{
					Time: time.Now(),
					What: "ok",
				})
			}
		}
	}
	dataFile.Save()

	// review functions
	audioReview := func(word Word) bool {
		var reply string
		retry := 1
	play:
		pt("playing audio\n")
		err := exec.Command("mpv", filepath.Join(dir, fmt.Sprintf("%s.mp3", word.Text))).Run()
		checkErr("play audio", err)
	ask1:
		pt("'j' to show text, 'r' to replay\n")
		fmt.Scanf("%s", &reply)
		switch reply {
		case "j":
			pt("%s\n", word.Text)
		ask2:
			pt("'y' to level up, 'n' to keep\n")
			fmt.Scanf("%s", &reply)
			switch reply {
			case "y":
				return true
			case "n":
				return false
			default:
				goto ask2
			}
		case "r":
			if retry > 0 {
				retry--
				goto play
			} else {
				pt("no more replay\n")
				goto ask1
			}
		default:
			goto ask1
		}
		return false
	}

	textReview := func(word Word) bool {
		pt("showing text\n")
		pt("%s\n", word.Text)
	ask2:
		pt("'j' to play audio\n")
		var reply string
		fmt.Scanf("%s\n", &reply)
		switch reply {
		case "j":
			pt("playing audio\n")
			err := exec.Command("mpv", filepath.Join(dir, fmt.Sprintf("%s.mp3", word.Text))).Run()
			checkErr("play audio", err)
		ask:
			pt("'y' to level up, 'n' to keep\n")
			fmt.Scanf("%s\n", &reply)
			switch reply {
			case "y":
				return true
			case "n":
				return false
			default:
				goto ask
			}
		default:
			goto ask2
		}
		return false
	}

	usageReview := func(word Word) bool {
		pt("showing usage\n")
		pt("%s\n", word.Desc)
	ask:
		pt("'j' to show answer\n")
		var reply string
		fmt.Scanf("%s\n", &reply)
		switch reply {
		case "j":
			pt("playing audio\n")
			err := exec.Command("mpv", filepath.Join(dir, fmt.Sprintf("%s.mp3", word.Text))).Run()
			checkErr("play audio", err)
			pt("%s\n", word.Text)
		ask2:
			pt("'y' to level up, 'n' to keep\n")
			fmt.Scanf("%s\n", &reply)
			switch reply {
			case "y":
				return true
			case "n":
				return false
			default:
				goto ask2
			}
		default:
			goto ask
		}
		return false
	}

	reviewFuncs := map[string]func(Word) bool{
		"audio": audioReview,
		"text":  textReview,
		"usage": usageReview,
	}

	practicedWords := map[string]struct{}{}
	for practice, history := range data.History {
		// calculate fade and max
		last := history[len(history)-1]
		fade := time.Now().Sub(last.Time)
		var max time.Duration
		for i := 1; i < len(history); i++ {
			if history[i].What != "ok" {
				continue
			}
			if d := history[i].Time.Sub(history[i-1].Time); d > max {
				max = d
			}
		}
		// filter
		if fade < max {
			continue
		}
		if fade < time.Second*30 { // skip newly added
			continue
		}
		if _, ok := practicedWords[practice.Text]; ok {
			continue
		}
		practicedWords[practice.Text] = struct{}{}
		pt("practice %s fade %v max %v\n", practice.Type, fade, max)
		// practice
		var what string
		if reviewFuncs[practice.Type](words[practice.Text]) {
			what = "ok"
		} else {
			what = "fail"
		}
		data.History[practice] = append(data.History[practice], HistoryEntry{
			What: what,
			Time: time.Now(),
		})
		dataFile.Save()
	}
}

func checkErr(desc string, err error) {
	if err != nil {
		log.Fatalf("%s error %v", desc, err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"ponydownloader/settings"
	"strconv"

	"github.com/vaughan0/go-ini"
)

//	default variables
var (
	QDEPTH    int64       = 20    //Depth of the queue buffer - how many images are enqueued
	IMGDIR    string      = "img" //Default download directory
	TAG       string      = ""    //Default tag string is empty, it should be extracted from command line and only command line
	STARTPAGE int         = 1     //Default start page, derpiboo.ru 1-indexed
	STOPPAGE  int         = 0     //Default stop page, would stop parsing json when stop page is reached or site reaches the end of search
	elog      *log.Logger         //The logger for errors
)

type Image struct {
	imgid    int
	url      string
	filename string
	//	hash     string
}

func main() {

	fmt.Println("Derpiboo.ru Downloader version 0.2.0")

	elog, logfile := settings.SetLog() //setting up logging of errors

	defer logfile.Close() //Almost forgot. Always close the file in the end.

	config, err := ini.LoadFile("config.ini") // Loading default config file and checking for various errors.

	if os.IsNotExist(err) {
		elog.Fatalln("Config.ini does not exist, create it") //We can not live without config. We could, in theory, but writing default config if none exist can wait
	}

	if err != nil {
		elog.Panicln(err) //Oh, something is broken beyond my understanding. Sorry.
	}

	//Getting stuff from config, overwriting hardwired defaults when needed

	key, ok := config.Get("main", "key")

	if !ok || key == "" {
		elog.Println("'key' variable missing from 'main' section. It is vital for server-side filtering") //Empty key or key does not exist. Derpibooru works with this, but default image filter filters too much. Use key to set your own!
	}

	Q_temp, _ := config.Get("main", "workers")

	if Q_temp != "" {
		QDEPTH, err = strconv.ParseInt(Q_temp, 10, 0)

		if err != nil {
			elog.Fatalln("Wrong configuration: Depth of the buffer queue is not a number")

		}
	}

	ID_temp, _ := config.Get("main", "downdir")

	if ID_temp != "" {
		IMGDIR = ID_temp
	}

	//Here we are parsing all the flags. Command line argument hold priority to config. Except for 'key'. API-key is config-only

	flag.StringVar(&TAG, "t", TAG, "Tags to download")
	flag.IntVar(&STARTPAGE, "p", STARTPAGE, "Starting page for search")
	flag.IntVar(&STOPPAGE, "sp", STOPPAGE, "Stopping page for search, 0 - parse all all search pages")

	flag.Parse()

	if flag.NArg() == 0 && TAG == "" { //If no arguments after flags and empty/unchanged tag, what we should download? Sane end of line.
		log.SetPrefix("Done at ") //We can not do this with elog!
		log.Println("Nothing to download, bye!")
		os.Exit(0)
	}

	//Creating directory for downloads if it does not yet exist
	if err := os.MkdirAll(IMGDIR, 0644); err != nil { //Execute? No need to execute any image. Also, all those other users can not do anything beyond enjoying our images.
		elog.Fatalln(err) //We can not create folder for images, end of line.
	}

	//	Creating channels to pass info to downloader and to signal job well done
	imgdat := make(chan Image, QDEPTH) //Better leave default queue depth. Experiment shown that depth about 20 provides optimal perfomance on my system
	done := make(chan bool)

	if TAG == "" { //Because we can put imgid with flags. Why not?

		//	Checking argument for being a number and then getting image data

		imgid := flag.Arg(0) //0-indexed, unlike os.Args. os.Args[0] is path to program. It needs to be used later, when we are searching for what directory we are writing in
		_, err = strconv.Atoi(imgid)

		if err != nil {
			elog.Fatalln("Wrong input: can not parse", imgid, "as a number")
		}

		log.Println("Processing image No", imgid)

		go ParseImg(imgdat, imgid, key) // Sending imgid to parser. Here validity is our problem

	} else {

		//	and here we send tags to getter/parser. Validity is server problem, mostly

		log.Println("Processing tags", TAG)
		go ParseTag(imgdat, TAG, key)
	}

	log.Println("Starting worker") //It would be funny if worker goroutine does not start
	go DlImg(imgdat, done)

	<-done
	log.SetPrefix("Done at ")
	log.Println("Finised")
	//And we are done here! Hooray!
	return
}

func ParseImg(imgchan chan<- Image, imgid string, key string) {

	source := "http://derpiboo.ru/images/" + imgid + ".json?nofav=&nocomments="
	if key != "" {
		source = source + "&key=" + key
	}

	fmt.Println("Getting image info at:", source)

	resp, err := http.Get(source) //Getting our nice http response. Needs checking for 404 and other responses that are... less expected
	if err != nil {
		elog.Println(err)
		return
	}

	defer resp.Body.Close() //and not forgetting to close it when it's done

	var dat map[string]interface{}

	body, err := ioutil.ReadAll(resp.Body) //stolen from official documentation
	if err != nil {
		elog.Println(err)
		return
	}

	if err := json.Unmarshal(body, &dat); //transforming json into native map

	err != nil {
		elog.Println(err)
		return
	}

	InfoToChannel(dat, imgchan)

	close(imgchan) //closing channel, we are done here

	return
}

func DlImg(imgchan <-chan Image, done chan bool) {

	fmt.Println("Worker started; reading channel") //nice notification that we are not forgotten

	for {

		imgdata, more := <-imgchan

		if more { //checking that there is an image in channel

			if imgdata.filename == "" {
				elog.Println("Empty filename. Oops?") //something somewhere had gone wrong, going to the next image
			} else {

				fmt.Println("Saving as", imgdata.filename)

				func() { // to not hold all the files open when there is no need

					output, err := os.Create(IMGDIR + string(os.PathSeparator) + imgdata.filename) //And now, THE FILE!
					if err != err {
						elog.Println("Error when creating file for image" + strconv.Itoa(imgdata.imgid))
						elog.Println(err)
						return
					}
					defer output.Close() //Not forgetting to deal with it after completing download

					response, err := http.Get(imgdata.url)
					if err != nil {
						elog.Println("Error when getting image", imgdata.imgid)
						elog.Println(err)
						return
					}
					defer response.Body.Close() //Same, we shall not listen to the void when we finished getting image

					io.Copy(output, response.Body) //	Writing things we got from Derpibooru into the file and into hasher

				}()
			}

			//fmt.Println("\n", hex.EncodeToString(hash.Sum(nil)), "\n", imgdata.hash )

		} else {
			done <- true //well, there is no images in channel, it means we got them all, so synchronization is kicking in and ending the process
			break        //Just in case, so it would not stupidly die when program finishes - it will die smartly

		}
	}
}

func ParseTag(imgchan chan<- Image, tag string, key string) {

	source := "http://derpiboo.ru/search.json?nofav=&nocomments=" //yay hardwiring url strings!

	if key != "" {
		source = source + "&key=" + key
	}

	fmt.Println("Searching as", source+"&q="+tag)
	var working bool = true
	i := STARTPAGE
	for working {
		func() {
			fmt.Println("Searching page", i)
			resp, err := http.Get(source + "&q=" + tag + "&page=" + strconv.Itoa(i)) //Getting our nice http response. Needs checking for 404 and other responses that are... less expected
			defer resp.Body.Close()                                                  //and not forgetting to close it when it's done. And before we panic and die horribly.
			if err != nil {
				elog.Println("Error while getting search page", i)
				elog.Println(err)
				return
			}

			var dats []map[string]interface{} //Because we got array incoming instead of single object, we using an slive of maps!

			//fmt.Println(resp)

			body, err := ioutil.ReadAll(resp.Body) //stolen from official documentation
			if err != nil {
				elog.Println("Error while reading search page", i)
				elog.Println(err)
				return
			}

			//fmt.Println(body)

			if err := json.Unmarshal(body, &dats); //transforming json into native slice of maps

			err != nil {
				elog.Println("Error while parsing search page", i)
				elog.Println(err)
				return

			}

			if len(dats) == 0 {
				fmt.Println("Pages are over")
				working = false
				return
			} //exit due to finishing all pages

			for _, dat := range dats {
				InfoToChannel(dat, imgchan)
			}
			if i == STOPPAGE {
				working = false
				return
			}
			i++

		}()
	}

	close(imgchan)
}

func InfoToChannel(dat map[string]interface{}, imgchan chan<- Image) {

	var imgdata Image

	imgdata.url = "http:" + dat["image"].(string)
	//	imgdata.hash = dat["sha512_hash"].(string)
	imgdata.filename = (strconv.FormatFloat(dat["id_number"].(float64), 'f', -1, 64) + "." + dat["file_name"].(string) + "." + dat["original_format"].(string))
	imgdata.imgid = int(dat["id_number"].(float64))

	//	for troubleshooting - possibly debug flag?
	//	fmt.Println(dat)
	//	fmt.Println(imgdata.url)
	//	fmt.Println(imgdata.hash)
	//	fmt.Println(imgdata.filename)

	imgchan <- imgdata
}
<commit_msg>Added flags to enable primitive image filtration<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"ponydownloader/settings"
	"strconv"

	"github.com/vaughan0/go-ini"
)

//	default variables
var (
	QDEPTH    int64       = 20    //Depth of the queue buffer - how many images are enqueued
	IMGDIR    string      = "img" //Default download directory
	TAG       string      = ""    //Default tag string is empty, it should be extracted from command line and only command line
	STARTPAGE int         = 1     //Default start page, derpiboo.ru 1-indexed
	STOPPAGE  int         = 0     //Default stop page, would stop parsing json when stop page is reached or site reaches the end of search
	elog      *log.Logger         //The logger for errors
	SCRFILTER int				  //So we can ignore things with limited
	FILTERFLAG bool = false		  //Gah, not sure how to make it better.
)

type Image struct {
	imgid    int
	url      string
	filename string
	//	hash     string
}

func main() {

	fmt.Println("Derpiboo.ru Downloader version 0.2.0")

	elog, logfile := settings.SetLog() //setting up logging of errors

	defer logfile.Close() //Almost forgot. Always close the file in the end.

	config, err := ini.LoadFile("config.ini") // Loading default config file and checking for various errors.

	if os.IsNotExist(err) {
		elog.Fatalln("Config.ini does not exist, create it") //We can not live without config. We could, in theory, but writing default config if none exist can wait
	}

	if err != nil {
		elog.Panicln(err) //Oh, something is broken beyond my understanding. Sorry.
	}

	//Getting stuff from config, overwriting hardwired defaults when needed

	key, ok := config.Get("main", "key")

	if !ok || key == "" {
		elog.Println("'key' variable missing from 'main' section. It is vital for server-side filtering") //Empty key or key does not exist. Derpibooru works with this, but default image filter filters too much. Use key to set your own!
	}

	Q_temp, _ := config.Get("main", "workers")

	if Q_temp != "" {
		QDEPTH, err = strconv.ParseInt(Q_temp, 10, 0)

		if err != nil {
			elog.Fatalln("Wrong configuration: Depth of the buffer queue is not a number")

		}
	}

	ID_temp, _ := config.Get("main", "downdir")

	if ID_temp != "" {
		IMGDIR = ID_temp
	}

	//Here we are parsing all the flags. Command line argument hold priority to config. Except for 'key'. API-key is config-only

	flag.StringVar(&TAG, "t", TAG, "Tags to download")
	flag.IntVar(&STARTPAGE, "p", STARTPAGE, "Starting page for search")
	flag.IntVar(&STOPPAGE, "sp", STOPPAGE, "Stopping page for search, 0 - parse all all search pages")
	flag.IntVar(&SCRFILTER, "fav", SCRFILTER, "Minimal score of image for it to be downloaded")
	flag.BoolVar(&FILTERFLAG, "filter", FILTERFLAG, "If set to true, enables client-side filtration of downloaded images")

	flag.Parse()

	if flag.NArg() == 0 && TAG == "" { //If no arguments after flags and empty/unchanged tag, what we should download? Sane end of line.
		log.SetPrefix("Done at ") //We can not do this with elog!
		log.Println("Nothing to download, bye!")
		os.Exit(0)
	}

	//Creating directory for downloads if it does not yet exist
	if err := os.MkdirAll(IMGDIR, 0644); err != nil { //Execute? No need to execute any image. Also, all those other users can not do anything beyond enjoying our images.
		elog.Fatalln(err) //We can not create folder for images, end of line.
	}

	//	Creating channels to pass info to downloader and to signal job well done
	imgdat := make(chan Image, QDEPTH) //Better leave default queue depth. Experiment shown that depth about 20 provides optimal perfomance on my system
	done := make(chan bool)

	if TAG == "" { //Because we can put imgid with flags. Why not?

		//	Checking argument for being a number and then getting image data

		imgid := flag.Arg(0) //0-indexed, unlike os.Args. os.Args[0] is path to program. It needs to be used later, when we are searching for what directory we are writing in
		_, err = strconv.Atoi(imgid)

		if err != nil {
			elog.Fatalln("Wrong input: can not parse", imgid, "as a number")
		}

		log.Println("Processing image No", imgid)

		go ParseImg(imgdat, imgid, key) // Sending imgid to parser. Here validity is our problem

	} else {

		//	and here we send tags to getter/parser. Validity is server problem, mostly

		log.Println("Processing tags", TAG)
		go ParseTag(imgdat, TAG, key)
	}

	log.Println("Starting worker") //It would be funny if worker goroutine does not start
	go DlImg(imgdat, done)

	<-done
	log.SetPrefix("Done at ")
	log.Println("Finised")
	//And we are done here! Hooray!
	return
}

func ParseImg(imgchan chan<- Image, imgid string, key string) {

	source := "http://derpiboo.ru/images/" + imgid + ".json?nofav=&nocomments="
	if key != "" {
		source = source + "&key=" + key
	}

	fmt.Println("Getting image info at:", source)

	resp, err := http.Get(source) //Getting our nice http response. Needs checking for 404 and other responses that are... less expected
	if err != nil {
		elog.Println(err)
		return
	}

	defer resp.Body.Close() //and not forgetting to close it when it's done

	var dat map[string]interface{}

	body, err := ioutil.ReadAll(resp.Body) //stolen from official documentation
	if err != nil {
		elog.Println(err)
		return
	}

	if err := json.Unmarshal(body, &dat); //transforming json into native map

	err != nil {
		elog.Println(err)
		return
	}

	InfoToChannel(dat, imgchan)

	close(imgchan) //closing channel, we are done here

	return
}

func DlImg(imgchan <-chan Image, done chan bool) {

	fmt.Println("Worker started; reading channel") //nice notification that we are not forgotten

	for {

		imgdata, more := <-imgchan

		if more { //checking that there is an image in channel

			if imgdata.filename == "" {
				elog.Println("Empty filename. Oops?") //something somewhere had gone wrong, going to the next image
			} else {

				fmt.Println("Saving as", imgdata.filename)

				func() { // to not hold all the files open when there is no need

					output, err := os.Create(IMGDIR + string(os.PathSeparator) + imgdata.filename) //And now, THE FILE!
					if err != err {
						elog.Println("Error when creating file for image" + strconv.Itoa(imgdata.imgid))
						elog.Println(err)
						return
					}
					defer output.Close() //Not forgetting to deal with it after completing download

					response, err := http.Get(imgdata.url)
					if err != nil {
						elog.Println("Error when getting image", imgdata.imgid)
						elog.Println(err)
						return
					}
					defer response.Body.Close() //Same, we shall not listen to the void when we finished getting image

					io.Copy(output, response.Body) //	Writing things we got from Derpibooru into the file and into hasher

				}()
			}

			//fmt.Println("\n", hex.EncodeToString(hash.Sum(nil)), "\n", imgdata.hash )

		} else {
			done <- true //well, there is no images in channel, it means we got them all, so synchronization is kicking in and ending the process
			break        //Just in case, so it would not stupidly die when program finishes - it will die smartly

		}
	}
}

func ParseTag(imgchan chan<- Image, tag string, key string) {

	source := "http://derpiboo.ru/search.json?nofav=&nocomments=" //yay hardwiring url strings!

	if key != "" {
		source = source + "&key=" + key
	}

	fmt.Println("Searching as", source+"&q="+tag)
	var working bool = true
	i := STARTPAGE
	for working {
		func() {
			fmt.Println("Searching page", i)
			resp, err := http.Get(source + "&q=" + tag + "&page=" + strconv.Itoa(i)) //Getting our nice http response. Needs checking for 404 and other responses that are... less expected
			defer resp.Body.Close()                                                  //and not forgetting to close it when it's done. And before we panic and die horribly.
			if err != nil {
				elog.Println("Error while getting search page", i)
				elog.Println(err)
				return
			}

			var dats []map[string]interface{} //Because we got array incoming instead of single object, we using an slive of maps!

			//fmt.Println(resp)

			body, err := ioutil.ReadAll(resp.Body) //stolen from official documentation
			if err != nil {
				elog.Println("Error while reading search page", i)
				elog.Println(err)
				return
			}

			//fmt.Println(body)

			if err := json.Unmarshal(body, &dats); //transforming json into native slice of maps

			err != nil {
				elog.Println("Error while parsing search page", i)
				elog.Println(err)
				return

			}

			if len(dats) == 0 {
				fmt.Println("Pages are over")
				working = false
				return
			} //exit due to finishing all pages

			for _, dat := range dats {
				InfoToChannel(dat, imgchan)
			}
			if i == STOPPAGE {
				working = false
				return
			}
			i++

		}()
	}

	close(imgchan)
}

func InfoToChannel(dat map[string]interface{}, imgchan chan<- Image) {

	var imgdata Image

	imgdata.url = "http:" + dat["image"].(string)
	//	imgdata.hash = dat["sha512_hash"].(string)
	imgdata.filename = (strconv.FormatFloat(dat["id_number"].(float64), 'f', -1, 64) + "." + dat["file_name"].(string) + "." + dat["original_format"].(string))
	imgdata.imgid = int(dat["id_number"].(float64))

	//	for troubleshooting - possibly debug flag?
	//	fmt.Println(dat)
	//	fmt.Println(imgdata.url)
	//	fmt.Println(imgdata.hash)
	//	fmt.Println(imgdata.filename)

	imgchan <- imgdata
}
<|endoftext|>


<commit_before>package server

import (
	"bufio"
	"bytes"
	"echo/bpool"
	"echo/libsodium"
	"echo/netstring"
	"io"
	"log"
	"net"
	"os"
	"time"
)

var homeip string = ":0"

func tcpListener(addr string) (*net.TCPListener, error) {
	tcpaddr, err := net.ResolveTCPAddr("tcp", addr)
	if err != nil {
		return nil, err
	}
	return net.ListenTCP("tcp", tcpaddr)
}

func TcpServe(addr string, encryptKey []byte) error {
	tcplistener, err := tcpListener(addr)
	log.SetOutput(os.Stdout)
	if err != nil {
		return err
	}
	bpool := bpool.NewSizedBufferPool(20, 4096)

	for {
		tcpconn, err := tcplistener.AcceptTCP()
		if err != nil {
			log.Printf("AcceptTCP error : %v\n", err)
			continue
		}
		buffer := bpool.Get()

		go handleTCPConn(tcpconn, encryptKey, buffer)
	}
}

func handleTCPConn(tcpconn *net.TCPConn, encryptKey []byte, buffer *bytes.Buffer) {
	defer tcpconn.Close()
	//receiveData := make([]byte, 50)
	var receiveData []byte
	//tcpconn need read all data in 20 second ,otherwise Timeout() will be true
	tcpconn.SetReadDeadline(time.Now().Add(time.Duration(20) * time.Second))
	//receiveDatalen, err := tcpconn.Read(receiveData)
	bufReader := bufio.NewReader(tcpconn)
	for {
		rData, err := bufReader.ReadBytes([]byte(",")[0])
		if err != nil {
			if err == io.EOF {
				log.Printf("TCPConn Read error : %v\n", err)
				return
			}
			buffer.Write(rData)
			continue
		}

		buffer.Write(rData)

		unmarshallData, err := netstring.Unmarshall(buffer.Bytes())
		if err == netstring.ErrNsLenNotEqaulOrgLen {
			continue
		}
		if err != nil {
			log.Printf("netstring unmarshall error : %v\n", err)
			return
		}
		receiveData = unmarshallData

		break
	}

	_, err := libsodium.DecryptData(encryptKey, receiveData)
	if err != nil {
		log.Printf("DecryptData error : %v\n", err)
		return
	}

	tcpconn.SetWriteDeadline(time.Now().Add(time.Duration(20) * time.Second))
	_, err = tcpconn.Write(netstring.Marshall([]byte(homeip)))
	if err != nil {
		log.Printf("tcpconn error : %v\n", err)
	}
}

func UdpServe(addr string, key []byte) error {
	udpaddr, err := net.ResolveUDPAddr("udp", addr)
	if err != nil {
		return err
	}

	udpconn, err := net.ListenUDP("udp", udpaddr)
	for {
		if err != nil {
			log.Printf("udpServer error : %v\n", err)
			continue
		}
		handleUDPConn(udpconn, key)
	}
}

func handleUDPConn(udpconn *net.UDPConn, key []byte) {
	receiveData := make([]byte, 50)
	receiveDatalen, addr, err := udpconn.ReadFrom(receiveData)

	if err != nil {
		log.Printf("udp readfrom error : %v\n", err)
		return
	}

	_, err = libsodium.DecryptData(key, receiveData[:receiveDatalen])
	if err != nil {
		log.Printf("DecryptData error : %v\n", err)
		return
	}

	homeip = addr.String()
}
<commit_msg>add netstring,bpool to tcpudp untest unfinish<commit_after>package server

import (
	"bufio"
	"bytes"
	"echo/bpool"
	"echo/libsodium"
	"echo/netstring"
	"io"
	"log"
	"net"
	"os"
	"time"
)

var homeip string = ":0"

func tcpListener(addr string) (*net.TCPListener, error) {
	tcpaddr, err := net.ResolveTCPAddr("tcp", addr)
	if err != nil {
		return nil, err
	}
	return net.ListenTCP("tcp", tcpaddr)
}

func TcpServe(addr string, encryptKey []byte) error {
	tcplistener, err := tcpListener(addr)
	log.SetOutput(os.Stdout)
	if err != nil {
		return err
	}
	bpool := bpool.NewSizedBufferPool(20, 4096)

	for {
		tcpconn, err := tcplistener.AcceptTCP()
		if err != nil {
			log.Printf("AcceptTCP error : %v\n", err)
			continue
		}
		buffer := bpool.Get()

		go handleTCPConn(tcpconn, encryptKey, buffer)
	}
}

func handleTCPConn(tcpconn *net.TCPConn, encryptKey []byte, buffer *bytes.Buffer) {
	defer tcpconn.Close()
	//receiveData := make([]byte, 50)
	var receiveData []byte
	//tcpconn need read all data in 20 second ,otherwise Timeout() will be true
	//tcpconn.SetReadDeadline(time.Now().Add(time.Duration(20) * time.Second))
	//receiveDatalen, err := tcpconn.Read(receiveData)
	bufReader := bufio.NewReader(tcpconn)
	for {
		rData, err := bufReader.ReadBytes([]byte(",")[0])
		if err != nil {
			if err != io.EOF {
				log.Printf("TCPConn Read error : %v\n", err)
				return
			}
			buffer.Write(rData)
			continue
		}

		buffer.Write(rData)

		unmarshallData, err := netstring.Unmarshall(buffer.Bytes())
		if err == netstring.ErrNsLenNotEqaulOrgLen {
			continue
		}
		if err != nil {
			log.Printf("netstring unmarshall error : %v\n", err)
			return
		}
		receiveData = unmarshallData

		break
	}

	_, err := libsodium.DecryptData(encryptKey, receiveData)
	if err != nil {
		log.Printf("DecryptData error : %v\n", err)
		return
	}

	tcpconn.SetWriteDeadline(time.Now().Add(time.Duration(20) * time.Second))
	_, err = tcpconn.Write(netstring.Marshall([]byte(homeip)))
	if err != nil {
		log.Printf("tcpconn error : %v\n", err)
	}
}

func UdpServe(addr string, key []byte) error {
	udpaddr, err := net.ResolveUDPAddr("udp", addr)
	if err != nil {
		return err
	}

	udpconn, err := net.ListenUDP("udp", udpaddr)
	for {
		if err != nil {
			log.Printf("udpServer error : %v\n", err)
			continue
		}
		handleUDPConn(udpconn, key)
	}
}

func handleUDPConn(udpconn *net.UDPConn, key []byte) {
	receiveData := make([]byte, 50)
	receiveDatalen, addr, err := udpconn.ReadFrom(receiveData)

	if err != nil {
		log.Printf("udp readfrom error : %v\n", err)
		return
	}

	_, err = libsodium.DecryptData(key, receiveData[:receiveDatalen])
	if err != nil {
		log.Printf("DecryptData error : %v\n", err)
		return
	}

	homeip = addr.String()
}
<|endoftext|>


<commit_before>// Tote is a CLI application for generating structs which store SQL queries
// as defined by the directory and file structure supplied (default is
// "./sqltote").  Only .sql files are read.
//
// 	Available flags:
// 	--in={dir}          Set the SQL storage directory.
// 	--out={dir}         Set the tote package directory.
// 	--file={filename}   Set the tote file name.
// 	--pkg={package}     Set the tote package name.
// 	--prefix={name}     Set the tote struct prefix.
//
// Normally, this command should be called using go:generate.  The following
// usage will produce a package named "totepkg" within the "totepkg"
// directory:
// 	//go:generate tote -in=resources/sql/tote -out=totepkg
//
// The following usage will add a second file to the "totepkg" package:
// 	//go:generate tote -in=other/sql/tote -out=totepkg -prefix=other -file=other.go
//
// Queries are accessible in this way:
// 	import "vcs-storage.nil/mycurrentproject/totepkg"
//
// 	func main() {
// 		// File originally located at "./resources/sql/tote/user/all.sql"
// 		fmt.Println(totepkg.User.All)
//
// 		// File originally located at "./resources/sql/tote/user/role/many_by_user.sql"
// 		fmt.Println(totepkg.UserRole.ManyByUser)
//
// 		// File originally located at "./other/sql/tote/user/one_by_name.sql"
// 		fmt.Println(totepkg.OtherUser.OneByName)
// 	}
//
// The main caveat seems to be naming collisions which was the primary
// motivation for the prefix flag.  Stay aware and problems can be avoided.
//
// This package started as a fork of smotes/purse.
package main

import (
	"bytes"
	"errors"
	"flag"
	"go/format"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"
	"text/template"
	"unicode"
)

type tote struct {
	Items map[string][]item
}

type item struct {
	Name  string
	Query string
}

func newTote(dir, prefix string) (t *tote, err error) {
	t = &tote{Items: make(map[string][]item)}
	if _, err = os.Stat(dir); err != nil {
		return nil, err
	}

	err = filepath.Walk(dir, func(path string, i os.FileInfo, e error) error {
		if e != nil {
			return e
		}
		if !i.IsDir() && filepath.Ext(path) == ".sql" {
			f, err := os.Open(path)
			if err != nil {
				return err
			}
			defer func() {
				if err := f.Close(); err != nil {
					log.Fatalln(err)
				}
			}()

			b, err := ioutil.ReadAll(f)
			if err != nil {
				return err
			}
			t.Items[path2Key(path, dir, prefix)] = append(
				t.Items[path2Key(path, dir, prefix)],
				item{Name: path2Name(path),
					Query: "`" + string(b) + "`",
				},
			)
		}
		return nil
	})

	return t, err
}

func main() {
	const (
		defFile = "sqltote.go"
		defIn   = "sqltote"
		defOut  = "./"
		envar   = "GOPACKAGE"
	)
	var file, in, out, pkg, prefix string

	flag.StringVar(&in, "in", defIn, "directory of the input SQL file(s)")
	flag.StringVar(&out, "out", defOut, "directory of the output source file")
	flag.StringVar(&file, "file", defFile, "name of the output source file")
	flag.StringVar(&pkg, "pkg", "", "name of the go package for the generated source file")
	flag.StringVar(&prefix, "prefix", "", "prefix for struct names")
	flag.Parse()

	if pkg == "" {
		if out == defOut {
			if os.Getenv(envar) == "" {
				log.Fatalln(errors.New("error: package name receiving " +
					"generated source must be provided"))
			}
			pkg = os.Getenv(envar)
		} else {
			pkg = filepath.Base(out)
		}
	}

	f, err := os.Create(filepath.Join(out, file))
	if err != nil {
		log.Fatalln(err)
	}
	defer func() {
		if err := f.Close(); err != nil {
			log.Fatalln(err)
		}
	}()

	t, err := newTote(in, prefix)
	if err != nil {
		log.Fatalln(err)
	}

	ctx := &tmplContext{
		Pkg:   pkg,
		Items: t.Items,
	}

	b := &bytes.Buffer{}
	pt, err := template.New(defIn).Parse(tmpl)
	if err != nil {
		log.Fatalln(err)
	}
	if err = pt.Execute(b, ctx); err != nil {
		log.Fatalln(err)
	}

	fb, err := format.Source(b.Bytes())
	if err != nil {
		log.Fatalln(err)
	}

	if _, err := f.Write(fb); err != nil {
		log.Fatalln(err)
	}
}

func path2Key(p, dir, prefix string) string {
	r := camel(strings.TrimPrefix(filepath.Dir(p), dir), true)
	if prefix != "" {
		r = camel(prefix, true) + r
	}
	if r == "" {
		r = "Root"
	}
	return r
}

func path2Name(p string) string {
	return camel(strings.TrimSuffix(filepath.Base(p), ".sql"), true)
}

func camel(s string, ucFirst bool) string {
	rs := []rune(s)
	l := len(rs)
	buf := make([]rune, 0, l)

	for i := 0; i < l; i++ {
		if unicode.IsLetter(rs[i]) {
			if i == 0 && ucFirst || i > 0 && !unicode.IsLetter(rs[i-1]) {
				buf = append(buf, unicode.ToUpper(rs[i]))
			} else {
				buf = append(buf, rs[i])
			}
		}
		if unicode.IsNumber(rs[i]) {
			buf = append(buf, rs[i])
		}
	}

	return string(buf)
}
<commit_msg>Tentatively added commonInitialisms handling to camel function. Also added gimmicky ability to capitalize sections by separating sections with '-_' instead of just '_'.<commit_after>// Tote is a CLI application for generating structs which store SQL queries
// as defined by the directory and file structure supplied (default is
// "./sqltote").  Only .sql files are read.
//
// 	Available flags:
// 	--in={dir}          Set the SQL storage directory.
// 	--out={dir}         Set the tote package directory.
// 	--file={filename}   Set the tote file name.
// 	--pkg={package}     Set the tote package name.
// 	--prefix={name}     Set the tote struct prefix.
//
// Normally, this command should be called using go:generate.  The following
// usage will produce a package named "totepkg" within the "totepkg"
// directory:
// 	//go:generate tote -in=resources/sql/tote -out=totepkg
//
// The following usage will add a second file to the "totepkg" package:
// 	//go:generate tote -in=other/sql/tote -out=totepkg -prefix=other -file=other.go
//
// Queries are accessible in this way:
// 	import "vcs-storage.nil/mycurrentproject/totepkg"
//
// 	func main() {
// 		// File originally located at "./resources/sql/tote/user/all.sql"
// 		fmt.Println(totepkg.User.All)
//
// 		// File originally located at "./resources/sql/tote/user/role/many_by_user.sql"
// 		fmt.Println(totepkg.UserRole.ManyByUser)
//
// 		// File originally located at "./other/sql/tote/user/one_by_name.sql"
// 		fmt.Println(totepkg.OtherUser.OneByName)
// 	}
//
// The main caveat seems to be naming collisions which was the primary
// motivation for the prefix flag.  Stay aware and problems can be avoided.
//
// This package started as a fork of smotes/purse.
package main

import (
	"bytes"
	"errors"
	"flag"
	"go/format"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"
	"text/template"
	"unicode"
)

type tote struct {
	Items map[string][]item
}

type item struct {
	Name  string
	Query string
}

func newTote(dir, prefix string) (t *tote, err error) {
	t = &tote{Items: make(map[string][]item)}
	if _, err = os.Stat(dir); err != nil {
		return nil, err
	}

	err = filepath.Walk(dir, func(path string, i os.FileInfo, e error) error {
		if e != nil {
			return e
		}
		if !i.IsDir() && filepath.Ext(path) == ".sql" {
			f, err := os.Open(path)
			if err != nil {
				return err
			}
			defer func() {
				if err := f.Close(); err != nil {
					log.Fatalln(err)
				}
			}()

			b, err := ioutil.ReadAll(f)
			if err != nil {
				return err
			}
			t.Items[path2Key(path, dir, prefix)] = append(
				t.Items[path2Key(path, dir, prefix)],
				item{Name: path2Name(path),
					Query: "`" + string(b) + "`",
				},
			)
		}
		return nil
	})

	return t, err
}

func main() {
	const (
		defFile = "sqltote.go"
		defIn   = "sqltote"
		defOut  = "./"
		envar   = "GOPACKAGE"
	)
	var file, in, out, pkg, prefix string

	flag.StringVar(&in, "in", defIn, "directory of the input SQL file(s)")
	flag.StringVar(&out, "out", defOut, "directory of the output source file")
	flag.StringVar(&file, "file", defFile, "name of the output source file")
	flag.StringVar(&pkg, "pkg", "", "name of the go package for the generated source file")
	flag.StringVar(&prefix, "prefix", "", "prefix for struct names")
	flag.Parse()

	if pkg == "" {
		if out == defOut {
			if os.Getenv(envar) == "" {
				log.Fatalln(errors.New("error: package name receiving " +
					"generated source must be provided"))
			}
			pkg = os.Getenv(envar)
		} else {
			pkg = filepath.Base(out)
		}
	}

	f, err := os.Create(filepath.Join(out, file))
	if err != nil {
		log.Fatalln(err)
	}
	defer func() {
		if err := f.Close(); err != nil {
			log.Fatalln(err)
		}
	}()

	t, err := newTote(in, prefix)
	if err != nil {
		log.Fatalln(err)
	}

	ctx := &tmplContext{
		Pkg:   pkg,
		Items: t.Items,
	}

	b := &bytes.Buffer{}
	pt, err := template.New(defIn).Parse(tmpl)
	if err != nil {
		log.Fatalln(err)
	}
	if err = pt.Execute(b, ctx); err != nil {
		log.Fatalln(err)
	}

	fb, err := format.Source(b.Bytes())
	if err != nil {
		log.Fatalln(err)
	}

	if _, err := f.Write(fb); err != nil {
		log.Fatalln(err)
	}
}

func path2Key(p, dir, prefix string) string {
	r := camel(strings.TrimPrefix(filepath.Dir(p), dir), true)
	if prefix != "" {
		r = camel(prefix, true) + r
	}
	if r == "" {
		r = "Root"
	}
	return r
}

func path2Name(p string) string {
	return camel(strings.TrimSuffix(filepath.Base(p), ".sql"), true)
}

func camel(s string, ucFirst bool) string {
	rs := []rune(s)
	l := len(rs)
	buf := make([]rune, 0, l)

	var tmpBufCap int
	for k := range commonInitialisms {
		if len(k) > tmpBufCap {
			tmpBufCap = len(k)
		}
	}

	for i := 0; i < l; i++ {
		if unicode.IsLetter(rs[i]) {
			if i == 0 || !unicode.IsLetter(rs[i-1]) {
				tmpBuf := make([]rune, 0, tmpBufCap)
				for n := i; n < l && n-i < tmpBufCap; n++ {
					tmpBuf = append(tmpBuf, unicode.ToUpper(rs[n]))
					if n < l-1 && !unicode.IsLetter(rs[n+1]) && !unicode.IsDigit(rs[n+1]) {
						break
					}
				}
				if commonInitialisms[string(tmpBuf)] {
					buf = append(buf, tmpBuf...)
					i += len(tmpBuf)
					continue
				}
				if i+len(tmpBuf) < l-2 && rs[i+len(tmpBuf)] == '-' && rs[i+len(tmpBuf)+1] == '_' {
					buf = append(buf, tmpBuf...)
					i += len(tmpBuf)
					continue
				}
			}

			if i == 0 && ucFirst || i > 0 && !unicode.IsLetter(rs[i-1]) {
				buf = append(buf, unicode.ToUpper(rs[i]))
			} else {
				buf = append(buf, rs[i])
			}
		}
		if unicode.IsDigit(rs[i]) {
			buf = append(buf, rs[i])
		}
	}

	return string(buf)
}

// commonInitialisms - github.com/golang/lint/blob/master/lint.go
var commonInitialisms = map[string]bool{
	"API":   true,
	"ASCII": true,
	"CPU":   true,
	"CSS":   true,
	"DNS":   true,
	"EOF":   true,
	"GUID":  true,
	"HTML":  true,
	"HTTP":  true,
	"HTTPS": true,
	"ID":    true,
	"IP":    true,
	"JSON":  true,
	"LHS":   true,
	"QPS":   true,
	"RAM":   true,
	"RHS":   true,
	"RPC":   true,
	"SLA":   true,
	"SMTP":  true,
	"SSH":   true,
	"TCP":   true,
	"TLS":   true,
	"TTL":   true,
	"UDP":   true,
	"UI":    true,
	"UID":   true,
	"UUID":  true,
	"URI":   true,
	"URL":   true,
	"UTF8":  true,
	"VM":    true,
	"XML":   true,
	"XSRF":  true,
	"XSS":   true,
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/tls"
	"log"
	"regexp"

	"github.com/thoj/go-ircevent"
)

func main() {
	CHAN := "#swehack"
	SERVER := "irc.swehack.org:6697"
	NICK := "placebot"

	i := irc.IRC(NICK, NICK)
	i.Version = "666"
	i.UseTLS = true
	i.TLSConfig = &tls.Config{InsecureSkipVerify: true}
	err := i.Connect(SERVER)
	if err != nil {
		log.Fatal(err)
	}

	i.AddCallback("PRIVMSG", func(event *irc.Event) {
		if event.Nick == i.GetNick() {
			return
		}

		re := regexp.MustCompile("!gif ([A-z0-9 \\.\\,\\-]+)")
		gifMatch := re.FindStringSubmatch(event.Message())

		if len(gifMatch) != 0 {
			r, err := GetGif(gifMatch[1])
			if err != nil {
				return
			}
			if r != "" {
				i.Privmsg(CHAN, r)
			}
			return
		}

		urlreg := regexp.MustCompile("(https?:\\/\\/)([a-z\\-0-9\\._]+\\.[a-z]{2,3}[a-z0-9%\\-\\._?#&\\/=]*)")
		urlMatch := urlreg.FindStringSubmatch(event.Message())
		if len(urlMatch) != 0 {
			clientPtr := prepareProxyClient()
			title, err := httpGetTitle(clientPtr, urlMatch[0])
			if err != nil || title == "" {
				return
			}

			i.Privmsg(CHAN, "[Title] "+title)
			return
		}
		if event.Message() == ".inflik" {
			i.Privmsg(event.Arguments[0], "Jag skulle bara vilja inflika för ett ögonblick.")
			i.Privmsg(event.Arguments[0], "Vad du kallar Linux är faktiskt GNU/Linux, eller som jag själv kallar det, GNU+Linux. Linux är inte ett operativsystem i sig själv, utan snarare ännu en del utav ett funktionellt GNU-system, som görs användbart av GNU corelibs, shell-utils, och andra nödvändiga delar, som tillsammans definerar ett OS enligt POSIX.")
			i.Privmsg(event.Arguments[0], "Många datorer kör ett modifierat GNU-system varje dag, utan att inse det. Genom en lustig härva av händelser kallas det GNU som används ofta Linux, och många av dess användare inser inte att de använder GNU-systemet, som utvecklats utav GNU-projektet.")
			i.Privmsg(event.Arguments[0], "Det finns ett Linux, och dessa människor använder det, men det är bara en del av systemet de använder. Linux är kerneln, programmet i systemet som allokerar maskinens resurser till de andra programmen du kör. Kerneln är en viktig del utav ett operativsystem, men helt oanvändbart i sig själv; den kan bara fungera i samband med ett helt operativsystem.")
			i.Privmsg(event.Arguments[0], "Linux används oftast i samband med GNU-operativsystemet: hela systemet är bara GNU med Linux tillagt, eller GNU/Linux. Alla så kallade Linux-distrubitioner är egentligen distrubutioner utav GNU/Linux!")
		}
		if event.Message() == ".interject" {
			i.Privmsg(event.Arguments[0], "I'd just like to interject for moment. What you're refering to as Linux, is in fact, GNU/Linux, or as I've recently taken to calling it, GNU plus Linux. Linux is not an operating system unto itself, but rather another free component of a fully functioning GNU system made useful by the GNU corelibs, shell utilities and vital system components comprising a full OS as defined by POSIX.")
			i.Privmsg(event.Arguments[0], "Many computer users run a modified version of the GNU system every day, without realizing it. Through a peculiar turn of events, the version of GNU which is widely used today is often called Linux, and many of its users are not aware that it is basically the GNU system, developed by the GNU Project. ")
			i.Privmsg(event.Arguments[0], "There really is a Linux, and these people are using it, but it is just a part of the system they usevent. Linux is the kernel: the program in the system that allocates the machine's resources to the other programs that you run. ")
			i.Privmsg(event.Arguments[0], "The kernel is an essential part of an operating system, but useless by itself; it can only function in the context of a complete operating system. Linux is normally used in combination with the GNU operating system: the whole system is basically GNU with Linux added, or GNU/Linux. All the so-called Linux distributions are really distributions of GNU/Linux! ")
		}
		if event.Message() == "wow #rude" {
			i.Privmsg(event.Arguments[0], "I feel offended by your recent action(s). Please read http://stop-irc-bullying.eu/stop")
		}
	})

	i.Debug = true
	i.Loop()
}
<commit_msg>Case insensitive<commit_after>package main

import (
	"crypto/tls"
	"log"
	"regexp"

	"github.com/thoj/go-ircevent"
)

func main() {
	CHAN := "#swehack"
	SERVER := "irc.swehack.org:6697"
	NICK := "placebot"

	i := irc.IRC(NICK, NICK)
	i.Version = "666"
	i.UseTLS = true
	i.TLSConfig = &tls.Config{InsecureSkipVerify: true}
	err := i.Connect(SERVER)
	if err != nil {
		log.Fatal(err)
	}

	i.AddCallback("PRIVMSG", func(event *irc.Event) {
		if event.Nick == i.GetNick() {
			return
		}

		re := regexp.MustCompile("!gif ([A-z0-9 \\.\\,\\-]+)")
		gifMatch := re.FindStringSubmatch(event.Message())

		if len(gifMatch) != 0 {
			r, err := GetGif(gifMatch[1])
			if err != nil {
				return
			}
			if r != "" {
				i.Privmsg(CHAN, r)
			}
			return
		}

		urlreg := regexp.MustCompile("(?i)(https?:\\/\\/)([a-z\\-0-9\\._]+\\.[a-z]{2,3}[a-z0-9%\\-\\._?#&\\/=]*)")
		urlMatch := urlreg.FindStringSubmatch(event.Message())
		if len(urlMatch) != 0 {
			clientPtr := prepareProxyClient()
			title, err := httpGetTitle(clientPtr, urlMatch[0])
			if err != nil || title == "" {
				return
			}

			i.Privmsg(CHAN, "[Title] "+title)
			return
		}
		if event.Message() == ".inflik" {
			i.Privmsg(event.Arguments[0], "Jag skulle bara vilja inflika för ett ögonblick.")
			i.Privmsg(event.Arguments[0], "Vad du kallar Linux är faktiskt GNU/Linux, eller som jag själv kallar det, GNU+Linux. Linux är inte ett operativsystem i sig själv, utan snarare ännu en del utav ett funktionellt GNU-system, som görs användbart av GNU corelibs, shell-utils, och andra nödvändiga delar, som tillsammans definerar ett OS enligt POSIX.")
			i.Privmsg(event.Arguments[0], "Många datorer kör ett modifierat GNU-system varje dag, utan att inse det. Genom en lustig härva av händelser kallas det GNU som används ofta Linux, och många av dess användare inser inte att de använder GNU-systemet, som utvecklats utav GNU-projektet.")
			i.Privmsg(event.Arguments[0], "Det finns ett Linux, och dessa människor använder det, men det är bara en del av systemet de använder. Linux är kerneln, programmet i systemet som allokerar maskinens resurser till de andra programmen du kör. Kerneln är en viktig del utav ett operativsystem, men helt oanvändbart i sig själv; den kan bara fungera i samband med ett helt operativsystem.")
			i.Privmsg(event.Arguments[0], "Linux används oftast i samband med GNU-operativsystemet: hela systemet är bara GNU med Linux tillagt, eller GNU/Linux. Alla så kallade Linux-distrubitioner är egentligen distrubutioner utav GNU/Linux!")
		}
		if event.Message() == ".interject" {
			i.Privmsg(event.Arguments[0], "I'd just like to interject for moment. What you're refering to as Linux, is in fact, GNU/Linux, or as I've recently taken to calling it, GNU plus Linux. Linux is not an operating system unto itself, but rather another free component of a fully functioning GNU system made useful by the GNU corelibs, shell utilities and vital system components comprising a full OS as defined by POSIX.")
			i.Privmsg(event.Arguments[0], "Many computer users run a modified version of the GNU system every day, without realizing it. Through a peculiar turn of events, the version of GNU which is widely used today is often called Linux, and many of its users are not aware that it is basically the GNU system, developed by the GNU Project. ")
			i.Privmsg(event.Arguments[0], "There really is a Linux, and these people are using it, but it is just a part of the system they usevent. Linux is the kernel: the program in the system that allocates the machine's resources to the other programs that you run. ")
			i.Privmsg(event.Arguments[0], "The kernel is an essential part of an operating system, but useless by itself; it can only function in the context of a complete operating system. Linux is normally used in combination with the GNU operating system: the whole system is basically GNU with Linux added, or GNU/Linux. All the so-called Linux distributions are really distributions of GNU/Linux! ")
		}
		if event.Message() == "wow #rude" {
			i.Privmsg(event.Arguments[0], "I feel offended by your recent action(s). Please read http://stop-irc-bullying.eu/stop")
		}
	})

	i.Debug = true
	i.Loop()
}
<|endoftext|>