<commit_before>// Copyright 2013 bee 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.

// Bee is a tool for developling applications based on beego framework.
package main

import (
	"flag"
	"fmt"
	"html/template"
	"io"
	"log"
	"os"
	"strings"
)

const version = "1.2.1"

type Command struct {
	// Run runs the command.
	// The args are the arguments after the command name.
	Run func(cmd *Command, args []string) int

	// UsageLine is the one-line usage message.
	// The first word in the line is taken to be the command name.
	UsageLine string

	// Short is the short description shown in the 'go help' output.
	Short template.HTML

	// Long is the long message shown in the 'go help <this-command>' output.
	Long template.HTML

	// Flag is a set of flags specific to this command.
	Flag flag.FlagSet

	// CustomFlags indicates that the command will do its own
	// flag parsing.
	CustomFlags bool
}

// Name returns the command's name: the first word in the usage line.
func (c *Command) Name() string {
	name := c.UsageLine
	i := strings.Index(name, " ")
	if i >= 0 {
		name = name[:i]
	}
	return name
}

func (c *Command) Usage() {
	fmt.Fprintf(os.Stderr, "usage: %s\n\n", c.UsageLine)
	fmt.Fprintf(os.Stderr, "%s\n", strings.TrimSpace(string(c.Long)))
	os.Exit(2)
}

// Runnable reports whether the command can be run; otherwise
// it is a documentation pseudo-command such as importpath.
func (c *Command) Runnable() bool {
	return c.Run != nil
}

var commands = []*Command{
	cmdNew,
	cmdRun,
	cmdPack,
	cmdApiapp,
	cmdRouter,
	cmdTest,
	cmdBale,
	cmdVersion,
	cmdGenerate,
	cmdRundocs,
	cmdMigrate,
}

func main() {
	flag.Usage = usage
	flag.Parse()
	log.SetFlags(0)

	args := flag.Args()
	if len(args) < 1 {
		usage()
	}

	if args[0] == "help" {
		help(args[1:])
		return
	}

	for _, cmd := range commands {
		if cmd.Name() == args[0] && cmd.Run != nil {
			cmd.Flag.Usage = func() { cmd.Usage() }
			if cmd.CustomFlags {
				args = args[1:]
			} else {
				cmd.Flag.Parse(args[1:])
				args = cmd.Flag.Args()
			}
			os.Exit(cmd.Run(cmd, args))
			return
		}
	}

	fmt.Fprintf(os.Stderr, "bee: unknown subcommand %q\nRun 'bee help' for usage.\n", args[0])
	os.Exit(2)
}

var usageTemplate = `Bee is a tool for managing beego framework.

Usage:

	bee command [arguments]

The commands are:
{{range .}}{{if .Runnable}}
    {{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}}

Use "bee help [command]" for more information about a command.

Additional help topics:
{{range .}}{{if not .Runnable}}
    {{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}}

Use "bee help [topic]" for more information about that topic.

`

var helpTemplate = `{{if .Runnable}}usage: bee {{.UsageLine}}

{{end}}{{.Long | trim}}
`

func usage() {
	tmpl(os.Stdout, usageTemplate, commands)
	os.Exit(2)
}

func tmpl(w io.Writer, text string, data interface{}) {
	t := template.New("top")
	t.Funcs(template.FuncMap{"trim": func(s template.HTML) template.HTML {
		return template.HTML(strings.TrimSpace(string(s)))
	}})
	template.Must(t.Parse(text))
	if err := t.Execute(w, data); err != nil {
		panic(err)
	}
}

func help(args []string) {
	if len(args) == 0 {
		usage()
		// not exit 2: succeeded at 'go help'.
		return
	}
	if len(args) != 1 {
		fmt.Fprintf(os.Stdout, "usage: bee help command\n\nToo many arguments given.\n")
		os.Exit(2) // failed at 'bee help'
	}

	arg := args[0]

	for _, cmd := range commands {
		if cmd.Name() == arg {
			tmpl(os.Stdout, helpTemplate, cmd)
			// not exit 2: succeeded at 'go help cmd'.
			return
		}
	}

	fmt.Fprintf(os.Stdout, "Unknown help topic %#q.  Run 'bee help'.\n", arg)
	os.Exit(2) // failed at 'bee help cmd'
}
<commit_msg>comments router test rundocs<commit_after>// Copyright 2013 bee 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.

// Bee is a tool for developling applications based on beego framework.
package main

import (
	"flag"
	"fmt"
	"html/template"
	"io"
	"log"
	"os"
	"strings"
)

const version = "1.2.1"

type Command struct {
	// Run runs the command.
	// The args are the arguments after the command name.
	Run func(cmd *Command, args []string) int

	// UsageLine is the one-line usage message.
	// The first word in the line is taken to be the command name.
	UsageLine string

	// Short is the short description shown in the 'go help' output.
	Short template.HTML

	// Long is the long message shown in the 'go help <this-command>' output.
	Long template.HTML

	// Flag is a set of flags specific to this command.
	Flag flag.FlagSet

	// CustomFlags indicates that the command will do its own
	// flag parsing.
	CustomFlags bool
}

// Name returns the command's name: the first word in the usage line.
func (c *Command) Name() string {
	name := c.UsageLine
	i := strings.Index(name, " ")
	if i >= 0 {
		name = name[:i]
	}
	return name
}

func (c *Command) Usage() {
	fmt.Fprintf(os.Stderr, "usage: %s\n\n", c.UsageLine)
	fmt.Fprintf(os.Stderr, "%s\n", strings.TrimSpace(string(c.Long)))
	os.Exit(2)
}

// Runnable reports whether the command can be run; otherwise
// it is a documentation pseudo-command such as importpath.
func (c *Command) Runnable() bool {
	return c.Run != nil
}

var commands = []*Command{
	cmdNew,
	cmdRun,
	cmdPack,
	cmdApiapp,
	//cmdRouter,
	//cmdTest,
	cmdBale,
	cmdVersion,
	cmdGenerate,
	//cmdRundocs,
	cmdMigrate,
}

func main() {
	flag.Usage = usage
	flag.Parse()
	log.SetFlags(0)

	args := flag.Args()
	if len(args) < 1 {
		usage()
	}

	if args[0] == "help" {
		help(args[1:])
		return
	}

	for _, cmd := range commands {
		if cmd.Name() == args[0] && cmd.Run != nil {
			cmd.Flag.Usage = func() { cmd.Usage() }
			if cmd.CustomFlags {
				args = args[1:]
			} else {
				cmd.Flag.Parse(args[1:])
				args = cmd.Flag.Args()
			}
			os.Exit(cmd.Run(cmd, args))
			return
		}
	}

	fmt.Fprintf(os.Stderr, "bee: unknown subcommand %q\nRun 'bee help' for usage.\n", args[0])
	os.Exit(2)
}

var usageTemplate = `Bee is a tool for managing beego framework.

Usage:

	bee command [arguments]

The commands are:
{{range .}}{{if .Runnable}}
    {{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}}

Use "bee help [command]" for more information about a command.

Additional help topics:
{{range .}}{{if not .Runnable}}
    {{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}}

Use "bee help [topic]" for more information about that topic.

`

var helpTemplate = `{{if .Runnable}}usage: bee {{.UsageLine}}

{{end}}{{.Long | trim}}
`

func usage() {
	tmpl(os.Stdout, usageTemplate, commands)
	os.Exit(2)
}

func tmpl(w io.Writer, text string, data interface{}) {
	t := template.New("top")
	t.Funcs(template.FuncMap{"trim": func(s template.HTML) template.HTML {
		return template.HTML(strings.TrimSpace(string(s)))
	}})
	template.Must(t.Parse(text))
	if err := t.Execute(w, data); err != nil {
		panic(err)
	}
}

func help(args []string) {
	if len(args) == 0 {
		usage()
		// not exit 2: succeeded at 'go help'.
		return
	}
	if len(args) != 1 {
		fmt.Fprintf(os.Stdout, "usage: bee help command\n\nToo many arguments given.\n")
		os.Exit(2) // failed at 'bee help'
	}

	arg := args[0]

	for _, cmd := range commands {
		if cmd.Name() == arg {
			tmpl(os.Stdout, helpTemplate, cmd)
			// not exit 2: succeeded at 'go help cmd'.
			return
		}
	}

	fmt.Fprintf(os.Stdout, "Unknown help topic %#q.  Run 'bee help'.\n", arg)
	os.Exit(2) // failed at 'bee help cmd'
}
<|endoftext|>


<commit_before>// Package bj4 provides one-task-at-a-time task scheduling.
package bj4

import "time"

const (
	minWaitTime = 1 * time.Hour
)

// Config configures the scheduler
type Config struct {
	// Logger provides logging. Leaving it nil and BJ4 will not log at all.
	// If basic logging is needed, use BuiltinLogger, or if you want
	// fancier one, LogrusLogger.
	Logger Logger
}

// BJ4 is the scheduler struct itself. Refer to its member functions for
// details.
type BJ4 struct {
	tasks     map[string]*Task
	taskAdded chan *Task
	logger    Logger
}

// New initiates the scheduler
func New(config *Config) *BJ4 {
	if config.Logger == nil {
		config.Logger = &NilLogger{}
	}
	return &BJ4{
		tasks:     make(map[string]*Task),
		taskAdded: make(chan *Task, 16),
		logger:    config.Logger,
	}
}

// Start starts the scheduler
func (bj4 *BJ4) Start() {
	bj4.logger.OnStart()
	for {
		bj4.run()
		bj4.wait()
	}
}

func (bj4 *BJ4) run() {
	for _, task := range bj4.tasks {
		task.run()
	}
}

func (bj4 *BJ4) wait() {
	for {
		waitTime := bj4.getWaitTime()
		timeout := make(chan bool, 1)
		go func() {
			time.Sleep(waitTime)
			timeout <- true
		}()
		select {
		case task := <-bj4.taskAdded:
			bj4.enqueueTask(task)
		case <-timeout:
			return
		}
	}
}

func (bj4 *BJ4) enqueueTask(task *Task) {
	bj4.tasks[task.Name] = task
}

func (bj4 *BJ4) getWaitTime() time.Duration {
	wt := minWaitTime
	now := time.Now()
	for _, task := range bj4.tasks {
		t := task.NextUpdate.Sub(now)
		if wt > t {
			wt = t
		}
	}

	return wt
}

// SetTask runs the task on the scheduler as soon as possible
func (bj4 *BJ4) SetTask(name string, fn TaskFunction) <-chan error {
	return bj4.SetScheduledTask(name, fn, time.Now())
}

// SetScheduledTask sets the task running on specific time
func (bj4 *BJ4) SetScheduledTask(name string, fn TaskFunction, nextUpdate time.Time) <-chan error {
	task := &Task{
		TaskStatus: TaskStatus{
			Name:       name,
			NextUpdate: nextUpdate,
			Status:     "added",
		},
		function:  fn,
		bj4:       bj4,
		errorChan: make(chan error, 1),
	}
	bj4.taskAdded <- task

	bj4.logger.OnTaskAdded(task)

	return task.errorChan
}

// GetTasks gets the tasks from the scheduler in slice format
func (bj4 *BJ4) GetTasks() []TaskStatus {
	taskStatus := make([]TaskStatus, len(bj4.tasks))
	idx := 0
	for _, task := range bj4.tasks {
		taskStatus[idx] = task.TaskStatus
		idx++
	}
	return taskStatus
}
<commit_msg>feat: configurable minWaitTime<commit_after>// Package bj4 provides one-task-at-a-time task scheduling.
package bj4

import "time"

const (
	minWaitTime = 1 * time.Hour
)

// Config configures the scheduler
type Config struct {
	// Logger provides logging. Leaving it nil and BJ4 will not log at all.
	// If basic logging is needed, use BuiltinLogger, or if you want
	// fancier one, LogrusLogger.
	Logger Logger
	// MinWaitTime is the minimum wait time for scheduler.
	MinWaitTime time.Duration
}

// BJ4 is the scheduler struct itself. Refer to its member functions for
// details.
type BJ4 struct {
	tasks       map[string]*Task
	taskAdded   chan *Task
	logger      Logger
	minWaitTime time.Duration
}

// New initiates the scheduler
func New(config *Config) *BJ4 {
	if config.Logger == nil {
		config.Logger = &NilLogger{}
	}
	if config.MinWaitTime == 0 {
		config.MinWaitTime = minWaitTime
	}
	return &BJ4{
		tasks:       make(map[string]*Task),
		taskAdded:   make(chan *Task, 16),
		logger:      config.Logger,
		minWaitTime: config.MinWaitTime,
	}
}

// Start starts the scheduler
func (bj4 *BJ4) Start() {
	bj4.logger.OnStart()
	for {
		bj4.run()
		bj4.wait()
	}
}

func (bj4 *BJ4) run() {
	for _, task := range bj4.tasks {
		task.run()
	}
}

func (bj4 *BJ4) wait() {
	for {
		waitTime := bj4.getWaitTime()
		timeout := make(chan bool, 1)
		go func() {
			time.Sleep(waitTime)
			timeout <- true
		}()
		select {
		case task := <-bj4.taskAdded:
			bj4.enqueueTask(task)
		case <-timeout:
			return
		}
	}
}

func (bj4 *BJ4) enqueueTask(task *Task) {
	bj4.tasks[task.Name] = task
}

func (bj4 *BJ4) getWaitTime() time.Duration {
	wt := bj4.minWaitTime
	now := time.Now()
	for _, task := range bj4.tasks {
		t := task.NextUpdate.Sub(now)
		if wt > t {
			wt = t
		}
	}

	return wt
}

// SetTask runs the task on the scheduler as soon as possible
func (bj4 *BJ4) SetTask(name string, fn TaskFunction) <-chan error {
	return bj4.SetScheduledTask(name, fn, time.Now())
}

// SetScheduledTask sets the task running on specific time
func (bj4 *BJ4) SetScheduledTask(name string, fn TaskFunction, nextUpdate time.Time) <-chan error {
	task := &Task{
		TaskStatus: TaskStatus{
			Name:       name,
			NextUpdate: nextUpdate,
			Status:     "added",
		},
		function:  fn,
		bj4:       bj4,
		errorChan: make(chan error, 1),
	}
	bj4.taskAdded <- task

	bj4.logger.OnTaskAdded(task)

	return task.errorChan
}

// GetTasks gets the tasks from the scheduler in slice format
func (bj4 *BJ4) GetTasks() []TaskStatus {
	taskStatus := make([]TaskStatus, len(bj4.tasks))
	idx := 0
	for _, task := range bj4.tasks {
		taskStatus[idx] = task.TaskStatus
		idx++
	}
	return taskStatus
}
<|endoftext|>


<commit_before>package tlbot

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"mime/multipart"
	"net/http"
	"net/url"
	"os"
	"strconv"
)

const baseURL = "https://api.telegram.org/bot"

type ParseMode string

// Parse modes
const (
	ModeNone     ParseMode = ""
	ModeMarkdown ParseMode = "markdown"
)

// Bot represent a Telegram bot.
type Bot struct {
	token string
	Info  User
}

// New creates a new Telegram bot with the given token, which is given by
// Botfather. See https://core.telegram.org/bots#botfather
func New(token string) Bot {
	u, _ := getMe(token)

	return Bot{token: token, Info: u}
}

// Listen listens on the given address addr and returns a read-only Message
// channel.
func (b Bot) Listen(addr string) <-chan Message {
	messageCh := make(chan Message)

	http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		defer w.WriteHeader(http.StatusOK)

		var u Update
		if err := json.NewDecoder(req.Body).Decode(&u); err != nil {
			log.Printf("error decoding request body: %v\n", err)
			return

		}
		messageCh <- u.Payload
	})

	go func() {
		// ListenAndServe always returns non-nil error
		err := http.ListenAndServe(addr, nil)
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}()

	return messageCh
}

// SetWebhook assigns bot's webhook url with the given url.
func (b Bot) SetWebhook(webhook string) error {
	urlvalues := url.Values{"url": {webhook}}

	resp, err := http.PostForm(baseURL+b.token+"/setWebhook", urlvalues)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	var r struct {
		OK      bool   `json:"ok"`
		ErrCode int    `json:"errorcode"`
		Desc    string `json:"description"`
	}

	if err := json.NewDecoder(resp.Body).Decode(&r); err != nil {
		return err
	}

	if !r.OK {
		return fmt.Errorf("%v (%v)", r.Desc, r.ErrCode)
	}

	return nil
}

// SendMessage sends text message to the recipient. Callers can send plain
// text or markdown messages by setting mode parameter.
func (b Bot) SendMessage(recipient User, message string, mode ParseMode, preview bool, opts *SendOptions) error {
	urlvalues := url.Values{
		"chat_id":                  {strconv.Itoa(recipient.ID)},
		"text":                     {message},
		"parse_mode":               {string(mode)},
		"disable_web_page_preview": {strconv.FormatBool(!preview)},
	}

	if opts != nil && (opts.ReplyMarkup.Keyboard != nil || opts.ReplyMarkup.ForceReply || opts.ReplyMarkup.Hide) {
		replymarkup, _ := json.Marshal(opts.ReplyMarkup)
		urlvalues.Set("reply_markup", string(replymarkup))
	}

	resp, err := http.PostForm(baseURL+b.token+"/sendMessage", urlvalues)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	var r struct {
		OK      bool   `json:"ok"`
		Desc    string `json:"description"`
		ErrCode int    `json:"errorcode"`
	}

	if err := json.NewDecoder(resp.Body).Decode(&r); err != nil {
		return err
	}

	if !r.OK {
		return fmt.Errorf("%v (%v)", r.Desc, r.ErrCode)
	}

	return nil
}

// TODO(ig): implement
func (b Bot) forwardMessage(recipient User, message Message) error {
	panic("not implemented yet")
}

// SendPhoto sends given photo to recipient. Only remote URLs are supported for now.
// A trivial example is:
//
//  b := bot.New("your-token-here")
//  photo := bot.Photo{FileURL: "http://i.imgur.com/6S9naG6.png"}
//  err := b.SendPhoto(recipient, photo, "sample image", nil)
//
func (b Bot) SendPhoto(recipient User, photo Photo, caption string, opts *SendOptions) error {
	// TODO(ig): implement sending already sent photos
	if photo.Exists() {
		panic("files reside in telegram servers can not be sent for now.")
	}

	// TODO(ig): implement local file upload
	if photo.IsLocal() {
		panic("local files can not be sent for now.")
	}

	resp, err := http.Get(photo.FileURL)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("Fetch failed (errcode: %v). Remote URL: '%v'", resp.StatusCode, photo.FileURL)
	}

	var buf bytes.Buffer
	w := multipart.NewWriter(&buf)

	part, err := w.CreateFormFile("photo", "image.jpg")
	if err != nil {
		return err
	}

	if _, err := io.Copy(part, resp.Body); err != nil {
		return err
	}

	w.WriteField("chat_id", strconv.Itoa(recipient.ID))

	if err := w.Close(); err != nil {
		return err
	}

	resp, err = http.Post(baseURL+b.token+"/sendPhoto", w.FormDataContentType(), &buf)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	var r struct {
		OK      bool   `json:"ok"`
		ErrCode int    `json:"errorcode"`
		Desc    string `json:"description"`
	}

	if err := json.NewDecoder(resp.Body).Decode(&r); err != nil {
		return err
	}

	if !r.OK {
		return fmt.Errorf("%v (%v)", r.Desc, r.ErrCode)
	}

	return nil
}

// TODO(ig): implement
//
// SendAudio sends audio files, if you want Telegram clients to display
// them in the music player. audio must be in the .mp3 format and must not
// exceed 50 MB in size.
func (b Bot) sendAudio(recipient User, audio Audio, opts *SendOptions) error {
	panic("not implemented yet")
}

// TODO(ig): implement
//
// SendDocument sends general files. Documents must not exceed 50 MB in size.
func (b Bot) sendDocument(recipient User, document Document, opts *SendOptions) error {
	panic("not implemented yet")
}

// TODO(ig): implement
//
//SendSticker sends stickers with .webp extensions.
func (b Bot) sendSticker(recipient User, sticker Sticker, opts *SendOptions) error {
	panic("not implemented yet")
}

// TODO(ig): implement
//
// SendVideo sends video files. Telegram clients support mp4 videos (other
// formats may be sent as Document). Video files must not exceed 50 MB in size.
func (b Bot) sendVideo(recipient User, video Video, opts *SendOptions) error {
	panic("not implemented yet")
}

// TODO(ig): implement
//
// SendVoice sends audio files, if you want Telegram clients to display
// the file as a playable voice message. For this to work, your audio must be
// in an .ogg file encoded with OPUS (other formats may be sent as Audio or
// Document). audio must not exceed 50 MB in size.
func (b Bot) sendVoice(recipient User, audio Audio, opts *SendOptions) error {
	panic("not implemented yet")
}

// TODO(ig): implement
//
// SendLocation sends location point on the map.
func (b Bot) sendLocation(recipient User, location Location, opts *SendOptions) error {
	panic("not implemented yet")
}

// SendChatAction broadcasts type of action to recipient, such as `typing`,
// `uploading a photo` etc.
func (b Bot) SendChatAction(recipient User, action Action) error {
	urlvalues := url.Values{
		"chat_id": {strconv.Itoa(recipient.ID)},
		"action":  {string(action)},
	}

	resp, err := http.PostForm(baseURL+b.token+"/sendChatAction", urlvalues)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	var r struct {
		OK      bool   `json:"ok"`
		ErrCode int    `json:"error_code"`
		Desc    string `json:"description"`
	}

	if err := json.NewDecoder(resp.Body).Decode(&r); err != nil {
		return nil
	}

	if !r.OK {
		return fmt.Errorf("%v (%v)", r.Desc, r.ErrCode)
	}

	return nil
}

type SendOptions struct {
	// If the message is a reply, ID of the original message
	ReplyToMessageID int

	ReplyMarkup ReplyMarkup
}

func getMe(token string) (User, error) {
	resp, err := http.PostForm(baseURL+token+"/getMe", url.Values{})
	if err != nil {
		return User{}, err
	}
	defer resp.Body.Close()

	var r struct {
		OK      bool   `json:"ok"`
		User    User   `json:"result"`
		Desc    string `json:"description"`
		ErrCode int    `json:"error_code"`
	}

	if err := json.NewDecoder(resp.Body).Decode(&r); err != nil {
		return User{}, err
	}

	if !r.OK {
		return User{}, fmt.Errorf("%v (%v)", r.Desc, r.ErrCode)
	}

	return r.User, nil
}
<commit_msg>Use a muxer instead of using the default one<commit_after>package tlbot

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"mime/multipart"
	"net/http"
	"net/url"
	"os"
	"strconv"
)

const baseURL = "https://api.telegram.org/bot"

type ParseMode string

// Parse modes
const (
	ModeNone     ParseMode = ""
	ModeMarkdown ParseMode = "markdown"
)

// Bot represent a Telegram bot.
type Bot struct {
	token string
	Info  User
}

// New creates a new Telegram bot with the given token, which is given by
// Botfather. See https://core.telegram.org/bots#botfather
func New(token string) Bot {
	u, _ := getMe(token)

	return Bot{token: token, Info: u}
}

// Listen listens on the given address addr and returns a read-only Message
// channel.
func (b Bot) Listen(addr string) <-chan Message {
	messageCh := make(chan Message)

	mux := http.NewServeMux()
	mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		defer w.WriteHeader(http.StatusOK)

		var u Update
		if err := json.NewDecoder(req.Body).Decode(&u); err != nil {
			log.Printf("error decoding request body: %v\n", err)
			return

		}
		messageCh <- u.Payload
	})

	go func() {
		// ListenAndServe always returns non-nil error
		err := http.ListenAndServe(addr, mux)
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}()

	return messageCh
}

// SetWebhook assigns bot's webhook url with the given url.
func (b Bot) SetWebhook(webhook string) error {
	urlvalues := url.Values{"url": {webhook}}

	resp, err := http.PostForm(baseURL+b.token+"/setWebhook", urlvalues)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	var r struct {
		OK      bool   `json:"ok"`
		ErrCode int    `json:"errorcode"`
		Desc    string `json:"description"`
	}

	if err := json.NewDecoder(resp.Body).Decode(&r); err != nil {
		return err
	}

	if !r.OK {
		return fmt.Errorf("%v (%v)", r.Desc, r.ErrCode)
	}

	return nil
}

// SendMessage sends text message to the recipient. Callers can send plain
// text or markdown messages by setting mode parameter.
func (b Bot) SendMessage(recipient User, message string, mode ParseMode, preview bool, opts *SendOptions) error {
	urlvalues := url.Values{
		"chat_id":                  {strconv.Itoa(recipient.ID)},
		"text":                     {message},
		"parse_mode":               {string(mode)},
		"disable_web_page_preview": {strconv.FormatBool(!preview)},
	}

	if opts != nil && (opts.ReplyMarkup.Keyboard != nil || opts.ReplyMarkup.ForceReply || opts.ReplyMarkup.Hide) {
		replymarkup, _ := json.Marshal(opts.ReplyMarkup)
		urlvalues.Set("reply_markup", string(replymarkup))
	}

	resp, err := http.PostForm(baseURL+b.token+"/sendMessage", urlvalues)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	var r struct {
		OK      bool   `json:"ok"`
		Desc    string `json:"description"`
		ErrCode int    `json:"errorcode"`
	}

	if err := json.NewDecoder(resp.Body).Decode(&r); err != nil {
		return err
	}

	if !r.OK {
		return fmt.Errorf("%v (%v)", r.Desc, r.ErrCode)
	}

	return nil
}

// TODO(ig): implement
func (b Bot) forwardMessage(recipient User, message Message) error {
	panic("not implemented yet")
}

// SendPhoto sends given photo to recipient. Only remote URLs are supported for now.
// A trivial example is:
//
//  b := bot.New("your-token-here")
//  photo := bot.Photo{FileURL: "http://i.imgur.com/6S9naG6.png"}
//  err := b.SendPhoto(recipient, photo, "sample image", nil)
//
func (b Bot) SendPhoto(recipient User, photo Photo, caption string, opts *SendOptions) error {
	// TODO(ig): implement sending already sent photos
	if photo.Exists() {
		panic("files reside in telegram servers can not be sent for now.")
	}

	// TODO(ig): implement local file upload
	if photo.IsLocal() {
		panic("local files can not be sent for now.")
	}

	resp, err := http.Get(photo.FileURL)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("Fetch failed (errcode: %v). Remote URL: '%v'", resp.StatusCode, photo.FileURL)
	}

	var buf bytes.Buffer
	w := multipart.NewWriter(&buf)

	part, err := w.CreateFormFile("photo", "image.jpg")
	if err != nil {
		return err
	}

	if _, err := io.Copy(part, resp.Body); err != nil {
		return err
	}

	w.WriteField("chat_id", strconv.Itoa(recipient.ID))

	if err := w.Close(); err != nil {
		return err
	}

	resp, err = http.Post(baseURL+b.token+"/sendPhoto", w.FormDataContentType(), &buf)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	var r struct {
		OK      bool   `json:"ok"`
		ErrCode int    `json:"errorcode"`
		Desc    string `json:"description"`
	}

	if err := json.NewDecoder(resp.Body).Decode(&r); err != nil {
		return err
	}

	if !r.OK {
		return fmt.Errorf("%v (%v)", r.Desc, r.ErrCode)
	}

	return nil
}

// TODO(ig): implement
//
// SendAudio sends audio files, if you want Telegram clients to display
// them in the music player. audio must be in the .mp3 format and must not
// exceed 50 MB in size.
func (b Bot) sendAudio(recipient User, audio Audio, opts *SendOptions) error {
	panic("not implemented yet")
}

// TODO(ig): implement
//
// SendDocument sends general files. Documents must not exceed 50 MB in size.
func (b Bot) sendDocument(recipient User, document Document, opts *SendOptions) error {
	panic("not implemented yet")
}

// TODO(ig): implement
//
//SendSticker sends stickers with .webp extensions.
func (b Bot) sendSticker(recipient User, sticker Sticker, opts *SendOptions) error {
	panic("not implemented yet")
}

// TODO(ig): implement
//
// SendVideo sends video files. Telegram clients support mp4 videos (other
// formats may be sent as Document). Video files must not exceed 50 MB in size.
func (b Bot) sendVideo(recipient User, video Video, opts *SendOptions) error {
	panic("not implemented yet")
}

// TODO(ig): implement
//
// SendVoice sends audio files, if you want Telegram clients to display
// the file as a playable voice message. For this to work, your audio must be
// in an .ogg file encoded with OPUS (other formats may be sent as Audio or
// Document). audio must not exceed 50 MB in size.
func (b Bot) sendVoice(recipient User, audio Audio, opts *SendOptions) error {
	panic("not implemented yet")
}

// TODO(ig): implement
//
// SendLocation sends location point on the map.
func (b Bot) sendLocation(recipient User, location Location, opts *SendOptions) error {
	panic("not implemented yet")
}

// SendChatAction broadcasts type of action to recipient, such as `typing`,
// `uploading a photo` etc.
func (b Bot) SendChatAction(recipient User, action Action) error {
	urlvalues := url.Values{
		"chat_id": {strconv.Itoa(recipient.ID)},
		"action":  {string(action)},
	}

	resp, err := http.PostForm(baseURL+b.token+"/sendChatAction", urlvalues)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	var r struct {
		OK      bool   `json:"ok"`
		ErrCode int    `json:"error_code"`
		Desc    string `json:"description"`
	}

	if err := json.NewDecoder(resp.Body).Decode(&r); err != nil {
		return nil
	}

	if !r.OK {
		return fmt.Errorf("%v (%v)", r.Desc, r.ErrCode)
	}

	return nil
}

type SendOptions struct {
	// If the message is a reply, ID of the original message
	ReplyToMessageID int

	ReplyMarkup ReplyMarkup
}

func getMe(token string) (User, error) {
	resp, err := http.PostForm(baseURL+token+"/getMe", url.Values{})
	if err != nil {
		return User{}, err
	}
	defer resp.Body.Close()

	var r struct {
		OK      bool   `json:"ok"`
		User    User   `json:"result"`
		Desc    string `json:"description"`
		ErrCode int    `json:"error_code"`
	}

	if err := json.NewDecoder(resp.Body).Decode(&r); err != nil {
		return User{}, err
	}

	if !r.OK {
		return User{}, fmt.Errorf("%v (%v)", r.Desc, r.ErrCode)
	}

	return r.User, nil
}
<|endoftext|>


<commit_before>package tlbot

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"mime/multipart"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"time"
)

type ParseMode string

// Parse modes
const (
	ModeNone     ParseMode = ""
	ModeMarkdown ParseMode = "markdown"
)

// Bot represent a Telegram bot.
type Bot struct {
	token   string
	baseURL string
	client  *http.Client
}

// New creates a new Telegram bot with the given token, which is given by
// Botfather. See https://core.telegram.org/bots#botfather
func New(token string) Bot {
	return Bot{
		token:   token,
		baseURL: fmt.Sprintf("https://api.telegram.org/bot%v/", token),
		client:  &http.Client{Timeout: 30 * time.Second},
	}
}

// Listen listens on the given address addr and returns a read-only Message
// channel.
func (b Bot) Listen(addr string) <-chan Message {
	messageCh := make(chan Message)

	mux := http.NewServeMux()
	mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		defer w.WriteHeader(http.StatusOK)

		var u Update
		err := json.NewDecoder(req.Body).Decode(&u)
		if err != nil {
			log.Printf("error decoding request body: %v\n", err)
			return

		}
		messageCh <- u.Payload
	})

	go func() {
		// ListenAndServe always returns non-nil error
		err := http.ListenAndServe(addr, mux)
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}()

	return messageCh
}

// SetWebhook assigns bot's webhook url with the given url.
func (b Bot) SetWebhook(webhook string) error {
	params := url.Values{}
	params.Set("url", webhook)

	var r struct {
		OK      bool   `json:"ok"`
		Desc    string `json:"description"`
		ErrCode int    `json:"errorcode"`
	}
	err := b.sendCommand("setWebhook", params, &r)
	if err != nil {
		return err
	}

	if !r.OK {
		return fmt.Errorf("%v (%v)", r.Desc, r.ErrCode)
	}

	return nil
}

// SendMessage sends text message to the recipient. Callers can send plain
// text or markdown messages by setting mode parameter.
func (b Bot) SendMessage(recipient int, message string, opts *SendOptions) (Message, error) {
	params := url.Values{
		"chat_id": {strconv.Itoa(recipient)},
		"text":    {message},
	}

	mapSendOptions(&params, opts)

	var r struct {
		OK      bool   `json:"ok"`
		Desc    string `json:"description"`
		ErrCode int    `json:"errorcode"`
		Message Message
	}
	b.sendCommand("sendMessage", params, &r)

	if !r.OK {
		return Message{}, fmt.Errorf("%v (%v)", r.Desc, r.ErrCode)
	}
	return r.Message, nil
}

func (b Bot) forwardMessage(recipient User, message Message) (Message, error) {
	panic("not implemented yet")
}

// SendPhoto sends given photo to recipient. Only remote URLs are supported for now.
// A trivial example is:
//
//  b := bot.New("your-token-here")
//  photo := bot.Photo{URL: "http://i.imgur.com/6S9naG6.png"}
//  err := b.SendPhoto(recipient, photo, "sample image", nil)
func (b Bot) SendPhoto(recipient int, photo Photo, opts *SendOptions) (Message, error) {
	params := url.Values{}
	params.Set("chat_id", strconv.Itoa(recipient))
	params.Set("caption", photo.Caption)

	mapSendOptions(&params, opts)
	var r struct {
		OK      bool    `json:"ok"`
		Desc    string  `json:"description"`
		ErrCode int     `json:"error_code"`
		Message Message `json:"message"`
	}

	var err error
	if photo.Exists() {
		params.Set("photo", photo.FileID)
		err = b.sendCommand("sendPhoto", params, &r)
	} else if photo.URL != "" {
		params.Set("photo", photo.URL)
		err = b.sendCommand("sendPhoto", params, &r)
	} else {
		err = b.sendFile("sendPhoto", photo.File, "photo", params, &r)
	}

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

	if !r.OK {
		return Message{}, fmt.Errorf("%v (%v)", r.Desc, r.ErrCode)
	}

	return r.Message, nil
}

func (b Bot) sendFile(method string, f File, form string, params url.Values, v interface{}) error {
	u := fmt.Sprintf("%v/%v/%v", b.baseURL, b.token, method)

	var buf bytes.Buffer
	w := multipart.NewWriter(&buf)
	part, err := w.CreateFormFile(form, f.Name)
	if err != nil {
		return err
	}

	_, err = io.Copy(part, f.Body)
	if err != nil {
		return err
	}

	for k, v := range params {
		w.WriteField(k, v[0])
	}

	err = w.Close()
	if err != nil {
		return err
	}

	resp, err := b.client.Post(u, w.FormDataContentType(), &buf)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	return json.NewDecoder(resp.Body).Decode(&v)
}

// SendAudio sends audio files, if you want Telegram clients to display
// them in the music player. audio must be in the .mp3 format and must not
// exceed 50 MB in size.
func (b Bot) sendAudio(recipient User, audio Audio, opts *SendOptions) (Message, error) {
	panic("not implemented yet")
}

// SendDocument sends general files. Documents must not exceed 50 MB in size.
func (b Bot) sendDocument(recipient User, document Document, opts *SendOptions) (Message, error) {
	panic("not implemented yet")
}

//SendSticker sends stickers with .webp extensions.
func (b Bot) sendSticker(recipient User, sticker Sticker, opts *SendOptions) (Message, error) {
	panic("not implemented yet")
}

// SendVideo sends video files. Telegram clients support mp4 videos (other
// formats may be sent as Document). Video files must not exceed 50 MB in size.
func (b Bot) sendVideo(recipient User, video Video, opts *SendOptions) (Message, error) {
	panic("not implemented yet")
}

// SendVoice sends audio files, if you want Telegram clients to display
// the file as a playable voice message. For this to work, your audio must be
// in an .ogg file encoded with OPUS (other formats may be sent as Audio or
// Document). audio must not exceed 50 MB in size.
func (b Bot) sendVoice(recipient User, audio Audio, opts *SendOptions) (Message, error) {
	panic("not implemented yet")
}

// SendLocation sends location point on the map.
func (b Bot) SendLocation(recipient int, location Location, opts *SendOptions) (Message, error) {
	params := url.Values{}
	params.Set("chat_id", strconv.Itoa(recipient))
	params.Set("latitude", strconv.FormatFloat(location.Lat, 'f', -1, 64))
	params.Set("longitude", strconv.FormatFloat(location.Long, 'f', -1, 64))

	mapSendOptions(&params, opts)

	var r struct {
		OK      bool    `json:"ok"`
		Desc    string  `json:"description"`
		ErrCode int     `json:"errorcode"`
		Message Message `json:"message"`
	}
	err := b.sendCommand("sendLocation", params, &r)
	if err != nil {
		return Message{}, err
	}

	if !r.OK {
		return Message{}, fmt.Errorf("%v (%v)", r.Desc, r.ErrCode)
	}

	return r.Message, nil
}

// SendVenue sends information about a venue.
func (b Bot) SendVenue(recipient int, venue Venue, opts *SendOptions) (Message, error) {
	params := url.Values{}
	params.Set("chat_id", strconv.Itoa(recipient))
	params.Set("latitude", strconv.FormatFloat(venue.Location.Lat, 'f', -1, 64))
	params.Set("longitude", strconv.FormatFloat(venue.Location.Long, 'f', -1, 64))
	params.Set("title", venue.Title)
	params.Set("address", venue.Address)

	mapSendOptions(&params, opts)

	var r struct {
		OK      bool    `json:"ok"`
		Desc    string  `json:"description"`
		ErrCode int     `json:"errorcode"`
		Message Message `json:"message"`
	}
	err := b.sendCommand("sendVenue", params, &r)
	if err != nil {
		return Message{}, err
	}

	if !r.OK {
		return Message{}, fmt.Errorf("%v (%v)", r.Desc, r.ErrCode)
	}
	return r.Message, nil
}

// SendChatAction broadcasts type of action to recipient, such as `typing`,
// `uploading a photo` etc.
func (b Bot) SendChatAction(recipient int, action Action) error {
	params := url.Values{}
	params.Set("chat_id", strconv.Itoa(recipient))
	params.Set("action", string(action))

	var r struct {
		OK      bool   `json:"ok"`
		Desc    string `json:"description"`
		ErrCode int    `json:"error_code"`
	}

	err := b.sendCommand("sendChatAction", params, &r)
	if err != nil {
		return err

	}
	if !r.OK {
		return fmt.Errorf("%v (%v)", r.Desc, r.ErrCode)
	}

	return nil
}

type SendOptions struct {
	ReplyTo int

	ParseMode ParseMode

	DisableWebPagePreview bool

	DisableNotification bool

	ReplyMarkup ReplyMarkup
}

func (b Bot) GetFile(fileID string) (File, error) {
	params := url.Values{}
	params.Set("file_id", fileID)

	var r struct {
		OK      bool   `json:"ok"`
		Desc    string `json:"description"`
		ErrCode int    `json:"errorcode"`
		File    File   `json:"result"`
	}
	err := b.sendCommand("getFile", params, &r)
	if err != nil {
		return File{}, err
	}

	if !r.OK {
		return File{}, fmt.Errorf("%v (%v)", r.Desc, r.ErrCode)
	}

	return r.File, nil
}

func (b Bot) GetFileDownloadURL(fileID string) (string, error) {
	f, err := b.GetFile(fileID)
	if err != nil {
		return "", err
	}

	u := "https://api.telegram.org/file/bot" + b.token + "/" + f.FilePath
	return u, nil
}

func (b Bot) sendCommand(method string, params url.Values, v interface{}) error {
	resp, err := b.client.PostForm(b.baseURL+method, params)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	return json.NewDecoder(resp.Body).Decode(&v)
}

func (b Bot) getMe() (User, error) {
	var r struct {
		OK      bool   `json:"ok"`
		Desc    string `json:"description"`
		ErrCode int    `json:"error_code"`

		User User `json:"result"`
	}
	err := b.sendCommand("getMe", url.Values{}, &r)
	if err != nil {
		return User{}, err
	}

	if !r.OK {
		return User{}, fmt.Errorf("%v (%v)", r.Desc, r.ErrCode)
	}

	return r.User, nil
}

func mapSendOptions(m *url.Values, opts *SendOptions) {
	if opts == nil {
		return
	}

	if opts.ReplyTo != 0 {
		m.Set("reply_to_message_id", strconv.Itoa(opts.ReplyTo))
	}

	if opts.DisableWebPagePreview {
		m.Set("disable_web_page_preview", "true")
	}

	if opts.DisableNotification {
		m.Set("disable_notification", "true")
	}

	if opts.ParseMode != ModeNone {
		m.Set("parse_mode", string(opts.ParseMode))
	}

	// TODO: map ReplyMarkup options as well
}
<commit_msg>Fix wrong base URL for sending files<commit_after>package tlbot

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"mime/multipart"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"time"
)

type ParseMode string

// Parse modes
const (
	ModeNone     ParseMode = ""
	ModeMarkdown ParseMode = "markdown"
)

// Bot represent a Telegram bot.
type Bot struct {
	token   string
	baseURL string
	client  *http.Client
}

// New creates a new Telegram bot with the given token, which is given by
// Botfather. See https://core.telegram.org/bots#botfather
func New(token string) Bot {
	return Bot{
		token:   token,
		baseURL: fmt.Sprintf("https://api.telegram.org/bot%v/", token),
		client:  &http.Client{Timeout: 30 * time.Second},
	}
}

// Listen listens on the given address addr and returns a read-only Message
// channel.
func (b Bot) Listen(addr string) <-chan Message {
	messageCh := make(chan Message)

	mux := http.NewServeMux()
	mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		defer w.WriteHeader(http.StatusOK)

		var u Update
		err := json.NewDecoder(req.Body).Decode(&u)
		if err != nil {
			log.Printf("error decoding request body: %v\n", err)
			return

		}
		messageCh <- u.Payload
	})

	go func() {
		// ListenAndServe always returns non-nil error
		err := http.ListenAndServe(addr, mux)
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}()

	return messageCh
}

// SetWebhook assigns bot's webhook url with the given url.
func (b Bot) SetWebhook(webhook string) error {
	params := url.Values{}
	params.Set("url", webhook)

	var r struct {
		OK      bool   `json:"ok"`
		Desc    string `json:"description"`
		ErrCode int    `json:"errorcode"`
	}
	err := b.sendCommand("setWebhook", params, &r)
	if err != nil {
		return err
	}

	if !r.OK {
		return fmt.Errorf("%v (%v)", r.Desc, r.ErrCode)
	}

	return nil
}

// SendMessage sends text message to the recipient. Callers can send plain
// text or markdown messages by setting mode parameter.
func (b Bot) SendMessage(recipient int, message string, opts *SendOptions) (Message, error) {
	params := url.Values{
		"chat_id": {strconv.Itoa(recipient)},
		"text":    {message},
	}

	mapSendOptions(&params, opts)

	var r struct {
		OK      bool   `json:"ok"`
		Desc    string `json:"description"`
		ErrCode int    `json:"errorcode"`
		Message Message
	}
	b.sendCommand("sendMessage", params, &r)

	if !r.OK {
		return Message{}, fmt.Errorf("%v (%v)", r.Desc, r.ErrCode)
	}
	return r.Message, nil
}

func (b Bot) forwardMessage(recipient User, message Message) (Message, error) {
	panic("not implemented yet")
}

// SendPhoto sends given photo to recipient. Only remote URLs are supported for now.
// A trivial example is:
//
//  b := bot.New("your-token-here")
//  photo := bot.Photo{URL: "http://i.imgur.com/6S9naG6.png"}
//  err := b.SendPhoto(recipient, photo, "sample image", nil)
func (b Bot) SendPhoto(recipient int, photo Photo, opts *SendOptions) (Message, error) {
	params := url.Values{}
	params.Set("chat_id", strconv.Itoa(recipient))
	params.Set("caption", photo.Caption)

	mapSendOptions(&params, opts)
	var r struct {
		OK      bool    `json:"ok"`
		Desc    string  `json:"description"`
		ErrCode int     `json:"error_code"`
		Message Message `json:"message"`
	}

	var err error
	if photo.Exists() {
		params.Set("photo", photo.FileID)
		err = b.sendCommand("sendPhoto", params, &r)
	} else if photo.URL != "" {
		params.Set("photo", photo.URL)
		err = b.sendCommand("sendPhoto", params, &r)
	} else {
		err = b.sendFile("sendPhoto", photo.File, "photo", params, &r)
	}

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

	if !r.OK {
		return Message{}, fmt.Errorf("%v (%v)", r.Desc, r.ErrCode)
	}

	return r.Message, nil
}

func (b Bot) sendFile(method string, f File, form string, params url.Values, v interface{}) error {
	var buf bytes.Buffer
	w := multipart.NewWriter(&buf)
	part, err := w.CreateFormFile(form, f.Name)
	if err != nil {
		return err
	}

	_, err = io.Copy(part, f.Body)
	if err != nil {
		return err
	}

	for k, v := range params {
		w.WriteField(k, v[0])
	}

	err = w.Close()
	if err != nil {
		return err
	}

	resp, err := b.client.Post(b.baseURL+method, w.FormDataContentType(), &buf)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	return json.NewDecoder(resp.Body).Decode(&v)
}

// SendAudio sends audio files, if you want Telegram clients to display
// them in the music player. audio must be in the .mp3 format and must not
// exceed 50 MB in size.
func (b Bot) sendAudio(recipient User, audio Audio, opts *SendOptions) (Message, error) {
	panic("not implemented yet")
}

// SendDocument sends general files. Documents must not exceed 50 MB in size.
func (b Bot) sendDocument(recipient User, document Document, opts *SendOptions) (Message, error) {
	panic("not implemented yet")
}

//SendSticker sends stickers with .webp extensions.
func (b Bot) sendSticker(recipient User, sticker Sticker, opts *SendOptions) (Message, error) {
	panic("not implemented yet")
}

// SendVideo sends video files. Telegram clients support mp4 videos (other
// formats may be sent as Document). Video files must not exceed 50 MB in size.
func (b Bot) sendVideo(recipient User, video Video, opts *SendOptions) (Message, error) {
	panic("not implemented yet")
}

// SendVoice sends audio files, if you want Telegram clients to display
// the file as a playable voice message. For this to work, your audio must be
// in an .ogg file encoded with OPUS (other formats may be sent as Audio or
// Document). audio must not exceed 50 MB in size.
func (b Bot) sendVoice(recipient User, audio Audio, opts *SendOptions) (Message, error) {
	panic("not implemented yet")
}

// SendLocation sends location point on the map.
func (b Bot) SendLocation(recipient int, location Location, opts *SendOptions) (Message, error) {
	params := url.Values{}
	params.Set("chat_id", strconv.Itoa(recipient))
	params.Set("latitude", strconv.FormatFloat(location.Lat, 'f', -1, 64))
	params.Set("longitude", strconv.FormatFloat(location.Long, 'f', -1, 64))

	mapSendOptions(&params, opts)

	var r struct {
		OK      bool    `json:"ok"`
		Desc    string  `json:"description"`
		ErrCode int     `json:"errorcode"`
		Message Message `json:"message"`
	}
	err := b.sendCommand("sendLocation", params, &r)
	if err != nil {
		return Message{}, err
	}

	if !r.OK {
		return Message{}, fmt.Errorf("%v (%v)", r.Desc, r.ErrCode)
	}

	return r.Message, nil
}

// SendVenue sends information about a venue.
func (b Bot) SendVenue(recipient int, venue Venue, opts *SendOptions) (Message, error) {
	params := url.Values{}
	params.Set("chat_id", strconv.Itoa(recipient))
	params.Set("latitude", strconv.FormatFloat(venue.Location.Lat, 'f', -1, 64))
	params.Set("longitude", strconv.FormatFloat(venue.Location.Long, 'f', -1, 64))
	params.Set("title", venue.Title)
	params.Set("address", venue.Address)

	mapSendOptions(&params, opts)

	var r struct {
		OK      bool    `json:"ok"`
		Desc    string  `json:"description"`
		ErrCode int     `json:"errorcode"`
		Message Message `json:"message"`
	}
	err := b.sendCommand("sendVenue", params, &r)
	if err != nil {
		return Message{}, err
	}

	if !r.OK {
		return Message{}, fmt.Errorf("%v (%v)", r.Desc, r.ErrCode)
	}
	return r.Message, nil
}

// SendChatAction broadcasts type of action to recipient, such as `typing`,
// `uploading a photo` etc.
func (b Bot) SendChatAction(recipient int, action Action) error {
	params := url.Values{}
	params.Set("chat_id", strconv.Itoa(recipient))
	params.Set("action", string(action))

	var r struct {
		OK      bool   `json:"ok"`
		Desc    string `json:"description"`
		ErrCode int    `json:"error_code"`
	}

	err := b.sendCommand("sendChatAction", params, &r)
	if err != nil {
		return err

	}
	if !r.OK {
		return fmt.Errorf("%v (%v)", r.Desc, r.ErrCode)
	}

	return nil
}

type SendOptions struct {
	ReplyTo int

	ParseMode ParseMode

	DisableWebPagePreview bool

	DisableNotification bool

	ReplyMarkup ReplyMarkup
}

func (b Bot) GetFile(fileID string) (File, error) {
	params := url.Values{}
	params.Set("file_id", fileID)

	var r struct {
		OK      bool   `json:"ok"`
		Desc    string `json:"description"`
		ErrCode int    `json:"errorcode"`
		File    File   `json:"result"`
	}
	err := b.sendCommand("getFile", params, &r)
	if err != nil {
		return File{}, err
	}

	if !r.OK {
		return File{}, fmt.Errorf("%v (%v)", r.Desc, r.ErrCode)
	}

	return r.File, nil
}

func (b Bot) GetFileDownloadURL(fileID string) (string, error) {
	f, err := b.GetFile(fileID)
	if err != nil {
		return "", err
	}

	u := "https://api.telegram.org/file/bot" + b.token + "/" + f.FilePath
	return u, nil
}

func (b Bot) sendCommand(method string, params url.Values, v interface{}) error {
	resp, err := b.client.PostForm(b.baseURL+method, params)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	return json.NewDecoder(resp.Body).Decode(&v)
}

func (b Bot) getMe() (User, error) {
	var r struct {
		OK      bool   `json:"ok"`
		Desc    string `json:"description"`
		ErrCode int    `json:"error_code"`

		User User `json:"result"`
	}
	err := b.sendCommand("getMe", url.Values{}, &r)
	if err != nil {
		return User{}, err
	}

	if !r.OK {
		return User{}, fmt.Errorf("%v (%v)", r.Desc, r.ErrCode)
	}

	return r.User, nil
}

func mapSendOptions(m *url.Values, opts *SendOptions) {
	if opts == nil {
		return
	}

	if opts.ReplyTo != 0 {
		m.Set("reply_to_message_id", strconv.Itoa(opts.ReplyTo))
	}

	if opts.DisableWebPagePreview {
		m.Set("disable_web_page_preview", "true")
	}

	if opts.DisableNotification {
		m.Set("disable_notification", "true")
	}

	if opts.ParseMode != ModeNone {
		m.Set("parse_mode", string(opts.ParseMode))
	}

	// TODO: map ReplyMarkup options as well
}
<|endoftext|>


<commit_before>package irg

import (
	"fmt"
	"regexp"
	"strings"
)

var (
	pingMatcher = regexp.MustCompile("^PING")
	netMatcher  = regexp.MustCompile(":.*")
	msgMatcher  = regexp.MustCompile(" :.+")
	chanMatcher = regexp.MustCompile("#\\w+")
	userMatcher = regexp.MustCompile("^:(.+)!")
	joinMatcher = regexp.MustCompile("JOIN")
)

type Bot struct {
	Nick    string
	Conn    *Conn
	Plugins []*Plugin
}

/*
	Connects to the given irc server and returning an instance of Bot.
*/
func Connect(server string) *Bot {
	conn := Dial(server)

	return &Bot{"GoBot", conn, nil}
}

func (b *Bot) write(msg string) {
	b.Conn.Out <- msg
}

/*
	Joins the given channel.
*/
func (b *Bot) JoinChan(ch string) {
	b.write("JOIN :" + ch)
}

/*
	Logs into the server with the given nick and user.
*/
func (b *Bot) Login(nick, user string) {
	b.Nick = nick
	b.write("NICK :" + b.Nick)
	b.write("USER " + b.Nick + " * 0 :" + user)
}

/*
 Sends a PRIVMSG containing msg to the given channel.
*/
func (b *Bot) Msg(ch, msg string) {
	b.write("PRIVMSG " + ch + " :" + msg)
}

/*
	Sends a PRIVMSG containing msg to the given user
*/
func (b *Bot) Pm(user, msg string) {
	b.write("PRIVMSG " + user + " :" + msg)
}

/*
	Adds a Plugin to the bots Plugin slice.
*/
func (b *Bot) AddPlugin(plugin *Plugin) {
	b.Plugins = append(b.Plugins, plugin)
}

/*
	Loops endlessly reading each line placed
	into the bots In channel in the order they are received
	also loops over the bots Plugin slice for any matches on the current line
	if the line matches the Plugins Runner function is executed.
*/
func (b *Bot) RunLoop() {
	for {
		ln := <-b.Conn.In
		fmt.Print(ln)
		if pingMatcher.Match([]byte(ln)) {
			b.write("PONG " + netMatcher.FindString(ln))
		}

		for _, plugin := range b.Plugins {
			go func() {
				if plugin.Matcher.Match([]byte(ln)) {
					plugin.Runner(
						b,
						chanMatcher.FindString(ln),
						strings.Trim(userMatcher.FindString(ln), ":!"),
						msgMatcher.FindString(ln),
					)
				}
			}()
		}
	}
}
<commit_msg>remove strings.Trim until I figure out whats up here<commit_after>package irg

import (
	"fmt"
	"regexp"
)

var (
	pingMatcher = regexp.MustCompile("^PING")
	netMatcher  = regexp.MustCompile(":.*")
	msgMatcher  = regexp.MustCompile(" :.+")
	chanMatcher = regexp.MustCompile("#\\w+")
	userMatcher = regexp.MustCompile("^:(.+)!")
	joinMatcher = regexp.MustCompile("JOIN")
)

type Bot struct {
	Nick    string
	Conn    *Conn
	Plugins []*Plugin
}

/*
	Connects to the given irc server and returning an instance of Bot.
*/
func Connect(server string) *Bot {
	conn := Dial(server)

	return &Bot{"GoBot", conn, nil}
}

func (b *Bot) write(msg string) {
	b.Conn.Out <- msg
}

/*
	Joins the given channel.
*/
func (b *Bot) JoinChan(ch string) {
	b.write("JOIN :" + ch)
}

/*
	Logs into the server with the given nick and user.
*/
func (b *Bot) Login(nick, user string) {
	b.Nick = nick
	b.write("NICK :" + b.Nick)
	b.write("USER " + b.Nick + " * 0 :" + user)
}

/*
 Sends a PRIVMSG containing msg to the given channel.
*/
func (b *Bot) Msg(ch, msg string) {
	b.write("PRIVMSG " + ch + " :" + msg)
}

/*
	Sends a PRIVMSG containing msg to the given user
*/
func (b *Bot) Pm(user, msg string) {
	b.write("PRIVMSG " + user + " :" + msg)
}

/*
	Adds a Plugin to the bots Plugin slice.
*/
func (b *Bot) AddPlugin(plugin *Plugin) {
	b.Plugins = append(b.Plugins, plugin)
}

/*
	Loops endlessly reading each line placed
	into the bots In channel in the order they are received
	also loops over the bots Plugin slice for any matches on the current line
	if the line matches the Plugins Runner function is executed.
*/
func (b *Bot) RunLoop() {
	for {
		ln := <-b.Conn.In
		fmt.Print(ln)
		if pingMatcher.Match([]byte(ln)) {
			b.write("PONG " + netMatcher.FindString(ln))
		}

		for _, plugin := range b.Plugins {
			go func() {
				if plugin.Matcher.Match([]byte(ln)) {
					plugin.Runner(
						b,
						chanMatcher.FindString(ln),
						userMatcher.FindString(ln),
						msgMatcher.FindString(ln),
					)
				}
			}()
		}
	}
}
<|endoftext|>


<commit_before>package cat

import "fmt"

var cat_lock chan int = make(chan int, 1)
var cat_initialized bool = false

//A tool instance for CAT.
//Use it to create Transaction, Event, Heartbeat, Trace...
//Every Cat instance has 1 Tree instance.
type Cat interface {
	//Tree provides methods to create different kinds of messages
	Tree
	//Create a new Event whose type is error and status is ERROR,
	//nil is ignored
	LogError(e error)
	//Create a new Event whose type is panic and status is ERROR,
	//nil is ignored
	LogPanic(e Panic)
}

type cat struct {
	Tree
}

//LogError
func (c *cat) LogError(err error) {
	if err != nil {
		e := c.NewEvent("error", err.Error())
		e.SetStatus("ERROR")
		e.Complete()
	}
}

func (c *cat) LogPanic(err Panic) {
	if err != nil {
		e := c.NewEvent("panic", fmt.Sprintf("%v", err))
		e.SetStatus("ERROR")
		e.Complete()
	}
}

//Cat_init_if initialize cat.go,
//which must be down before any other operations,
//for which Instance called it automatically.
func Cat_init_if() {
	cat_lock <- 0
	if !cat_initialized {
		cat_config_init_if()
		go sender_run()
		cat_initialized = true
	}
	<-cat_lock
}

//As it's not recommended to apply thread local in go,
//apps with cat.go have to call Instance,
//keep and manage the instance returned properly.
func Instance() Cat {
	Cat_init_if()
	return &cat{
		NewTree(),
	}
}
<commit_msg>call sender_init instead of invoke goroutines<commit_after>package cat

import "fmt"

var cat_lock chan int = make(chan int, 1)
var cat_initialized bool = false

//A tool instance for CAT.
//Use it to create Transaction, Event, Heartbeat, Trace...
//Every Cat instance has 1 Tree instance.
type Cat interface {
	//Tree provides methods to create different kinds of messages
	Tree
	//Create a new Event whose type is error and status is ERROR,
	//nil is ignored
	LogError(e error)
	//Create a new Event whose type is panic and status is ERROR,
	//nil is ignored
	LogPanic(e Panic)
}

type cat struct {
	Tree
}

//LogError
func (c *cat) LogError(err error) {
	if err != nil {
		e := c.NewEvent("error", err.Error())
		e.SetStatus("ERROR")
		e.Complete()
	}
}

func (c *cat) LogPanic(err Panic) {
	if err != nil {
		e := c.NewEvent("panic", fmt.Sprintf("%v", err))
		e.SetStatus("ERROR")
		e.Complete()
	}
}

//Cat_init_if initialize cat.go,
//which must be down before any other operations,
//for which Instance called it automatically.
func Cat_init_if() {
	cat_lock <- 0
	if !cat_initialized {
		cat_config_init()
		cat_sender_init()
		cat_initialized = true
	}
	<-cat_lock
}

//As it's not recommended to apply thread local in go,
//apps with cat.go have to call Instance,
//keep and manage the instance returned properly.
func Instance() Cat {
	Cat_init_if()
	return &cat{
		NewTree(),
	}
}
<|endoftext|>


<commit_before>// Diglet is a set of geospatial tools focused around rendering large feature sets efficiently.
package main

import (
	"os"
	"strings"

	"github.com/buckhx/diglet/mbt"
	"github.com/buckhx/diglet/resources"
	"github.com/buckhx/diglet/util"
	"github.com/buckhx/diglet/wms"

	"github.com/codegangsta/cli"
	"github.com/davecheney/profile"
)

//go:generate go run scripts/include.go static/static.html
func client(args []string) {
	app := cli.NewApp()
	app.Name = "diglet"
	app.Usage = "Your friend in the tile business"
	app.Version = resources.Version()
	app.Commands = []cli.Command{
		{
			Name:        "wms",
			Aliases:     []string{"serve"},
			Usage:       "Starts the diglet Web Map Service",
			Description: "Starts the diglet Web Map Service",
			ArgsUsage:   "mbtiles_directory",
			Action: func(c *cli.Context) {
				port := c.String("port")
				args := c.Args()
				if len(args) < 1 {
					die(c, "directory path to serve mbtiles from is required")
				}
				mbt := args[0]
				if mbt == "" {
					die(c, "mbtiles_directory is required")
				}
				cert := c.String("cert")
				key := c.String("key")
				server := wms.MBTServer(mbt, port)
				if (cert != "") && (key != "") {
					server.RunTLS(&cert, &key)
				} else if cert != "" || key != "" {
					die(c, "Both cert & key are required to serve over TLS/SSL")
				} else {
					server.Run()
				}
			},
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "port",
					Value: "8080",
					Usage: "Port to bind",
				},
				cli.StringFlag{
					Name:  "cert, tls-certificate",
					Usage: "Path to .pem TLS Certificate. Both cert & key required to serve HTTPS",
				},
				cli.StringFlag{
					Name:  "key, tls-private-key",
					Usage: "Path to .pem TLS Private Key. Both cert & key required to serve HTTPS",
				},
				cli.BoolFlag{
					Name:  "tms-origin",
					Usage: "NOT IMPLEMENTED: Use TMS origin, SW origin w/ Y increasing North-wise",
				},
			},
		},
		{
			Name:        "mbt",
			Aliases:     []string{"build"},
			Usage:       "Builds an mbtiles database from the input data source",
			Description: "Builds an mbtiles database from the given format",
			ArgsUsage:   "input_source",
			Action: func(c *cli.Context) {
				out := c.String("output")
				desc := c.String("desc")
				layer := c.String("layer-name")
				zmin := uint(c.Int("min"))
				zmax := uint(c.Int("max"))
				extent := uint(c.Int("extent"))
				args := c.Args()
				if len(args) < 1 {
					die(c, "an input data source is required")
				}
				in := args[0]
				if in == "" || out == "" {
					die(c, "--in & --out required")
				}
				if zmax < zmin || zmin < 0 || zmax > 23 {
					die(c, "--max > --min, --min > 9 --max < 24 not satisfied")
				}
				force := c.Bool("force")
				if force {
					os.Remove(out)
				}
				/*
						lat := c.String("csv-lat")
						lon := c.String("csv-lon")
						delim := c.String("csv-delimiter")
						source := mbt.CsvTiles(in, delim, lat, lon)
					source := mbt.GeojsonTiles(in)
					mbt.BuildTileset(ts, source, zmin, zmax)
				*/
				upsert := c.Bool("upsert")
				filter := strings.Split(c.String("filter"), ",")
				if tiles, err := mbt.InitTiles(in, out, upsert, filter, desc, extent); err != nil {
					util.Fatal(err.Error())
				} else {
					err = tiles.Build(layer, zmin, zmax)
					if err != nil {
						util.Fatal(err.Error())
					} else {
						file, _ := os.Open(out)
						defer file.Close()
						stat, _ := file.Stat()
						exp := float64(stat.Size()) / float64(1<<20)
						util.Info("%s was successfully caught!", out)
						util.Info("Diglet gained %f MB of EXP!", exp)
					}
				}
			},
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "o, output",
					Usage: "REQUIRED: Path to write mbtiles to",
				},
				cli.StringFlag{
					Name:  "input-type",
					Value: "sniff",
					Usage: "Type of input files, 'sniff' will pick type based on the extension",
				},
				cli.BoolFlag{
					Name:  "f, force",
					Usage: "Remove the existing .mbtiles file before running.",
				},
				cli.BoolFlag{
					Name:  "u, upsert",
					Usage: "Upsert into mbtiles instead of replacing.",
				},
				cli.StringFlag{
					Name:  "layer-name",
					Value: "features",
					Usage: "Name of the layer for the features to be added to",
				},
				cli.StringFlag{
					Name:  "desc, description",
					Value: "Generated from Diglet",
					Usage: "Value inserted into the description entry of the mbtiles",
				},
				cli.IntFlag{
					Name:  "extent",
					Value: 4096,
					Usage: "Extent of tiles to be built. Default is 4096",
				},
				cli.IntFlag{
					Name:  "max, max-zoom",
					Value: 10,
					Usage: "Maximum zoom level to build tiles for. Not Implemented.",
				},
				cli.IntFlag{
					Name:  "min, min-zoom",
					Value: 5,
					Usage: "Minimum zoom level to build tiles from. Not Implemented.",
				},
				cli.StringFlag{
					Name: "filter",
					Usage: "Only include fields keys in this comma delimited list.\t" +
						"EXAMPLE --filter name,date,case_number,id\t" +
						"NOTE all fields are lowercased and non-word chars replaced with '_'",
				},
				cli.StringFlag{
					Name:  "csv-lat",
					Value: "latitude",
				},
				cli.StringFlag{
					Name:  "csv-lon",
					Value: "longitude",
				},
				cli.StringFlag{
					Name:  "csv-geometry",
					Value: "geometry",
					Usage: "Column containing geometry in geojson-like 'coordinates' form",
				},
				cli.StringFlag{
					Name:  "csv-delimiter",
					Value: ",",
				},
			},
		},
	}
	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:  "debug",
			Usage: "Print debugging log lines",
		},
	}
	app.Before = func(c *cli.Context) error {
		if c.Bool("debug") {
			util.DEBUG = true
		}
		return nil
	}
	app.Run(args)
}

func main() {
	for _, arg := range os.Args {
		if arg == "--debug" {
			config := &profile.Config{
				MemProfile: true,
				CPUProfile: true,
			}
			defer profile.Start(config).Stop()
		}
	}
	client(os.Args)
}

func die(c *cli.Context, msg string) {
	cli.ShowSubcommandHelp(c)
	util.Fatal(msg)
}

/*
Go! Diglet!
Diglet used Earthquake
Foe DEWGONG fainted
Diglet gain 1960 EXP. Points
ELITE FOUR LORELEI is about to use CLOYSTER
sent out
Diglet fainted!
*/
<commit_msg>hotfix: cleanup cache & default filter<commit_after>// Diglet is a set of geospatial tools focused around rendering large feature sets efficiently.
package main

import (
	"os"
	"os/signal"
	"strings"
	"syscall"

	"github.com/buckhx/diglet/mbt"
	"github.com/buckhx/diglet/resources"
	"github.com/buckhx/diglet/util"
	"github.com/buckhx/diglet/wms"

	"github.com/codegangsta/cli"
	"github.com/davecheney/profile"
)

//go:generate go run scripts/include.go static/static.html
func client(args []string) {
	app := cli.NewApp()
	app.Name = "diglet"
	app.Usage = "Your friend in the tile business"
	app.Version = resources.Version()
	app.Commands = []cli.Command{
		{
			Name:        "wms",
			Aliases:     []string{"serve"},
			Usage:       "Starts the diglet Web Map Service",
			Description: "Starts the diglet Web Map Service",
			ArgsUsage:   "mbtiles_directory",
			Action: func(c *cli.Context) {
				port := c.String("port")
				args := c.Args()
				if len(args) < 1 {
					die(c, "directory path to serve mbtiles from is required")
				}
				mbt := args[0]
				if mbt == "" {
					die(c, "mbtiles_directory is required")
				}
				cert := c.String("cert")
				key := c.String("key")
				server := wms.MBTServer(mbt, port)
				if (cert != "") && (key != "") {
					server.RunTLS(&cert, &key)
				} else if cert != "" || key != "" {
					die(c, "Both cert & key are required to serve over TLS/SSL")
				} else {
					sigs := make(chan os.Signal, 1)
					done := make(chan bool, 1)
					signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
					defer os.Remove(mbt + "/" + wms.CacheName) //TODO make path.Join
					go func() {
						err := server.Run()
						util.Error(err)
						done <- true
					}()
					go func() {
						<-sigs
						done <- true
					}()
					<-done
				}
			},
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "port",
					Value: "8080",
					Usage: "Port to bind",
				},
				cli.StringFlag{
					Name:  "cert, tls-certificate",
					Usage: "Path to .pem TLS Certificate. Both cert & key required to serve HTTPS",
				},
				cli.StringFlag{
					Name:  "key, tls-private-key",
					Usage: "Path to .pem TLS Private Key. Both cert & key required to serve HTTPS",
				},
				cli.BoolFlag{
					Name:  "tms-origin",
					Usage: "NOT IMPLEMENTED: Use TMS origin, SW origin w/ Y increasing North-wise",
				},
			},
		},
		{
			Name:        "mbt",
			Aliases:     []string{"build"},
			Usage:       "Builds an mbtiles database from the input data source",
			Description: "Builds an mbtiles database from the given format",
			ArgsUsage:   "input_source",
			Action: func(c *cli.Context) {
				out := c.String("output")
				desc := c.String("desc")
				layer := c.String("layer-name")
				zmin := uint(c.Int("min"))
				zmax := uint(c.Int("max"))
				extent := uint(c.Int("extent"))
				args := c.Args()
				if len(args) < 1 {
					die(c, "an input data source is required")
				}
				in := args[0]
				if in == "" || out == "" {
					die(c, "--in & --out required")
				}
				if zmax < zmin || zmin < 0 || zmax > 23 {
					die(c, "--max > --min, --min > 9 --max < 24 not satisfied")
				}
				force := c.Bool("force")
				if force {
					os.Remove(out)
				}
				/*
						lat := c.String("csv-lat")
						lon := c.String("csv-lon")
						delim := c.String("csv-delimiter")
						source := mbt.CsvTiles(in, delim, lat, lon)
					source := mbt.GeojsonTiles(in)
					mbt.BuildTileset(ts, source, zmin, zmax)
				*/
				upsert := c.Bool("upsert")
				var filter []string
				if len(c.String("filter")) > 0 {
					filter = strings.Split(c.String("filter"), ",")
				}
				if tiles, err := mbt.InitTiles(in, out, upsert, filter, desc, extent); err != nil {
					util.Fatal(err.Error())
				} else {
					err = tiles.Build(layer, zmin, zmax)
					if err != nil {
						util.Fatal(err.Error())
					} else {
						file, _ := os.Open(out)
						defer file.Close()
						stat, _ := file.Stat()
						exp := float64(stat.Size()) / float64(1<<20)
						util.Info("%s was successfully caught!", out)
						util.Info("Diglet gained %f MB of EXP!", exp)
					}
				}
			},
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "o, output",
					Usage: "REQUIRED: Path to write mbtiles to",
				},
				cli.StringFlag{
					Name:  "input-type",
					Value: "sniff",
					Usage: "Type of input files, 'sniff' will pick type based on the extension",
				},
				cli.BoolFlag{
					Name:  "f, force",
					Usage: "Remove the existing .mbtiles file before running.",
				},
				cli.BoolFlag{
					Name:  "u, upsert",
					Usage: "Upsert into mbtiles instead of replacing.",
				},
				cli.StringFlag{
					Name:  "layer-name",
					Value: "features",
					Usage: "Name of the layer for the features to be added to",
				},
				cli.StringFlag{
					Name:  "desc, description",
					Value: "Generated from Diglet",
					Usage: "Value inserted into the description entry of the mbtiles",
				},
				cli.IntFlag{
					Name:  "extent",
					Value: 4096,
					Usage: "Extent of tiles to be built. Default is 4096",
				},
				cli.IntFlag{
					Name:  "max, max-zoom",
					Value: 10,
					Usage: "Maximum zoom level to build tiles for. Not Implemented.",
				},
				cli.IntFlag{
					Name:  "min, min-zoom",
					Value: 5,
					Usage: "Minimum zoom level to build tiles from. Not Implemented.",
				},
				cli.StringFlag{
					Name: "filter",
					Usage: "Only include fields keys in this comma delimited list.\t" +
						"EXAMPLE --filter name,date,case_number,id\t" +
						"NOTE all fields are lowercased and non-word chars replaced with '_'",
				},
				cli.StringFlag{
					Name:  "csv-lat",
					Value: "latitude",
				},
				cli.StringFlag{
					Name:  "csv-lon",
					Value: "longitude",
				},
				cli.StringFlag{
					Name:  "csv-geometry",
					Value: "geometry",
					Usage: "Column containing geometry in geojson-like 'coordinates' form",
				},
				cli.StringFlag{
					Name:  "csv-delimiter",
					Value: ",",
				},
			},
		},
	}
	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:  "debug",
			Usage: "Print debugging log lines",
		},
	}
	app.Before = func(c *cli.Context) error {
		if c.Bool("debug") {
			util.DEBUG = true
		}
		return nil
	}
	app.Run(args)
}

func main() {
	for _, arg := range os.Args {
		if arg == "--debug" {
			config := &profile.Config{
				MemProfile: true,
				CPUProfile: true,
			}
			defer profile.Start(config).Stop()
		}
	}
	client(os.Args)
}

func die(c *cli.Context, msg string) {
	cli.ShowSubcommandHelp(c)
	util.Fatal(msg)
}

/*
Go! Diglet!
Diglet used Earthquake
Foe DEWGONG fainted
Diglet gain 1960 EXP. Points
ELITE FOUR LORELEI is about to use CLOYSTER
sent out
Diglet fainted!
*/
<|endoftext|>


<commit_before>package main

import (
	"log"
	"os"
	"path"

	"github.com/codegangsta/cli"
)

// Logger is used to log errors; if nil, the default log.Logger is used.
var Logger *log.Logger

// logger is an helper function to retrieve the available logger
func logger() *log.Logger {
	if Logger == nil {
		Logger = log.New(os.Stderr, "", log.LstdFlags)
	}
	return Logger
}

func main() {

	app := cli.NewApp()
	app.Name = "lego"
	app.Usage = "Let's encrypt client to go!"
	app.Version = "0.0.2"

	cwd, err := os.Getwd()
	if err != nil {
		logger().Fatal("Could not determine current working directory. Please pass --path.")
	}
	defaultPath := path.Join(cwd, ".lego")

	app.Commands = []cli.Command{
		{
			Name:   "run",
			Usage:  "Register an account, then create and install a certificate",
			Action: run,
		},
		{
			Name:   "revoke",
			Usage:  "Revoke a certificate",
			Action: revoke,
		},
		{
			Name:   "renew",
			Usage:  "Renew a certificate",
			Action: renew,
		},
	}

	app.Flags = []cli.Flag{
		cli.StringSliceFlag{
			Name:  "domains, d",
			Usage: "Add domains to the process",
		},
		cli.StringFlag{
			Name:  "server, s",
			Value: "https://acme-staging.api.letsencrypt.org/",
			Usage: "CA hostname (and optionally :port). The server certificate must be trusted in order to avoid further modifications to the client.",
		},
		cli.StringFlag{
			Name:  "email, m",
			Usage: "Email used for registration and recovery contact.",
		},
		cli.IntFlag{
			Name:  "rsa-key-size, B",
			Value: 2048,
			Usage: "Size of the RSA key.",
		},
		cli.StringFlag{
			Name:  "path",
			Usage: "Directory to use for storing the data",
			Value: defaultPath,
		},
		cli.StringFlag{
			Name:  "port",
			Usage: "Challenges will use this port to listen on. Please make sure to forward port 443 to this port on your machine. Otherwise use setcap on the binary",
		},
	}

	app.Run(os.Args)
}
<commit_msg>Update CLI to default to the live LE endpoint.<commit_after>package main

import (
	"log"
	"os"
	"path"

	"github.com/codegangsta/cli"
)

// Logger is used to log errors; if nil, the default log.Logger is used.
var Logger *log.Logger

// logger is an helper function to retrieve the available logger
func logger() *log.Logger {
	if Logger == nil {
		Logger = log.New(os.Stderr, "", log.LstdFlags)
	}
	return Logger
}

func main() {

	app := cli.NewApp()
	app.Name = "lego"
	app.Usage = "Let's encrypt client to go!"
	app.Version = "0.0.2"

	cwd, err := os.Getwd()
	if err != nil {
		logger().Fatal("Could not determine current working directory. Please pass --path.")
	}
	defaultPath := path.Join(cwd, ".lego")

	app.Commands = []cli.Command{
		{
			Name:   "run",
			Usage:  "Register an account, then create and install a certificate",
			Action: run,
		},
		{
			Name:   "revoke",
			Usage:  "Revoke a certificate",
			Action: revoke,
		},
		{
			Name:   "renew",
			Usage:  "Renew a certificate",
			Action: renew,
		},
	}

	app.Flags = []cli.Flag{
		cli.StringSliceFlag{
			Name:  "domains, d",
			Usage: "Add domains to the process",
		},
		cli.StringFlag{
			Name:  "server, s",
			Value: "https://acme-v01.api.letsencrypt.org/directory",
			Usage: "CA hostname (and optionally :port). The server certificate must be trusted in order to avoid further modifications to the client.",
		},
		cli.StringFlag{
			Name:  "email, m",
			Usage: "Email used for registration and recovery contact.",
		},
		cli.IntFlag{
			Name:  "rsa-key-size, B",
			Value: 2048,
			Usage: "Size of the RSA key.",
		},
		cli.StringFlag{
			Name:  "path",
			Usage: "Directory to use for storing the data",
			Value: defaultPath,
		},
		cli.StringFlag{
			Name:  "port",
			Usage: "Challenges will use this port to listen on. Please make sure to forward port 443 to this port on your machine. Otherwise use setcap on the binary",
		},
	}

	app.Run(os.Args)
}
<|endoftext|>


<commit_before>// Command ghr is a tool to create a Github Release and upload your
// artifacts in parallel.
package main

import (
	"context"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"regexp"
	"runtime"
	"time"

	"github.com/google/go-github/github"
	"github.com/mitchellh/colorstring"
	"github.com/tcnksm/go-gitconfig"
)

const (
	// EnvGitHubToken is an environment var containing the GitHub API token
	EnvGitHubToken = "GITHUB_TOKEN"

	// EnvGitHubAPI is an environment var containing the GitHub API base endpoint.
	// This is used mainly by GitHub Enterprise users.
	EnvGitHubAPI = "GITHUB_API"

	// EnvDebug is an environment var to handle debug mode
	EnvDebug = "GHR_DEBUG"
)

// Exit codes are set to a value that represent an exit code for a particular error.
const (
	ExitCodeOK int = 0

	// Errors start at 10
	ExitCodeError = 10 + iota
	ExitCodeParseFlagsError
	ExitCodeBadArgs
	ExitCodeInvalidURL
	ExitCodeTokenNotFound
	ExitCodeOwnerNotFound
	ExitCodeRepoNotFound
	ExitCodeReleaseError
)

const (
	defaultCheckTimeout = 2 * time.Second
	defaultBaseURL      = "https://api.github.com/"
	defaultParallel     = -1
)

// Debugf prints debug output when EnvDebug is set
func Debugf(format string, args ...interface{}) {
	if env := os.Getenv(EnvDebug); len(env) != 0 {
		log.Printf("[DEBUG] "+format+"\n", args...)
	}
}

// PrintRedf prints red error message to console.
func PrintRedf(w io.Writer, format string, args ...interface{}) {
	format = fmt.Sprintf("[red]%s[reset]", format)
	fmt.Fprint(w,
		colorstring.Color(fmt.Sprintf(format, args...)))
}

// CLI is the main command line object
type CLI struct {
	// outStream and errStream correspond to stdout and stderr, respectively,
	// to take messages from the CLI.
	outStream, errStream io.Writer
}

// Run invokes the CLI with the given arguments.
func (cli *CLI) Run(args []string) int {

	var (
		owner string
		repo  string
		token string

		commitish  string
		name       string
		body       string
		draft      bool
		prerelease bool

		parallel int

		recreate bool
		replace  bool
		soft     bool

		stat    bool
		version bool
		debug   bool
	)

	flags := flag.NewFlagSet(Name, flag.ContinueOnError)
	flags.SetOutput(cli.errStream)
	flags.Usage = func() {
		fmt.Fprint(cli.errStream, helpText)
	}

	flags.StringVar(&owner, "username", "", "")
	flags.StringVar(&owner, "owner", "", "")
	flags.StringVar(&owner, "u", "", "")

	flags.StringVar(&repo, "repository", "", "")
	flags.StringVar(&repo, "r", "", "")

	flags.StringVar(&token, "token", os.Getenv(EnvGitHubToken), "")
	flags.StringVar(&token, "t", os.Getenv(EnvGitHubToken), "")

	flags.StringVar(&commitish, "commitish", "", "")
	flags.StringVar(&commitish, "c", "", "")

	flags.StringVar(&name, "name", "", "")
	flags.StringVar(&name, "n", "", "")

	flags.StringVar(&body, "body", "", "")
	flags.StringVar(&body, "b", "", "")

	flags.BoolVar(&draft, "draft", false, "")
	flags.BoolVar(&prerelease, "prerelease", false, "")

	flags.IntVar(&parallel, "parallel", defaultParallel, "")
	flags.IntVar(&parallel, "p", defaultParallel, "")

	flags.BoolVar(&recreate, "delete", false, "")
	flags.BoolVar(&recreate, "recreate", false, "")

	flags.BoolVar(&replace, "replace", false, "")

	flags.BoolVar(&soft, "soft", false, "")

	flags.BoolVar(&version, "version", false, "")
	flags.BoolVar(&version, "v", false, "")

	flags.BoolVar(&debug, "debug", false, "")

	// Deprecated
	flags.BoolVar(&stat, "stat", false, "")

	// Parse flags
	if err := flags.Parse(args[1:]); err != nil {
		return ExitCodeParseFlagsError
	}

	if debug {
		os.Setenv(EnvDebug, "1")
		Debugf("Run as DEBUG mode")
	}

	// Show version and check latest version release
	if version {
		fmt.Fprintf(cli.outStream, OutputVersion())
		return ExitCodeOK
	}

	parsedArgs := flags.Args()
	if len(parsedArgs) != 2 {
		PrintRedf(cli.errStream,
			"Invalid argument: you must set TAG and PATH name.\n")
		return ExitCodeBadArgs
	}
	tag, path := parsedArgs[0], parsedArgs[1]

	// Extract github repository owner username.
	// If it's not provided via command line flag, read it from .gitconfig
	// (github user or git user).
	if len(owner) == 0 {
		origin, err := gitconfig.OriginURL()
		if err == nil {
			owner = retrieveOwnerName(origin)
		}
		if len(owner) == 0 {
			owner, err = gitconfig.GithubUser()
			if err != nil {
				owner, err = gitconfig.Username()
			}

			if err != nil {
				PrintRedf(cli.errStream,
					"Failed to set up ghr: repository owner name not found\n")
				fmt.Fprintf(cli.errStream,
					"Please set it via `-u` option.\n\n"+
						"You can set default owner name in `github.username` or `user.name`\n"+
						"in `~/.gitconfig` file\n")
				return ExitCodeOwnerNotFound
			}
		}
	}
	Debugf("Owner: %s", owner)

	// Extract repository name from files.
	// If not provided, read it from .git/config file.
	if len(repo) == 0 {
		var err error
		repo, err = gitconfig.Repository()
		if err != nil {
			PrintRedf(cli.errStream,
				"Failed to set up ghr: repository name not found\n")
			fmt.Fprintf(cli.errStream,
				"ghr reads it from `.git/config` file. Change directory to \n"+
					"repository root directory or setup git repository.\n"+
					"Or set it via `-r` option.\n")
			return ExitCodeRepoNotFound
		}
	}
	Debugf("Repository: %s", repo)

	// If GitHub API token is not provided via command line flag
	// or env var then read it from .gitconfig file.
	if len(token) == 0 {
		var err error
		token, err = gitconfig.GithubToken()
		if err != nil {
			PrintRedf(cli.errStream, "Failed to set up ghr: token not found\n")
			fmt.Fprintf(cli.errStream,
				"To use ghr, you need a GitHub API token.\n"+
					"Please set it via `%s` env var or `-t` option.\n\n"+
					"If you don't have one, visit official doc (goo.gl/jSnoI)\n"+
					"and get it first.\n",
				EnvGitHubToken)
			return ExitCodeTokenNotFound
		}
	}
	Debugf("Github API Token: %s", maskString(token))

	// Set Base GitHub API URL. Base URL can also be provided via env var for use with GHE.
	baseURLStr := defaultBaseURL
	if urlStr := os.Getenv(EnvGitHubAPI); len(urlStr) != 0 {
		baseURLStr = urlStr
	}
	Debugf("Base GitHub API URL: %s", baseURLStr)

	if parallel <= 0 {
		parallel = runtime.NumCPU()
	}
	Debugf("Parallel factor: %d", parallel)

	localAssets, err := LocalAssets(path)
	if err != nil {
		PrintRedf(cli.errStream,
			"Failed to find assets from %s: %s\n", path, err)
		return ExitCodeError
	}
	Debugf("Number of file to upload: %d", len(localAssets))

	// Create a GitHub client
	gitHubClient, err := NewGitHubClient(owner, repo, token, baseURLStr)
	if err != nil {
		PrintRedf(cli.errStream, "Failed to construct GitHub client: %s\n", err)
		return ExitCodeError
	}

	ghr := GHR{
		GitHub:    gitHubClient,
		outStream: cli.outStream,
	}

	Debugf("Name: %s", name)

	// Prepare create release request
	req := &github.RepositoryRelease{
		Name:            github.String(name),
		TagName:         github.String(tag),
		Prerelease:      github.Bool(prerelease),
		Draft:           github.Bool(draft),
		TargetCommitish: github.String(commitish),
		Body:            github.String(body),
	}

	ctx := context.TODO()

	if soft {
		_, err := ghr.GitHub.GetRelease(ctx, *req.TagName)

		if err == nil {
			fmt.Fprintf(cli.outStream, "ghr aboted since tag `%s` already exists\n", *req.TagName)
			return ExitCodeOK
		}

		if err != nil && err != ErrReleaseNotFound {
			PrintRedf(cli.errStream, "Failed to get GitHub release: %s\n", err)
			return ExitCodeError
		}
	}

	release, err := ghr.CreateRelease(ctx, req, recreate)
	if err != nil {
		PrintRedf(cli.errStream, "Failed to create GitHub release page: %s\n", err)
		return ExitCodeError
	}

	if replace {
		err := ghr.DeleteAssets(ctx, *release.ID, localAssets, parallel)
		if err != nil {
			PrintRedf(cli.errStream, "Failed to delete existing assets: %s\n", err)
			return ExitCodeError
		}
	}

	// FIXME(tcnksm): More ideal way to change this
	// This is for Github enterprise
	if err := ghr.GitHub.SetUploadURL(*release.UploadURL); err != nil {
		fmt.Fprintf(cli.errStream, "Failed to set upload URL %s: %s\n", *release.UploadURL, err)
		return ExitCodeError
	}

	err = ghr.UploadAssets(ctx, *release.ID, localAssets, parallel)
	if err != nil {
		PrintRedf(cli.errStream, "Failed to upload one of assets: %s\n", err)
		return ExitCodeError
	}

	if !draft {
		_, err := ghr.GitHub.EditRelease(ctx, *release.ID, &github.RepositoryRelease{
			Draft: github.Bool(false),
		})
		if err != nil {
			PrintRedf(cli.errStream, "Failed to publish release: %s\n", err)
			return ExitCodeError
		}
	}

	return ExitCodeOK
}

var ownerNameReg = regexp.MustCompile(`([-a-zA-Z0-9]+)/[^/]+$`)

func retrieveOwnerName(repoURL string) string {
	matched := ownerNameReg.FindStringSubmatch(repoURL)
	if len(matched) < 2 {
		return ""
	}
	return matched[1]
}

// maskString is used to mask a string which should not be displayed
// directly, like the auth token
func maskString(s string) string {
	if len(s) < 5 {
		return "**** (masked)"
	}

	return s[:5] + "**** (masked)"
}

var helpText = `Usage: ghr [options...] TAG PATH

ghr is a tool to create Release on Github and upload your
artifacts to it. ghr parallelizes upload of multiple artifacts.

You must specify tag (e.g., v1.0.0) and PATH to local artifacts.
If PATH is directory, ghr globs all files in the directory and
upload it. If PATH is a file then, upload only it.

And you also must provide GitHub API token which has enough permission
(For a private repository you need the 'repo' scope and for a public
repository need 'public_repo' scope). You can get token from GitHub's
account setting page.

You can use ghr on GitHub Enterprise. Set base URL via GITHUB_API
environment variable.

Options:

  -username, -u      Github repository owner name. By default, ghr
                     extracts it from global gitconfig value.

  -repository, -r    GitHub repository name. By default, ghr extracts
                     repository name from current directory's .git/config.

  -name, -n          GitHub release title. By default the tag is used.

  -token, -t         GitHub API Token. By default, ghr reads it from
                     'GITHUB_TOKEN' env var.

  -parallel=-1       Parallelization factor. This option limits amount
                     of parallelism of uploading. By default, ghr uses
                     number of logic CPU.

  -recreate          Recreate release if it already exists. If want to
                     upload to same release and replace use '-replace'.

  -replace           Replace artifacts if it is already uploaded. ghr
                     thinks it's same when local artifact base name
                     and uploaded file name are same.

  -soft              Stop uploading if the repository already has release
                     with the specified tag.
`
<commit_msg>Reformat help text to be more man-line<commit_after>// Command ghr is a tool to create a Github Release and upload your
// artifacts in parallel.
package main

import (
	"context"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"regexp"
	"runtime"
	"time"

	"github.com/google/go-github/github"
	"github.com/mitchellh/colorstring"
	"github.com/tcnksm/go-gitconfig"
)

const (
	// EnvGitHubToken is an environment var containing the GitHub API token
	EnvGitHubToken = "GITHUB_TOKEN"

	// EnvGitHubAPI is an environment var containing the GitHub API base endpoint.
	// This is used mainly by GitHub Enterprise users.
	EnvGitHubAPI = "GITHUB_API"

	// EnvDebug is an environment var to handle debug mode
	EnvDebug = "GHR_DEBUG"
)

// Exit codes are set to a value that represent an exit code for a particular error.
const (
	ExitCodeOK int = 0

	// Errors start at 10
	ExitCodeError = 10 + iota
	ExitCodeParseFlagsError
	ExitCodeBadArgs
	ExitCodeInvalidURL
	ExitCodeTokenNotFound
	ExitCodeOwnerNotFound
	ExitCodeRepoNotFound
	ExitCodeReleaseError
)

const (
	defaultCheckTimeout = 2 * time.Second
	defaultBaseURL      = "https://api.github.com/"
	defaultParallel     = -1
)

// Debugf prints debug output when EnvDebug is set
func Debugf(format string, args ...interface{}) {
	if env := os.Getenv(EnvDebug); len(env) != 0 {
		log.Printf("[DEBUG] "+format+"\n", args...)
	}
}

// PrintRedf prints red error message to console.
func PrintRedf(w io.Writer, format string, args ...interface{}) {
	format = fmt.Sprintf("[red]%s[reset]", format)
	fmt.Fprint(w,
		colorstring.Color(fmt.Sprintf(format, args...)))
}

// CLI is the main command line object
type CLI struct {
	// outStream and errStream correspond to stdout and stderr, respectively,
	// to take messages from the CLI.
	outStream, errStream io.Writer
}

// Run invokes the CLI with the given arguments.
func (cli *CLI) Run(args []string) int {

	var (
		owner string
		repo  string
		token string

		commitish  string
		name       string
		body       string
		draft      bool
		prerelease bool

		parallel int

		recreate bool
		replace  bool
		soft     bool

		stat    bool
		version bool
		debug   bool
	)

	flags := flag.NewFlagSet(Name, flag.ContinueOnError)
	flags.SetOutput(cli.errStream)
	flags.Usage = func() {
		fmt.Fprint(cli.errStream, helpText)
	}

	flags.StringVar(&owner, "username", "", "")
	flags.StringVar(&owner, "owner", "", "")
	flags.StringVar(&owner, "u", "", "")

	flags.StringVar(&repo, "repository", "", "")
	flags.StringVar(&repo, "r", "", "")

	flags.StringVar(&token, "token", os.Getenv(EnvGitHubToken), "")
	flags.StringVar(&token, "t", os.Getenv(EnvGitHubToken), "")

	flags.StringVar(&commitish, "commitish", "", "")
	flags.StringVar(&commitish, "c", "", "")

	flags.StringVar(&name, "name", "", "")
	flags.StringVar(&name, "n", "", "")

	flags.StringVar(&body, "body", "", "")
	flags.StringVar(&body, "b", "", "")

	flags.BoolVar(&draft, "draft", false, "")
	flags.BoolVar(&prerelease, "prerelease", false, "")

	flags.IntVar(&parallel, "parallel", defaultParallel, "")
	flags.IntVar(&parallel, "p", defaultParallel, "")

	flags.BoolVar(&recreate, "delete", false, "")
	flags.BoolVar(&recreate, "recreate", false, "")

	flags.BoolVar(&replace, "replace", false, "")

	flags.BoolVar(&soft, "soft", false, "")

	flags.BoolVar(&version, "version", false, "")
	flags.BoolVar(&version, "v", false, "")

	flags.BoolVar(&debug, "debug", false, "")

	// Deprecated
	flags.BoolVar(&stat, "stat", false, "")

	// Parse flags
	if err := flags.Parse(args[1:]); err != nil {
		return ExitCodeParseFlagsError
	}

	if debug {
		os.Setenv(EnvDebug, "1")
		Debugf("Run as DEBUG mode")
	}

	// Show version and check latest version release
	if version {
		fmt.Fprintf(cli.outStream, OutputVersion())
		return ExitCodeOK
	}

	parsedArgs := flags.Args()
	if len(parsedArgs) != 2 {
		PrintRedf(cli.errStream,
			"Invalid argument: you must set TAG and PATH name.\n")
		return ExitCodeBadArgs
	}
	tag, path := parsedArgs[0], parsedArgs[1]

	// Extract github repository owner username.
	// If it's not provided via command line flag, read it from .gitconfig
	// (github user or git user).
	if len(owner) == 0 {
		origin, err := gitconfig.OriginURL()
		if err == nil {
			owner = retrieveOwnerName(origin)
		}
		if len(owner) == 0 {
			owner, err = gitconfig.GithubUser()
			if err != nil {
				owner, err = gitconfig.Username()
			}

			if err != nil {
				PrintRedf(cli.errStream,
					"Failed to set up ghr: repository owner name not found\n")
				fmt.Fprintf(cli.errStream,
					"Please set it via `-u` option.\n\n"+
						"You can set default owner name in `github.username` or `user.name`\n"+
						"in `~/.gitconfig` file\n")
				return ExitCodeOwnerNotFound
			}
		}
	}
	Debugf("Owner: %s", owner)

	// Extract repository name from files.
	// If not provided, read it from .git/config file.
	if len(repo) == 0 {
		var err error
		repo, err = gitconfig.Repository()
		if err != nil {
			PrintRedf(cli.errStream,
				"Failed to set up ghr: repository name not found\n")
			fmt.Fprintf(cli.errStream,
				"ghr reads it from `.git/config` file. Change directory to \n"+
					"repository root directory or setup git repository.\n"+
					"Or set it via `-r` option.\n")
			return ExitCodeRepoNotFound
		}
	}
	Debugf("Repository: %s", repo)

	// If GitHub API token is not provided via command line flag
	// or env var then read it from .gitconfig file.
	if len(token) == 0 {
		var err error
		token, err = gitconfig.GithubToken()
		if err != nil {
			PrintRedf(cli.errStream, "Failed to set up ghr: token not found\n")
			fmt.Fprintf(cli.errStream,
				"To use ghr, you need a GitHub API token.\n"+
					"Please set it via `%s` env var or `-t` option.\n\n"+
					"If you don't have one, visit official doc (goo.gl/jSnoI)\n"+
					"and get it first.\n",
				EnvGitHubToken)
			return ExitCodeTokenNotFound
		}
	}
	Debugf("Github API Token: %s", maskString(token))

	// Set Base GitHub API URL. Base URL can also be provided via env var for use with GHE.
	baseURLStr := defaultBaseURL
	if urlStr := os.Getenv(EnvGitHubAPI); len(urlStr) != 0 {
		baseURLStr = urlStr
	}
	Debugf("Base GitHub API URL: %s", baseURLStr)

	if parallel <= 0 {
		parallel = runtime.NumCPU()
	}
	Debugf("Parallel factor: %d", parallel)

	localAssets, err := LocalAssets(path)
	if err != nil {
		PrintRedf(cli.errStream,
			"Failed to find assets from %s: %s\n", path, err)
		return ExitCodeError
	}
	Debugf("Number of file to upload: %d", len(localAssets))

	// Create a GitHub client
	gitHubClient, err := NewGitHubClient(owner, repo, token, baseURLStr)
	if err != nil {
		PrintRedf(cli.errStream, "Failed to construct GitHub client: %s\n", err)
		return ExitCodeError
	}

	ghr := GHR{
		GitHub:    gitHubClient,
		outStream: cli.outStream,
	}

	Debugf("Name: %s", name)

	// Prepare create release request
	req := &github.RepositoryRelease{
		Name:            github.String(name),
		TagName:         github.String(tag),
		Prerelease:      github.Bool(prerelease),
		Draft:           github.Bool(draft),
		TargetCommitish: github.String(commitish),
		Body:            github.String(body),
	}

	ctx := context.TODO()

	if soft {
		_, err := ghr.GitHub.GetRelease(ctx, *req.TagName)

		if err == nil {
			fmt.Fprintf(cli.outStream, "ghr aboted since tag `%s` already exists\n", *req.TagName)
			return ExitCodeOK
		}

		if err != nil && err != ErrReleaseNotFound {
			PrintRedf(cli.errStream, "Failed to get GitHub release: %s\n", err)
			return ExitCodeError
		}
	}

	release, err := ghr.CreateRelease(ctx, req, recreate)
	if err != nil {
		PrintRedf(cli.errStream, "Failed to create GitHub release page: %s\n", err)
		return ExitCodeError
	}

	if replace {
		err := ghr.DeleteAssets(ctx, *release.ID, localAssets, parallel)
		if err != nil {
			PrintRedf(cli.errStream, "Failed to delete existing assets: %s\n", err)
			return ExitCodeError
		}
	}

	// FIXME(tcnksm): More ideal way to change this
	// This is for Github enterprise
	if err := ghr.GitHub.SetUploadURL(*release.UploadURL); err != nil {
		fmt.Fprintf(cli.errStream, "Failed to set upload URL %s: %s\n", *release.UploadURL, err)
		return ExitCodeError
	}

	err = ghr.UploadAssets(ctx, *release.ID, localAssets, parallel)
	if err != nil {
		PrintRedf(cli.errStream, "Failed to upload one of assets: %s\n", err)
		return ExitCodeError
	}

	if !draft {
		_, err := ghr.GitHub.EditRelease(ctx, *release.ID, &github.RepositoryRelease{
			Draft: github.Bool(false),
		})
		if err != nil {
			PrintRedf(cli.errStream, "Failed to publish release: %s\n", err)
			return ExitCodeError
		}
	}

	return ExitCodeOK
}

var ownerNameReg = regexp.MustCompile(`([-a-zA-Z0-9]+)/[^/]+$`)

func retrieveOwnerName(repoURL string) string {
	matched := ownerNameReg.FindStringSubmatch(repoURL)
	if len(matched) < 2 {
		return ""
	}
	return matched[1]
}

// maskString is used to mask a string which should not be displayed
// directly, like the auth token
func maskString(s string) string {
	if len(s) < 5 {
		return "**** (masked)"
	}

	return s[:5] + "**** (masked)"
}

var helpText = `Usage: ghr [options...] TAG PATH

ghr is a tool to create Release on Github and upload your
artifacts to it. ghr parallelizes upload of multiple artifacts.

You must specify tag (e.g., v1.0.0) and PATH to local artifacts.
If PATH is directory, ghr globs all files in the directory and
upload it. If PATH is a file then, upload only it.

And you also must provide GitHub API token which has enough permission
(For a private repository you need the 'repo' scope and for a public
repository need 'public_repo' scope). You can get token from GitHub's
account setting page.

You can use ghr on GitHub Enterprise. Set base URL via GITHUB_API
environment variable.

Options:

-username, -u
	Github repository owner name. By default, ghr extracts it from global
	gitconfig value.

-repository, -r
	GitHub repository name. By default, ghr extracts repository name from
	current directory's .git/config.

-name, -n
	GitHub release title. By default the tag is used.

-token, -t
	GitHub API Token. By default, ghr reads it from 'GITHUB_TOKEN' env var.

-parallel=-1
	Parallelization factor. This option limits amount of parallelism of
	uploading. By default, ghr uses number of logic CPU.

-recreate
	Recreate release if it already exists. If want to upload to same release
	and replace use '-replace'.

-replace
	Replace artifacts if it is already uploaded. ghr thinks it's same when
	local artifact base name and uploaded file name are same.

-soft
	Stop uploading if the repository already has release with the specified
	tag.
`
<|endoftext|>


<commit_before>package main

import (
	"log"
	"os"
	"path"

	"github.com/codegangsta/cli"
)

// Logger is used to log errors; if nil, the default log.Logger is used.
var Logger *log.Logger

// logger is an helper function to retrieve the available logger
func logger() *log.Logger {
	if Logger == nil {
		Logger = log.New(os.Stderr, "", log.LstdFlags)
	}
	return Logger
}

func main() {

	app := cli.NewApp()
	app.Name = "lego"
	app.Usage = "Let's encrypt client to go!"
	app.Version = "0.0.1"

	cwd, err := os.Getwd()
	if err != nil {
		logger().Fatal("Could not determine current working directory. Please pass --path.")
	}
	defaultPath := path.Join(cwd, ".lego")

	app.Commands = []cli.Command{
		{
			Name:   "run",
			Usage:  "Create and install a certificate",
			Action: run,
		},
		{
			Name:  "auth",
			Usage: "Create a certificate",
			Action: func(c *cli.Context) {
				logger().Fatal("Not implemented")
			},
		},
		{
			Name:  "install",
			Usage: "Install a certificate",
			Action: func(c *cli.Context) {
				logger().Fatal("Not implemented")
			},
		},
		{
			Name:  "revoke",
			Usage: "Revoke a certificate",
			Action: func(c *cli.Context) {
				logger().Fatal("Not implemented")
			},
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "certificate",
					Usage: "Revoke a specific certificate",
				},
				cli.StringFlag{
					Name:  "key",
					Usage: "Revoke all certs generated by the provided authorized key.",
				},
			},
		},
		{
			Name:  "rollback",
			Usage: "Rollback a certificate",
			Action: func(c *cli.Context) {
				logger().Fatal("Not implemented")
			},
			Flags: []cli.Flag{
				cli.IntFlag{
					Name:  "checkpoints",
					Usage: "Revert configuration N number of checkpoints",
				},
			},
		},
	}

	app.Flags = []cli.Flag{
		cli.StringSliceFlag{
			Name:  "domains, d",
			Usage: "Add domains to the process",
		},
		cli.StringFlag{
			Name:  "server, s",
			Value: "https://www.letsencrypt-demo.org/acme/new-reg",
			Usage: "CA hostname (and optionally :port). The server certificate must be trusted in order to avoid further modifications to the client.",
		},
		cli.StringFlag{
			Name:  "email, m",
			Usage: "Email used for registration and recovery contact.",
		},
		cli.IntFlag{
			Name:  "rsa-key-size, B",
			Value: 2048,
			Usage: "Size of the RSA key.",
		},
		cli.BoolFlag{
			Name:  "no-confirm",
			Usage: "Turn off confirmation screens.",
		},
		cli.BoolFlag{
			Name:  "agree-tos, e",
			Usage: "Skip the end user license agreement screen.",
		},
		cli.StringFlag{
			Name:  "path",
			Usage: "Directory to use for storing the data",
			Value: defaultPath,
		},
		cli.StringFlag{
			Name:  "port",
			Usage: "Challenges will use this port to listen on. Please make sure to forward port 443 to this port on your machine. Otherwise use setcap on the binary",
		},
	}

	app.Run(os.Args)
}
<commit_msg>Update standard to release URL<commit_after>package main

import (
	"log"
	"os"
	"path"

	"github.com/codegangsta/cli"
)

// Logger is used to log errors; if nil, the default log.Logger is used.
var Logger *log.Logger

// logger is an helper function to retrieve the available logger
func logger() *log.Logger {
	if Logger == nil {
		Logger = log.New(os.Stderr, "", log.LstdFlags)
	}
	return Logger
}

func main() {

	app := cli.NewApp()
	app.Name = "lego"
	app.Usage = "Let's encrypt client to go!"
	app.Version = "0.0.1"

	cwd, err := os.Getwd()
	if err != nil {
		logger().Fatal("Could not determine current working directory. Please pass --path.")
	}
	defaultPath := path.Join(cwd, ".lego")

	app.Commands = []cli.Command{
		{
			Name:   "run",
			Usage:  "Create and install a certificate",
			Action: run,
		},
		{
			Name:  "auth",
			Usage: "Create a certificate",
			Action: func(c *cli.Context) {
				logger().Fatal("Not implemented")
			},
		},
		{
			Name:  "install",
			Usage: "Install a certificate",
			Action: func(c *cli.Context) {
				logger().Fatal("Not implemented")
			},
		},
		{
			Name:  "revoke",
			Usage: "Revoke a certificate",
			Action: func(c *cli.Context) {
				logger().Fatal("Not implemented")
			},
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "certificate",
					Usage: "Revoke a specific certificate",
				},
				cli.StringFlag{
					Name:  "key",
					Usage: "Revoke all certs generated by the provided authorized key.",
				},
			},
		},
		{
			Name:  "rollback",
			Usage: "Rollback a certificate",
			Action: func(c *cli.Context) {
				logger().Fatal("Not implemented")
			},
			Flags: []cli.Flag{
				cli.IntFlag{
					Name:  "checkpoints",
					Usage: "Revert configuration N number of checkpoints",
				},
			},
		},
	}

	app.Flags = []cli.Flag{
		cli.StringSliceFlag{
			Name:  "domains, d",
			Usage: "Add domains to the process",
		},
		cli.StringFlag{
			Name:  "server, s",
			Value: "https://acme-staging.api.letsencrypt.org/acme/new-reg",
			Usage: "CA hostname (and optionally :port). The server certificate must be trusted in order to avoid further modifications to the client.",
		},
		cli.StringFlag{
			Name:  "email, m",
			Usage: "Email used for registration and recovery contact.",
		},
		cli.IntFlag{
			Name:  "rsa-key-size, B",
			Value: 2048,
			Usage: "Size of the RSA key.",
		},
		cli.BoolFlag{
			Name:  "no-confirm",
			Usage: "Turn off confirmation screens.",
		},
		cli.BoolFlag{
			Name:  "agree-tos, e",
			Usage: "Skip the end user license agreement screen.",
		},
		cli.StringFlag{
			Name:  "path",
			Usage: "Directory to use for storing the data",
			Value: defaultPath,
		},
		cli.StringFlag{
			Name:  "port",
			Usage: "Challenges will use this port to listen on. Please make sure to forward port 443 to this port on your machine. Otherwise use setcap on the binary",
		},
	}

	app.Run(os.Args)
}
<|endoftext|>