<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"github.com/blevesearch/bleve"
	"os"
	"path/filepath"
	"sync"
	"time"
)

func loadOffer(store *Store, id string) (*jsonOffer, error) {
	data, err := store.Get(id)
	if err != nil {
		return nil, err
	}
	offer := &jsonOffer{}
	err = json.Unmarshal(data, offer)
	return offer, err
}

type offerResult struct {
	Id    string
	Offer *jsonOffer
	Err   error
}

func loadOffers(store *Store) ([]*jsonOffer, error) {
	ids := store.List()
	pending := make(chan string, len(ids))
	for _, id := range ids {
		pending <- id
	}
	close(pending)

	results := make(chan offerResult, len(ids))
	running := &sync.WaitGroup{}
	jobs := 4
	for i := 0; i < jobs; i++ {
		running.Add(1)
		go func() {
			defer running.Done()
			for id := range pending {
				offer, err := loadOffer(store, id)
				results <- offerResult{
					Id:    id,
					Offer: offer,
					Err:   err,
				}
			}
		}()
	}
	go func() {
		running.Wait()
		close(results)
	}()

	offers := []*jsonOffer{}
	for r := range results {
		if r.Err != nil {
			fmt.Printf("loading error for %s: %s\n", r.Id, r.Err)
			continue
		}
		offers = append(offers, r.Offer)
	}
	return offers, nil
}

func NewOfferIndex(dir string) (bleve.Index, error) {
	err := os.MkdirAll(dir, 0755)
	if err != nil {
		return nil, err
	}

	html := bleve.NewTextFieldMapping()
	html.Store = false
	html.IncludeInAll = false
	html.IncludeTermVectors = false

	title := bleve.NewDocumentStaticMapping()
	title.AddFieldMappingsAt("text", html)

	offer := bleve.NewDocumentStaticMapping()
	offer.AddFieldMappingsAt("texteHtml", html)
	offer.AddFieldMappingsAt("intitule", html)
	offer.AddSubDocumentMapping("Title", title)
	offer.Dynamic = false

	m := bleve.NewIndexMapping()
	m.AddDocumentMapping("offer", offer)
	m.DefaultMapping = offer

	index, err := bleve.New(filepath.Join(dir, "index"), m)
	if err != nil {
		return nil, err
	}
	return index, nil
}

type OfferTitle struct {
	Text string `json:"text"`
}

type Offer struct {
	HTML  string `json:"texteHtml"`
	Title OfferTitle
}

var (
	indexCmd      = app.Command("index", "index APEC offers")
	indexStoreDir = indexCmd.Arg("store", "data store directory").Required().String()
	indexIndexDir = indexCmd.Arg("index", "index directory").Required().String()
)

func indexOffers() error {
	store, err := OpenStore(*indexStoreDir)
	if err != nil {
		return err
	}
	index, err := NewOfferIndex(*indexIndexDir)
	if err != nil {
		return err
	}
	offers, err := loadOffers(store)
	if err != nil {
		return err
	}
	if len(offers) > 20 {
		offers = offers[:20]
	}
	start := time.Now()
	for i, offer := range offers {
		if (i+1)%500 == 0 {
			now := time.Now()
			elapsed := float64(now.Sub(start)) / float64(time.Second)
			fmt.Printf("%d indexed, %.1f/s\n", i+1, float64(i+1)/elapsed)
		}
		err = index.Index(offer.Id, &Offer{
			HTML: offer.HTML,
			Title: OfferTitle{
				Text: offer.Title,
			},
		})
		//err = index.Index(offer.Id, offer)
		if err != nil {
			return err
		}
	}
	err = index.Close()
	if err != nil {
		return err
	}
	end := time.Now()
	fmt.Printf("%d documents indexed in %.2fs\n", len(offers),
		float64(end.Sub(start))/float64(time.Second))
	return nil
}
<commit_msg>index: add -max-count items to index option<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"github.com/blevesearch/bleve"
	"os"
	"path/filepath"
	"sync"
	"time"
)

func loadOffer(store *Store, id string) (*jsonOffer, error) {
	data, err := store.Get(id)
	if err != nil {
		return nil, err
	}
	offer := &jsonOffer{}
	err = json.Unmarshal(data, offer)
	return offer, err
}

type offerResult struct {
	Id    string
	Offer *jsonOffer
	Err   error
}

func loadOffers(store *Store) ([]*jsonOffer, error) {
	ids := store.List()
	pending := make(chan string, len(ids))
	for _, id := range ids {
		pending <- id
	}
	close(pending)

	results := make(chan offerResult, len(ids))
	running := &sync.WaitGroup{}
	jobs := 4
	for i := 0; i < jobs; i++ {
		running.Add(1)
		go func() {
			defer running.Done()
			for id := range pending {
				offer, err := loadOffer(store, id)
				results <- offerResult{
					Id:    id,
					Offer: offer,
					Err:   err,
				}
			}
		}()
	}
	go func() {
		running.Wait()
		close(results)
	}()

	offers := []*jsonOffer{}
	for r := range results {
		if r.Err != nil {
			fmt.Printf("loading error for %s: %s\n", r.Id, r.Err)
			continue
		}
		offers = append(offers, r.Offer)
	}
	return offers, nil
}

func NewOfferIndex(dir string) (bleve.Index, error) {
	err := os.MkdirAll(dir, 0755)
	if err != nil {
		return nil, err
	}

	html := bleve.NewTextFieldMapping()
	html.Store = false
	html.IncludeInAll = false
	html.IncludeTermVectors = false

	title := bleve.NewDocumentStaticMapping()
	title.AddFieldMappingsAt("text", html)

	offer := bleve.NewDocumentStaticMapping()
	offer.AddFieldMappingsAt("texteHtml", html)
	offer.AddFieldMappingsAt("intitule", html)
	offer.AddSubDocumentMapping("Title", title)
	offer.Dynamic = false

	m := bleve.NewIndexMapping()
	m.AddDocumentMapping("offer", offer)
	m.DefaultMapping = offer

	index, err := bleve.New(filepath.Join(dir, "index"), m)
	if err != nil {
		return nil, err
	}
	return index, nil
}

type OfferTitle struct {
	Text string `json:"text"`
}

type Offer struct {
	HTML  string `json:"texteHtml"`
	Title OfferTitle
}

var (
	indexCmd      = app.Command("index", "index APEC offers")
	indexStoreDir = indexCmd.Arg("store", "data store directory").Required().String()
	indexIndexDir = indexCmd.Arg("index", "index directory").Required().String()
	indexMaxSize  = indexCmd.Flag("max-count", "maximum number of items to index").
			Short('n').Default("0").Int()
)

func indexOffers() error {
	store, err := OpenStore(*indexStoreDir)
	if err != nil {
		return err
	}
	index, err := NewOfferIndex(*indexIndexDir)
	if err != nil {
		return err
	}
	offers, err := loadOffers(store)
	if err != nil {
		return err
	}
	if *indexMaxSize > 0 && len(offers) > *indexMaxSize {
		offers = offers[:*indexMaxSize]
	}
	start := time.Now()
	for i, offer := range offers {
		if (i+1)%500 == 0 {
			now := time.Now()
			elapsed := float64(now.Sub(start)) / float64(time.Second)
			fmt.Printf("%d indexed, %.1f/s\n", i+1, float64(i+1)/elapsed)
		}
		err = index.Index(offer.Id, &Offer{
			HTML: offer.HTML,
			Title: OfferTitle{
				Text: offer.Title,
			},
		})
		//err = index.Index(offer.Id, offer)
		if err != nil {
			return err
		}
	}
	err = index.Close()
	if err != nil {
		return err
	}
	end := time.Now()
	fmt.Printf("%d documents indexed in %.2fs\n", len(offers),
		float64(end.Sub(start))/float64(time.Second))
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"net/http"
	"sort"
	"strconv"
	"time"

	"github.com/dim13/gold/articles"
	"github.com/dim13/gold/storage"
)

type page struct {
	Config    storage.Config
	URL       string
	Title     string
	Articles  articles.Articles
	Error     error
	PrevPage  int
	NextPage  int
	TagCloud  articles.TagCloud
	Year      int
	Month     time.Month
	Archive   []year
	FirstYear int
	LastYear  int
}

type byYear []year
type year struct {
	Year  int
	Count int
	Month []month
}

type byMonth []month
type month struct {
	Month    time.Month
	Year     int
	Count    int
	Articles articles.Articles
}

func (m byMonth) Len() int           { return len(m) }
func (m byMonth) Swap(i, j int)      { m[i], m[j] = m[j], m[i] }
func (m byMonth) Less(i, j int) bool { return m[i].Month < m[j].Month }

func (y byYear) Len() int           { return len(y) }
func (y byYear) Swap(i, j int)      { y[i], y[j] = y[j], y[i] }
func (y byYear) Less(i, j int) bool { return y[i].Year < y[j].Year }

func atoiMust(s string) int {
	i, err := strconv.Atoi(s)
	if err != nil {
		log.Println(err)
		return 0
	}
	return i
}

func (p *page) MakeArchive() {
	if p.Year == 0 {
		p.Year = p.Articles.Head().Year()
	}
	if p.Month == 0 {
		p.Month = p.Articles.Head().Month()
	}
	for y, v := range art.Enabled().YearMap() {
		year := year{
			Year:  y,
			Count: len(v),
		}
		if p.Year == y {
			for m, v := range v.MonthMap() {
				month := month{
					Year:  y,
					Month: time.Month(m),
					Count: len(v),
				}
				if p.Month == time.Month(m) {
					month.Articles = v
				}
				year.Month = append(year.Month, month)
			}
			sort.Sort(byMonth(year.Month))
		}
		p.Archive = append(p.Archive, year)
	}
	sort.Sort(sort.Reverse(byYear(p.Archive)))
}

func (p *page) Pager(arg string) {
	app := conf.Blog.ArticlesPerPage
	pg := atoiMust(arg)

	if pg <= 1 {
		pg = 1
	} else {
		p.PrevPage = pg - 1
	}

	n := len(p.Articles)
	lastpage := n/app + 1

	if pg >= lastpage {
		pg = lastpage
	} else {
		p.NextPage = pg + 1
	}

	from := (pg - 1) * app
	to := from + app - 1

	if to > n {
		to = n
	}

	p.Articles = p.Articles[from:to]
}

func (p page) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	e := art.Enabled()
	p.URL = "http://" + r.Host
	p.Pager(r.URL.Query().Get("page"))
	p.TagCloud = e.TagCloud()
	p.Config = conf
	p.MakeArchive()
	p.FirstYear = e.Tail().Year()
	p.LastYear = e.Head().Year()

	err := tmpl.ExecuteTemplate(w, "index.tmpl", p)
	if err != nil {
		log.Println(err)
	}
}

type tagPage struct{ page }

func (p *tagPage) Select(match []string) bool {
	s := match[0]
	p.Articles = art.Tag(s)
	p.Title = s
	return true
}

type indexPage struct{ page }

func (p *indexPage) Select(_ []string) bool {
	p.Articles = art.Enabled()
	return true
}

type slugPage struct{ page }

func (p *slugPage) Select(match []string) bool {
	if a, ok := art.Enabled().Find(match[0]); ok {
		p.Title = a.Title
		p.Articles = articles.Articles{a}
		p.Year = a.Year()
		p.Month = a.Month()
		return true
	}
	return false
}

type yearPage struct{ page }

func (p *yearPage) Select(match []string) bool {
	p.Year = atoiMust(match[0])
	p.Articles = art.Enabled().Year(p.Year)
	p.Title = fmt.Sprint(p.Year)
	return true
}

type monthPage struct{ page }

func (p *monthPage) Select(match []string) bool {
	p.Year = atoiMust(match[0])
	p.Month = time.Month(atoiMust(match[1]))
	p.Articles = art.Enabled().Year(p.Year).Month(p.Month)
	p.Title = fmt.Sprint(p.Month, p.Year)
	return true
}
<commit_msg>Parse page only if needed<commit_after>package main

import (
	"fmt"
	"log"
	"net/http"
	"sort"
	"strconv"
	"time"

	"github.com/dim13/gold/articles"
	"github.com/dim13/gold/storage"
)

type page struct {
	Config    storage.Config
	URL       string
	Title     string
	Articles  articles.Articles
	Error     error
	PrevPage  int
	NextPage  int
	TagCloud  articles.TagCloud
	Year      int
	Month     time.Month
	Archive   []year
	FirstYear int
	LastYear  int
}

type byYear []year
type year struct {
	Year  int
	Count int
	Month []month
}

type byMonth []month
type month struct {
	Month    time.Month
	Year     int
	Count    int
	Articles articles.Articles
}

func (m byMonth) Len() int           { return len(m) }
func (m byMonth) Swap(i, j int)      { m[i], m[j] = m[j], m[i] }
func (m byMonth) Less(i, j int) bool { return m[i].Month < m[j].Month }

func (y byYear) Len() int           { return len(y) }
func (y byYear) Swap(i, j int)      { y[i], y[j] = y[j], y[i] }
func (y byYear) Less(i, j int) bool { return y[i].Year < y[j].Year }

func atoiMust(s string) int {
	i, err := strconv.Atoi(s)
	if err != nil {
		log.Println(err)
		return 0
	}
	return i
}

func (p *page) MakeArchive() {
	if p.Year == 0 {
		p.Year = p.Articles.Head().Year()
	}
	if p.Month == 0 {
		p.Month = p.Articles.Head().Month()
	}
	for y, v := range art.Enabled().YearMap() {
		year := year{
			Year:  y,
			Count: len(v),
		}
		if p.Year == y {
			for m, v := range v.MonthMap() {
				month := month{
					Year:  y,
					Month: time.Month(m),
					Count: len(v),
				}
				if p.Month == time.Month(m) {
					month.Articles = v
				}
				year.Month = append(year.Month, month)
			}
			sort.Sort(byMonth(year.Month))
		}
		p.Archive = append(p.Archive, year)
	}
	sort.Sort(sort.Reverse(byYear(p.Archive)))
}

func (p *page) Pager(arg string) {
	app := conf.Blog.ArticlesPerPage
	pg := 1
	if arg != "" {
		pg = atoiMust(arg)
	}

	if pg <= 1 {
		pg = 1
	} else {
		p.PrevPage = pg - 1
	}

	n := len(p.Articles)
	lastpage := n/app + 1

	if pg >= lastpage {
		pg = lastpage
	} else {
		p.NextPage = pg + 1
	}

	from := (pg - 1) * app
	to := from + app - 1

	if to > n {
		to = n
	}

	p.Articles = p.Articles[from:to]
}

func (p page) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	e := art.Enabled()
	p.URL = "http://" + r.Host
	p.Pager(r.URL.Query().Get("page"))
	p.TagCloud = e.TagCloud()
	p.Config = conf
	p.MakeArchive()
	p.FirstYear = e.Tail().Year()
	p.LastYear = e.Head().Year()

	err := tmpl.ExecuteTemplate(w, "index.tmpl", p)
	if err != nil {
		log.Println(err)
	}
}

type tagPage struct{ page }

func (p *tagPage) Select(match []string) bool {
	s := match[0]
	p.Articles = art.Tag(s)
	p.Title = s
	return true
}

type indexPage struct{ page }

func (p *indexPage) Select(_ []string) bool {
	p.Articles = art.Enabled()
	return true
}

type slugPage struct{ page }

func (p *slugPage) Select(match []string) bool {
	if a, ok := art.Enabled().Find(match[0]); ok {
		p.Title = a.Title
		p.Articles = articles.Articles{a}
		p.Year = a.Year()
		p.Month = a.Month()
		return true
	}
	return false
}

type yearPage struct{ page }

func (p *yearPage) Select(match []string) bool {
	p.Year = atoiMust(match[0])
	p.Articles = art.Enabled().Year(p.Year)
	p.Title = fmt.Sprint(p.Year)
	return true
}

type monthPage struct{ page }

func (p *monthPage) Select(match []string) bool {
	p.Year = atoiMust(match[0])
	p.Month = time.Month(atoiMust(match[1]))
	p.Articles = art.Enabled().Year(p.Year).Month(p.Month)
	p.Title = fmt.Sprint(p.Month, p.Year)
	return true
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"html/template"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strconv"
	"time"

	ab "gopkg.in/authboss.v0"
	_ "gopkg.in/authboss.v0/auth"
	_ "gopkg.in/authboss.v0/confirm"
	_ "gopkg.in/authboss.v0/lock"
	_ "gopkg.in/authboss.v0/recover"
	_ "gopkg.in/authboss.v0/register"
	_ "gopkg.in/authboss.v0/remember"

	"github.com/aarondl/tpl"
	"github.com/gorilla/mux"
	"github.com/gorilla/schema"
	"github.com/gorilla/securecookie"
	"github.com/gorilla/sessions"
	"github.com/justinas/alice"
	"github.com/justinas/nosurf"
)

var funcs = template.FuncMap{
	"formatDate": func(date time.Time) string {
		return date.Format("2006/01/02 03:04pm")
	},
	"yield": func() string { return "" },
}

var (
	database  = NewMemStorer()
	templates = tpl.Must(tpl.Load("views", "views/partials", "layout.html.tpl", funcs))
	schemaDec = schema.NewDecoder()
)

func setupAuthboss() {
	ab.Cfg = ab.NewConfig()
	ab.Cfg.Storer = database
	ab.Cfg.MountPath = "/auth"
	ab.Cfg.LogWriter = os.Stdout
	ab.Cfg.HostName = `http://localhost:3000`

	ab.Cfg.LayoutDataMaker = layoutData

	b, err := ioutil.ReadFile(`views\layout.html.tpl`)
	if err != nil {
		panic(err)
	}
	ab.Cfg.Layout = template.Must(template.New("layout").Funcs(funcs).Parse(string(b)))
	//ab.Cfg.LayoutEmail = template.Must(template.New("layout").Parse(`{{template "authboss" .}}`))

	ab.Cfg.XSRFName = "csrf_token"
	ab.Cfg.XSRFMaker = func(_ http.ResponseWriter, r *http.Request) string {
		return nosurf.Token(r)
	}

	ab.Cfg.CookieStoreMaker = NewCookieStorer
	ab.Cfg.SessionStoreMaker = NewSessionStorer

	//ab.Cfg.Mailer = ab.SMTPMailer("smtp.gmail.com:587", smtp.PlainAuth("bits128@gmail.com", "bits128@gmail.com", "fbzyhhlgrcyxwkqz", "smtp.gmail.com"))
	ab.Cfg.Mailer = ab.LogMailer(os.Stdout)

	if err := ab.Init(); err != nil {
		log.Fatal(err)
	}
}

func main() {
	// Initialize Sessions and Cookies
	cookieStore = securecookie.New([]byte("very-secret"), nil)
	sessionStore = sessions.NewCookieStore([]byte("asdf"))

	// Initialize ab.
	setupAuthboss()

	// Set up our router
	schemaDec.IgnoreUnknownKeys(true)
	mux := mux.NewRouter()

	// Routes
	gets := mux.Methods("GET").Subrouter()
	posts := mux.Methods("POST").Subrouter()

	mux.PathPrefix("/auth").Handler(ab.NewRouter())

	gets.HandleFunc("/blogs/new", new)
	gets.HandleFunc("/blogs/{id}/edit", edit)
	gets.HandleFunc("/blogs/{id}", show)
	gets.HandleFunc("/blogs", index)
	gets.HandleFunc("/", index)

	posts.HandleFunc("/blogs/{id}", update)
	posts.HandleFunc("/blogs", create)

	// This should actually be a destroys.X but I can't be bothered to make a proper
	// destroy link using javascript atm.
	gets.HandleFunc("/blogs/{id}/destroy", destroy)

	mux.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusNotFound)
		io.WriteString(w, "Not found")
	})

	// Set up our middleware chain
	stack := alice.New(logger /*, nosurfing*/, ab.ExpireMiddleware).Then(mux)

	// Start the server
	port := os.Getenv("PORT")
	if len(port) == 0 {
		port = "8080"
	}
	log.Println(http.ListenAndServe("localhost:"+port, stack))
}

func layoutData(w http.ResponseWriter, r *http.Request) ab.HTMLData {
	currentUserName := ""
	userInter, err := ab.CurrentUser(w, r)
	if userInter != nil && err == nil {
		currentUserName = userInter.(*User).Name
	}

	return ab.HTMLData{
		"loggedin":          userInter != nil,
		"username":          "",
		ab.FlashSuccessKey:  ab.FlashSuccess(w, r),
		ab.FlashErrorKey:    ab.FlashError(w, r),
		"csrf_token":        nosurf.Token(r),
		"current_user_name": currentUserName,
	}
}

func index(w http.ResponseWriter, r *http.Request) {
	data := layoutData(w, r).MergeKV("posts", blogs)
	mustRender(w, "index", data)
}

func show(w http.ResponseWriter, r *http.Request) {
	id, ok := blogID(w, r)
	if !ok {
		return
	}

	data := layoutData(w, r).MergeKV("post", blogs.Get(id))
	err := templates.Render(w, "show", data)
	if err != nil {
		panic(err)
	}
}

func new(w http.ResponseWriter, r *http.Request) {
	data := layoutData(w, r).MergeKV("post", Blog{})
	mustRender(w, "new", data)
}

var nextID = len(blogs) + 1

func create(w http.ResponseWriter, r *http.Request) {
	err := r.ParseForm()
	if badRequest(w, err) {
		return
	}

	// TODO: Validation

	var b Blog
	if badRequest(w, schemaDec.Decode(&b, r.PostForm)) {
		return
	}

	b.ID = nextID
	nextID++
	b.Date = time.Now()
	b.AuthorID = "Zeratul"

	blogs = append(blogs, b)

	data := layoutData(w, r).MergeKV("post", b)
	mustRender(w, "show", data)
}

func edit(w http.ResponseWriter, r *http.Request) {
	id, ok := blogID(w, r)
	if !ok {
		return
	}

	data := layoutData(w, r).MergeKV("post", blogs.Get(id))
	mustRender(w, "edit", data)
}

func update(w http.ResponseWriter, r *http.Request) {
	err := r.ParseForm()
	if badRequest(w, err) {
		return
	}

	id, ok := blogID(w, r)
	if !ok {
		return
	}

	// TODO: Validation

	var b = blogs.Get(id)
	if badRequest(w, schemaDec.Decode(b, r.PostForm)) {
		return
	}

	b.Date = time.Now()

	data := layoutData(w, r).MergeKV("post", b)
	mustRender(w, "show", data)
}

func destroy(w http.ResponseWriter, r *http.Request) {
	id, ok := blogID(w, r)
	if !ok {
		return
	}

	blogs.Delete(id)

	data := layoutData(w, r).MergeKV("posts", blogs)
	mustRender(w, "index", data)
}

func blogID(w http.ResponseWriter, r *http.Request) (int, bool) {
	vars := mux.Vars(r)
	str := vars["id"]

	id, err := strconv.Atoi(str)
	if err != nil {
		log.Println("Error parsing blog id:", err)
		http.Redirect(w, r, "/", http.StatusFound)
		return 0, false
	}

	if id <= 0 {
		http.Redirect(w, r, "/", http.StatusFound)
		return 0, false
	}

	return id, true
}

func mustRender(w http.ResponseWriter, name string, data interface{}) {
	err := templates.Render(w, name, data)
	if err == nil {
		return
	}

	w.Header().Set("Content-Type", "text/plain")
	w.WriteHeader(http.StatusInternalServerError)
	fmt.Fprintln(w, "Error occurred rendering template:", err)
}

func badRequest(w http.ResponseWriter, err error) bool {
	if err == nil {
		return false
	}

	w.Header().Set("Content-Type", "text/plain")
	w.WriteHeader(http.StatusBadRequest)
	fmt.Fprintln(w, "Bad request:", err)

	return true
}
<commit_msg>XSRF now works magically somehow.<commit_after>package main

import (
	"fmt"
	"html/template"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"time"

	ab "gopkg.in/authboss.v0"
	_ "gopkg.in/authboss.v0/auth"
	_ "gopkg.in/authboss.v0/confirm"
	_ "gopkg.in/authboss.v0/lock"
	_ "gopkg.in/authboss.v0/recover"
	_ "gopkg.in/authboss.v0/register"
	_ "gopkg.in/authboss.v0/remember"

	"github.com/aarondl/tpl"
	"github.com/gorilla/mux"
	"github.com/gorilla/schema"
	"github.com/gorilla/securecookie"
	"github.com/gorilla/sessions"
	"github.com/justinas/alice"
	"github.com/justinas/nosurf"
)

var funcs = template.FuncMap{
	"formatDate": func(date time.Time) string {
		return date.Format("2006/01/02 03:04pm")
	},
	"yield": func() string { return "" },
}

var (
	database  = NewMemStorer()
	templates = tpl.Must(tpl.Load("views", "views/partials", "layout.html.tpl", funcs))
	schemaDec = schema.NewDecoder()
)

func setupAuthboss() {
	ab.Cfg = ab.NewConfig()
	ab.Cfg.Storer = database
	ab.Cfg.MountPath = "/auth"
	ab.Cfg.LogWriter = os.Stdout
	ab.Cfg.HostName = `http://localhost:3000`

	ab.Cfg.LayoutDataMaker = layoutData

	b, err := ioutil.ReadFile(filepath.Join("views", "layout.html.tpl"))
	if err != nil {
		panic(err)
	}
	ab.Cfg.Layout = template.Must(template.New("layout").Funcs(funcs).Parse(string(b)))
	//ab.Cfg.LayoutEmail = template.Must(template.New("layout").Parse(`{{template "authboss" .}}`))

	ab.Cfg.XSRFName = "csrf_token"
	ab.Cfg.XSRFMaker = func(_ http.ResponseWriter, r *http.Request) string {
		return nosurf.Token(r)
	}

	ab.Cfg.CookieStoreMaker = NewCookieStorer
	ab.Cfg.SessionStoreMaker = NewSessionStorer

	//ab.Cfg.Mailer = ab.SMTPMailer("smtp.gmail.com:587", smtp.PlainAuth("you@gmail.com", "you@gmail.com", "password", "smtp.gmail.com"))
	ab.Cfg.Mailer = ab.LogMailer(os.Stdout)

	if err := ab.Init(); err != nil {
		log.Fatal(err)
	}
}

func main() {
	// Initialize Sessions and Cookies
	cookieStore = securecookie.New([]byte("very-secret"), nil)
	sessionStore = sessions.NewCookieStore([]byte("asdf"))

	// Initialize ab.
	setupAuthboss()

	// Set up our router
	schemaDec.IgnoreUnknownKeys(true)
	mux := mux.NewRouter()

	// Routes
	gets := mux.Methods("GET").Subrouter()
	posts := mux.Methods("POST").Subrouter()

	mux.PathPrefix("/auth").Handler(ab.NewRouter())

	gets.HandleFunc("/blogs/new", new)
	gets.HandleFunc("/blogs/{id}/edit", edit)
	gets.HandleFunc("/blogs/{id}", show)
	gets.HandleFunc("/blogs", index)
	gets.HandleFunc("/", index)

	posts.HandleFunc("/blogs/{id}", update)
	posts.HandleFunc("/blogs", create)

	// This should actually be a destroys.X but I can't be bothered to make a proper
	// destroy link using javascript atm.
	gets.HandleFunc("/blogs/{id}/destroy", destroy)

	mux.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusNotFound)
		io.WriteString(w, "Not found")
	})

	// Set up our middleware chain
	stack := alice.New(logger, nosurfing, ab.ExpireMiddleware).Then(mux)

	// Start the server
	port := os.Getenv("PORT")
	if len(port) == 0 {
		port = "8080"
	}
	log.Println(http.ListenAndServe("localhost:"+port, stack))
}

func layoutData(w http.ResponseWriter, r *http.Request) ab.HTMLData {
	currentUserName := ""
	userInter, err := ab.CurrentUser(w, r)
	if userInter != nil && err == nil {
		currentUserName = userInter.(*User).Name
	}

	return ab.HTMLData{
		"loggedin":          userInter != nil,
		"username":          "",
		ab.FlashSuccessKey:  ab.FlashSuccess(w, r),
		ab.FlashErrorKey:    ab.FlashError(w, r),
		"csrf_token":        nosurf.Token(r),
		"current_user_name": currentUserName,
	}
}

func index(w http.ResponseWriter, r *http.Request) {
	data := layoutData(w, r).MergeKV("posts", blogs)
	mustRender(w, "index", data)
}

func show(w http.ResponseWriter, r *http.Request) {
	id, ok := blogID(w, r)
	if !ok {
		return
	}

	data := layoutData(w, r).MergeKV("post", blogs.Get(id))
	err := templates.Render(w, "show", data)
	if err != nil {
		panic(err)
	}
}

func new(w http.ResponseWriter, r *http.Request) {
	data := layoutData(w, r).MergeKV("post", Blog{})
	mustRender(w, "new", data)
}

var nextID = len(blogs) + 1

func create(w http.ResponseWriter, r *http.Request) {
	err := r.ParseForm()
	if badRequest(w, err) {
		return
	}

	// TODO: Validation

	var b Blog
	if badRequest(w, schemaDec.Decode(&b, r.PostForm)) {
		return
	}

	b.ID = nextID
	nextID++
	b.Date = time.Now()
	b.AuthorID = "Zeratul"

	blogs = append(blogs, b)

	data := layoutData(w, r).MergeKV("post", b)
	mustRender(w, "show", data)
}

func edit(w http.ResponseWriter, r *http.Request) {
	id, ok := blogID(w, r)
	if !ok {
		return
	}

	data := layoutData(w, r).MergeKV("post", blogs.Get(id))
	mustRender(w, "edit", data)
}

func update(w http.ResponseWriter, r *http.Request) {
	err := r.ParseForm()
	if badRequest(w, err) {
		return
	}

	id, ok := blogID(w, r)
	if !ok {
		return
	}

	// TODO: Validation

	var b = blogs.Get(id)
	if badRequest(w, schemaDec.Decode(b, r.PostForm)) {
		return
	}

	b.Date = time.Now()

	data := layoutData(w, r).MergeKV("post", b)
	mustRender(w, "show", data)
}

func destroy(w http.ResponseWriter, r *http.Request) {
	id, ok := blogID(w, r)
	if !ok {
		return
	}

	blogs.Delete(id)

	data := layoutData(w, r).MergeKV("posts", blogs)
	mustRender(w, "index", data)
}

func blogID(w http.ResponseWriter, r *http.Request) (int, bool) {
	vars := mux.Vars(r)
	str := vars["id"]

	id, err := strconv.Atoi(str)
	if err != nil {
		log.Println("Error parsing blog id:", err)
		http.Redirect(w, r, "/", http.StatusFound)
		return 0, false
	}

	if id <= 0 {
		http.Redirect(w, r, "/", http.StatusFound)
		return 0, false
	}

	return id, true
}

func mustRender(w http.ResponseWriter, name string, data interface{}) {
	err := templates.Render(w, name, data)
	if err == nil {
		return
	}

	w.Header().Set("Content-Type", "text/plain")
	w.WriteHeader(http.StatusInternalServerError)
	fmt.Fprintln(w, "Error occurred rendering template:", err)
}

func badRequest(w http.ResponseWriter, err error) bool {
	if err == nil {
		return false
	}

	w.Header().Set("Content-Type", "text/plain")
	w.WriteHeader(http.StatusBadRequest)
	fmt.Fprintln(w, "Bad request:", err)

	return true
}
<|endoftext|>


<commit_before>package render

import (
	"encoding/json"
	"io"
	"io/ioutil"
	"net/http"
)

func JSON(w http.ResponseWriter, status int, v interface{}) {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.WriteHeader(status)
	enc := json.NewEncoder(w)
	enc.SetEscapeHTML(true)

	if err := enc.Encode(v); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
}

func BindJSON(r io.Reader, v interface{}) error {
	defer io.Copy(ioutil.Discard, r)
	return json.NewDecoder(r).Decode(v)
}
<commit_msg>fix render json<commit_after>package render

import (
	"encoding/json"
	"io"
	"io/ioutil"
	"net/http"
)

func Json(w http.ResponseWriter, status int, v interface{}) {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.WriteHeader(status)
	enc := json.NewEncoder(w)
	enc.SetEscapeHTML(true)

	if err := enc.Encode(v); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
}

func BindJson(r io.Reader, v interface{}) error {
	defer io.Copy(ioutil.Discard, r)
	return json.NewDecoder(r).Decode(v)
}
<|endoftext|>


<commit_before>package render

import (
	"image"
	"image/color"
	"image/draw"

	"github.com/oakmound/oak/render/mod"
)

// A Sprite is a basic wrapper around image data and a point. The most basic Renderable.
type Sprite struct {
	LayeredPoint
	r *image.RGBA
}

// NewEmptySprite returns a sprite of the given dimensions with a blank RGBA
func NewEmptySprite(x, y float64, w, h int) *Sprite {
	r := image.NewRGBA(image.Rect(0, 0, w, h))
	return NewSprite(x, y, r)
}

// NewSprite creates a new sprite
func NewSprite(x, y float64, r *image.RGBA) *Sprite {
	return &Sprite{
		LayeredPoint: NewLayeredPoint(x, y, 0),
		r:            r,
	}
}

// GetRGBA returns the rgba behind this sprite
func (s *Sprite) GetRGBA() *image.RGBA {
	return s.r
}

// GetDims returns the dimensions of this sprite, or if this sprite has no
// defined RGBA returns default values.
// BUG: The reason the default values of 6,6 are returned is to cover a bug
// in the library we are using for polygon intersection. Too small objects
// will always be considered to intersect a draw polygon.
func (s *Sprite) GetDims() (int, int) {
	if s.r == nil {
		return 6, 6
	}
	return s.r.Bounds().Max.X, s.r.Bounds().Max.Y
}

// SetRGBA will replace the rgba behind this sprite
func (s *Sprite) SetRGBA(r *image.RGBA) {
	s.r = r
}

// Bounds is an alternative to GetDims that alows a sprite
// to satisy draw.Image.
func (s *Sprite) Bounds() image.Rectangle {
	return s.r.Bounds()
}

// ColorModel allows sprites to satisfy draw.Image. Returns
// color.RGBA.
func (s *Sprite) ColorModel() color.Model {
	return s.r.ColorModel()
}

// At returns the color of a given pixel location
func (s *Sprite) At(x, y int) color.Color {
	return s.r.At(x, y)
}

// Set sets a color of a given pixel location
func (s *Sprite) Set(x, y int, c color.Color) {
	s.r.Set(x, y, c)
}

// DrawOffset draws this sprite at +xOff, +yOff
func (s *Sprite) DrawOffset(buff draw.Image, xOff, yOff float64) {
	ShinyDraw(buff, s.r, int(s.X()+xOff), int(s.Y()+yOff))
}

// Draw draws this sprite onto the input buffer
func (s *Sprite) Draw(buff draw.Image) {
	ShinyDraw(buff, s.r, int(s.X()), int(s.Y()))
}

// Copy returns a copy of this Sprite
func (s *Sprite) Copy() Modifiable {
	newS := new(Sprite)
	if s.r != nil {
		newS.r = new(image.RGBA)
		*newS.r = *s.r
	}
	newS.LayeredPoint = s.LayeredPoint.Copy()
	return newS
}

// IsNil returns whether or not this sprite's rgba is nil.
func (s *Sprite) IsNil() bool {
	return s.r == nil
}

// Modify takes in modifications (modify.go) and alters this sprite accordingly
func (s *Sprite) Modify(ms ...mod.Mod) Modifiable {
	for _, m := range ms {
		s.r = m(s.GetRGBA())
	}
	return s
}

// Filter filters this sprite's rgba on all the input filters
func (s *Sprite) Filter(fs ...mod.Filter) {
	for _, f := range fs {
		f(s.r)
	}
}

// OverlaySprites combines sprites together through masking to form a single sprite
func OverlaySprites(sps []Sprite) *Sprite {
	tmpSprite := sps[len(sps)-1].Copy().(*Sprite)
	for i := len(sps) - 1; i > 0; i-- {
		mod.FillMask(*sps[i-1].GetRGBA())(tmpSprite.GetRGBA())
	}
	return tmpSprite
}

// ParseSubSprite pulls a sprite from a loaded sheet
func ParseSubSprite(sheet string, x, y, w, h, pad int) *Sprite {
	sh, _ := LoadSheet(dir, sheet, w, h, pad)
	return sh.SubSprite(x, y)
}
<commit_msg>Fix to sprite Copy<commit_after>package render

import (
	"image"
	"image/color"
	"image/draw"

	"github.com/oakmound/oak/render/mod"
)

// A Sprite is a basic wrapper around image data and a point. The most basic Renderable.
type Sprite struct {
	LayeredPoint
	r *image.RGBA
}

// NewEmptySprite returns a sprite of the given dimensions with a blank RGBA
func NewEmptySprite(x, y float64, w, h int) *Sprite {
	r := image.NewRGBA(image.Rect(0, 0, w, h))
	return NewSprite(x, y, r)
}

// NewSprite creates a new sprite
func NewSprite(x, y float64, r *image.RGBA) *Sprite {
	return &Sprite{
		LayeredPoint: NewLayeredPoint(x, y, 0),
		r:            r,
	}
}

// GetRGBA returns the rgba behind this sprite
func (s *Sprite) GetRGBA() *image.RGBA {
	return s.r
}

// GetDims returns the dimensions of this sprite, or if this sprite has no
// defined RGBA returns default values.
// BUG: The reason the default values of 6,6 are returned is to cover a bug
// in the library we are using for polygon intersection. Too small objects
// will always be considered to intersect a draw polygon.
func (s *Sprite) GetDims() (int, int) {
	if s.r == nil {
		return 6, 6
	}
	return s.r.Bounds().Max.X, s.r.Bounds().Max.Y
}

// SetRGBA will replace the rgba behind this sprite
func (s *Sprite) SetRGBA(r *image.RGBA) {
	s.r = r
}

// Bounds is an alternative to GetDims that alows a sprite
// to satisy draw.Image.
func (s *Sprite) Bounds() image.Rectangle {
	return s.r.Bounds()
}

// ColorModel allows sprites to satisfy draw.Image. Returns
// color.RGBA.
func (s *Sprite) ColorModel() color.Model {
	return s.r.ColorModel()
}

// At returns the color of a given pixel location
func (s *Sprite) At(x, y int) color.Color {
	return s.r.At(x, y)
}

// Set sets a color of a given pixel location
func (s *Sprite) Set(x, y int, c color.Color) {
	s.r.Set(x, y, c)
}

// DrawOffset draws this sprite at +xOff, +yOff
func (s *Sprite) DrawOffset(buff draw.Image, xOff, yOff float64) {
	ShinyDraw(buff, s.r, int(s.X()+xOff), int(s.Y()+yOff))
}

// Draw draws this sprite onto the input buffer
func (s *Sprite) Draw(buff draw.Image) {
	ShinyDraw(buff, s.r, int(s.X()), int(s.Y()))
}

// Copy returns a copy of this Sprite
func (s *Sprite) Copy() Modifiable {
	newS := new(Sprite)
	if s.r != nil {
		newS.r = rgbaCopy(s.r)
	}
	newS.LayeredPoint = s.LayeredPoint.Copy()
	return newS
}

func rgbaCopy(r *image.RGBA) *image.RGBA {
	newRgba := new(image.RGBA)
	newRgba.Rect = r.Rect
	newRgba.Stride = r.Stride
	newRgba.Pix = make([]uint8, len(r.Pix))
	for i, p := range r.Pix {
		newRgba.Pix[i] = p
	}
	return newRgba
}

// IsNil returns whether or not this sprite's rgba is nil.
func (s *Sprite) IsNil() bool {
	return s.r == nil
}

// Modify takes in modifications (modify.go) and alters this sprite accordingly
func (s *Sprite) Modify(ms ...mod.Mod) Modifiable {
	for _, m := range ms {
		s.r = m(s.GetRGBA())
	}
	return s
}

// Filter filters this sprite's rgba on all the input filters
func (s *Sprite) Filter(fs ...mod.Filter) {
	for _, f := range fs {
		f(s.r)
	}
}

// OverlaySprites combines sprites together through masking to form a single sprite
func OverlaySprites(sps []Sprite) *Sprite {
	tmpSprite := sps[len(sps)-1].Copy().(*Sprite)
	for i := len(sps) - 1; i > 0; i-- {
		mod.FillMask(*sps[i-1].GetRGBA())(tmpSprite.GetRGBA())
	}
	return tmpSprite
}

// ParseSubSprite pulls a sprite from a loaded sheet
func ParseSubSprite(sheet string, x, y, w, h, pad int) *Sprite {
	sh, _ := LoadSheet(dir, sheet, w, h, pad)
	return sh.SubSprite(x, y)
}
<|endoftext|>


<commit_before>package main

import irc "github.com/fluffle/goirc/client"
import "math/rand"
import "regexp"

// Guilhem Bot core
func handleMessageGuilhem(conn *irc.Conn, line *irc.Line) {
	technicalAnswers := []string{
		"Mais ! C'est de la merde !",
		"On doit le réecrire en GO ça !",
		"Mais c'est pas du tout ça le problème, en fait […]",
		"C'est fait roulé sous les aisselles",
		"I have no *FRAKING* idea!",
		"NAN ! Je suis en copie, je suis perché !",
		"je veux pas devenir un SPOF",
		"'Tain IRC ! Protocole de merde ! Je vais mettre en place un XM Peuh Peuh",
		"Non mais ça c'est soooo 2011 !",
		"Les mecs de chez […] ont rien compris, j'te fouterai tout le monde à la porte moi.",
		"Désolé d'avoir raison",
	}

	personalAnswers := []string{
		"Mais j'suis pas super fort, j'suis mieux qu'ça même. J'suis surpuissant.",
		"Je parle fort… J'ai du charisme, c'est tout !",
		"Yves, Yves, YVES ! Laisse nous parler",
		"Yves, Yves, YVES ! Après le daily stp !",
		"J'ai un super nom pour le deuxième DC ! WATTOO WATTOO",
		"Attends, y'a mon nom sur un t-shirt s'il-te-plaît !",
		"J'me vois pas me priver de bonheur parce que les autres veulent une vie de merde",
		"«Vous ne pouvez *résoudre* un problème avec le *même* type de *pensée* qui a créé le problème» A.Einstein",
		"Il faut que je me sente… Important",
		"Oula oui, au moins ça !",
		"«rm -Rf de-toute-l-équipe», oui !",
		"Nan, mais j'illumine la tour eiffel depuis chez moi !",
	}

	openstackRegexp, _ := regexp.Compile("(?i)jarvis|openstack|keystone|deploy|datacenter|bug|meeting|horizon|glance|cinder|contrail|coreos|dc|nova|neutron")
	technicalPoint := openstackRegexp.FindString(line.Text())
	randomLine := rand.Intn(40)

	if technicalPoint != "" {
		conn.Privmsg(
			channelName,
			technicalAnswers[rand.Intn(len(technicalAnswers))],
		)
	} else if randomLine < 3 {
		conn.Privmsg(
			channelName,
			personalAnswers[rand.Intn(len(personalAnswers))],
		)
	}
}

// ygbo Bot core
func handleMessageYgbot(conn *irc.Conn, line *irc.Line) {
	technicalAnswers := []string{
		"Ça a été codé avec le cul!",
		"C'est fait par un mec qu'a le QI d'un anus artificiel!",
		"J'y crois pas! jamais vu une horreur pareille!",
		"Une GUI pensée avec un QI minimum est sensé être plus simple que la CLI, si la CLI est plus simple, c'est que la target de la GUI est ratée!",
		"Les paquets Debian sont au développement ce que les cartons d'emballage sont aux meubles Ikéa.",
	}

	personalAnswers := []string{
		"Hein?",
		"Hein?",
		"Hein?",
		"Tu disait?",
	}

	personalAnswersFix := []string{
		"",
		"",
		"",
		"s/disait/disias/",
	}

	personalAnswersFixFix := []string{
		"",
		"",
		"",
		"t'ain d'clavire!",
	}

	openstackRegexp, _ := regexp.Compile("(?i)javascript|bug|git|kibana|jira|selenium|packaging|kde|unity")
	technicalPoint := openstackRegexp.FindString(line.Text())
	randomLine := rand.Intn(40)

	if technicalPoint != "" {
		randomAnswer := rand.Intn(len(technicalAnswers))
		conn.Privmsg(
			channelName,
			technicalAnswers[randomAnswer],
		)
	} else if randomLine < 3 {
		randomAnswer := rand.Intn(len(personalAnswers))
		conn.Privmsg(
			channelName,
			personalAnswers[randomAnswer],
		)
		if personalAnswersFix[randomAnswer] != "" {
			conn.Privmsg(
				channelName,
				personalAnswersFix[randomAnswer],
			)
			if personalAnswersFixFix[randomAnswer] != "" {
				conn.Privmsg(
					channelName,
					personalAnswersFixFix[randomAnswer],
				)
			}
		}
	}
}

// Carot Bot core
func handleMessageCarot(conn *irc.Conn, line *irc.Line) {
	technicalAnswers := []string{
		"La test suite est rouge, mais ça a marché",
		"Adil, le front est lent !",
		"J'peux pas valider la story, il manque un accent circonflexe...",
		"Chez moi ça marche, donc c'est bon",
		"C'est pas automatisable. On ne peut pas valider la story.",
		"Regarde dans 2 minutes",
		"C'est KO, mais c'est OK",
		"Vide ton cache",
		"Relance ton navigateur",
		"C'est pas un bug, c'est une feature...",
	}
	personalAnswers := []string{
		"J'vais t'peter la gueule",
		"Tu veux t'battre ?",
		"Oh, il est beau ton bleu !",
		"Chocolat !?",
		"Est-ce que t'es bon au baby ?",
		"Demain, j'ramène des cookies",
		"On va boire une bière vite fait ce soir ?",
		"Lâche pas les manettes !!",
		"Y a des croissants chez BSS :)",
		"Faut la rouler sous les pieds",
		"Tu me dois une bière !",
		"T'es pas sur l'IRC !",
		"C'est toi le [...] !",
	}

	randomLine := rand.Intn(40)
	openstackRegexp, _ := regexp.Compile("(?i)selenium|test suite|stg0|dev32|jira|dev37|bug|story|front|billing|CRM")
	personalRegexp, _ := regexp.Compile("(?i)biere|chocolat|battre|baby|foot")
	technicalPoint := openstackRegexp.FindString(line.Text())
	personalPoint := personalRegexp.FindString(line.Text())

	if technicalPoint != "" {
		conn.Privmsg(
			channelName,
			technicalAnswers[rand.Intn(len(technicalAnswers))],
		)
	} else if randomLine < 3 || personalPoint != "" {
		conn.Privmsg(
			channelName,
			personalAnswers[rand.Intn(len(personalAnswers))],
		)
	}

}

// VBAbot Bot core
func handleMessageVBAbot(conn *irc.Conn, line *irc.Line) {
	technicalAnswers := []string{
		"Un truc codé avec emacs par un manchot !",
		"Evidemment c est encore la faute aux sysadmins :(",
		"Ils auraient du le faire en cobol !",
		"Cette bouse me reveille toutes les nuits !",
		"As tu teste en dev ? integration ? staging ?",
		"Dans le doute reboot ?",
		"Et si on le recodait en LISP ?",
		"As tu fait le ticket pour ce change ?",
		"VMware !",
		"Faudrait refactor le cookbook au fait :)",
		"Mieux vaut tar que gz",
		"C'est un René Pierre !",
		"Et bim…",
	}

	personalAnswers := []string{
		"ASV ?",
		"L'informatique dans les nuages…",
		"Jamais le vendredi !",
		"Oui ce serait effectivement mieux avec jabber",
		"Ce sera en salle cumulonimbus",
		"J'aime pas les cookies a la cachuete.",
		"Plus, mieux, mais moins cher !",
	}

	techRegexp, _ := regexp.Compile("(?i)opencontrail|contrail|neutron|django|nova|nagios|kibana|bss|prod|staging|integration|irc|bot")
	technicalPoint := techRegexp.FindString(line.Text())
	randomLine := rand.Intn(40)

	if technicalPoint != "" {
		randomAnswer := rand.Intn(len(technicalAnswers))
		conn.Privmsg(
			channelName,
			technicalAnswers[randomAnswer],
		)
	} else if randomLine < 3 {
		randomAnswer := rand.Intn(len(personalAnswers))
		conn.Privmsg(
			channelName,
			personalAnswers[randomAnswer],
		)
	}
}
<commit_msg>Add personal answer<commit_after>package main

import irc "github.com/fluffle/goirc/client"
import "math/rand"
import "regexp"

// Guilhem Bot core
func handleMessageGuilhem(conn *irc.Conn, line *irc.Line) {
	technicalAnswers := []string{
		"Mais ! C'est de la merde !",
		"On doit le réecrire en GO ça !",
		"Mais c'est pas du tout ça le problème, en fait […]",
		"C'est fait roulé sous les aisselles",
		"I have no *FRAKING* idea!",
		"NAN ! Je suis en copie, je suis perché !",
		"je veux pas devenir un SPOF",
		"'Tain IRC ! Protocole de merde ! Je vais mettre en place un XM Peuh Peuh",
		"Non mais ça c'est soooo 2011 !",
		"Les mecs de chez […] ont rien compris, j'te fouterai tout le monde à la porte moi.",
		"Désolé d'avoir raison",
		"Je ne veux pas d'un consensus mou",
	}

	personalAnswers := []string{
		"Mais j'suis pas super fort, j'suis mieux qu'ça même. J'suis surpuissant.",
		"Je parle fort… J'ai du charisme, c'est tout !",
		"Yves, Yves, YVES ! Laisse nous parler",
		"Yves, Yves, YVES ! Après le daily stp !",
		"J'ai un super nom pour le deuxième DC ! WATTOO WATTOO",
		"Attends, y'a mon nom sur un t-shirt s'il-te-plaît !",
		"J'me vois pas me priver de bonheur parce que les autres veulent une vie de merde",
		"«Vous ne pouvez *résoudre* un problème avec le *même* type de *pensée* qui a créé le problème» A.Einstein",
		"Il faut que je me sente… Important",
		"Oula oui, au moins ça !",
		"«rm -Rf de-toute-l-équipe», oui !",
		"Nan, mais j'illumine la tour eiffel depuis chez moi !",
	}

	openstackRegexp, _ := regexp.Compile("(?i)jarvis|openstack|keystone|deploy|datacenter|bug|meeting|horizon|glance|cinder|contrail|coreos|dc|nova|neutron")
	technicalPoint := openstackRegexp.FindString(line.Text())
	randomLine := rand.Intn(40)

	if technicalPoint != "" {
		conn.Privmsg(
			channelName,
			technicalAnswers[rand.Intn(len(technicalAnswers))],
		)
	} else if randomLine < 3 {
		conn.Privmsg(
			channelName,
			personalAnswers[rand.Intn(len(personalAnswers))],
		)
	}
}

// ygbo Bot core
func handleMessageYgbot(conn *irc.Conn, line *irc.Line) {
	technicalAnswers := []string{
		"Ça a été codé avec le cul!",
		"C'est fait par un mec qu'a le QI d'un anus artificiel!",
		"J'y crois pas! jamais vu une horreur pareille!",
		"Une GUI pensée avec un QI minimum est sensé être plus simple que la CLI, si la CLI est plus simple, c'est que la target de la GUI est ratée!",
		"Les paquets Debian sont au développement ce que les cartons d'emballage sont aux meubles Ikéa.",
	}

	personalAnswers := []string{
		"Hein?",
		"Hein?",
		"Hein?",
		"Tu disait?",
	}

	personalAnswersFix := []string{
		"",
		"",
		"",
		"s/disait/disias/",
	}

	personalAnswersFixFix := []string{
		"",
		"",
		"",
		"t'ain d'clavire!",
	}

	openstackRegexp, _ := regexp.Compile("(?i)javascript|bug|git|kibana|jira|selenium|packaging|kde|unity")
	technicalPoint := openstackRegexp.FindString(line.Text())
	randomLine := rand.Intn(40)

	if technicalPoint != "" {
		randomAnswer := rand.Intn(len(technicalAnswers))
		conn.Privmsg(
			channelName,
			technicalAnswers[randomAnswer],
		)
	} else if randomLine < 3 {
		randomAnswer := rand.Intn(len(personalAnswers))
		conn.Privmsg(
			channelName,
			personalAnswers[randomAnswer],
		)
		if personalAnswersFix[randomAnswer] != "" {
			conn.Privmsg(
				channelName,
				personalAnswersFix[randomAnswer],
			)
			if personalAnswersFixFix[randomAnswer] != "" {
				conn.Privmsg(
					channelName,
					personalAnswersFixFix[randomAnswer],
				)
			}
		}
	}
}

// Carot Bot core
func handleMessageCarot(conn *irc.Conn, line *irc.Line) {
	technicalAnswers := []string{
		"La test suite est rouge, mais ça a marché",
		"Adil, le front est lent !",
		"J'peux pas valider la story, il manque un accent circonflexe...",
		"Chez moi ça marche, donc c'est bon",
		"C'est pas automatisable. On ne peut pas valider la story.",
		"Regarde dans 2 minutes",
		"C'est KO, mais c'est OK",
		"Vide ton cache",
		"Relance ton navigateur",
		"C'est pas un bug, c'est une feature...",
	}
	personalAnswers := []string{
		"J'vais t'peter la gueule",
		"Tu veux t'battre ?",
		"Oh, il est beau ton bleu !",
		"Chocolat !?",
		"Est-ce que t'es bon au baby ?",
		"Demain, j'ramène des cookies",
		"On va boire une bière vite fait ce soir ?",
		"Lâche pas les manettes !!",
		"Y a des croissants chez BSS :)",
		"Faut la rouler sous les pieds",
		"Tu me dois une bière !",
		"T'es pas sur l'IRC !",
		"C'est toi le [...] !",
	}

	randomLine := rand.Intn(40)
	openstackRegexp, _ := regexp.Compile("(?i)selenium|test suite|stg0|dev32|jira|dev37|bug|story|front|billing|CRM")
	personalRegexp, _ := regexp.Compile("(?i)biere|chocolat|battre|baby|foot")
	technicalPoint := openstackRegexp.FindString(line.Text())
	personalPoint := personalRegexp.FindString(line.Text())

	if technicalPoint != "" {
		conn.Privmsg(
			channelName,
			technicalAnswers[rand.Intn(len(technicalAnswers))],
		)
	} else if randomLine < 3 || personalPoint != "" {
		conn.Privmsg(
			channelName,
			personalAnswers[rand.Intn(len(personalAnswers))],
		)
	}

}

// VBAbot Bot core
func handleMessageVBAbot(conn *irc.Conn, line *irc.Line) {
	technicalAnswers := []string{
		"Un truc codé avec emacs par un manchot !",
		"Evidemment c est encore la faute aux sysadmins :(",
		"Ils auraient du le faire en cobol !",
		"Cette bouse me reveille toutes les nuits !",
		"As tu teste en dev ? integration ? staging ?",
		"Dans le doute reboot ?",
		"Et si on le recodait en LISP ?",
		"As tu fait le ticket pour ce change ?",
		"VMware !",
		"Faudrait refactor le cookbook au fait :)",
		"Mieux vaut tar que gz",
		"C'est un René Pierre !",
		"Et bim…",
	}

	personalAnswers := []string{
		"ASV ?",
		"L'informatique dans les nuages…",
		"Jamais le vendredi !",
		"Oui ce serait effectivement mieux avec jabber",
		"Ce sera en salle cumulonimbus",
		"J'aime pas les cookies a la cachuete.",
		"Plus, mieux, mais moins cher !",
	}

	techRegexp, _ := regexp.Compile("(?i)opencontrail|contrail|neutron|django|nova|nagios|kibana|bss|prod|staging|integration|irc|bot")
	technicalPoint := techRegexp.FindString(line.Text())
	randomLine := rand.Intn(40)

	if technicalPoint != "" {
		randomAnswer := rand.Intn(len(technicalAnswers))
		conn.Privmsg(
			channelName,
			technicalAnswers[randomAnswer],
		)
	} else if randomLine < 3 {
		randomAnswer := rand.Intn(len(personalAnswers))
		conn.Privmsg(
			channelName,
			personalAnswers[randomAnswer],
		)
	}
}
<|endoftext|>


<commit_before>// Package prime provides functionality to produce prime numbers using all
// available cpu cores. https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes
// can be an starting point to find more information about how to calculate
// prime numbers.
//
// The method used in Primes function is more memory consuming than a simpler
// Trial division method (https://en.wikipedia.org/wiki/Trial_division)
package prime

import (
	"math"
	"runtime"
)

func fill(nums []bool, i uint64, max uint64) {
	a := 2 * i
	for a <= max {
		nums[a] = true
		a = a + i
	}
}

func go_fill(nums []bool, i uint64, max uint64, ready chan bool) {
	fill(nums, i, max)
	<-ready
}

// Primes returns a slice of all prime numbers equal or lower than max.
func Primes(max uint64) []uint64 {
	runtime.GOMAXPROCS(runtime.NumCPU())
	ready := make(chan bool, runtime.NumCPU())

	var nums = make([]bool, max+1)
	m := uint64(math.Sqrt(float64(max)))
	for i := uint64(2); i <= m; i = i + 2 {

		if nums[i] == false {
			go go_fill(nums, i, max, ready)
			ready <- true
		}
		if i == 2 {
			i = 1
		}

	}

	for i := 0; i < runtime.NumCPU(); i++ {
		ready <- true
	}

	var ps []uint64
	m = uint64(len(nums))
	for i := uint64(2); i < uint64(len(nums)); i++ {
		if nums[i] == false {
			ps = append(ps, i)
		}
	}
	return ps
}
<commit_msg>next might be more clear<commit_after>// Package prime provides functionality to produce prime numbers using all
// available cpu cores. https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes
// can be an starting point to find more information about how to calculate
// prime numbers.
//
// The method used in Primes function is more memory consuming than a simpler
// Trial division method (https://en.wikipedia.org/wiki/Trial_division)
package prime

import (
	"math"
	"runtime"
)

func fill(nums []bool, i uint64, max uint64) {
	a := 2 * i
	for a <= max {
		nums[a] = true
		a = a + i
	}
}

func go_fill(nums []bool, i uint64, max uint64, next chan bool) {
	fill(nums, i, max)
	<-next
}

// Primes returns a slice of all prime numbers equal or lower than max.
func Primes(max uint64) []uint64 {
	runtime.GOMAXPROCS(runtime.NumCPU())
	next := make(chan bool, runtime.NumCPU())

	var nums = make([]bool, max+1)
	m := uint64(math.Sqrt(float64(max)))
	for i := uint64(2); i <= m; i = i + 2 {

		if nums[i] == false {
			go go_fill(nums, i, max, next)
			next <- true
		}
		if i == 2 {
			i = 1
		}

	}

	for i := 0; i < runtime.NumCPU(); i++ {
		next <- true
	}

	var ps []uint64
	m = uint64(len(nums))
	for i := uint64(2); i < uint64(len(nums)); i++ {
		if nums[i] == false {
			ps = append(ps, i)
		}
	}
	return ps
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"log"
	"net/url"
	"runtime/debug"
	"strings"
	"sync"
	"time"

	"code.cloudfoundry.org/bytefmt"
	"github.com/valyala/fasthttp"
	"github.com/xtrafrancyz/vk-proxy/replacer"
)

const (
	readBufferSize = 8192
)

var (
	atPath = []byte("/@")

	gzip            = []byte("gzip")
	vkProxyName     = []byte("vk-proxy")
	setCookie       = []byte("Set-Cookie")
	acceptEncoding  = []byte("Accept-Encoding")
	contentEncoding = []byte("Content-Encoding")
	serverHeader    = []byte("Server")
)

type ProxyConfig struct {
	ReduceMemoryUsage bool
	BaseDomain        string
	LogVerbosity      int
	GzipUpstream      bool
	FilterFeed        bool
}

type Proxy struct {
	client   *fasthttp.Client
	server   *fasthttp.Server
	replacer *replacer.Replacer
	tracker  *tracker
	config   ProxyConfig
}

func NewProxy(config ProxyConfig) *Proxy {
	p := &Proxy{
		client: &fasthttp.Client{
			Name:           "vk-proxy",
			ReadBufferSize: readBufferSize,
		},
		replacer: &replacer.Replacer{},
		tracker: &tracker{
			uniqueUsers: make(map[string]bool),
		},
		config: config,
	}
	p.server = &fasthttp.Server{
		Handler:           p.handleProxy,
		ReduceMemoryUsage: config.ReduceMemoryUsage,
		ReadBufferSize:    readBufferSize,
		Name:              "vk-proxy",
	}
	if p.config.LogVerbosity > 0 {
		p.tracker.start()
	}
	return p
}

func (p *Proxy) ListenTCP(host string) error {
	log.Printf("Starting server on http://%s", host)
	return p.server.ListenAndServe(host)
}

func (p *Proxy) ListenUnix(path string) error {
	log.Printf("Starting server on http://unix:%s", path)
	return p.server.ListenAndServeUNIX(path, 0777)
}

func (p *Proxy) handler(ctx *fasthttp.RequestCtx) {
	switch string(ctx.Path()) {
	case "/away", "/away.php":
		p.handleAway(ctx)
	default:
		p.handleProxy(ctx)
	}
}

func (p *Proxy) handleAway(ctx *fasthttp.RequestCtx) {
	to := string(ctx.QueryArgs().Peek("to"))
	if to == "" {
		ctx.Error("Bad Request: 'to' argument is not set", 400)
		return
	}
	to, err := url.QueryUnescape(to)
	if err != nil {
		ctx.Error("Bad Request: could not unescape url", 400)
		return
	}
	ctx.Redirect(to, fasthttp.StatusMovedPermanently)
}

func (p *Proxy) handleProxy(ctx *fasthttp.RequestCtx) {
	defer func() {
		if r := recover(); r != nil {
			log.Printf("panic when proxying the request: %s%s", r, debug.Stack())
			ctx.Error("500 Internal Server Error", 500)
		}
	}()
	start := time.Now()

	var baseDomain string
	if p.config.BaseDomain != "" {
		baseDomain = p.config.BaseDomain
	} else {
		baseDomain = string(ctx.Host())
	}

	if !p.prepareProxyRequest(ctx) {
		ctx.Error("400 Bad Request", 400)
		return
	}

	err := p.client.DoTimeout(&ctx.Request, &ctx.Response, 30*time.Second)
	if err == nil {
		err = p.processProxyResponse(baseDomain, ctx)
	}

	elapsed := time.Since(start).Round(100 * time.Microsecond)

	if err != nil {
		log.Printf("%s %s error: %s", elapsed, ctx.Path(), err)
		if strings.Contains(err.Error(), "timed out") || strings.Contains(err.Error(), "timeout") {
			ctx.Error("408 Request Timeout", 408)
		} else {
			ctx.Error("500 Internal Server Error", 500)
		}
		return
	}

	if p.config.LogVerbosity > 0 {
		ip := ctx.Request.Header.Peek("CF-Connecting-IP") // Cloudflare
		if ip == nil {
			ip = ctx.Request.Header.Peek("X-Real-IP") // nginx
		}
		if ip == nil {
			ip = []byte(ctx.RemoteIP().String()) // real
		}
		p.tracker.trackRequest(string(ip), len(ctx.Response.Body()))
	}

	if p.config.LogVerbosity == 2 {
		log.Printf("%s %s %s", elapsed, ctx.Path(), bytefmt.ByteSize(uint64(len(ctx.Response.Body()))))
	} else if p.config.LogVerbosity == 3 {
		log.Printf("%s %s %s\n%s", elapsed, ctx.Path(), bytefmt.ByteSize(uint64(len(ctx.Response.Body()))), ctx.Response.Body())
	}
}

func (p *Proxy) prepareProxyRequest(ctx *fasthttp.RequestCtx) bool {
	req := &ctx.Request
	path := req.RequestURI()
	if bytes.HasPrefix(path, atPath) {
		slashIndex := bytes.IndexRune(path[1:], '/')
		if slashIndex == -1 {
			return false
		}
		endpoint := string(path[len(atPath) : slashIndex+1])
		if endpoint != "vk.com" && !strings.HasSuffix(endpoint, ".vk.com") {
			return false
		}
		req.Header.SetHost(endpoint)
		req.SetRequestURIBytes([]byte(path[1+slashIndex:]))
	} else {
		req.SetHost("api.vk.com")
	}
	// After req.URI() call it is impossible to modify URI
	req.URI().SetScheme("https")
	if p.config.GzipUpstream {
		req.Header.SetBytesKV(acceptEncoding, gzip)
	} else {
		req.Header.DelBytes(acceptEncoding)
	}
	return true
}

func (p *Proxy) processProxyResponse(baseDomain string, ctx *fasthttp.RequestCtx) error {
	res := &ctx.Response
	res.Header.DelBytes(setCookie)
	res.Header.SetBytesKV(serverHeader, vkProxyName)

	var body []byte
	// Gunzip body if needed
	if bytes.Contains(res.Header.PeekBytes(contentEncoding), gzip) {
		res.Header.DelBytes(contentEncoding)
		var err error
		body, err = res.BodyGunzip()
		if err != nil {
			return err
		}
	} else {
		body = res.Body()
	}

	body = p.replacer.DoReplace(body, replacer.ReplaceContext{
		BaseDomain: baseDomain,
		Domain:     string(ctx.Host()),
		Path:       string(ctx.Path()),
		FilterFeed: p.config.FilterFeed,
	})

	res.SetBody(body)
	return nil
}

type tracker struct {
	lock        sync.Mutex
	requests    uint32
	bytes       uint64
	uniqueUsers map[string]bool
}

func (t *tracker) start() {
	go func() {
		for range time.Tick(60 * time.Second) {
			t.lock.Lock()
			log.Printf("Requests: %d, Traffic: %s, Online: %d", t.requests, bytefmt.ByteSize(t.bytes), len(t.uniqueUsers))
			t.requests = 0
			t.bytes = 0
			t.uniqueUsers = make(map[string]bool)
			t.lock.Unlock()
		}
	}()
}

func (t *tracker) trackRequest(ip string, size int) {
	t.lock.Lock()

	t.uniqueUsers[ip] = true
	t.requests++
	t.bytes += uint64(size)

	t.lock.Unlock()
}
<commit_msg>Отключена проверка серта вк<commit_after>package main

import (
	"bytes"
	"crypto/tls"
	"log"
	"net/url"
	"runtime/debug"
	"strings"
	"sync"
	"time"

	"code.cloudfoundry.org/bytefmt"
	"github.com/valyala/fasthttp"
	"github.com/xtrafrancyz/vk-proxy/replacer"
)

const (
	readBufferSize = 8192
)

var (
	atPath = []byte("/@")

	gzip            = []byte("gzip")
	vkProxyName     = []byte("vk-proxy")
	setCookie       = []byte("Set-Cookie")
	acceptEncoding  = []byte("Accept-Encoding")
	contentEncoding = []byte("Content-Encoding")
	serverHeader    = []byte("Server")
)

type ProxyConfig struct {
	ReduceMemoryUsage bool
	BaseDomain        string
	LogVerbosity      int
	GzipUpstream      bool
	FilterFeed        bool
}

type Proxy struct {
	client   *fasthttp.Client
	server   *fasthttp.Server
	replacer *replacer.Replacer
	tracker  *tracker
	config   ProxyConfig
}

func NewProxy(config ProxyConfig) *Proxy {
	p := &Proxy{
		client: &fasthttp.Client{
			Name:           "vk-proxy",
			ReadBufferSize: readBufferSize,
			TLSConfig:      &tls.Config{InsecureSkipVerify: true},
		},
		replacer: &replacer.Replacer{},
		tracker: &tracker{
			uniqueUsers: make(map[string]bool),
		},
		config: config,
	}
	p.server = &fasthttp.Server{
		Handler:           p.handleProxy,
		ReduceMemoryUsage: config.ReduceMemoryUsage,
		ReadBufferSize:    readBufferSize,
		Name:              "vk-proxy",
	}
	if p.config.LogVerbosity > 0 {
		p.tracker.start()
	}
	return p
}

func (p *Proxy) ListenTCP(host string) error {
	log.Printf("Starting server on http://%s", host)
	return p.server.ListenAndServe(host)
}

func (p *Proxy) ListenUnix(path string) error {
	log.Printf("Starting server on http://unix:%s", path)
	return p.server.ListenAndServeUNIX(path, 0777)
}

func (p *Proxy) handler(ctx *fasthttp.RequestCtx) {
	switch string(ctx.Path()) {
	case "/away", "/away.php":
		p.handleAway(ctx)
	default:
		p.handleProxy(ctx)
	}
}

func (p *Proxy) handleAway(ctx *fasthttp.RequestCtx) {
	to := string(ctx.QueryArgs().Peek("to"))
	if to == "" {
		ctx.Error("Bad Request: 'to' argument is not set", 400)
		return
	}
	to, err := url.QueryUnescape(to)
	if err != nil {
		ctx.Error("Bad Request: could not unescape url", 400)
		return
	}
	ctx.Redirect(to, fasthttp.StatusMovedPermanently)
}

func (p *Proxy) handleProxy(ctx *fasthttp.RequestCtx) {
	defer func() {
		if r := recover(); r != nil {
			log.Printf("panic when proxying the request: %s%s", r, debug.Stack())
			ctx.Error("500 Internal Server Error", 500)
		}
	}()
	start := time.Now()

	var baseDomain string
	if p.config.BaseDomain != "" {
		baseDomain = p.config.BaseDomain
	} else {
		baseDomain = string(ctx.Host())
	}

	if !p.prepareProxyRequest(ctx) {
		ctx.Error("400 Bad Request", 400)
		return
	}

	err := p.client.DoTimeout(&ctx.Request, &ctx.Response, 30*time.Second)
	if err == nil {
		err = p.processProxyResponse(baseDomain, ctx)
	}

	elapsed := time.Since(start).Round(100 * time.Microsecond)

	if err != nil {
		log.Printf("%s %s error: %s", elapsed, ctx.Path(), err)
		if strings.Contains(err.Error(), "timed out") || strings.Contains(err.Error(), "timeout") {
			ctx.Error("408 Request Timeout", 408)
		} else {
			ctx.Error("500 Internal Server Error", 500)
		}
		return
	}

	if p.config.LogVerbosity > 0 {
		ip := ctx.Request.Header.Peek("CF-Connecting-IP") // Cloudflare
		if ip == nil {
			ip = ctx.Request.Header.Peek("X-Real-IP") // nginx
		}
		if ip == nil {
			ip = []byte(ctx.RemoteIP().String()) // real
		}
		p.tracker.trackRequest(string(ip), len(ctx.Response.Body()))
	}

	if p.config.LogVerbosity == 2 {
		log.Printf("%s %s %s", elapsed, ctx.Path(), bytefmt.ByteSize(uint64(len(ctx.Response.Body()))))
	} else if p.config.LogVerbosity == 3 {
		log.Printf("%s %s %s\n%s", elapsed, ctx.Path(), bytefmt.ByteSize(uint64(len(ctx.Response.Body()))), ctx.Response.Body())
	}
}

func (p *Proxy) prepareProxyRequest(ctx *fasthttp.RequestCtx) bool {
	req := &ctx.Request
	path := req.RequestURI()
	if bytes.HasPrefix(path, atPath) {
		slashIndex := bytes.IndexRune(path[1:], '/')
		if slashIndex == -1 {
			return false
		}
		endpoint := string(path[len(atPath) : slashIndex+1])
		if endpoint != "vk.com" && !strings.HasSuffix(endpoint, ".vk.com") {
			return false
		}
		req.Header.SetHost(endpoint)
		req.SetRequestURIBytes([]byte(path[1+slashIndex:]))
	} else {
		req.SetHost("api.vk.com")
	}
	// After req.URI() call it is impossible to modify URI
	req.URI().SetScheme("https")
	if p.config.GzipUpstream {
		req.Header.SetBytesKV(acceptEncoding, gzip)
	} else {
		req.Header.DelBytes(acceptEncoding)
	}
	return true
}

func (p *Proxy) processProxyResponse(baseDomain string, ctx *fasthttp.RequestCtx) error {
	res := &ctx.Response
	res.Header.DelBytes(setCookie)
	res.Header.SetBytesKV(serverHeader, vkProxyName)

	var body []byte
	// Gunzip body if needed
	if bytes.Contains(res.Header.PeekBytes(contentEncoding), gzip) {
		res.Header.DelBytes(contentEncoding)
		var err error
		body, err = res.BodyGunzip()
		if err != nil {
			return err
		}
	} else {
		body = res.Body()
	}

	body = p.replacer.DoReplace(body, replacer.ReplaceContext{
		BaseDomain: baseDomain,
		Domain:     string(ctx.Host()),
		Path:       string(ctx.Path()),
		FilterFeed: p.config.FilterFeed,
	})

	res.SetBody(body)
	return nil
}

type tracker struct {
	lock        sync.Mutex
	requests    uint32
	bytes       uint64
	uniqueUsers map[string]bool
}

func (t *tracker) start() {
	go func() {
		for range time.Tick(60 * time.Second) {
			t.lock.Lock()
			log.Printf("Requests: %d, Traffic: %s, Online: %d", t.requests, bytefmt.ByteSize(t.bytes), len(t.uniqueUsers))
			t.requests = 0
			t.bytes = 0
			t.uniqueUsers = make(map[string]bool)
			t.lock.Unlock()
		}
	}()
}

func (t *tracker) trackRequest(ip string, size int) {
	t.lock.Lock()

	t.uniqueUsers[ip] = true
	t.requests++
	t.bytes += uint64(size)

	t.lock.Unlock()
}
<|endoftext|>


<commit_before>package multiplexity

import (
	"fmt"
	"time"
)

type Proxy struct {
	config *Config

	serverClient *ServerClient
	clientServer *ClientServer

	clientCmdChan CommandChan
	serverCmdChan CommandChan

	clients ClientList
}

func NewProxy(config *Config) *Proxy {
	return &Proxy{
		config:        config,
		clientCmdChan: make(CommandChan),
		serverCmdChan: make(CommandChan),
		clients:       make(ClientList, 0, 0),
	}
}

func (p *Proxy) Start() {
	p.createClientServer()
	p.createServerClient()

	serverCmdHnd := NewServerCommandHandler(p.config, p.serverWriteMsg, p.clientWriteMsg)
	clientCmdHnd := NewClientCommandHandler(p.config, p.serverWriteMsg, p.clientWriteMsg)

	pingTick := time.Tick(time.Minute)
	for {
		select {
		case <-pingTick:
			pingMsg := &Message{
				Command:  "PING",
				Trailing: "something really cool just happened",
			}
			p.clientWriteMsg(pingMsg)

		case cmd, _ := <-p.serverCmdChan:
			serverCmdHnd.Handle(cmd)

		case cmd, _ := <-p.clientCmdChan:
			clientCmdHnd.Handle(cmd)

			// hack client list needs to be outside of proxy and cmd handelr
			p.clients = clientCmdHnd.clients
		}
	}
}

func (p *Proxy) serverWriteMsg(message *Message) {
	if p.serverClient != nil && p.serverClient.IsConnected {
		p.serverClient.Write(message)
	}
}
func (p *Proxy) clientWriteMsg(message *Message) {
	for _, client := range p.clients {
		outMsg := message.Copy()

		switch message.Command {
		case "PING": // do nothing
		case "JOIN": // sub client info
			outMsg.Prefix = fmt.Sprintf("%s!~%s@%s", client.Nick, client.Nick, p.config.Client.Hostname)

		default:
			outMsg.Prefix = p.config.Client.Hostname
		}
		client.Write(outMsg)
	}
}

func (p *Proxy) createServerClient() error {
	p.serverClient = NewServerClient(p.serverCmdChan)
	p.serverClient.Connect(p.config.Server.Address)
	go p.serverClient.ReadWritePumps()

	return nil
}

func (p *Proxy) createClientServer() error {
	p.clientServer = NewClientServer(p.clientCmdChan)
	go p.clientServer.Listen(p.config.Client.ListenAddress)

	return nil
}
<commit_msg>Client join commands will now respond with the correct remote host<commit_after>package multiplexity

import (
	"fmt"
	"time"
)

type Proxy struct {
	config *Config

	serverClient *ServerClient
	clientServer *ClientServer

	clientCmdChan CommandChan
	serverCmdChan CommandChan

	clients ClientList
}

func NewProxy(config *Config) *Proxy {
	return &Proxy{
		config:        config,
		clientCmdChan: make(CommandChan),
		serverCmdChan: make(CommandChan),
		clients:       make(ClientList, 0, 0),
	}
}

func (p *Proxy) Start() {
	p.createClientServer()
	p.createServerClient()

	serverCmdHnd := NewServerCommandHandler(p.config, p.serverWriteMsg, p.clientWriteMsg)
	clientCmdHnd := NewClientCommandHandler(p.config, p.serverWriteMsg, p.clientWriteMsg)

	pingTick := time.Tick(time.Minute)
	for {
		select {
		case <-pingTick:
			pingMsg := &Message{
				Command:  "PING",
				Trailing: "something really cool just happened",
			}
			p.clientWriteMsg(pingMsg)

		case cmd, _ := <-p.serverCmdChan:
			serverCmdHnd.Handle(cmd)

		case cmd, _ := <-p.clientCmdChan:
			clientCmdHnd.Handle(cmd)

			// hack client list needs to be outside of proxy and cmd handelr
			p.clients = clientCmdHnd.clients
		}
	}
}

func (p *Proxy) serverWriteMsg(message *Message) {
	if p.serverClient != nil && p.serverClient.IsConnected {
		p.serverClient.Write(message)
	}
}
func (p *Proxy) clientWriteMsg(message *Message) {
	for _, client := range p.clients {
		outMsg := message.Copy()

		switch message.Command {
		case "PING": // do nothing
		case "JOIN": // sub client info
			outMsg.Prefix = fmt.Sprintf("%s!~%s@%s", client.Nick, client.Nick, client.RemoteHost)

		default:
			outMsg.Prefix = p.config.Client.Hostname
		}
		client.Write(outMsg)
	}
}

func (p *Proxy) createServerClient() error {
	p.serverClient = NewServerClient(p.serverCmdChan)
	p.serverClient.Connect(p.config.Server.Address)
	go p.serverClient.ReadWritePumps()

	return nil
}

func (p *Proxy) createClientServer() error {
	p.clientServer = NewClientServer(p.clientCmdChan)
	go p.clientServer.Listen(p.config.Client.ListenAddress)

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/hashicorp/consul/api"
	"gopkg.in/alecthomas/kingpin.v2"
)

type catalogServicesMerger func(a []*api.CatalogService, b []*api.CatalogService) (mergedList []*api.CatalogService)

func queryMulti(
	config *api.Config,
	service string,
	tags []string,
	mergeFunc catalogServicesMerger,
) []*api.CatalogService {
	// handle the case of no tags
	if len(tags) == 0 {
		return query(config, service, "")
	}

	// for multiple tags, fork join
	c := make(chan []*api.CatalogService)

	for _, tag := range tags {
		go func(tag string) {
			c <- query(config, service, tag)
		}(tag)
	}

	// collect all results. perhaps we should use a waitgroup instead?
	var results []*api.CatalogService = nil
	for i := 0; i < len(tags); i++ {
		select {
		case res := <-c:
			if results == nil {
				results = res
			} else {
				results = mergeFunc(results, res)
			}
		}
	}
	return results
}

func query(config *api.Config, service string, tag string) []*api.CatalogService {
	client, err := api.NewClient(config)

	services, _, err := client.Catalog().Service(service, tag, &api.QueryOptions{AllowStale: true, RequireConsistent: false})
	if err != nil {
		kingpin.Fatalf("Error querying Consul: %s\n", err.Error())
		return nil
	}

	return services
}

type CatalogServiceList []*api.CatalogService

func (c CatalogServiceList) Contains(elmnt *api.CatalogService) bool {
	for _, x := range c {
		if x.Node == elmnt.Node {
			return true
		}
	}
	return false
}

// i really don't care about efficiency. merge every two items with these functions
func unionMerge(a []*api.CatalogService, b []*api.CatalogService) []*api.CatalogService {
	res := make(CatalogServiceList, 0, len(a)+len(b))
	for _, x := range a {
		res = append(res, x)
	}
	for _, x := range b {
		if !res.Contains(x) {
			res = append(res, x)
		}
	}
	return res
}

func intersectionMerge(a []*api.CatalogService, b []*api.CatalogService) []*api.CatalogService {
	res := make([]*api.CatalogService, 0)
	for _, x := range a {
		if CatalogServiceList(b).Contains(x) {
			res = append(res, x)
		}
	}
	return res
}
<commit_msg>use stream and parallel libraries, makes code sooo much prettier<commit_after>package main

import (
	"github.com/hashicorp/consul/api"
	"github.com/wushilin/parallel"
	"github.com/wushilin/stream"
	"gopkg.in/alecthomas/kingpin.v2"
)

type catalogServicesMerger func(a []*api.CatalogService, b []*api.CatalogService) (mergedList []*api.CatalogService)

func queryMulti(
	config *api.Config,
	service string,
	tags []string,
	mergeFunc catalogServicesMerger,
) []*api.CatalogService {
	// handle the case of no tags
	if len(tags) == 0 {
		return query(config, service, "")
	}

	res, ok := stream.FromArray(tags).Map(func(tag interface{}) parallel.Future {
		return parallel.MakeFuture(func(tag interface{}) interface{} {
			return query(config, service, tag.(string))
		}, tag)
	}).Map(func(future parallel.Future) interface{} {
		return future.Wait()
	}).Reduce(mergeFunc).Value()

	if ok {
		return res.([]*api.CatalogService)
	} else {
		// perhaps blow up instead?
		return nil
	}
}

func query(config *api.Config, service string, tag string) []*api.CatalogService {
	client, err := api.NewClient(config)

	services, _, err := client.Catalog().Service(service, tag, &api.QueryOptions{AllowStale: true, RequireConsistent: false})
	if err != nil {
		kingpin.Fatalf("Error querying Consul: %s\n", err.Error())
		return nil
	}

	return services
}

type CatalogServiceList []*api.CatalogService

func (c CatalogServiceList) Contains(elmnt *api.CatalogService) bool {
	for _, x := range c {
		if x.Node == elmnt.Node {
			return true
		}
	}
	return false
}

// i really don't care about efficiency. merge every two items with these functions
func unionMerge(a []*api.CatalogService, b []*api.CatalogService) []*api.CatalogService {
	res := make(CatalogServiceList, 0, len(a)+len(b))
	for _, x := range a {
		res = append(res, x)
	}
	for _, x := range b {
		if !res.Contains(x) {
			res = append(res, x)
		}
	}
	return res
}

func intersectionMerge(a []*api.CatalogService, b []*api.CatalogService) []*api.CatalogService {
	res := make([]*api.CatalogService, 0)
	for _, x := range a {
		if CatalogServiceList(b).Contains(x) {
			res = append(res, x)
		}
	}
	return res
}
<|endoftext|>


<commit_before>package clui

import (
	xs "github.com/huandu/xstrings"
	term "github.com/nsf/termbox-go"
)

/*
Radio button control. Unite a few radios in one radio group to
make a user select one of available choices.
*/
type Radio struct {
	BaseControl
	group    *RadioGroup
	selected bool
}

/*
NewRadio creates a new radio button.
view - is a View that manages the control
parent - is container that keeps the control. The same View can be a view and a parent at the same time.
width - is minimal width of the control.
title - radio title.
scale - the way of scaling the control when the parent is resized. Use DoNotScale constant if the
control should keep its original size.
*/
func CreateRadio(parent Control, width int, title string, scale int) *Radio {
	c := new(Radio)
	c.BaseControl = NewBaseControl()

	if width == AutoSize {
		width = xs.Len(title) + 4
	}

	c.parent = parent

	c.SetSize(width, 1) // TODO: only one line heigth is supported at that moment
	c.SetConstraints(width, 1)
	c.SetTitle(title)
	c.SetTabStop(true)
	c.SetScale(scale)

	if parent != nil {
		parent.AddChild(c)
	}

	return c
}

// Repaint draws the control on its View surface
func (c *Radio) Draw() {
	if c.hidden {
		return
	}

	PushAttributes()
	defer PopAttributes()

	x, y := c.Pos()
	w, h := c.Size()

	fg, bg := RealColor(c.fg, c.Style(), ColorControlText), RealColor(c.bg, c.Style(), ColorControlBack)
	if !c.Enabled() {
		fg, bg = RealColor(c.fg, c.Style(), ColorControlDisabledText), RealColor(c.bg, c.Style(), ColorControlDisabledBack)
	} else if c.Active() {
		fg, bg = RealColor(c.fg, c.Style(), ColorControlActiveText), RealColor(c.bg, c.Style(), ColorControlActiveBack)
	}

	parts := []rune(SysObject(ObjRadio))
	cOpen, cClose, cEmpty, cCheck := parts[0], parts[1], parts[2], parts[3]
	cState := cEmpty
	if c.selected {
		cState = cCheck
	}

	SetTextColor(fg)
	SetBackColor(bg)
	FillRect(x, y, w, h, ' ')
	if w < 3 {
		return
	}

	PutChar(x, y, cOpen)
	PutChar(x+2, y, cClose)
	PutChar(x+1, y, cState)

	if w < 5 {
		return
	}

	shift, text := AlignColorizedText(c.title, w-4, c.align)
	DrawText(x+4+shift, y, text)
}

/*
ProcessEvent processes all events come from the control parent. If a control
processes an event it should return true. If the method returns false it means
that the control do not want or cannot process the event and the caller sends
the event to the control parent.
The control processes only space button and mouse clicks to make control selected. Deselecting control is not possible: one has to click another radio of the radio group to deselect this button
*/
func (c *Radio) ProcessEvent(event Event) bool {
	if (!c.Active() && event.Type == EventKey) || !c.Enabled() {
		return false
	}

	if (event.Type == EventKey && event.Key == term.KeySpace) || event.Type == EventMouse {
		if c.group == nil {
			c.SetSelected(true)
		} else {
			c.group.SelectItem(c)
		}
		return true
	}

	return false
}

// SetSelected makes the button selected. One should not use
// the method directly, it is for RadioGroup control
func (c *Radio) SetSelected(val bool) {
	c.selected = val
}

// Selected returns if the radio is selected
func (c *Radio) Selected() bool {
	return c.selected
}

// SetGroup sets the radio group to which the radio belongs
func (c *Radio) SetGroup(group *RadioGroup) {
	c.group = group
}
<commit_msg>Add OnChange() callback function on radio buttons<commit_after>package clui

import (
	xs "github.com/huandu/xstrings"
	term "github.com/nsf/termbox-go"
)

/*
Radio button control. Unite a few radios in one radio group to
make a user select one of available choices.
*/
type Radio struct {
	BaseControl
	group    *RadioGroup
	selected bool

	onChange func(bool)
}

/*
NewRadio creates a new radio button.
view - is a View that manages the control
parent - is container that keeps the control. The same View can be a view and a parent at the same time.
width - is minimal width of the control.
title - radio title.
scale - the way of scaling the control when the parent is resized. Use DoNotScale constant if the
control should keep its original size.
*/
func CreateRadio(parent Control, width int, title string, scale int) *Radio {
	c := new(Radio)
	c.BaseControl = NewBaseControl()

	if width == AutoSize {
		width = xs.Len(title) + 4
	}

	c.parent = parent

	c.SetSize(width, 1) // TODO: only one line heigth is supported at that moment
	c.SetConstraints(width, 1)
	c.SetTitle(title)
	c.SetTabStop(true)
	c.SetScale(scale)

	c.onChange = nil

	if parent != nil {
		parent.AddChild(c)
	}

	return c
}

// Repaint draws the control on its View surface
func (c *Radio) Draw() {
	if c.hidden {
		return
	}

	PushAttributes()
	defer PopAttributes()

	x, y := c.Pos()
	w, h := c.Size()

	fg, bg := RealColor(c.fg, c.Style(), ColorControlText), RealColor(c.bg, c.Style(), ColorControlBack)
	if !c.Enabled() {
		fg, bg = RealColor(c.fg, c.Style(), ColorControlDisabledText), RealColor(c.bg, c.Style(), ColorControlDisabledBack)
	} else if c.Active() {
		fg, bg = RealColor(c.fg, c.Style(), ColorControlActiveText), RealColor(c.bg, c.Style(), ColorControlActiveBack)
	}

	parts := []rune(SysObject(ObjRadio))
	cOpen, cClose, cEmpty, cCheck := parts[0], parts[1], parts[2], parts[3]
	cState := cEmpty
	if c.selected {
		cState = cCheck
	}

	SetTextColor(fg)
	SetBackColor(bg)
	FillRect(x, y, w, h, ' ')
	if w < 3 {
		return
	}

	PutChar(x, y, cOpen)
	PutChar(x+2, y, cClose)
	PutChar(x+1, y, cState)

	if w < 5 {
		return
	}

	shift, text := AlignColorizedText(c.title, w-4, c.align)
	DrawText(x+4+shift, y, text)
}

/*
ProcessEvent processes all events come from the control parent. If a control
processes an event it should return true. If the method returns false it means
that the control do not want or cannot process the event and the caller sends
the event to the control parent.
The control processes only space button and mouse clicks to make control selected. Deselecting control is not possible: one has to click another radio of the radio group to deselect this button
*/
func (c *Radio) ProcessEvent(event Event) bool {
	if (!c.Active() && event.Type == EventKey) || !c.Enabled() {
		return false
	}

	if (event.Type == EventKey && event.Key == term.KeySpace) || event.Type == EventClick {
		if c.group == nil {
			c.SetSelected(true)
		} else {
			c.group.SelectItem(c)
		}
		return true
	}

	return false
}

// SetSelected makes the button selected. One should not use
// the method directly, it is for RadioGroup control
func (c *Radio) SetSelected(val bool) {
	c.selected = val

	if c.onChange != nil {
		go c.onChange(val)
	}
}

// Selected returns if the radio is selected
func (c *Radio) Selected() bool {
	return c.selected
}

// SetGroup sets the radio group to which the radio belongs
func (c *Radio) SetGroup(group *RadioGroup) {
	c.group = group
}

// OnChange sets the callback that is called whenever the state
// of the Radio is changed. Argument of callback is the current
func (c *Radio) OnChange(fn func(bool)) {
	c.mtx.Lock()
	defer c.mtx.Unlock()

	c.onChange = fn
}
<|endoftext|>