<commit_before>package hpcloud

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"strings"
)

/* Server flavours Smallest to Largest */
type Flavor int

const (
	XSmall = Flavor(100) + iota
	Small
	Medium
	Large
	XLarge
	DblXLarge
)

/* Available images */
type ServerImage int

var (
	UbuntuLucid10_04Kernel    = ServerImage(1235)
	UbuntuLucid10_04          = ServerImage(1236)
	UbuntuMaverick10_10Kernel = ServerImage(1237)
	UbuntuMaverick10_10       = ServerImage(1238)
	UbuntuNatty11_04Kernel    = ServerImage(1239)
	UbuntuNatty11_04          = ServerImage(1240)
	UbuntuOneiric11_10        = ServerImage(5579)
	UbuntuPrecise12_04        = ServerImage(8419)
	CentOS5_8Server64         = ServerImage(54021)
	CentOS6_2Server64Kernel   = ServerImage(1356)
	CentOS6_2Server64Ramdisk  = ServerImage(1357)
	CentOS6_2Server64         = ServerImage(1358)
	DebianSqueeze6_0_3Kernel  = ServerImage(1359)
	DebianSqueeze6_0_3Ramdisk = ServerImage(1360)
	DebianSqueeze6_0_3Server  = ServerImage(1361)
	Fedora16Server64          = ServerImage(16291)
	BitNamiDrupal7_14_0       = ServerImage(22729)
	BitNamiWebPack1_2_0       = ServerImage(22731)
	BitNamiDevPack1_0_0       = ServerImage(4654)
	ActiveStateStackatov1_2_6 = ServerImage(14345)
	ActiveStateStackatov2_2_2 = ServerImage(59297)
	ActiveStateStackatov2_2_3 = ServerImage(60815)
	EnterpriseDBPPAS9_1_2     = ServerImage(9953)
	EnterpriseDBPSQL9_1_3     = ServerImage(9995)
)

type Link struct {
	HREF string `json:"href"`
	Rel  string `json:"rel"`
}

/*
  Several embedded types are simply an ID string with a slice of Link
*/
type IDLink struct {
	Name  string `json:"name"`
	ID    string `json:"id"`
	Links []Link `json:"links"`
}

type Image struct {
	I struct {
		Name     string            `json:"name"`
		ID       string            `json:"id"`
		Links    []Link            `json:"links"`
		Progress int               `json:"progress"`
		Metadata map[string]string `json:"metadata"`
		Status   string            `json:"status"`
		Updated  string            `json:"updated"`
	} `json:"image"`
}

type Images struct {
	I []IDLink `json:"images"`
}

/*
  This type describes the JSON data which should be sent to the create
  server resource.
*/
type Server struct {
	ConfigDrive    bool              `json:"config_drive"`
	FlavorRef      Flavor            `json:"flavorRef"`
	ImageRef       ServerImage       `json:"imageRef"`
	MaxCount       int               `json:"max_count"`
	MinCount       int               `json:"min_count"`
	Name           string            `json:"name"`
	Key            string            `json:"key_name"`
	Personality    string            `json:"personality"`
	UserData       string            `json:"user_data"`
	SecurityGroups []IDLink          `json:"security_groups"`
	Metadata       map[string]string `json:"metadata"`
}

type createImageRequest struct {
	C imageRequest `json:"createImage"`
}

type imageRequest struct {
	Metadata *map[string]string `json:"metadata"`
	Name     string             `json:"image"`
}

func (c createImageRequest) MarshalJSON() ([]byte, error) {
	output_buffer := bytes.NewBufferString(`{"createImage":{`)
	output_buffer.WriteString(fmt.Sprintf(`"name": "%s"`, c.C.Name))
	metadata_buffer := &bytes.Buffer{}
	if c.C.Metadata != nil {
		if len(*c.C.Metadata) > 0 {
			metadata_buffer = bytes.NewBufferString("metadata:{")
			cnt := 0
			for k, v := range *c.C.Metadata {
				if len(k) > 255 {
					return nil, errors.New(fmt.Sprintf("Key: %s has a length >255", k))
				}
				if len(v) > 255 {
					return nil, errors.New(fmt.Sprintf("Value: %s has a length >255", v))
				}
				metadata_buffer.WriteString(
					fmt.Sprintf(`"%s":"%s"`, k, v),
				)
				if cnt+1 != len(*c.C.Metadata) {
					metadata_buffer.WriteString(",")
					cnt++
				} else {
					metadata_buffer.WriteString("}")
				}
			}
		}
		output_buffer.WriteString(",")
		output_buffer.WriteString(metadata_buffer.String())

	}
	output_buffer.WriteString("}}")
	return output_buffer.Bytes(), nil
}

/*
  This type describes the JSON response from a successful CreateServer
  call.
*/
type ServerResponse struct {
	S struct {
		Status         string            `json:"status"`
		Updated        string            `json:"update"`
		HostID         string            `json:"hostId"`
		UserID         string            `json:"user_id"`
		Name           string            `json:"name"`
		Links          []Link            `json:"links"`
		Addresses      interface{}       `json:"addresses"`
		TenantID       string            `json:"tenant_id"`
		Image          IDLink            `json:"image"`
		Created        string            `json:"created"`
		UUID           string            `json:"uuid"`
		AccessIPv4     string            `json:"accessIPv4"`
		AccessIPv6     string            `json:"accessIPv6"`
		KeyName        string            `json:"key_name"`
		AdminPass      string            `json:"adminPass"`
		Flavor         IDLink            `json:"flavor"`
		ConfigDrive    string            `json:"config_drive"`
		ID             int64             `json:"id"`
		SecurityGroups []IDLink          `json:"security_groups"`
		Metadata       map[string]string `json:"metadata"`
	} `json:"server"`
}

/*
  CreateServer creates a new server in the HPCloud using the
  settings found in the Server instance passed to this function.

  This function implements the interface as described in:-
  * https://docs.hpcloud.com/api/compute/
  * section 4.4.5.2 Create Server
*/
func (a Access) CreateServer(s Server) (*ServerResponse, error) {
	b, err := s.MarshalJSON()
	if err != nil {
		return nil, err
	}

	body, err := a.baseComputeRequest("servers", "POST",
		strings.NewReader(string(b)),
	)
	if err != nil {
		return nil, err
	}
	sr := &ServerResponse{}
	err = json.Unmarshal(body, sr)
	if err != nil {
		return nil, err
	}
	return sr, nil
}

/*
  DeleteServer deletes the server with the `server_id`.

  This function implements the interface described in:-
  * https://docs.hpcloud.com/api/compute/
  * Section 4.4.6.3 Delete Server
*/
func (a Access) DeleteServer(server_id string) error {
	_, err := a.baseComputeRequest(
		fmt.Sprintf("servers/%s", server_id),
		"DELETE", nil,
	)
	if err != nil {
		return err
	}
	return nil
}

/*
  RebootServer will reboot the server with the `server_id`.

  This function implements the interface described in:-
  * https://docs.hpcloud.com/api/compute/
  * Section 4.4.7.1 Reboot Server
*/
func (a Access) RebootServer(server_id string) error {
	/*
			 The docs mention that a hard reboot will be used
		     no matter what, so there's no point making a type
		     or make the type of reboot an option
	*/
	s := `{"reboot":{"type":"HARD"}}`
	_, err := a.baseComputeRequest(
		fmt.Sprintf("servers/%s/action", server_id),
		"POST", strings.NewReader(s),
	)
	if err != nil {
		return err
	}
	return nil

}

/*
  ListFlavors will list all the available flavours
  on the HPCloud compute API.
*/
func (a Access) ListFlavors() (*Flavors, error) {
	body, err := a.baseComputeRequest("flavors", "GET", nil)
	if err != nil {
		return nil, err
	}

	fl := &Flavors{}
	err = json.Unmarshal(body, fl)
	if err != nil {
		return nil, err
	}
	return fl, nil
}
func (a Access) CreateImage(server_id string, metadata *map[string]string) error {
	cir := &createImageRequest{C: imageRequest{Name: server_id, Metadata: metadata}}
	jsonbody, err := cir.MarshalJSON()
	if err != nil {
		return err
	}
	_, err = a.baseComputeRequest(
		fmt.Sprintf("servers/%s/action", server_id),
		"POST",
		bytes.NewReader(jsonbody),
	)
	if err != nil {
		return err
	}
	return nil
}

func (a Access) ListImages() (*Images, error) {
	body, err := a.baseComputeRequest("images", "GET", nil)
	if err != nil {
		return nil, err
	}
	im := &Images{}
	err = json.Unmarshal(body, im)
	if err != nil {
		return nil, err
	}
	return im, nil
}

func (a Access) DeleteImage(image_id string) error {
	_, err := a.baseComputeRequest(
		fmt.Sprintf("images/%s", image_id), "DELETE", nil,
	)
	if err != nil {
		return err
	}
	return nil
}

func (a Access) ListImage(image_id string) (*Image, error) {
	body, err := a.baseComputeRequest(
		fmt.Sprintf("images/%s", image_id), "GET", nil,
	)
	if err != nil {
		return nil, err
	}
	i := &Image{}
	err = json.Unmarshal(body, i)
	if err != nil {
		return nil, err
	}
	return i, nil
}

/*
  baseComputeRequest encapsulates the main basic request
  which is done for each endpoint in the Compute API.

  In the ComputeAPI all endpoints generally succeed on
  a 200/202 return code and fail on the usual fail codes.

  We simply check for the known good return codes and return
  the body in those cases or we fail with the appropriate
  response.
*/
func (a Access) baseComputeRequest(url, method string, b io.Reader) ([]byte, error) {
	path := fmt.Sprintf("%s%s/%s", COMPUTE_URL, a.TenantID, url)
	return a.baseRequest(path, method, b)
}

/*
  MarshalJSON implements the Marshaler interface for the
  Server type.

  We implement this interface because when creating a server
  we have optional values and since Go has zero-values and
  does *not* have configurable zero values we need to make
  sure that zero-values are converted to known good values.

  As such:
    * FlavorRef is checked if it's a valid reference.
    * Ditto for ImageRef.
    * Name cannot be blank.
    * If the key is missing, it'll not put anything in.
    * The config_drive defaults to false anyway, no need
      to send a false value.
    * Min/MaxCount are ignored if they are zero.
    * UserData is ignored if it's a blank string.
    * Personality is ignored if it's a blank string.
    * Metadata/SecurityGroups are ignored if they have len(0)
*/
func (s Server) MarshalJSON() ([]byte, error) {
	b := bytes.NewBufferString("")
	b.WriteString(`{"server":{`)
	/* The available images are 100-105, x-small to x-large. */
	if s.FlavorRef < 100 || s.FlavorRef > 105 {
		return []byte{},
			errors.New("Flavor Reference refers to a non-existant flavour.")
	} else {
		b.WriteString(fmt.Sprintf(`"flavorRef":%d`, s.FlavorRef))
	}
	if s.ImageRef == 0 {
		return []byte{},
			errors.New("An image name is required.")
	} else {
		b.WriteString(fmt.Sprintf(`,"imageRef":%d`, s.ImageRef))
	}
	if s.Name == "" {
		return []byte{},
			errors.New("A name is required")
	} else {
		b.WriteString(fmt.Sprintf(`,"name":"%s"`, s.Name))
	}

	/* Optional items */
	/* The max size of a personality string is 255 bytes. */
	if len(s.Personality) > 255 {
		return []byte{},
			errors.New("Server's personality cannot have >255 bytes.")
	} else if s.Personality != "" {
		b.WriteString(fmt.Sprintf(`,"personality":"%s",`, s.Personality))
	}
	if s.Key != "" {
		b.WriteString(fmt.Sprintf(`,"key_name":"%s"`, s.Key))
	}
	if s.ConfigDrive {
		b.WriteString(`,"config_drive": true`)
	}
	if s.MinCount > 0 {
		b.WriteString(fmt.Sprintf(`,"min_count":%d`, s.MinCount))
	}
	if s.MaxCount > 0 {
		b.WriteString(fmt.Sprintf(`,"max_count":%d`, s.MaxCount))
	}
	if s.UserData != "" {
		/* user_data needs to be base64'd */
		newb := make([]byte, 0, len(s.UserData))
		base64.StdEncoding.Encode([]byte(s.UserData), newb)
		b.WriteString(fmt.Sprintf(`,"user_data": "%s",`, string(newb)))
	}

	/* Ignore the metadata if there isn't any, it's optional. */
	if len(s.Metadata) > 0 {
		fmt.Println(len(s.Metadata))
		b.WriteString(`,"metadata":{`)
		cnt := 0
		for key, value := range s.Metadata {
			b.WriteString(fmt.Sprintf(`"%s": "%s"`, key, value))
			if cnt+1 != len(s.Metadata) {
				b.WriteString(",")
				cnt++
			} else {
				b.WriteString("}")
			}
		}
	}
	/* Ignore the Security Groups if there isn't any, it's optional. */
	if len(s.SecurityGroups) > 0 {
		b.WriteString(`,"security_groups":[`)
		cnt := 0
		for _, sg := range s.SecurityGroups {
			b.WriteString(fmt.Sprintf(`{"name": "%s"}`, sg.Name))
			if cnt+1 != len(s.SecurityGroups) {
				b.WriteString(",")
				cnt++
			} else {
				b.WriteString("]")
			}
		}
	}
	b.WriteString("}}")
	return b.Bytes(), nil
}
<commit_msg>Whitespace.<commit_after>package hpcloud

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"strings"
)

/* Server flavours Smallest to Largest */
type Flavor int

const (
	XSmall = Flavor(100) + iota
	Small
	Medium
	Large
	XLarge
	DblXLarge
)

/* Available images */
type ServerImage int

var (
	UbuntuLucid10_04Kernel    = ServerImage(1235)
	UbuntuLucid10_04          = ServerImage(1236)
	UbuntuMaverick10_10Kernel = ServerImage(1237)
	UbuntuMaverick10_10       = ServerImage(1238)
	UbuntuNatty11_04Kernel    = ServerImage(1239)
	UbuntuNatty11_04          = ServerImage(1240)
	UbuntuOneiric11_10        = ServerImage(5579)
	UbuntuPrecise12_04        = ServerImage(8419)
	CentOS5_8Server64         = ServerImage(54021)
	CentOS6_2Server64Kernel   = ServerImage(1356)
	CentOS6_2Server64Ramdisk  = ServerImage(1357)
	CentOS6_2Server64         = ServerImage(1358)
	DebianSqueeze6_0_3Kernel  = ServerImage(1359)
	DebianSqueeze6_0_3Ramdisk = ServerImage(1360)
	DebianSqueeze6_0_3Server  = ServerImage(1361)
	Fedora16Server64          = ServerImage(16291)
	BitNamiDrupal7_14_0       = ServerImage(22729)
	BitNamiWebPack1_2_0       = ServerImage(22731)
	BitNamiDevPack1_0_0       = ServerImage(4654)
	ActiveStateStackatov1_2_6 = ServerImage(14345)
	ActiveStateStackatov2_2_2 = ServerImage(59297)
	ActiveStateStackatov2_2_3 = ServerImage(60815)
	EnterpriseDBPPAS9_1_2     = ServerImage(9953)
	EnterpriseDBPSQL9_1_3     = ServerImage(9995)
)

type Link struct {
	HREF string `json:"href"`
	Rel  string `json:"rel"`
}

/*
  Several embedded types are simply an ID string with a slice of Link
*/
type IDLink struct {
	Name  string `json:"name"`
	ID    string `json:"id"`
	Links []Link `json:"links"`
}

type Image struct {
	I struct {
		Name     string            `json:"name"`
		ID       string            `json:"id"`
		Links    []Link            `json:"links"`
		Progress int               `json:"progress"`
		Metadata map[string]string `json:"metadata"`
		Status   string            `json:"status"`
		Updated  string            `json:"updated"`
	} `json:"image"`
}

type Images struct {
	I []IDLink `json:"images"`
}

/*
  This type describes the JSON data which should be sent to the create
  server resource.
*/
type Server struct {
	ConfigDrive    bool              `json:"config_drive"`
	FlavorRef      Flavor            `json:"flavorRef"`
	ImageRef       ServerImage       `json:"imageRef"`
	MaxCount       int               `json:"max_count"`
	MinCount       int               `json:"min_count"`
	Name           string            `json:"name"`
	Key            string            `json:"key_name"`
	Personality    string            `json:"personality"`
	UserData       string            `json:"user_data"`
	SecurityGroups []IDLink          `json:"security_groups"`
	Metadata       map[string]string `json:"metadata"`
}

type createImageRequest struct {
	C imageRequest `json:"createImage"`
}

type imageRequest struct {
	Metadata *map[string]string `json:"metadata"`
	Name     string             `json:"image"`
}

func (c createImageRequest) MarshalJSON() ([]byte, error) {
	output_buffer := bytes.NewBufferString(`{"createImage":{`)
	output_buffer.WriteString(fmt.Sprintf(`"name": "%s"`, c.C.Name))
	metadata_buffer := &bytes.Buffer{}
	if c.C.Metadata != nil {
		if len(*c.C.Metadata) > 0 {
			metadata_buffer = bytes.NewBufferString("metadata:{")
			cnt := 0
			for k, v := range *c.C.Metadata {
				if len(k) > 255 {
					return nil, errors.New(fmt.Sprintf("Key: %s has a length >255", k))
				}
				if len(v) > 255 {
					return nil, errors.New(fmt.Sprintf("Value: %s has a length >255", v))
				}
				metadata_buffer.WriteString(
					fmt.Sprintf(`"%s":"%s"`, k, v),
				)
				if cnt+1 != len(*c.C.Metadata) {
					metadata_buffer.WriteString(",")
					cnt++
				} else {
					metadata_buffer.WriteString("}")
				}
			}
		}
		output_buffer.WriteString(",")
		output_buffer.WriteString(metadata_buffer.String())

	}
	output_buffer.WriteString("}}")
	return output_buffer.Bytes(), nil
}

/*
  This type describes the JSON response from a successful CreateServer
  call.
*/
type ServerResponse struct {
	S struct {
		Status         string            `json:"status"`
		Updated        string            `json:"update"`
		HostID         string            `json:"hostId"`
		UserID         string            `json:"user_id"`
		Name           string            `json:"name"`
		Links          []Link            `json:"links"`
		Addresses      interface{}       `json:"addresses"`
		TenantID       string            `json:"tenant_id"`
		Image          IDLink            `json:"image"`
		Created        string            `json:"created"`
		UUID           string            `json:"uuid"`
		AccessIPv4     string            `json:"accessIPv4"`
		AccessIPv6     string            `json:"accessIPv6"`
		KeyName        string            `json:"key_name"`
		AdminPass      string            `json:"adminPass"`
		Flavor         IDLink            `json:"flavor"`
		ConfigDrive    string            `json:"config_drive"`
		ID             int64             `json:"id"`
		SecurityGroups []IDLink          `json:"security_groups"`
		Metadata       map[string]string `json:"metadata"`
	} `json:"server"`
}

/*
  CreateServer creates a new server in the HPCloud using the
  settings found in the Server instance passed to this function.

  This function implements the interface as described in:-
  * https://docs.hpcloud.com/api/compute/
  * section 4.4.5.2 Create Server
*/
func (a Access) CreateServer(s Server) (*ServerResponse, error) {
	b, err := s.MarshalJSON()
	if err != nil {
		return nil, err
	}

	body, err := a.baseComputeRequest("servers", "POST",
		strings.NewReader(string(b)),
	)
	if err != nil {
		return nil, err
	}
	sr := &ServerResponse{}
	err = json.Unmarshal(body, sr)
	if err != nil {
		return nil, err
	}
	return sr, nil
}

/*
  DeleteServer deletes the server with the `server_id`.

  This function implements the interface described in:-
  * https://docs.hpcloud.com/api/compute/
  * Section 4.4.6.3 Delete Server
*/
func (a Access) DeleteServer(server_id string) error {
	_, err := a.baseComputeRequest(
		fmt.Sprintf("servers/%s", server_id),
		"DELETE", nil,
	)
	if err != nil {
		return err
	}
	return nil
}

/*
  RebootServer will reboot the server with the `server_id`.

  This function implements the interface described in:-
  * https://docs.hpcloud.com/api/compute/
  * Section 4.4.7.1 Reboot Server
*/
func (a Access) RebootServer(server_id string) error {
	/*
			 The docs mention that a hard reboot will be used
		     no matter what, so there's no point making a type
		     or make the type of reboot an option
	*/
	s := `{"reboot":{"type":"HARD"}}`
	_, err := a.baseComputeRequest(
		fmt.Sprintf("servers/%s/action", server_id),
		"POST", strings.NewReader(s),
	)
	if err != nil {
		return err
	}
	return nil

}

/*
  ListFlavors will list all the available flavours
  on the HPCloud compute API.
*/
func (a Access) ListFlavors() (*Flavors, error) {
	body, err := a.baseComputeRequest("flavors", "GET", nil)
	if err != nil {
		return nil, err
	}
	fl := &Flavors{}
	err = json.Unmarshal(body, fl)
	if err != nil {
		return nil, err
	}
	return fl, nil
}
func (a Access) CreateImage(server_id string, metadata *map[string]string) error {
	cir := &createImageRequest{C: imageRequest{Name: server_id, Metadata: metadata}}
	jsonbody, err := cir.MarshalJSON()
	if err != nil {
		return err
	}
	_, err = a.baseComputeRequest(
		fmt.Sprintf("servers/%s/action", server_id),
		"POST",
		bytes.NewReader(jsonbody),
	)
	if err != nil {
		return err
	}
	return nil
}

func (a Access) ListImages() (*Images, error) {
	body, err := a.baseComputeRequest("images", "GET", nil)
	if err != nil {
		return nil, err
	}
	im := &Images{}
	err = json.Unmarshal(body, im)
	if err != nil {
		return nil, err
	}
	return im, nil
}

func (a Access) DeleteImage(image_id string) error {
	_, err := a.baseComputeRequest(
		fmt.Sprintf("images/%s", image_id), "DELETE", nil,
	)
	if err != nil {
		return err
	}
	return nil
}

func (a Access) ListImage(image_id string) (*Image, error) {
	body, err := a.baseComputeRequest(
		fmt.Sprintf("images/%s", image_id), "GET", nil,
	)
	if err != nil {
		return nil, err
	}
	i := &Image{}
	err = json.Unmarshal(body, i)
	if err != nil {
		return nil, err
	}
	return i, nil
}

/*
  baseComputeRequest encapsulates the main basic request
  which is done for each endpoint in the Compute API.

  In the ComputeAPI all endpoints generally succeed on
  a 200/202 return code and fail on the usual fail codes.

  We simply check for the known good return codes and return
  the body in those cases or we fail with the appropriate
  response.
*/
func (a Access) baseComputeRequest(url, method string, b io.Reader) ([]byte, error) {
	path := fmt.Sprintf("%s%s/%s", COMPUTE_URL, a.TenantID, url)
	return a.baseRequest(path, method, b)
}

/*
  MarshalJSON implements the Marshaler interface for the
  Server type.

  We implement this interface because when creating a server
  we have optional values and since Go has zero-values and
  does *not* have configurable zero values we need to make
  sure that zero-values are converted to known good values.

  As such:
    * FlavorRef is checked if it's a valid reference.
    * Ditto for ImageRef.
    * Name cannot be blank.
    * If the key is missing, it'll not put anything in.
    * The config_drive defaults to false anyway, no need
      to send a false value.
    * Min/MaxCount are ignored if they are zero.
    * UserData is ignored if it's a blank string.
    * Personality is ignored if it's a blank string.
    * Metadata/SecurityGroups are ignored if they have len(0)
*/
func (s Server) MarshalJSON() ([]byte, error) {
	b := bytes.NewBufferString("")
	b.WriteString(`{"server":{`)
	/* The available images are 100-105, x-small to x-large. */
	if s.FlavorRef < 100 || s.FlavorRef > 105 {
		return []byte{},
			errors.New("Flavor Reference refers to a non-existant flavour.")
	} else {
		b.WriteString(fmt.Sprintf(`"flavorRef":%d`, s.FlavorRef))
	}
	if s.ImageRef == 0 {
		return []byte{},
			errors.New("An image name is required.")
	} else {
		b.WriteString(fmt.Sprintf(`,"imageRef":%d`, s.ImageRef))
	}
	if s.Name == "" {
		return []byte{},
			errors.New("A name is required")
	} else {
		b.WriteString(fmt.Sprintf(`,"name":"%s"`, s.Name))
	}

	/* Optional items */
	/* The max size of a personality string is 255 bytes. */
	if len(s.Personality) > 255 {
		return []byte{},
			errors.New("Server's personality cannot have >255 bytes.")
	} else if s.Personality != "" {
		b.WriteString(fmt.Sprintf(`,"personality":"%s",`, s.Personality))
	}
	if s.Key != "" {
		b.WriteString(fmt.Sprintf(`,"key_name":"%s"`, s.Key))
	}
	if s.ConfigDrive {
		b.WriteString(`,"config_drive": true`)
	}
	if s.MinCount > 0 {
		b.WriteString(fmt.Sprintf(`,"min_count":%d`, s.MinCount))
	}
	if s.MaxCount > 0 {
		b.WriteString(fmt.Sprintf(`,"max_count":%d`, s.MaxCount))
	}
	if s.UserData != "" {
		/* user_data needs to be base64'd */
		newb := make([]byte, 0, len(s.UserData))
		base64.StdEncoding.Encode([]byte(s.UserData), newb)
		b.WriteString(fmt.Sprintf(`,"user_data": "%s",`, string(newb)))
	}

	/* Ignore the metadata if there isn't any, it's optional. */
	if len(s.Metadata) > 0 {
		fmt.Println(len(s.Metadata))
		b.WriteString(`,"metadata":{`)
		cnt := 0
		for key, value := range s.Metadata {
			b.WriteString(fmt.Sprintf(`"%s": "%s"`, key, value))
			if cnt+1 != len(s.Metadata) {
				b.WriteString(",")
				cnt++
			} else {
				b.WriteString("}")
			}
		}
	}
	/* Ignore the Security Groups if there isn't any, it's optional. */
	if len(s.SecurityGroups) > 0 {
		b.WriteString(`,"security_groups":[`)
		cnt := 0
		for _, sg := range s.SecurityGroups {
			b.WriteString(fmt.Sprintf(`{"name": "%s"}`, sg.Name))
			if cnt+1 != len(s.SecurityGroups) {
				b.WriteString(",")
				cnt++
			} else {
				b.WriteString("]")
			}
		}
	}
	b.WriteString("}}")
	return b.Bytes(), nil
}
<|endoftext|>


<commit_before>package keycdn

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strconv"
	"time"
)

const BaseURL = "https://api.keycdn.com"

type Client struct {
	apikey string
	Base   string
	http   *http.Client
}

func New(key string) Client {
	return Client{
		apikey: key,
		Base:   BaseURL,
	}
}

type response struct {
	Status      string `json:"status"`
	Description string `json:"description"`
}

type Zone struct {
	ID                      uint64
	Name                    string
	Status                  string
	Type                    string
	ForceDownload           bool
	CORS                    bool
	Gzip                    bool
	Expire                  int
	HTTP2                   bool
	SecureToken             bool
	SecureTokenKey          string
	SSLCert                 string
	CustomSSLKey            string
	CunstomSSLCert          string
	ForceSSL                bool
	OriginURL               string
	CacheMaxExpire          int
	CacheIgnoreCacheControl bool
	CacheIgnoreQueryString  bool
	CacheStripCookies       bool
	CachePullKey            string
	CacheCanonical          bool
	CacheRobots             bool
}

type zonesResp struct {
	response
	Data map[string][]zoneResp
}

type zoneResp map[string]string

func (z zoneResp) ToZone() Zone {
	zone := Zone{}
	if idStr, found := z["id"]; found {
		id, err := strconv.ParseUint(idStr, 10, 64)
		if err == nil {
			zone.ID = id
		}
	}
	if name, found := z["name"]; found {
		zone.Name = name
	}
	// TODO fill out other fields as well
	return zone
}

type stateStatResponse struct {
	response
	Data map[string][]stateAmountResp `json:"data"`
}

type stateAmountResp map[string]string

func (s stateAmountResp) Get(key string) uint64 {
	if v, found := s[key]; found {
		iv, err := strconv.Atoi(v)
		if err != nil {
			return 0
		}
		return uint64(iv)
	}
	return 0
}

type trafficAmountResp struct {
	Amount    string `json:"amount"`
	Timestamp string `json:"timestamp"`
}

func (t trafficAmountResp) Count() uint64 {
	iv, err := strconv.Atoi(t.Amount)
	if err != nil {
		return 0
	}
	return uint64(iv)
}

func (t trafficAmountResp) Time() time.Time {
	iv, err := strconv.Atoi(t.Timestamp)
	if err != nil {
		return time.Now()
	}
	return time.Unix(int64(iv), 0)
}

type trafficResponse struct {
	response
	Data map[string][]trafficAmountResp `json:"data"`
}

func (c Client) Zones() (map[uint64]Zone, error) {
	zones := make(map[uint64]Zone, 2)
	b, err := c.get("/zones.json", map[string]string{})
	if err != nil {
		return zones, err
	}
	var zr zonesResp
	err = json.Unmarshal(b, &zr)
	if err != nil {
		return zones, err
	}
	if _, found := zr.Data["zones"]; !found {
		return zones, fmt.Errorf("zones not found in data")
	}
	for _, z := range zr.Data["zones"] {
		zone := z.ToZone()
		zones[zone.ID] = zone
	}
	return zones, nil
}

func (c Client) Traffic(zoneID uint64, from, to time.Time) (uint64, error) {
	args := make(map[string]string, 4)
	args["zone_id"] = strconv.FormatUint(zoneID, 10)
	args["start"] = strconv.Itoa(int(from.Unix()))
	args["end"] = strconv.Itoa(int(to.Unix()))
	args["interval"] = "hour"
	b, err := c.get("/reports/traffic.json", args)
	if err != nil {
		return 0, err
	}
	//fmt.Printf("Body: %s\n", b)
	var tr trafficResponse
	err = json.Unmarshal(b, &tr)
	if err != nil {
		return 0, err
	}
	if _, found := tr.Data["stats"]; !found {
		return 0, fmt.Errorf("stats not found in data")
	}
	var sum uint64
	for _, a := range tr.Data["stats"] {
		//fmt.Printf("TS: %s - Amount: %d\n", a.Time().Format(time.RFC3339), a.Count()/1024/1024)
		sum += a.Count()
	}
	return sum, nil
}

func (c Client) Status(zoneID uint64, from, to time.Time) (map[string]uint64, error) {
	ret := make(map[string]uint64, 4)
	args := make(map[string]string, 4)
	args["zone_id"] = strconv.FormatUint(zoneID, 10)
	args["start"] = strconv.Itoa(int(from.Unix()))
	args["end"] = strconv.Itoa(int(to.Unix()))
	args["interval"] = "hour"
	b, err := c.get("/reports/statestats.json", args)
	if err != nil {
		return ret, err
	}
	var ssr stateStatResponse
	err = json.Unmarshal(b, &ssr)
	if err != nil {
		return ret, err
	}
	if _, found := ssr.Data["stats"]; !found {
		return ret, fmt.Errorf("stats not found in data")
	}
	for _, a := range ssr.Data["stats"] {
		for _, k := range []string{"totalcachehit", "totalcachemiss", "totalsuccess", "totalerror"} {
			ret[k] += a.Get(k)
		}
	}
	return ret, nil
}

func (c Client) get(file string, args map[string]string) ([]byte, error) {
	vs := url.Values{}
	for k, v := range args {
		vs.Set(k, v)
	}
	url := c.Base + file + "?" + vs.Encode()

	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return []byte{}, err
	}
	req.SetBasicAuth(c.apikey, "")
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return []byte{}, err
	}
	defer resp.Body.Close()
	return ioutil.ReadAll(resp.Body)
}
<commit_msg>Add Purge Methods<commit_after>package keycdn

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strconv"
	"time"
)

const BaseURL = "https://api.keycdn.com"

type Client struct {
	apikey string
	Base   string
	http   *http.Client
}

func New(key string) Client {
	return Client{
		apikey: key,
		Base:   BaseURL,
	}
}

type response struct {
	Status      string `json:"status"`
	Description string `json:"description"`
}

type Zone struct {
	ID                      uint64
	Name                    string
	Status                  string
	Type                    string
	ForceDownload           bool
	CORS                    bool
	Gzip                    bool
	Expire                  int
	HTTP2                   bool
	SecureToken             bool
	SecureTokenKey          string
	SSLCert                 string
	CustomSSLKey            string
	CunstomSSLCert          string
	ForceSSL                bool
	OriginURL               string
	CacheMaxExpire          int
	CacheIgnoreCacheControl bool
	CacheIgnoreQueryString  bool
	CacheStripCookies       bool
	CachePullKey            string
	CacheCanonical          bool
	CacheRobots             bool
}

type zonesResp struct {
	response
	Data map[string][]zoneResp
}

type zoneResp map[string]string

func (z zoneResp) ToZone() Zone {
	zone := Zone{}
	if idStr, found := z["id"]; found {
		id, err := strconv.ParseUint(idStr, 10, 64)
		if err == nil {
			zone.ID = id
		}
	}
	if name, found := z["name"]; found {
		zone.Name = name
	}
	// TODO fill out other fields as well
	return zone
}

type stateStatResponse struct {
	response
	Data map[string][]stateAmountResp `json:"data"`
}

type stateAmountResp map[string]string

func (s stateAmountResp) Get(key string) uint64 {
	if v, found := s[key]; found {
		iv, err := strconv.Atoi(v)
		if err != nil {
			return 0
		}
		return uint64(iv)
	}
	return 0
}

type trafficAmountResp struct {
	Amount    string `json:"amount"`
	Timestamp string `json:"timestamp"`
}

func (t trafficAmountResp) Count() uint64 {
	iv, err := strconv.Atoi(t.Amount)
	if err != nil {
		return 0
	}
	return uint64(iv)
}

func (t trafficAmountResp) Time() time.Time {
	iv, err := strconv.Atoi(t.Timestamp)
	if err != nil {
		return time.Now()
	}
	return time.Unix(int64(iv), 0)
}

type trafficResponse struct {
	response
	Data map[string][]trafficAmountResp `json:"data"`
}

func (c Client) Zones() (map[uint64]Zone, error) {
	zones := make(map[uint64]Zone, 2)
	b, err := c.get("/zones.json", map[string]string{})
	if err != nil {
		return zones, err
	}
	var zr zonesResp
	err = json.Unmarshal(b, &zr)
	if err != nil {
		return zones, err
	}
	if _, found := zr.Data["zones"]; !found {
		return zones, fmt.Errorf("zones not found in data")
	}
	for _, z := range zr.Data["zones"] {
		zone := z.ToZone()
		zones[zone.ID] = zone
	}
	return zones, nil
}

func (c Client) Traffic(zoneID uint64, from, to time.Time) (uint64, error) {
	args := make(map[string]string, 4)
	args["zone_id"] = strconv.FormatUint(zoneID, 10)
	args["start"] = strconv.Itoa(int(from.Unix()))
	args["end"] = strconv.Itoa(int(to.Unix()))
	args["interval"] = "hour"
	b, err := c.get("/reports/traffic.json", args)
	if err != nil {
		return 0, err
	}
	//fmt.Printf("Body: %s\n", b)
	var tr trafficResponse
	err = json.Unmarshal(b, &tr)
	if err != nil {
		return 0, err
	}
	if _, found := tr.Data["stats"]; !found {
		return 0, fmt.Errorf("stats not found in data")
	}
	var sum uint64
	for _, a := range tr.Data["stats"] {
		//fmt.Printf("TS: %s - Amount: %d\n", a.Time().Format(time.RFC3339), a.Count()/1024/1024)
		sum += a.Count()
	}
	return sum, nil
}

func (c Client) Status(zoneID uint64, from, to time.Time) (map[string]uint64, error) {
	ret := make(map[string]uint64, 4)
	args := make(map[string]string, 4)
	args["zone_id"] = strconv.FormatUint(zoneID, 10)
	args["start"] = strconv.Itoa(int(from.Unix()))
	args["end"] = strconv.Itoa(int(to.Unix()))
	args["interval"] = "hour"
	b, err := c.get("/reports/statestats.json", args)
	if err != nil {
		return ret, err
	}
	var ssr stateStatResponse
	err = json.Unmarshal(b, &ssr)
	if err != nil {
		return ret, err
	}
	if _, found := ssr.Data["stats"]; !found {
		return ret, fmt.Errorf("stats not found in data")
	}
	for _, a := range ssr.Data["stats"] {
		for _, k := range []string{"totalcachehit", "totalcachemiss", "totalsuccess", "totalerror"} {
			ret[k] += a.Get(k)
		}
	}
	return ret, nil
}

func (c Client) PurgeZoneCache(zoneID uint64) error {
	zone := strconv.FormatUint(zoneID, 10)
	b, err := c.get("/zones/purge/"+zone+".json", nil)
	if err != nil {
		return err
	}
	var resp response
	err = json.Unmarshal(b, &resp)
	if err != nil {
		return err
	}
	if resp.Status != "success" {
		return fmt.Errorf("Failed to purge Zone %d: %s", zoneID, resp.Description)
	}
	return nil
}

type URLs struct {
	URLs []string `json:"urls"`
}

func (c Client) PurgeZoneURL(zoneID uint64, urls []string) error {
	zones, err := c.Zones()
	if err != nil {
		return err
	}
	zone, found := zones[zoneID]
	if !found {
		return fmt.Errorf("Zone %d not found", zoneID)
	}
	// TODO check urls have the correct prefix
	_ = zone
	zID := strconv.FormatUint(zoneID, 10)
	u := URLs{URLs: urls}
	b, err := c.delete("/zones/purgeurl/"+zID+".json", u)
	if err != nil {
		return err
	}
	var resp response
	err = json.Unmarshal(b, &resp)
	if err != nil {
		return err
	}
	if resp.Status != "success" {
		return fmt.Errorf("Failed to purge Zone %d: %s", zoneID, resp.Description)
	}
	return nil
}

type Tags struct {
	Tags []string `json:"tags"`
}

func (c Client) PurgeZoneTag(zoneID uint64, tags []string) error {
	zID := strconv.FormatUint(zoneID, 10)
	t := Tags{Tags: tags}
	b, err := c.delete("/zones/purgetag/"+zID+".json", t)
	if err != nil {
		return err
	}
	var resp response
	err = json.Unmarshal(b, &resp)
	if err != nil {
		return err
	}
	if resp.Status != "success" {
		return fmt.Errorf("Failed to purge Zone %d: %s", zoneID, resp.Description)
	}
	return nil
}

func (c Client) get(file string, args map[string]string) ([]byte, error) {
	vs := url.Values{}
	for k, v := range args {
		vs.Set(k, v)
	}
	url := c.Base + file + "?" + vs.Encode()

	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return []byte{}, err
	}
	req.SetBasicAuth(c.apikey, "")
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return []byte{}, err
	}
	defer resp.Body.Close()
	return ioutil.ReadAll(resp.Body)
}

func (c Client) delete(file string, body interface{}) ([]byte, error) {
	url := c.Base + file

	b, err := json.Marshal(body)
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest("DELETE", url, bytes.NewBuffer(b))
	if err != nil {
		return nil, err
	}
	req.SetBasicAuth(c.apikey, "")
	req.Header.Add("Content-Type", "application/json")
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	return ioutil.ReadAll(resp.Body)
}
<|endoftext|>


<commit_before>package main

import (
	"./helpers"
	"strings"
)

func Adduser(msg string) {
	helpers.TRACE.Println("socket.io: adduser", msg)

}

func Logon(msg string) {
	ipNumbers := strings.Split(msg, " ")
	helpers.TRACE.Println("socket.io: Logon", ipNumbers)

}
<commit_msg>javascript logon test<commit_after>package main

import (
	"./helpers"
	"crypto/md5"
	"strings"
)

func Adduser(msg string) {
	helpers.TRACE.Println("socket.io: adduser", msg)

}

func Logon(msg string) {
	ipNumbers := strings.Split(msg, " ")
	helpers.TRACE.Println("socket.io: Logon", ipNumbers)

	space := md5.Sum([]byte(ipNumbers[0]))
	helpers.TRACE.Println("socket.io: Logon", space)

}
<|endoftext|>


<commit_before>package main

import (
	"./helpers"
	"code.google.com/p/go-uuid/uuid"
	"encoding/json"
	"github.com/garyburd/redigo/redis"
	"github.com/googollee/go-socket.io"
	"net/http"
	"strings"
)

type JsonError struct {
	Error string
}

type Space struct {
	Channel string
	SpaceIp string
	Space   []Player
}

type Player struct {
	UserId   string
	UserName string
}

func Adduser(msg string) {
	helpers.TRACE.Println("socket.io: adduser", msg)

}

func Logon(so socketio.Socket, msg string) {

	var space Space
	var known bool = false

	ipNumbers := strings.Split(msg, " ")
	helpers.TRACE.Println("socket.io->Logon: IP", ipNumbers)

	userId := ipNumbers[0]
	spaceIp := ipNumbers[1]
	helpers.TRACE.Println("socket.io->Logon: SpaceIp", spaceIp)

	redisDB := RedisPool.Get()
	defer redisDB.Close()

	jsonSpace, err := redis.Bytes(redisDB.Do("GET", spaceIp))
	if err != nil {
		// so the user is in a new space we add him
		known = true
		TRACE.Println("socket.io->Logon: newSpace", err)
		space = Space{
			Channel: uuid.New(),
			SpaceIp: spaceIp,
			Space: []Player{
				{
					UserId:   uuid.New(),
					UserName: "JonDoe",
				},
			},
		}
		jsonSpace, err := json.Marshal(space)
		if err != nil {
			ERROR.Println("socket.io->Logon json.Marshal error: ", err)
		}
		_, err = redisDB.Do("SET", spaceIp, jsonSpace)
		if err != nil {
			ERROR.Println("socket.io->Logon RedisDB SET error: ", err)
		}

	} else {
		// else unmarshal the json object
		err = json.Unmarshal(jsonSpace, &space)
		if err != nil {
			ERROR.Println("socket.io->Logon json.Unmarshal error: ", err)
		}
	}

	// check if the user is known
	for _, element := range space.Space {
		if element.UserId == userId {
			known = true
			TRACE.Println("socket.io->Logon known userId", element.UserId, "in Space", spaceIp)
		}
	}

	// if ip is unknow add it to the space
	if !known {
		TRACE.Println("socket.io->Logon unknown userId", userId, "give him a new one")

		player := Player{
			UserId:   uuid.New(),
			UserName: "JonDoe",
		}

		space.Space = append(space.Space, player)
		jsonSpace, err := json.Marshal(space)
		if err != nil {
			ERROR.Println("socket.io->Logon json.Marshal error: ", err)
		}
		_, err = redisDB.Do("SET", spaceIp, jsonSpace)
		if err != nil {
			ERROR.Println("socket.io->Logon RedisDB SET error: ", err)
		}

		TRACE.Println("socket.io->Logon added", space)
	}

	TRACE.Println("socket.io->Logon Emit Answer with Id", so.Id(), space)
	r := so.Emit("space", space)
	TRACE.Println("socket.io response:", r)
	TRACE.Println("socket.io->Logon Brodcast Answer with Id", so.Id(), space)
	r = so.BroadcastTo("chat", "space", space)
	TRACE.Println("socket.io response:", r)
}

func HttpNewUser(w http.ResponseWriter, r *http.Request) {

	var space Space
	var taken bool
	var response interface{}
	//var jsonSpace []byte

	err := r.ParseForm()
	if err != nil {
		ERROR.Println("http-api->NewUser: err", err)
	}

	userName := r.FormValue("userName")
	spaceIp := strings.Split(r.RemoteAddr, ":")[0]
	helpers.TRACE.Println("http-api->NewUser: IP", spaceIp)

	redisDB := RedisPool.Get()
	defer redisDB.Close()

	jsonSpace, err := redis.Bytes(redisDB.Do("GET", spaceIp))
	if err != nil {
		// so the user is in a new space we add him
		TRACE.Println("http-api->NewUser: newSpace", err)
		space = Space{
			Channel: uuid.New(),
			SpaceIp: spaceIp,
			Space: []Player{
				{
					UserId:   uuid.New(),
					UserName: userName,
				},
			},
		}
		response = space

	} else {
		// space exists
		// else unmarshal the json object
		err = json.Unmarshal(jsonSpace, &space)
		if err != nil {
			ERROR.Println("http-api->NewUser json.Unmarshal error: ", err)
		}

		// check if username is taken
		for _, element := range space.Space {
			if element.UserName == userName {
				taken = true
				TRACE.Println("http-api->NewUser known userId", element.UserId, "in Space", spaceIp)
			}
		}
		if taken {
			response = JsonError{Error: "user exists"}
		}
	}

	jsonSpace, err = json.Marshal(response)
	if err != nil {
		ERROR.Println("http-api->NewUser json.Marshal error: ", err)
	}
	_, err = redisDB.Do("SET", spaceIp, jsonSpace)
	if err != nil {
		ERROR.Println("http-api->NewUser RedisDB SET error: ", err)
	}

	TRACE.Println("http-api->NewUser Answer", space)
	w.Header().Set("Content-Type", "application/json")
	w.Write(jsonSpace)
}

func HttpLogon(w http.ResponseWriter, r *http.Request) {

	var space Space
	var known bool = false
	var jsonSpace []byte

	err := r.ParseForm()
	if err != nil {
		ERROR.Println("http-api->Logon: err", err)
	}

	userId := r.FormValue("userId")
	spaceIp := strings.Split(r.RemoteAddr, ":")[0]
	helpers.TRACE.Println("http-api->Logon: IP", spaceIp)

	redisDB := RedisPool.Get()
	defer redisDB.Close()

	jsonSpace, err = redis.Bytes(redisDB.Do("GET", spaceIp))
	if err != nil {
		// so the user is in a new space we add him
		known = true
		TRACE.Println("http-api->Logon: newSpace", err)
		space = Space{
			Channel: uuid.New(),
			SpaceIp: spaceIp,
			Space: []Player{
				{
					UserId:   uuid.New(),
					UserName: "JonDoe",
				},
			},
		}
		jsonSpace, err := json.Marshal(space)
		if err != nil {
			ERROR.Println("http-api->Logon json.Marshal error: ", err)
		}
		_, err = redisDB.Do("SET", spaceIp, jsonSpace)
		if err != nil {
			ERROR.Println("http-api->Logon RedisDB SET error: ", err)
		}

	} else {
		// else unmarshal the json object
		err = json.Unmarshal(jsonSpace, &space)
		if err != nil {
			ERROR.Println("http-api->Logon json.Unmarshal error: ", err)
		}
	}

	// check if the user is known
	for _, element := range space.Space {
		if element.UserId == userId {
			known = true
			TRACE.Println("http-api->Logon known userId", element.UserId, "in Space", spaceIp)
		}
	}

	// if id is unknow add it to the space
	if !known {
		TRACE.Println("http-api->Logon unknown UserId", userId, ", lets give the poor guy one")

		player := Player{
			UserId:   uuid.New(),
			UserName: "JonDoe",
		}

		space.Space = append(space.Space, player)
		jsonSpace, err := json.Marshal(space)
		if err != nil {
			ERROR.Println("http-api->Logon json.Marshal error: ", err)
		}
		_, err = redisDB.Do("SET", spaceIp, jsonSpace)
		if err != nil {
			ERROR.Println("http-api->Logon RedisDB SET error: ", err)
		}

		TRACE.Println("http-api->Logon added", space)

		// generate a answer with only one user
		space = Space{
			Channel: space.Channel,
			SpaceIp: spaceIp,
			Space: []Player{
				{
					UserId:   player.UserId,
					UserName: player.UserName,
				},
			},
		}
		jsonSpace, err = json.Marshal(space)
		if err != nil {
			ERROR.Println("http-api->Logon json.Marshal error: ", err)
		}
		_, err = redisDB.Do("SET", spaceIp, jsonSpace)
		if err != nil {
			ERROR.Println("http-api->Logon RedisDB SET error: ", err)
		}

	}

	TRACE.Println("http-api->Logon Answer", space)
	w.Header().Set("Content-Type", "application/json")
	w.Write(jsonSpace)

}

func JoinGame(so socketio.Socket, msg string) {

	helpers.TRACE.Println("socket.io: Join", msg)

	so.Emit("channel", "abcde")
}
<commit_msg>http api<commit_after>package main

import (
	"./helpers"
	"code.google.com/p/go-uuid/uuid"
	"encoding/json"
	"github.com/garyburd/redigo/redis"
	"github.com/googollee/go-socket.io"
	"net/http"
	"strings"
)

type JsonError struct {
	Error string
}

type Space struct {
	Channel string
	SpaceIp string
	Space   []Player
}

type Player struct {
	UserId   string
	UserName string
}

func Adduser(msg string) {
	helpers.TRACE.Println("socket.io: adduser", msg)

}

func Logon(so socketio.Socket, msg string) {

	var space Space
	var known bool = false

	ipNumbers := strings.Split(msg, " ")
	helpers.TRACE.Println("socket.io->Logon: IP", ipNumbers)

	userId := ipNumbers[0]
	spaceIp := ipNumbers[1]
	helpers.TRACE.Println("socket.io->Logon: SpaceIp", spaceIp)

	redisDB := RedisPool.Get()
	defer redisDB.Close()

	jsonSpace, err := redis.Bytes(redisDB.Do("GET", spaceIp))
	if err != nil {
		// so the user is in a new space we add him
		known = true
		TRACE.Println("socket.io->Logon: newSpace", err)
		space = Space{
			Channel: uuid.New(),
			SpaceIp: spaceIp,
			Space: []Player{
				{
					UserId:   uuid.New(),
					UserName: "JonDoe",
				},
			},
		}
		jsonSpace, err := json.Marshal(space)
		if err != nil {
			ERROR.Println("socket.io->Logon json.Marshal error: ", err)
		}
		_, err = redisDB.Do("SET", spaceIp, jsonSpace)
		if err != nil {
			ERROR.Println("socket.io->Logon RedisDB SET error: ", err)
		}

	} else {
		// else unmarshal the json object
		err = json.Unmarshal(jsonSpace, &space)
		if err != nil {
			ERROR.Println("socket.io->Logon json.Unmarshal error: ", err)
		}
	}

	// check if the user is known
	for _, element := range space.Space {
		if element.UserId == userId {
			known = true
			TRACE.Println("socket.io->Logon known userId", element.UserId, "in Space", spaceIp)
		}
	}

	// if ip is unknow add it to the space
	if !known {
		TRACE.Println("socket.io->Logon unknown userId", userId, "give him a new one")

		player := Player{
			UserId:   uuid.New(),
			UserName: "JonDoe",
		}

		space.Space = append(space.Space, player)
		jsonSpace, err := json.Marshal(space)
		if err != nil {
			ERROR.Println("socket.io->Logon json.Marshal error: ", err)
		}
		_, err = redisDB.Do("SET", spaceIp, jsonSpace)
		if err != nil {
			ERROR.Println("socket.io->Logon RedisDB SET error: ", err)
		}

		TRACE.Println("socket.io->Logon added", space)
	}

	TRACE.Println("socket.io->Logon Emit Answer with Id", so.Id(), space)
	r := so.Emit("space", space)
	TRACE.Println("socket.io response:", r)
	TRACE.Println("socket.io->Logon Brodcast Answer with Id", so.Id(), space)
	r = so.BroadcastTo("chat", "space", space)
	TRACE.Println("socket.io response:", r)
}

func HttpNewUser(w http.ResponseWriter, r *http.Request) {

	var space Space
	var response interface{}
	var jsonResponse []byte
	var jsonSpace []byte
	var taken bool

	err := r.ParseForm()
	if err != nil {
		ERROR.Println("http-api->NewUser: err", err)
	}

	userName := r.FormValue("userName")
	spaceIp := strings.Split(r.RemoteAddr, ":")[0]
	helpers.TRACE.Println("http-api->NewUser: IP", spaceIp)

	redisDB := RedisPool.Get()
	defer redisDB.Close()

	jsonSpace, err = redis.Bytes(redisDB.Do("GET", spaceIp))
	if err != nil {
		// so the user is in a new space we add him
		TRACE.Println("http-api->NewUser: newSpace", err)
		response = Space{
			Channel: uuid.New(),
			SpaceIp: spaceIp,
			Space: []Player{
				{
					UserId:   uuid.New(),
					UserName: userName,
				},
			},
		}
		jsonResponse, err = json.Marshal(response)
		if err != nil {
			ERROR.Println("socket.io->NewUser json.Marshal error: ", err)
		}
		_, err = redisDB.Do("SET", spaceIp, jsonResponse)
		if err != nil {
			ERROR.Println("socket.io->NewUser RedisDB SET error: ", err)
		}

	} else {
		// space exists
		// else unmarshal the json object
		err = json.Unmarshal(jsonSpace, &space)
		if err != nil {
			ERROR.Println("http-api->NewUser json.Unmarshal error: ", err)
		}

		// check if username is taken
		for _, element := range space.Space {
			if element.UserName == userName {
				taken = true
				TRACE.Println("http-api->NewUser known userId", element.UserId, "in Space", spaceIp)
			}
		}

		if taken {
			// error user exists allready, try an other alias
			response = JsonError{Error: "user exists"}
			jsonResponse, err = json.Marshal(response)
			if err != nil {
				ERROR.Println("socket.io->NewUser json.Marshal error: ", err)
			}
		} else {
			// add user to space
			player := Player{
				UserId:   uuid.New(),
				UserName: userName,
			}

			// add user to json object in database
			space.Space = append(space.Space, player)
			jsonSpace, err := json.Marshal(space)
			if err != nil {
				ERROR.Println("socket.io->NewUser json.Marshal error: ", err)
			}
			_, err = redisDB.Do("SET", spaceIp, jsonSpace)
			if err != nil {
				ERROR.Println("socket.io->NewUser RedisDB SET error: ", err)
			}
			// return onle the new user to the request
			response = Space{
				Channel: uuid.New(),
				SpaceIp: spaceIp,
				Space: []Player{
					player,
				},
			}

			jsonResponse, err = json.Marshal(response)
			if err != nil {
				ERROR.Println("socket.io->NewUser json.Marshal error: ", err)
			}

		}
	}

	TRACE.Println("http-api->NewUser Answer", response)
	w.Header().Set("Content-Type", "application/json")
	w.Write(jsonResponse)
}

func HttpLogon(w http.ResponseWriter, r *http.Request) {

	var space Space
	var known bool = false
	var jsonSpace []byte

	err := r.ParseForm()
	if err != nil {
		ERROR.Println("http-api->Logon: err", err)
	}

	userId := r.FormValue("userId")
	spaceIp := strings.Split(r.RemoteAddr, ":")[0]
	helpers.TRACE.Println("http-api->Logon: IP", spaceIp)

	redisDB := RedisPool.Get()
	defer redisDB.Close()

	jsonSpace, err = redis.Bytes(redisDB.Do("GET", spaceIp))
	if err != nil {
		// so the user is in a new space we add him
		known = true
		TRACE.Println("http-api->Logon: newSpace", err)
		space = Space{
			Channel: uuid.New(),
			SpaceIp: spaceIp,
			Space: []Player{
				{
					UserId:   uuid.New(),
					UserName: "JonDoe",
				},
			},
		}
		jsonSpace, err := json.Marshal(space)
		if err != nil {
			ERROR.Println("http-api->Logon json.Marshal error: ", err)
		}
		_, err = redisDB.Do("SET", spaceIp, jsonSpace)
		if err != nil {
			ERROR.Println("http-api->Logon RedisDB SET error: ", err)
		}

	} else {
		// else unmarshal the json object
		err = json.Unmarshal(jsonSpace, &space)
		if err != nil {
			ERROR.Println("http-api->Logon json.Unmarshal error: ", err)
		}
	}

	// check if the user is known
	for _, element := range space.Space {
		if element.UserId == userId {
			known = true
			TRACE.Println("http-api->Logon known userId", element.UserId, "in Space", spaceIp)
		}
	}

	// if id is unknow add it to the space
	if !known {
		TRACE.Println("http-api->Logon unknown UserId", userId, ", lets give the poor guy one")

		player := Player{
			UserId:   uuid.New(),
			UserName: "JonDoe",
		}

		space.Space = append(space.Space, player)
		jsonSpace, err := json.Marshal(space)
		if err != nil {
			ERROR.Println("http-api->Logon json.Marshal error: ", err)
		}
		_, err = redisDB.Do("SET", spaceIp, jsonSpace)
		if err != nil {
			ERROR.Println("http-api->Logon RedisDB SET error: ", err)
		}

		TRACE.Println("http-api->Logon added", space)

		// generate a answer with only one user
		space = Space{
			Channel: space.Channel,
			SpaceIp: spaceIp,
			Space: []Player{
				{
					UserId:   player.UserId,
					UserName: player.UserName,
				},
			},
		}
		jsonSpace, err = json.Marshal(space)
		if err != nil {
			ERROR.Println("http-api->Logon json.Marshal error: ", err)
		}
		_, err = redisDB.Do("SET", spaceIp, jsonSpace)
		if err != nil {
			ERROR.Println("http-api->Logon RedisDB SET error: ", err)
		}

	}

	TRACE.Println("http-api->Logon Answer", space)
	w.Header().Set("Content-Type", "application/json")
	w.Write(jsonSpace)

}

func JoinGame(so socketio.Socket, msg string) {

	helpers.TRACE.Println("socket.io: Join", msg)

	so.Emit("channel", "abcde")
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"os"
	"os/exec"
	"strings"
)

type App struct {
	ui  *UI
	nav *Nav
}

func waitKey() error {
	// TODO: this should be done with termbox somehow

	c := `echo
	      echo -n 'Press any key to continue'
	      old=$(stty -g)
	      stty raw -echo
	      eval "ignore=\$(dd bs=1 count=1 2> /dev/null)"
	      stty $old
	      echo`

	cmd := exec.Command(gOpts.shell, "-c", c)

	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	if err := cmd.Run(); err != nil {
		return fmt.Errorf("waiting key: %s", err)
	}

	return nil
}

func (app *App) handleInp() {
	for {
		if gExitFlag {
			log.Print("bye!")

			if gLastDirPath != "" {
				f, err := os.Create(gLastDirPath)
				if err != nil {
					log.Printf("opening last dir file: %s", err)
				}
				defer f.Close()

				dir := app.nav.currDir()

				_, err = f.WriteString(dir.path)
				if err != nil {
					log.Printf("writing last dir file: %s", err)
				}
			}

			return
		}
		e := app.ui.getExpr(app.nav)
		if e == nil {
			continue
		}
		e.eval(app, nil)
		app.ui.draw(app.nav)
	}
}

func (app *App) exportVars() {
	dir := app.nav.currDir()

	var envFile string
	if len(dir.fi) != 0 {
		envFile = app.nav.currPath()
	}

	marks := app.nav.currMarks()

	envFiles := strings.Join(marks, ":")

	os.Setenv("f", envFile)
	os.Setenv("fs", envFiles)

	if len(marks) == 0 {
		os.Setenv("fx", envFile)
	} else {
		os.Setenv("fx", envFiles)
	}
}

// This function is used to run a command in shell. Following modes are used:
//
// Prefix  Wait  Async  Stdin/Stdout/Stderr  UI action
// $       No    No     Yes                  Pause and then resume
// !       Yes   No     Yes                  Pause and then resume
// &       No    Yes    No                   Do nothing
//
// Waiting async commands are not used for now.
func (app *App) runShell(s string, args []string, wait bool, async bool) {
	app.exportVars()

	if len(gOpts.ifs) != 0 {
		s = fmt.Sprintf("IFS='%s'; %s", gOpts.ifs, s)
	}

	args = append([]string{"-c", s, "--"}, args...)
	cmd := exec.Command(gOpts.shell, args...)

	if !async {
		cmd.Stdin = os.Stdin
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr

		app.ui.pause()
		defer app.ui.resume()
		defer app.nav.renew(app.ui.wins[0].h)
	}

	var err error
	if async {
		err = cmd.Start()
	} else {
		err = cmd.Run()
	}

	if err != nil {
		msg := fmt.Sprintf("running shell: %s", err)
		app.ui.message = msg
		log.Print(msg)
	}

	if wait {
		if err := waitKey(); err != nil {
			msg := fmt.Sprintf("waiting shell: %s", err)
			app.ui.message = msg
			log.Print(msg)
		}
	}
}
<commit_msg>skip ui drawing if exit flag is set<commit_after>package main

import (
	"fmt"
	"log"
	"os"
	"os/exec"
	"strings"
)

type App struct {
	ui  *UI
	nav *Nav
}

func waitKey() error {
	// TODO: this should be done with termbox somehow

	c := `echo
	      echo -n 'Press any key to continue'
	      old=$(stty -g)
	      stty raw -echo
	      eval "ignore=\$(dd bs=1 count=1 2> /dev/null)"
	      stty $old
	      echo`

	cmd := exec.Command(gOpts.shell, "-c", c)

	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	if err := cmd.Run(); err != nil {
		return fmt.Errorf("waiting key: %s", err)
	}

	return nil
}

func (app *App) handleInp() {
	for {
		// exit check is done on the top just in case user quits
		// before input handling for some reason (e.g. in configuration file)
		if gExitFlag {
			log.Print("bye!")

			if gLastDirPath != "" {
				f, err := os.Create(gLastDirPath)
				if err != nil {
					log.Printf("opening last dir file: %s", err)
				}
				defer f.Close()

				dir := app.nav.currDir()

				_, err = f.WriteString(dir.path)
				if err != nil {
					log.Printf("writing last dir file: %s", err)
				}
			}

			return
		}
		e := app.ui.getExpr(app.nav)
		if e == nil {
			continue
		}
		e.eval(app, nil)
		if gExitFlag {
			continue
		}
		app.ui.draw(app.nav)
	}
}

func (app *App) exportVars() {
	dir := app.nav.currDir()

	var envFile string
	if len(dir.fi) != 0 {
		envFile = app.nav.currPath()
	}

	marks := app.nav.currMarks()

	envFiles := strings.Join(marks, ":")

	os.Setenv("f", envFile)
	os.Setenv("fs", envFiles)

	if len(marks) == 0 {
		os.Setenv("fx", envFile)
	} else {
		os.Setenv("fx", envFiles)
	}
}

// This function is used to run a command in shell. Following modes are used:
//
// Prefix  Wait  Async  Stdin/Stdout/Stderr  UI action
// $       No    No     Yes                  Pause and then resume
// !       Yes   No     Yes                  Pause and then resume
// &       No    Yes    No                   Do nothing
//
// Waiting async commands are not used for now.
func (app *App) runShell(s string, args []string, wait bool, async bool) {
	app.exportVars()

	if len(gOpts.ifs) != 0 {
		s = fmt.Sprintf("IFS='%s'; %s", gOpts.ifs, s)
	}

	args = append([]string{"-c", s, "--"}, args...)
	cmd := exec.Command(gOpts.shell, args...)

	if !async {
		cmd.Stdin = os.Stdin
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr

		app.ui.pause()
		defer app.ui.resume()
		defer app.nav.renew(app.ui.wins[0].h)
	}

	var err error
	if async {
		err = cmd.Start()
	} else {
		err = cmd.Run()
	}

	if err != nil {
		msg := fmt.Sprintf("running shell: %s", err)
		app.ui.message = msg
		log.Print(msg)
	}

	if wait {
		if err := waitKey(); err != nil {
			msg := fmt.Sprintf("waiting shell: %s", err)
			app.ui.message = msg
			log.Print(msg)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/gorilla/mux"
	"html/template"
	"net/http"
	"strings"
)

func hello(w http.ResponseWriter, r *http.Request) {
    t, _ := template.ParseFiles("templates/index.html")
    _ = t.ExecuteTemplate(w, "index.html", "")
}

func main() {
	r := mux.NewRouter()
	r.HandleFunc("/", hello)
	r.PathPrefix("/").Handler(http.StripPrefix("/", http.FileServer(http.Dir("assets/"))))
	err := http.ListenAndServe(":8080", r)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}
<commit_msg>Remove superfluous imports<commit_after>package main

import (
	log "github.com/Sirupsen/logrus"
	"github.com/gorilla/mux"
	"html/template"
	"net/http"
)

var templates = template.Must(template.ParseFiles("templates/index.html"))

func hello(w http.ResponseWriter, r *http.Request) {
	//	r.ParseForm()
	//	fmt.Println(r.Form)
	//	fmt.Println(r.URL)
	//	for k, v := range r.Form {
	//		fmt.Println("key:", k)
	//		fmt.Println("val:", strings.Join(v, ","))
	//	}

    err := templates.ExecuteTemplate(w, "index.html", "")
	if err != nil {
		panic(err)
	}
}

func main() {
	r := mux.NewRouter()
	r.HandleFunc("/", hello)
	r.PathPrefix("/").Handler(http.StripPrefix("/", http.FileServer(http.Dir("assets/"))))

	fmt.Println("Listening on port 8080")

	err := http.ListenAndServe(":8080", r)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}
<|endoftext|>


<commit_before>// +build ignore

// go run generate.go && go fmt

// The generate-charset-data command generates the Go source code
// for code.google.com/p/go-charset/data from the data files
// found in code.google.com/p/go-charset/datafiles.
// It should be run in the go-charset root directory.
// The resulting Go files will need gofmt'ing.
package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"text/template"
)

type info struct {
	Path    string
}

var tfuncs = template.FuncMap{
	"basename": func(s string) string {
		return filepath.Base(s)
	},
	"read": func(path string) ([]byte, error) {
		return ioutil.ReadFile(path)
	},
}

var tmpl = template.Must(template.New("").Funcs(tfuncs).Parse(`
	// This file is automatically generated by generate-charset-data.
	// Do not hand-edit.

	package data
	import (
		"code.google.com/p/go-charset/charset"
		"io"
		"io/ioutil"
		"strings"
	)

	func init() {
		charset.RegisterDataFile({{basename .Path | printf "%q"}}, func() (io.ReadCloser, error) {
			r := strings.NewReader({{read .Path | printf "%q"}})
			return ioutil.NopCloser(r), nil
		})
	}
`))

var docTmpl = template.Must(template.New("").Funcs(tfuncs).Parse(`
	// This file is automatically generated by generate-charset-data.
	// Do not hand-edit.

	// The {{basename .Package}} package embeds all the charset
	// data files as Go data. It registers the data with the charset
	// package as a side effect of its import. To use:
	//
	//	import _ "code.google.com/p/go-charset"
	package {{basename .Package}}
`))

func main() {
	dataDir := filepath.Join("..", "datafiles")
	d, err := os.Open(dataDir)
	if err != nil {
		fatalf("%v", err)
	}
	names, err := d.Readdirnames(0)
	if err != nil {
		fatalf("cannot read datafiles dir: %v", err)
	}
	for _, name := range names {
		writeFile("data_"+name+".go", tmpl, info{
			Path:    filepath.Join(dataDir, name),
		})
	}
}

func writeFile(name string, t *template.Template, data interface{}) {
	w, err := os.Create(name)
	if err != nil {
		fatalf("cannot create output file: %v", err)
	}
	defer w.Close()
	err = t.Execute(w, data)
	if err != nil {
		fatalf("template execute %q: %v", name, err)
	}
}

func fatalf(f string, a ...interface{}) {
	fmt.Fprintf(os.Stderr, "%s\n", fmt.Sprintf(f, a...))
	os.Exit(2)
}
<commit_msg>gofmt<commit_after>// +build ignore

// go run generate.go && go fmt

// The generate-charset-data command generates the Go source code
// for code.google.com/p/go-charset/data from the data files
// found in code.google.com/p/go-charset/datafiles.
// It should be run in the go-charset root directory.
// The resulting Go files will need gofmt'ing.
package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"text/template"
)

type info struct {
	Path string
}

var tfuncs = template.FuncMap{
	"basename": func(s string) string {
		return filepath.Base(s)
	},
	"read": func(path string) ([]byte, error) {
		return ioutil.ReadFile(path)
	},
}

var tmpl = template.Must(template.New("").Funcs(tfuncs).Parse(`
	// This file is automatically generated by generate-charset-data.
	// Do not hand-edit.

	package data
	import (
		"code.google.com/p/go-charset/charset"
		"io"
		"io/ioutil"
		"strings"
	)

	func init() {
		charset.RegisterDataFile({{basename .Path | printf "%q"}}, func() (io.ReadCloser, error) {
			r := strings.NewReader({{read .Path | printf "%q"}})
			return ioutil.NopCloser(r), nil
		})
	}
`))

var docTmpl = template.Must(template.New("").Funcs(tfuncs).Parse(`
	// This file is automatically generated by generate-charset-data.
	// Do not hand-edit.

	// The {{basename .Package}} package embeds all the charset
	// data files as Go data. It registers the data with the charset
	// package as a side effect of its import. To use:
	//
	//	import _ "code.google.com/p/go-charset"
	package {{basename .Package}}
`))

func main() {
	dataDir := filepath.Join("..", "datafiles")
	d, err := os.Open(dataDir)
	if err != nil {
		fatalf("%v", err)
	}
	names, err := d.Readdirnames(0)
	if err != nil {
		fatalf("cannot read datafiles dir: %v", err)
	}
	for _, name := range names {
		writeFile("data_"+name+".go", tmpl, info{
			Path: filepath.Join(dataDir, name),
		})
	}
}

func writeFile(name string, t *template.Template, data interface{}) {
	w, err := os.Create(name)
	if err != nil {
		fatalf("cannot create output file: %v", err)
	}
	defer w.Close()
	err = t.Execute(w, data)
	if err != nil {
		fatalf("template execute %q: %v", name, err)
	}
}

func fatalf(f string, a ...interface{}) {
	fmt.Fprintf(os.Stderr, "%s\n", fmt.Sprintf(f, a...))
	os.Exit(2)
}
<|endoftext|>


<commit_before>//
// @file updateHostsForWindows.go
// @brief for windows
// @author cloud@txthinking.com
// @version 0.0.1
// @date 2013-03-15
//
package main

import (
    "os"
    "io"
    "bufio"
    "strings"
    "net/http"
)

const (
    HOSTS_PATH string = "C:\\windows\\system32\\drivers\\etc\\hosts"
    SEARCH_STRING string = "#GOOGLE-HOSTS"
    HOSTS_SOURCE string = "http://tx.txthinking.com/hosts"
)

func main(){
    var hosts string
    f, _ := os.OpenFile(HOSTS_PATH, os.O_RDONLY, 0444)
    bnr := bufio.NewReader(f)
    for{
        line, err := bnr.ReadString('\n')
        if strings.Contains(line, SEARCH_STRING) {
            break
        }
        hosts += line
        if err==io.EOF {
            break
        }
    }
    f.Close();
    hosts += "\r\n"
    hosts += SEARCH_STRING
    hosts += "\r\n"

    res, _ := http.Get(HOSTS_SOURCE)
    bnr = bufio.NewReader(res.Body)
    for{
        line, err := bnr.ReadString('\n')
        hosts += line[0:len(line)-1] + "\r\n"
        if err==io.EOF {
            break
        }
    }

    os.Rename(HOSTS_PATH, HOSTS_PATH+".BAK")
    f, _ = os.OpenFile(HOSTS_PATH, os.O_WRONLY|os.O_CREATE, 0644)
    f.WriteString(hosts);
    println("Success!")
}

<commit_msg>Update updateHosts.go<commit_after>//
// @file updateHostsForWindows.go
// @brief for windows
// @author cloud@txthinking.com
// @version 0.0.1
// @date 2013-03-15
//
package main

import (
    "os"
    "io"
    "bufio"
    "strings"
    "net/http"
)

const (
    HOSTS_PATH string = "C:\\windows\\system32\\drivers\\etc\\hosts"
    SEARCH_STRING string = "#TX-HOSTS"
    HOSTS_SOURCE string = "http://tx.txthinking.com/hosts"
)

func main(){
    var hosts string
    f, _ := os.OpenFile(HOSTS_PATH, os.O_RDONLY, 0444)
    bnr := bufio.NewReader(f)
    for{
        line, err := bnr.ReadString('\n')
        if strings.Contains(line, SEARCH_STRING) {
            break
        }
        hosts += line
        if err==io.EOF {
            break
        }
    }
    f.Close();
    hosts += "\r\n"
    hosts += SEARCH_STRING
    hosts += "\r\n"

    res, _ := http.Get(HOSTS_SOURCE)
    bnr = bufio.NewReader(res.Body)
    for{
        line, err := bnr.ReadString('\n')
        hosts += line[0:len(line)-1] + "\r\n"
        if err==io.EOF {
            break
        }
    }

    os.Rename(HOSTS_PATH, HOSTS_PATH+".BAK")
    f, _ = os.OpenFile(HOSTS_PATH, os.O_WRONLY|os.O_CREATE, 0644)
    f.WriteString(hosts);
    println("Success!")
}

<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/aws/aws-lambda-go/lambda"
	"github.com/gocolly/colly"
	"io"
	"log"
	"net/http"
	"os"
	"strconv"
	"time"

	"github.com/go-telegram-bot-api/telegram-bot-api"
)

type post struct {
	URL       string
	Timestamp int64
	Author    string
	Comment   string
	Images    []string
}

var (
	TOKEN = os.Getenv("token")
	URL   = "https://www.kaffee-netz.de"
)

var topics = map[string]string{
	"Wie sieht eure Kaffee-Ecke aus?":                     "/threads/wie-sieht-eure-kaffee-ecke-aus.13966",
	"Der \"Ich habe gerade Kaffeekram gekauft\" Thread":   "/threads/der-ich-habe-gerade-kaffeekram-gekauft-thread.62180",
	"Und plötzlich war da Latte Art":                      "/threads/und-ploetzlich-war-da-latte-art.7785",
	"Ich trinke gerade diesen Filterkaffee/Brühkaffee...": "/threads/ich-trinke-gerade-diesen-espresso.19308",
	"3rd Wave Röster und Röstungen":                       "/threads/3rd-wave-roester-und-roestungen.79568",
}

func HandleRequest() {
	bot, err := tgbotapi.NewBotAPI(TOKEN)
	if err != nil {
		log.Panic(err)
	}

	posts := make([]*post, 0)

	c := colly.NewCollector(
		colly.UserAgent("Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36"),
	)

	c.OnRequest(func(r *colly.Request) {
		r.Ctx.Put("url", r.URL.String())
	})

	c.OnHTML(".mainContent", func(e *colly.HTMLElement) {
		lastPage := e.ChildAttr(`.PageNav`, "data-last")
		currentUrl := e.Response.Ctx.Get("url")
		_ = e.Request.Visit(currentUrl + "/page-" + lastPage)

		e.ForEach("ol.messageList li", func(i int, element *colly.HTMLElement) {
			// Only last posts have timestamps
			timestamp, err := strconv.Atoi(element.ChildAttr(".DateTime", "data-time"))
			if err == nil {
				// If images are found, put it to the array
				images := element.ChildAttrs(".LbImage", "src")
				if images != nil {
					p := &post{
						URL:       URL + "/" + element.ChildAttr("a.hashPermalink", "href"),
						Timestamp: int64(timestamp * 1000), // To milliseconds
						Author:    element.Attr("data-author"),
						Comment:   element.ChildText(".messageText"),
						Images:    images,
					}
					posts = append(posts, p)
				}
			}
		})
	})

	for _, topic := range topics {
		_ = c.Visit(URL + topic)
	}

	enc := json.NewEncoder(os.Stdout)
	enc.SetIndent("", "  ")

	for _, post := range posts {
		_ = enc.Encode(post)
		if post.Timestamp+3600000*2 >= Now() {
			SendPost(bot, *post)
		}
	}
}

func SendPost(bot *tgbotapi.BotAPI, p post) {
	chatId := int64(405001)

	msg := fmt.Sprintf("%s: %s", p.Author, p.Comment)
	msg += "\n" + p.URL
	textMsg := tgbotapi.NewMessage(chatId, msg)
	bot.Send(textMsg)

	for _, image := range p.Images {
		data, _ := DownloadFile(image)
		file := tgbotapi.FileBytes{Name: p.URL, Bytes: data}
		msg := tgbotapi.NewPhotoUpload(chatId, file)
		bot.Send(msg)
	}
}

func DownloadFile(url string) ([]byte, error) {

	// Get the data
	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	// Write the body to file
	buf := new(bytes.Buffer)
	_, err = io.Copy(buf, resp.Body)
	if err != nil {
		return nil, err
	}

	return buf.Bytes(), nil
}

func Now() int64 {
	return time.Now().UnixNano() / int64(time.Millisecond)
}

func main() {
	lambda.Start(HandleRequest)
}
<commit_msg>Fix timezone issue<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/aws/aws-lambda-go/lambda"
	"github.com/gocolly/colly"
	"io"
	"log"
	"net/http"
	"os"
	"strconv"
	"time"

	"github.com/go-telegram-bot-api/telegram-bot-api"
)

type post struct {
	URL       string
	Timestamp int64
	Author    string
	Comment   string
	Images    []string
}

var (
	TOKEN = os.Getenv("token")
	URL   = "https://www.kaffee-netz.de"
)

var topics = map[string]string{
	"Wie sieht eure Kaffee-Ecke aus?":                     "/threads/wie-sieht-eure-kaffee-ecke-aus.13966",
	"Der \"Ich habe gerade Kaffeekram gekauft\" Thread":   "/threads/der-ich-habe-gerade-kaffeekram-gekauft-thread.62180",
	"Und plötzlich war da Latte Art":                      "/threads/und-ploetzlich-war-da-latte-art.7785",
	"Ich trinke gerade diesen Filterkaffee/Brühkaffee...": "/threads/ich-trinke-gerade-diesen-espresso.19308",
	"3rd Wave Röster und Röstungen":                       "/threads/3rd-wave-roester-und-roestungen.79568",
}

func HandleRequest() {
	bot, err := tgbotapi.NewBotAPI(TOKEN)
	if err != nil {
		log.Panic(err)
	}

	posts := make([]*post, 0)

	c := colly.NewCollector(
		colly.UserAgent("Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36"),
	)

	c.OnRequest(func(r *colly.Request) {
		r.Ctx.Put("url", r.URL.String())
	})

	c.OnHTML(".mainContent", func(e *colly.HTMLElement) {
		lastPage := e.ChildAttr(`.PageNav`, "data-last")
		currentUrl := e.Response.Ctx.Get("url")
		_ = e.Request.Visit(currentUrl + "/page-" + lastPage)

		e.ForEach("ol.messageList li", func(i int, element *colly.HTMLElement) {
			// Only last posts have timestamps
			timestamp, err := strconv.Atoi(element.ChildAttr(".DateTime", "data-time"))
			if err == nil {
				// If images are found, put it to the array
				images := element.ChildAttrs(".LbImage", "src")
				if images != nil {
					p := &post{
						URL:       URL + "/" + element.ChildAttr("a.hashPermalink", "href"),
						Timestamp: int64(timestamp * 1000), // To milliseconds
						Author:    element.Attr("data-author"),
						Comment:   element.ChildText(".messageText"),
						Images:    images,
					}
					posts = append(posts, p)
				}
			}
		})
	})

	for _, topic := range topics {
		_ = c.Visit(URL + topic)
	}

	enc := json.NewEncoder(os.Stdout)
	enc.SetIndent("", "  ")

	for _, post := range posts {
		_ = enc.Encode(post)
		if post.Timestamp+3600000 >= Now() {
			SendPost(bot, *post)
		}
	}
}

func SendPost(bot *tgbotapi.BotAPI, p post) {
	chatId := int64(405001)

	msg := fmt.Sprintf("%s: %s", p.Author, p.Comment)
	msg += "\n" + p.URL
	textMsg := tgbotapi.NewMessage(chatId, msg)
	bot.Send(textMsg)

	for _, image := range p.Images {
		data, _ := DownloadFile(image)
		file := tgbotapi.FileBytes{Name: p.URL, Bytes: data}
		msg := tgbotapi.NewPhotoUpload(chatId, file)
		bot.Send(msg)
	}
}

func DownloadFile(url string) ([]byte, error) {

	// Get the data
	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	// Write the body to file
	buf := new(bytes.Buffer)
	_, err = io.Copy(buf, resp.Body)
	if err != nil {
		return nil, err
	}

	return buf.Bytes(), nil
}

func Now() int64 {
	return time.Now().UnixNano()/int64(time.Millisecond) - 3600000 // Locale: Europe/Berlin
}

func main() {
	lambda.Start(HandleRequest)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"regexp"
	"strings"
	"time"

	"github.com/Seklfreak/Robyul2/cache"
	"github.com/Seklfreak/Robyul2/emojis"
	"github.com/Seklfreak/Robyul2/helpers"
	"github.com/Seklfreak/Robyul2/metrics"
	"github.com/Seklfreak/Robyul2/modules"
	"github.com/Seklfreak/Robyul2/ratelimits"
	"github.com/bwmarrin/discordgo"
	"github.com/getsentry/raven-go"
)

// BotOnReady gets called after the gateway connected
func BotOnReady(session *discordgo.Session, event *discordgo.Ready) {
	log := cache.GetLogger()

	log.WithField("module", "bot").Info("Connected to discord!")
	log.WithField("module", "bot").Info("Invite link: " + fmt.Sprintf(
		"https://discordapp.com/oauth2/authorize?client_id=%s&scope=bot&permissions=%s",
		helpers.GetConfig().Path("discord.id").Data().(string),
		helpers.GetConfig().Path("discord.perms").Data().(string),
	))

	// Cache the session
	cache.SetSession(session)

	// Load and init all modules
	modules.Init(session)

	// Run async worker for guild changes
	go helpers.GuildSettingsUpdater()

	// Run async game-changer
	go changeGameInterval(session)

	// request guild members from the gateway
	go func() {
		time.Sleep(30 * time.Second)

		for _, guild := range session.State.Guilds {
			err := session.RequestGuildMembers(guild.ID, "", 0)
			if err != nil {
				raven.CaptureError(fmt.Errorf("%#v", err), map[string]string{})
			}
		}
	}()

	// Run auto-leaver for non-beta guilds
	//go autoLeaver(session)

	// Run ratelimiter
	ratelimits.Container.Init()

	go func() {
		time.Sleep(3 * time.Second)

		configName := helpers.GetConfig().Path("bot.name").Data().(string)
		configAvatar := helpers.GetConfig().Path("bot.avatar").Data().(string)

		// Change avatar if desired
		if configAvatar != "" && configAvatar != session.State.User.Avatar {
			session.UserUpdate(
				"",
				"",
				session.State.User.Username,
				configAvatar,
				"",
			)
		}

		// Change name if desired
		if configName != "" && configName != session.State.User.Username {
			session.UserUpdate(
				"",
				"",
				configName,
				session.State.User.Avatar,
				"",
			)
		}
	}()

	// Run async game-changer
	//go changeGameInterval(session)

	// Run auto-leaver for non-beta guilds
	//go autoLeaver(session)
}

func BotOnMemberListChunk(session *discordgo.Session, members *discordgo.GuildMembersChunk) {
	cache.GetLogger().WithField("module", "bot").Debug(
		fmt.Sprintf("received guild member chunk for guild: %s (%d members)",
			members.GuildID, len(members.Members)))
	var err error
	for _, member := range members.Members {
		member.GuildID = members.GuildID
		err = session.State.MemberAdd(member)
		if err != nil {
			raven.CaptureError(fmt.Errorf("%#v", err), map[string]string{})
		}
	}
}

func BotGuildOnPresenceUpdate(session *discordgo.Session, presence *discordgo.PresenceUpdate) {
	if presence.GuildID == "" {
		return
	}

	member, err := helpers.GetGuildMemberWithoutApi(presence.GuildID, presence.User.ID)
	if err != nil {
		if strings.Contains(err.Error(), "Member not found") {
			return
		}
		raven.CaptureError(fmt.Errorf("%#v", err), map[string]string{})
		return
	}

	change := false
	if presence.User.Avatar != "" {
		member.User.Avatar = presence.User.Avatar
		change = true
	}
	if presence.User.Discriminator != "" {
		member.User.Discriminator = presence.User.Discriminator
		change = true
	}
	if presence.User.Email != "" {
		member.User.Email = presence.User.Email
		change = true
	}
	if presence.User.Token != "" {
		member.User.Token = presence.User.Token
		change = true
	}
	if presence.User.Username != "" {
		member.User.Username = presence.User.Username
		change = true
	}

	if change == true {
		err = session.State.MemberAdd(member)
		if err != nil {
			raven.CaptureError(fmt.Errorf("%#v", err), map[string]string{})
		}
	}
}

func BotOnGuildMemberAdd(session *discordgo.Session, member *discordgo.GuildMemberAdd) {
	modules.CallExtendedPluginOnGuildMemberAdd(
		member.Member,
	)
}

func BotOnGuildMemberRemove(session *discordgo.Session, member *discordgo.GuildMemberRemove) {
	modules.CallExtendedPluginOnGuildMemberRemove(
		member.Member,
	)
}

func BotOnGuildBanAdd(session *discordgo.Session, user *discordgo.GuildBanAdd) {
	modules.CallExtendedPluginOnGuildBanAdd(
		user,
	)
}

func BotOnGuildBanRemove(session *discordgo.Session, user *discordgo.GuildBanRemove) {
	modules.CallExtendedPluginOnGuildBanRemove(
		user,
	)
}

// BotOnMessageCreate gets called after a new message was sent
// This will be called after *every* message on *every* server so it should die as soon as possible
// or spawn costly work inside of coroutines.
func BotOnMessageCreate(session *discordgo.Session, message *discordgo.MessageCreate) {
	// Ignore other bots and @everyone/@here
	if message.Author.Bot || message.MentionEveryone {
		return
	}

	if helpers.IsBlacklisted(message.Author.ID) {
		return
	}

	// Get the channel
	// Ignore the event if we cannot resolve the channel
	channel, err := helpers.GetChannel(message.ChannelID)
	if err != nil {
		go raven.CaptureError(err, map[string]string{})
		return
	}

	/*
		if channel.Type == discordgo.ChannelTypeDM {
			// Track usage
			metrics.CleverbotRequests.Add(1)

			// Mark typing
			session.ChannelTyping(message.ChannelID)

			// Prepare content for editing
			msg := message.Content

			/// Remove our @mention
			msg = strings.Replace(msg, "<@"+session.State.User.ID+">", "", -1)

			// Trim message
			msg = strings.TrimSpace(msg)

			// Resolve other @mentions before sending the message
			for _, user := range message.Mentions {
				msg = strings.Replace(msg, "<@"+user.ID+">", user.Username, -1)
			}

			// Remove smileys
			msg = regexp.MustCompile(`:\w+:`).ReplaceAllString(msg, "")

			// Send to cleverbot
			helpers.CleverbotSend(session, channel.ID, msg)
			return
		}*/

	// Check if the message contains @mentions for us
	if strings.HasPrefix(message.Content, "<@") && len(message.Mentions) > 0 && message.Mentions[0].ID == session.State.User.ID {
		// Consume a key for this action
		e := ratelimits.Container.Drain(1, message.Author.ID)
		if e != nil {
			return
		}

		// Prepare content for editing
		msg := message.Content

		/// Remove our @mention
		msg = strings.Replace(msg, "<@"+session.State.User.ID+">", "", -1)

		// Trim message
		msg = strings.TrimSpace(msg)

		// Convert to []byte before matching
		bmsg := []byte(msg)

		// Match against common task patterns
		// Send to cleverbot if nothing matches
		switch {
		case regexp.MustCompile("(?i)^HELP.*").Match(bmsg):
			metrics.CommandsExecuted.Add(1)
			sendHelp(message)
			return

		case regexp.MustCompile("(?i)^PREFIX.*").Match(bmsg):
			metrics.CommandsExecuted.Add(1)
			prefix := helpers.GetPrefixForServer(channel.GuildID)
			if prefix == "" {
				cache.GetSession().ChannelMessageSend(
					channel.ID,
					helpers.GetText("bot.prefix.not-set"),
				)
			}

			cache.GetSession().ChannelMessageSend(
				channel.ID,
				helpers.GetTextF("bot.prefix.is", prefix),
			)
			return

			/*
				case regexp.MustCompile("(?i)^REFRESH CHAT SESSION$").Match(bmsg):
					metrics.CommandsExecuted.Add(1)
					helpers.RequireAdmin(message.Message, func() {
						// Refresh cleverbot session
						helpers.CleverbotRefreshSession(channel.ID)
						cache.GetSession().ChannelMessageSend(channel.ID, helpers.GetText("bot.cleverbot.refreshed"))
					})
					return*/

		case regexp.MustCompile("(?i)^SET PREFIX (.){1,25}$").Match(bmsg):
			metrics.CommandsExecuted.Add(1)
			helpers.RequireAdmin(message.Message, func() {
				// Extract prefix
				prefix := strings.Fields(regexp.MustCompile("(?i)^SET PREFIX\\s").ReplaceAllString(msg, ""))[0]

				// Set new prefix
				err := helpers.SetPrefixForServer(
					channel.GuildID,
					prefix,
				)

				if err != nil {
					helpers.SendError(message.Message, err)
				} else {
					cache.GetSession().ChannelMessageSend(channel.ID, helpers.GetTextF("bot.prefix.saved", prefix))
				}
			})
			return

			/*
				default:
					// Track usage
					metrics.CleverbotRequests.Add(1)

					// Mark typing
					session.ChannelTyping(message.ChannelID)

					// Resolve other @mentions before sending the message
					for _, user := range message.Mentions {
						msg = strings.Replace(msg, "<@"+user.ID+">", user.Username, -1)
					}

					// Remove smileys
					msg = regexp.MustCompile(`:\w+:`).ReplaceAllString(msg, "")

					// Send to cleverbot
					helpers.CleverbotSend(session, channel.ID, msg)
					return*/
		}
	}

	modules.CallExtendedPlugin(
		message.Content,
		message.Message,
	)

	// Only continue if a prefix is set
	prefix := helpers.GetPrefixForServer(channel.GuildID)
	if prefix == "" {
		return
	}

	// Check if the message is prefixed for us
	// If not exit
	if !strings.HasPrefix(message.Content, prefix) {
		return
	}

	// Check if the user is allowed to request commands
	if !ratelimits.Container.HasKeys(message.Author.ID) && !helpers.IsBotAdmin(message.Author.ID) {
		session.ChannelMessageSend(message.ChannelID, helpers.GetTextF("bot.ratelimit.hit", message.Author.ID))

		ratelimits.Container.Set(message.Author.ID, -1)
		return
	}

	// Split the message into parts
	parts := strings.Fields(message.Content)

	// Save a sanitized version of the command (no prefix)
	cmd := strings.Replace(parts[0], prefix, "", 1)

	// Check if the user calls for help
	if cmd == "h" || cmd == "help" {
		metrics.CommandsExecuted.Add(1)
		sendHelp(message)
		return
	}

	// Separate arguments from the command
	content := strings.TrimSpace(strings.Replace(message.Content, prefix+cmd, "", -1))

	// Log commands
	// TODO: add guild id to log
	cache.GetLogger().WithField("module", "bot").Debug(fmt.Sprintf("%s (#%s): %s",
		message.Author.Username, message.Author.ID, message.Content))

	// Check if a module matches said command
	modules.CallBotPlugin(cmd, content, message.Message)

	// Check if a trigger matches
	modules.CallTriggerPlugin(cmd, content, message.Message)
}

func BotOnMessageDelete(session *discordgo.Session, message *discordgo.MessageDelete) {
	modules.CallExtendedPluginOnMessageDelete(message)
}

// BotOnReactionAdd gets called after a reaction is added
// This will be called after *every* reaction added on *every* server so it
// should die as soon as possible or spawn costly work inside of coroutines.
// This is currently used for the *poll* plugin.
func BotOnReactionAdd(session *discordgo.Session, reaction *discordgo.MessageReactionAdd) {
	modules.CallExtendedPluginOnReactionAdd(reaction)

	if reaction.UserID == session.State.User.ID {
		return
	}

	channel, err := helpers.GetChannel(reaction.ChannelID)
	if err != nil {
		return
	}
	if emojis.ToNumber(reaction.Emoji.Name) == -1 {
		//session.MessageReactionRemove(reaction.ChannelID, reaction.MessageID, reaction.Emoji.Name, reaction.UserID)
		return
	}
	if helpers.VotePollIfItsOne(channel.GuildID, reaction.MessageReaction) {
		helpers.UpdatePollMsg(channel.GuildID, reaction.MessageID)
	}

}

func BotOnReactionRemove(session *discordgo.Session, reaction *discordgo.MessageReactionRemove) {
	modules.CallExtendedPluginOnReactionRemove(reaction)
}

func sendHelp(message *discordgo.MessageCreate) {
	channel, err := helpers.GetChannel(message.ChannelID)
	if err != nil {
		channel.GuildID = ""
	}

	cache.GetSession().ChannelMessageSend(
		message.ChannelID,
		helpers.GetTextF("bot.help", message.Author.ID, channel.GuildID),
	)
}

// Changes the game interval every ten minutes after called
func changeGameInterval(session *discordgo.Session) {
	for {
		users := make(map[string]string)
		guilds := session.State.Guilds

		for _, guild := range guilds {
			for _, u := range guild.Members {
				users[u.User.ID] = u.User.Username
			}
		}

		err := session.UpdateStatus(0, fmt.Sprintf("%d users on %d servers | robyul.chat | _help", len(users), len(guilds)))
		if err != nil {
			raven.CaptureError(err, map[string]string{})
		}

		time.Sleep(1 * time.Hour)
	}
}
<commit_msg>[core] improves member presence update<commit_after>package main

import (
	"fmt"
	"regexp"
	"strings"
	"time"

	"github.com/Seklfreak/Robyul2/cache"
	"github.com/Seklfreak/Robyul2/emojis"
	"github.com/Seklfreak/Robyul2/helpers"
	"github.com/Seklfreak/Robyul2/metrics"
	"github.com/Seklfreak/Robyul2/modules"
	"github.com/Seklfreak/Robyul2/ratelimits"
	"github.com/bwmarrin/discordgo"
	"github.com/getsentry/raven-go"
)

// BotOnReady gets called after the gateway connected
func BotOnReady(session *discordgo.Session, event *discordgo.Ready) {
	log := cache.GetLogger()

	log.WithField("module", "bot").Info("Connected to discord!")
	log.WithField("module", "bot").Info("Invite link: " + fmt.Sprintf(
		"https://discordapp.com/oauth2/authorize?client_id=%s&scope=bot&permissions=%s",
		helpers.GetConfig().Path("discord.id").Data().(string),
		helpers.GetConfig().Path("discord.perms").Data().(string),
	))

	// Cache the session
	cache.SetSession(session)

	// Load and init all modules
	modules.Init(session)

	// Run async worker for guild changes
	go helpers.GuildSettingsUpdater()

	// Run async game-changer
	go changeGameInterval(session)

	// request guild members from the gateway
	go func() {
		time.Sleep(30 * time.Second)

		for _, guild := range session.State.Guilds {
			err := session.RequestGuildMembers(guild.ID, "", 0)
			if err != nil {
				raven.CaptureError(fmt.Errorf("%#v", err), map[string]string{})
			}
		}
	}()

	// Run auto-leaver for non-beta guilds
	//go autoLeaver(session)

	// Run ratelimiter
	ratelimits.Container.Init()

	go func() {
		time.Sleep(3 * time.Second)

		configName := helpers.GetConfig().Path("bot.name").Data().(string)
		configAvatar := helpers.GetConfig().Path("bot.avatar").Data().(string)

		// Change avatar if desired
		if configAvatar != "" && configAvatar != session.State.User.Avatar {
			session.UserUpdate(
				"",
				"",
				session.State.User.Username,
				configAvatar,
				"",
			)
		}

		// Change name if desired
		if configName != "" && configName != session.State.User.Username {
			session.UserUpdate(
				"",
				"",
				configName,
				session.State.User.Avatar,
				"",
			)
		}
	}()

	// Run async game-changer
	//go changeGameInterval(session)

	// Run auto-leaver for non-beta guilds
	//go autoLeaver(session)
}

func BotOnMemberListChunk(session *discordgo.Session, members *discordgo.GuildMembersChunk) {
	cache.GetLogger().WithField("module", "bot").Debug(
		fmt.Sprintf("received guild member chunk for guild: %s (%d members)",
			members.GuildID, len(members.Members)))
	var err error
	for _, member := range members.Members {
		member.GuildID = members.GuildID
		err = session.State.MemberAdd(member)
		if err != nil {
			raven.CaptureError(fmt.Errorf("%#v", err), map[string]string{})
		}
	}
}

func BotGuildOnPresenceUpdate(session *discordgo.Session, presence *discordgo.PresenceUpdate) {
	if presence.GuildID == "" {
		return
	}

	member, err := cache.GetSession().State.Member(presence.GuildID, presence.User.ID)
	if err != nil {
		if strings.Contains(err.Error(), "state cache not found") {
			return
		}
		raven.CaptureError(fmt.Errorf("%#v", err), map[string]string{})
		return
	}

	change := false
	if presence.User.Avatar != "" {
		member.User.Avatar = presence.User.Avatar
		change = true
	}
	if presence.User.Discriminator != "" {
		member.User.Discriminator = presence.User.Discriminator
		change = true
	}
	if presence.User.Email != "" {
		member.User.Email = presence.User.Email
		change = true
	}
	if presence.User.Token != "" {
		member.User.Token = presence.User.Token
		change = true
	}
	if presence.User.Username != "" {
		member.User.Username = presence.User.Username
		change = true
	}

	if change == true {
		err = session.State.MemberAdd(member)
		if err != nil {
			raven.CaptureError(fmt.Errorf("%#v", err), map[string]string{})
		}
	}
}

func BotOnGuildMemberAdd(session *discordgo.Session, member *discordgo.GuildMemberAdd) {
	modules.CallExtendedPluginOnGuildMemberAdd(
		member.Member,
	)
}

func BotOnGuildMemberRemove(session *discordgo.Session, member *discordgo.GuildMemberRemove) {
	modules.CallExtendedPluginOnGuildMemberRemove(
		member.Member,
	)
}

func BotOnGuildBanAdd(session *discordgo.Session, user *discordgo.GuildBanAdd) {
	modules.CallExtendedPluginOnGuildBanAdd(
		user,
	)
}

func BotOnGuildBanRemove(session *discordgo.Session, user *discordgo.GuildBanRemove) {
	modules.CallExtendedPluginOnGuildBanRemove(
		user,
	)
}

// BotOnMessageCreate gets called after a new message was sent
// This will be called after *every* message on *every* server so it should die as soon as possible
// or spawn costly work inside of coroutines.
func BotOnMessageCreate(session *discordgo.Session, message *discordgo.MessageCreate) {
	// Ignore other bots and @everyone/@here
	if message.Author.Bot || message.MentionEveryone {
		return
	}

	if helpers.IsBlacklisted(message.Author.ID) {
		return
	}

	// Get the channel
	// Ignore the event if we cannot resolve the channel
	channel, err := helpers.GetChannel(message.ChannelID)
	if err != nil {
		go raven.CaptureError(err, map[string]string{})
		return
	}

	/*
		if channel.Type == discordgo.ChannelTypeDM {
			// Track usage
			metrics.CleverbotRequests.Add(1)

			// Mark typing
			session.ChannelTyping(message.ChannelID)

			// Prepare content for editing
			msg := message.Content

			/// Remove our @mention
			msg = strings.Replace(msg, "<@"+session.State.User.ID+">", "", -1)

			// Trim message
			msg = strings.TrimSpace(msg)

			// Resolve other @mentions before sending the message
			for _, user := range message.Mentions {
				msg = strings.Replace(msg, "<@"+user.ID+">", user.Username, -1)
			}

			// Remove smileys
			msg = regexp.MustCompile(`:\w+:`).ReplaceAllString(msg, "")

			// Send to cleverbot
			helpers.CleverbotSend(session, channel.ID, msg)
			return
		}*/

	// Check if the message contains @mentions for us
	if strings.HasPrefix(message.Content, "<@") && len(message.Mentions) > 0 && message.Mentions[0].ID == session.State.User.ID {
		// Consume a key for this action
		e := ratelimits.Container.Drain(1, message.Author.ID)
		if e != nil {
			return
		}

		// Prepare content for editing
		msg := message.Content

		/// Remove our @mention
		msg = strings.Replace(msg, "<@"+session.State.User.ID+">", "", -1)

		// Trim message
		msg = strings.TrimSpace(msg)

		// Convert to []byte before matching
		bmsg := []byte(msg)

		// Match against common task patterns
		// Send to cleverbot if nothing matches
		switch {
		case regexp.MustCompile("(?i)^HELP.*").Match(bmsg):
			metrics.CommandsExecuted.Add(1)
			sendHelp(message)
			return

		case regexp.MustCompile("(?i)^PREFIX.*").Match(bmsg):
			metrics.CommandsExecuted.Add(1)
			prefix := helpers.GetPrefixForServer(channel.GuildID)
			if prefix == "" {
				cache.GetSession().ChannelMessageSend(
					channel.ID,
					helpers.GetText("bot.prefix.not-set"),
				)
			}

			cache.GetSession().ChannelMessageSend(
				channel.ID,
				helpers.GetTextF("bot.prefix.is", prefix),
			)
			return

			/*
				case regexp.MustCompile("(?i)^REFRESH CHAT SESSION$").Match(bmsg):
					metrics.CommandsExecuted.Add(1)
					helpers.RequireAdmin(message.Message, func() {
						// Refresh cleverbot session
						helpers.CleverbotRefreshSession(channel.ID)
						cache.GetSession().ChannelMessageSend(channel.ID, helpers.GetText("bot.cleverbot.refreshed"))
					})
					return*/

		case regexp.MustCompile("(?i)^SET PREFIX (.){1,25}$").Match(bmsg):
			metrics.CommandsExecuted.Add(1)
			helpers.RequireAdmin(message.Message, func() {
				// Extract prefix
				prefix := strings.Fields(regexp.MustCompile("(?i)^SET PREFIX\\s").ReplaceAllString(msg, ""))[0]

				// Set new prefix
				err := helpers.SetPrefixForServer(
					channel.GuildID,
					prefix,
				)

				if err != nil {
					helpers.SendError(message.Message, err)
				} else {
					cache.GetSession().ChannelMessageSend(channel.ID, helpers.GetTextF("bot.prefix.saved", prefix))
				}
			})
			return

			/*
				default:
					// Track usage
					metrics.CleverbotRequests.Add(1)

					// Mark typing
					session.ChannelTyping(message.ChannelID)

					// Resolve other @mentions before sending the message
					for _, user := range message.Mentions {
						msg = strings.Replace(msg, "<@"+user.ID+">", user.Username, -1)
					}

					// Remove smileys
					msg = regexp.MustCompile(`:\w+:`).ReplaceAllString(msg, "")

					// Send to cleverbot
					helpers.CleverbotSend(session, channel.ID, msg)
					return*/
		}
	}

	modules.CallExtendedPlugin(
		message.Content,
		message.Message,
	)

	// Only continue if a prefix is set
	prefix := helpers.GetPrefixForServer(channel.GuildID)
	if prefix == "" {
		return
	}

	// Check if the message is prefixed for us
	// If not exit
	if !strings.HasPrefix(message.Content, prefix) {
		return
	}

	// Check if the user is allowed to request commands
	if !ratelimits.Container.HasKeys(message.Author.ID) && !helpers.IsBotAdmin(message.Author.ID) {
		session.ChannelMessageSend(message.ChannelID, helpers.GetTextF("bot.ratelimit.hit", message.Author.ID))

		ratelimits.Container.Set(message.Author.ID, -1)
		return
	}

	// Split the message into parts
	parts := strings.Fields(message.Content)

	// Save a sanitized version of the command (no prefix)
	cmd := strings.Replace(parts[0], prefix, "", 1)

	// Check if the user calls for help
	if cmd == "h" || cmd == "help" {
		metrics.CommandsExecuted.Add(1)
		sendHelp(message)
		return
	}

	// Separate arguments from the command
	content := strings.TrimSpace(strings.Replace(message.Content, prefix+cmd, "", -1))

	// Log commands
	// TODO: add guild id to log
	cache.GetLogger().WithField("module", "bot").Debug(fmt.Sprintf("%s (#%s): %s",
		message.Author.Username, message.Author.ID, message.Content))

	// Check if a module matches said command
	modules.CallBotPlugin(cmd, content, message.Message)

	// Check if a trigger matches
	modules.CallTriggerPlugin(cmd, content, message.Message)
}

func BotOnMessageDelete(session *discordgo.Session, message *discordgo.MessageDelete) {
	modules.CallExtendedPluginOnMessageDelete(message)
}

// BotOnReactionAdd gets called after a reaction is added
// This will be called after *every* reaction added on *every* server so it
// should die as soon as possible or spawn costly work inside of coroutines.
// This is currently used for the *poll* plugin.
func BotOnReactionAdd(session *discordgo.Session, reaction *discordgo.MessageReactionAdd) {
	modules.CallExtendedPluginOnReactionAdd(reaction)

	if reaction.UserID == session.State.User.ID {
		return
	}

	channel, err := helpers.GetChannel(reaction.ChannelID)
	if err != nil {
		return
	}
	if emojis.ToNumber(reaction.Emoji.Name) == -1 {
		//session.MessageReactionRemove(reaction.ChannelID, reaction.MessageID, reaction.Emoji.Name, reaction.UserID)
		return
	}
	if helpers.VotePollIfItsOne(channel.GuildID, reaction.MessageReaction) {
		helpers.UpdatePollMsg(channel.GuildID, reaction.MessageID)
	}

}

func BotOnReactionRemove(session *discordgo.Session, reaction *discordgo.MessageReactionRemove) {
	modules.CallExtendedPluginOnReactionRemove(reaction)
}

func sendHelp(message *discordgo.MessageCreate) {
	channel, err := helpers.GetChannel(message.ChannelID)
	if err != nil {
		channel.GuildID = ""
	}

	cache.GetSession().ChannelMessageSend(
		message.ChannelID,
		helpers.GetTextF("bot.help", message.Author.ID, channel.GuildID),
	)
}

// Changes the game interval every ten minutes after called
func changeGameInterval(session *discordgo.Session) {
	for {
		users := make(map[string]string)
		guilds := session.State.Guilds

		for _, guild := range guilds {
			for _, u := range guild.Members {
				users[u.User.ID] = u.User.Username
			}
		}

		err := session.UpdateStatus(0, fmt.Sprintf("%d users on %d servers | robyul.chat | _help", len(users), len(guilds)))
		if err != nil {
			raven.CaptureError(err, map[string]string{})
		}

		time.Sleep(1 * time.Hour)
	}
}
<|endoftext|>