<commit_before>package main

import (
	"database/sql"
	"errors"
	"flag"
	"fmt"
	"github.com/gorilla/mux"
	"github.com/kylelemons/go-gypsy/yaml"
	_ "github.com/lib/pq"
	"github.com/shawnps/gr"
	"github.com/shawnps/rt"
	"github.com/shawnps/sp"
	"html/template"
	"log"
	"net/http"
	"net/url"
	"sync"
	"time"
)

var (
	port       = flag.String("p", "8000", "Port number (default 8000)")
	configFile = flag.String("c", "config.yml", "Config file (default config.yml)")
)

func parseYAML() (rtKey, grKey, grSecret string, err error) {
	config, err := yaml.ReadFile(*configFile)
	if err != nil {
		return
	}
	rtKey, err = config.Get("rt")
	if err != nil {
		return
	}
	grKey, err = config.Get("gr.key")
	if err != nil {
		return
	}
	grSecret, err = config.Get("gr.secret")
	if err != nil {
		return
	}

	return rtKey, grKey, grSecret, nil
}

func insertEntry(db sql.DB, title, link, mediaType string) error {
	tx, err := db.Begin()
	if err != nil {
		return errors.New("Error connecting to database: " + err.Error())
	}
	stmt, err := tx.Prepare("insert into entries(title, link, media_type) values(?, ?, ?)")
	if err != nil {
		return errors.New("Error inserting to database: " + err.Error())
	}
	defer stmt.Close()
	_, err = stmt.Exec(title, link, mediaType)
	if err != nil {
		return errors.New("Error executing statement on database: " + err.Error())
	}
	tx.Commit()
	return nil
}

func truncate(s, suf string, l int) string {
	if len(s) < l {
		return s
	} else {
		return s[:l] + suf
	}
}

// Search Rotten Tomatoes, Goodreads, and Spotify.
func Search(q string, rtClient rt.RottenTomatoes, grClient gr.Goodreads, spClient sp.Spotify) (m []rt.Movie, g gr.GoodreadsResponse, s sp.SearchAlbumsResponse) {
	var wg sync.WaitGroup
	wg.Add(3)
	go func(q string) {
		defer wg.Done()
		movies, err := rtClient.SearchMovies(q)
		if err != nil {
			fmt.Println("ERROR (rt): ", err.Error())
		}
		for _, mov := range movies {
			mov.Title = truncate(mov.Title, "...", 60)
			m = append(m, mov)
		}
	}(q)
	go func(q string) {
		defer wg.Done()
		books, err := grClient.SearchBooks(q)
		if err != nil {
			fmt.Println("ERROR (gr): ", err.Error())
		}
		for i, w := range books.Search.Works {
			w.BestBook.Title = truncate(w.BestBook.Title, "...", 60)
			books.Search.Works[i] = w
		}
		g = books
	}(q)
	go func(q string) {
		defer wg.Done()
		albums, err := spClient.SearchAlbums(q)
		if err != nil {
			fmt.Println("ERROR (sp): ", err.Error())
		}
		for i, a := range albums.Albums {
			a.Name = truncate(a.Name, "...", 60)
			albums.Albums[i] = a
		}
		s = albums
	}(q)
	wg.Wait()
	return m, g, s
}

func HomeHandler(w http.ResponseWriter, r *http.Request) {
	t, err := template.New("index.html").ParseFiles("templates/index.html", "templates/base.html")
	if err != nil {
		log.Panic(err)
	}
	// Render the template
	err = t.ExecuteTemplate(w, "base", map[string]interface{}{"Page": "home"})
	if err != nil {
		log.Panic(err)
	}
}

func SearchHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	q := vars["query"]
	q, err := url.QueryUnescape(q)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
	rtKey, grKey, grSecret, err := parseYAML()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
	rtClient := rt.RottenTomatoes{rtKey}
	grClient := gr.Goodreads{grKey, grSecret}
	spClient := sp.Spotify{}
	m, g, s := Search(q, rtClient, grClient, spClient)
	// Since spotify: URIs are not trusted, have to pass a
	// URL function to the template to use in hrefs
	funcMap := template.FuncMap{
		"URL": func(q string) template.URL { return template.URL(q) },
	}
	t, err := template.New("search.html").Funcs(funcMap).ParseFiles("templates/search.html", "templates/base.html")
	if err != nil {
		log.Panic(err)
	}
	// Render the template
	err = t.ExecuteTemplate(w, "base", map[string]interface{}{"Movies": m, "Books": g, "Albums": s.Albums})
	if err != nil {
		log.Panic(err)
	}
}

func SaveHandler(w http.ResponseWriter, r *http.Request) {
	db, err := sql.Open("postgres", "user=postgres dbname=wrl")
	if err != nil {
		log.Println("Error opening db connection: " + err.Error())
		return
	}
	defer db.Close()
	t := r.FormValue("title")
	l := r.FormValue("link")
	m := r.FormValue("media_type")
	err = insertEntry(*db, t, l, m)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	http.Redirect(w, r, "/list", http.StatusFound)
}

func ListHandler(w http.ResponseWriter, r *http.Request) {
	db, err := sql.Open("postgres", "user=postgres dbname=wrl")
	if err != nil {
		http.Error(w, "Error opening db connection: "+err.Error(), http.StatusInternalServerError)
		return
	}
	defer db.Close()
	q := "select id, title, link, media_type, timestamp from entries"
	rows, err := db.Query(q)
	if err != nil {
		http.Error(w, "Error querying db: "+err.Error(), http.StatusInternalServerError)
		return
	}
	type Entry struct {
		Id        int
		Title     string
		Link      string
		MediaType string `db:"media_type"`
		Timestamp time.Time
	}
	entries := map[string][]Entry{}
	defer rows.Close()
	for rows.Next() {
		e := Entry{}
		var id int
		var title, link, mediaType string
		var timestamp time.Time
		rows.Scan(&id, &title, &link, &mediaType, &timestamp)
		e.Id = id
		e.Title = title
		e.Link = link
		e.MediaType = mediaType
		e.Timestamp = timestamp
		entries[mediaType] = append(entries[mediaType], e)
	}
	// Create and parse Template
	t, err := template.New("list.html").ParseFiles("templates/list.html", "templates/base.html")
	if err != nil {
		log.Panic(err)
	}
	// Render the template
	t.ExecuteTemplate(w, "base", map[string]interface{}{"Entries": entries, "Page": "list"})
}

func main() {
	err := createDb()
	if err != nil {
		log.Fatal(err)
	}
	r := mux.NewRouter()
	r.HandleFunc("/", HomeHandler)
	r.HandleFunc("/search/{query}", SearchHandler)
	r.HandleFunc("/save", SaveHandler)
	r.HandleFunc("/list", ListHandler)
	http.Handle("/", r)
	fmt.Println("Running on localhost:" + *port)

	log.Fatal(http.ListenAndServe(":"+*port, nil))
}
<commit_msg>update postgres connection params, remove createDb call in main<commit_after>package main

import (
	"database/sql"
	"errors"
	"flag"
	"fmt"
	"github.com/gorilla/mux"
	"github.com/kylelemons/go-gypsy/yaml"
	_ "github.com/lib/pq"
	"github.com/shawnps/gr"
	"github.com/shawnps/rt"
	"github.com/shawnps/sp"
	"html/template"
	"log"
	"net/http"
	"net/url"
	"sync"
	"time"
)

var (
	port       = flag.String("p", "8000", "Port number (default 8000)")
	configFile = flag.String("c", "config.yml", "Config file (default config.yml)")
)

func parseYAML() (rtKey, grKey, grSecret string, err error) {
	config, err := yaml.ReadFile(*configFile)
	if err != nil {
		return
	}
	rtKey, err = config.Get("rt")
	if err != nil {
		return
	}
	grKey, err = config.Get("gr.key")
	if err != nil {
		return
	}
	grSecret, err = config.Get("gr.secret")
	if err != nil {
		return
	}

	return rtKey, grKey, grSecret, nil
}

func insertEntry(db sql.DB, title, link, mediaType string) error {
	tx, err := db.Begin()
	if err != nil {
		return errors.New("Error connecting to database: " + err.Error())
	}
	stmt, err := tx.Prepare("insert into entries(title, link, media_type) values(?, ?, ?)")
	if err != nil {
		return errors.New("Error inserting to database: " + err.Error())
	}
	defer stmt.Close()
	_, err = stmt.Exec(title, link, mediaType)
	if err != nil {
		return errors.New("Error executing statement on database: " + err.Error())
	}
	tx.Commit()
	return nil
}

func truncate(s, suf string, l int) string {
	if len(s) < l {
		return s
	} else {
		return s[:l] + suf
	}
}

// Search Rotten Tomatoes, Goodreads, and Spotify.
func Search(q string, rtClient rt.RottenTomatoes, grClient gr.Goodreads, spClient sp.Spotify) (m []rt.Movie, g gr.GoodreadsResponse, s sp.SearchAlbumsResponse) {
	var wg sync.WaitGroup
	wg.Add(3)
	go func(q string) {
		defer wg.Done()
		movies, err := rtClient.SearchMovies(q)
		if err != nil {
			fmt.Println("ERROR (rt): ", err.Error())
		}
		for _, mov := range movies {
			mov.Title = truncate(mov.Title, "...", 60)
			m = append(m, mov)
		}
	}(q)
	go func(q string) {
		defer wg.Done()
		books, err := grClient.SearchBooks(q)
		if err != nil {
			fmt.Println("ERROR (gr): ", err.Error())
		}
		for i, w := range books.Search.Works {
			w.BestBook.Title = truncate(w.BestBook.Title, "...", 60)
			books.Search.Works[i] = w
		}
		g = books
	}(q)
	go func(q string) {
		defer wg.Done()
		albums, err := spClient.SearchAlbums(q)
		if err != nil {
			fmt.Println("ERROR (sp): ", err.Error())
		}
		for i, a := range albums.Albums {
			a.Name = truncate(a.Name, "...", 60)
			albums.Albums[i] = a
		}
		s = albums
	}(q)
	wg.Wait()
	return m, g, s
}

func HomeHandler(w http.ResponseWriter, r *http.Request) {
	t, err := template.New("index.html").ParseFiles("templates/index.html", "templates/base.html")
	if err != nil {
		log.Panic(err)
	}
	// Render the template
	err = t.ExecuteTemplate(w, "base", map[string]interface{}{"Page": "home"})
	if err != nil {
		log.Panic(err)
	}
}

func SearchHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	q := vars["query"]
	q, err := url.QueryUnescape(q)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
	rtKey, grKey, grSecret, err := parseYAML()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
	rtClient := rt.RottenTomatoes{rtKey}
	grClient := gr.Goodreads{grKey, grSecret}
	spClient := sp.Spotify{}
	m, g, s := Search(q, rtClient, grClient, spClient)
	// Since spotify: URIs are not trusted, have to pass a
	// URL function to the template to use in hrefs
	funcMap := template.FuncMap{
		"URL": func(q string) template.URL { return template.URL(q) },
	}
	t, err := template.New("search.html").Funcs(funcMap).ParseFiles("templates/search.html", "templates/base.html")
	if err != nil {
		log.Panic(err)
	}
	// Render the template
	err = t.ExecuteTemplate(w, "base", map[string]interface{}{"Movies": m, "Books": g, "Albums": s.Albums})
	if err != nil {
		log.Panic(err)
	}
}

func SaveHandler(w http.ResponseWriter, r *http.Request) {
	db, err := sql.Open("postgres", "user=postgres dbname=wrl sslmode=require")
	if err != nil {
		log.Println("Error opening db connection: " + err.Error())
		return
	}
	defer db.Close()
	t := r.FormValue("title")
	l := r.FormValue("link")
	m := r.FormValue("media_type")
	err = insertEntry(*db, t, l, m)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	http.Redirect(w, r, "/list", http.StatusFound)
}

func ListHandler(w http.ResponseWriter, r *http.Request) {
	db, err := sql.Open("postgres", "user=postgres dbname=wrl sslmode=require")
	if err != nil {
		http.Error(w, "Error opening db connection: "+err.Error(), http.StatusInternalServerError)
		return
	}
	defer db.Close()
	q := "select id, title, link, media_type, ctime from entries"
	rows, err := db.Query(q)
	if err != nil {
		http.Error(w, "Error querying db: "+err.Error(), http.StatusInternalServerError)
		return
	}
	type Entry struct {
		Id        int
		Title     string
		Link      string
		MediaType string `db:"media_type"`
		Timestamp time.Time
	}
	entries := map[string][]Entry{}
	defer rows.Close()
	for rows.Next() {
		e := Entry{}
		var id int
		var title, link, mediaType string
		var timestamp time.Time
		rows.Scan(&id, &title, &link, &mediaType, &timestamp)
		e.Id = id
		e.Title = title
		e.Link = link
		e.MediaType = mediaType
		e.Timestamp = timestamp
		entries[mediaType] = append(entries[mediaType], e)
	}
	// Create and parse Template
	t, err := template.New("list.html").ParseFiles("templates/list.html", "templates/base.html")
	if err != nil {
		log.Panic(err)
	}
	// Render the template
	t.ExecuteTemplate(w, "base", map[string]interface{}{"Entries": entries, "Page": "list"})
}

func main() {
	r := mux.NewRouter()
	r.HandleFunc("/", HomeHandler)
	r.HandleFunc("/search/{query}", SearchHandler)
	r.HandleFunc("/save", SaveHandler)
	r.HandleFunc("/list", ListHandler)
	http.Handle("/", r)
	fmt.Println("Running on localhost:" + *port)

	log.Fatal(http.ListenAndServe(":"+*port, nil))
}
<|endoftext|>


<commit_before>package main

import "github.com/skip2/go-qrcode"

func main() {
	qrCode, error := qrcode.New("http://www.twitter.com/dvlc_", qrcode.Medium)
	if error != nil {
		panic(error)
	}
	qrCode.WriteFile(250, "test.png")
}
<commit_msg>Created endpoint to generate qrcode<commit_after>package main

import (
	"github.com/skip2/go-qrcode"
	"gopkg.in/gin-gonic/gin.v1"
	"gopkg.in/gin-gonic/gin.v1/render"
)

func main() {
	r := gin.Default()

	r.POST("/qrcode", func(context *gin.Context) {
		url := context.PostForm("url")
		qrcode, error := generateQrCode(url)
		if error != nil {
			panic(error)
		}

		bytes, error := qrcode.PNG(256)
		if error != nil {
			panic(error)
		}

		context.Render(200, render.Data{ContentType: "image/png", Data: bytes})
	})

	r.Run()
}

func generateQrCode(url string) (*qrcode.QRCode, error) {
	qrcode, error := qrcode.New(url, qrcode.Medium)
	if error != nil {
		return nil, error
	}

	return qrcode, error
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"net"
	//"golang.org/x/crypto/openpgp"
)

var outputChannel = make(chan chan string, 5)
func main() {
	go printAll(outputChannel)
	onMessageReceived(outputChannel, "msg1")
	onMessageReceived(outputChannel, "msg2")
	onMessageReceived(outputChannel, "msg3")
	onMessageReceived(outputChannel, "msg4")
	listen()
}
func onMessageReceived(outputChannel chan chan string, message string) {
	messageChannel := make(chan string, 100)
	outputChannel <- messageChannel
	go func(){
		defer close(messageChannel)
	 	processMessage(message,messageChannel)
	}()
}
func processMessage(message string, messageChannel chan string) {
	messageChannel<-"Beginning processsing. "
	messageChannel<-"Done processing. "
	messageChannel<-"Here's the message: "
	messageChannel<-message
}

func handleConn(conn net.Conn) {
	fmt.Println("CONNECTION BABE")
	status, err := bufio.NewReader(conn).ReadString('\n')
	if err != nil {
		panic(err)
	}
	fmt.Println(status)
}

func listen() {
	ln, err := net.Listen("tcp", ":8080")
	if err != nil {
		panic(err)
	}
	for {
		conn, err := ln.Accept()
		if err != nil {
			panic(err)
		}
		go handleConn(conn)
	}
}

func printAll(stringChanChan <-chan chan string) {
	for {
		strChan := <-stringChanChan
		for{
			str, ok:= <-strChan
			if ok{
				fmt.Printf(str)
			}else{
				break
			}
		}
		fmt.Println()
	}
}
<commit_msg>updated<commit_after>package main

import (
	"bufio"
	"fmt"
	"strings"
	"net"
	//"golang.org/x/crypto/openpgp"
)

var outputChannel = make(chan chan string, 5)
func main() {
	go printAll(outputChannel)
	onMessageReceived(outputChannel, "msg1")
	onMessageReceived(outputChannel, "msg2")
	onMessageReceived(outputChannel, "msg3")
	onMessageReceived(outputChannel, "msg4")
	listen()
}
func onMessageReceived(outputChannel chan chan string, message string) {
	messageChannel := make(chan string, 100)
	outputChannel <- messageChannel
	go func(){
		defer close(messageChannel)
	 	processMessage(message,messageChannel)
	}()
}
func processMessage(message string, messageChannel chan string) {
	messageChannel<-"Beginning processsing. "
	messageChannel<-"Done processing. "
	messageChannel<-"Here's the message: "
	messageChannel<-message
}

func handleConn(conn net.Conn) {
	defer conn.Close()
	fmt.Println("CONNECTION BABE")
	username, err := bufio.NewReader(conn).ReadString('\n')
	if err != nil {
		panic(err)
	}
	username=strings.TrimSpace(username)
	fmt.Println("Username: "+username)
	for {
		message, err := bufio.NewReader(conn).ReadString('\n')
		if err!=nil{
			panic(err)
		}
		message=strings.TrimSpace(message)
		fmt.Println("Message from "+username+": "+message)
	}
}

func listen() {
	ln, err := net.Listen("tcp", ":8080")
	if err != nil {
		panic(err)
	}
	defer ln.Close()
	for {
		conn, err := ln.Accept()
		if err != nil {
			panic(err)
		}
		go handleConn(conn)
	}
}

func printAll(stringChanChan <-chan chan string) {
	for {
		strChan := <-stringChanChan
		for{
			str, ok:= <-strChan
			if ok{
				fmt.Printf(str)
			}else{
				break
			}
		}
		fmt.Println()
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"expvar"
	"log"
	"net/http"
	nurl "net/url"
	"os"
	"strconv"
	"strings"
	"net"
	"flag"
	"reflect"

	"github.com/donovanhide/eventsource"
	"github.com/monnand/goredis"
	"github.com/fiorix/freegeoip"
)

const (
	// ssePath is the PATH for the eventsource handler is going to be mounted on
	ssePath = "/push/"
)

var (
	numRegisterUsers   = expvar.NewInt("num_register_users")
	numUnregisterUsers = expvar.NewInt("num_unregister_users")
	numMessages        = expvar.NewInt("num_messages")
)

// Message is the bit of information that is transfered via eventsource
type Message struct {
	Idx           string
	Channel, HTML string
}

// Id is required to implement the eventsource.Event interface
func (c *Message) Id() string { return c.Idx }

// Event is required to implement the eventsource.Event interface
func (c *Message) Event() string { return c.Channel }

// Data is required to implement the eventsource.Event interface
func (c *Message) Data() string {
	return c.HTML
}

// Connection is use to relate a user token to a channel
type Connection struct {
	token   string
	channel string
}

type Location struct {
	Latitude float64
	Longitude float64
	timezone  string
}

// Hub maintains the states
type Hub struct {
	Data       map[string][]string // Key is the channel, value is a slice of token
	Users      map[string]string   // Key is the token, value is a channel
	register   chan Connection
	unregister chan string
	messages   chan goredis.Message
	srv        *eventsource.Server
	client     goredis.Client
	ipdb      *freegeoip.DB
}

func (h *Hub) userExists(token string) bool {
	_, ok := h.Users[token]
	return ok
}

func (h *Hub) run() {
	log.Println("[Info] Start the Hub")
	var payload [4]string
	psub := make(chan string, 0)
	go h.client.Subscribe(nil, nil, psub, nil, h.messages)

	// Listening to all channel updates
	psub <- "channel_update:*"

	for {
		select {
		case conn := <-h.register:
			log.Println("[Info] register user: ", conn.token)
			h.Users[conn.token] = conn.channel
			h.Data[conn.channel] = append(h.Data[conn.channel], conn.token)
			numRegisterUsers.Add(1)

		case token := <-h.unregister:
			log.Println("[Info] Unregister user: ", token)
			ch, ok := h.Users[token]
			if ok {
				delete(h.Users, token)
				delete(h.Data, ch)
			}
			numUnregisterUsers.Add(1)

		case msg := <-h.messages:
			err := json.Unmarshal(msg.Message, &payload)
			if err != nil {
				log.Println("[Error] An error occured while Unmarshalling the msg: ", msg)
			}
			message := &Message{
				Idx:     payload[2],
				Channel: payload[0],
				HTML:    payload[1],
			}

			val, ok := h.Data[msg.Channel]
			if ok && len(val) >= 1 {
				//log.Println("[Debug] Publishing message", message)
				h.srv.Publish(val, message)
			}

			if h.ipdb != nil {
				// lookup the host
				ips, err := net.LookupIP(payload[3])
				if err != nil || len(ips) > 0 {
					var result map[string]interface{}
					err = h.ipdb.Lookup(ips[0], &result)
					if err != nil {
						log.Println(err)
					} else {
						location := reflect.ValueOf(result["location"])
						lat := location.Interface().(map[string]interface{})["latitude"]
						lon := location.Interface().(map[string]interface{})["longitude"]

						val, _ := json.Marshal([]float64{reflect.ValueOf(lat).Interface().(float64),
							                             reflect.ValueOf(lon).Interface().(float64),})
						message2 := &Message{
							Idx:     payload[2],
							Channel: "loc",
							HTML:     string(val),
						}

						//log.Println("[Debug] Sending ip to glob ", message2)
						h.srv.Publish([]string{"glob"}, message2)
					}
				} else{
					log.Println("[Error] Cannot resolve", payload[3], err)
				}
			}

			numMessages.Add(1)
		}
	}
}

// EventSourceHandler implements the Handler interface
func (h *Hub) EventSourceHandler(w http.ResponseWriter, req *http.Request) {
	token := req.URL.Path[len(ssePath):]

	// The reserved name "glob" is accepted as a global channel that just sends stats
	if token == "glob"{
		log.Println("[Info] Connecting to the global channel")
		h.register <- Connection{"_", "glob"} // TODO: Need to come up with random, unique user ids
		defer func(u string) {
			h.unregister <- u
		}("_")
		h.srv.Handler(token)(w, req)

	} else{
		if h.userExists(token) {
			log.Println("[Info] Forbiden, user already connected")
			http.Error(w, "Forbiden", http.StatusForbidden)
		} else {
			log.Println("[Info] Exchange token against the channel list", token)
			val, err := h.client.Getset(token, []byte{})
			if err != nil {
				log.Println("[Error] occured while exchanging the your security token.", token, ":", err)
				http.Error(w, "Error occured while exchanging the your security token", http.StatusUnauthorized)

			} else if chanName := string(val); chanName != "" {
				log.Println("[Info] Connecting", token, "to the channel", chanName)
				h.register <- Connection{token, chanName}
				defer func(u string) {
					h.unregister <- u
				}(token)
				h.srv.Handler(token)(w, req)
			}
			_, err = h.client.Del(token)
			if err != nil {
				log.Println("[Error] An error occured while trying to delete the token from redis", err)
			}
		}
	}
}

// NewHub returns a pointer to a initialized and running Hub
func NewHub() *Hub {
	// Handle the flags. TODO: add flags for redis
	var dbPath string
	flag.StringVar(&dbPath, "ipdb-path", os.Getenv("IPDB_PATH"),
		"The path to the ip->location DB")
	flag.Parse()

	// Initialze the hub
	log.Println("[Info] Starting the eventsource Hub")

	var db *freegeoip.DB
	var err error
	if dbPath != "" {
		log.Println("[Info] Using ip->location DB ", dbPath)
		db, err = freegeoip.Open(dbPath)
		if err != nil {
			log.Fatal(err)
		}
	} else {
		log.Println("[Info] No ip->location DB provided")
	}

	redisURLString := os.Getenv("REDIS_SSEQUEUE_URL")
	if redisURLString == "" {
		// Use db 2 by default for  pub/sub
		redisURLString = "redis://localhost:6379/2"
	}
	log.Println("[Info] Redis configuration used for pub/sub", redisURLString)
	redisURL, err := nurl.Parse(redisURLString)
	if err != nil {
		log.Fatal("Could not read Redis string", err)
	}

	redisDb, err := strconv.Atoi(strings.TrimLeft(redisURL.Path, "/"))
	if err != nil {
		log.Fatal("[Error] Could not read Redis path", err)
	}

	server := eventsource.NewServer()
	server.AllowCORS = true

	h := Hub{
		Data:       make(map[string][]string),
		Users:      make(map[string]string),
		register:   make(chan Connection, 0),
		unregister: make(chan string, 0),
		messages:   make(chan goredis.Message, 0),
		srv:        server,
		client:     goredis.Client{Addr: redisURL.Host, Db: redisDb},
		ipdb:       db,
	}

	go h.run()
	return &h
}

func main() {
	sseString := os.Getenv("SSE_HOST")

	if sseString == "" {
		log.Fatal("SSE_HOST is not set, example: SSE_HOST=localhost:3000")
	}
	h := NewHub()

	// eventsource endpoints
	log.Println("[Info] botbot-eventsource is listening on " + sseString)
	http.HandleFunc(ssePath, h.EventSourceHandler)

	log.Fatalln(http.ListenAndServe(sseString, nil))
}
<commit_msg>fixed formating<commit_after>package main

import (
	"encoding/json"
	"expvar"
	"flag"
	"log"
	"net"
	"net/http"
	nurl "net/url"
	"os"
	"reflect"
	"strconv"
	"strings"

	"github.com/donovanhide/eventsource"
	"github.com/fiorix/freegeoip"
	"github.com/monnand/goredis"
)

const (
	// ssePath is the PATH for the eventsource handler is going to be mounted on
	ssePath = "/push/"
)

var (
	numRegisterUsers   = expvar.NewInt("num_register_users")
	numUnregisterUsers = expvar.NewInt("num_unregister_users")
	numMessages        = expvar.NewInt("num_messages")
)

// Message is the bit of information that is transfered via eventsource
type Message struct {
	Idx           string
	Channel, HTML string
}

// Id is required to implement the eventsource.Event interface
func (c *Message) Id() string { return c.Idx }

// Event is required to implement the eventsource.Event interface
func (c *Message) Event() string { return c.Channel }

// Data is required to implement the eventsource.Event interface
func (c *Message) Data() string {
	return c.HTML
}

// Connection is use to relate a user token to a channel
type Connection struct {
	token   string
	channel string
}

type Location struct {
	Latitude  float64
	Longitude float64
	timezone  string
}

// Hub maintains the states
type Hub struct {
	Data       map[string][]string // Key is the channel, value is a slice of token
	Users      map[string]string   // Key is the token, value is a channel
	register   chan Connection
	unregister chan string
	messages   chan goredis.Message
	srv        *eventsource.Server
	client     goredis.Client
	ipdb       *freegeoip.DB
}

func (h *Hub) userExists(token string) bool {
	_, ok := h.Users[token]
	return ok
}

func (h *Hub) run() {
	log.Println("[Info] Start the Hub")
	var payload [4]string
	psub := make(chan string, 0)
	go h.client.Subscribe(nil, nil, psub, nil, h.messages)

	// Listening to all channel updates
	psub <- "channel_update:*"

	for {
		select {
		case conn := <-h.register:
			log.Println("[Info] register user: ", conn.token)
			h.Users[conn.token] = conn.channel
			h.Data[conn.channel] = append(h.Data[conn.channel], conn.token)
			numRegisterUsers.Add(1)

		case token := <-h.unregister:
			log.Println("[Info] Unregister user: ", token)
			ch, ok := h.Users[token]
			if ok {
				delete(h.Users, token)
				delete(h.Data, ch)
			}
			numUnregisterUsers.Add(1)

		case msg := <-h.messages:
			err := json.Unmarshal(msg.Message, &payload)
			if err != nil {
				log.Println("[Error] An error occured while Unmarshalling the msg: ", msg)
			}
			message := &Message{
				Idx:     payload[2],
				Channel: payload[0],
				HTML:    payload[1],
			}

			val, ok := h.Data[msg.Channel]
			if ok && len(val) >= 1 {
				//log.Println("[Debug] Publishing message", message)
				h.srv.Publish(val, message)
			}

			if h.ipdb != nil {
				// lookup the host
				ips, err := net.LookupIP(payload[3])
				if err != nil || len(ips) > 0 {
					var result map[string]interface{}
					err = h.ipdb.Lookup(ips[0], &result)
					if err != nil {
						log.Println(err)
					} else {
						location := reflect.ValueOf(result["location"])
						lat := location.Interface().(map[string]interface{})["latitude"]
						lon := location.Interface().(map[string]interface{})["longitude"]

						val, _ := json.Marshal([]float64{reflect.ValueOf(lat).Interface().(float64),
							reflect.ValueOf(lon).Interface().(float64)})
						message2 := &Message{
							Idx:     payload[2],
							Channel: "loc",
							HTML:    string(val),
						}

						//log.Println("[Debug] Sending ip to glob ", message2)
						h.srv.Publish([]string{"glob"}, message2)
					}
				} else {
					log.Println("[Error] Cannot resolve", payload[3], err)
				}
			}

			numMessages.Add(1)
		}
	}
}

// EventSourceHandler implements the Handler interface
func (h *Hub) EventSourceHandler(w http.ResponseWriter, req *http.Request) {
	token := req.URL.Path[len(ssePath):]

	// The reserved name "glob" is accepted as a global channel that just sends stats
	if token == "glob" {
		log.Println("[Info] Connecting to the global channel")
		h.register <- Connection{"_", "glob"} // TODO: Need to come up with random, unique user ids
		defer func(u string) {
			h.unregister <- u
		}("_")
		h.srv.Handler(token)(w, req)

	} else {
		if h.userExists(token) {
			log.Println("[Info] Forbiden, user already connected")
			http.Error(w, "Forbiden", http.StatusForbidden)
		} else {
			log.Println("[Info] Exchange token against the channel list", token)
			val, err := h.client.Getset(token, []byte{})
			if err != nil {
				log.Println("[Error] occured while exchanging the your security token.", token, ":", err)
				http.Error(w, "Error occured while exchanging the your security token", http.StatusUnauthorized)

			} else if chanName := string(val); chanName != "" {
				log.Println("[Info] Connecting", token, "to the channel", chanName)
				h.register <- Connection{token, chanName}
				defer func(u string) {
					h.unregister <- u
				}(token)
				h.srv.Handler(token)(w, req)
			}
			_, err = h.client.Del(token)
			if err != nil {
				log.Println("[Error] An error occured while trying to delete the token from redis", err)
			}
		}
	}
}

// NewHub returns a pointer to a initialized and running Hub
func NewHub() *Hub {
	// Handle the flags. TODO: add flags for redis
	var dbPath string
	flag.StringVar(&dbPath, "ipdb-path", os.Getenv("IPDB_PATH"),
		"The path to the ip->location DB")
	flag.Parse()

	// Initialze the hub
	log.Println("[Info] Starting the eventsource Hub")

	var db *freegeoip.DB
	var err error
	if dbPath != "" {
		log.Println("[Info] Using ip->location DB ", dbPath)
		db, err = freegeoip.Open(dbPath)
		if err != nil {
			log.Fatal(err)
		}
	} else {
		log.Println("[Info] No ip->location DB provided")
	}

	redisURLString := os.Getenv("REDIS_SSEQUEUE_URL")
	if redisURLString == "" {
		// Use db 2 by default for  pub/sub
		redisURLString = "redis://localhost:6379/2"
	}
	log.Println("[Info] Redis configuration used for pub/sub", redisURLString)
	redisURL, err := nurl.Parse(redisURLString)
	if err != nil {
		log.Fatal("Could not read Redis string", err)
	}

	redisDb, err := strconv.Atoi(strings.TrimLeft(redisURL.Path, "/"))
	if err != nil {
		log.Fatal("[Error] Could not read Redis path", err)
	}

	server := eventsource.NewServer()
	server.AllowCORS = true

	h := Hub{
		Data:       make(map[string][]string),
		Users:      make(map[string]string),
		register:   make(chan Connection, 0),
		unregister: make(chan string, 0),
		messages:   make(chan goredis.Message, 0),
		srv:        server,
		client:     goredis.Client{Addr: redisURL.Host, Db: redisDb},
		ipdb:       db,
	}

	go h.run()
	return &h
}

func main() {
	sseString := os.Getenv("SSE_HOST")

	if sseString == "" {
		log.Fatal("SSE_HOST is not set, example: SSE_HOST=localhost:3000")
	}
	h := NewHub()

	// eventsource endpoints
	log.Println("[Info] botbot-eventsource is listening on " + sseString)
	http.HandleFunc(ssePath, h.EventSourceHandler)

	log.Fatalln(http.ListenAndServe(sseString, nil))
}
<|endoftext|>


<commit_before>package main

import (
	"os"

	log "github.com/Sirupsen/logrus"
)

func init() {
	log.SetLevel(log.DebugLevel)
}

func main() {
	if os.Args[0] == "unc-fork" {
		if err := fork(); err != nil {
			log.Fatalf("Fork error: %v", err)
		}
		os.Exit(0)
	}
	args := os.Args[1:]
	if len(args) == 0 {
		args = []string{os.Getenv("SHELL")}
	}
	c := &Container{
		Args: args,
		Uid:  os.Getuid(),
		Gid:  os.Getgid(),
	}
	if err := c.Start(); err != nil {
		log.Fatalf("Container start failed: %v", err)
	}
}
<commit_msg>Use logrus as identifier<commit_after>package main

import (
	"os"

	"github.com/Sirupsen/logrus"
)

func init() {
	logrus.SetLevel(logrus.DebugLevel)
}

func main() {
	if os.Args[0] == "unc-fork" {
		if err := fork(); err != nil {
			logrus.Fatalf("Fork error: %v", err)
		}
		os.Exit(0)
	}
	args := os.Args[1:]
	if len(args) == 0 {
		args = []string{os.Getenv("SHELL")}
	}
	c := &Container{
		Args: args,
		Uid:  os.Getuid(),
		Gid:  os.Getgid(),
	}
	if err := c.Start(); err != nil {
		logrus.Fatalf("Container start failed: %v", err)
	}
}
<|endoftext|>


<commit_before>// Copyright 2014 James McGuire. All rights reserved.
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

package main

import (
	"database/sql"
	"encoding/json"
	"flag"
	irc "github.com/fluffle/goirc/client"
	_ "github.com/go-sql-driver/mysql"
	"html"
	"io"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"net/url"
	"os"
	"regexp"
	"strings"
	"time"
	"unicode/utf8"
)

var (
	config   Config
	urlRegex = regexp.MustCompile(`(?i)\b((?:https?://|www\d{0,3}[.]|[` +
		`a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+` +
		`\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s` + "`" + `!()\[` +
		`\]{};:'".,<>?«»“”‘’]))`)
	httpRegex = regexp.MustCompile(`https?://.*`)
	db           *sql.DB
	badWords     = make(map[string]*regexp.Regexp)
	rebuildWords = flag.Bool("rebuild-words", false, "Rebuild the entire history of cursing. This sucks.")
)

type Config struct {
	Channel       string
	DBConn        string
	Nick          string
	Ident         string
	FullName      string
	FlickrAPIKey  string
	WolframAPIKey string
	IRCPass       string
	Commands      []struct {
		Name string
		Text string
	}
	BadWords []struct {
		Word  string
		Query string
	}
}

// Try and grab the title for any URL's posted in the channel
func sendUrl(channel, unparsedURL string, conn *irc.Conn) {
	if !httpRegex.MatchString(unparsedURL) {
		unparsedURL = `http://` + unparsedURL
	}
	postedUrl, err := url.Parse(unparsedURL)
	if err != nil {
		log.Println(err)
		return
	}
	log.Println("Fetching title for " + postedUrl.String() + " In channel " + channel)

	resp, err := http.Get(postedUrl.String())
	if err != nil {
		log.Println(err)
		return
	}
	defer resp.Body.Close()
	if resp.StatusCode >= 400 {
		log.Println("http server return error.")
		return
	}
	respbody := []byte{}
	if resp.Header.Get("Content-Type") == "" {
		buf := make([]byte, 512)
		bufsize, err := resp.Body.Read(buf)
		if err != nil {
			log.Println("adding content type failed")
		}
		resp.Header.Set("Content-Type", http.DetectContentType(buf[:bufsize]))
		respbody = append(respbody, buf[:bufsize]...)
	}

	if !strings.Contains(resp.Header.Get("Content-Type"), "text/html") {
		log.Println("content-type is not text/html")
		return
	}

	restofbody, err := ioutil.ReadAll(io.LimitReader(resp.Body, 50000))
	if err != nil {
		log.Println("error reading posted link")
		return
	}
	respbody = append(respbody, restofbody...)
	stringbody := string(respbody)
	titlestart := strings.Index(stringbody, "<title>")
	titleend := strings.Index(stringbody, "</title>")
	if titlestart != -1 && titlestart != -1 {
		title := string(respbody[titlestart+7 : titleend])
		title = strings.TrimSpace(title)
		if title != "" && utf8.ValidString(title) {
			// Example:
			// Title: sadbox . org (at sadbox.org)
			title = "Title: " + html.UnescapeString(title) + " (at " + postedUrl.Host + ")"
			log.Println(title)
			conn.Privmsg(channel, title)
		}
	}
}

func logMessage(line *irc.Line, channel, message string) {
	_, err := db.Exec("insert into messages (Nick, Ident, Host, Src, Cmd, Channel,"+
		" Message, Time) values (?, ?, ?, ?, ?, ?, ?, ?)", line.Nick, line.Ident,
		line.Host, line.Src, line.Cmd, channel, message, line.Time)
	if err != nil {
		log.Println(err)
	}
	err = updateWords(line.Nick, message)
	if err != nil {
		log.Println(err)
	}
}

func checkForUrl(channel string, splitmessage []string, conn *irc.Conn) {
	urllist := []string{}
	numlinks := 0
NextWord:
	for _, word := range splitmessage {
		word = strings.TrimSpace(word)
		if urlRegex.MatchString(word) {
			for _, subUrl := range urllist {
				if subUrl == word {
					continue NextWord
				}
			}
			numlinks++
			if numlinks > 3 {
				break
			}
			urllist = append(urllist, word)
			go sendUrl(channel, word, conn)
		}
	}
}

// This function does all the dispatching for various commands
// as well as logging each message to the database
func handleMessage(conn *irc.Conn, line *irc.Line) {
	// This is so that the bot can properly respond to pm's
	var channel string
	if conn.Me.Nick == line.Args[0] {
		channel = line.Nick
	} else {
		channel = line.Args[0]
	}
	message := line.Args[1]
	splitmessage := strings.Split(message, " ")

	// Special commands
	switch strings.TrimSpace(splitmessage[0]) {
	case "!dance":
		if line.Nick == "sadbox" {
			go dance(channel, conn)
		}
	case "!audio":
		if line.Nick == "sadbox" {
			go conn.Privmsg(channel, "https://sadbox.org/static/stuff/audiophile.html")
		}
	case "!cst":
		if line.Nick == "sadbox" {
			go conn.Privmsg(channel, "\u00039,13#CSTMASTERRACE")
		}
	case "!haata":
		go haata(channel, conn)
	case "!search":
		go googSearch(channel, message, conn)
	case "!chatter":
		if line.Nick == "sadbox" {
			go markov(channel, conn)
		}
	case "!ask":
		go wolfram(channel, message, line.Nick, conn)
	case "!meebcast":
		var command string
		if len(splitmessage) >= 2 {
			command = strings.TrimSpace(splitmessage[1])
		}
		go meeba(channel, line.Nick, command, conn)
	}

	// Commands that are read in from the config file
	for _, command := range config.Commands {
		if strings.TrimSpace(splitmessage[0]) == command.Name {
			go conn.Privmsg(channel, command.Text)
		}
	}

	// This is what looks at each word and tries to figure out if it's a URL
	go checkForUrl(channel, splitmessage, conn)

	// Shove that shit in the database!
	go logMessage(line, channel, message)
}

func init() {
	log.Println("Starting sadbot")

	flag.Parse()

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

	configfile, err := os.Open("config.json")
	if err != nil {
		log.Fatal(err)
	}
	decoder := json.NewDecoder(configfile)
	err = decoder.Decode(&config)
	if err != nil {
		log.Fatal(err)
	}

	for _, word := range config.BadWords {
		badWords[word.Word] = regexp.MustCompile(word.Query)
	}

	log.Println("Loaded config file!")
	log.Printf("Joining channel %s", config.Channel)
	log.Printf("Nick: %s", config.Nick)
	log.Printf("Ident: %s", config.Ident)
	log.Printf("FullName: %s", config.FullName)

	log.Printf("Found %d commands", len(config.Commands))
	for index, command := range config.Commands {
		log.Printf("%d %s: %s", index+1, command.Name, command.Text)
	}
}

func main() {
	var err error
	db, err = sql.Open("mysql", config.DBConn)
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	go makeMarkov()

	if *rebuildWords {
		go genTables()
	}
	c := irc.SimpleClient(config.Nick, config.Ident, config.FullName)

	c.SSL = true

	c.AddHandler(irc.CONNECTED,
		func(conn *irc.Conn, line *irc.Line) {
			conn.Join(config.Channel)
			log.Println("Connected!")
		})

	quit := make(chan bool)

	c.AddHandler(irc.DISCONNECTED,
		func(conn *irc.Conn, line *irc.Line) { quit <- true })

	c.AddHandler("PRIVMSG", handleMessage)
	c.AddHandler("ACTION", handleMessage)

	if err := c.Connect("irc.freenode.net", config.Nick+":"+config.IRCPass); err != nil {
		log.Fatalln("Connection error: %s\n", err)
	}

	<-quit
}
<commit_msg>maxopenconns due to mysql errors<commit_after>// Copyright 2014 James McGuire. All rights reserved.
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

package main

import (
	"database/sql"
	"encoding/json"
	"flag"
	irc "github.com/fluffle/goirc/client"
	_ "github.com/go-sql-driver/mysql"
	"html"
	"io"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"net/url"
	"os"
	"regexp"
	"strings"
	"time"
	"unicode/utf8"
)

var (
	config   Config
	urlRegex = regexp.MustCompile(`(?i)\b((?:https?://|www\d{0,3}[.]|[` +
		`a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+` +
		`\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s` + "`" + `!()\[` +
		`\]{};:'".,<>?«»“”‘’]))`)
	httpRegex    = regexp.MustCompile(`https?://.*`)
	db           *sql.DB
	badWords     = make(map[string]*regexp.Regexp)
	rebuildWords = flag.Bool("rebuild-words", false, "Rebuild the entire history of cursing. This sucks.")
)

type Config struct {
	Channel       string
	DBConn        string
	Nick          string
	Ident         string
	FullName      string
	FlickrAPIKey  string
	WolframAPIKey string
	IRCPass       string
	Commands      []struct {
		Name string
		Text string
	}
	BadWords []struct {
		Word  string
		Query string
	}
}

// Try and grab the title for any URL's posted in the channel
func sendUrl(channel, unparsedURL string, conn *irc.Conn) {
	if !httpRegex.MatchString(unparsedURL) {
		unparsedURL = `http://` + unparsedURL
	}
	postedUrl, err := url.Parse(unparsedURL)
	if err != nil {
		log.Println(err)
		return
	}
	log.Println("Fetching title for " + postedUrl.String() + " In channel " + channel)

	resp, err := http.Get(postedUrl.String())
	if err != nil {
		log.Println(err)
		return
	}
	defer resp.Body.Close()
	if resp.StatusCode >= 400 {
		log.Println("http server return error.")
		return
	}
	respbody := []byte{}
	if resp.Header.Get("Content-Type") == "" {
		buf := make([]byte, 512)
		bufsize, err := resp.Body.Read(buf)
		if err != nil {
			log.Println("adding content type failed")
		}
		resp.Header.Set("Content-Type", http.DetectContentType(buf[:bufsize]))
		respbody = append(respbody, buf[:bufsize]...)
	}

	if !strings.Contains(resp.Header.Get("Content-Type"), "text/html") {
		log.Println("content-type is not text/html")
		return
	}

	restofbody, err := ioutil.ReadAll(io.LimitReader(resp.Body, 50000))
	if err != nil {
		log.Println("error reading posted link")
		return
	}
	respbody = append(respbody, restofbody...)
	stringbody := string(respbody)
	titlestart := strings.Index(stringbody, "<title>")
	titleend := strings.Index(stringbody, "</title>")
	if titlestart != -1 && titlestart != -1 {
		title := string(respbody[titlestart+7 : titleend])
		title = strings.TrimSpace(title)
		if title != "" && utf8.ValidString(title) {
			// Example:
			// Title: sadbox . org (at sadbox.org)
			title = "Title: " + html.UnescapeString(title) + " (at " + postedUrl.Host + ")"
			log.Println(title)
			conn.Privmsg(channel, title)
		}
	}
}

func logMessage(line *irc.Line, channel, message string) {
	_, err := db.Exec("insert into messages (Nick, Ident, Host, Src, Cmd, Channel,"+
		" Message, Time) values (?, ?, ?, ?, ?, ?, ?, ?)", line.Nick, line.Ident,
		line.Host, line.Src, line.Cmd, channel, message, line.Time)
	if err != nil {
		log.Println(err)
	}
	err = updateWords(line.Nick, message)
	if err != nil {
		log.Println(err)
	}
}

func checkForUrl(channel string, splitmessage []string, conn *irc.Conn) {
	urllist := []string{}
	numlinks := 0
NextWord:
	for _, word := range splitmessage {
		word = strings.TrimSpace(word)
		if urlRegex.MatchString(word) {
			for _, subUrl := range urllist {
				if subUrl == word {
					continue NextWord
				}
			}
			numlinks++
			if numlinks > 3 {
				break
			}
			urllist = append(urllist, word)
			go sendUrl(channel, word, conn)
		}
	}
}

// This function does all the dispatching for various commands
// as well as logging each message to the database
func handleMessage(conn *irc.Conn, line *irc.Line) {
	// This is so that the bot can properly respond to pm's
	var channel string
	if conn.Me.Nick == line.Args[0] {
		channel = line.Nick
	} else {
		channel = line.Args[0]
	}
	message := line.Args[1]
	splitmessage := strings.Split(message, " ")

	// Special commands
	switch strings.TrimSpace(splitmessage[0]) {
	case "!dance":
		if line.Nick == "sadbox" {
			go dance(channel, conn)
		}
	case "!audio":
		if line.Nick == "sadbox" {
			go conn.Privmsg(channel, "https://sadbox.org/static/stuff/audiophile.html")
		}
	case "!cst":
		if line.Nick == "sadbox" {
			go conn.Privmsg(channel, "\u00039,13#CSTMASTERRACE")
		}
	case "!haata":
		go haata(channel, conn)
	case "!search":
		go googSearch(channel, message, conn)
	case "!chatter":
		if line.Nick == "sadbox" {
			go markov(channel, conn)
		}
	case "!ask":
		go wolfram(channel, message, line.Nick, conn)
	case "!meebcast":
		var command string
		if len(splitmessage) >= 2 {
			command = strings.TrimSpace(splitmessage[1])
		}
		go meeba(channel, line.Nick, command, conn)
	}

	// Commands that are read in from the config file
	for _, command := range config.Commands {
		if strings.TrimSpace(splitmessage[0]) == command.Name {
			go conn.Privmsg(channel, command.Text)
		}
	}

	// This is what looks at each word and tries to figure out if it's a URL
	go checkForUrl(channel, splitmessage, conn)

	// Shove that shit in the database!
	go logMessage(line, channel, message)
}

func init() {
	log.Println("Starting sadbot")

	flag.Parse()

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

	configfile, err := os.Open("config.json")
	if err != nil {
		log.Fatal(err)
	}
	decoder := json.NewDecoder(configfile)
	err = decoder.Decode(&config)
	if err != nil {
		log.Fatal(err)
	}

	for _, word := range config.BadWords {
		badWords[word.Word] = regexp.MustCompile(word.Query)
	}

	log.Println("Loaded config file!")
	log.Printf("Joining channel %s", config.Channel)
	log.Printf("Nick: %s", config.Nick)
	log.Printf("Ident: %s", config.Ident)
	log.Printf("FullName: %s", config.FullName)

	log.Printf("Found %d commands", len(config.Commands))
	for index, command := range config.Commands {
		log.Printf("%d %s: %s", index+1, command.Name, command.Text)
	}
}

func main() {
	var err error
	db, err = sql.Open("mysql", config.DBConn)
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()
	db.SetMaxIdleConns(50)
	db.SetMaxOpenConns(100)

	go makeMarkov()

	if *rebuildWords {
		go genTables()
	}
	c := irc.SimpleClient(config.Nick, config.Ident, config.FullName)

	c.SSL = true

	c.AddHandler(irc.CONNECTED,
		func(conn *irc.Conn, line *irc.Line) {
			conn.Join(config.Channel)
			log.Println("Connected!")
		})

	quit := make(chan bool)

	c.AddHandler(irc.DISCONNECTED,
		func(conn *irc.Conn, line *irc.Line) { quit <- true })

	c.AddHandler("PRIVMSG", handleMessage)
	c.AddHandler("ACTION", handleMessage)

	if err := c.Connect("irc.freenode.net", config.Nick+":"+config.IRCPass); err != nil {
		log.Fatalln("Connection error: %s\n", err)
	}

	<-quit
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
	"log"
	"net/url"
	"time"
)

var mongoUrl = flag.String("url", "mongodb://localhost:27017/test", "MongoDB connection URI.")
var collection = flag.String("c", "sessions", "MongoDB collection to cleanup.")
var field = flag.String("f", "updated_at", "MongoDB collection field with type 'time.Time'.")
var retention = flag.Int("r", 168, "MongoDB retention delai in hour(s). Default is 7 days (168 hours).")
var simulation = flag.Bool("s", false, "Simulation mode, no deletion are send to the MongoDB database.")

// Version is initialized at compilation time
var Version = "0.0.0"

// BuildTime is initialized at compilation time
var BuildTime = time.Now().Format(time.RFC3339)

func main() {
	flag.Parse()

	hostPort, err := url.Parse(*mongoUrl)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("MongoDB connection URL:              ", hostPort.Host)
	fmt.Println("MongoDB collection to clean:         ", *collection)
	fmt.Println("MongoDB collection field:            ", *field)
	fmt.Println("MongoDB retention periode in hour(s):", *retention)
	fmt.Println("Simulation mode:                     ", *simulation)
	fmt.Println()

	session, err := mgo.Dial(*mongoUrl)
	if err != nil {
		panic(err)
	}
	defer session.Close()

	session.SetMode(mgo.Monotonic, true)

	Sessions := session.DB("").C(*collection)
	counter, err := Sessions.Count()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(fmt.Sprintf("Number of item(s) in           %s: %d", *collection, counter))

	duration := time.Now().Add(time.Duration(-*retention) * time.Hour)
	query := bson.M{fmt.Sprintf("%s", *field): bson.M{"$lte": duration}}

	counter_to_delete, err := Sessions.Find(query).Count()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(fmt.Sprintf("Number of item(s) to delete in %s: %d", *collection, counter_to_delete))

	if *simulation == false {
		info, err := Sessions.RemoveAll(query)
		if err != nil {
			log.Fatal(err)
		}
		fmt.Println(fmt.Sprintf("Number of item(s) deleted in   %s: %d", *collection, info.Removed))
	} else {
		fmt.Println("[SIMULATION_MODE] In simulation mode no elements are deleted!!!")
	}
}
<commit_msg>Code review.<commit_after>package main

import (
	"flag"
	"fmt"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
	"log"
	"net/url"
	"time"
)

var urlFlag = flag.String("url", "mongodb://localhost:27017/test", "MongoDB connection URI.")
var cFlag = flag.String("c", "sessions", "MongoDB collection to cleanup.")
var fFlag = flag.String("f", "updated_at", "MongoDB collection field with type 'time.Time'.")
var rFlag = flag.Int("r", 168, "MongoDB retention delai in hour(s). Default is 7 days (168 hours).")
var sFlag = flag.Bool("s", false, "Simulation mode, no deletion are send to the MongoDB database.")

// Version is initialized at compilation time
var Version = "0.0.0"

// BuildTime is initialized at compilation time
var BuildTime = time.Now().Format(time.RFC3339)

func main() {
	flag.Parse()

	hostPort, err := url.Parse(*urlFlag)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(fmt.Sprintf("[Mongo clean session - Version %s - at %s]", Version, BuildTime))
	fmt.Println("MongoDB connection URL:              ", hostPort.Host)
	fmt.Println("MongoDB collection to clean:         ", *cFlag)
	fmt.Println("MongoDB collection field:            ", *fFlag)
	fmt.Println("MongoDB retention periode in hour(s):", *rFlag)
	fmt.Println("Simulation mode:                     ", *sFlag)
	fmt.Println()

	session, err := mgo.Dial(*urlFlag)
	if err != nil {
		panic(err)
	}
	defer session.Close()

	session.SetMode(mgo.Monotonic, true)

	Sessions := session.DB("").C(*cFlag)
	counter, err := Sessions.Count()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(fmt.Sprintf("Number of item(s) in           %s: %d", *cFlag, counter))

	duration := time.Now().Add(time.Duration(-*rFlag) * time.Hour)
	query := bson.M{fmt.Sprintf("%s", *fFlag): bson.M{"$lte": duration}}

	counter_to_delete, err := Sessions.Find(query).Count()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(fmt.Sprintf("Number of item(s) to delete in %s: %d", *cFlag, counter_to_delete))

	if *sFlag == false {
		info, err := Sessions.RemoveAll(query)
		if err != nil {
			log.Fatal(err)
		}
		fmt.Println(fmt.Sprintf("Number of item(s) deleted in   %s: %d", *cFlag, info.Removed))
	} else {
		fmt.Println("[SIMULATION_MODE] In simulation mode no elements are deleted!!!")
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io"
	"net/url"
	"os"
	"os/exec"
	"strings"

	cors "gopkg.in/gin-contrib/cors.v1"

	"github.com/gin-gonic/gin"
	"github.com/grandcat/zeroconf"
	"github.com/sirupsen/logrus"
)

const (
	version     = "0.0.5b3"
	defaultPort = 8080

	zeroConfName    = "OMX Remote"
	zeroConfService = "_omx-remote-api._tcp"
	zeroConfDomain  = "local."
)

var (
	// Commands mapping to control OMXPlayer, these are piped via STDIN to omxplayer process
	Commands = map[string]string{
		"pause":             "p",            // Pause/continue playback
		"stop":              "q",            // Stop playback and exit
		"volume_up":         "+",            // Change volume by +3dB
		"volume_down":       "-",            // Change volume by -3dB
		"subtitles":         "s",            // Enable/disable subtitles
		"seek_back":         "\x1b\x5b\x44", // Seek -30 seconds
		"seek_back_fast":    "\x1b\x5b\x42", // Seek -600 second
		"seek_forward":      "\x1b\x5b\x43", // Seek +30 second
		"seek_forward_fast": "\x1b\x5b\x41", // Seek +600 seconds
		"next_audio_stream": "k",            // next audio stream
		"prev_audio_stream": "j",            // previous audio stream
	}

	// OmxPath is path to omxplayer executable
	OmxPath string

	// Omx is a child process for spawning omxplayer
	Omx *exec.Cmd

	// OmxIn is a child process STDIN pipe to send commands
	OmxIn io.WriteCloser

	// Command is a channel to pass along commands to the player routine
	Command chan string

	// StatusStream channel to broadcast any changes in playing media via SSE
	StatusStream = make(chan *MediaEntry)

	// PlayingMedia represents currently playing media
	PlayingMedia *MediaEntry

	// PlayList is a list of media entries to play sequentially
	PlayList *PList

	// LOG is a global app logger
	LOG *logrus.Logger
)

// MediaEntry describes model of currently playable video.
type MediaEntry struct {
	RawURL    string                 `json:"url,omitempty"`
	MediaInfo map[string]interface{} `json:"media_info,omitempty"`
}

// APIErr is a generic structure for all errors returned from API
type APIErr struct {
	Message string `json:"message,omitempty"`
}

// PList holds the list of media items with pointer to the playing one
type PList struct {
	CurrentIndex int
	Entries      []*MediaEntry
}

// Next move pointer to a current element to the next element in the list and
// returns the media entry
func (pl *PList) Next() *MediaEntry {
	if len(pl.Entries) == 0 {
		return nil
	}

	nextIndex := pl.CurrentIndex + 1
	if len(pl.Entries) < nextIndex+1 {
		return nil
	}

	pl.CurrentIndex = nextIndex
	return pl.Entries[nextIndex]
}

// Select move pointer to a current element to the specific element refered by its index
// and return the media entry
func (pl *PList) Select(position int) *MediaEntry {
	plistSize := len(pl.Entries)
	if plistSize == 0 {
		return nil
	}

	if plistSize < position {
		return nil
	}

	pl.CurrentIndex = position
	return pl.Entries[position]
}

// AddEntry adds a new media entry to the end of the playlist.
func (pl *PList) AddEntry(entry *MediaEntry) {
	pl.Entries = append(pl.Entries, entry)
}

func nextToPlay() *MediaEntry {
	if PlayList == nil {
		return nil
	}

	return PlayList.Next()
}

// Determine the full path to omxplayer executable. Returns error if not found.
func omxDetect() error {
	buff, err := exec.Command("which", "omxplayer").Output()
	if err != nil {
		return err
	}

	// Set path in global variable
	OmxPath = strings.TrimSpace(string(buff))

	return nil
}

// Start command listener. Commands are coming in through a channel.
func omxListen() {
	Command = make(chan string)

	for {
		command := <-Command

		// Skip command handling of omx player is not active
		if Omx == nil {
			continue
		}

		// Send command to the player
		omxWrite(command)

		// Attempt to kill the process if stop command is requested
		if command == "stop" {
			omxStop()
		}
	}
}

// Start omxplayer playback for a given video file. Returns error if start fails.
func omxPlay(c MediaEntry) error {
	contentURL, err := url.Parse(c.RawURL)
	if err != nil {
		return err
	}

	Omx = exec.Command(
		OmxPath,             // path to omxplayer executable
		"--blank",           // set background to black
		"--stats",           // Pts and buffer stats
		"--with-info",       // dump stream format before playback
		"--adev",            // audio out device
		"hdmi",              // using hdmi for audio/video
		contentURL.String(), // path to video file
	)

	// Grab child process STDIN
	stdin, err := Omx.StdinPipe()
	if err != nil {
		return err
	}

	defer stdin.Close()

	// Grab child process STDOUT
	stdout, err := Omx.StdoutPipe()
	if err != nil {
		return err
	}

	defer stdout.Close()

	// Grab child process STDERR
	stderr, err := Omx.StderrPipe()
	if err != nil {
		return err
	}

	defer stderr.Close()

	// read child process STDOUT to get status
	// status := OmxProcessStatus{Stdout: stdout, Stderr: stderr, Logger: LOG}
	// status.Start()

	// Start omxplayer execution.
	// If successful, something will appear on HDMI display.
	err = Omx.Start()
	if err != nil {
		return err
	}

	setPlayingMedia(&c)

	// Make child's STDIN globally available
	OmxIn = stdin

	// Wait until child process is finished
	err = Omx.Wait()
	if err != nil {
		LOG.Error(fmt.Sprintln("Process exited with error:", err))
	} else {
		LOG.Info("Process exited without errors.")
	}

	omxCleanup()

	if next := nextToPlay(); next != nil {
		go omxPlay(*next)
	}

	return nil
}

// Write a command string to the omxplayer process's STDIN
func omxWrite(command string) {
	if OmxIn != nil {
		LOG.Debug("Write omx command: " + command)
		n, err := io.WriteString(OmxIn, Commands[command])
		if err != nil {
			LOG.Error(err.Error())
			return
		}

		LOG.Debug(fmt.Sprintf("%d bytes succsessfully written", n))
	}
}

func omxStop() {
	if !omxIsActive() {
		return
	}

	err := Omx.Process.Kill()
	if err != nil {
		LOG.Error(err.Error())
	}
	omxCleanup()
}

// Terminate any running omxplayer processes. Fixes random hangs.
func omxKill() {
	exec.Command("killall", "omxplayer.bin").Output()
	exec.Command("killall", "omxplayer").Output()
}

// Reset internal state and stop any running processes
func omxCleanup() {
	Omx = nil
	OmxIn = nil
	setPlayingMedia(nil)

	omxKill()
}

// Check if player is currently active
func omxIsActive() bool {
	return Omx != nil
}

func setPlayingMedia(m *MediaEntry) {
	PlayingMedia = m

	select {
	case StatusStream <- m:
		LOG.WithField("prefix", "broadcaster").Debug("send update")
	default:
	}
}

func terminate(message string, code int) {
	fmt.Println(message)
	os.Exit(code)
}

func main() {
	LOG = newLogger()
	LOG.Printf("omx-remote-api v%v", version)

	// Check if player is installed
	if omxDetect() != nil {
		terminate("omxplayer is not installed", 1)
	}

	// Make sure nothing is running
	omxCleanup()

	// Start a remote command listener
	go omxListen()

	// Register as a zero config service
	LOG.Infof("Starting zeroconf service [%s]", zeroConfName)
	server, err := zeroconf.Register(zeroConfName, zeroConfService, zeroConfDomain, defaultPort, nil, nil)
	if err != nil {
		LOG.Errorf("Cannot start zeroconf service: %s", err.Error())
	}
	defer server.Shutdown()

	// Disable debugging mode
	gin.SetMode("release")

	// Setup HTTP server
	router := gin.New()
	router.Use(gin.Recovery())

	// CORS
	router.Use(cors.Default())

	// Logger
	router.Use(HTTPLogger(LOG))

	router.GET("/status", httpStatus)
	router.GET("/status/stream", streamStatus)
	router.POST("/play", httpPlay)
	router.POST("/commands/:command", httpCommand)

	// playlist management
	router.PUT("/plist", httpNewPList)
	router.POST("/plist/commands/next", httpPListNext)
	router.POST("/plist/commands/select", httpPListSelect)
	router.POST("/plist/entries/", httpPListAddEntry)

	LOG.Printf("Starting http server on 0.0.0.0:%d", defaultPort)
	router.Run(fmt.Sprintf(":%d", defaultPort))
}
<commit_msg>update json output of playlist<commit_after>package main

import (
	"fmt"
	"io"
	"net/url"
	"os"
	"os/exec"
	"strings"

	cors "gopkg.in/gin-contrib/cors.v1"

	"github.com/gin-gonic/gin"
	"github.com/grandcat/zeroconf"
	"github.com/sirupsen/logrus"
)

const (
	version     = "0.0.5b3"
	defaultPort = 8080

	zeroConfName    = "OMX Remote"
	zeroConfService = "_omx-remote-api._tcp"
	zeroConfDomain  = "local."
)

var (
	// Commands mapping to control OMXPlayer, these are piped via STDIN to omxplayer process
	Commands = map[string]string{
		"pause":             "p",            // Pause/continue playback
		"stop":              "q",            // Stop playback and exit
		"volume_up":         "+",            // Change volume by +3dB
		"volume_down":       "-",            // Change volume by -3dB
		"subtitles":         "s",            // Enable/disable subtitles
		"seek_back":         "\x1b\x5b\x44", // Seek -30 seconds
		"seek_back_fast":    "\x1b\x5b\x42", // Seek -600 second
		"seek_forward":      "\x1b\x5b\x43", // Seek +30 second
		"seek_forward_fast": "\x1b\x5b\x41", // Seek +600 seconds
		"next_audio_stream": "k",            // next audio stream
		"prev_audio_stream": "j",            // previous audio stream
	}

	// OmxPath is path to omxplayer executable
	OmxPath string

	// Omx is a child process for spawning omxplayer
	Omx *exec.Cmd

	// OmxIn is a child process STDIN pipe to send commands
	OmxIn io.WriteCloser

	// Command is a channel to pass along commands to the player routine
	Command chan string

	// StatusStream channel to broadcast any changes in playing media via SSE
	StatusStream = make(chan *MediaEntry)

	// PlayingMedia represents currently playing media
	PlayingMedia *MediaEntry

	// PlayList is a list of media entries to play sequentially
	PlayList *PList

	// LOG is a global app logger
	LOG *logrus.Logger
)

// MediaEntry describes model of currently playable video.
type MediaEntry struct {
	RawURL    string                 `json:"url,omitempty"`
	MediaInfo map[string]interface{} `json:"media_info,omitempty"`
}

// APIErr is a generic structure for all errors returned from API
type APIErr struct {
	Message string `json:"message,omitempty"`
}

// PList holds the list of media items with pointer to the playing one
type PList struct {
	CurrentIndex int           `json:"current_index,omitempty"`
	Entries      []*MediaEntry `json:"entries,omitempty"`
}

// Next move pointer to a current element to the next element in the list and
// returns the media entry
func (pl *PList) Next() *MediaEntry {
	if len(pl.Entries) == 0 {
		return nil
	}

	nextIndex := pl.CurrentIndex + 1
	if len(pl.Entries) < nextIndex+1 {
		return nil
	}

	pl.CurrentIndex = nextIndex
	return pl.Entries[nextIndex]
}

// Select move pointer to a current element to the specific element refered by its index
// and return the media entry
func (pl *PList) Select(position int) *MediaEntry {
	plistSize := len(pl.Entries)
	if plistSize == 0 {
		return nil
	}

	if plistSize < position {
		return nil
	}

	pl.CurrentIndex = position
	return pl.Entries[position]
}

// AddEntry adds a new media entry to the end of the playlist.
func (pl *PList) AddEntry(entry *MediaEntry) {
	pl.Entries = append(pl.Entries, entry)
}

func nextToPlay() *MediaEntry {
	if PlayList == nil {
		return nil
	}

	return PlayList.Next()
}

// Determine the full path to omxplayer executable. Returns error if not found.
func omxDetect() error {
	buff, err := exec.Command("which", "omxplayer").Output()
	if err != nil {
		return err
	}

	// Set path in global variable
	OmxPath = strings.TrimSpace(string(buff))

	return nil
}

// Start command listener. Commands are coming in through a channel.
func omxListen() {
	Command = make(chan string)

	for {
		command := <-Command

		// Skip command handling of omx player is not active
		if Omx == nil {
			continue
		}

		// Send command to the player
		omxWrite(command)

		// Attempt to kill the process if stop command is requested
		if command == "stop" {
			omxStop()
		}
	}
}

// Start omxplayer playback for a given video file. Returns error if start fails.
func omxPlay(c MediaEntry) error {
	contentURL, err := url.Parse(c.RawURL)
	if err != nil {
		return err
	}

	Omx = exec.Command(
		OmxPath,             // path to omxplayer executable
		"--blank",           // set background to black
		"--stats",           // Pts and buffer stats
		"--with-info",       // dump stream format before playback
		"--adev",            // audio out device
		"hdmi",              // using hdmi for audio/video
		contentURL.String(), // path to video file
	)

	// Grab child process STDIN
	stdin, err := Omx.StdinPipe()
	if err != nil {
		return err
	}

	defer stdin.Close()

	// Grab child process STDOUT
	stdout, err := Omx.StdoutPipe()
	if err != nil {
		return err
	}

	defer stdout.Close()

	// Grab child process STDERR
	stderr, err := Omx.StderrPipe()
	if err != nil {
		return err
	}

	defer stderr.Close()

	// read child process STDOUT to get status
	// status := OmxProcessStatus{Stdout: stdout, Stderr: stderr, Logger: LOG}
	// status.Start()

	// Start omxplayer execution.
	// If successful, something will appear on HDMI display.
	err = Omx.Start()
	if err != nil {
		return err
	}

	setPlayingMedia(&c)

	// Make child's STDIN globally available
	OmxIn = stdin

	// Wait until child process is finished
	err = Omx.Wait()
	if err != nil {
		LOG.Error(fmt.Sprintln("Process exited with error:", err))
	} else {
		LOG.Info("Process exited without errors.")
	}

	omxCleanup()

	if next := nextToPlay(); next != nil {
		go omxPlay(*next)
	}

	return nil
}

// Write a command string to the omxplayer process's STDIN
func omxWrite(command string) {
	if OmxIn != nil {
		LOG.Debug("Write omx command: " + command)
		n, err := io.WriteString(OmxIn, Commands[command])
		if err != nil {
			LOG.Error(err.Error())
			return
		}

		LOG.Debug(fmt.Sprintf("%d bytes succsessfully written", n))
	}
}

func omxStop() {
	if !omxIsActive() {
		return
	}

	err := Omx.Process.Kill()
	if err != nil {
		LOG.Error(err.Error())
	}
	omxCleanup()
}

// Terminate any running omxplayer processes. Fixes random hangs.
func omxKill() {
	exec.Command("killall", "omxplayer.bin").Output()
	exec.Command("killall", "omxplayer").Output()
}

// Reset internal state and stop any running processes
func omxCleanup() {
	Omx = nil
	OmxIn = nil
	setPlayingMedia(nil)

	omxKill()
}

// Check if player is currently active
func omxIsActive() bool {
	return Omx != nil
}

func setPlayingMedia(m *MediaEntry) {
	PlayingMedia = m

	select {
	case StatusStream <- m:
		LOG.WithField("prefix", "broadcaster").Debug("send update")
	default:
	}
}

func terminate(message string, code int) {
	fmt.Println(message)
	os.Exit(code)
}

func main() {
	LOG = newLogger()
	LOG.Printf("omx-remote-api v%v", version)

	// Check if player is installed
	if omxDetect() != nil {
		terminate("omxplayer is not installed", 1)
	}

	// Make sure nothing is running
	omxCleanup()

	// Start a remote command listener
	go omxListen()

	// Register as a zero config service
	LOG.Infof("Starting zeroconf service [%s]", zeroConfName)
	server, err := zeroconf.Register(zeroConfName, zeroConfService, zeroConfDomain, defaultPort, nil, nil)
	if err != nil {
		LOG.Errorf("Cannot start zeroconf service: %s", err.Error())
	}
	defer server.Shutdown()

	// Disable debugging mode
	gin.SetMode("release")

	// Setup HTTP server
	router := gin.New()
	router.Use(gin.Recovery())

	// CORS
	router.Use(cors.Default())

	// Logger
	router.Use(HTTPLogger(LOG))

	router.GET("/status", httpStatus)
	router.GET("/status/stream", streamStatus)
	router.POST("/play", httpPlay)
	router.POST("/commands/:command", httpCommand)

	// playlist management
	router.PUT("/plist", httpNewPList)
	router.POST("/plist/commands/next", httpPListNext)
	router.POST("/plist/commands/select", httpPListSelect)
	router.POST("/plist/entries/", httpPListAddEntry)

	LOG.Printf("Starting http server on 0.0.0.0:%d", defaultPort)
	router.Run(fmt.Sprintf(":%d", defaultPort))
}
<|endoftext|>


<commit_before>// Copyright 2016, RadiantBlue Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"strconv"
	"strings"

	"github.com/venicegeo/pzsvc-exec/pzsvc"
)

type ConfigType struct {
	CliCmd string
	PzJobAddr string
	PzFileAddr string
}

type OutStruct struct {
    InFiles map[string] string
    OutFiles map[string] string
	ProgReturn string
}

func main() {

	// first argument after the base call should be the path to the config file.
	// ReadFile returns the contents of the file as a byte buffer.
	configBuf, err := ioutil.ReadFile(os.Args[1])
	if err != nil { fmt.Println("error:", err) }

	var configObj ConfigType
	err = json.Unmarshal(configBuf, &configObj)
	if err != nil { fmt.Println("error:", err) }

	//- check that config file data is complete.  Checks other dependency requirements (if any)
	//- register on Pz

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		r.ParseForm()
		switch r.URL.Path{
			case "/":
				fmt.Fprintf(w, "hello.")
			case "/execute": {

				var cmdParam string
				var inFileStr string
				var outTiffStr string
				var outTxtStr string
				var outGeoJStr string
				var usePz string

// might be time to start looking into that "help" thing.

				if r.Method == "GET" {
					cmdParam = r.URL.Query().Get("cmd")
					inFileStr = r.URL.Query().Get("inFiles")
					outTiffStr = r.URL.Query().Get("outTiffs")
					outTxtStr = r.URL.Query().Get("outTxts")
					outGeoJStr = r.URL.Query().Get("outGeoJson")
					usePz = r.URL.Query().Get("pz")
				} else {
					cmdParam = r.FormValue("cmd")
					inFileStr = r.FormValue("inFiles")
					outTiffStr = r.FormValue("outTiffs")
					outTxtStr = r.FormValue("outTxts")
					outGeoJStr = r.FormValue("outGeoJson")
					usePz = r.FormValue("pz")
				}

				cmdConfigSlice := splitOrNil(configObj.CliCmd, " ")
				cmdParamSlice := splitOrNil(cmdParam, " ")
				cmdSlice := append(cmdConfigSlice, cmdParamSlice...)

				inFileSlice := splitOrNil(inFileStr, ",")
				outTiffSlice := splitOrNil(outTiffStr, ",")
				outTxtSlice := splitOrNil(outTxtStr, ",")
				outGeoJSlice := splitOrNil(outGeoJStr, ",")

				var output OutStruct

				if len(inFileSlice) > 0 { output.InFiles = make(map[string]string) }
				if len(outTiffSlice) + len(outTxtSlice) + len(outGeoJSlice)  > 0 {
					output.OutFiles = make(map[string]string)
				}

				for _, inFile := range inFileSlice {

					fName, err := pzsvc.Download(inFile, configObj.PzFileAddr)
					if err != nil {
						fmt.Fprintf(w, err.Error())
					} else {
						output.InFiles[configObj.PzFileAddr] = fName
					}
				}

				if len(cmdSlice) == 0 {
					fmt.Fprintf(w, `No cmd specified in config file.  Please provide "cmd" param.`)
					break
				}

				clc := exec.Command(cmdSlice[0], cmdSlice[1:]...)

				var b bytes.Buffer
				clc.Stdout = &b
				clc.Stderr = os.Stderr

				err = clc.Run()
				if err != nil { fmt.Fprintf(w, err.Error()) }

				output.ProgReturn = b.String()

				for _, outTiff := range outTiffSlice {
					dataId, err := pzsvc.IngestTiff(outTiff, configObj.PzJobAddr, cmdSlice[0])
					if err != nil {
						fmt.Fprintf(w, err.Error())
					} else {
						output.OutFiles[outTiff] = dataId
					}
				}

				for _, outTxt := range outTxtSlice {
					dataId, err := pzsvc.IngestTxt(outTxt, configObj.PzJobAddr, cmdSlice[0])
					if err != nil {
						fmt.Fprintf(w, err.Error())
					} else {
						output.OutFiles[outTxt] = dataId
					}
				}

				for _, outGeoJ := range outGeoJSlice {
					dataId, err := pzsvc.IngestGeoJson(outGeoJ, configObj.PzJobAddr, cmdSlice[0])
					if err != nil {
						fmt.Fprintf(w, err.Error())
					} else {
						output.OutFiles[outGeoJ] = dataId
					}
				}

				outBuf, err := json.Marshal(output)
				if err != nil { fmt.Fprintf(w, err.Error()) }

				outStr := string(outBuf)

				if usePz != "" {
					outStr = strconv.QuoteToASCII(outStr)
					outStr = fmt.Sprintf ( `{ "dataType": { "type": "text", "content": "%s" "mimeType": "text/plain" }, "metadata": {} }`, outStr )
				}

				fmt.Fprintf(w, outStr)

			}
			case "/help":
				help(w)
			default:
				other(w)
		}
	})

// might want to update Port number at some point - possibly to os.Getenve(“PORT”),
// possibly to some other defined port - talk with the Pz folks over what their
// system is
	log.Fatal(http.ListenAndServe(":8080", nil))
}

func splitOrNil(inString, knife string) []string {
fmt.Printf("SplitOrNull: \"%s\", split by \"%s\".\n", inString, knife)
	if inString == "" {
		return nil
	}
	return strings.Split(inString, knife)
}

func other(w http.ResponseWriter) {
	fmt.Fprintf(w, "Command undefined.  Try help?\n")
}

func help(w http.ResponseWriter) {
	fmt.Fprintf(w, "We're sorry, help is not yet implemented\n")
}

<commit_msg>minor fix to bring output format more into alignment with initial intent<commit_after>// Copyright 2016, RadiantBlue Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"strconv"
	"strings"

	"github.com/venicegeo/pzsvc-exec/pzsvc"
)

type ConfigType struct {
	CliCmd string
	PzJobAddr string
	PzFileAddr string
}

type OutStruct struct {
    InFiles map[string] string
    OutFiles map[string] string
	ProgReturn string
}

func main() {

	// first argument after the base call should be the path to the config file.
	// ReadFile returns the contents of the file as a byte buffer.
	configBuf, err := ioutil.ReadFile(os.Args[1])
	if err != nil { fmt.Println("error:", err) }

	var configObj ConfigType
	err = json.Unmarshal(configBuf, &configObj)
	if err != nil { fmt.Println("error:", err) }

	//- check that config file data is complete.  Checks other dependency requirements (if any)
	//- register on Pz

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		r.ParseForm()
		switch r.URL.Path{
			case "/":
				fmt.Fprintf(w, "hello.")
			case "/execute": {

				var cmdParam string
				var inFileStr string
				var outTiffStr string
				var outTxtStr string
				var outGeoJStr string
				var usePz string

// might be time to start looking into that "help" thing.

				if r.Method == "GET" {
					cmdParam = r.URL.Query().Get("cmd")
					inFileStr = r.URL.Query().Get("inFiles")
					outTiffStr = r.URL.Query().Get("outTiffs")
					outTxtStr = r.URL.Query().Get("outTxts")
					outGeoJStr = r.URL.Query().Get("outGeoJson")
					usePz = r.URL.Query().Get("pz")
				} else {
					cmdParam = r.FormValue("cmd")
					inFileStr = r.FormValue("inFiles")
					outTiffStr = r.FormValue("outTiffs")
					outTxtStr = r.FormValue("outTxts")
					outGeoJStr = r.FormValue("outGeoJson")
					usePz = r.FormValue("pz")
				}

				cmdConfigSlice := splitOrNil(configObj.CliCmd, " ")
				cmdParamSlice := splitOrNil(cmdParam, " ")
				cmdSlice := append(cmdConfigSlice, cmdParamSlice...)

				inFileSlice := splitOrNil(inFileStr, ",")
				outTiffSlice := splitOrNil(outTiffStr, ",")
				outTxtSlice := splitOrNil(outTxtStr, ",")
				outGeoJSlice := splitOrNil(outGeoJStr, ",")

				var output OutStruct

				if len(inFileSlice) > 0 { output.InFiles = make(map[string]string) }
				if len(outTiffSlice) + len(outTxtSlice) + len(outGeoJSlice)  > 0 {
					output.OutFiles = make(map[string]string)
				}

				for _, inFile := range inFileSlice {

					fName, err := pzsvc.Download(inFile, configObj.PzFileAddr)
					if err != nil {
						fmt.Fprintf(w, err.Error())
					} else {
						output.InFiles[inFile] = fName
					}
				}

				if len(cmdSlice) == 0 {
					fmt.Fprintf(w, `No cmd specified in config file.  Please provide "cmd" param.`)
					break
				}

				clc := exec.Command(cmdSlice[0], cmdSlice[1:]...)

				var b bytes.Buffer
				clc.Stdout = &b
				clc.Stderr = os.Stderr

				err = clc.Run()
				if err != nil { fmt.Fprintf(w, err.Error()) }

				output.ProgReturn = b.String()

				for _, outTiff := range outTiffSlice {
					dataId, err := pzsvc.IngestTiff(outTiff, configObj.PzJobAddr, cmdSlice[0])
					if err != nil {
						fmt.Fprintf(w, err.Error())
					} else {
						output.OutFiles[outTiff] = dataId
					}
				}

				for _, outTxt := range outTxtSlice {
					dataId, err := pzsvc.IngestTxt(outTxt, configObj.PzJobAddr, cmdSlice[0])
					if err != nil {
						fmt.Fprintf(w, err.Error())
					} else {
						output.OutFiles[outTxt] = dataId
					}
				}

				for _, outGeoJ := range outGeoJSlice {
					dataId, err := pzsvc.IngestGeoJson(outGeoJ, configObj.PzJobAddr, cmdSlice[0])
					if err != nil {
						fmt.Fprintf(w, err.Error())
					} else {
						output.OutFiles[outGeoJ] = dataId
					}
				}

				outBuf, err := json.Marshal(output)
				if err != nil { fmt.Fprintf(w, err.Error()) }

				outStr := string(outBuf)

				if usePz != "" {
					outStr = strconv.QuoteToASCII(outStr)
					outStr = fmt.Sprintf ( `{ "dataType": { "type": "text", "content": "%s" "mimeType": "text/plain" }, "metadata": {} }`, outStr )
				}

				fmt.Fprintf(w, outStr)

			}
			case "/help":
				help(w)
			default:
				other(w)
		}
	})

// might want to update Port number at some point - possibly to os.Getenve(“PORT”),
// possibly to some other defined port - talk with the Pz folks over what their
// system is
	log.Fatal(http.ListenAndServe(":8080", nil))
}

func splitOrNil(inString, knife string) []string {
fmt.Printf("SplitOrNull: \"%s\", split by \"%s\".\n", inString, knife)
	if inString == "" {
		return nil
	}
	return strings.Split(inString, knife)
}

func other(w http.ResponseWriter) {
	fmt.Fprintf(w, "Command undefined.  Try help?\n")
}

func help(w http.ResponseWriter) {
	fmt.Fprintf(w, "We're sorry, help is not yet implemented\n")
}

<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"path"
	"strconv"
	"time"

	"github.com/coreos/etcd/etcdserver"
	"github.com/coreos/etcd/etcdserver/etcdhttp"
	"github.com/coreos/etcd/raft"
	"github.com/coreos/etcd/store"
	"github.com/coreos/etcd/wal"
)

const (
	// the owner can make/remove files inside the directory
	privateDirMode = 0700
)

var (
	fid     = flag.String("id", "0xBEEF", "Id of this server")
	timeout = flag.Duration("timeout", 10*time.Second, "Request Timeout")
	laddr   = flag.String("l", ":8080", "HTTP service address (e.g., ':8080')")
	dir     = flag.String("data-dir", "", "Path to the data directory")
	peers   = etcdhttp.Peers{}
)

func init() {
	flag.Var(peers, "peers", "your peers")
}

func main() {
	flag.Parse()

	id, err := strconv.ParseInt(*fid, 0, 64)
	if err != nil {
		log.Fatal(err)
	}

	if peers.Pick(id) == "" {
		log.Fatalf("%#x=<addr> must be specified in peers", id)
	}

	if *dir == "" {
		*dir = fmt.Sprintf("%v", *fid)
		log.Printf("main: no data-dir is given, using default data-dir ./%s", *dir)
	}
	if err := os.MkdirAll(*dir, privateDirMode); err != nil {
		log.Fatalf("main: cannot create data directory: %v", err)
	}

	n, w := startRaft(id, peers.Ids(), path.Join(*dir, "wal"))

	tk := time.NewTicker(100 * time.Millisecond)
	s := &etcdserver.Server{
		Store:  store.New(),
		Node:   n,
		Save:   w.Save,
		Send:   etcdhttp.Sender(peers),
		Ticker: tk.C,
	}
	etcdserver.Start(s)
	h := &etcdhttp.Handler{
		Timeout: *timeout,
		Server:  s,
	}
	http.Handle("/", h)
	log.Fatal(http.ListenAndServe(*laddr, nil))
}

// startRaft starts a raft node from the given wal dir.
// If the wal dir does not exist, startRaft will start a new raft node.
// If the wal dir exists, startRaft will restart the previous raft node.
// startRaft returns the started raft node and the opened wal.
func startRaft(id int64, peerIds []int64, waldir string) (raft.Node, *wal.WAL) {
	if !wal.Exist(waldir) {
		w, err := wal.Create(waldir)
		if err != nil {
			log.Fatal(err)
		}
		n := raft.Start(id, peerIds, 10, 1)
		return n, w
	}

	// restart a node from previous wal
	// TODO(xiangli): check snapshot; not open from zero
	w, err := wal.OpenFromIndex(waldir, 0)
	if err != nil {
		log.Fatal(err)
	}
	wid, st, ents, err := w.ReadAll()
	// TODO(xiangli): save/recovery nodeID?
	if wid != 0 {
		log.Fatal("unimplemented: nodeid")
	}
	if err != nil {
		log.Fatal(err)
	}
	n := raft.Restart(id, peerIds, 10, 1, st, ents)
	return n, w
}
<commit_msg>main: make default data to have _data_etcd suffix<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"path"
	"strconv"
	"time"

	"github.com/coreos/etcd/etcdserver"
	"github.com/coreos/etcd/etcdserver/etcdhttp"
	"github.com/coreos/etcd/raft"
	"github.com/coreos/etcd/store"
	"github.com/coreos/etcd/wal"
)

const (
	// the owner can make/remove files inside the directory
	privateDirMode = 0700
)

var (
	fid     = flag.String("id", "0xBEEF", "Id of this server")
	timeout = flag.Duration("timeout", 10*time.Second, "Request Timeout")
	laddr   = flag.String("l", ":8080", "HTTP service address (e.g., ':8080')")
	dir     = flag.String("data-dir", "", "Path to the data directory")
	peers   = etcdhttp.Peers{}
)

func init() {
	flag.Var(peers, "peers", "your peers")
}

func main() {
	flag.Parse()

	id, err := strconv.ParseInt(*fid, 0, 64)
	if err != nil {
		log.Fatal(err)
	}

	if peers.Pick(id) == "" {
		log.Fatalf("%#x=<addr> must be specified in peers", id)
	}

	if *dir == "" {
		*dir = fmt.Sprintf("%v_etcd_data", *fid)
		log.Printf("main: no data-dir is given, using default data-dir ./%s", *dir)
	}
	if err := os.MkdirAll(*dir, privateDirMode); err != nil {
		log.Fatalf("main: cannot create data directory: %v", err)
	}

	n, w := startRaft(id, peers.Ids(), path.Join(*dir, "wal"))

	tk := time.NewTicker(100 * time.Millisecond)
	s := &etcdserver.Server{
		Store:  store.New(),
		Node:   n,
		Save:   w.Save,
		Send:   etcdhttp.Sender(peers),
		Ticker: tk.C,
	}
	etcdserver.Start(s)
	h := &etcdhttp.Handler{
		Timeout: *timeout,
		Server:  s,
	}
	http.Handle("/", h)
	log.Fatal(http.ListenAndServe(*laddr, nil))
}

// startRaft starts a raft node from the given wal dir.
// If the wal dir does not exist, startRaft will start a new raft node.
// If the wal dir exists, startRaft will restart the previous raft node.
// startRaft returns the started raft node and the opened wal.
func startRaft(id int64, peerIds []int64, waldir string) (raft.Node, *wal.WAL) {
	if !wal.Exist(waldir) {
		w, err := wal.Create(waldir)
		if err != nil {
			log.Fatal(err)
		}
		n := raft.Start(id, peerIds, 10, 1)
		return n, w
	}

	// restart a node from previous wal
	// TODO(xiangli): check snapshot; not open from zero
	w, err := wal.OpenFromIndex(waldir, 0)
	if err != nil {
		log.Fatal(err)
	}
	wid, st, ents, err := w.ReadAll()
	// TODO(xiangli): save/recovery nodeID?
	if wid != 0 {
		log.Fatal("unimplemented: nodeid")
	}
	if err != nil {
		log.Fatal(err)
	}
	n := raft.Restart(id, peerIds, 10, 1, st, ents)
	return n, w
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	flag "github.com/ogier/pflag"
	"os"
	"path/filepath"
	"strings"
	"image"
	_ "image/gif"
	_ "image/jpeg"
	_ "image/png"
	"github.com/Knorkebrot/ansirgb"
	"github.com/olekukonko/ts"
)

type Block struct {
	top	*ansirgb.Color
	bottom	*ansirgb.Color
}

func (b *Block) String() string {
	ret := fmt.Sprintf("\033[48;5;%dm", b.bottom.Code)
	if b.top != nil {
		ret += fmt.Sprintf("\033[38;5;%dm", b.top.Code)
		// If it's not a UTF-8 terminal, fall back to '#'
		if strings.Contains(os.Getenv("LC_ALL"), "UTF-8") ||
		   strings.Contains(os.Getenv("LANG"), "UTF-8") {
			ret += "\u2580"
		} else {
			ret += "#"
		}
	} else {
		ret += " "
	}
	return ret
}

func cursorUp(count int) {
	fmt.Printf("\033[%dA", count)
}

func reset() {
	// add a space to prevent artifacts after resizing
	fmt.Printf("\033[0m ")
}

func usage() {
	fmt.Fprintf(os.Stderr, "Usage: %s file [...]\n", os.Args[0])
	flag.PrintDefaults()
}

func main() {
	var width int
	flag.IntVarP(&width, "width", "w", 0, "Output width, use 0 for terminal width")
	flag.Usage = usage
	flag.Parse()

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

	if width == 0 {
		size, err := ts.GetSize()
		if err != nil {
			fmt.Fprintln(os.Stderr, err, "\nYou may need to "+
				"set width manually using -w num")
			os.Exit(2)
		}
		width = size.Col() - 1	// -1 for the reset column
	}

	for _, fpath := range flag.Args() {
		fh, err := os.Open(fpath)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(10)
		}

		img, _, err := image.Decode(fh)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			fh.Close()
			os.Exit(20)
		}
		fh.Close()

		imgWidth := img.Bounds().Dx()
		imgHeight := img.Bounds().Dy()

		if imgWidth < width {
			width = imgWidth
		}

		ratio := float64(imgWidth) / float64(width)
		rows := int(float64(imgHeight) / ratio)

		for i := 1; i < rows; i += 2 {
			fmt.Println("")
		}
		cursorUp(rows / 2)

		for i := 1; i < rows; i += 2 {
			for j := 0; j < width; j++ {
				// TODO: get average color of the area instead
				// of one pixel?
				x := int(ratio * float64(j))
				yTop := int(ratio * float64(i - 1))
				yBottom := int(ratio * float64(i))

				top := ansirgb.Convert(img.At(x, yTop))
				bottom := ansirgb.Convert(img.At(x, yBottom))

				b := &Block{}
				b.bottom = bottom

				// Foreground colors are lighter in some terminals.
				// Ignore top (FG) if it's the same color anyway
				if top.Code != bottom.Code {
					b.top = top
				}

				fmt.Printf("%s", b)
			}
			reset()
			fmt.Printf("\n")
		}
		fmt.Println("File:", filepath.Base(fpath), "size:", imgWidth, "x", imgHeight)
	}
}
<commit_msg>Use ansirgb.Color's Fg() and Bg() methods<commit_after>package main

import (
	"fmt"
	flag "github.com/ogier/pflag"
	"os"
	"path/filepath"
	"strings"
	"image"
	_ "image/gif"
	_ "image/jpeg"
	_ "image/png"
	"github.com/Knorkebrot/ansirgb"
	"github.com/olekukonko/ts"
)

type Block struct {
	top	*ansirgb.Color
	bottom	*ansirgb.Color
}

func (b *Block) String() string {
	ret := b.bottom.Bg()
	if b.top != nil {
		ret += b.top.Fg()
		// If it's not a UTF-8 terminal, fall back to '#'
		if strings.Contains(os.Getenv("LC_ALL"), "UTF-8") ||
		   strings.Contains(os.Getenv("LANG"), "UTF-8") {
			ret += "\u2580"
		} else {
			ret += "#"
		}
	} else {
		ret += " "
	}
	return ret
}

func cursorUp(count int) {
	fmt.Printf("\033[%dA", count)
}

func reset() {
	// add a space to prevent artifacts after resizing
	fmt.Printf("\033[0m ")
}

func usage() {
	fmt.Fprintf(os.Stderr, "Usage: %s file [...]\n", os.Args[0])
	flag.PrintDefaults()
}

func main() {
	var width int
	flag.IntVarP(&width, "width", "w", 0, "Output width, use 0 for terminal width")
	flag.Usage = usage
	flag.Parse()

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

	if width == 0 {
		size, err := ts.GetSize()
		if err != nil {
			fmt.Fprintln(os.Stderr, err, "\nYou may need to "+
				"set width manually using -w num")
			os.Exit(2)
		}
		width = size.Col() - 1	// -1 for the reset column
	}

	for _, fpath := range flag.Args() {
		fh, err := os.Open(fpath)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(10)
		}

		img, _, err := image.Decode(fh)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			fh.Close()
			os.Exit(20)
		}
		fh.Close()

		imgWidth := img.Bounds().Dx()
		imgHeight := img.Bounds().Dy()

		if imgWidth < width {
			width = imgWidth
		}

		ratio := float64(imgWidth) / float64(width)
		rows := int(float64(imgHeight) / ratio)

		for i := 1; i < rows; i += 2 {
			fmt.Println("")
		}
		cursorUp(rows / 2)

		for i := 1; i < rows; i += 2 {
			for j := 0; j < width; j++ {
				// TODO: get average color of the area instead
				// of one pixel?
				x := int(ratio * float64(j))
				yTop := int(ratio * float64(i - 1))
				yBottom := int(ratio * float64(i))

				top := ansirgb.Convert(img.At(x, yTop))
				bottom := ansirgb.Convert(img.At(x, yBottom))

				b := &Block{}
				b.bottom = bottom

				// Foreground colors are lighter in some terminals.
				// Ignore top (FG) if it's the same color anyway
				if top.Code != bottom.Code {
					b.top = top
				}

				fmt.Printf("%s", b)
			}
			reset()
			fmt.Printf("\n")
		}
		fmt.Println("File:", filepath.Base(fpath), "size:", imgWidth, "x", imgHeight)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/codegangsta/cli"
	"github.com/exercism/cli/configuration"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
)

func main() {
	app := cli.NewApp()
	app.Name = "exercism"
	app.Usage = "A command line tool to interact with http://exercism.io"
	app.Version = VERSION
	app.Commands = []cli.Command{
		{
			Name:      "current",
			ShortName: "c",
			Usage:     "Show the current assignments",
			Action: func(c *cli.Context) {
				config, err := configuration.FromFile(configuration.HomeDir())
				if err != nil {
					fmt.Println("Are you sure you are logged in? Please login again.")
					return
				}
				currentAssignments, err := FetchAssignments(config, FetchEndpoints["current"])
				if err != nil {
					fmt.Println(err)
					return
				}

				fmt.Println("Current Assignments")

				for _, a := range currentAssignments {
					fmt.Printf("%v: %v\n", strings.Title(a.Track), a.Slug)
				}
			},
		},
		{
			Name:      "demo",
			ShortName: "d",
			Usage:     "Fetch first assignment for each language from exercism.io",
			Action: func(c *cli.Context) {
				config, err := configuration.FromFile(configuration.HomeDir())
				if err != nil {
					config, err = configuration.Demo()
					if err != nil {
						fmt.Println(err)
						return
					}
				}
				assignments, err := FetchAssignments(config, FetchEndpoints["demo"])
				if err != nil {
					fmt.Println(err)
					return
				}

				for _, a := range assignments {
					err := SaveAssignment(config.ExercismDirectory, a)
					if err != nil {
						fmt.Println(err)
					}
				}
			},
		},
		{
			Name:      "fetch",
			ShortName: "f",
			Usage:     "Fetch assignments from exercism.io",
			Action: func(c *cli.Context) {
				if len(c.Args()) != 0 && len(c.Args()) != 2 {
					fmt.Println("Usage: exercism fetch\n   or: exercism fetch LANGUAGE EXERCISE")
					return
				}

				config, err := configuration.FromFile(configuration.HomeDir())

				if err != nil {
					if len(c.Args()) == 0 {
						fmt.Println("Are you sure you are logged in? Please login again.")
						return
					} else {
						config, err = configuration.Demo()
						if err != nil {
							fmt.Println(err)
							return
						}
					}
				}

				assignments, err := FetchAssignments(config, FetchEndpoint(c.Args()))
				if err != nil {
					fmt.Println(err)
					return
				}

				for _, a := range assignments {
					err := SaveAssignment(config.ExercismDirectory, a)
					if err != nil {
						fmt.Println(err)
					}
				}

				fmt.Printf("Exercises written to %s\n", config.ExercismDirectory)
			},
		},
		{
			Name:      "login",
			ShortName: "l",
			Usage:     "Save exercism.io api credentials",
			Action: func(c *cli.Context) {
				configuration.ToFile(configuration.HomeDir(), askForConfigInfo())
			},
		},
		{
			Name:      "logout",
			ShortName: "o",
			Usage:     "Clear exercism.io api credentials",
			Action: func(c *cli.Context) {
				logout(configuration.HomeDir())
			},
		},
		{
			Name:      "peek",
			ShortName: "p",
			Usage:     "Fetch upcoming assignment from exercism.io",
			Action: func(c *cli.Context) {
				config, err := configuration.FromFile(configuration.HomeDir())
				if err != nil {
					fmt.Println("Are you sure you are logged in? Please login again.")
					return
				}
				assignments, err := FetchAssignments(config,
					FetchEndpoints["next"])
				if err != nil {
					fmt.Println(err)
					return
				}

				for _, a := range assignments {
					err := SaveAssignment(config.ExercismDirectory, a)
					if err != nil {
						fmt.Println(err)
					}
				}
			},
		},
		{
			Name:      "submit",
			ShortName: "s",
			Usage:     "Submit code to exercism.io on your current assignment",
			Action: func(c *cli.Context) {
				config, err := configuration.FromFile(configuration.HomeDir())
				if err != nil {
					fmt.Println("Are you sure you are logged in? Please login again.")
					return
				}

				if len(c.Args()) == 0 {
					fmt.Println("Please enter a file name")
					return
				}

				filename := c.Args()[0]

				// Make filename relative to config.ExercismDirectory.
				absPath, err := absolutePath(filename)
				if err != nil {
					fmt.Printf("Couldn't find %v: %v\n", filename, err)
					return
				}
				exDir := config.ExercismDirectory + string(filepath.Separator)
				if !strings.HasPrefix(absPath, exDir) {
					fmt.Printf("%v is not under your exercism project path (%v)\n", absPath, exDir)
					return
				}
				filename = absPath[len(exDir):]

				if IsTest(filename) {
					fmt.Println("It looks like this is a test, please enter an example file name.")
					return
				}

				code, err := ioutil.ReadFile(absPath)
				if err != nil {
					fmt.Printf("Error reading %v: %v\n", absPath, err)
					return
				}

				response, err := SubmitAssignment(config, filename, code)
				if err != nil {
					fmt.Printf("There was an issue with your submission: %v\n", err)
					return
				}

				fmt.Printf("For feedback on your submission visit %s%s%s.\n",
					config.Hostname, "/submissions/", response.Id)

			},
		},
		{
			Name:      "unsubmit",
			ShortName: "u",
			Usage:     "Delete the last submission",
			Action: func(c *cli.Context) {
				config, err := configuration.FromFile(configuration.HomeDir())
				if err != nil {
					fmt.Println("Are you sure you are logged in? Please login again.")
					return
				}

				response, err := UnsubmitAssignment(config)
				if err != nil {
					fmt.Println(err)
					return
				}

				if response != "" {
					return
				}

				fmt.Println("The last submission was successfully deleted.")
			},
		},
		{
			Name:      "whoami",
			ShortName: "w",
			Usage:     "Get the github username that you are logged in as",
			Action: func(c *cli.Context) {
				config, err := configuration.FromFile(configuration.HomeDir())
				if err != nil {
					fmt.Println("Are you sure you are logged in? Please login again.")
					return
				}

				fmt.Println(config.GithubUsername)
			},
		},
	}
	err := app.Run(os.Args)
	if err != nil {
		fmt.Errorf("%v", err)
		os.Exit(1)
	}
}

<commit_msg>Removed trailing . from 'For feedback on your submission visit $url.'<commit_after>package main

import (
	"fmt"
	"github.com/codegangsta/cli"
	"github.com/exercism/cli/configuration"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
)

func main() {
	app := cli.NewApp()
	app.Name = "exercism"
	app.Usage = "A command line tool to interact with http://exercism.io"
	app.Version = VERSION
	app.Commands = []cli.Command{
		{
			Name:      "current",
			ShortName: "c",
			Usage:     "Show the current assignments",
			Action: func(c *cli.Context) {
				config, err := configuration.FromFile(configuration.HomeDir())
				if err != nil {
					fmt.Println("Are you sure you are logged in? Please login again.")
					return
				}
				currentAssignments, err := FetchAssignments(config, FetchEndpoints["current"])
				if err != nil {
					fmt.Println(err)
					return
				}

				fmt.Println("Current Assignments")

				for _, a := range currentAssignments {
					fmt.Printf("%v: %v\n", strings.Title(a.Track), a.Slug)
				}
			},
		},
		{
			Name:      "demo",
			ShortName: "d",
			Usage:     "Fetch first assignment for each language from exercism.io",
			Action: func(c *cli.Context) {
				config, err := configuration.FromFile(configuration.HomeDir())
				if err != nil {
					config, err = configuration.Demo()
					if err != nil {
						fmt.Println(err)
						return
					}
				}
				assignments, err := FetchAssignments(config, FetchEndpoints["demo"])
				if err != nil {
					fmt.Println(err)
					return
				}

				for _, a := range assignments {
					err := SaveAssignment(config.ExercismDirectory, a)
					if err != nil {
						fmt.Println(err)
					}
				}
			},
		},
		{
			Name:      "fetch",
			ShortName: "f",
			Usage:     "Fetch assignments from exercism.io",
			Action: func(c *cli.Context) {
				if len(c.Args()) != 0 && len(c.Args()) != 2 {
					fmt.Println("Usage: exercism fetch\n   or: exercism fetch LANGUAGE EXERCISE")
					return
				}

				config, err := configuration.FromFile(configuration.HomeDir())

				if err != nil {
					if len(c.Args()) == 0 {
						fmt.Println("Are you sure you are logged in? Please login again.")
						return
					} else {
						config, err = configuration.Demo()
						if err != nil {
							fmt.Println(err)
							return
						}
					}
				}

				assignments, err := FetchAssignments(config, FetchEndpoint(c.Args()))
				if err != nil {
					fmt.Println(err)
					return
				}

				for _, a := range assignments {
					err := SaveAssignment(config.ExercismDirectory, a)
					if err != nil {
						fmt.Println(err)
					}
				}

				fmt.Printf("Exercises written to %s\n", config.ExercismDirectory)
			},
		},
		{
			Name:      "login",
			ShortName: "l",
			Usage:     "Save exercism.io api credentials",
			Action: func(c *cli.Context) {
				configuration.ToFile(configuration.HomeDir(), askForConfigInfo())
			},
		},
		{
			Name:      "logout",
			ShortName: "o",
			Usage:     "Clear exercism.io api credentials",
			Action: func(c *cli.Context) {
				logout(configuration.HomeDir())
			},
		},
		{
			Name:      "peek",
			ShortName: "p",
			Usage:     "Fetch upcoming assignment from exercism.io",
			Action: func(c *cli.Context) {
				config, err := configuration.FromFile(configuration.HomeDir())
				if err != nil {
					fmt.Println("Are you sure you are logged in? Please login again.")
					return
				}
				assignments, err := FetchAssignments(config,
					FetchEndpoints["next"])
				if err != nil {
					fmt.Println(err)
					return
				}

				for _, a := range assignments {
					err := SaveAssignment(config.ExercismDirectory, a)
					if err != nil {
						fmt.Println(err)
					}
				}
			},
		},
		{
			Name:      "submit",
			ShortName: "s",
			Usage:     "Submit code to exercism.io on your current assignment",
			Action: func(c *cli.Context) {
				config, err := configuration.FromFile(configuration.HomeDir())
				if err != nil {
					fmt.Println("Are you sure you are logged in? Please login again.")
					return
				}

				if len(c.Args()) == 0 {
					fmt.Println("Please enter a file name")
					return
				}

				filename := c.Args()[0]

				// Make filename relative to config.ExercismDirectory.
				absPath, err := absolutePath(filename)
				if err != nil {
					fmt.Printf("Couldn't find %v: %v\n", filename, err)
					return
				}
				exDir := config.ExercismDirectory + string(filepath.Separator)
				if !strings.HasPrefix(absPath, exDir) {
					fmt.Printf("%v is not under your exercism project path (%v)\n", absPath, exDir)
					return
				}
				filename = absPath[len(exDir):]

				if IsTest(filename) {
					fmt.Println("It looks like this is a test, please enter an example file name.")
					return
				}

				code, err := ioutil.ReadFile(absPath)
				if err != nil {
					fmt.Printf("Error reading %v: %v\n", absPath, err)
					return
				}

				response, err := SubmitAssignment(config, filename, code)
				if err != nil {
					fmt.Printf("There was an issue with your submission: %v\n", err)
					return
				}

				fmt.Printf("For feedback on your submission visit %s%s%s\n",
					config.Hostname, "/submissions/", response.Id)

			},
		},
		{
			Name:      "unsubmit",
			ShortName: "u",
			Usage:     "Delete the last submission",
			Action: func(c *cli.Context) {
				config, err := configuration.FromFile(configuration.HomeDir())
				if err != nil {
					fmt.Println("Are you sure you are logged in? Please login again.")
					return
				}

				response, err := UnsubmitAssignment(config)
				if err != nil {
					fmt.Println(err)
					return
				}

				if response != "" {
					return
				}

				fmt.Println("The last submission was successfully deleted.")
			},
		},
		{
			Name:      "whoami",
			ShortName: "w",
			Usage:     "Get the github username that you are logged in as",
			Action: func(c *cli.Context) {
				config, err := configuration.FromFile(configuration.HomeDir())
				if err != nil {
					fmt.Println("Are you sure you are logged in? Please login again.")
					return
				}

				fmt.Println(config.GithubUsername)
			},
		},
	}
	err := app.Run(os.Args)
	if err != nil {
		fmt.Errorf("%v", err)
		os.Exit(1)
	}
}

<|endoftext|>