<commit_before>// Reddit audiences crawler
// Rémy Mathieu © 2016
package api

import (
	"encoding/json"
	"log"
	"net/http"
)

type ErrorResponse struct {
	Message string `json:"message"`
	Code    int    `json:"code"`
}

func render(w http.ResponseWriter, code int, r interface{}) {
	if d, err := json.Marshal(r); err != nil {
		w.WriteHeader(500)
		log.Printf("err: while rendering: %s", err.Error())
		return
	} else {
		w.Write(d)
		w.WriteHeader(code)
		return
	}
}
<commit_msg>api: write after write header.<commit_after>// Reddit audiences crawler
// Rémy Mathieu © 2016
package api

import (
	"encoding/json"
	"log"
	"net/http"
)

type ErrorResponse struct {
	Message string `json:"message"`
	Code    int    `json:"code"`
}

func render(w http.ResponseWriter, code int, r interface{}) {
	if d, err := json.Marshal(r); err != nil {
		w.WriteHeader(500)
		log.Printf("err: while rendering: %s", err.Error())
		return
	} else {
		w.WriteHeader(code)
		w.Write(d)
		return
	}
}
<|endoftext|>


<commit_before>package v3_helpers

import (
	"encoding/json"
	"fmt"
	"strings"

	"github.com/cloudfoundry-incubator/cf-test-helpers/cf"
	"github.com/cloudfoundry-incubator/cf-test-helpers/helpers"
	"github.com/cloudfoundry/cf-acceptance-tests/helpers/config"

	. "github.com/cloudfoundry/cf-acceptance-tests/cats_suite_helpers"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gbytes"
	. "github.com/onsi/gomega/gexec"
)

const (
	V3_DEFAULT_MEMORY_LIMIT = "256"
	V3_JAVA_MEMORY_LIMIT    = "512"
)

func StartApp(appGuid string) {
	startURL := fmt.Sprintf("/v3/apps/%s/start", appGuid)
	Expect(cf.Cf("curl", startURL, "-X", "PUT").Wait(Config.DefaultTimeoutDuration())).To(Exit(0))
}

func StopApp(appGuid string) {
	stopURL := fmt.Sprintf("/v3/apps/%s/stop", appGuid)
	Expect(cf.Cf("curl", stopURL, "-X", "PUT").Wait(Config.DefaultTimeoutDuration())).To(Exit(0))
}

func CreateApp(appName, spaceGuid, environmentVariables string) string {
	session := cf.Cf("curl", "/v3/apps", "-X", "POST", "-d", fmt.Sprintf(`{"name":"%s", "relationships": {"space": {"data": {"guid": "%s"}}}, "environment_variables":%s}`, appName, spaceGuid, environmentVariables))
	bytes := session.Wait(Config.DefaultTimeoutDuration()).Out.Contents()
	var app struct {
		Guid string `json:"guid"`
	}
	json.Unmarshal(bytes, &app)
	return app.Guid
}

func CreateDockerApp(appName, spaceGuid, environmentVariables string) string {
	session := cf.Cf("curl", "/v3/apps", "-X", "POST", "-d", fmt.Sprintf(`{"name":"%s", "relationships": {"space": {"data": {"guid": "%s"}}}, "environment_variables":%s, "lifecycle": {"type": "docker", "data": {} } }`, appName, spaceGuid, environmentVariables))
	bytes := session.Wait(Config.DefaultTimeoutDuration()).Out.Contents()
	var app struct {
		Guid string `json:"guid"`
	}
	json.Unmarshal(bytes, &app)
	return app.Guid
}

func DeleteApp(appGuid string) {
	session := cf.Cf("curl", fmt.Sprintf("/v3/apps/%s", appGuid), "-X", "DELETE", "-v")
	bytes := session.Wait(Config.DefaultTimeoutDuration()).Out.Contents()
	Expect(bytes).To(ContainSubstring("204 No Content"))
}

func WaitForPackageToBeReady(packageGuid string) {
	pkgUrl := fmt.Sprintf("/v3/packages/%s", packageGuid)
	Eventually(func() *Session {
		session := cf.Cf("curl", pkgUrl)
		Expect(session.Wait(Config.DefaultTimeoutDuration())).To(Exit(0))
		return session
	}, Config.LongCurlTimeoutDuration()).Should(Say("READY"))
}

func WaitForDropletToStage(dropletGuid string) {
	dropletPath := fmt.Sprintf("/v3/droplets/%s", dropletGuid)
	Eventually(func() *Session {
		session := cf.Cf("curl", dropletPath).Wait(Config.DefaultTimeoutDuration())
		Expect(session).NotTo(Say("FAILED"))
		return session
	}, Config.CfPushTimeoutDuration()).Should(Say("STAGED"))
}

func CreatePackage(appGuid string) string {
	packageCreateUrl := fmt.Sprintf("/v3/packages")
	session := cf.Cf("curl", packageCreateUrl, "-X", "POST", "-d", fmt.Sprintf(`{"relationships":{"app":{"data":{"guid":"%s"}}},"type":"bits"}`, appGuid))
	bytes := session.Wait(Config.DefaultTimeoutDuration()).Out.Contents()
	var pac struct {
		Guid string `json:"guid"`
	}
	json.Unmarshal(bytes, &pac)
	return pac.Guid
}

func CreateDockerPackage(appGuid, imagePath string) string {
	packageCreateUrl := fmt.Sprintf("/v3/packages")
	session := cf.Cf("curl", packageCreateUrl, "-X", "POST", "-d", fmt.Sprintf(`{"relationships":{"app":{"data":{"guid":"%s"}}},"type":"docker", "data": {"image": "%s"}}`, appGuid, imagePath))
	bytes := session.Wait(Config.DefaultTimeoutDuration()).Out.Contents()
	var pac struct {
		Guid string `json:"guid"`
	}
	json.Unmarshal(bytes, &pac)
	return pac.Guid
}

func GetSpaceGuidFromName(spaceName string) string {
	session := cf.Cf("space", spaceName, "--guid")
	bytes := session.Wait(Config.DefaultTimeoutDuration()).Out.Contents()
	return strings.TrimSpace(string(bytes))
}

func GetAuthToken() string {
	session := cf.Cf("oauth-token")
	bytes := session.Wait(Config.DefaultTimeoutDuration()).Out.Contents()
	return strings.TrimSpace(string(bytes))
}

func UploadPackage(uploadUrl, packageZipPath, token string) {
	bits := fmt.Sprintf(`bits=@%s`, packageZipPath)
	curl := helpers.Curl(Config, "-v", "-s", uploadUrl, "-F", bits, "-H", fmt.Sprintf("Authorization: %s", token)).Wait(Config.DefaultTimeoutDuration())
	Expect(curl).To(Exit(0))
}

func StageBuildpackPackage(packageGuid, buildpack string) string {
	stageBody := fmt.Sprintf(`{"lifecycle":{ "type": "buildpack", "data": { "buildpacks": ["%s"] } }}`, buildpack)
	stageUrl := fmt.Sprintf("/v3/packages/%s/droplets", packageGuid)
	session := cf.Cf("curl", stageUrl, "-X", "POST", "-d", stageBody)
	bytes := session.Wait(Config.DefaultTimeoutDuration()).Out.Contents()
	var droplet struct {
		Guid string `json:"guid"`
	}
	json.Unmarshal(bytes, &droplet)
	Expect(droplet.Guid).NotTo(BeEmpty())
	return droplet.Guid
}

func StageDockerPackage(packageGuid string) string {
	stageUrl := fmt.Sprintf("/v3/packages/%s/droplets", packageGuid)
	session := cf.Cf("curl", stageUrl, "-X", "POST", "-d", "")
	bytes := session.Wait(Config.DefaultTimeoutDuration()).Out.Contents()
	var droplet struct {
		Guid string `json:"guid"`
	}
	json.Unmarshal(bytes, &droplet)
	return droplet.Guid
}

func CreateAndMapRoute(appGuid, space, domain, host string) {
	CreateRoute(space, domain, host)
	getRoutePath := fmt.Sprintf("/v2/routes?q=host:%s", host)
	routeBody := cf.Cf("curl", getRoutePath).Wait(Config.DefaultTimeoutDuration()).Out.Contents()
	routeJSON := struct {
		Resources []struct {
			Metadata struct {
				Guid string `json:"guid"`
			} `json:"metadata"`
		} `json:"resources"`
	}{}
	json.Unmarshal([]byte(routeBody), &routeJSON)
	routeGuid := routeJSON.Resources[0].Metadata.Guid
	addRouteBody := fmt.Sprintf(`
	{
		"relationships": {
			"app":   {"guid": "%s"},
			"route": {"guid": "%s"}
		}
	}`, appGuid, routeGuid)
	Expect(cf.Cf("curl", "/v3/route_mappings", "-X", "POST", "-d", addRouteBody).Wait(Config.DefaultTimeoutDuration())).To(Exit(0))
}

func AssignDropletToApp(appGuid, dropletGuid string) {
	appUpdatePath := fmt.Sprintf("/v3/apps/%s/droplets/current", appGuid)
	appUpdateBody := fmt.Sprintf(`{"droplet_guid":"%s"}`, dropletGuid)
	Expect(cf.Cf("curl", appUpdatePath, "-X", "PUT", "-d", appUpdateBody).Wait(Config.DefaultTimeoutDuration())).To(Exit(0))

	for _, process := range GetProcesses(appGuid, "") {
		ScaleProcess(appGuid, process.Type, V3_DEFAULT_MEMORY_LIMIT)
	}
}

func FetchRecentLogs(appGuid, oauthToken string, config config.CatsConfig) *Session {
	loggregatorEndpoint := getHttpLoggregatorEndpoint()
	logUrl := fmt.Sprintf("%s/apps/%s/recentlogs", loggregatorEndpoint, appGuid)
	session := helpers.Curl(Config, logUrl, "-H", fmt.Sprintf("Authorization: %s", oauthToken))
	Expect(session.Wait(Config.DefaultTimeoutDuration())).To(Exit(0))
	return session
}

func ScaleProcess(appGuid, processType, memoryInMb string) {
	scalePath := fmt.Sprintf("/v3/apps/%s/processes/%s/scale", appGuid, processType)
	scaleBody := fmt.Sprintf(`{"memory_in_mb":"%s"}`, memoryInMb)
	Expect(cf.Cf("curl", scalePath, "-X", "PUT", "-d", scaleBody).Wait(Config.DefaultTimeoutDuration())).To(Exit(0))
}

func CreateRoute(space, domain, host string) {
	Expect(cf.Cf("create-route", space, domain, "-n", host).Wait(Config.DefaultTimeoutDuration())).To(Exit(0))
}

func getHttpLoggregatorEndpoint() string {
	infoCommand := cf.Cf("curl", "/v2/info")
	Expect(infoCommand.Wait(Config.DefaultTimeoutDuration())).To(Exit(0))

	var response struct {
		DopplerLoggingEndpoint string `json:"doppler_logging_endpoint"`
	}

	err := json.Unmarshal(infoCommand.Buffer().Contents(), &response)
	Expect(err).NotTo(HaveOccurred())

	return strings.Replace(response.DopplerLoggingEndpoint, "ws", "http", 1)
}
<commit_msg>Update v3 helpers with new current droplet structure<commit_after>package v3_helpers

import (
	"encoding/json"
	"fmt"
	"strings"

	"github.com/cloudfoundry-incubator/cf-test-helpers/cf"
	"github.com/cloudfoundry-incubator/cf-test-helpers/helpers"
	"github.com/cloudfoundry/cf-acceptance-tests/helpers/config"

	. "github.com/cloudfoundry/cf-acceptance-tests/cats_suite_helpers"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gbytes"
	. "github.com/onsi/gomega/gexec"
)

const (
	V3_DEFAULT_MEMORY_LIMIT = "256"
	V3_JAVA_MEMORY_LIMIT    = "512"
)

func StartApp(appGuid string) {
	startURL := fmt.Sprintf("/v3/apps/%s/start", appGuid)
	Expect(cf.Cf("curl", startURL, "-X", "PUT").Wait(Config.DefaultTimeoutDuration())).To(Exit(0))
}

func StopApp(appGuid string) {
	stopURL := fmt.Sprintf("/v3/apps/%s/stop", appGuid)
	Expect(cf.Cf("curl", stopURL, "-X", "PUT").Wait(Config.DefaultTimeoutDuration())).To(Exit(0))
}

func CreateApp(appName, spaceGuid, environmentVariables string) string {
	session := cf.Cf("curl", "/v3/apps", "-X", "POST", "-d", fmt.Sprintf(`{"name":"%s", "relationships": {"space": {"data": {"guid": "%s"}}}, "environment_variables":%s}`, appName, spaceGuid, environmentVariables))
	bytes := session.Wait(Config.DefaultTimeoutDuration()).Out.Contents()
	var app struct {
		Guid string `json:"guid"`
	}
	json.Unmarshal(bytes, &app)
	return app.Guid
}

func CreateDockerApp(appName, spaceGuid, environmentVariables string) string {
	session := cf.Cf("curl", "/v3/apps", "-X", "POST", "-d", fmt.Sprintf(`{"name":"%s", "relationships": {"space": {"data": {"guid": "%s"}}}, "environment_variables":%s, "lifecycle": {"type": "docker", "data": {} } }`, appName, spaceGuid, environmentVariables))
	bytes := session.Wait(Config.DefaultTimeoutDuration()).Out.Contents()
	var app struct {
		Guid string `json:"guid"`
	}
	json.Unmarshal(bytes, &app)
	return app.Guid
}

func DeleteApp(appGuid string) {
	session := cf.Cf("curl", fmt.Sprintf("/v3/apps/%s", appGuid), "-X", "DELETE", "-v")
	bytes := session.Wait(Config.DefaultTimeoutDuration()).Out.Contents()
	Expect(bytes).To(ContainSubstring("204 No Content"))
}

func WaitForPackageToBeReady(packageGuid string) {
	pkgUrl := fmt.Sprintf("/v3/packages/%s", packageGuid)
	Eventually(func() *Session {
		session := cf.Cf("curl", pkgUrl)
		Expect(session.Wait(Config.DefaultTimeoutDuration())).To(Exit(0))
		return session
	}, Config.LongCurlTimeoutDuration()).Should(Say("READY"))
}

func WaitForDropletToStage(dropletGuid string) {
	dropletPath := fmt.Sprintf("/v3/droplets/%s", dropletGuid)
	Eventually(func() *Session {
		session := cf.Cf("curl", dropletPath).Wait(Config.DefaultTimeoutDuration())
		Expect(session).NotTo(Say("FAILED"))
		return session
	}, Config.CfPushTimeoutDuration()).Should(Say("STAGED"))
}

func CreatePackage(appGuid string) string {
	packageCreateUrl := fmt.Sprintf("/v3/packages")
	session := cf.Cf("curl", packageCreateUrl, "-X", "POST", "-d", fmt.Sprintf(`{"relationships":{"app":{"data":{"guid":"%s"}}},"type":"bits"}`, appGuid))
	bytes := session.Wait(Config.DefaultTimeoutDuration()).Out.Contents()
	var pac struct {
		Guid string `json:"guid"`
	}
	json.Unmarshal(bytes, &pac)
	return pac.Guid
}

func CreateDockerPackage(appGuid, imagePath string) string {
	packageCreateUrl := fmt.Sprintf("/v3/packages")
	session := cf.Cf("curl", packageCreateUrl, "-X", "POST", "-d", fmt.Sprintf(`{"relationships":{"app":{"data":{"guid":"%s"}}},"type":"docker", "data": {"image": "%s"}}`, appGuid, imagePath))
	bytes := session.Wait(Config.DefaultTimeoutDuration()).Out.Contents()
	var pac struct {
		Guid string `json:"guid"`
	}
	json.Unmarshal(bytes, &pac)
	return pac.Guid
}

func GetSpaceGuidFromName(spaceName string) string {
	session := cf.Cf("space", spaceName, "--guid")
	bytes := session.Wait(Config.DefaultTimeoutDuration()).Out.Contents()
	return strings.TrimSpace(string(bytes))
}

func GetAuthToken() string {
	session := cf.Cf("oauth-token")
	bytes := session.Wait(Config.DefaultTimeoutDuration()).Out.Contents()
	return strings.TrimSpace(string(bytes))
}

func UploadPackage(uploadUrl, packageZipPath, token string) {
	bits := fmt.Sprintf(`bits=@%s`, packageZipPath)
	curl := helpers.Curl(Config, "-v", "-s", uploadUrl, "-F", bits, "-H", fmt.Sprintf("Authorization: %s", token)).Wait(Config.DefaultTimeoutDuration())
	Expect(curl).To(Exit(0))
}

func StageBuildpackPackage(packageGuid, buildpack string) string {
	stageBody := fmt.Sprintf(`{"lifecycle":{ "type": "buildpack", "data": { "buildpacks": ["%s"] } }}`, buildpack)
	stageUrl := fmt.Sprintf("/v3/packages/%s/droplets", packageGuid)
	session := cf.Cf("curl", stageUrl, "-X", "POST", "-d", stageBody)
	bytes := session.Wait(Config.DefaultTimeoutDuration()).Out.Contents()
	var droplet struct {
		Guid string `json:"guid"`
	}
	json.Unmarshal(bytes, &droplet)
	Expect(droplet.Guid).NotTo(BeEmpty())
	return droplet.Guid
}

func StageDockerPackage(packageGuid string) string {
	stageUrl := fmt.Sprintf("/v3/packages/%s/droplets", packageGuid)
	session := cf.Cf("curl", stageUrl, "-X", "POST", "-d", "")
	bytes := session.Wait(Config.DefaultTimeoutDuration()).Out.Contents()
	var droplet struct {
		Guid string `json:"guid"`
	}
	json.Unmarshal(bytes, &droplet)
	return droplet.Guid
}

func CreateAndMapRoute(appGuid, space, domain, host string) {
	CreateRoute(space, domain, host)
	getRoutePath := fmt.Sprintf("/v2/routes?q=host:%s", host)
	routeBody := cf.Cf("curl", getRoutePath).Wait(Config.DefaultTimeoutDuration()).Out.Contents()
	routeJSON := struct {
		Resources []struct {
			Metadata struct {
				Guid string `json:"guid"`
			} `json:"metadata"`
		} `json:"resources"`
	}{}
	json.Unmarshal([]byte(routeBody), &routeJSON)
	routeGuid := routeJSON.Resources[0].Metadata.Guid
	addRouteBody := fmt.Sprintf(`
	{
		"relationships": {
			"app":   {"guid": "%s"},
			"route": {"guid": "%s"}
		}
	}`, appGuid, routeGuid)
	Expect(cf.Cf("curl", "/v3/route_mappings", "-X", "POST", "-d", addRouteBody).Wait(Config.DefaultTimeoutDuration())).To(Exit(0))
}

func AssignDropletToApp(appGuid, dropletGuid string) {
	appUpdatePath := fmt.Sprintf("/v3/apps/%s/relationships/current_droplet", appGuid)
	appUpdateBody := fmt.Sprintf(`{"data": {"guid":"%s"}}`, dropletGuid)
	Expect(cf.Cf("curl", appUpdatePath, "-X", "PATCH", "-d", appUpdateBody).Wait(Config.DefaultTimeoutDuration())).To(Exit(0))

	for _, process := range GetProcesses(appGuid, "") {
		ScaleProcess(appGuid, process.Type, V3_DEFAULT_MEMORY_LIMIT)
	}
}

func FetchRecentLogs(appGuid, oauthToken string, config config.CatsConfig) *Session {
	loggregatorEndpoint := getHttpLoggregatorEndpoint()
	logUrl := fmt.Sprintf("%s/apps/%s/recentlogs", loggregatorEndpoint, appGuid)
	session := helpers.Curl(Config, logUrl, "-H", fmt.Sprintf("Authorization: %s", oauthToken))
	Expect(session.Wait(Config.DefaultTimeoutDuration())).To(Exit(0))
	return session
}

func ScaleProcess(appGuid, processType, memoryInMb string) {
	scalePath := fmt.Sprintf("/v3/apps/%s/processes/%s/scale", appGuid, processType)
	scaleBody := fmt.Sprintf(`{"memory_in_mb":"%s"}`, memoryInMb)
	Expect(cf.Cf("curl", scalePath, "-X", "PUT", "-d", scaleBody).Wait(Config.DefaultTimeoutDuration())).To(Exit(0))
}

func CreateRoute(space, domain, host string) {
	Expect(cf.Cf("create-route", space, domain, "-n", host).Wait(Config.DefaultTimeoutDuration())).To(Exit(0))
}

func getHttpLoggregatorEndpoint() string {
	infoCommand := cf.Cf("curl", "/v2/info")
	Expect(infoCommand.Wait(Config.DefaultTimeoutDuration())).To(Exit(0))

	var response struct {
		DopplerLoggingEndpoint string `json:"doppler_logging_endpoint"`
	}

	err := json.Unmarshal(infoCommand.Buffer().Contents(), &response)
	Expect(err).NotTo(HaveOccurred())

	return strings.Replace(response.DopplerLoggingEndpoint, "ws", "http", 1)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net/smtp"
)

type Notifier interface {
	SendProblem(problem Problem) error
}

type EmailNotifier struct {
	Contacts []*EmailContact
	Relay    SMTPRelay
}

func (n *EmailNotifier) SendProblem(problem Problem) error {
	content := ("A Problem occured: " + problem.Description + "\r\n" +
		"ReplicaSet: " + fmt.Sprint(problem.ReplicaSet) + "\r\n" +
		"Slave: " + fmt.Sprint(problem.Slave) + "\r\n" +
		"long Description:" + problem.LongDescription) + "\r\n"
	subject := ("Subject:" + "KIT-MAMID: Problem in " + fmt.Sprint(problem.ReplicaSet) + "/" + fmt.Sprint(problem.Slave))
	msg := []byte("From: " + n.Relay.MailFrom + "\r\n" +
		subject + "\r\n" +
		content)
	return n.sendMailToContacts(msg)
}

func (n *EmailNotifier) sendMailToContacts(msg []byte) error {
	var to []string
	for i := 0; i < len(n.Contacts); i++ {
		to = append(to, n.Contacts[i].Address)
	}
	err := smtp.SendMail(
		n.Relay.Hostname,
		nil,
		n.Relay.MailFrom,
		to,
		msg)
	return err
}
<commit_msg>UPD: reformatted mail subject<commit_after>package main

import (
	"fmt"
	"net/smtp"
)

type Notifier interface {
	SendProblem(problem Problem) error
}

type EmailNotifier struct {
	Contacts []*EmailContact
	Relay    SMTPRelay
}

func (n *EmailNotifier) SendProblem(problem Problem) error {
	content := ("A Problem occured: " + problem.Description + "\r\n" +
		"ReplicaSet: " + fmt.Sprint(problem.ReplicaSet) + "\r\n" +
		"Slave: " + fmt.Sprint(problem.Slave) + "\r\n" +
		"long Description:" + problem.LongDescription) + "\r\n"
	subject := ("Subject: [MAMID] Problem: " + problem.Description)
	msg := []byte("From: " + n.Relay.MailFrom + "\r\n" +
		subject + "\r\n" +
		content)
	return n.sendMailToContacts(msg)
}

func (n *EmailNotifier) sendMailToContacts(msg []byte) error {
	var to []string
	for i := 0; i < len(n.Contacts); i++ {
		to = append(to, n.Contacts[i].Address)
	}
	err := smtp.SendMail(
		n.Relay.Hostname,
		nil,
		n.Relay.MailFrom,
		to,
		msg)
	return err
}
<|endoftext|>


<commit_before>package entities

import (
    "math"
    "encoding/json"
    "fmt"
    "log"
)

type Planet struct {
    coords []int
    Texture int
    Size int
    ShipCount int
    MaxShipCount int
    Owner string
}

func (self Planet) String() string {
    // TODO: Improve this
    return self.Owner
}

func (self Planet) GetKey() string {
    return fmt.Sprintf("planet.%d_%d", self.coords[0], self.coords[1])
}

func (self Planet) Serialize() (string, []byte) {
    result, err := json.Marshal(self)
    if err != nil {
        log.Fatal(err)
    }
    return self.GetKey(), result
}

func GeneratePlanets(hash string, sun_position []int) ([]Planet, *Planet) {

    hashElement := func(index int) float64 {
        return float64(hash[index]) - 48
    }

    result := []Planet{}
    ring_offset := float64(80)
    planet_radius := float64(50)

    for ix:=0; ix<9; ix++ {
        planet_in_creation := Planet{[]int{0,0}, 0, 0, 0, 0, ""}
        ring_offset += planet_radius + hashElement(4 * ix)

        planet_in_creation.coords[0] = int(float64(sun_position[0]) + ring_offset * math.Cos(
            hashElement(4 * ix + 1) * 40))
        planet_in_creation.coords[1] = int(float64(sun_position[1]) + ring_offset * math.Sin(
            hashElement(4 * ix + 1) * 40))

        planet_in_creation.Texture = int(hashElement(4 * ix + 2))
        planet_in_creation.Size = 1 + int(hashElement(4 * ix + 3))
        result = append(result, planet_in_creation)
    }
    return result, &result[int(hashElement(37)) - 1]
}

<commit_msg>Add Planet.GetCoords()<commit_after>package entities

import (
    "math"
    "encoding/json"
    "fmt"
    "log"
    "strings"
)

type Planet struct {
    coords []int
    Texture int
    Size int
    ShipCount int
    MaxShipCount int
    Owner string
}

func (self Planet) String() string {
    // TODO: Improve this
    return self.Owner
}

func (self Planet) GetKey() string {
    return fmt.Sprintf("planet.%d_%d", self.coords[0], self.coords[1])
}

func (self Planet) GetCoords() []int {
    return self.coords
}

func (self Planet) Serialize() (string, []byte) {
    result, err := json.Marshal(self)
    if err != nil {
        log.Fatal(err)
    }
    return self.GetKey(), result
}

func GeneratePlanets(hash string, sun_position []int) ([]Planet, *Planet) {

    hashElement := func(index int) float64 {
        return float64(hash[index]) - 48
    }

    result := []Planet{}
    ring_offset := float64(80)
    planet_radius := float64(50)

    for ix:=0; ix<9; ix++ {
        planet_in_creation := Planet{[]int{0,0}, 0, 0, 0, 0, ""}
        ring_offset += planet_radius + hashElement(4 * ix)

        planet_in_creation.coords[0] = int(float64(sun_position[0]) + ring_offset * math.Cos(
            hashElement(4 * ix + 1) * 40))
        planet_in_creation.coords[1] = int(float64(sun_position[1]) + ring_offset * math.Sin(
            hashElement(4 * ix + 1) * 40))

        planet_in_creation.Texture = int(hashElement(4 * ix + 2))
        planet_in_creation.Size = 1 + int(hashElement(4 * ix + 3))
        result = append(result, planet_in_creation)
    }
    return result, &result[int(hashElement(37)) - 1]
}

<|endoftext|>


<commit_before>package entities

import (
	"fmt"
	"time"

	"github.com/Vladimiroff/vec2d"
)

type Player struct {
	Username       string
	Color          Color
	TwitterID      string
	HomePlanet     string
	ScreenSize     []uint16
	ScreenPosition *vec2d.Vector
}

// Database key.
func (p *Player) Key() string {
	return fmt.Sprintf("player.%s", p.Username)
}

// Returns the sorted set by X or Y where this entity has to be put in
func (p *Player) AreaSet() string {
	homePlanet, _ := Get(p.HomePlanet)
	return homePlanet.AreaSet()
}

// Starts missions to one of the players planet to some other. Each mission have type
// and the user decides which part of the planet's fleet he would like to send.
func (p *Player) StartMission(source, target *Planet, fleet int32, missionType string) *Mission {
	if fleet > 100 {
		fleet = 100
	} else if fleet <= 0 {
		fleet = 10
	}
	currentTime := time.Now().UnixNano() / 1e6
	baseShipCount := source.GetShipCount()
	shipCount := int32(baseShipCount * fleet / 100)
	source.SetShipCount(baseShipCount - shipCount)

	mission := Mission{
		Color: p.Color,
		Source: embeddedPlanet{
			Name:     source.Name,
			Position: source.Position,
		},
		Target: embeddedPlanet{
			Name:     target.Name,
			Position: target.Position,
		},
		Type:      missionType,
		StartTime: currentTime,
		Player:    p.Username,
		ShipCount: shipCount,
		areaSet:   source.AreaSet(),
	}
	mission.TravelTime = calculateTravelTime(source.Position, target.Position, mission.GetSpeed())
	return &mission
}

// Creates new player after the authentication and generates color based on the unique hash
func CreatePlayer(username, TwitterID string, HomePlanet *Planet) *Player {
	userhash := simplifyHash(usernameHash(username))

	red := []uint8{151, 218, 233, 72, 245, 84}
	green := []uint8{8, 75, 177, 140, 105, 146}
	blue := []uint8{14, 15, 4, 19, 145, 219}
	hashValue := func(index uint8) uint8 {
		return uint8((userhash[0] - 48) / 2)
	}

	color := Color{red[hashValue(0)], green[hashValue(0)], blue[hashValue(0)]}
	player := Player{username, color, TwitterID, HomePlanet.Key(), []uint16{0, 0}, &vec2d.Vector{2, 2}}
	HomePlanet.Owner = username
	HomePlanet.Color = color
	return &player
}
<commit_msg>Oops. Soft-code the default starting position<commit_after>package entities

import (
	"fmt"
	"time"

	"github.com/Vladimiroff/vec2d"
)

type Player struct {
	Username       string
	Color          Color
	TwitterID      string
	HomePlanet     string
	ScreenSize     []uint16
	ScreenPosition *vec2d.Vector
}

// Database key.
func (p *Player) Key() string {
	return fmt.Sprintf("player.%s", p.Username)
}

// Returns the sorted set by X or Y where this entity has to be put in
func (p *Player) AreaSet() string {
	homePlanet, _ := Get(p.HomePlanet)
	return homePlanet.AreaSet()
}

// Starts missions to one of the players planet to some other. Each mission have type
// and the user decides which part of the planet's fleet he would like to send.
func (p *Player) StartMission(source, target *Planet, fleet int32, missionType string) *Mission {
	if fleet > 100 {
		fleet = 100
	} else if fleet <= 0 {
		fleet = 10
	}
	currentTime := time.Now().UnixNano() / 1e6
	baseShipCount := source.GetShipCount()
	shipCount := int32(baseShipCount * fleet / 100)
	source.SetShipCount(baseShipCount - shipCount)

	mission := Mission{
		Color: p.Color,
		Source: embeddedPlanet{
			Name:     source.Name,
			Position: source.Position,
		},
		Target: embeddedPlanet{
			Name:     target.Name,
			Position: target.Position,
		},
		Type:      missionType,
		StartTime: currentTime,
		Player:    p.Username,
		ShipCount: shipCount,
		areaSet:   source.AreaSet(),
	}
	mission.TravelTime = calculateTravelTime(source.Position, target.Position, mission.GetSpeed())
	return &mission
}

// Creates new player after the authentication and generates color based on the unique hash
func CreatePlayer(username, TwitterID string, HomePlanet *Planet) *Player {
	userhash := simplifyHash(usernameHash(username))

	red := []uint8{151, 218, 233, 72, 245, 84}
	green := []uint8{8, 75, 177, 140, 105, 146}
	blue := []uint8{14, 15, 4, 19, 145, 219}
	hashValue := func(index uint8) uint8 {
		return uint8((userhash[0] - 48) / 2)
	}

	color := Color{red[hashValue(0)], green[hashValue(0)], blue[hashValue(0)]}
	player := Player{username, color, TwitterID, HomePlanet.Key(), []uint16{0, 0}, HomePlanet.Position}
	HomePlanet.Owner = username
	HomePlanet.Color = color
	return &player
}
<|endoftext|>


<commit_before>package graph

import (
	"errors"
	"github.com/jkomoros/boardgame/enum"
)

//EdgeFilter is a type of function that can be passed to filter in edges.
type EdgeFilter func(enum enum.Enum, from, to int) bool

//TODO: create a bunch of EdgeFilters.

//NewGridConnectedness is a helper function to create a finished graph
//representing the connections between a grid. By default it adds edges
//between each of the 8 adjacent cells. However, all neighbors must pass the
//provided filters to be added. This package also defines a number of
//Neighbor* EdgeFilters. The enum passed must be a ranged, 2 dimensional enum.
func NewGridConnectedness(ranged2DEnum enum.Enum, filter ...EdgeFilter) (Graph, error) {
	if !ranged2DEnum.IsRange() {
		return nil, errors.New("The enum was not created with AddRange")
	}
	if len(ranged2DEnum.RangeDimensions()) != 2 {
		return nil, errors.New("The enum did not have two dimensions")
	}

	graph := New(false, ranged2DEnum)

	for _, val := range ranged2DEnum.Values() {

		theNeighbors := neighbors(ranged2DEnum, val)

		for _, theFilter := range filter {
			var tempNeighbors []int
			for _, n := range theNeighbors {
				if theFilter(ranged2DEnum, val, n) {
					tempNeighbors = append(tempNeighbors, n)
				}
			}
			theNeighbors = tempNeighbors
		}

		if err := graph.AddEdges(val, theNeighbors...); err != nil {
			return nil, err
		}

	}

	graph.Finish()

	return graph, nil

}

//assumes that theEnum is a 2d ranged enum, and that start is a valid value in
//it.
func neighbors(theEnum enum.Enum, start int) []int {
	var result []int
	indexes := theEnum.ValueToRange(start)
	for rOffset := -1; rOffset < 2; rOffset++ {
		for cOffset := -1; cOffset < 2; cOffset++ {

			if rOffset == 0 && cOffset == 0 {
				//This is the start cell
				continue
			}

			r := indexes[0] + rOffset
			c := indexes[1] + cOffset

			val := theEnum.RangeToValue(r, c)

			if val > 0 {
				result = append(result, val)
			}

		}
	}
	return result
}
<commit_msg>Defined a number of EdgeFilters for use with NewGridConnectedness. Part of #493.<commit_after>package graph

import (
	"errors"
	"github.com/jkomoros/boardgame/enum"
)

//EdgeFilter is a type of function that can be passed to filter in edges. Only
//edges that return true will be kept.
type EdgeFilter func(enum enum.Enum, from, to int) bool

//DirectionUp will return true if to is in a strictly lower-indexed row then
//from.
func DirectionUp(enum enum.Enum, from, to int) bool {
	fromIndexes := enum.ValueToRange(from)
	toIndexes := enum.ValueToRange(to)
	return fromIndexes[0] > toIndexes[0]
}

//DirectionDown will return true if to is in a strictly higher-indexed row
//then from.
func DirectionDown(enum enum.Enum, from, to int) bool {
	fromIndexes := enum.ValueToRange(from)
	toIndexes := enum.ValueToRange(to)
	return fromIndexes[0] > toIndexes[0]
}

//DirectionLeft will return true if to is in a strictly lower-indexed col then
//from.
func DirectionLeft(enum enum.Enum, from, to int) bool {
	fromIndexes := enum.ValueToRange(from)
	toIndexes := enum.ValueToRange(to)
	return fromIndexes[1] > toIndexes[1]
}

//DirectionRight will return true if to is in a strictly higher-indexed col
//then from.
func DirectionRight(enum enum.Enum, from, to int) bool {
	fromIndexes := enum.ValueToRange(from)
	toIndexes := enum.ValueToRange(to)
	return fromIndexes[1] > toIndexes[1]
}

//DirectionPerpendicular will return true if to is perpendicular to from (in the
//same row or col).
func DirectionPerpendicular(enum enum.Enum, from, to int) bool {
	fromIndexes := enum.ValueToRange(from)
	toIndexes := enum.ValueToRange(to)
	if fromIndexes[0] == toIndexes[0] {
		return true
	}
	return fromIndexes[1] == fromIndexes[1]
}

//DirectionDiagonal will return true if to is non-perpendicular to from.
func DirectionDiagonal(enum enum.Enum, from, to int) bool {
	return !DirectionPerpendicular(enum, from, to)
}

//NewGridConnectedness is a helper function to create a finished graph
//representing the connections between a grid. By default it adds edges
//between each of the 8 adjacent cells. However, all neighbors must pass the
//provided filters to be added. This package also defines a number of
//Direction* EdgeFilters. The enum passed must be a ranged, 2 dimensional enum.
func NewGridConnectedness(ranged2DEnum enum.Enum, filter ...EdgeFilter) (Graph, error) {
	if !ranged2DEnum.IsRange() {
		return nil, errors.New("The enum was not created with AddRange")
	}
	if len(ranged2DEnum.RangeDimensions()) != 2 {
		return nil, errors.New("The enum did not have two dimensions")
	}

	graph := New(false, ranged2DEnum)

	for _, val := range ranged2DEnum.Values() {

		theNeighbors := neighbors(ranged2DEnum, val)

		for _, theFilter := range filter {
			var tempNeighbors []int
			for _, n := range theNeighbors {
				if theFilter(ranged2DEnum, val, n) {
					tempNeighbors = append(tempNeighbors, n)
				}
			}
			theNeighbors = tempNeighbors
		}

		if err := graph.AddEdges(val, theNeighbors...); err != nil {
			return nil, err
		}

	}

	graph.Finish()

	return graph, nil

}

//assumes that theEnum is a 2d ranged enum, and that start is a valid value in
//it.
func neighbors(theEnum enum.Enum, start int) []int {
	var result []int
	indexes := theEnum.ValueToRange(start)
	for rOffset := -1; rOffset < 2; rOffset++ {
		for cOffset := -1; cOffset < 2; cOffset++ {

			if rOffset == 0 && cOffset == 0 {
				//This is the start cell
				continue
			}

			r := indexes[0] + rOffset
			c := indexes[1] + cOffset

			val := theEnum.RangeToValue(r, c)

			if val > 0 {
				result = append(result, val)
			}

		}
	}
	return result
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"crypto/cipher"
	"crypto/des"
	"encoding/base64"
	"encoding/json"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"os"
	"strconv"
	"time"
)

var (
	IV          []byte = []byte{1, 2, 3, 4, 5, 6, 7, 8}
	WoServer    string = "http://112.96.28.144:8080/woserver/"
	PhoneNumber string = os.Getenv("WO_PHONENUMBER")
	Password    string = os.Getenv("WO_PASSWORD")
	Key         string
	Jsessionid  string
	Client      *http.Client
)

func PKCS5Padding(ciphertext []byte, blockSize int) []byte {
	padding := blockSize - len(ciphertext)%blockSize
	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
	return append(ciphertext, padtext...)
}

func DesEncrypt(origData, key []byte) ([]byte, error) {
	block, err := des.NewCipher(key)
	if err != nil {
		return nil, err
	}
	origData = PKCS5Padding(origData, block.BlockSize())
	blockMode := cipher.NewCBCEncrypter(block, IV)
	crypted := make([]byte, len(origData))
	blockMode.CryptBlocks(crypted, origData)
	return crypted, nil
}

func Encrypt(origData, key string) string {
	crypted, err := DesEncrypt([]byte(origData), []byte(key))
	if err != nil {
		log.Panicln(err)
	}
	return base64.StdEncoding.EncodeToString(crypted)
}

func getDate() string {
	return time.Now().Format("2006-01-02 15:04:05")
}

func fetch(method, urlStr string, values url.Values) []byte {
	var req *http.Request
	var err error

	if method == "POST" {
		req, err = http.NewRequest(method, urlStr, bytes.NewBufferString(values.Encode()))
		if err != nil {
			log.Panicln(err)
		}
		req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
		req.Header.Set("User-Agent", "")
	} else if method == "GET" {
		req, err = http.NewRequest(method, urlStr, nil)
		if err != nil {
			log.Panicln(err)
		}
		req.URL.RawQuery = values.Encode()
		req.Header.Set("User-Agent", "Mozilla/5.0 (Linux; Android 5.1.1; Nexus 5 Build/LMY48B; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/43.0.2357.65 Mobile Safari/537.36")
	} else {
		log.Fatalln("fetch failed: unknow method", method)
	}

	resp, err := Client.Do(req)
	if err != nil {
		log.Panicln(err)
	}

	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Panicln(err)
	}
	return body
}

func getKey() string {
	if Key != "" {
		return Key
	}

	body := fetch("POST", WoServer+"woclient", url.Values{
		"u": {Encrypt("17woclient"+getDate(), "17woclient"[0:8])},
	})

	var res struct {
		Status string
		U      string
	}

	json.Unmarshal(body, &res)
	if res.Status == "0" && len(res.U) == 8 {
		Key = res.U
		log.Println("getKeyFromRemote:", Key)
	} else {
		log.Fatalln("getKeyFromRemote failed.")
	}

	return Key
}

func getJsessionid() string {
	if Jsessionid != "" {
		return Jsessionid
	}

	// login phase 1 / 2
	body := fetch("POST", WoServer+"login", url.Values{
		"loginType": {Encrypt("1", getKey())},
		"mobile":    {Encrypt(PhoneNumber, getKey())},
		"password":  {Encrypt(Password, getKey())},
		"username":  {""},
	})

	var res struct {
		Code   string
		Result struct {
			ResultCode    int
			ResultMessage string
			Properties    struct {
				Jsessionid string
			}
		}
	}

	json.Unmarshal(body, &res)
	if res.Code == "00000000" && res.Result.ResultCode == 0 {
		log.Println("login phase 1 / 2 success.")
	} else {
		log.Fatalln("login phase 1 / 2 failed.")
	}

	// login phase 2 / 2
	body = fetch("POST", WoServer+"woClientLoginServlet", url.Values{
		"phone_number": {Encrypt(PhoneNumber, getKey())},
	})

	json.Unmarshal(body, &res)
	if res.Code == "00000000" && res.Result.ResultCode == 0 {
		Jsessionid = res.Result.Properties.Jsessionid
		log.Println("login phase 2 / 2 success:", "Jsessionid =", Jsessionid)
	} else {
		log.Fatalln("login phase 2 / 2 failed.")
	}

	return Jsessionid
}

func getUserinfo() {
	body := fetch("POST", WoServer+"userInfo", url.Values{
		"date":       {Encrypt(getDate(), getKey())},
		"jsessionid": {Encrypt(getJsessionid(), getKey())},
		"mobile":     {Encrypt(PhoneNumber, getKey())},
	})

	var res struct {
		Userlogo      string
		Username      string
		Grade         int
		Growup        int
		MaxGrowup     int
		Is_distribute string
		Is_share      string
		Status        string
		Message       string
	}

	json.Unmarshal(body, &res)
	if res.Status == "0000" {
		log.Printf("getUserinfo success: username = %s, grade = %d, growup = %d\n",
			res.Username, res.Grade, res.Growup)
	} else {
		log.Println("getUserinfo faild.")
	}
}

func getSigninfo() {
	body := fetch("POST", WoServer+"signInfo", url.Values{
		"jsessionid": {Encrypt(getJsessionid(), getKey())},
		"mobile":     {Encrypt(PhoneNumber, getKey())},
	})

	var res struct {
		Status  string
		Message string
		Data    struct {
			ContinusDay4Week int
			Flowrate         int
			HasSigned        bool
			ContinusDay      int
			SignMonthTotal   int
		}
	}

	json.Unmarshal(body, &res)
	if res.Status == "0" {
		log.Printf("getSigninfo success: Flowrate = %d, HasSigned = %t, ContinusDay = %d\n",
			res.Data.Flowrate, res.Data.HasSigned, res.Data.ContinusDay)
	} else {
		log.Println("getSigninfo failed.")
	}
}

func signAndReceviFlow() {
	body := fetch("POST", WoServer+"signAndReceviFlow", url.Values{
		"dayInt":     {Encrypt("1", getKey())},
		"jsessionid": {Encrypt(getJsessionid(), getKey())},
		"mobile":     {Encrypt(PhoneNumber, getKey())},
	})

	var res struct {
		Status  string
		Message string
		Data    struct {
			LastSignDay      string
			ContinusDay      int
			IsTodayFirstSign bool
			LastSignTime     string
		}
		ReceviFlowData struct {
			ApplyAwardResult struct {
				AwardType  int
				AwardValue int
			}
		}
	}

	json.Unmarshal(body, &res)
	if res.Status == "2" {
		log.Println("signAndReceviFlow success: AwardValue =", res.ReceviFlowData.ApplyAwardResult.AwardValue)
	} else {
		log.Println("signAndReceviFlow failed.")
	}
}

func getUnixMillis() string {
	return strconv.FormatInt(time.Now().UnixNano()/1000000, 10)
}

func initWap() {
	jsessionid, _ := url.QueryUnescape(getJsessionid())
	fetch("GET", "http://wap.17wo.cn/Index.action", url.Values{
		"from":       {"17woclient"},
		"jsessionid": {jsessionid},
	})
}

func luckDraw() {
	body := fetch("GET", "http://wap.17wo.cn/FlowRedPacket!LuckDraw.action", nil)
	log.Println(string(body))

	body = fetch("GET", "http://wap.17wo.cn/FlowRedPacket!share.action", url.Values{
		"sendid":       {""},
		"sharecontent": {"undefined"},
		"subjectId":    {"0"},
		"cpd":          {""},
		"_":            {getUnixMillis()},
	})
	log.Println(string(body))

}

func earnflow() {
	for k := 0; k < 3; k++ {
		body := fetch("GET", "http://wap.17wo.cn/FlowRedPacket!LuckDraw.action", url.Values{
			"pageName": {"earnflow"},
			"_":        {getUnixMillis()},
		})
		log.Println(string(body))
	}
}

func gainTaskAwards() {
	// 任务：登录 ("taskId", "28")
	// 任务：签到 ("taskId", "29")
	// 任务：派红包 ("taskId", "36")
	// 任务：下载一起沃客户端 ("taskId", "38")
	// 任务：订购“365一起沃产品” ("taskId", TODO)
	taskIds := []string{"28", "29"}

	for _, taskId := range taskIds {
		body := fetch("GET", "http://wap.17wo.cn/UserCenterGrowup!gainTaskAwards.action", url.Values{
			"aId":    {"117"},
			"taskId": {taskId},
			"_":      {getUnixMillis()},
		})
		log.Println(string(body))
	}
}

func getStatusOfDiamonds() {
	body := fetch("GET", "http://wap.17wo.cn/DiamondFlow!getStatusOfDiamonds.action", nil)
	log.Println(string(body))
}

func getUserFlowInfo() {
	body := fetch("GET", "http://wap.17wo.cn/DiamondFlow!getUserFlowInfo.action", nil)
	log.Println(string(body))
}

func changeStatusOfDiamonds() {
	diamonds := []string{"green-con", "red-con", "yellow-con"}
	for _, diamond := range diamonds {
		body := fetch("GET", "http://wap.17wo.cn/DiamondFlow!changeStatusOfDiamonds.action", url.Values{
			"diamondButton": {diamond},
		})
		log.Println(string(body))
	}
}

func getTurnAwardLuckDraw() {
	body := fetch("GET", "http://wap.17wo.cn/PlayTurntable!getTurnAwardLuckDraw.action", url.Values{
		"_": {getUnixMillis()},
	})
	log.Println(string(body))
}

func main() {
	log.Println("===== wo.go start =====")

	// init
	cj, _ := cookiejar.New(nil)
	Client = &http.Client{
		Jar: cj,
	}

	// app
	getUserinfo()
	getSigninfo()
	signAndReceviFlow()

	// wap
	initWap()
	luckDraw()
	earnflow()
	gainTaskAwards()
	getStatusOfDiamonds()
	getUserFlowInfo()
	changeStatusOfDiamonds()
	getTurnAwardLuckDraw()

	log.Println("===== wo.go end =====\n")
}
<commit_msg>replae wap.17wo.cn to 17wo.cn<commit_after>package main

import (
	"bytes"
	"crypto/cipher"
	"crypto/des"
	"encoding/base64"
	"encoding/json"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"os"
	"strconv"
	"time"
)

var (
	IV          []byte = []byte{1, 2, 3, 4, 5, 6, 7, 8}
	WoServer    string = "http://112.96.28.144:8080/woserver/"
	PhoneNumber string = os.Getenv("WO_PHONENUMBER")
	Password    string = os.Getenv("WO_PASSWORD")
	Key         string
	Jsessionid  string
	Client      *http.Client
)

func PKCS5Padding(ciphertext []byte, blockSize int) []byte {
	padding := blockSize - len(ciphertext)%blockSize
	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
	return append(ciphertext, padtext...)
}

func DesEncrypt(origData, key []byte) ([]byte, error) {
	block, err := des.NewCipher(key)
	if err != nil {
		return nil, err
	}
	origData = PKCS5Padding(origData, block.BlockSize())
	blockMode := cipher.NewCBCEncrypter(block, IV)
	crypted := make([]byte, len(origData))
	blockMode.CryptBlocks(crypted, origData)
	return crypted, nil
}

func Encrypt(origData, key string) string {
	crypted, err := DesEncrypt([]byte(origData), []byte(key))
	if err != nil {
		log.Panicln(err)
	}
	return base64.StdEncoding.EncodeToString(crypted)
}

func getDate() string {
	return time.Now().Format("2006-01-02 15:04:05")
}

func fetch(method, urlStr string, values url.Values) []byte {
	var req *http.Request
	var err error

	if method == "POST" {
		req, err = http.NewRequest(method, urlStr, bytes.NewBufferString(values.Encode()))
		if err != nil {
			log.Panicln(err)
		}
		req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
		req.Header.Set("User-Agent", "")
	} else if method == "GET" {
		req, err = http.NewRequest(method, urlStr, nil)
		if err != nil {
			log.Panicln(err)
		}
		req.URL.RawQuery = values.Encode()
		req.Header.Set("User-Agent", "Mozilla/5.0 (Linux; Android 5.1.1; Nexus 5 Build/LMY48B; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/43.0.2357.65 Mobile Safari/537.36")
	} else {
		log.Fatalln("fetch failed: unknow method", method)
	}

	resp, err := Client.Do(req)
	if err != nil {
		log.Panicln(err)
	}

	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Panicln(err)
	}
	return body
}

func getKey() string {
	if Key != "" {
		return Key
	}

	body := fetch("POST", WoServer+"woclient", url.Values{
		"u": {Encrypt("17woclient"+getDate(), "17woclient"[0:8])},
	})

	var res struct {
		Status string
		U      string
	}

	json.Unmarshal(body, &res)
	if res.Status == "0" && len(res.U) == 8 {
		Key = res.U
		log.Println("getKeyFromRemote:", Key)
	} else {
		log.Fatalln("getKeyFromRemote failed.")
	}

	return Key
}

func getJsessionid() string {
	if Jsessionid != "" {
		return Jsessionid
	}

	// login phase 1 / 2
	body := fetch("POST", WoServer+"login", url.Values{
		"loginType": {Encrypt("1", getKey())},
		"mobile":    {Encrypt(PhoneNumber, getKey())},
		"password":  {Encrypt(Password, getKey())},
		"username":  {""},
	})

	var res struct {
		Code   string
		Result struct {
			ResultCode    int
			ResultMessage string
			Properties    struct {
				Jsessionid string
			}
		}
	}

	json.Unmarshal(body, &res)
	if res.Code == "00000000" && res.Result.ResultCode == 0 {
		log.Println("login phase 1 / 2 success.")
	} else {
		log.Fatalln("login phase 1 / 2 failed.")
	}

	// login phase 2 / 2
	body = fetch("POST", WoServer+"woClientLoginServlet", url.Values{
		"phone_number": {Encrypt(PhoneNumber, getKey())},
	})

	json.Unmarshal(body, &res)
	if res.Code == "00000000" && res.Result.ResultCode == 0 {
		Jsessionid = res.Result.Properties.Jsessionid
		log.Println("login phase 2 / 2 success:", "Jsessionid =", Jsessionid)
	} else {
		log.Fatalln("login phase 2 / 2 failed.")
	}

	return Jsessionid
}

func getUserinfo() {
	body := fetch("POST", WoServer+"userInfo", url.Values{
		"date":       {Encrypt(getDate(), getKey())},
		"jsessionid": {Encrypt(getJsessionid(), getKey())},
		"mobile":     {Encrypt(PhoneNumber, getKey())},
	})

	var res struct {
		Userlogo      string
		Username      string
		Grade         int
		Growup        int
		MaxGrowup     int
		Is_distribute string
		Is_share      string
		Status        string
		Message       string
	}

	json.Unmarshal(body, &res)
	if res.Status == "0000" {
		log.Printf("getUserinfo success: username = %s, grade = %d, growup = %d\n",
			res.Username, res.Grade, res.Growup)
	} else {
		log.Println("getUserinfo faild.")
	}
}

func getSigninfo() {
	body := fetch("POST", WoServer+"signInfo", url.Values{
		"jsessionid": {Encrypt(getJsessionid(), getKey())},
		"mobile":     {Encrypt(PhoneNumber, getKey())},
	})

	var res struct {
		Status  string
		Message string
		Data    struct {
			ContinusDay4Week int
			Flowrate         int
			HasSigned        bool
			ContinusDay      int
			SignMonthTotal   int
		}
	}

	json.Unmarshal(body, &res)
	if res.Status == "0" {
		log.Printf("getSigninfo success: Flowrate = %d, HasSigned = %t, ContinusDay = %d\n",
			res.Data.Flowrate, res.Data.HasSigned, res.Data.ContinusDay)
	} else {
		log.Println("getSigninfo failed.")
	}
}

func signAndReceviFlow() {
	body := fetch("POST", WoServer+"signAndReceviFlow", url.Values{
		"dayInt":     {Encrypt("1", getKey())},
		"jsessionid": {Encrypt(getJsessionid(), getKey())},
		"mobile":     {Encrypt(PhoneNumber, getKey())},
	})

	var res struct {
		Status  string
		Message string
		Data    struct {
			LastSignDay      string
			ContinusDay      int
			IsTodayFirstSign bool
			LastSignTime     string
		}
		ReceviFlowData struct {
			ApplyAwardResult struct {
				AwardType  int
				AwardValue int
			}
		}
	}

	json.Unmarshal(body, &res)
	if res.Status == "2" {
		log.Println("signAndReceviFlow success: AwardValue =", res.ReceviFlowData.ApplyAwardResult.AwardValue)
	} else {
		log.Println("signAndReceviFlow failed.")
	}
}

func getUnixMillis() string {
	return strconv.FormatInt(time.Now().UnixNano()/1000000, 10)
}

func loginWeb() {
	jsessionid, _ := url.QueryUnescape(getJsessionid())
	fetch("GET", "http://17wo.cn/Index.action", url.Values{
		"from":       {"17woclient"},
		"jsessionid": {jsessionid},
	})
}

func luckDraw() {
	body := fetch("GET", "http://17wo.cn/FlowRedPacket!LuckDraw.action", nil)
	log.Println(string(body))

	body = fetch("GET", "http://17wo.cn/FlowRedPacket!share.action", url.Values{
		"sendid":       {""},
		"sharecontent": {"undefined"},
		"subjectId":    {"0"},
		"cpd":          {""},
		"_":            {getUnixMillis()},
	})
	log.Println(string(body))

}

func earnflow() {
	for k := 0; k < 3; k++ {
		body := fetch("GET", "http://17wo.cn/FlowRedPacket!LuckDraw.action", url.Values{
			"pageName": {"earnflow"},
			"_":        {getUnixMillis()},
		})
		log.Println(string(body))
	}
}

func gainTaskAwards() {
	// 任务：登录 ("taskId", "28")
	// 任务：签到 ("taskId", "29")
	// 任务：派红包 ("taskId", "36")
	// 任务：下载一起沃客户端 ("taskId", "38")
	// 任务：订购“365一起沃产品” ("taskId", TODO)
	taskIds := []string{"28", "29"}

	for _, taskId := range taskIds {
		body := fetch("GET", "http://17wo.cn/UserCenterGrowup!gainTaskAwards.action", url.Values{
			"aId":    {"117"},
			"taskId": {taskId},
			"_":      {getUnixMillis()},
		})
		log.Println(string(body))
	}
}

func getStatusOfDiamonds() {
	body := fetch("GET", "http://17wo.cn/DiamondFlow!getStatusOfDiamonds.action", nil)
	log.Println(string(body))
}

func getUserFlowInfo() {
	body := fetch("GET", "http://17wo.cn/DiamondFlow!getUserFlowInfo.action", nil)
	log.Println(string(body))
}

func changeStatusOfDiamonds() {
	diamonds := []string{"green-con", "red-con", "yellow-con"}
	for _, diamond := range diamonds {
		body := fetch("GET", "http://17wo.cn/DiamondFlow!changeStatusOfDiamonds.action", url.Values{
			"diamondButton": {diamond},
		})
		log.Println(string(body))
	}
}

func getTurnAwardLuckDraw() {
	body := fetch("GET", "http://17wo.cn/PlayTurntable!getTurnAwardLuckDraw.action", url.Values{
		"_": {getUnixMillis()},
	})
	log.Println(string(body))
}

func main() {
	log.Println("===== wo.go start =====")

	// init
	cj, _ := cookiejar.New(nil)
	Client = &http.Client{
		Jar: cj,
	}

	// app
	getUserinfo()
	getSigninfo()
	signAndReceviFlow()

	// web
	loginWeb()
	luckDraw()
	earnflow()
	gainTaskAwards()
	getStatusOfDiamonds()
	getUserFlowInfo()
	changeStatusOfDiamonds()
	getTurnAwardLuckDraw()

	log.Println("===== wo.go end =====\n")
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"net/http"
	"time"

	"github.com/gorilla/websocket"
	"github.com/pjebs/tokbox"
)

const (
	ROOM_DATA  = "ROOM_DATA"
	RESET_ROOM = "RESET_ROOM"

	JOIN_ACTION          = "JOIN_ACTION"
	JOIN                 = "JOIN"
	LEAVE_ACTION         = "LEAVE_ACTION"
	LEAVE                = "LEAVE"
	ADD_COMMENT_ACTION   = "ADD_COMMENT_ACTION"
	ADD_COMMENT          = "ADD_COMMENT"
	CHANGE_TITLE_ACTION  = "CHANGE_TITLE_ACTION"
	CHANGE_TITLE         = "CHANGE_TITLE"
	ACCEPT_CALLER_ACTION = "ACCEPT_CALLER_ACTION"
	ACCEPT_CALLER        = "ACCEPT_CALLER"
	LEAVE_SEAT_ACTION    = "LEAVE_SEAT_ACTION"
	LEAVE_SEAT           = "LEAVE_SEAT"
	CALL_ACTION          = "CALL_ACTION"
	CALL                 = "CALL"
	CANCEL_CALL_ACTION   = "CANCEL_CALL_ACTION"
	CANCEL_CALL          = "CANCEL_CALL"
)

var upgrader = websocket.Upgrader{
	CheckOrigin: func(r *http.Request) bool { return true },
}

func getMessage(
	messageType string, payload map[string]interface{},
) map[string]interface{} {
	return map[string]interface{}{
		"type":    messageType,
		"payload": payload,
	}
}

func newToken(session *tokbox.Session, role tokbox.Role, userId string) (string, error) {
	return session.Token(role, "userId="+userId, tokbox.Days30)
}

/* func subToken(session *tokbox.Session, subRole tokbox.Role, userId string) (string, error) {
	return session.Token(subRole, "userId="+userId, tokbox.Days30)
}*/

func (s *Server) HandleWS(w http.ResponseWriter, r *http.Request) {
	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Println("upgrade:", err)
		return
	}
	defer conn.Close()

	session, err := s.RediStore.Get(r, sessionPrefix)
	if err != nil {
		handleInternalServerError(err, w)
		return
	}

	// Store the authenticated user as userId
	userId := ""
	if session.Values[tokenCredKey] != nil {
		userId = session.Values[usernameKey].(string)
	}

	// Keep the connection alive
	timeout := time.Duration(60) * time.Second
	tickDuration := timeout / 2
	tick := time.Tick(tickDuration)
	tickCloseCh := make(chan bool)
	go func() {
		for {
			select {
			case <-tick:
				deadline := time.Now().Add(tickDuration)
				err := conn.WriteControl(
					websocket.PingMessage,
					nil,
					deadline,
				)
				if err != nil {
					log.Println("tick error:", err)
					return
				}
			case <-tickCloseCh:
				return
			}
		}
	}()

	defer func() {
		tickCloseCh <- true
	}()

	// Setup state
	// Only one room available per connection
	currentRoomId := ""
	isHost := false
	var roomMessages chan StateMessage

	leave := func() {
		if currentRoomId != "" {
			s.State.CancelCall(currentRoomId, roomMessages, userId)
			s.State.LeaveSeat(currentRoomId, roomMessages, userId)
			s.State.Leave(currentRoomId, roomMessages, userId)
			if isHost {
				s.State.Reset(currentRoomId, roomMessages)
			}
		}
	}
	defer leave()

	// Receive messages
	for {
		var message StateMessage
		if err := conn.ReadJSON(&message); err != nil {
			if !websocket.IsCloseError(err, websocket.CloseGoingAway) {
				log.Println("read error:", err)
			}
			return
		}

		switch message.Type {
		case JOIN_ACTION:
			leave()
			currentRoomId = message.Payload.(string)
			isHost = userId != "" && userId == currentRoomId
			room, messages := s.State.Join(currentRoomId, userId, r.RemoteAddr)
			roomMessages = messages

			// Create the room's TokBox session if it does not exist.
			var session *tokbox.Session
			if room.sessionId == "" {
				// Add a new session id to the room.
				session, err = s.TokBox.NewSession("", tokbox.MediaRouter)
				if err != nil {
					log.Println("tokbox new session error:", err)
					return
				}

				s.State.SetRoomSessionId(currentRoomId, session.SessionId)
				// Set this before the JSON serialization.
				room.sessionId = session.SessionId
			} else {
				session = &tokbox.Session{
					SessionId: room.sessionId,
					T:         s.TokBox,
				}
			}

			role := tokbox.Role(tokbox.Publisher)
			// TODO: send specific tokens based on the user role. this
			// current solution is a vulnerability.
			// role := tokbox.Role(tokbox.Subscriber)
			// if isHost {
			// 	// Only the host gets the publish ability on initial connection.
			// 	role = tokbox.Role(tokbox.Publisher)
			// }
			// subRole := tokbox.Role(tokbox.Subscriber)

			token, err := newToken(session, role, userId)
			if err != nil {
				println("err", err.Error())
				log.Println("token generation error:", err)
				return
			}

			/* subToken, err := subToken(session, subRole, userId)
			if err != nil {
				println("err", err.Error())
				log.Println("token generation error:", err)
				return
			}*/

			// Send the initial payload on join.
			roomData := room.ToJSON()
			roomData["token"] = token
			// roomData["subToken"] = subToken
			roomData["tokBoxKey"] = s.TokBoxKey
			roomMessage := getMessage(ROOM_DATA, roomData)
			if err = conn.WriteJSON(roomMessage); err != nil {
				log.Println("join error:", err)
				return
			}

			go func() {
				for {
					message := <-roomMessages
					if message.End {
						return
					}

					if err = conn.WriteJSON(map[string]interface{}{
						"payload": message.Payload,
						"type":    message.Type,
					}); err != nil {
						log.Println("send error:", err)
					}
				}
			}()

		case LEAVE_ACTION:
			leave()

		case ADD_COMMENT_ACTION:
			// Checks that:
			// 1. A room is already loaded.
			// 2. The user is authenticated.
			if currentRoomId != "" && userId != "" {
				s.State.AddComment(currentRoomId, roomMessages, Comment{
					SenderId: userId,
					Text:     message.Payload.(string),
				})
			}

		case CHANGE_TITLE_ACTION:
			// Checks that:
			// 1. A room is already loaded.
			// 2. The user is authenticated.
			// 3. The user is the host.
			if isHost {
				title := message.Payload.(string)
				s.State.ChangeRoomTitle(currentRoomId, roomMessages, title)
			}

		case ACCEPT_CALLER_ACTION:
			if isHost {
				callerId := message.Payload.(string)
				s.State.AcceptCaller(currentRoomId, roomMessages, callerId)
			}

		case LEAVE_SEAT_ACTION:
			leavingUserId := message.Payload.(string)
			if isHost || leavingUserId == userId {
				s.State.LeaveSeat(currentRoomId, roomMessages, leavingUserId)
			}

		case CALL_ACTION:
			if currentRoomId != "" && userId != "" && currentRoomId != userId {
				s.State.Call(currentRoomId, roomMessages, userId)
			}

		case CANCEL_CALL_ACTION:
			if currentRoomId != "" && userId != "" && currentRoomId != userId {
				s.State.CancelCall(currentRoomId, roomMessages, userId)
			}
		}
	}
}
<commit_msg>added remoteAddr workaround<commit_after>package main

import (
	"strings"
	"bytes"
	"log"
	"net/http"
	"net"
	"time"

	"github.com/gorilla/websocket"
	"github.com/pjebs/tokbox"
)

const (
	ROOM_DATA  = "ROOM_DATA"
	RESET_ROOM = "RESET_ROOM"

	JOIN_ACTION          = "JOIN_ACTION"
	JOIN                 = "JOIN"
	LEAVE_ACTION         = "LEAVE_ACTION"
	LEAVE                = "LEAVE"
	ADD_COMMENT_ACTION   = "ADD_COMMENT_ACTION"
	ADD_COMMENT          = "ADD_COMMENT"
	CHANGE_TITLE_ACTION  = "CHANGE_TITLE_ACTION"
	CHANGE_TITLE         = "CHANGE_TITLE"
	ACCEPT_CALLER_ACTION = "ACCEPT_CALLER_ACTION"
	ACCEPT_CALLER        = "ACCEPT_CALLER"
	LEAVE_SEAT_ACTION    = "LEAVE_SEAT_ACTION"
	LEAVE_SEAT           = "LEAVE_SEAT"
	CALL_ACTION          = "CALL_ACTION"
	CALL                 = "CALL"
	CANCEL_CALL_ACTION   = "CANCEL_CALL_ACTION"
	CANCEL_CALL          = "CANCEL_CALL"
)

var upgrader = websocket.Upgrader{
	CheckOrigin: func(r *http.Request) bool { return true },
}

func getMessage(
	messageType string, payload map[string]interface{},
) map[string]interface{} {
	return map[string]interface{}{
		"type":    messageType,
		"payload": payload,
	}
}

func newToken(session *tokbox.Session, role tokbox.Role, userId string) (string, error) {
	return session.Token(role, "userId="+userId, tokbox.Days30)
}

type ipRange struct {
    start net.IP
    end net.IP
}

// inRange - check to see if a given ip address is within a range given
func inRange(r ipRange, ipAddress net.IP) bool {
    // strcmp type byte comparison
    if bytes.Compare(ipAddress, r.start) >= 0 && bytes.Compare(ipAddress, r.end) < 0 {
        return true
    }
    return false
}

var privateRanges = []ipRange{
    ipRange{
        start: net.ParseIP("10.0.0.0"),
        end:   net.ParseIP("10.255.255.255"),
    },
    ipRange{
        start: net.ParseIP("100.64.0.0"),
        end:   net.ParseIP("100.127.255.255"),
    },
    ipRange{
        start: net.ParseIP("172.16.0.0"),
        end:   net.ParseIP("172.31.255.255"),
    },
    ipRange{
        start: net.ParseIP("192.0.0.0"),
        end:   net.ParseIP("192.0.0.255"),
    },
    ipRange{
        start: net.ParseIP("192.168.0.0"),
        end:   net.ParseIP("192.168.255.255"),
    },
    ipRange{
        start: net.ParseIP("198.18.0.0"),
        end:   net.ParseIP("198.19.255.255"),
    },
}

// isPrivateSubnet - check to see if this ip is in a private subnet
func isPrivateSubnet(ipAddress net.IP) bool {
    // my use case is only concerned with ipv4 atm
    if ipCheck := ipAddress.To4(); ipCheck != nil {
        // iterate over all our ranges
        for _, r := range privateRanges {
            // check if this ip is in a private range
            if inRange(r, ipAddress){
                return true
            }
        }
    }
    return false
}

func getIPAdress(r *http.Request) string {
    for _, h := range []string{"X-Forwarded-For", "X-Real-Ip"} {
        addresses := strings.Split(r.Header.Get(h), ",")
        // march from right to left until we get a public address
        // that will be the address right before our proxy.
        for i := len(addresses) -1 ; i >= 0; i-- {
            ip := strings.TrimSpace(addresses[i])
            // header can contain spaces too, strip those out.
            realIP := net.ParseIP(ip)
            if !realIP.IsGlobalUnicast() || isPrivateSubnet(realIP) {
                // bad address, go to next
                continue
            }
            return ip
        }
    }
    return ""
}

func (s *Server) HandleWS(w http.ResponseWriter, r *http.Request) {
	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Println("upgrade:", err)
		return
	}
	defer conn.Close()

	session, err := s.RediStore.Get(r, sessionPrefix)
	if err != nil {
		handleInternalServerError(err, w)
		return
	}

	// Store the authenticated user as userId
	userId := ""
	if session.Values[tokenCredKey] != nil {
		userId = session.Values[usernameKey].(string)
	}

	// Keep the connection alive
	timeout := time.Duration(60) * time.Second
	tickDuration := timeout / 2
	tick := time.Tick(tickDuration)
	tickCloseCh := make(chan bool)
	go func() {
		for {
			select {
			case <-tick:
				deadline := time.Now().Add(tickDuration)
				err := conn.WriteControl(
					websocket.PingMessage,
					nil,
					deadline,
				)
				if err != nil {
					log.Println("tick error:", err)
					return
				}
			case <-tickCloseCh:
				return
			}
		}
	}()

	defer func() {
		tickCloseCh <- true
	}()

	// Setup state
	// Only one room available per connection
	currentRoomId := ""
	isHost := false
	var roomMessages chan StateMessage

	leave := func() {
		if currentRoomId != "" {
			s.State.CancelCall(currentRoomId, roomMessages, userId)
			s.State.LeaveSeat(currentRoomId, roomMessages, userId)
			s.State.Leave(currentRoomId, roomMessages, userId)
			if isHost {
				s.State.Reset(currentRoomId, roomMessages)
			}
		}
	}
	defer leave()

	// Receive messages
	for {
		var message StateMessage
		if err := conn.ReadJSON(&message); err != nil {
			if !websocket.IsCloseError(err, websocket.CloseGoingAway) {
				log.Println("read error:", err)
			}
			return
		}

		switch message.Type {
		case JOIN_ACTION:
			leave()
			currentRoomId = message.Payload.(string)
			isHost = userId != "" && userId == currentRoomId
			getIPAdress := getIPAdress(r)
			//TODO allow multiple clients to join same room via LAN/RemoteAddr
			room, messages := s.State.Join(currentRoomId, userId, getIPAdress)
			roomMessages = messages

			// Create the room's TokBox session if it does not exist.
			var session *tokbox.Session
			if room.sessionId == "" {
				// Add a new session id to the room.
				session, err = s.TokBox.NewSession("", tokbox.MediaRouter)
				if err != nil {
					log.Println("tokbox new session error:", err)
					return
				}

				s.State.SetRoomSessionId(currentRoomId, session.SessionId)
				// Set this before the JSON serialization.
				room.sessionId = session.SessionId
			} else {
				session = &tokbox.Session{
					SessionId: room.sessionId,
					T:         s.TokBox,
				}
			}

			role := tokbox.Role(tokbox.Publisher)
			// TODO: send specific tokens based on the user role. this
			// current solution is a vulnerability.
			// role := tokbox.Role(tokbox.Subscriber)
			// if isHost {
			// 	// Only the host gets the publish ability on initial connection.
			// 	role = tokbox.Role(tokbox.Publisher)
			// }
			// subRole := tokbox.Role(tokbox.Subscriber)

			token, err := newToken(session, role, userId)
			if err != nil {
				println("err", err.Error())
				log.Println("token generation error:", err)
				return
			}

			/* subToken, err := subToken(session, subRole, userId)
			if err != nil {
				println("err", err.Error())
				log.Println("token generation error:", err)
				return
			}*/

			// Send the initial payload on join.
			roomData := room.ToJSON()
			roomData["token"] = token
			// roomData["subToken"] = subToken
			roomData["tokBoxKey"] = s.TokBoxKey
			roomMessage := getMessage(ROOM_DATA, roomData)
			if err = conn.WriteJSON(roomMessage); err != nil {
				log.Println("join error:", err)
				return
			}

			go func() {
				for {
					message := <-roomMessages
					if message.End {
						return
					}

					if err = conn.WriteJSON(map[string]interface{}{
						"payload": message.Payload,
						"type":    message.Type,
					}); err != nil {
						log.Println("send error:", err)
					}
				}
			}()

		case LEAVE_ACTION:
			leave()

		case ADD_COMMENT_ACTION:
			// Checks that:
			// 1. A room is already loaded.
			// 2. The user is authenticated.
			if currentRoomId != "" && userId != "" {
				s.State.AddComment(currentRoomId, roomMessages, Comment{
					SenderId: userId,
					Text:     message.Payload.(string),
				})
			}

		case CHANGE_TITLE_ACTION:
			// Checks that:
			// 1. A room is already loaded.
			// 2. The user is authenticated.
			// 3. The user is the host.
			if isHost {
				title := message.Payload.(string)
				s.State.ChangeRoomTitle(currentRoomId, roomMessages, title)
			}

		case ACCEPT_CALLER_ACTION:
			if isHost {
				callerId := message.Payload.(string)
				s.State.AcceptCaller(currentRoomId, roomMessages, callerId)
			}

		case LEAVE_SEAT_ACTION:
			leavingUserId := message.Payload.(string)
			if isHost || leavingUserId == userId {
				s.State.LeaveSeat(currentRoomId, roomMessages, leavingUserId)
			}

		case CALL_ACTION:
			if currentRoomId != "" && userId != "" && currentRoomId != userId {
				s.State.Call(currentRoomId, roomMessages, userId)
			}

		case CANCEL_CALL_ACTION:
			if currentRoomId != "" && userId != "" && currentRoomId != userId {
				s.State.CancelCall(currentRoomId, roomMessages, userId)
			}
		}
	}
}
<|endoftext|>


<commit_before>package wshandler

import (
	"net/http"
	"fmt"
	"github.com/gorilla/websocket"
	"sync"
)

var upgrader websocket.Upgrader
var clients = make(map[*Client]bool)
var lock = sync.RWMutex{}
var rooms = make(map[string][]*Client)

const DEFAULT_ROOM  = "wshandler-room"

type Client struct {
	Conn *websocket.Conn
	Room string
	Req  *http.Request
	Res  *http.ResponseWriter
	ID string
}

func (client *Client) Add() {
	lock.Lock()
	defer lock.Unlock()
	clients[client] = true
}

func (client *Client) Remove() {
	lock.Lock()
	defer lock.Unlock()
	delete(clients, client)
}

func (client *Client) JoinRoom(room string) {
	lock.Lock()
	defer lock.Unlock()
	client.Room = room
	rooms[room] = append(rooms[room], client)
}

func (client *Client) LeaveRoom(room string) {
	lock.Lock()
	defer lock.Unlock()
	delete(rooms, room)
}

func (client *Client) Send(msg []byte, room interface{}) {
	lock.RLock()
	defer lock.RUnlock()

	if room == nil {
		room = DEFAULT_ROOM
	}

	for roomName, cl := range rooms {
		for _, roomClient := range cl {
			if room == roomName {
				roomClient.Conn.WriteMessage(websocket.TextMessage, msg)
			}
		}
	}

}

func Broadcast(msg []byte) {
	lock.RLock()
	defer lock.RUnlock()
	for c := range clients {
		c.Conn.WriteMessage(websocket.TextMessage, msg)
	}
}

func WebSocketHandler(w http.ResponseWriter, r *http.Request, OnEvent *WebSocketEvent) {
	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		fmt.Println(err)
		return
	}
	defer conn.Close()

	room := r.URL.Query().Get("room")
	if room == "" {
		room = DEFAULT_ROOM
	}
	client := Client{
		Conn: conn,
		Room: room,
		Req: r,
		Res: &w,
	}

	OnEvent.OnConnect(&client)
	client.Add()
	for {
		_, msg, err := conn.ReadMessage()
		if err != nil {
			OnEvent.OnDisconnect(&client)
			client.Remove()
			return
		}
		OnEvent.OnTextMessage(&client, msg)
	}
}

type WebSocketEvent struct {
	OnDisconnect  func(c *Client)
	OnConnect     func(c *Client)
	OnTextMessage func(c *Client, msg []byte)
}

<commit_msg>remove rooms<commit_after>package wshandler

import (
	"net/http"
	"fmt"
	"github.com/gorilla/websocket"
	"sync"
)

var upgrader websocket.Upgrader
var clients = make(map[*Client]bool)
var lock = sync.RWMutex{}

const DEFAULT_ROOM  = "wshandler-room"

type Client struct {
	Conn *websocket.Conn
	Room string
	Req  *http.Request
	Res  *http.ResponseWriter
	ID string
}

func (client *Client) Add() {
	lock.Lock()
	defer lock.Unlock()
	clients[client] = true
}

func (client *Client) Remove() {
	lock.Lock()
	defer lock.Unlock()
	delete(clients, client)
}

func (client *Client) Send(msg []byte, room interface{}) {
	lock.RLock()
	defer lock.RUnlock()

	if room == nil {
		room = DEFAULT_ROOM
	}
	for c := range clients {
		if c.Room == room {
			c.Conn.WriteMessage(websocket.TextMessage, msg)
		}
	}

}

func Broadcast(msg []byte) {
	lock.RLock()
	defer lock.RUnlock()
	for c := range clients {
		c.Conn.WriteMessage(websocket.TextMessage, msg)
	}
}

func WebSocketHandler(w http.ResponseWriter, r *http.Request, OnEvent *WebSocketEvent) {
	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		fmt.Println(err)
		return
	}
	defer conn.Close()

	room := r.URL.Query().Get("room")
	if room == "" {
		room = DEFAULT_ROOM
	}
	client := Client{
		Conn: conn,
		Room: room,
		Req: r,
		Res: &w,
	}

	OnEvent.OnConnect(&client)
	client.Add()
	for {
		_, msg, err := conn.ReadMessage()
		if err != nil {
			OnEvent.OnDisconnect(&client)
			client.Remove()
			return
		}
		OnEvent.OnTextMessage(&client, msg)
	}
}

type WebSocketEvent struct {
	OnDisconnect  func(c *Client)
	OnConnect     func(c *Client)
	OnTextMessage func(c *Client, msg []byte)
}

<|endoftext|>


<commit_before>/**
   Copyright 2012 Matthew Baird

   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 api

import (
	"bytes"
	"encoding/json"
	"fmt"
	hostpool "github.com/bitly/go-hostpool"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"runtime"
	"strings"
	"sync"
	"time"
)

type Request struct {
	*http.Request
	hostResponse hostpool.HostPoolResponse
}

const (
	Version         = "0.0.1"
	DefaultProtocol = "http"
	DefaultDomain   = "localhost"
	DefaultPort     = "9200"

	// A decay duration of zero results in the default behaviour
	DefaultDecayDuration = 0
)

var (
	_ = log.Ldate

	// Maintain these for backwards compatibility
	Protocol string = DefaultProtocol
	Domain   string = DefaultDomain
	Port     string = DefaultPort

	// Store a slice of hosts in a hostpool
	Hosts []string
	hp    hostpool.HostPool
	once  sync.Once

	// To compute the weighting scores, we perform a weighted average of recent response times,
	// over the course of `DecayDuration`. DecayDuration may be set to 0 to use the default
	// value of 5 minutes. The EpsilonValueCalculator uses this to calculate a score
	// from the weighted average response time.
	DecayDuration time.Duration = time.Duration(DefaultDecayDuration * time.Second)
)

type Error struct {
	Status  int    `json:"status"` // HTTP status code (200, 403, ...)
	Message string `json:"error"`  // The human readable error message
}

func (err *Error) Error() string {
	if err.Status == 0 {
		return err.Message
	}

	return fmt.Sprintf("Error [%s] Status [%v]", err.Message, err.Status)
}

// Build HTTP request

func ElasticSearchRequest(method, path string) (*Request, error) {

	// Initialise a hostpool on our first run
	once.Do(initialiseHostPool)

	// Get a host from the host pool
	hr := hp.Get()

	// Get the final host and port
	host, portNum := splitHostnamePartsFromHost(hr.Host(), Port)

	// Build request
	req, err := http.NewRequest(method, fmt.Sprintf("%s://%s:%s%s", Protocol, host, portNum, path), nil)
	if err != nil {
		return nil, err
	}
	req.Header.Add("Accept", "application/json")
	req.Header.Add("User-Agent", "elasticSearch/"+Version+" ("+runtime.GOOS+"-"+runtime.GOARCH+")")

	newRequest := &Request{
		Request:      req,
		hostResponse: hr,
	}

	return newRequest, nil
}

func (r *Request) SetBodyJson(data interface{}) error {
	body, err := json.Marshal(data)
	if err != nil {
		return err
	}
	r.SetBody(bytes.NewReader(body))
	r.Header.Set("Content-Type", "application/json")
	return nil
}

func (r *Request) SetBodyString(body string) {
	r.SetBody(strings.NewReader(body))
}

func (r *Request) SetBody(body io.Reader) {
	rc, ok := body.(io.ReadCloser)
	if !ok && body != nil {
		rc = ioutil.NopCloser(body)
	}
	r.Body = rc
	if body != nil {
		switch v := body.(type) {
		case *strings.Reader:
			r.ContentLength = int64(v.Len())
		case *bytes.Buffer:
			r.ContentLength = int64(v.Len())
		}
	}
}

func (r *Request) Do(v interface{}) (int, []byte, error) {
	res, err := http.DefaultClient.Do(r.Request)

	// Inform the HostPool of what happened to the request and allow it to update
	r.hostResponse.Mark(err)

	if err != nil {
		return 0, nil, err
	}

	defer res.Body.Close()
	bodyBytes, err := ioutil.ReadAll(res.Body)

	if res.StatusCode > 304 && v != nil {
		jsonErr := json.Unmarshal(bodyBytes, v)
		if jsonErr != nil {
			return res.StatusCode, nil, err
		}
	}
	return res.StatusCode, bodyBytes, nil
}

func SetHosts(newhosts []string) {

	// Store the new host list
	Hosts = newhosts

	// Reinitialise the host pool
	// Pretty naive as this will nuke the current hostpool, and therefore reset any scoring
	initialiseHostPool()

}

// Initialise the host pool to be used
func initialiseHostPool() {

	// If no hosts are set, fallback to defaults
	if len(Hosts) == 0 {
		Hosts = append(Hosts, fmt.Sprintf("%s:%s", Domain, Port))
	}

	// Epsilon Greedy is an algorithm that allows HostPool not only to track failure state,
	// but also to learn about "better" options in terms of speed, and to pick from available hosts
	// based on how well they perform. This gives a weighted request rate to better
	// performing hosts, while still distributing requests to all hosts (proportionate to their performance).
	// The interface is the same as the standard HostPool, but be sure to mark the HostResponse immediately
	// after executing the request to the host, as that will stop the implicitly running request timer.
	//
	// A good overview of Epsilon Greedy is here http://stevehanov.ca/blog/index.php?id=132

	hp = hostpool.NewEpsilonGreedy(Hosts, DecayDuration, &hostpool.LinearEpsilonValueCalculator{})

}

// Split apart the hostname on colon
// Return the host and a default port if there is no separator
func splitHostnamePartsFromHost(fullHost string, defaultPortNum string) (string, string) {

	h := strings.Split(fullHost, ":")

	if len(h) == 2 {
		return h[0], h[1]
	}

	return h[0], defaultPortNum
}
<commit_msg>If the hostpool already exists set the hosts rather than creating a new hostpool<commit_after>/**
   Copyright 2012 Matthew Baird

   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 api

import (
	"bytes"
	"encoding/json"
	"fmt"
	hostpool "github.com/hailocab/go-hostpool"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"runtime"
	"strings"
	"sync"
	"time"
)

type Request struct {
	*http.Request
	hostResponse hostpool.HostPoolResponse
}

const (
	Version         = "0.0.1"
	DefaultProtocol = "http"
	DefaultDomain   = "localhost"
	DefaultPort     = "9200"

	// A decay duration of zero results in the default behaviour
	DefaultDecayDuration = 0
)

var (
	_ = log.Ldate

	// Maintain these for backwards compatibility
	Protocol string = DefaultProtocol
	Domain   string = DefaultDomain
	Port     string = DefaultPort

	// Store a slice of hosts in a hostpool
	Hosts []string
	hp    hostpool.HostPool
	once  sync.Once

	// To compute the weighting scores, we perform a weighted average of recent response times,
	// over the course of `DecayDuration`. DecayDuration may be set to 0 to use the default
	// value of 5 minutes. The EpsilonValueCalculator uses this to calculate a score
	// from the weighted average response time.
	DecayDuration time.Duration = time.Duration(DefaultDecayDuration * time.Second)
)

type Error struct {
	Status  int    `json:"status"` // HTTP status code (200, 403, ...)
	Message string `json:"error"`  // The human readable error message
}

func (err *Error) Error() string {
	if err.Status == 0 {
		return err.Message
	}

	return fmt.Sprintf("Error [%s] Status [%v]", err.Message, err.Status)
}

// Build HTTP request

func ElasticSearchRequest(method, path string) (*Request, error) {

	// Initialise a hostpool on our first run
	once.Do(initialiseHostPool)

	// Get a host from the host pool
	hr := hp.Get()

	// Get the final host and port
	host, portNum := splitHostnamePartsFromHost(hr.Host(), Port)

	// Build request
	req, err := http.NewRequest(method, fmt.Sprintf("%s://%s:%s%s", Protocol, host, portNum, path), nil)
	if err != nil {
		return nil, err
	}
	req.Header.Add("Accept", "application/json")
	req.Header.Add("User-Agent", "elasticSearch/"+Version+" ("+runtime.GOOS+"-"+runtime.GOARCH+")")

	newRequest := &Request{
		Request:      req,
		hostResponse: hr,
	}

	return newRequest, nil
}

func (r *Request) SetBodyJson(data interface{}) error {
	body, err := json.Marshal(data)
	if err != nil {
		return err
	}
	r.SetBody(bytes.NewReader(body))
	r.Header.Set("Content-Type", "application/json")
	return nil
}

func (r *Request) SetBodyString(body string) {
	r.SetBody(strings.NewReader(body))
}

func (r *Request) SetBody(body io.Reader) {
	rc, ok := body.(io.ReadCloser)
	if !ok && body != nil {
		rc = ioutil.NopCloser(body)
	}
	r.Body = rc
	if body != nil {
		switch v := body.(type) {
		case *strings.Reader:
			r.ContentLength = int64(v.Len())
		case *bytes.Buffer:
			r.ContentLength = int64(v.Len())
		}
	}
}

func (r *Request) Do(v interface{}) (int, []byte, error) {
	res, err := http.DefaultClient.Do(r.Request)

	// Inform the HostPool of what happened to the request and allow it to update
	r.hostResponse.Mark(err)

	if err != nil {
		return 0, nil, err
	}

	defer res.Body.Close()
	bodyBytes, err := ioutil.ReadAll(res.Body)

	if res.StatusCode > 304 && v != nil {
		jsonErr := json.Unmarshal(bodyBytes, v)
		if jsonErr != nil {
			return res.StatusCode, nil, err
		}
	}
	return res.StatusCode, bodyBytes, nil
}

func SetHosts(newhosts []string) {

	// Store the new host list
	Hosts = newhosts

	// Reinitialise the host pool
	// Pretty naive as this will nuke the current hostpool, and therefore reset any scoring
	initialiseHostPool()

}

// Initialise the host pool to be used
func initialiseHostPool() {

	// If no hosts are set, fallback to defaults
	if len(Hosts) == 0 {
		Hosts = append(Hosts, fmt.Sprintf("%s:%s", Domain, Port))
	}

	// Epsilon Greedy is an algorithm that allows HostPool not only to track failure state,
	// but also to learn about "better" options in terms of speed, and to pick from available hosts
	// based on how well they perform. This gives a weighted request rate to better
	// performing hosts, while still distributing requests to all hosts (proportionate to their performance).
	// The interface is the same as the standard HostPool, but be sure to mark the HostResponse immediately
	// after executing the request to the host, as that will stop the implicitly running request timer.
	//
	// A good overview of Epsilon Greedy is here http://stevehanov.ca/blog/index.php?id=132

	// If the host pool is already initialised set the hosts.
	if hp != nil {
		hp.SetHosts(Hosts)
	} else {
		hp = hostpool.NewEpsilonGreedy(Hosts, DecayDuration, &hostpool.LinearEpsilonValueCalculator{})
	}

}

// Split apart the hostname on colon
// Return the host and a default port if there is no separator
func splitHostnamePartsFromHost(fullHost string, defaultPortNum string) (string, string) {

	h := strings.Split(fullHost, ":")

	if len(h) == 2 {
		return h[0], h[1]
	}

	return h[0], defaultPortNum
}
<|endoftext|>


<commit_before>package u

import (
	"bufio"
	"bytes"
	"crypto/sha1"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/dustin/go-humanize"
)

// PathExists returns true if a filesystem path exists
// Treats any error (e.g. lack of access due to permissions) as non-existence
func PathExists(path string) bool {
	_, err := os.Stat(path)
	return err == nil
}

// FileExists returns true if a given path exists and is a file
func FileExists(path string) bool {
	st, err := os.Stat(path)
	return err == nil && st.Mode().IsRegular()
}

// DirExists returns true if a given path exists and is a directory
func DirExists(path string) bool {
	st, err := os.Stat(path)
	return err == nil && st.IsDir()
}

// PathIsDir returns true if a path exists and is a directory
// Returns false, nil if a path exists and is not a directory (e.g. a file)
// Returns undefined, error if there was an error e.g. because a path doesn't exists
func PathIsDir(path string) (isDir bool, err error) {
	fi, err := os.Stat(path)
	if err != nil {
		return false, err
	}
	return fi.IsDir(), nil
}

// GetFileSize returns size of the file
func GetFileSize(path string) (int64, error) {
	fi, err := os.Lstat(path)
	if err != nil {
		return 0, err
	}
	return fi.Size(), nil
}

// CreateDirIfNotExists creates a directory if it doesn't exist
func CreateDirIfNotExists(dir string) error {
	return os.MkdirAll(dir, 0755)
}

// CreateDirIfNotExistsMust creates a directory. Panics on error
func CreateDirIfNotExistsMust(dir string) string {
	err := os.MkdirAll(dir, 0755)
	Must(err)
	return dir
}

// CreateDirMust creates a directory. Panics on error
func CreateDirMust(path string) {
	err := CreateDirIfNotExists(path)
	Must(err)
}

// CreateDirForFile creates intermediary directories for a file
func CreateDirForFile(path string) error {
	dir := filepath.Dir(path)
	return CreateDirIfNotExists(dir)
}

// CreateDirForFileMust is like CreateDirForFile. Panics on error.
func CreateDirForFileMust(path string) string {
	dir := filepath.Dir(path)
	err := CreateDirIfNotExists(dir)
	Must(err)
	return dir
}

// WriteBytesToFile is like ioutil.WriteFile() but also creates intermediary
// directories
func WriteBytesToFile(d []byte, path string) error {
	if err := CreateDirIfNotExists(filepath.Dir(path)); err != nil {
		return err
	}
	return ioutil.WriteFile(path, d, 0644)
}

func WriteFileMust(path string, data []byte) {
	err := ioutil.WriteFile(path, data, 0644)
	Must(err)
}

func ReadFileMust(path string) []byte {
	d, err := ioutil.ReadFile(path)
	Must(err)
	return d
}

// like io.Closer Close() but ignores an error so better to use as
// defer CloseNoError(f)
func CloseNoError(f io.Closer) {
	_ = f.Close()
}

// ListFilesInDir returns a list of files in a directory
func ListFilesInDir(dir string, recursive bool) []string {
	files := make([]string, 0)
	filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		isDir, err := PathIsDir(path)
		if err != nil {
			return err
		}
		if isDir {
			if recursive || path == dir {
				return nil
			}
			return filepath.SkipDir
		}
		files = append(files, path)
		return nil
	})
	return files
}

func RemoveFilesInDirMust(dir string) {
	if !DirExists(dir) {
		return
	}
	files, err := ioutil.ReadDir(dir)
	Must(err)
	for _, fi := range files {
		if !fi.Mode().IsRegular() {
			continue
		}
		path := filepath.Join(dir, fi.Name())
		err = os.Remove(path)
		Must(err)
	}
}

func RemoveFileLogged(path string) {
	err := os.Remove(path)
	if err == nil {
		Logf("removeFile('%s')", path)
		return
	}
	if os.IsNotExist(err) {
		// TODO: maybe should print note
		return
	}
	Logf("os.Remove('%s') failed with '%s'\n", path, err)
}

// CopyFile copies a file
func CopyFile(dst, src string) error {
	fsrc, err := os.Open(src)
	if err != nil {
		return err
	}
	defer fsrc.Close()
	fdst, err := os.Create(dst)
	if err != nil {
		return err
	}
	defer fdst.Close()
	if _, err = io.Copy(fdst, fsrc); err != nil {
		return err
	}
	return nil
}

func CopyFileMust(dst, src string) {
	Must(CopyFile(dst, src))
}

// ReadLinesFromReader reads all lines from io.Reader. Newlines are not included.
func ReadLinesFromReader(r io.Reader) ([]string, error) {
	res := make([]string, 0)
	scanner := bufio.NewScanner(r)
	for scanner.Scan() {
		res = append(res, scanner.Text())
	}

	if err := scanner.Err(); err != nil {
		return res, err
	}
	return res, nil
}

// ReadLinesFromFile reads all lines from a file. Newlines are not included.
func ReadLinesFromFile(path string) ([]string, error) {
	f, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	return ReadLinesFromReader(f)
}

// Sha1OfFile returns 20-byte sha1 of file content
func Sha1OfFile(path string) ([]byte, error) {
	f, err := os.Open(path)
	if err != nil {
		//fmt.Printf("os.Open(%s) failed with %s\n", path, err.Error())
		return nil, err
	}
	defer f.Close()
	h := sha1.New()
	_, err = io.Copy(h, f)
	if err != nil {
		//fmt.Printf("io.Copy() failed with %s\n", err.Error())
		return nil, err
	}
	return h.Sum(nil), nil
}

// Sha1HexOfFile returns 40-byte hex sha1 of file content
func Sha1HexOfFile(path string) (string, error) {
	sha1, err := Sha1OfFile(path)
	if err != nil {
		return "", err
	}
	return fmt.Sprintf("%x", sha1), nil
}

// PathMatchesExtensions returns true if path matches any of the extensions
func PathMatchesExtensions(path string, extensions []string) bool {
	if len(extensions) == 0 {
		return true
	}
	ext := strings.ToLower(filepath.Ext(path))
	for _, allowed := range extensions {
		if ext == allowed {
			return true
		}
	}
	return false
}

// DeleteFilesIf deletes a files in a given directory if shouldDelete callback
// returns true
func DeleteFilesIf(dir string, shouldDelete func(os.FileInfo) bool) error {
	files, err := ioutil.ReadDir(dir)
	if err != nil {
		return err
	}
	for _, fi := range files {
		if fi.IsDir() || !fi.Mode().IsRegular() {
			continue
		}
		if shouldDelete(fi) {
			path := filepath.Join(dir, fi.Name())
			err = os.Remove(path)
			// Maybe: keep deleting?
			if err != nil {
				return err
			}
		}
	}
	return nil
}

// absolute path of the current directory
func CurrDirAbsMust() string {
	dir, err := filepath.Abs(".")
	Must(err)
	return dir
}

// we are executed for do/ directory so top dir is parent dir
func CdUpDir(dirName string) {
	startDir := CurrDirAbsMust()
	dir := startDir
	for {
		// we're already in top directory
		if filepath.Base(dir) == dirName && DirExists(dir) {
			err := os.Chdir(dir)
			Must(err)
			return
		}
		parentDir := filepath.Dir(dir)
		PanicIf(dir == parentDir, "invalid startDir: '%s', dir: '%s'", startDir, dir)
		dir = parentDir
	}
}

func FmtSizeHuman(size int64) string {
	return humanize.Bytes(uint64(size))
}

func PrintFileSize(path string) {
	st, err := os.Stat(path)
	if err != nil {
		fmt.Printf("File '%s' doesn't exist\n", path)
		return
	}
	fmt.Printf("'%s': %s\n", path, FmtSizeHuman(st.Size()))
}

func AreFilesEuqalMust(path1, path2 string) bool {
	d1 := ReadFileMust(path1)
	d2 := ReadFileMust(path2)
	return bytes.Equal(d1, d2)
}

func FilesSameSize(path1, path2 string) bool {
	s1, err := GetFileSize(path1)
	if err != nil {
		return false
	}
	s2, err := GetFileSize(path2)
	if err != nil {
		return false
	}
	return s1 == s2
}

func CopyDirRecurMust(dstDir, srcDir string, shouldCopyFn func(path string) bool) {
	CreateDirMust(dstDir)
	fileInfos, err := ioutil.ReadDir(srcDir)
	Must(err)
	for _, fi := range fileInfos {
		name := fi.Name()
		if fi.IsDir() {
			dst := filepath.Join(dstDir, name)
			src := filepath.Join(srcDir, name)
			CopyDirRecurMust(dst, src, shouldCopyFn)
			continue
		}

		src := filepath.Join(srcDir, name)
		dst := filepath.Join(dstDir, name)
		shouldCopy := true
		if shouldCopyFn != nil {
			shouldCopy = shouldCopyFn(src)
		}
		if !shouldCopy {
			continue
		}
		if FilesSameSize(dst, src) {
			continue
		}
		CopyFileMust(dst, src)
	}
}
<commit_msg>rename CopyDirRecurMust() => DirCopyRecurMust() and return number of copied files<commit_after>package u

import (
	"bufio"
	"bytes"
	"crypto/sha1"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/dustin/go-humanize"
)

// PathExists returns true if a filesystem path exists
// Treats any error (e.g. lack of access due to permissions) as non-existence
func PathExists(path string) bool {
	_, err := os.Stat(path)
	return err == nil
}

// FileExists returns true if a given path exists and is a file
func FileExists(path string) bool {
	st, err := os.Stat(path)
	return err == nil && st.Mode().IsRegular()
}

// DirExists returns true if a given path exists and is a directory
func DirExists(path string) bool {
	st, err := os.Stat(path)
	return err == nil && st.IsDir()
}

// PathIsDir returns true if a path exists and is a directory
// Returns false, nil if a path exists and is not a directory (e.g. a file)
// Returns undefined, error if there was an error e.g. because a path doesn't exists
func PathIsDir(path string) (isDir bool, err error) {
	fi, err := os.Stat(path)
	if err != nil {
		return false, err
	}
	return fi.IsDir(), nil
}

// GetFileSize returns size of the file
func GetFileSize(path string) (int64, error) {
	fi, err := os.Lstat(path)
	if err != nil {
		return 0, err
	}
	return fi.Size(), nil
}

// CreateDirIfNotExists creates a directory if it doesn't exist
func CreateDirIfNotExists(dir string) error {
	return os.MkdirAll(dir, 0755)
}

// CreateDirIfNotExistsMust creates a directory. Panics on error
func CreateDirIfNotExistsMust(dir string) string {
	err := os.MkdirAll(dir, 0755)
	Must(err)
	return dir
}

// CreateDirMust creates a directory. Panics on error
func CreateDirMust(path string) {
	err := CreateDirIfNotExists(path)
	Must(err)
}

// CreateDirForFile creates intermediary directories for a file
func CreateDirForFile(path string) error {
	dir := filepath.Dir(path)
	return CreateDirIfNotExists(dir)
}

// CreateDirForFileMust is like CreateDirForFile. Panics on error.
func CreateDirForFileMust(path string) string {
	dir := filepath.Dir(path)
	err := CreateDirIfNotExists(dir)
	Must(err)
	return dir
}

// WriteBytesToFile is like ioutil.WriteFile() but also creates intermediary
// directories
func WriteBytesToFile(d []byte, path string) error {
	if err := CreateDirIfNotExists(filepath.Dir(path)); err != nil {
		return err
	}
	return ioutil.WriteFile(path, d, 0644)
}

func WriteFileMust(path string, data []byte) {
	err := ioutil.WriteFile(path, data, 0644)
	Must(err)
}

func ReadFileMust(path string) []byte {
	d, err := ioutil.ReadFile(path)
	Must(err)
	return d
}

// like io.Closer Close() but ignores an error so better to use as
// defer CloseNoError(f)
func CloseNoError(f io.Closer) {
	_ = f.Close()
}

// ListFilesInDir returns a list of files in a directory
func ListFilesInDir(dir string, recursive bool) []string {
	files := make([]string, 0)
	filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		isDir, err := PathIsDir(path)
		if err != nil {
			return err
		}
		if isDir {
			if recursive || path == dir {
				return nil
			}
			return filepath.SkipDir
		}
		files = append(files, path)
		return nil
	})
	return files
}

func RemoveFilesInDirMust(dir string) {
	if !DirExists(dir) {
		return
	}
	files, err := ioutil.ReadDir(dir)
	Must(err)
	for _, fi := range files {
		if !fi.Mode().IsRegular() {
			continue
		}
		path := filepath.Join(dir, fi.Name())
		err = os.Remove(path)
		Must(err)
	}
}

func RemoveFileLogged(path string) {
	err := os.Remove(path)
	if err == nil {
		Logf("removeFile('%s')", path)
		return
	}
	if os.IsNotExist(err) {
		// TODO: maybe should print note
		return
	}
	Logf("os.Remove('%s') failed with '%s'\n", path, err)
}

// CopyFile copies a file
func CopyFile(dst, src string) error {
	fsrc, err := os.Open(src)
	if err != nil {
		return err
	}
	defer fsrc.Close()
	fdst, err := os.Create(dst)
	if err != nil {
		return err
	}
	defer fdst.Close()
	if _, err = io.Copy(fdst, fsrc); err != nil {
		return err
	}
	return nil
}

func CopyFileMust(dst, src string) {
	Must(CopyFile(dst, src))
}

// ReadLinesFromReader reads all lines from io.Reader. Newlines are not included.
func ReadLinesFromReader(r io.Reader) ([]string, error) {
	res := make([]string, 0)
	scanner := bufio.NewScanner(r)
	for scanner.Scan() {
		res = append(res, scanner.Text())
	}

	if err := scanner.Err(); err != nil {
		return res, err
	}
	return res, nil
}

// ReadLinesFromFile reads all lines from a file. Newlines are not included.
func ReadLinesFromFile(path string) ([]string, error) {
	f, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	return ReadLinesFromReader(f)
}

// Sha1OfFile returns 20-byte sha1 of file content
func Sha1OfFile(path string) ([]byte, error) {
	f, err := os.Open(path)
	if err != nil {
		//fmt.Printf("os.Open(%s) failed with %s\n", path, err.Error())
		return nil, err
	}
	defer f.Close()
	h := sha1.New()
	_, err = io.Copy(h, f)
	if err != nil {
		//fmt.Printf("io.Copy() failed with %s\n", err.Error())
		return nil, err
	}
	return h.Sum(nil), nil
}

// Sha1HexOfFile returns 40-byte hex sha1 of file content
func Sha1HexOfFile(path string) (string, error) {
	sha1, err := Sha1OfFile(path)
	if err != nil {
		return "", err
	}
	return fmt.Sprintf("%x", sha1), nil
}

// PathMatchesExtensions returns true if path matches any of the extensions
func PathMatchesExtensions(path string, extensions []string) bool {
	if len(extensions) == 0 {
		return true
	}
	ext := strings.ToLower(filepath.Ext(path))
	for _, allowed := range extensions {
		if ext == allowed {
			return true
		}
	}
	return false
}

// DeleteFilesIf deletes a files in a given directory if shouldDelete callback
// returns true
func DeleteFilesIf(dir string, shouldDelete func(os.FileInfo) bool) error {
	files, err := ioutil.ReadDir(dir)
	if err != nil {
		return err
	}
	for _, fi := range files {
		if fi.IsDir() || !fi.Mode().IsRegular() {
			continue
		}
		if shouldDelete(fi) {
			path := filepath.Join(dir, fi.Name())
			err = os.Remove(path)
			// Maybe: keep deleting?
			if err != nil {
				return err
			}
		}
	}
	return nil
}

// absolute path of the current directory
func CurrDirAbsMust() string {
	dir, err := filepath.Abs(".")
	Must(err)
	return dir
}

// we are executed for do/ directory so top dir is parent dir
func CdUpDir(dirName string) {
	startDir := CurrDirAbsMust()
	dir := startDir
	for {
		// we're already in top directory
		if filepath.Base(dir) == dirName && DirExists(dir) {
			err := os.Chdir(dir)
			Must(err)
			return
		}
		parentDir := filepath.Dir(dir)
		PanicIf(dir == parentDir, "invalid startDir: '%s', dir: '%s'", startDir, dir)
		dir = parentDir
	}
}

func FmtSizeHuman(size int64) string {
	return humanize.Bytes(uint64(size))
}

func PrintFileSize(path string) {
	st, err := os.Stat(path)
	if err != nil {
		fmt.Printf("File '%s' doesn't exist\n", path)
		return
	}
	fmt.Printf("'%s': %s\n", path, FmtSizeHuman(st.Size()))
}

func AreFilesEuqalMust(path1, path2 string) bool {
	d1 := ReadFileMust(path1)
	d2 := ReadFileMust(path2)
	return bytes.Equal(d1, d2)
}

func FilesSameSize(path1, path2 string) bool {
	s1, err := GetFileSize(path1)
	if err != nil {
		return false
	}
	s2, err := GetFileSize(path2)
	if err != nil {
		return false
	}
	return s1 == s2
}

func DirCopyRecurMust(dstDir, srcDir string, shouldCopyFn func(path string) bool) int {
	n, err := DirCopyRecur(dstDir, srcDir, shouldCopyFn)
	Must(err)
	return n
}

func DirCopyRecur(dstDir, srcDir string, shouldCopyFn func(path string) bool) (int, error) {
	err := CreateDirIfNotExists(dstDir)
	if err != nil {
		return 0, err
	}
	fileInfos, err := ioutil.ReadDir(srcDir)
	if err != nil {
		return 0, err
	}
	nCopied := 0
	for _, fi := range fileInfos {
		name := fi.Name()
		if fi.IsDir() {
			dst := filepath.Join(dstDir, name)
			src := filepath.Join(srcDir, name)
			n, err := DirCopyRecur(dst, src, shouldCopyFn)
			if err != nil {
				return 0, err
			}
			nCopied += n
			continue
		}

		src := filepath.Join(srcDir, name)
		dst := filepath.Join(dstDir, name)
		shouldCopy := true
		if shouldCopyFn != nil {
			shouldCopy = shouldCopyFn(src)
		}
		if !shouldCopy {
			continue
		}
		if FilesSameSize(dst, src) {
			continue
		}
		CopyFileMust(dst, src)
	}
	return nCopied, nil
}
<|endoftext|>