<commit_before>// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.

package api

import (
	"bytes"
	l4g "code.google.com/p/log4go"
	"fmt"
	"github.com/goamz/goamz/aws"
	"github.com/goamz/goamz/s3"
	"github.com/gorilla/mux"
	"github.com/mattermost/platform/model"
	"github.com/mattermost/platform/utils"
	"github.com/nfnt/resize"
	_ "golang.org/x/image/bmp"
	"image"
	_ "image/gif"
	"image/jpeg"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"
)

func InitFile(r *mux.Router) {
	l4g.Debug("Initializing file api routes")

	sr := r.PathPrefix("/files").Subrouter()
	sr.Handle("/upload", ApiUserRequired(uploadFile)).Methods("POST")
	sr.Handle("/get/{channel_id:[A-Za-z0-9]+}/{user_id:[A-Za-z0-9]+}/{filename:([A-Za-z0-9]+/)?.+\\.[A-Za-z0-9]{3,}}", ApiAppHandler(getFile)).Methods("GET")
	sr.Handle("/get_public_link", ApiUserRequired(getPublicLink)).Methods("POST")
}

func uploadFile(c *Context, w http.ResponseWriter, r *http.Request) {
	if !utils.IsS3Configured() && !utils.Cfg.ServiceSettings.UseLocalStorage {
		c.Err = model.NewAppError("uploadFile", "Unable to upload file. Amazon S3 not configured and local server storage turned off. ", "")
		c.Err.StatusCode = http.StatusNotImplemented
		return
	}

	err := r.ParseMultipartForm(model.MAX_FILE_SIZE)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	m := r.MultipartForm

	props := m.Value

	if len(props["channel_id"]) == 0 {
		c.SetInvalidParam("uploadFile", "channel_id")
		return
	}
	channelId := props["channel_id"][0]
	if len(channelId) == 0 {
		c.SetInvalidParam("uploadFile", "channel_id")
		return
	}

	cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, channelId, c.Session.UserId)

	files := m.File["files"]

	resStruct := &model.FileUploadResponse{
		Filenames: []string{}}

	imageNameList := []string{}
	imageDataList := [][]byte{}

	if !c.HasPermissionsToChannel(cchan, "uploadFile") {
		return
	}

	for i, _ := range files {
		file, err := files[i].Open()
		defer file.Close()
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		buf := bytes.NewBuffer(nil)
		io.Copy(buf, file)

		filename := filepath.Base(files[i].Filename)

		uid := model.NewId()

		path := "teams/" + c.Session.TeamId + "/channels/" + channelId + "/users/" + c.Session.UserId + "/" + uid + "/" + filename

		if err := writeFile(buf.Bytes(), path); err != nil {
			c.Err = err
			return
		}

		if model.IsFileExtImage(filepath.Ext(files[i].Filename)) {
			imageNameList = append(imageNameList, uid+"/"+filename)
			imageDataList = append(imageDataList, buf.Bytes())
		}

		encName := utils.UrlEncode(filename)

		fileUrl := "/" + channelId + "/" + c.Session.UserId + "/" + uid + "/" + encName
		resStruct.Filenames = append(resStruct.Filenames, fileUrl)
	}

	fireAndForgetHandleImages(imageNameList, imageDataList, c.Session.TeamId, channelId, c.Session.UserId)

	w.Write([]byte(resStruct.ToJson()))
}

func fireAndForgetHandleImages(filenames []string, fileData [][]byte, teamId, channelId, userId string) {

	go func() {
		dest := "teams/" + teamId + "/channels/" + channelId + "/users/" + userId + "/"

		for i, filename := range filenames {
			name := filename[:strings.LastIndex(filename, ".")]
			go func() {
				// Decode image bytes into Image object
				img, _, err := image.Decode(bytes.NewReader(fileData[i]))
				if err != nil {
					l4g.Error("Unable to decode image channelId=%v userId=%v filename=%v err=%v", channelId, userId, filename, err)
					return
				}

				// Decode image config
				imgConfig, _, err := image.DecodeConfig(bytes.NewReader(fileData[i]))
				if err != nil {
					l4g.Error("Unable to decode image config channelId=%v userId=%v filename=%v err=%v", channelId, userId, filename, err)
					return
				}

				// Create thumbnail
				go func() {
					var thumbnail image.Image
					if imgConfig.Width > int(utils.Cfg.ImageSettings.ThumbnailWidth) {
						thumbnail = resize.Resize(utils.Cfg.ImageSettings.ThumbnailWidth, utils.Cfg.ImageSettings.ThumbnailHeight, img, resize.NearestNeighbor)
					} else {
						thumbnail = img
					}

					buf := new(bytes.Buffer)
					err = jpeg.Encode(buf, thumbnail, &jpeg.Options{Quality: 90})
					if err != nil {
						l4g.Error("Unable to encode image as jpeg channelId=%v userId=%v filename=%v err=%v", channelId, userId, filename, err)
						return
					}

					if err := writeFile(buf.Bytes(), dest+name+"_thumb.jpg"); err != nil {
						l4g.Error("Unable to upload thumbnail channelId=%v userId=%v filename=%v err=%v", channelId, userId, filename, err)
						return
					}
				}()

				// Create preview
				go func() {
					var preview image.Image
					if imgConfig.Width > int(utils.Cfg.ImageSettings.PreviewWidth) {
						preview = resize.Resize(utils.Cfg.ImageSettings.PreviewWidth, utils.Cfg.ImageSettings.PreviewHeight, img, resize.NearestNeighbor)
					} else {
						preview = img
					}

					buf := new(bytes.Buffer)

					err = jpeg.Encode(buf, preview, &jpeg.Options{Quality: 90})
					if err != nil {
						l4g.Error("Unable to encode image as preview jpg channelId=%v userId=%v filename=%v err=%v", channelId, userId, filename, err)
						return
					}

					if err := writeFile(buf.Bytes(), dest+name+"_preview.jpg"); err != nil {
						l4g.Error("Unable to upload preview channelId=%v userId=%v filename=%v err=%v", channelId, userId, filename, err)
						return
					}
				}()
			}()
		}
	}()
}

type ImageGetResult struct {
	Error     error
	ImageData []byte
}

func getFile(c *Context, w http.ResponseWriter, r *http.Request) {
	if !utils.IsS3Configured() && !utils.Cfg.ServiceSettings.UseLocalStorage {
		c.Err = model.NewAppError("getFile", "Unable to upload file. Amazon S3 not configured and local server storage turned off. ", "")
		c.Err.StatusCode = http.StatusNotImplemented
		return
	}

	params := mux.Vars(r)

	channelId := params["channel_id"]
	if len(channelId) != 26 {
		c.SetInvalidParam("getFile", "channel_id")
		return
	}

	userId := params["user_id"]
	if len(userId) != 26 {
		c.SetInvalidParam("getFile", "user_id")
		return
	}

	filename := params["filename"]
	if len(filename) == 0 {
		c.SetInvalidParam("getFile", "filename")
		return
	}

	hash := r.URL.Query().Get("h")
	data := r.URL.Query().Get("d")
	teamId := r.URL.Query().Get("t")

	cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, channelId, c.Session.UserId)

	path := ""
	if len(teamId) == 26 {
		path = "teams/" + teamId + "/channels/" + channelId + "/users/" + userId + "/" + filename
	} else {
		path = "teams/" + c.Session.TeamId + "/channels/" + channelId + "/users/" + userId + "/" + filename
	}

	fileData := make(chan []byte)
	asyncGetFile(path, fileData)

	if len(hash) > 0 && len(data) > 0 && len(teamId) == 26 {
		if !model.ComparePassword(hash, fmt.Sprintf("%v:%v", data, utils.Cfg.ServiceSettings.PublicLinkSalt)) {
			c.Err = model.NewAppError("getFile", "The public link does not appear to be valid", "")
			return
		}
		props := model.MapFromJson(strings.NewReader(data))

		t, err := strconv.ParseInt(props["time"], 10, 64)
		if err != nil || model.GetMillis()-t > 1000*60*60*24*7 { // one week
			c.Err = model.NewAppError("getFile", "The public link has expired", "")
			return
		}
	} else if !c.HasPermissionsToChannel(cchan, "getFile") {
		return
	}

	f := <-fileData

	if f == nil {
		c.Err = model.NewAppError("getFile", "Could not find file.", "path="+path)
		c.Err.StatusCode = http.StatusNotFound
		return
	}

	w.Header().Set("Cache-Control", "max-age=2592000, public")
	w.Header().Set("Content-Length", strconv.Itoa(len(f)))
	w.Write(f)
}

func asyncGetFile(path string, fileData chan []byte) {
	go func() {
		data, getErr := readFile(path)
		if getErr != nil {
			l4g.Error(getErr)
			fileData <- nil
		} else {
			fileData <- data
		}
	}()
}

func getPublicLink(c *Context, w http.ResponseWriter, r *http.Request) {
	if !utils.Cfg.TeamSettings.AllowPublicLink {
		c.Err = model.NewAppError("getPublicLink", "Public links have been disabled", "")
		c.Err.StatusCode = http.StatusForbidden
	}

	if !utils.IsS3Configured() && !utils.Cfg.ServiceSettings.UseLocalStorage {
		c.Err = model.NewAppError("getPublicLink", "Unable to upload file. Amazon S3 not configured and local server storage turned off. ", "")
		c.Err.StatusCode = http.StatusNotImplemented
		return
	}

	props := model.MapFromJson(r.Body)

	filename := props["filename"]
	if len(filename) == 0 {
		c.SetInvalidParam("getPublicLink", "filename")
		return
	}

	matches := model.PartialUrlRegex.FindAllStringSubmatch(filename, -1)
	if len(matches) == 0 || len(matches[0]) < 4 {
		c.SetInvalidParam("getPublicLink", "filename")
		return
	}

	channelId := matches[0][1]
	userId := matches[0][2]
	filename = matches[0][3]

	cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, channelId, c.Session.UserId)

	newProps := make(map[string]string)
	newProps["filename"] = filename
	newProps["time"] = fmt.Sprintf("%v", model.GetMillis())

	data := model.MapToJson(newProps)
	hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.ServiceSettings.PublicLinkSalt))

	url := fmt.Sprintf("%s/api/v1/files/get/%s/%s/%s?d=%s&h=%s&t=%s", c.GetSiteURL(), channelId, userId, filename, url.QueryEscape(data), url.QueryEscape(hash), c.Session.TeamId)

	if !c.HasPermissionsToChannel(cchan, "getPublicLink") {
		return
	}

	rData := make(map[string]string)
	rData["public_link"] = url

	w.Write([]byte(model.MapToJson(rData)))
}

func writeFile(f []byte, path string) *model.AppError {

	if utils.IsS3Configured() && !utils.Cfg.ServiceSettings.UseLocalStorage {
		var auth aws.Auth
		auth.AccessKey = utils.Cfg.AWSSettings.S3AccessKeyId
		auth.SecretKey = utils.Cfg.AWSSettings.S3SecretAccessKey

		s := s3.New(auth, aws.Regions[utils.Cfg.AWSSettings.S3Region])
		bucket := s.Bucket(utils.Cfg.AWSSettings.S3Bucket)

		ext := filepath.Ext(path)

		var err error
		if model.IsFileExtImage(ext) {
			options := s3.Options{}
			err = bucket.Put(path, f, model.GetImageMimeType(ext), s3.Private, options)

		} else {
			options := s3.Options{}
			err = bucket.Put(path, f, "binary/octet-stream", s3.Private, options)
		}

		if err != nil {
			return model.NewAppError("writeFile", "Encountered an error writing to S3", err.Error())
		}
	} else if utils.Cfg.ServiceSettings.UseLocalStorage && len(utils.Cfg.ServiceSettings.StorageDirectory) > 0 {
		if err := os.MkdirAll(filepath.Dir(utils.Cfg.ServiceSettings.StorageDirectory+path), 0774); err != nil {
			return model.NewAppError("writeFile", "Encountered an error creating the directory for the new file", err.Error())
		}

		if err := ioutil.WriteFile(utils.Cfg.ServiceSettings.StorageDirectory+path, f, 0644); err != nil {
			return model.NewAppError("writeFile", "Encountered an error writing to local server storage", err.Error())
		}
	} else {
		return model.NewAppError("writeFile", "File storage not configured properly. Please configure for either S3 or local server file storage.", "")
	}

	return nil
}

func readFile(path string) ([]byte, *model.AppError) {

	if utils.IsS3Configured() && !utils.Cfg.ServiceSettings.UseLocalStorage {
		var auth aws.Auth
		auth.AccessKey = utils.Cfg.AWSSettings.S3AccessKeyId
		auth.SecretKey = utils.Cfg.AWSSettings.S3SecretAccessKey

		s := s3.New(auth, aws.Regions[utils.Cfg.AWSSettings.S3Region])
		bucket := s.Bucket(utils.Cfg.AWSSettings.S3Bucket)

		// try to get the file from S3 with some basic retry logic
		tries := 0
		for {
			tries++

			f, err := bucket.Get(path)

			if f != nil {
				return f, nil
			} else if tries >= 3 {
				return nil, model.NewAppError("readFile", "Unable to get file from S3", "path="+path+", err="+err.Error())
			}
			time.Sleep(3000 * time.Millisecond)
		}
	} else if utils.Cfg.ServiceSettings.UseLocalStorage && len(utils.Cfg.ServiceSettings.StorageDirectory) > 0 {
		if f, err := ioutil.ReadFile(utils.Cfg.ServiceSettings.StorageDirectory + path); err != nil {
			return nil, model.NewAppError("readFile", "Encountered an error reading from local server storage", err.Error())
		} else {
			return f, nil
		}
	} else {
		return nil, model.NewAppError("readFile", "File storage not configured properly. Please configure for either S3 or local server file storage.", "")
	}
}
<commit_msg>Change image rescaling algorithm back to Lanczos3 now that it works correctly in all cases<commit_after>// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.

package api

import (
	"bytes"
	l4g "code.google.com/p/log4go"
	"fmt"
	"github.com/goamz/goamz/aws"
	"github.com/goamz/goamz/s3"
	"github.com/gorilla/mux"
	"github.com/mattermost/platform/model"
	"github.com/mattermost/platform/utils"
	"github.com/nfnt/resize"
	_ "golang.org/x/image/bmp"
	"image"
	_ "image/gif"
	"image/jpeg"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"
)

func InitFile(r *mux.Router) {
	l4g.Debug("Initializing file api routes")

	sr := r.PathPrefix("/files").Subrouter()
	sr.Handle("/upload", ApiUserRequired(uploadFile)).Methods("POST")
	sr.Handle("/get/{channel_id:[A-Za-z0-9]+}/{user_id:[A-Za-z0-9]+}/{filename:([A-Za-z0-9]+/)?.+\\.[A-Za-z0-9]{3,}}", ApiAppHandler(getFile)).Methods("GET")
	sr.Handle("/get_public_link", ApiUserRequired(getPublicLink)).Methods("POST")
}

func uploadFile(c *Context, w http.ResponseWriter, r *http.Request) {
	if !utils.IsS3Configured() && !utils.Cfg.ServiceSettings.UseLocalStorage {
		c.Err = model.NewAppError("uploadFile", "Unable to upload file. Amazon S3 not configured and local server storage turned off. ", "")
		c.Err.StatusCode = http.StatusNotImplemented
		return
	}

	err := r.ParseMultipartForm(model.MAX_FILE_SIZE)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	m := r.MultipartForm

	props := m.Value

	if len(props["channel_id"]) == 0 {
		c.SetInvalidParam("uploadFile", "channel_id")
		return
	}
	channelId := props["channel_id"][0]
	if len(channelId) == 0 {
		c.SetInvalidParam("uploadFile", "channel_id")
		return
	}

	cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, channelId, c.Session.UserId)

	files := m.File["files"]

	resStruct := &model.FileUploadResponse{
		Filenames: []string{}}

	imageNameList := []string{}
	imageDataList := [][]byte{}

	if !c.HasPermissionsToChannel(cchan, "uploadFile") {
		return
	}

	for i, _ := range files {
		file, err := files[i].Open()
		defer file.Close()
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		buf := bytes.NewBuffer(nil)
		io.Copy(buf, file)

		filename := filepath.Base(files[i].Filename)

		uid := model.NewId()

		path := "teams/" + c.Session.TeamId + "/channels/" + channelId + "/users/" + c.Session.UserId + "/" + uid + "/" + filename

		if err := writeFile(buf.Bytes(), path); err != nil {
			c.Err = err
			return
		}

		if model.IsFileExtImage(filepath.Ext(files[i].Filename)) {
			imageNameList = append(imageNameList, uid+"/"+filename)
			imageDataList = append(imageDataList, buf.Bytes())
		}

		encName := utils.UrlEncode(filename)

		fileUrl := "/" + channelId + "/" + c.Session.UserId + "/" + uid + "/" + encName
		resStruct.Filenames = append(resStruct.Filenames, fileUrl)
	}

	fireAndForgetHandleImages(imageNameList, imageDataList, c.Session.TeamId, channelId, c.Session.UserId)

	w.Write([]byte(resStruct.ToJson()))
}

func fireAndForgetHandleImages(filenames []string, fileData [][]byte, teamId, channelId, userId string) {

	go func() {
		dest := "teams/" + teamId + "/channels/" + channelId + "/users/" + userId + "/"

		for i, filename := range filenames {
			name := filename[:strings.LastIndex(filename, ".")]
			go func() {
				// Decode image bytes into Image object
				img, _, err := image.Decode(bytes.NewReader(fileData[i]))
				if err != nil {
					l4g.Error("Unable to decode image channelId=%v userId=%v filename=%v err=%v", channelId, userId, filename, err)
					return
				}

				// Decode image config
				imgConfig, _, err := image.DecodeConfig(bytes.NewReader(fileData[i]))
				if err != nil {
					l4g.Error("Unable to decode image config channelId=%v userId=%v filename=%v err=%v", channelId, userId, filename, err)
					return
				}

				// Create thumbnail
				go func() {
					var thumbnail image.Image
					if imgConfig.Width > int(utils.Cfg.ImageSettings.ThumbnailWidth) {
						thumbnail = resize.Resize(utils.Cfg.ImageSettings.ThumbnailWidth, utils.Cfg.ImageSettings.ThumbnailHeight, img, resize.Lanczos3)
					} else {
						thumbnail = img
					}

					buf := new(bytes.Buffer)
					err = jpeg.Encode(buf, thumbnail, &jpeg.Options{Quality: 90})
					if err != nil {
						l4g.Error("Unable to encode image as jpeg channelId=%v userId=%v filename=%v err=%v", channelId, userId, filename, err)
						return
					}

					if err := writeFile(buf.Bytes(), dest+name+"_thumb.jpg"); err != nil {
						l4g.Error("Unable to upload thumbnail channelId=%v userId=%v filename=%v err=%v", channelId, userId, filename, err)
						return
					}
				}()

				// Create preview
				go func() {
					var preview image.Image
					if imgConfig.Width > int(utils.Cfg.ImageSettings.PreviewWidth) {
						preview = resize.Resize(utils.Cfg.ImageSettings.PreviewWidth, utils.Cfg.ImageSettings.PreviewHeight, img, resize.Lanczos3)
					} else {
						preview = img
					}

					buf := new(bytes.Buffer)

					err = jpeg.Encode(buf, preview, &jpeg.Options{Quality: 90})
					if err != nil {
						l4g.Error("Unable to encode image as preview jpg channelId=%v userId=%v filename=%v err=%v", channelId, userId, filename, err)
						return
					}

					if err := writeFile(buf.Bytes(), dest+name+"_preview.jpg"); err != nil {
						l4g.Error("Unable to upload preview channelId=%v userId=%v filename=%v err=%v", channelId, userId, filename, err)
						return
					}
				}()
			}()
		}
	}()
}

type ImageGetResult struct {
	Error     error
	ImageData []byte
}

func getFile(c *Context, w http.ResponseWriter, r *http.Request) {
	if !utils.IsS3Configured() && !utils.Cfg.ServiceSettings.UseLocalStorage {
		c.Err = model.NewAppError("getFile", "Unable to upload file. Amazon S3 not configured and local server storage turned off. ", "")
		c.Err.StatusCode = http.StatusNotImplemented
		return
	}

	params := mux.Vars(r)

	channelId := params["channel_id"]
	if len(channelId) != 26 {
		c.SetInvalidParam("getFile", "channel_id")
		return
	}

	userId := params["user_id"]
	if len(userId) != 26 {
		c.SetInvalidParam("getFile", "user_id")
		return
	}

	filename := params["filename"]
	if len(filename) == 0 {
		c.SetInvalidParam("getFile", "filename")
		return
	}

	hash := r.URL.Query().Get("h")
	data := r.URL.Query().Get("d")
	teamId := r.URL.Query().Get("t")

	cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, channelId, c.Session.UserId)

	path := ""
	if len(teamId) == 26 {
		path = "teams/" + teamId + "/channels/" + channelId + "/users/" + userId + "/" + filename
	} else {
		path = "teams/" + c.Session.TeamId + "/channels/" + channelId + "/users/" + userId + "/" + filename
	}

	fileData := make(chan []byte)
	asyncGetFile(path, fileData)

	if len(hash) > 0 && len(data) > 0 && len(teamId) == 26 {
		if !model.ComparePassword(hash, fmt.Sprintf("%v:%v", data, utils.Cfg.ServiceSettings.PublicLinkSalt)) {
			c.Err = model.NewAppError("getFile", "The public link does not appear to be valid", "")
			return
		}
		props := model.MapFromJson(strings.NewReader(data))

		t, err := strconv.ParseInt(props["time"], 10, 64)
		if err != nil || model.GetMillis()-t > 1000*60*60*24*7 { // one week
			c.Err = model.NewAppError("getFile", "The public link has expired", "")
			return
		}
	} else if !c.HasPermissionsToChannel(cchan, "getFile") {
		return
	}

	f := <-fileData

	if f == nil {
		c.Err = model.NewAppError("getFile", "Could not find file.", "path="+path)
		c.Err.StatusCode = http.StatusNotFound
		return
	}

	w.Header().Set("Cache-Control", "max-age=2592000, public")
	w.Header().Set("Content-Length", strconv.Itoa(len(f)))
	w.Write(f)
}

func asyncGetFile(path string, fileData chan []byte) {
	go func() {
		data, getErr := readFile(path)
		if getErr != nil {
			l4g.Error(getErr)
			fileData <- nil
		} else {
			fileData <- data
		}
	}()
}

func getPublicLink(c *Context, w http.ResponseWriter, r *http.Request) {
	if !utils.Cfg.TeamSettings.AllowPublicLink {
		c.Err = model.NewAppError("getPublicLink", "Public links have been disabled", "")
		c.Err.StatusCode = http.StatusForbidden
	}

	if !utils.IsS3Configured() && !utils.Cfg.ServiceSettings.UseLocalStorage {
		c.Err = model.NewAppError("getPublicLink", "Unable to upload file. Amazon S3 not configured and local server storage turned off. ", "")
		c.Err.StatusCode = http.StatusNotImplemented
		return
	}

	props := model.MapFromJson(r.Body)

	filename := props["filename"]
	if len(filename) == 0 {
		c.SetInvalidParam("getPublicLink", "filename")
		return
	}

	matches := model.PartialUrlRegex.FindAllStringSubmatch(filename, -1)
	if len(matches) == 0 || len(matches[0]) < 4 {
		c.SetInvalidParam("getPublicLink", "filename")
		return
	}

	channelId := matches[0][1]
	userId := matches[0][2]
	filename = matches[0][3]

	cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, channelId, c.Session.UserId)

	newProps := make(map[string]string)
	newProps["filename"] = filename
	newProps["time"] = fmt.Sprintf("%v", model.GetMillis())

	data := model.MapToJson(newProps)
	hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.ServiceSettings.PublicLinkSalt))

	url := fmt.Sprintf("%s/api/v1/files/get/%s/%s/%s?d=%s&h=%s&t=%s", c.GetSiteURL(), channelId, userId, filename, url.QueryEscape(data), url.QueryEscape(hash), c.Session.TeamId)

	if !c.HasPermissionsToChannel(cchan, "getPublicLink") {
		return
	}

	rData := make(map[string]string)
	rData["public_link"] = url

	w.Write([]byte(model.MapToJson(rData)))
}

func writeFile(f []byte, path string) *model.AppError {

	if utils.IsS3Configured() && !utils.Cfg.ServiceSettings.UseLocalStorage {
		var auth aws.Auth
		auth.AccessKey = utils.Cfg.AWSSettings.S3AccessKeyId
		auth.SecretKey = utils.Cfg.AWSSettings.S3SecretAccessKey

		s := s3.New(auth, aws.Regions[utils.Cfg.AWSSettings.S3Region])
		bucket := s.Bucket(utils.Cfg.AWSSettings.S3Bucket)

		ext := filepath.Ext(path)

		var err error
		if model.IsFileExtImage(ext) {
			options := s3.Options{}
			err = bucket.Put(path, f, model.GetImageMimeType(ext), s3.Private, options)

		} else {
			options := s3.Options{}
			err = bucket.Put(path, f, "binary/octet-stream", s3.Private, options)
		}

		if err != nil {
			return model.NewAppError("writeFile", "Encountered an error writing to S3", err.Error())
		}
	} else if utils.Cfg.ServiceSettings.UseLocalStorage && len(utils.Cfg.ServiceSettings.StorageDirectory) > 0 {
		if err := os.MkdirAll(filepath.Dir(utils.Cfg.ServiceSettings.StorageDirectory+path), 0774); err != nil {
			return model.NewAppError("writeFile", "Encountered an error creating the directory for the new file", err.Error())
		}

		if err := ioutil.WriteFile(utils.Cfg.ServiceSettings.StorageDirectory+path, f, 0644); err != nil {
			return model.NewAppError("writeFile", "Encountered an error writing to local server storage", err.Error())
		}
	} else {
		return model.NewAppError("writeFile", "File storage not configured properly. Please configure for either S3 or local server file storage.", "")
	}

	return nil
}

func readFile(path string) ([]byte, *model.AppError) {

	if utils.IsS3Configured() && !utils.Cfg.ServiceSettings.UseLocalStorage {
		var auth aws.Auth
		auth.AccessKey = utils.Cfg.AWSSettings.S3AccessKeyId
		auth.SecretKey = utils.Cfg.AWSSettings.S3SecretAccessKey

		s := s3.New(auth, aws.Regions[utils.Cfg.AWSSettings.S3Region])
		bucket := s.Bucket(utils.Cfg.AWSSettings.S3Bucket)

		// try to get the file from S3 with some basic retry logic
		tries := 0
		for {
			tries++

			f, err := bucket.Get(path)

			if f != nil {
				return f, nil
			} else if tries >= 3 {
				return nil, model.NewAppError("readFile", "Unable to get file from S3", "path="+path+", err="+err.Error())
			}
			time.Sleep(3000 * time.Millisecond)
		}
	} else if utils.Cfg.ServiceSettings.UseLocalStorage && len(utils.Cfg.ServiceSettings.StorageDirectory) > 0 {
		if f, err := ioutil.ReadFile(utils.Cfg.ServiceSettings.StorageDirectory + path); err != nil {
			return nil, model.NewAppError("readFile", "Encountered an error reading from local server storage", err.Error())
		} else {
			return f, nil
		}
	} else {
		return nil, model.NewAppError("readFile", "File storage not configured properly. Please configure for either S3 or local server file storage.", "")
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package gen_open_api

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"text/template"

	"github.com/kubernetes-incubator/reference-docs/lib"
	"github.com/kubernetes-incubator/reference-docs/gen_open_api/api"
)

func WriteTemplates(config *api.Config) {
	if _, err := os.Stat(*api.GenOpenApiDir + "/includes"); os.IsNotExist(err) {
		os.Mkdir(*api.GenOpenApiDir + "/includes", os.FileMode(0700))
	}

	// Write the index file importing each of the top level concept files
	WriteIndexFile(config)

	//// Write each concept file imported by the index file
	WriteConceptFiles(config)

	//// Write each definition file imported by the index file
	WriteDefinitionFiles(config)
}

func getTemplateFile(name string) string {
	return filepath.Join(*api.GenOpenApiDir, name)
}

func getStaticIncludesDir() string {
	return filepath.Join(*api.GenOpenApiDir, "static_includes")
}

func WriteIndexFile(config *api.Config) {
	includes := []string{}

	manifest := Manifest{}

	manifest.Copyright = "Copyright 2016 The Kubernetes Authors."

	if !*api.BuildOps {
		manifest.Title = "Kubernetes Resource Reference Docs"
	} else {
		manifest.Title = "Kubernetes API Reference Docs"
		manifest.Docs = append(manifest.Docs, Doc{"_overview.md"})
	}

	// Copy over the includes
	err := filepath.Walk(getStaticIncludesDir(), func(path string, info os.FileInfo, err error) error {
		if !info.IsDir() {
			to := filepath.Join(*api.GenOpenApiDir, "includes", filepath.Base(path))
			return os.Link(path, to)
		}
		return nil
	})
	if err != nil {
		fmt.Printf("Failed to copy includes %v.\n", err)
		return
	}

	// Add Toc Imports
	for _, c := range config.ResourceCategories {
		includes = append(includes, c.Include)
		manifest.Docs = append(manifest.Docs, Doc{"_" + c.Include + ".md"})
		for _, r := range c.Resources {
			includes = append(includes, GetConceptImport(r.Definition))
			manifest.Docs = append(manifest.Docs, Doc{"_" + GetConceptImport(r.Definition) + ".md"})
		}
	}

	// Add other definition imports
	definitions := api.SortDefinitionsByName{}
	for _, definition := range config.Definitions.GetAllDefinitions() {
		// Don't add definitions for top level resources in the toc or inlined resources
		if definition.InToc || definition.IsInlined || definition.IsOldVersion {
			continue
		}
		definitions = append(definitions, definition)
	}
	sort.Sort(definitions)
	manifest.Docs = append(manifest.Docs, Doc{"_definitions.md"})
	includes = append(includes, "definitions")
	for _, d := range definitions {
		//definitions[i] = GetDefinitionImport(name)
		manifest.Docs = append(manifest.Docs, Doc{"_" + GetDefinitionImport(d) + ".md"})
		includes = append(includes, GetDefinitionImport(d))
	}

	// Add definitions for older version of objects
	definitions = api.SortDefinitionsByName{}
	for _, definition := range config.Definitions.GetAllDefinitions() {
		// Don't add definitions for top level resources in the toc or inlined resources
		if definition.IsOldVersion {
			definitions = append(definitions, definition)
		}
	}
	sort.Sort(definitions)
	manifest.Docs = append(manifest.Docs, Doc{"_oldversions.md"})
	includes = append(includes, "oldversions")
	for _, d := range definitions {
		// Skip Inlined definitions
		if d.IsInlined {
			continue
		}
		manifest.Docs = append(manifest.Docs, Doc{"_" + GetConceptImport(d) + ".md"})
		includes = append(includes, GetConceptImport(d))
	}

	// Write out the json manifest
	jsonbytes, err := json.MarshalIndent(manifest, "", "  ")
	if err != nil {
		fmt.Printf("Could not Marshal manfiest %+v due to error: %v.\n", manifest, err)
	} else {
		jsonfile, err := os.Create(*api.GenOpenApiDir + "/" + lib.JsonOutputFile)
		if err != nil {
			fmt.Printf("Could not create file %s due to error: %v.\n", lib.JsonOutputFile, err)
		} else {
			defer jsonfile.Close()
			_, err := jsonfile.Write(jsonbytes)
			if err != nil {
				fmt.Printf("Failed to write bytes %s to file %s: %v.\n", jsonbytes, lib.JsonOutputFile, err)
			}
		}
	}
}

func WriteConceptFiles(config *api.Config) {
	// Setup the template to be instantiated
	t, err := template.New("concept.template").ParseFiles(getTemplateFile("/concept.template"))
	if err != nil {
		fmt.Printf("Failed to parse template: %v", err)
		os.Exit(1)
	}

	for _, d := range config.Definitions.GetAllDefinitions() {
		if !d.InToc {
			r := &api.Resource{Definition: d, Name: d.Name}
			WriteTemplate(t, r, GetConceptFilePath(d))
		}
	}
	for _, rc := range config.ResourceCategories {
		for _, r := range rc.Resources {
			WriteTemplate(t, r, GetConceptFilePath(r.Definition))
		}
	}
}

func WriteDefinitionFiles(config *api.Config) {
	// Setup the template to be instantiated
	t, err := template.New("definition.template").ParseFiles(getTemplateFile("definition.template"))
	if err != nil {
		fmt.Printf("Failed to parse template: %v", err)
		os.Exit(1)
	}

	for _, definition := range config.Definitions.GetAllDefinitions() {
		WriteTemplate(t, definition, GetDefinitionFilePath(definition))
	}
}

func WriteTemplate(t *template.Template, data interface{}, path string) {
	conceptFile, err := os.Create(path)
	defer conceptFile.Close()
	if err != nil {
		os.Stderr.WriteString(fmt.Sprintf("%v", err))
		os.Exit(1)
	}
	err = t.Execute(conceptFile, data)
	if err != nil {
		os.Stderr.WriteString(fmt.Sprintf("%v", err))
		os.Exit(1)
	}
}

func getLink(s string) string {
	return "#" + strings.ToLower(strings.Replace(s, " ", "-", -1))
}

func getImport(s string) string {
	return "generated_" + strings.ToLower(strings.Replace(s, ".", "_", 50))
}

func toFileName(s string) string {
	return fmt.Sprintf("%s/includes/_%s.md", *api.GenOpenApiDir, s)
}

func GetDefinitionImport(d *api.Definition) string {
	return fmt.Sprintf("%s_%s_definition", getImport(d.Name), d.Version)
}

func GetDefinitionFilePath(d *api.Definition) string {
	return toFileName(GetDefinitionImport(d))
}

// GetConceptImport returns the name to import in the index.html.md file
func GetConceptImport(d *api.Definition) string {
	return fmt.Sprintf("%s_%s_concept", getImport(d.Name), d.Version)
}

// GetConceptFilePath returns the filepath to write when instantiating a concept template
func GetConceptFilePath(d *api.Definition) string {
	return toFileName(GetConceptImport(d))
}

type Manifest struct {
	ExampleTabs     []ExampleTab    `json:"example_tabs,omitempty"`
	TableOfContents TableOfContents `json:"table_of_contents,omitempty"`
	Docs     []Doc    `json:"docs,omitempty"`
	Title     string `json:"title,omitempty"`
	Copyright string `json:"copyright,omitempty"`
}

type TableOfContents struct {
	Items []TableOfContentsItem `json:"body_md_files,omitempty"`
}

type TableOfContentsItem struct {
	DisplayName string                `json:"display_name,omitempty"`
	Type        string                `json:"type,omitempty"`
	Link        string                `json:"link,omitempty"`
	Items       []TableOfContentsItem `json:"items,omitempty"`
}

type Doc struct {
	Filename string `json:"filename,omitempty"`
}

type ExampleTab struct {
	DisplayName string `json:"display_name,omitempty"`
	SyntaxType  string `json:"syntax_type,omitempty"`
	HoverText   string `json:"hover_text,omitempty"`
}
<commit_msg>Skip generating unused definition and concept md files<commit_after>/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package gen_open_api

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"text/template"

	"github.com/kubernetes-incubator/reference-docs/lib"
	"github.com/kubernetes-incubator/reference-docs/gen_open_api/api"
)

func WriteTemplates(config *api.Config) {
	if _, err := os.Stat(*api.GenOpenApiDir + "/includes"); os.IsNotExist(err) {
		os.Mkdir(*api.GenOpenApiDir + "/includes", os.FileMode(0700))
	}

	// Write the index file importing each of the top level concept files
	WriteIndexFile(config)

	//// Write each concept file imported by the index file
	WriteConceptFiles(config)

	//// Write each definition file imported by the index file
	WriteDefinitionFiles(config)
}

func getTemplateFile(name string) string {
	return filepath.Join(*api.GenOpenApiDir, name)
}

func getStaticIncludesDir() string {
	return filepath.Join(*api.GenOpenApiDir, "static_includes")
}

func WriteIndexFile(config *api.Config) {
	includes := []string{}

	manifest := Manifest{}

	manifest.Copyright = "Copyright 2016 The Kubernetes Authors."

	if !*api.BuildOps {
		manifest.Title = "Kubernetes Resource Reference Docs"
	} else {
		manifest.Title = "Kubernetes API Reference Docs"
		manifest.Docs = append(manifest.Docs, Doc{"_overview.md"})
	}

	// Copy over the includes
	err := filepath.Walk(getStaticIncludesDir(), func(path string, info os.FileInfo, err error) error {
		if !info.IsDir() {
			to := filepath.Join(*api.GenOpenApiDir, "includes", filepath.Base(path))
			return os.Link(path, to)
		}
		return nil
	})
	if err != nil {
		fmt.Printf("Failed to copy includes %v.\n", err)
		return
	}

	// Add Toc Imports
	for _, c := range config.ResourceCategories {
		includes = append(includes, c.Include)
		manifest.Docs = append(manifest.Docs, Doc{"_" + c.Include + ".md"})
		for _, r := range c.Resources {
			includes = append(includes, GetConceptImport(r.Definition))
			manifest.Docs = append(manifest.Docs, Doc{"_" + GetConceptImport(r.Definition) + ".md"})
		}
	}

	// Add other definition imports
	definitions := api.SortDefinitionsByName{}
	for _, definition := range config.Definitions.GetAllDefinitions() {
		// Don't add definitions for top level resources in the toc or inlined resources
		if definition.InToc || definition.IsInlined || definition.IsOldVersion {
			continue
		}
		definitions = append(definitions, definition)
	}
	sort.Sort(definitions)
	manifest.Docs = append(manifest.Docs, Doc{"_definitions.md"})
	includes = append(includes, "definitions")
	for _, d := range definitions {
		//definitions[i] = GetDefinitionImport(name)
		manifest.Docs = append(manifest.Docs, Doc{"_" + GetDefinitionImport(d) + ".md"})
		includes = append(includes, GetDefinitionImport(d))
	}

	// Add definitions for older version of objects
	definitions = api.SortDefinitionsByName{}
	for _, definition := range config.Definitions.GetAllDefinitions() {
		// Don't add definitions for top level resources in the toc or inlined resources
		if definition.IsOldVersion {
			definitions = append(definitions, definition)
		}
	}
	sort.Sort(definitions)
	manifest.Docs = append(manifest.Docs, Doc{"_oldversions.md"})
	includes = append(includes, "oldversions")
	for _, d := range definitions {
		// Skip Inlined definitions
		if d.IsInlined {
			continue
		}
		manifest.Docs = append(manifest.Docs, Doc{"_" + GetConceptImport(d) + ".md"})
		includes = append(includes, GetConceptImport(d))
	}

	// Write out the json manifest
	jsonbytes, err := json.MarshalIndent(manifest, "", "  ")
	if err != nil {
		fmt.Printf("Could not Marshal manfiest %+v due to error: %v.\n", manifest, err)
	} else {
		jsonfile, err := os.Create(*api.GenOpenApiDir + "/" + lib.JsonOutputFile)
		if err != nil {
			fmt.Printf("Could not create file %s due to error: %v.\n", lib.JsonOutputFile, err)
		} else {
			defer jsonfile.Close()
			_, err := jsonfile.Write(jsonbytes)
			if err != nil {
				fmt.Printf("Failed to write bytes %s to file %s: %v.\n", jsonbytes, lib.JsonOutputFile, err)
			}
		}
	}
}

func WriteConceptFiles(config *api.Config) {
	// Setup the template to be instantiated
	t, err := template.New("concept.template").ParseFiles(getTemplateFile("/concept.template"))
	if err != nil {
		fmt.Printf("Failed to parse template: %v", err)
		os.Exit(1)
	}

	// Write concepts for old versions
	for _, d := range config.Definitions.GetAllDefinitions() {
		if !d.IsOldVersion {
			continue
		}
		r := &api.Resource{Definition: d, Name: d.Name}
		WriteTemplate(t, r, GetConceptFilePath(d))
	}
	// Write concepts for items in the Toc
	for _, rc := range config.ResourceCategories {
		for _, r := range rc.Resources {
			WriteTemplate(t, r, GetConceptFilePath(r.Definition))
		}
	}
}

func WriteDefinitionFiles(config *api.Config) {
	// Setup the template to be instantiated
	t, err := template.New("definition.template").ParseFiles(getTemplateFile("definition.template"))
	if err != nil {
		fmt.Printf("Failed to parse template: %v", err)
		os.Exit(1)
	}

	for _, definition := range config.Definitions.GetAllDefinitions() {
		// Skip things already present in concept docs
		if definition.InToc || definition.IsInlined || definition.IsOldVersion {
			continue
		}
		WriteTemplate(t, definition, GetDefinitionFilePath(definition))
	}
}

func WriteTemplate(t *template.Template, data interface{}, path string) {
	conceptFile, err := os.Create(path)
	defer conceptFile.Close()
	if err != nil {
		os.Stderr.WriteString(fmt.Sprintf("%v", err))
		os.Exit(1)
	}
	err = t.Execute(conceptFile, data)
	if err != nil {
		os.Stderr.WriteString(fmt.Sprintf("%v", err))
		os.Exit(1)
	}
}

func getLink(s string) string {
	return "#" + strings.ToLower(strings.Replace(s, " ", "-", -1))
}

func getImport(s string) string {
	return "generated_" + strings.ToLower(strings.Replace(s, ".", "_", 50))
}

func toFileName(s string) string {
	return fmt.Sprintf("%s/includes/_%s.md", *api.GenOpenApiDir, s)
}

func GetDefinitionImport(d *api.Definition) string {
	return fmt.Sprintf("%s_%s_definition", getImport(d.Name), d.Version)
}

func GetDefinitionFilePath(d *api.Definition) string {
	return toFileName(GetDefinitionImport(d))
}

// GetConceptImport returns the name to import in the index.html.md file
func GetConceptImport(d *api.Definition) string {
	return fmt.Sprintf("%s_%s_concept", getImport(d.Name), d.Version)
}

// GetConceptFilePath returns the filepath to write when instantiating a concept template
func GetConceptFilePath(d *api.Definition) string {
	return toFileName(GetConceptImport(d))
}

type Manifest struct {
	ExampleTabs     []ExampleTab    `json:"example_tabs,omitempty"`
	TableOfContents TableOfContents `json:"table_of_contents,omitempty"`
	Docs     []Doc    `json:"docs,omitempty"`
	Title     string `json:"title,omitempty"`
	Copyright string `json:"copyright,omitempty"`
}

type TableOfContents struct {
	Items []TableOfContentsItem `json:"body_md_files,omitempty"`
}

type TableOfContentsItem struct {
	DisplayName string                `json:"display_name,omitempty"`
	Type        string                `json:"type,omitempty"`
	Link        string                `json:"link,omitempty"`
	Items       []TableOfContentsItem `json:"items,omitempty"`
}

type Doc struct {
	Filename string `json:"filename,omitempty"`
}

type ExampleTab struct {
	DisplayName string `json:"display_name,omitempty"`
	SyntaxType  string `json:"syntax_type,omitempty"`
	HoverText   string `json:"hover_text,omitempty"`
}
<|endoftext|>


<commit_before>// Copyright 2016 The go-vgo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// https://github.com/go-vgo/robotgo/blob/master/LICENSE
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

package main

import (
	"fmt"

	"github.com/go-vgo/robotgo"
	// "go-vgo/robotgo"
)

func typeStr() {
	// importing "Hello World"
	robotgo.TypeStr("Hello World!", 1)
	robotgo.KeySleep = 100
	robotgo.TypeStr("だんしゃり")

	robotgo.TypeStr("Hi galaxy. こんにちは世界. 你好, 再见!")
	robotgo.Sleep(1)

	robotgo.TypeStr("So, hi, bye!")
	robotgo.MilliSleep(100)

	ustr := uint32(robotgo.CharCodeAt("So, hi, bye!", 0))
	robotgo.UnicodeType(ustr)

	err := robotgo.PasteStr("paste string")
	fmt.Println("PasteStr: ", err)
}

func keyTap() {
	// press "enter"
	robotgo.KeyTap("enter")
	robotgo.KeySleep = 200
	robotgo.KeyTap("a")
	robotgo.MilliSleep(100)
	robotgo.KeyTap("a", "ctrl")

	// hide window
	err := robotgo.KeyTap("h", "cmd")
	if err != nil {
		fmt.Println("robotgo.KeyTap run error is: ", err)
	}

	robotgo.KeyTap("h", "cmd")

	// press "i", "alt", "command" Key combination
	robotgo.KeyTap("i", "alt", "command")
	robotgo.KeyTap("i", "alt", "cmd")

	arr := []string{"alt", "cmd"}
	robotgo.KeyTap("i", arr)
	robotgo.KeyTap("i", arr)

	robotgo.KeyTap("i", "cmd", " alt", "shift")

	// close window
	robotgo.KeyTap("w", "cmd")

	// minimize window
	robotgo.KeyTap("m", "cmd")

	robotgo.KeyTap("f1", "ctrl")
	robotgo.KeyTap("a", "control")
}

func keyToggle() {
	// robotgo.KeySleep = 150
	robotgo.KeyToggle("a", "down")
	robotgo.KeyToggle("a", "down", "alt")
	robotgo.Sleep(1)

	robotgo.KeyToggle("a", "up", "alt", "cmd")
	robotgo.MilliSleep(100)
	robotgo.KeyToggle("q", "up", "alt", "cmd", "shift")

	err := robotgo.KeyToggle("enter", "down")
	if err != nil {
		fmt.Println("robotgo.KeyToggle run error is: ", err)
	}
}

func cilp() {
	// robotgo.TypeStr("en")

	// write string to clipboard
	e := robotgo.WriteAll("テストする")
	if e != nil {
		fmt.Println("robotgo.WriteAll err is: ", e)
	}

	// read string from clipboard
	text, err := robotgo.ReadAll()
	if err != nil {
		fmt.Println("robotgo.ReadAll err is: ", err)
	}
	fmt.Println("text: ", text)
}

func key() {
	////////////////////////////////////////////////////////////////////////////////
	// Control the keyboard
	////////////////////////////////////////////////////////////////////////////////

	typeStr()

	keyTap()
	keyToggle()

	cilp()
}

func main() {
	key()
}
<commit_msg>Update key examples<commit_after>// Copyright 2016 The go-vgo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// https://github.com/go-vgo/robotgo/blob/master/LICENSE
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

package main

import (
	"fmt"

	"github.com/go-vgo/robotgo"
	// "go-vgo/robotgo"
)

func typeStr() {
	// importing "Hello World"
	robotgo.TypeStr("Hello World!", 1)
	robotgo.KeySleep = 100
	robotgo.TypeStr("だんしゃり")

	robotgo.TypeStr("Hi galaxy. こんにちは世界. 你好, 再见!")
	robotgo.Sleep(1)

	robotgo.TypeStr("So, hi, bye!")
	robotgo.MilliSleep(100)

	ustr := uint32(robotgo.CharCodeAt("So, hi, bye!", 0))
	robotgo.UnicodeType(ustr)

	err := robotgo.PasteStr("paste string")
	fmt.Println("PasteStr: ", err)
}

func keyTap() {
	// press "enter"
	robotgo.KeyTap("enter")
	robotgo.KeyTap(robotgo.Enter)
	robotgo.KeySleep = 200
	robotgo.KeyTap("a")
	robotgo.MilliSleep(100)
	robotgo.KeyTap("a", "ctrl")

	// hide window
	err := robotgo.KeyTap("h", "cmd")
	if err != nil {
		fmt.Println("robotgo.KeyTap run error is: ", err)
	}

	robotgo.KeyTap("h", "cmd")

	// press "i", "alt", "command" Key combination
	robotgo.KeyTap(robotgo.KeyI, robotgo.Alt, robotgo.Cmd)
	robotgo.KeyTap("i", "alt", "cmd")

	arr := []string{"alt", "cmd"}
	robotgo.KeyTap("i", arr)
	robotgo.KeyTap("i", arr)

	robotgo.KeyTap("i", "cmd", " alt", "shift")

	// close window
	robotgo.KeyTap("w", "cmd")

	// minimize window
	robotgo.KeyTap("m", "cmd")

	robotgo.KeyTap("f1", "ctrl")
	robotgo.KeyTap("a", "control")
}

func keyToggle() {
	// robotgo.KeySleep = 150
	robotgo.KeyToggle(robotgo.KeyA)
	robotgo.KeyToggle("a", "down", "alt")
	robotgo.Sleep(1)

	robotgo.KeyToggle("a", "up", "alt", "cmd")
	robotgo.MilliSleep(100)
	robotgo.KeyToggle("q", "up", "alt", "cmd", "shift")

	err := robotgo.KeyToggle(robotgo.Enter)
	if err != nil {
		fmt.Println("robotgo.KeyToggle run error is: ", err)
	}
}

func cilp() {
	// robotgo.TypeStr("en")

	// write string to clipboard
	e := robotgo.WriteAll("テストする")
	if e != nil {
		fmt.Println("robotgo.WriteAll err is: ", e)
	}

	// read string from clipboard
	text, err := robotgo.ReadAll()
	if err != nil {
		fmt.Println("robotgo.ReadAll err is: ", err)
	}
	fmt.Println("text: ", text)
}

func key() {
	////////////////////////////////////////////////////////////////////////////////
	// Control the keyboard
	////////////////////////////////////////////////////////////////////////////////

	typeStr()

	keyTap()
	keyToggle()

	cilp()
}

func main() {
	key()
}
<|endoftext|>


<commit_before>// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package mixins

import (
	"github.com/google/gxui"
	"github.com/google/gxui/math"
	"github.com/google/gxui/mixins/base"
	"github.com/google/gxui/mixins/parts"
)

type DropDownListOuter interface {
	base.ContainerOuter
}

type DropDownList struct {
	base.Container
	parts.BackgroundBorderPainter
	parts.Focusable

	outer DropDownListOuter

	theme       gxui.Theme
	list        gxui.List
	listShowing bool
	itemSize    math.Size
	overlay     gxui.BubbleOverlay
	selected    gxui.Control
	onShowList  gxui.Event
	onHideList  gxui.Event
}

func (l *DropDownList) Init(outer DropDownListOuter, theme gxui.Theme) {
	l.outer = outer
	l.Container.Init(outer, theme)
	l.BackgroundBorderPainter.Init(outer)
	l.Focusable.Init(outer)

	l.theme = theme
	l.list = theme.CreateList()
	l.list.OnSelectionChanged(func(item gxui.AdapterItem) {
		adapter := l.list.Adapter()
		if item != nil && adapter != nil {
			l.selected = adapter.Create(l.theme, adapter.ItemIndex(item))
		} else {
			l.selected = nil
		}
		l.Relayout()
	})
	l.list.OnItemClicked(func(gxui.MouseEvent, gxui.AdapterItem) {
		l.HideList()
	})
	l.list.OnKeyPress(func(ev gxui.KeyboardEvent) {
		switch ev.Key {
		case gxui.KeyEnter, gxui.KeyEscape:
			l.HideList()
		}
	})
	l.list.OnLostFocus(l.HideList)
	l.OnDetach(l.HideList)
	l.SetMouseEventTarget(true)

	// Interface compliance test
	_ = gxui.DropDownList(l)
}

func (l *DropDownList) LayoutChildren() {
	if !l.RelayoutSuspended() {
		// Disable relayout on AddChild / RemoveChild as we're performing layout here.
		l.SetRelayoutSuspended(true)
		defer l.SetRelayoutSuspended(false)
	}

	l.outer.RemoveAll()

	if l.selected != nil {
		s := l.outer.Bounds().Size().Contract(l.Padding()).Max(math.ZeroSize)
		o := l.Padding().LT()
		l.selected.Layout(s.Rect().Offset(o))
		l.AddChild(l.selected)
	}
}

func (l *DropDownList) DesiredSize(min, max math.Size) math.Size {
	if l.selected != nil {
		return l.selected.DesiredSize(min, max).Expand(l.outer.Padding()).Clamp(min, max)
	} else {
		return l.itemSize.Expand(l.outer.Padding()).Clamp(min, max)
	}
}

func (l *DropDownList) DataReplaced() {
	adapter := l.list.Adapter()
	itemSize := adapter.Size(l.theme)
	l.itemSize = itemSize
	l.outer.Relayout()
}

func (l *DropDownList) ListShowing() bool {
	return l.listShowing
}

func (l *DropDownList) ShowList() bool {
	if l.listShowing || l.overlay == nil {
		return false
	}
	l.listShowing = true
	s := l.Bounds().Size()
	at := math.Point{X: s.W / 2, Y: s.H}
	l.overlay.Show(l.list, gxui.TransformCoordinate(at, l, l.overlay))
	gxui.SetFocus(l.list)
	if l.onShowList != nil {
		l.onShowList.Fire()
	}
	return true
}

func (l *DropDownList) HideList() {
	if l.listShowing {
		l.listShowing = false
		l.overlay.Hide()
		if l.Attached() {
			gxui.SetFocus(l)
		}
		if l.onHideList != nil {
			l.onHideList.Fire()
		}
	}
}

func (l *DropDownList) List() gxui.List {
	return l.list
}

// InputEventHandler override
func (l *DropDownList) Click(ev gxui.MouseEvent) (consume bool) {
	l.InputEventHandler.Click(ev)
	if l.ListShowing() {
		l.HideList()
	} else {
		l.ShowList()
	}
	return true
}

// gxui.DropDownList compliance
func (l *DropDownList) SetBubbleOverlay(overlay gxui.BubbleOverlay) {
	l.overlay = overlay
}

func (l *DropDownList) BubbleOverlay() gxui.BubbleOverlay {
	return l.overlay
}

func (l *DropDownList) Adapter() gxui.ListAdapter {
	return l.list.Adapter()
}

func (l *DropDownList) SetAdapter(adapter gxui.ListAdapter) {
	if l.list.Adapter() != adapter {
		l.list.SetAdapter(adapter)
		if adapter != nil {
			adapter.OnDataChanged(l.DataReplaced)
			adapter.OnDataReplaced(l.DataReplaced)
		}
		// TODO: Unlisten
		l.DataReplaced()
	}
}

func (l *DropDownList) Selected() gxui.AdapterItem {
	return l.list.Selected()
}

func (l *DropDownList) Select(item gxui.AdapterItem) {
	if l.list.Selected() != item {
		l.list.Select(item)
		l.LayoutChildren()
	}
}

func (l *DropDownList) OnSelectionChanged(f func(gxui.AdapterItem)) gxui.EventSubscription {
	return l.list.OnSelectionChanged(f)
}

func (l *DropDownList) OnShowList(f func()) gxui.EventSubscription {
	if l.onShowList == nil {
		l.onShowList = gxui.CreateEvent(f)
	}
	return l.onShowList.Listen(f)
}

func (l *DropDownList) OnHideList(f func()) gxui.EventSubscription {
	if l.onHideList == nil {
		l.onHideList = gxui.CreateEvent(f)
	}
	return l.onHideList.Listen(f)
}

// InputEventHandler overrides
func (l *DropDownList) KeyPress(ev gxui.KeyboardEvent) (consume bool) {
	if ev.Key == gxui.KeySpace || ev.Key == gxui.KeyEnter {
		me := gxui.MouseEvent{
			Button: gxui.MouseButtonLeft,
		}
		return l.Click(me)
	}
	return l.InputEventHandler.KeyPress(ev)
}

// parts.Container overrides
func (l *DropDownList) Paint(c gxui.Canvas) {
	r := l.outer.Bounds().Size().Rect()
	l.PaintBackground(c, r)
	l.Container.Paint(c)
	l.PaintBorder(c, r)
}
<commit_msg>Make the DropDownList attach the selected item immediately.<commit_after>// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package mixins

import (
	"github.com/google/gxui"
	"github.com/google/gxui/math"
	"github.com/google/gxui/mixins/base"
	"github.com/google/gxui/mixins/parts"
)

type DropDownListOuter interface {
	base.ContainerOuter
}

type DropDownList struct {
	base.Container
	parts.BackgroundBorderPainter
	parts.Focusable

	outer DropDownListOuter

	theme       gxui.Theme
	list        gxui.List
	listShowing bool
	itemSize    math.Size
	overlay     gxui.BubbleOverlay
	selected    gxui.Control
	onShowList  gxui.Event
	onHideList  gxui.Event
}

func (l *DropDownList) Init(outer DropDownListOuter, theme gxui.Theme) {
	l.outer = outer
	l.Container.Init(outer, theme)
	l.BackgroundBorderPainter.Init(outer)
	l.Focusable.Init(outer)

	l.theme = theme
	l.list = theme.CreateList()
	l.list.OnSelectionChanged(func(item gxui.AdapterItem) {
		l.outer.RemoveAll()
		adapter := l.list.Adapter()
		if item != nil && adapter != nil {
			l.selected = adapter.Create(l.theme, adapter.ItemIndex(item))
			l.AddChild(l.selected)
		} else {
			l.selected = nil
		}
		l.Relayout()
	})
	l.list.OnItemClicked(func(gxui.MouseEvent, gxui.AdapterItem) {
		l.HideList()
	})
	l.list.OnKeyPress(func(ev gxui.KeyboardEvent) {
		switch ev.Key {
		case gxui.KeyEnter, gxui.KeyEscape:
			l.HideList()
		}
	})
	l.list.OnLostFocus(l.HideList)
	l.OnDetach(l.HideList)
	l.SetMouseEventTarget(true)

	// Interface compliance test
	_ = gxui.DropDownList(l)
}

func (l *DropDownList) LayoutChildren() {
	if !l.RelayoutSuspended() {
		// Disable relayout on AddChild / RemoveChild as we're performing layout here.
		l.SetRelayoutSuspended(true)
		defer l.SetRelayoutSuspended(false)
	}

	if l.selected != nil {
		s := l.outer.Bounds().Size().Contract(l.Padding()).Max(math.ZeroSize)
		o := l.Padding().LT()
		l.selected.Layout(s.Rect().Offset(o))
	}
}

func (l *DropDownList) DesiredSize(min, max math.Size) math.Size {
	if l.selected != nil {
		return l.selected.DesiredSize(min, max).Expand(l.outer.Padding()).Clamp(min, max)
	} else {
		return l.itemSize.Expand(l.outer.Padding()).Clamp(min, max)
	}
}

func (l *DropDownList) DataReplaced() {
	adapter := l.list.Adapter()
	itemSize := adapter.Size(l.theme)
	l.itemSize = itemSize
	l.outer.Relayout()
}

func (l *DropDownList) ListShowing() bool {
	return l.listShowing
}

func (l *DropDownList) ShowList() bool {
	if l.listShowing || l.overlay == nil {
		return false
	}
	l.listShowing = true
	s := l.Bounds().Size()
	at := math.Point{X: s.W / 2, Y: s.H}
	l.overlay.Show(l.list, gxui.TransformCoordinate(at, l, l.overlay))
	gxui.SetFocus(l.list)
	if l.onShowList != nil {
		l.onShowList.Fire()
	}
	return true
}

func (l *DropDownList) HideList() {
	if l.listShowing {
		l.listShowing = false
		l.overlay.Hide()
		if l.Attached() {
			gxui.SetFocus(l)
		}
		if l.onHideList != nil {
			l.onHideList.Fire()
		}
	}
}

func (l *DropDownList) List() gxui.List {
	return l.list
}

// InputEventHandler override
func (l *DropDownList) Click(ev gxui.MouseEvent) (consume bool) {
	l.InputEventHandler.Click(ev)
	if l.ListShowing() {
		l.HideList()
	} else {
		l.ShowList()
	}
	return true
}

// gxui.DropDownList compliance
func (l *DropDownList) SetBubbleOverlay(overlay gxui.BubbleOverlay) {
	l.overlay = overlay
}

func (l *DropDownList) BubbleOverlay() gxui.BubbleOverlay {
	return l.overlay
}

func (l *DropDownList) Adapter() gxui.ListAdapter {
	return l.list.Adapter()
}

func (l *DropDownList) SetAdapter(adapter gxui.ListAdapter) {
	if l.list.Adapter() != adapter {
		l.list.SetAdapter(adapter)
		if adapter != nil {
			adapter.OnDataChanged(l.DataReplaced)
			adapter.OnDataReplaced(l.DataReplaced)
		}
		// TODO: Unlisten
		l.DataReplaced()
	}
}

func (l *DropDownList) Selected() gxui.AdapterItem {
	return l.list.Selected()
}

func (l *DropDownList) Select(item gxui.AdapterItem) {
	if l.list.Selected() != item {
		l.list.Select(item)
		l.LayoutChildren()
	}
}

func (l *DropDownList) OnSelectionChanged(f func(gxui.AdapterItem)) gxui.EventSubscription {
	return l.list.OnSelectionChanged(f)
}

func (l *DropDownList) OnShowList(f func()) gxui.EventSubscription {
	if l.onShowList == nil {
		l.onShowList = gxui.CreateEvent(f)
	}
	return l.onShowList.Listen(f)
}

func (l *DropDownList) OnHideList(f func()) gxui.EventSubscription {
	if l.onHideList == nil {
		l.onHideList = gxui.CreateEvent(f)
	}
	return l.onHideList.Listen(f)
}

// InputEventHandler overrides
func (l *DropDownList) KeyPress(ev gxui.KeyboardEvent) (consume bool) {
	if ev.Key == gxui.KeySpace || ev.Key == gxui.KeyEnter {
		me := gxui.MouseEvent{
			Button: gxui.MouseButtonLeft,
		}
		return l.Click(me)
	}
	return l.InputEventHandler.KeyPress(ev)
}

// parts.Container overrides
func (l *DropDownList) Paint(c gxui.Canvas) {
	r := l.outer.Bounds().Size().Rect()
	l.PaintBackground(c, r)
	l.Container.Paint(c)
	l.PaintBorder(c, r)
}
<|endoftext|>


<commit_before>package data

//----------------------------------------------------------------------
// This file is part of Gospel.
// Copyright (C) 2011-2020 Bernd Fix
//
// Gospel is free software: you can redistribute it and/or modify it
// under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License,
// or (at your option) any later version.
//
// Gospel is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
// SPDX-License-Identifier: AGPL3.0-or-later
//----------------------------------------------------------------------

import (
	"bytes"
	"crypto/sha256"
	"encoding/binary"
	"math"
	"math/big"
)

// A BloomFilter is a space/time efficient set of unique entries.
// It can not enumerate its elements, but can check if an entry is contained
// in the set. The check always succeeds for a contained entry, but can create
// "false-positives" (entries not contained in the map give a positive result).
// By adjusting the number of bits in the BloomFilter and the number of indices
// generated for an entry, a BloomFilter can handle a given number of entries
// with a desired upper-bound for the false-positive rate.
type BloomFilter struct {
	NumBits    uint32 `size:"big" json:"numBits"`     // number of bits in filter
	NumIdx     uint8  `size:"big" json:"numIdx"`      // number of indices
	NumIdxBits uint8  `json:"numIdxBits"`             // number of bits per index
	NumHash    uint8  `json:"numHash"`                // number of SHA256 hashes needed
	Bits       []byte `size:"(BitsSize)" json:"bits"` // bit storage
}

// NewBloomFilterDirect creates a new BloomFilter based on the number of bits
// in the filter and the number of indices to be used.
func NewBloomFilterDirect(numBits, numIdx int) *BloomFilter {
	numIdxBits := int(math.Ceil(math.Log2(float64(numBits))))
	return &BloomFilter{
		NumBits:    uint32(numBits),
		NumIdx:     uint8(numIdx),
		NumIdxBits: uint8(numIdxBits),
		NumHash:    uint8((numIdxBits*numIdx + 255) / 256),
		Bits:       make([]byte, (numBits+7)/8),
	}
}

// NewBloomFilter creates a new BloomFilter based on the upper-bounds for the
// number of entries and the "false-positive" rate.
func NewBloomFilter(numExpected int, falsePositiveRate float64) *BloomFilter {
	// do some math and calculate the number of indices and number of bits
	// in the new BloomFilter given an upper-bound for the number of entries
	// and the "false-positive" rate.
	numIdx := int(math.Ceil(-math.Log2(falsePositiveRate)))
	numBits := int(float64(numIdx*numExpected) / math.Ln2)
	return NewBloomFilterDirect(numBits, numIdx)
}

// BitsSize returns the size of the byte array representing the filter bits.
func (bf *BloomFilter) BitsSize() uint {
	return uint((bf.NumBits + 7) / 8)
}

// SameKind checks if two BloomFilter have the same parameters.
func (bf *BloomFilter) SameKind(bf2 *BloomFilter) bool {
	return bf.NumBits == bf2.NumBits &&
		bf.NumHash == bf2.NumHash &&
		bf.NumIdx == bf2.NumIdx &&
		bf.NumIdxBits == bf2.NumIdxBits
}

// Add an entry to the BloomFilter.
func (bf *BloomFilter) Add(entry []byte) {
	for _, idx := range bf.indexList(entry) {
		pos, mask := resolve(idx)
		bf.Bits[pos] |= mask
	}
}

// Combine merges two BloomFilters (of same kind) into a new one.
func (bf *BloomFilter) Combine(bf2 *BloomFilter) *BloomFilter {
	if !bf.SameKind(bf2) {
		return nil
	}
	res := &BloomFilter{
		NumBits:    bf.NumBits,
		NumIdx:     bf.NumIdx,
		NumIdxBits: bf.NumIdxBits,
		NumHash:    bf.NumHash,
		Bits:       make([]byte, len(bf.Bits)),
	}
	for i := range res.Bits {
		res.Bits[i] = bf.Bits[i] | bf2.Bits[i]
	}
	return res
}

// Contains returns true if the BloomFilter contains the given entry, and
// false otherwise. If an entry was added to the set, this function will
// always return 'true'. It can return 'true' for entries not in the set
// ("false-positives").
func (bf *BloomFilter) Contains(entry []byte) bool {
	for _, idx := range bf.indexList(entry) {
		pos, mask := resolve(idx)
		if (bf.Bits[pos] & mask) == 0 {
			return false
		}
	}
	return true
}

// Helper method to extract the list of indices for an entry.
func (bf *BloomFilter) indexList(entry []byte) []int {
	totalIdx := make([]byte, 0)
	hasher := sha256.New()
	var i uint8
	for i = 0; i < bf.NumHash; i++ {
		hasher.Write(entry)
		totalIdx = hasher.Sum(totalIdx)
	}
	v := new(big.Int).SetBytes(totalIdx)
	mask := big.NewInt((1 << uint(bf.NumIdxBits)) - 1)
	list := make([]int, bf.NumIdx)
	for i = 0; i < bf.NumIdx; i++ {
		j := new(big.Int).And(v, mask)
		list[i] = int(j.Int64()) % int(bf.NumBits)
		v = new(big.Int).Rsh(v, uint(bf.NumIdxBits))
	}
	return list
}

// Helper method to resolve an index into byte/bit positions in the data
// of the BloomFilter.
func resolve(idx int) (int, byte) {
	return idx >> 3, byte(1 << uint(idx&7))
}

//----------------------------------------------------------------------

// SaltedBloomFilter is a bloom filter where each entr is "salted" with
// a uint32 salt value before processing. As each filter have different
// salts, the same set of entries added to the filter will result in a
// different bit pattern for the filter resulting in different false-
// positives for the same set. Useful if a filter is repeatedly generated
// for the same (or similar) set of entries.
type SaltedBloomFilter struct {
	Salt []byte `size:"4"` // salt value
	BloomFilter
}

// NewSaltedBloomFilterDirect creates a new salted BloomFilter based on
// the number of bits in the filter and the number of indices to be used.
func NewSaltedBloomFilterDirect(salt uint32, numBits, numIdx int) *SaltedBloomFilter {
	bf := &SaltedBloomFilter{
		Salt:        make([]byte, 4),
		BloomFilter: *NewBloomFilterDirect(numBits, numIdx),
	}
	bf.setSalt(salt)
	return bf
}

// NewSaltedBloomFilter creates a new salted BloomFilter based on the
// upper-bounds for the number of entries and the "false-positive" rate.
func NewSaltedBloomFilter(salt uint32, numExpected int, falsePositiveRate float64) *SaltedBloomFilter {
	bf := &SaltedBloomFilter{
		Salt:        make([]byte, 4),
		BloomFilter: *NewBloomFilter(numExpected, falsePositiveRate),
	}
	bf.setSalt(salt)
	return bf
}

// Set salt for bloom filter
func (bf *SaltedBloomFilter) setSalt(salt uint32) {
	buf := new(bytes.Buffer)
	_ = binary.Write(buf, binary.BigEndian, salt)
	bf.Salt = buf.Bytes()
}

// Salt entry before processing
func (bf *SaltedBloomFilter) saltEntry(entry []byte) []byte {
	buf := make([]byte, len(entry)+4)
	copy(buf, bf.Salt)
	copy(buf[4:], entry)
	return buf
}

// Add an entry to the BloomFilter.
func (bf *SaltedBloomFilter) Add(entry []byte) {
	bf.BloomFilter.Add(bf.saltEntry(entry))
}

// Combine merges two salted BloomFilters (of same kind) into a new one.
func (bf *SaltedBloomFilter) Combine(bf2 *SaltedBloomFilter) *SaltedBloomFilter {
	if !bytes.Equal(bf.Salt, bf2.Salt) || !bf.BloomFilter.SameKind(&bf2.BloomFilter) {
		return nil
	}
	res := new(SaltedBloomFilter)
	res.Salt = make([]byte, 4)
	copy(res.Salt, bf.Salt)
	res.BloomFilter = *bf.BloomFilter.Combine(&bf2.BloomFilter)
	return res
}

// Contains returns true if the salted BloomFilter contains the given entry,
// and false otherwise. If an entry was added to the set, this function will
// always return 'true'. It can return 'true' for entries not in the set
// ("false-positives").
func (bf *SaltedBloomFilter) Contains(entry []byte) bool {
	return bf.BloomFilter.Contains(bf.saltEntry(entry))
}
<commit_msg>bloomfilter: added Size() method.<commit_after>package data

//----------------------------------------------------------------------
// This file is part of Gospel.
// Copyright (C) 2011-2020 Bernd Fix
//
// Gospel is free software: you can redistribute it and/or modify it
// under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License,
// or (at your option) any later version.
//
// Gospel is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
// SPDX-License-Identifier: AGPL3.0-or-later
//----------------------------------------------------------------------

import (
	"bytes"
	"crypto/sha256"
	"encoding/binary"
	"math"
	"math/big"
)

// A BloomFilter is a space/time efficient set of unique entries.
// It can not enumerate its elements, but can check if an entry is contained
// in the set. The check always succeeds for a contained entry, but can create
// "false-positives" (entries not contained in the map give a positive result).
// By adjusting the number of bits in the BloomFilter and the number of indices
// generated for an entry, a BloomFilter can handle a given number of entries
// with a desired upper-bound for the false-positive rate.
type BloomFilter struct {
	NumBits    uint32 `size:"big" json:"numBits"`     // number of bits in filter
	NumIdx     uint8  `size:"big" json:"numIdx"`      // number of indices
	NumIdxBits uint8  `json:"numIdxBits"`             // number of bits per index
	NumHash    uint8  `json:"numHash"`                // number of SHA256 hashes needed
	Bits       []byte `size:"(BitsSize)" json:"bits"` // bit storage
}

// NewBloomFilterDirect creates a new BloomFilter based on the number of bits
// in the filter and the number of indices to be used.
func NewBloomFilterDirect(numBits, numIdx int) *BloomFilter {
	numIdxBits := int(math.Ceil(math.Log2(float64(numBits))))
	return &BloomFilter{
		NumBits:    uint32(numBits),
		NumIdx:     uint8(numIdx),
		NumIdxBits: uint8(numIdxBits),
		NumHash:    uint8((numIdxBits*numIdx + 255) / 256),
		Bits:       make([]byte, (numBits+7)/8),
	}
}

// NewBloomFilter creates a new BloomFilter based on the upper-bounds for the
// number of entries and the "false-positive" rate.
func NewBloomFilter(numExpected int, falsePositiveRate float64) *BloomFilter {
	// do some math and calculate the number of indices and number of bits
	// in the new BloomFilter given an upper-bound for the number of entries
	// and the "false-positive" rate.
	numIdx := int(math.Ceil(-math.Log2(falsePositiveRate)))
	numBits := int(float64(numIdx*numExpected) / math.Ln2)
	return NewBloomFilterDirect(numBits, numIdx)
}

// BitsSize returns the size of the byte array representing the filter bits.
func (bf *BloomFilter) BitsSize() uint {
	return uint((bf.NumBits + 7) / 8)
}

// Size returns the size of the binary representation
func (bf *BloomFilter) Size() uint {
	return uint(7 + len(bf.Bits))
}

// SameKind checks if two BloomFilter have the same parameters.
func (bf *BloomFilter) SameKind(bf2 *BloomFilter) bool {
	return bf.NumBits == bf2.NumBits &&
		bf.NumHash == bf2.NumHash &&
		bf.NumIdx == bf2.NumIdx &&
		bf.NumIdxBits == bf2.NumIdxBits
}

// Add an entry to the BloomFilter.
func (bf *BloomFilter) Add(entry []byte) {
	for _, idx := range bf.indexList(entry) {
		pos, mask := resolve(idx)
		bf.Bits[pos] |= mask
	}
}

// Combine merges two BloomFilters (of same kind) into a new one.
func (bf *BloomFilter) Combine(bf2 *BloomFilter) *BloomFilter {
	if !bf.SameKind(bf2) {
		return nil
	}
	res := &BloomFilter{
		NumBits:    bf.NumBits,
		NumIdx:     bf.NumIdx,
		NumIdxBits: bf.NumIdxBits,
		NumHash:    bf.NumHash,
		Bits:       make([]byte, len(bf.Bits)),
	}
	for i := range res.Bits {
		res.Bits[i] = bf.Bits[i] | bf2.Bits[i]
	}
	return res
}

// Contains returns true if the BloomFilter contains the given entry, and
// false otherwise. If an entry was added to the set, this function will
// always return 'true'. It can return 'true' for entries not in the set
// ("false-positives").
func (bf *BloomFilter) Contains(entry []byte) bool {
	for _, idx := range bf.indexList(entry) {
		pos, mask := resolve(idx)
		if (bf.Bits[pos] & mask) == 0 {
			return false
		}
	}
	return true
}

// Helper method to extract the list of indices for an entry.
func (bf *BloomFilter) indexList(entry []byte) []int {
	totalIdx := make([]byte, 0)
	hasher := sha256.New()
	var i uint8
	for i = 0; i < bf.NumHash; i++ {
		hasher.Write(entry)
		totalIdx = hasher.Sum(totalIdx)
	}
	v := new(big.Int).SetBytes(totalIdx)
	mask := big.NewInt((1 << uint(bf.NumIdxBits)) - 1)
	list := make([]int, bf.NumIdx)
	for i = 0; i < bf.NumIdx; i++ {
		j := new(big.Int).And(v, mask)
		list[i] = int(j.Int64()) % int(bf.NumBits)
		v = new(big.Int).Rsh(v, uint(bf.NumIdxBits))
	}
	return list
}

// Helper method to resolve an index into byte/bit positions in the data
// of the BloomFilter.
func resolve(idx int) (int, byte) {
	return idx >> 3, byte(1 << uint(idx&7))
}

//----------------------------------------------------------------------

// SaltedBloomFilter is a bloom filter where each entr is "salted" with
// a uint32 salt value before processing. As each filter have different
// salts, the same set of entries added to the filter will result in a
// different bit pattern for the filter resulting in different false-
// positives for the same set. Useful if a filter is repeatedly generated
// for the same (or similar) set of entries.
type SaltedBloomFilter struct {
	Salt []byte `size:"4"` // salt value
	BloomFilter
}

// NewSaltedBloomFilterDirect creates a new salted BloomFilter based on
// the number of bits in the filter and the number of indices to be used.
func NewSaltedBloomFilterDirect(salt uint32, numBits, numIdx int) *SaltedBloomFilter {
	bf := &SaltedBloomFilter{
		Salt:        make([]byte, 4),
		BloomFilter: *NewBloomFilterDirect(numBits, numIdx),
	}
	bf.setSalt(salt)
	return bf
}

// NewSaltedBloomFilter creates a new salted BloomFilter based on the
// upper-bounds for the number of entries and the "false-positive" rate.
func NewSaltedBloomFilter(salt uint32, numExpected int, falsePositiveRate float64) *SaltedBloomFilter {
	bf := &SaltedBloomFilter{
		Salt:        make([]byte, 4),
		BloomFilter: *NewBloomFilter(numExpected, falsePositiveRate),
	}
	bf.setSalt(salt)
	return bf
}

// Size returns the size of the binary representation
func (bf *SaltedBloomFilter) Size() uint {
	return bf.BloomFilter.Size() + 4
}

// Set salt for bloom filter
func (bf *SaltedBloomFilter) setSalt(salt uint32) {
	buf := new(bytes.Buffer)
	_ = binary.Write(buf, binary.BigEndian, salt)
	bf.Salt = buf.Bytes()
}

// Salt entry before processing
func (bf *SaltedBloomFilter) saltEntry(entry []byte) []byte {
	buf := make([]byte, len(entry)+4)
	copy(buf, bf.Salt)
	copy(buf[4:], entry)
	return buf
}

// Add an entry to the BloomFilter.
func (bf *SaltedBloomFilter) Add(entry []byte) {
	bf.BloomFilter.Add(bf.saltEntry(entry))
}

// Combine merges two salted BloomFilters (of same kind) into a new one.
func (bf *SaltedBloomFilter) Combine(bf2 *SaltedBloomFilter) *SaltedBloomFilter {
	if !bytes.Equal(bf.Salt, bf2.Salt) || !bf.BloomFilter.SameKind(&bf2.BloomFilter) {
		return nil
	}
	res := new(SaltedBloomFilter)
	res.Salt = make([]byte, 4)
	copy(res.Salt, bf.Salt)
	res.BloomFilter = *bf.BloomFilter.Combine(&bf2.BloomFilter)
	return res
}

// Contains returns true if the salted BloomFilter contains the given entry,
// and false otherwise. If an entry was added to the set, this function will
// always return 'true'. It can return 'true' for entries not in the set
// ("false-positives").
func (bf *SaltedBloomFilter) Contains(entry []byte) bool {
	return bf.BloomFilter.Contains(bf.saltEntry(entry))
}
<|endoftext|>


<commit_before>// Copyright 2013 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"flag"
	"fmt"
	"github.com/bmizerany/pat"
	"github.com/globocom/config"
	"github.com/globocom/tsuru/app"
	"github.com/globocom/tsuru/log"
	"github.com/globocom/tsuru/provision"
	_ "github.com/globocom/tsuru/provision/juju"
	stdlog "log"
	"log/syslog"
	"net/http"
	"os"
)

func fatal(err error) {
	fmt.Fprintln(os.Stderr, err)
	log.Fatal(err)
}

func main() {
	logger, err := syslog.NewLogger(syslog.LOG_INFO, stdlog.LstdFlags)
	if err != nil {
		stdlog.Fatal(err)
	}
	log.SetLogger(logger)
	configFile := flag.String("config", "/etc/tsuru/tsuru.conf", "tsuru config file")
	dry := flag.Bool("dry", false, "dry-run: does not start the server (for testing purpose)")
	flag.Parse()
	err = config.ReadConfigFile(*configFile)
	if err != nil {
		fatal(err)
	}
	connString, err := config.GetString("database:url")
	if err != nil {
		fatal(err)
	}
	dbName, err := config.GetString("database:name")
	if err != nil {
		fatal(err)
	}
	fmt.Printf("Using the database %q from the server %q.\n\n", dbName, connString)

	m := pat.New()

	m.Get("/services/instances", AuthorizationRequiredHandler(ServicesInstancesHandler))
	m.Post("/services/instances", AuthorizationRequiredHandler(CreateInstanceHandler))
	m.Put("/services/instances/:instance/:app", AuthorizationRequiredHandler(BindHandler))
	m.Del("/services/instances/:instance/:app", AuthorizationRequiredHandler(UnbindHandler))
	m.Del("/services/c/instances/:name", AuthorizationRequiredHandler(RemoveServiceInstanceHandler))
	m.Get("/services/instances/:instance/status", AuthorizationRequiredHandler(ServiceInstanceStatusHandler))

	m.Get("/services", AuthorizationRequiredHandler(ServicesHandler))
	m.Post("/services", AuthorizationRequiredHandler(CreateHandler))
	m.Put("/services", AuthorizationRequiredHandler(UpdateHandler))
	m.Del("/services/:name", AuthorizationRequiredHandler(DeleteHandler))
	m.Get("/services/:name", AuthorizationRequiredHandler(ServiceInfoHandler))
	m.Get("/services/c/:name/doc", AuthorizationRequiredHandler(Doc))
	m.Get("/services/:name/doc", AuthorizationRequiredHandler(GetDocHandler))
	m.Put("/services/:name/doc", AuthorizationRequiredHandler(AddDocHandler))
	m.Put("/services/:service/:team", AuthorizationRequiredHandler(GrantServiceAccessToTeamHandler))
	m.Del("/services/:service/:team", AuthorizationRequiredHandler(RevokeServiceAccessFromTeamHandler))

	m.Del("/apps/:name", AuthorizationRequiredHandler(AppDelete))
	m.Get("/apps/:name/repository/clone", Handler(CloneRepositoryHandler))
	m.Get("/apps/:name/avaliable", Handler(AppIsAvailableHandler))
	m.Get("/apps/:name", AuthorizationRequiredHandler(AppInfo))
	m.Post("/apps/:name/run", AuthorizationRequiredHandler(RunCommand))
	m.Get("/apps/:name/restart", AuthorizationRequiredHandler(RestartHandler))
	m.Get("/apps/:name/env", AuthorizationRequiredHandler(GetEnv))
	m.Post("/apps/:name/env", AuthorizationRequiredHandler(SetEnv))
	m.Del("/apps/:name/env", AuthorizationRequiredHandler(UnsetEnv))
	m.Get("/apps", AuthorizationRequiredHandler(AppList))
	m.Post("/apps", AuthorizationRequiredHandler(CreateAppHandler))
	m.Put("/apps/:name/units", AuthorizationRequiredHandler(AddUnitsHandler))
	m.Del("/apps/:name/unit", AuthorizationRequiredHandler(RemoveUnitHandler))
	m.Del("/apps/:name/units", AuthorizationRequiredHandler(RemoveUnitsHandler))
	m.Put("/apps/:app/:team", AuthorizationRequiredHandler(GrantAccessToTeamHandler))
	m.Del("/apps/:app/:team", AuthorizationRequiredHandler(RevokeAccessFromTeamHandler))
	m.Get("/apps/:name/log", AuthorizationRequiredHandler(AppLog))
	m.Post("/apps/:name/log", Handler(AddLogHandler))

	m.Post("/users", Handler(CreateUser))
	m.Post("/users/:email/tokens", Handler(Login))
	m.Put("/users/password", AuthorizationRequiredHandler(ChangePassword))
	m.Del("/users", AuthorizationRequiredHandler(RemoveUser))
	m.Post("/users/keys", AuthorizationRequiredHandler(AddKeyToUser))
	m.Del("/users/keys", AuthorizationRequiredHandler(RemoveKeyFromUser))

	m.Get("/teams", AuthorizationRequiredHandler(ListTeams))
	m.Post("/teams", AuthorizationRequiredHandler(CreateTeam))
	m.Del("/teams/:name", AuthorizationRequiredHandler(RemoveTeam))
	m.Put("/teams/:team/:user", AuthorizationRequiredHandler(AddUserToTeam))
	m.Del("/teams/:team/:user", AuthorizationRequiredHandler(RemoveUserFromTeam))

	m.Get("/healers", Handler(healers))

	if !*dry {
		provisioner, err := config.GetString("provisioner")
		if err != nil {
			fmt.Printf("Warning: %q didn't declare a provisioner, using default provisioner.\n", configFile)
			provisioner = "juju"
		}
		app.Provisioner, err = provision.Get(provisioner)
		if err != nil {
			fatal(err)
		}
		fmt.Printf("Using %q provisioner.\n\n", provisioner)

		listen, err := config.GetString("listen")
		if err != nil {
			fatal(err)
		}
		tls, _ := config.GetBool("use-tls")
		if tls {
			certFile, err := config.GetString("tls-cert-file")
			if err != nil {
				fatal(err)
			}
			keyFile, err := config.GetString("tls-key-file")
			if err != nil {
				fatal(err)
			}
			fmt.Printf("tsuru HTTP/TLS server listening at %s...\n", listen)
			fatal(http.ListenAndServeTLS(listen, certFile, keyFile, m))
		} else {
			fmt.Printf("tsuru HTTP server listening at %s...\n", listen)
			fatal(http.ListenAndServe(listen, m))
		}
	}
}
<commit_msg>api: added route for healer handler.<commit_after>// Copyright 2013 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"flag"
	"fmt"
	"github.com/bmizerany/pat"
	"github.com/globocom/config"
	"github.com/globocom/tsuru/app"
	"github.com/globocom/tsuru/log"
	"github.com/globocom/tsuru/provision"
	_ "github.com/globocom/tsuru/provision/juju"
	stdlog "log"
	"log/syslog"
	"net/http"
	"os"
)

func fatal(err error) {
	fmt.Fprintln(os.Stderr, err)
	log.Fatal(err)
}

func main() {
	logger, err := syslog.NewLogger(syslog.LOG_INFO, stdlog.LstdFlags)
	if err != nil {
		stdlog.Fatal(err)
	}
	log.SetLogger(logger)
	configFile := flag.String("config", "/etc/tsuru/tsuru.conf", "tsuru config file")
	dry := flag.Bool("dry", false, "dry-run: does not start the server (for testing purpose)")
	flag.Parse()
	err = config.ReadConfigFile(*configFile)
	if err != nil {
		fatal(err)
	}
	connString, err := config.GetString("database:url")
	if err != nil {
		fatal(err)
	}
	dbName, err := config.GetString("database:name")
	if err != nil {
		fatal(err)
	}
	fmt.Printf("Using the database %q from the server %q.\n\n", dbName, connString)

	m := pat.New()

	m.Get("/services/instances", AuthorizationRequiredHandler(ServicesInstancesHandler))
	m.Post("/services/instances", AuthorizationRequiredHandler(CreateInstanceHandler))
	m.Put("/services/instances/:instance/:app", AuthorizationRequiredHandler(BindHandler))
	m.Del("/services/instances/:instance/:app", AuthorizationRequiredHandler(UnbindHandler))
	m.Del("/services/c/instances/:name", AuthorizationRequiredHandler(RemoveServiceInstanceHandler))
	m.Get("/services/instances/:instance/status", AuthorizationRequiredHandler(ServiceInstanceStatusHandler))

	m.Get("/services", AuthorizationRequiredHandler(ServicesHandler))
	m.Post("/services", AuthorizationRequiredHandler(CreateHandler))
	m.Put("/services", AuthorizationRequiredHandler(UpdateHandler))
	m.Del("/services/:name", AuthorizationRequiredHandler(DeleteHandler))
	m.Get("/services/:name", AuthorizationRequiredHandler(ServiceInfoHandler))
	m.Get("/services/c/:name/doc", AuthorizationRequiredHandler(Doc))
	m.Get("/services/:name/doc", AuthorizationRequiredHandler(GetDocHandler))
	m.Put("/services/:name/doc", AuthorizationRequiredHandler(AddDocHandler))
	m.Put("/services/:service/:team", AuthorizationRequiredHandler(GrantServiceAccessToTeamHandler))
	m.Del("/services/:service/:team", AuthorizationRequiredHandler(RevokeServiceAccessFromTeamHandler))

	m.Del("/apps/:name", AuthorizationRequiredHandler(AppDelete))
	m.Get("/apps/:name/repository/clone", Handler(CloneRepositoryHandler))
	m.Get("/apps/:name/avaliable", Handler(AppIsAvailableHandler))
	m.Get("/apps/:name", AuthorizationRequiredHandler(AppInfo))
	m.Post("/apps/:name/run", AuthorizationRequiredHandler(RunCommand))
	m.Get("/apps/:name/restart", AuthorizationRequiredHandler(RestartHandler))
	m.Get("/apps/:name/env", AuthorizationRequiredHandler(GetEnv))
	m.Post("/apps/:name/env", AuthorizationRequiredHandler(SetEnv))
	m.Del("/apps/:name/env", AuthorizationRequiredHandler(UnsetEnv))
	m.Get("/apps", AuthorizationRequiredHandler(AppList))
	m.Post("/apps", AuthorizationRequiredHandler(CreateAppHandler))
	m.Put("/apps/:name/units", AuthorizationRequiredHandler(AddUnitsHandler))
	m.Del("/apps/:name/unit", AuthorizationRequiredHandler(RemoveUnitHandler))
	m.Del("/apps/:name/units", AuthorizationRequiredHandler(RemoveUnitsHandler))
	m.Put("/apps/:app/:team", AuthorizationRequiredHandler(GrantAccessToTeamHandler))
	m.Del("/apps/:app/:team", AuthorizationRequiredHandler(RevokeAccessFromTeamHandler))
	m.Get("/apps/:name/log", AuthorizationRequiredHandler(AppLog))
	m.Post("/apps/:name/log", Handler(AddLogHandler))

	m.Post("/users", Handler(CreateUser))
	m.Post("/users/:email/tokens", Handler(Login))
	m.Put("/users/password", AuthorizationRequiredHandler(ChangePassword))
	m.Del("/users", AuthorizationRequiredHandler(RemoveUser))
	m.Post("/users/keys", AuthorizationRequiredHandler(AddKeyToUser))
	m.Del("/users/keys", AuthorizationRequiredHandler(RemoveKeyFromUser))

	m.Get("/teams", AuthorizationRequiredHandler(ListTeams))
	m.Post("/teams", AuthorizationRequiredHandler(CreateTeam))
	m.Del("/teams/:name", AuthorizationRequiredHandler(RemoveTeam))
	m.Put("/teams/:team/:user", AuthorizationRequiredHandler(AddUserToTeam))
	m.Del("/teams/:team/:user", AuthorizationRequiredHandler(RemoveUserFromTeam))

	m.Get("/healers", Handler(healers))
	m.Get("/healers/:healer", Handler(healer))

	if !*dry {
		provisioner, err := config.GetString("provisioner")
		if err != nil {
			fmt.Printf("Warning: %q didn't declare a provisioner, using default provisioner.\n", configFile)
			provisioner = "juju"
		}
		app.Provisioner, err = provision.Get(provisioner)
		if err != nil {
			fatal(err)
		}
		fmt.Printf("Using %q provisioner.\n\n", provisioner)

		listen, err := config.GetString("listen")
		if err != nil {
			fatal(err)
		}
		tls, _ := config.GetBool("use-tls")
		if tls {
			certFile, err := config.GetString("tls-cert-file")
			if err != nil {
				fatal(err)
			}
			keyFile, err := config.GetString("tls-key-file")
			if err != nil {
				fatal(err)
			}
			fmt.Printf("tsuru HTTP/TLS server listening at %s...\n", listen)
			fatal(http.ListenAndServeTLS(listen, certFile, keyFile, m))
		} else {
			fmt.Printf("tsuru HTTP server listening at %s...\n", listen)
			fatal(http.ListenAndServe(listen, m))
		}
	}
}
<|endoftext|>


<commit_before>package questagbot

import (
	"encoding/json"
	"fmt"
	"math/rand"
	"net/http"
	"os"
	"strconv"
	"strings"

	//hexapic "github.com/blan4/hexapic/core"
	"github.com/blan4/QuestagBot/telegram"
	"github.com/codegangsta/martini"
	"github.com/codegangsta/martini-contrib/binding"
	"github.com/joho/godotenv"
	"github.com/mjibson/goon"

	"appengine"
	"appengine/datastore"
)

var random = rand.New(rand.NewSource(42))

// Global is struct for saving state
type Global struct {
	InstagramClientID string
	APIURL            string
	Tags              []string
}

// Question is struct to store question object
type Question struct {
	Answer   string   `json:"answer"`
	Variants []string `json:"variants"`
}

// Gamer is object to store in appengine datastore
type Gamer struct {
	ChatID          int        `json:"chat_id"`
	Questions       []Question `json:"questions"`
	CurrentQuestion int        `json:"current_question"`
	RightAnswers    int        `json:"right_answers"`
	WrongAnswers    int        `json:"wrong_answers"`
}

// GamerData is wrapper for appengine data store
type GamerData struct {
	ChatID    string `datastore:"-" goon:"id"`
	GamerBlob string
	Gamer     *Gamer `datastore:"-"`
}

// Load is google store Question struct loader
func (data *GamerData) Load(p <-chan datastore.Property) error {
	if err := datastore.LoadStruct(data, p); err != nil {
		return err
	}
	return nil
}

// Save is google store Question struct saver
func (data *GamerData) Save(p chan<- datastore.Property) error {
	defer close(p)
	blob, err := json.Marshal(data.Gamer)
	if err != nil {
		panic(err)
	}

	p <- datastore.Property{
		Name:    "GamerBlob",
		Value:   string(blob),
		NoIndex: true,
	}
	return nil
}

func findGamer(c appengine.Context, gamer *Gamer) error {
	g := goon.FromContext(c)
	data := new(GamerData)
	data.ChatID = strconv.Itoa(gamer.ChatID)
	c.Debugf("data: %v", gamer.ChatID)
	if err := g.Get(data); err != nil {
		return err
	}
	return json.Unmarshal([]byte(data.GamerBlob), gamer)
}

func saveGamer(c appengine.Context, gamer *Gamer) (err error) {
	g := goon.FromContext(c)
	data := new(GamerData)
	data.ChatID = strconv.Itoa(gamer.ChatID)
	data.Gamer = gamer
	g.Put(data)

	return
}

func appEngine(c martini.Context, r *http.Request) {
	c.Map(appengine.NewContext(r))
}

var global Global

func init() {
	godotenv.Load("secrets.env")
	global.Tags = strings.Split(os.Getenv("TAGS"), ",")
	global.InstagramClientID = os.Getenv("INSTAGRAM_CLIENT_ID")
	global.APIURL = fmt.Sprintf("https://api.telegram.org/bot%v/", os.Getenv("TELEGRAM_KEY"))

	m := martini.Classic()
	m.Use(appEngine)
	m.Use(martini.Logger())
	m.Get("/", func() string {
		return "Hello world"
	})
	m.Post("/bothook", binding.Bind(telegram.Update{}), func(c appengine.Context, update telegram.Update, w http.ResponseWriter) string {
		c.Infof("%v", update)
		gamer, err := findOrCreateGamer(update, c)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			c.Errorf("Can't find or create gamer: %v", err)
		}
		c.Infof("Gamer : %v", gamer)
		//sendMessage(c, apiURL, update, "Hello")
		// if err := sendChatAction(c, update, "upload_photo"); err != nil {
		// 	log.Criticalf(c, "Can't sendChatAction %v", err)
		// }
		// if err := sendPhoto(c, update, ""); err != nil {
		// 	log.Criticalf(c, "Can't sendPhoto %v", err)
		// }
		return strconv.Itoa(update.ID)
	})
	http.Handle("/", m)
}

func findOrCreateGamer(update telegram.Update, c appengine.Context) (*Gamer, error) {
	gamer := new(Gamer)
	chatID := update.Message.Chat.ID
	gamer.ChatID = chatID
	if err := findGamer(c, gamer); err != nil {
		c.Infof("Can't find gamer object for this chat: %v, %v", chatID, err)
		gamer.handleStart()
		if err := saveGamer(c, gamer); err != nil {
			c.Errorf("Can't store in DB new gamer %v: %v", gamer, err)
			return nil, err
		}
		c.Infof("Saved: %v", gamer.ChatID)
	} else {
		c.Infof("Find gamer with id %v", chatID)
	}
	return gamer, nil
}

// func generateImage() {
// 	hexapicAPI := hexapic.NewSearchApi(global.InstagramClientID, httpClient)
// 	hexapicAPI.Count = 4
// 	imgs := hexapicAPI.SearchByTag(question.Answer)
// 	img := hexapic.GenerateCollage(imgs, 2, 2)
// 	question := state.NextQuestion()
// }

// GetCurrentQuestion is helper method to get current question
func (gamer *Gamer) GetCurrentQuestion() Question {
	return gamer.Questions[gamer.CurrentQuestion]
}
func (gamer *Gamer) handleStart() {
	gamer.Questions = generateQuestionsQueue()
	gamer.CurrentQuestion = 0
}
func (gamer *Gamer) handleStop() {
	gamer.Questions = nil
	gamer.CurrentQuestion = 0
}
func (gamer *Gamer) handleTop()  {}
func (gamer *Gamer) handleHelp() {}
func (gamer *Gamer) handleAnswer(answer string) (isRight bool) {
	currentQuestion := gamer.GetCurrentQuestion()
	if currentQuestion.Answer == answer {
		gamer.RightAnswers++
		isRight = true
	} else {
		gamer.WrongAnswers++
		isRight = false
	}

	return
}

// NextQuestion return next question
func (gamer *Gamer) NextQuestion() (question Question) {
	question = gamer.Questions[gamer.CurrentQuestion]
	gamer.CurrentQuestion++
	if gamer.CurrentQuestion == len(global.Tags) {
		gamer.CurrentQuestion = 0
	}
	return
}

func generateQuestionsQueue() []Question {
	tags := global.Tags
	answers := random.Perm(len(tags))
	questions := make([]Question, 0, len(tags))
	for answer := range answers {
		variants := perm(4, len(tags), answer)

		variantsStr := make([]string, len(variants))
		for i, variant := range variants {
			variantsStr[i] = tags[variant]
		}

		question := Question{
			Answer:   tags[answer],
			Variants: variantsStr,
		}

		questions = append(questions, question)
	}

	return questions
}

func perm(size int, limit int, exclude int) []int {
	array := make([]int, size)
	i := 0
	for i < size-1 {
		r := rand.Intn(limit)
		if r != exclude {
			array[i] = r
			i++
		}
	}
	array[size-1] = exclude
	return array
}
<commit_msg>change appengine on managed appengine<commit_after>package questagbot

import (
	"encoding/json"
	"fmt"
	"math/rand"
	"net/http"
	"os"
	"strconv"
	"strings"

	"golang.org/x/net/context"

	//hexapic "github.com/blan4/hexapic/core"
	"github.com/blan4/QuestagBot/telegram"
	"github.com/codegangsta/martini"
	"github.com/codegangsta/martini-contrib/binding"
	"github.com/joho/godotenv"

	"google.golang.org/appengine"
	"google.golang.org/appengine/datastore"
	"google.golang.org/appengine/log"
)

var random = rand.New(rand.NewSource(42))

// Global is struct for saving state
type Global struct {
	InstagramClientID string
	APIURL            string
	Tags              []string
}

// Question is struct to store question object
type Question struct {
	Answer   string   `json:"answer"`
	Variants []string `json:"variants"`
}

// Gamer is object to store in appengine datastore
type Gamer struct {
	ChatID          int        `json:"chat_id"`
	Questions       []Question `json:"questions"`
	CurrentQuestion int        `json:"current_question"`
	RightAnswers    int        `json:"right_answers"`
	WrongAnswers    int        `json:"wrong_answers"`
}

// GamerData is wrapper for appengine data store
type GamerData struct {
	GamerBlob string
	Gamer     *Gamer `datastore:"-"`
}

// Load is google store Question struct loader
func (data *GamerData) Load(p []datastore.Property) error {
	if err := datastore.LoadStruct(data, p); err != nil {
		return err
	}
	data.Gamer = new(Gamer)
	return json.Unmarshal([]byte(data.GamerBlob), data.Gamer)
}

// Save is google store Question struct saver
func (data *GamerData) Save() ([]datastore.Property, error) {
	blob, err := json.Marshal(data.Gamer)
	if err != nil {
		panic(err)
	}

	return []datastore.Property{
		datastore.Property{
			Name:    "GamerBlob",
			Value:   string(blob),
			NoIndex: true,
		},
	}, nil
}

func findGamer(c context.Context, id int64) (*Gamer, error) {
	data := new(GamerData)
	key := datastore.NewKey(c, "Gamer", "", id, nil)
	if err := datastore.Get(c, key, data); err != nil {
		return new(Gamer), err
	}
	return data.Gamer, nil
}

func saveGamer(c context.Context, gamer *Gamer) (err error) {
	data := new(GamerData)
	data.Gamer = gamer
	key := datastore.NewKey(c, "Gamer", "", int64(gamer.ChatID), nil)
	_, err = datastore.Put(c, key, data)
	return
}

func appEngine(c martini.Context, r *http.Request) {
	c.Map(appengine.NewContext(r))
}

var global Global

func init() {
	godotenv.Load("secrets.env")
	global.Tags = strings.Split(os.Getenv("TAGS"), ",")
	global.InstagramClientID = os.Getenv("INSTAGRAM_CLIENT_ID")
	global.APIURL = fmt.Sprintf("https://api.telegram.org/bot%v/", os.Getenv("TELEGRAM_KEY"))

	m := martini.Classic()
	m.Use(appEngine)
	m.Use(martini.Logger())
	m.Get("/", func() string {
		return "Hello world"
	})
	m.Post("/bothook", binding.Bind(telegram.Update{}), func(c context.Context, update telegram.Update, w http.ResponseWriter) string {
		log.Infof(c, "%v", update)
		gamer, err := findOrCreateGamer(update, c)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			log.Errorf(c, "Can't find or create gamer: %v", err)
		}
		log.Infof(c, "Gamer : %v", gamer.ChatID)
		//handleComand(update)
		//sendMessage(c, apiURL, update, "Hello")
		// if err := sendChatAction(c, update, "upload_photo"); err != nil {
		// 	log.Criticalf(c, "Can't sendChatAction %v", err)
		// }
		// if err := sendPhoto(c, update, ""); err != nil {
		// 	log.Criticalf(c, "Can't sendPhoto %v", err)
		// }
		return strconv.Itoa(update.ID)
	})
	http.Handle("/", m)
}

func findOrCreateGamer(update telegram.Update, c context.Context) (gamer *Gamer, err error) {
	chatID := update.Message.Chat.ID
	if gamer, err = findGamer(c, int64(chatID)); err != nil {
		log.Infof(c, "Can't find gamer object for this chat: %v, %v", chatID, err)
		gamer.handleStart()
		gamer.ChatID = chatID
		if err := saveGamer(c, gamer); err != nil {
			log.Errorf(c, "Can't store in DB new gamer %v: %v", gamer, err)
			return nil, err
		}
		log.Infof(c, "Saved: %v", gamer.ChatID)
	} else {
		log.Infof(c, "Find gamer with id %v", chatID)
	}
	return gamer, nil
}

// func generateImage() {
// 	hexapicAPI := hexapic.NewSearchApi(global.InstagramClientID, httpClient)
// 	hexapicAPI.Count = 4
// 	imgs := hexapicAPI.SearchByTag(question.Answer)
// 	img := hexapic.GenerateCollage(imgs, 2, 2)
// 	question := state.NextQuestion()
// }

// GetCurrentQuestion is helper method to get current question
func (gamer *Gamer) GetCurrentQuestion() Question {
	return gamer.Questions[gamer.CurrentQuestion]
}
func (gamer *Gamer) handleStart() {
	gamer.Questions = generateQuestionsQueue()
	gamer.CurrentQuestion = 0
}
func (gamer *Gamer) handleStop() {
	gamer.Questions = nil
	gamer.CurrentQuestion = 0
}
func (gamer *Gamer) handleTop()  {}
func (gamer *Gamer) handleHelp() {}
func (gamer *Gamer) handleAnswer(answer string) (isRight bool) {
	currentQuestion := gamer.GetCurrentQuestion()
	if currentQuestion.Answer == answer {
		gamer.RightAnswers++
		isRight = true
	} else {
		gamer.WrongAnswers++
		isRight = false
	}

	return
}

// NextQuestion return next question
func (gamer *Gamer) NextQuestion() (question Question) {
	question = gamer.Questions[gamer.CurrentQuestion]
	gamer.CurrentQuestion++
	if gamer.CurrentQuestion == len(global.Tags) {
		gamer.CurrentQuestion = 0
	}
	return
}

func generateQuestionsQueue() []Question {
	tags := global.Tags
	answers := random.Perm(len(tags))
	questions := make([]Question, 0, len(tags))
	for answer := range answers {
		variants := perm(4, len(tags), answer)

		variantsStr := make([]string, len(variants))
		for i, variant := range variants {
			variantsStr[i] = tags[variant]
		}

		question := Question{
			Answer:   tags[answer],
			Variants: variantsStr,
		}

		questions = append(questions, question)
	}

	return questions
}

func perm(size int, limit int, exclude int) []int {
	array := make([]int, size)
	i := 0
	for i < size-1 {
		r := rand.Intn(limit)
		if r != exclude {
			array[i] = r
			i++
		}
	}
	array[size-1] = exclude
	return array
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/gin-gonic/gin"
)

func main() {
	//gin.SetMode(gin.ReleaseMode) //For Release
	router := gin.Default()
	router.Use(Cors())

	v1 := router.Group("api/v1")

	users := v1.Group("users")
	{
		users.POST("/", PostUser)
		users.GET("/", GetUsers)
		users.GET("/:id", GetUser)
		users.PUT("/:id", UpdateUser)
		users.DELETE("/:id", DeleteUser)
		users.OPTIONS("/", OptionsUser)
		users.OPTIONS("/:id", OptionsUser)
	}

	router.Run(":8081")
}
<commit_msg>Update main.go<commit_after>package main

import (
	"github.com/gin-gonic/gin"
)

func main() {
	//gin.SetMode(gin.ReleaseMode) //For Release
	router := gin.Default()
	router.Use(Cors())

	v1 := router.Group("api/v1")

	users := v1.Group("users")
	{
		users.POST("/", PostUser)
		users.GET("/", GetUsers)
		users.GET("/:id", GetUser)
		users.PUT("/:id", UpdateUser)
		users.DELETE("/:id", DeleteUser)
		users.OPTIONS("/", OptionsUser)
		users.OPTIONS("/:id", OptionsUser)
	}

	router.Run(":8900")
}
<|endoftext|>