<commit_before>package bot

import (
	"log"
	"strings"
	"io/ioutil"
	"github.com/tidwall/gjson"
	"github.com/marcossegovia/apiai-go"
	"gopkg.in/telegram-bot-api.v4"
)

func routeCommand(update tgbotapi.Update, ctx *BotContext) {
	command := strings.ToLower(update.Message.Command())
	var response tgbotapi.MessageConfig
	var out string
	switch command {
	case "start":
        StartMessage(update, ctx)
	case "help":
        HelpMessage(update, ctx)
	case "domanda":
		text := update.Message.CommandArguments()
		if text != "" {
			question(update.Message.From.UserName, text, ctx.Bot)
			out = "Grazie della domanda, chiederò ai miei superiori"+
			" e ti risponderò il prima possibile! 😃"
		} else {
			out = "Per fare una domanda aggiungi il testo della domanda dopo il comando!"
		}
		response = tgbotapi.NewMessage(update.Message.Chat.ID, out)
		case "keyboard": fallthrough
	case "tastiera":
		out = "Ai tuoi ordini!"
		response = tgbotapi.NewMessage(update.Message.Chat.ID, out)
		button1 := tgbotapi.NewKeyboardButton("Eventi \xF0\x9F\x8E\xAB")
		button2 := tgbotapi.NewKeyboardButton("Tutoraggi \xE2\x9D\x93")
        button3 := tgbotapi.NewKeyboardButton("Chi siamo?")
		button4 := tgbotapi.NewKeyboardButton("Nascondi")
		key := tgbotapi.NewKeyboardButtonRow(button1, button2, button3, button4)
		response.ReplyMarkup = tgbotapi.NewReplyKeyboard(key)
	default:
		out = "Scusa, non ho capito! Prova a scrivere il testo senza lo slash"
		response = tgbotapi.NewMessage(update.Message.Chat.ID, out)
	}
	if response.Text != "" {
		response.ParseMode = "HTML"
		ctx.Bot.Send(response)
	}
}

func routeText(update tgbotapi.Update, ctx *BotContext) {
    qr, err := ctx.ApiAi.Query(apiai.Query{Query: []string{update.Message.Text}, SessionId:ctx.Config.ApiAiSessionId})
	var response tgbotapi.MessageConfig
	if err != nil {
		log.Print(err)
        return
	}
	if qr.Status.Code != 200 {
		response = tgbotapi.NewMessage(update.Message.Chat.ID, "In questo momento"+
		" non stò molto bene, potresti provare a scrivermi più tardi?")
	}
	switch qr.Result.Action {
	case "input.question":
		question(update.Message.From.UserName, qr.Result.Params["any"], ctx.Bot)
		response = tgbotapi.NewMessage(update.Message.Chat.ID,
		qr.Result.Fulfillment.Speech)
	case "information.events":
		showEvents(update.Message.Chat.ID, ctx.Bot)
	case "buttons.hide":
		response = tgbotapi.NewMessage(update.Message.Chat.ID, "Okay!")
		response.ReplyMarkup = tgbotapi.NewRemoveKeyboard(true)
	case "input.unknown":
		response = tgbotapi.NewMessage(update.Message.Chat.ID,
			qr.Result.Fulfillment.Speech +
			"Usa il comando /help per sapere cosa puoi chiedermi")
	case "input.history":
        HknHistory(update, ctx)
    case "support.problem":
        question(update.Message.From.UserName, "bug_reporting: "+qr.Result.Fulfillment.Speech, ctx.Bot)
	default:
		response = tgbotapi.NewMessage(update.Message.Chat.ID,
		qr.Result.Fulfillment.Speech)
	}
	if response.Text != "" {
		response.ParseMode = "HTML"
		ctx.Bot.Send(response)
	}
}

func routeInvalid(update tgbotapi.Update, ctx *BotContext) {
    response := tgbotapi.NewMessage(update.Message.Chat.ID, "Scusa ma io capisco solo il testo!")
    ctx.Bot.Send(response)
}

func RouteMessages(ctx *BotContext) {
	for update := range ctx.UpChannel {
		if update.Message == nil {
			continue
		}
		if update.Message.IsCommand() {
			routeCommand(update, ctx)
        } else if update.Message.Text == "" {
            routeInvalid(update, ctx)
        } else {
			routeText(update, ctx)
		}
	}
}

func showEvents(id int64, bot *tgbotapi.BotAPI) {
    msg := tgbotapi.NewMessage(id, "I prossimi eventi sono:")
    bot.Send(msg)
    // open the events file
    buf, err := ioutil.ReadFile("events.json")
    if err != nil {
        log.Print(err)
    }
    events := gjson.GetBytes(buf, "events")
    events.ForEach(func(key, value gjson.Result) bool {
        // prepare the messages from json
        out := ""+value.Get("name").String()+"\n"
        out += value.Get("description").String()
        msg = tgbotapi.NewMessage(id, out)
        msg.ParseMode = "HTML"
        button1 := tgbotapi.NewInlineKeyboardButtonData("Descrizione \xF0\x9F\x8E\xAB", "qu    estion")
        button2 := tgbotapi.NewInlineKeyboardButtonData("Registrati \xE2\x9D\x93", "registr    ati")
        row := tgbotapi.NewInlineKeyboardRow(button1, button2)
        keyboard := tgbotapi.NewInlineKeyboardMarkup(row)
        msg.ReplyMarkup = keyboard
        bot.Send(msg)
        return true // keep iterating
    })
}

func question(user string, text string, bot *tgbotapi.BotAPI) {
    var out string = "@"+user+": "+text
    msg := tgbotapi.NewMessageToChannel("-1001111552162", out)
    msg.ParseMode = "HTML"
    bot.Send(msg)
}
<commit_msg>fix indentation<commit_after>package bot

import (
	"log"
	"strings"
	"io/ioutil"
	"github.com/tidwall/gjson"
	"github.com/marcossegovia/apiai-go"
	"gopkg.in/telegram-bot-api.v4"
)

func routeCommand(update tgbotapi.Update, ctx *BotContext) {
	command := strings.ToLower(update.Message.Command())
	var response tgbotapi.MessageConfig
	var out string
	switch command {
	case "start":
        StartMessage(update, ctx)
	case "help":
        HelpMessage(update, ctx)
	case "domanda":
		text := update.Message.CommandArguments()
		if text != "" {
			question(update.Message.From.UserName, text, ctx.Bot)
			out = "Grazie della domanda, chiederò ai miei superiori"+
			" e ti risponderò il prima possibile! 😃"
		} else {
			out = "Per fare una domanda aggiungi il testo della domanda dopo il comando!"
		}
		response = tgbotapi.NewMessage(update.Message.Chat.ID, out)
    case "keyboard": fallthrough
	case "tastiera":
		out = "Ai tuoi ordini!"
		response = tgbotapi.NewMessage(update.Message.Chat.ID, out)
		button1 := tgbotapi.NewKeyboardButton("Eventi \xF0\x9F\x8E\xAB")
		button2 := tgbotapi.NewKeyboardButton("Tutoraggi \xE2\x9D\x93")
        button3 := tgbotapi.NewKeyboardButton("Chi siamo?")
		button4 := tgbotapi.NewKeyboardButton("Nascondi")
		key := tgbotapi.NewKeyboardButtonRow(button1, button2, button3, button4)
		response.ReplyMarkup = tgbotapi.NewReplyKeyboard(key)
	default:
		out = "Scusa, non ho capito! Prova a scrivere il testo senza lo slash"
		response = tgbotapi.NewMessage(update.Message.Chat.ID, out)
	}
	if response.Text != "" {
		response.ParseMode = "HTML"
		ctx.Bot.Send(response)
	}
}

func routeText(update tgbotapi.Update, ctx *BotContext) {
    qr, err := ctx.ApiAi.Query(apiai.Query{Query: []string{update.Message.Text}, SessionId:ctx.Config.ApiAiSessionId})
	var response tgbotapi.MessageConfig
	if err != nil {
		log.Print(err)
        return
	}
	if qr.Status.Code != 200 {
		response = tgbotapi.NewMessage(update.Message.Chat.ID, "In questo momento"+
		" non stò molto bene, potresti provare a scrivermi più tardi?")
	}
	switch qr.Result.Action {
	case "input.question":
		question(update.Message.From.UserName, qr.Result.Params["any"], ctx.Bot)
		response = tgbotapi.NewMessage(update.Message.Chat.ID,
		qr.Result.Fulfillment.Speech)
	case "information.events":
		showEvents(update.Message.Chat.ID, ctx.Bot)
	case "buttons.hide":
		response = tgbotapi.NewMessage(update.Message.Chat.ID, "Okay!")
		response.ReplyMarkup = tgbotapi.NewRemoveKeyboard(true)
	case "input.unknown":
		response = tgbotapi.NewMessage(update.Message.Chat.ID,
			qr.Result.Fulfillment.Speech +
			"Usa il comando /help per sapere cosa puoi chiedermi")
	case "input.history":
        HknHistory(update, ctx)
    case "support.problem":
        question(update.Message.From.UserName, "bug_reporting: "+qr.Result.Fulfillment.Speech, ctx.Bot)
	default:
		response = tgbotapi.NewMessage(update.Message.Chat.ID,
		qr.Result.Fulfillment.Speech)
	}
	if response.Text != "" {
		response.ParseMode = "HTML"
		ctx.Bot.Send(response)
	}
}

func routeInvalid(update tgbotapi.Update, ctx *BotContext) {
    response := tgbotapi.NewMessage(update.Message.Chat.ID, "Scusa ma io capisco solo il testo!")
    ctx.Bot.Send(response)
}

func RouteMessages(ctx *BotContext) {
	for update := range ctx.UpChannel {
		if update.Message == nil {
			continue
		}
		if update.Message.IsCommand() {
			routeCommand(update, ctx)
        } else if update.Message.Text == "" {
            routeInvalid(update, ctx)
        } else {
			routeText(update, ctx)
		}
	}
}

func showEvents(id int64, bot *tgbotapi.BotAPI) {
    msg := tgbotapi.NewMessage(id, "I prossimi eventi sono:")
    bot.Send(msg)
    // open the events file
    buf, err := ioutil.ReadFile("events.json")
    if err != nil {
        log.Print(err)
    }
    events := gjson.GetBytes(buf, "events")
    events.ForEach(func(key, value gjson.Result) bool {
        // prepare the messages from json
        out := ""+value.Get("name").String()+"\n"
        out += value.Get("description").String()
        msg = tgbotapi.NewMessage(id, out)
        msg.ParseMode = "HTML"
        button1 := tgbotapi.NewInlineKeyboardButtonData("Descrizione \xF0\x9F\x8E\xAB", "qu    estion")
        button2 := tgbotapi.NewInlineKeyboardButtonData("Registrati \xE2\x9D\x93", "registr    ati")
        row := tgbotapi.NewInlineKeyboardRow(button1, button2)
        keyboard := tgbotapi.NewInlineKeyboardMarkup(row)
        msg.ReplyMarkup = keyboard
        bot.Send(msg)
        return true // keep iterating
    })
}

func question(user string, text string, bot *tgbotapi.BotAPI) {
    var out string = "@"+user+": "+text
    msg := tgbotapi.NewMessageToChannel("-1001111552162", out)
    msg.ParseMode = "HTML"
    bot.Send(msg)
}
<|endoftext|>


<commit_before>package changes

import (
	"reflect"
	"strings"
	"sync"
)

// KeyMapper defines an interface for finding the key name for a given type field.
type KeyMapper interface {
	// KeyIndexes returns the key names, and their location, for comparing. This
	// operation should be heavily cached to avoid runtime performance issues.
	KeyIndexes(reflect.Value) (KeyIndexes, error)
}

// TagMapper is a KeyMapper implements that looks up tags in a sorted order for
// key names, then falls back to the field name.
type TagMapper struct {
	tags  []string
	types map[reflect.Type]KeyIndexes
	sync.RWMutex
}

// KeyIndexes provides an ordered list of keys with their reflection indexes.
type KeyIndexes struct {
	Keys    []string
	Indexes map[string][]int
}

// NewKeyIndexes initialises a new KeyIndexes.
func NewKeyIndexes() KeyIndexes {
	return KeyIndexes{
		Keys:    []string{},
		Indexes: map[string][]int{},
	}
}

// NewTagMapper returns a new TagMapper instance.
func NewTagMapper(tags ...string) *TagMapper {
	return &TagMapper{
		tags:  tags,
		types: map[reflect.Type]KeyIndexes{},
	}
}

// KeyIndexes implements the KeyMapper interface and returns the keys and their
// locations in the value's type.
func (mapper *TagMapper) KeyIndexes(value reflect.Value) (KeyIndexes, error) {
	mapper.RLock()
	defer mapper.RUnlock()

	typ := value.Type()
	if indexes, ok := mapper.types[typ]; ok {
		return indexes, nil
	}
	return mapper.registerValue(value)
}

// registerValue will create an index lookup, save it for later use, and return it.
func (mapper *TagMapper) registerValue(value reflect.Value) (KeyIndexes, error) {
	mapper.Lock()
	indexes := mapper.registerPart("", NewKeyIndexes(), value, []int{})
	mapper.types[value.Type()] = indexes
	mapper.Unlock()
	return indexes, nil
}

func (mapper *TagMapper) registerPart(prefix string, indexes KeyIndexes, val reflect.Value, runningIndex []int) KeyIndexes {
	// Get the type of the instance supplied.
	typ := val.Type()

	for i := 0; i < typ.NumField(); i++ {
		// Loop over every field, and add the sideload entity if it exists.
		field := typ.Field(i)

		// Exclude unexported fields.
		if field.PkgPath != "" {
			continue
		}

		fieldType := field.Type
		value := val.Field(i)
		// If this is a pointer, we need to take the pointer field and value.
		if fieldType.Kind() == reflect.Ptr {
			fieldType = fieldType.Elem()
			value = value.Elem()
		}

		// Create the index value.
		index := make([]int, len(runningIndex)+1)
		copy(index, runningIndex)
		index[len(runningIndex)] = i

		/**
		 * Due to the complicity of differentiating between a straight up struct,
		 * and an implementation of a type, we just look at the first level by default.
		 * For example, how do we determine the difference between an embedded struct
		 * and a stdlib struct such as `time.Time`. Chances are we want to compare
		 * the `time.Time` vs the underlying values.
		 * An optional tag can force recursion of a struct. FieldName string `diff:"include"`
		 */
		diffTag := field.Tag.Get("diff")
		if diffTag == "exclude" {
			continue
		}

		// Generate a name for this field, which can be set via a tag.
		var tagName string
		for _, tag := range mapper.tags {
			tagName = field.Tag.Get(tag)
			if tagName != "" {
				break
			}
		}

		var name string
		if tagName != "" {
			name = prefix + tagName
		} else {
			// If recursion of a struct append field name to given prefix.
			name = prefix + field.Name

			// If struct has single field, strip period from prefix and don't append
			// inner field name.
			if typ.NumField() == 1 {
				name = strings.TrimSuffix(prefix, ".")
			}
		}

		// If this is a struct, we can go deeper - but only if it tells us to.
		if fieldType.Kind() == reflect.Struct && diffTag == "include" {
			// IsValid is true if it's not the zero value and CanInterface is true
			// if it's an exported field.
			if !value.IsValid() || !value.CanInterface() {
				continue
			}

			// If this is a struct, recurse down the chain with the interface.
			indexes = mapper.registerPart(name+".", indexes, reflect.ValueOf(value.Interface()), index)
			continue
		}
		indexes.Keys = append(indexes.Keys, name)
		indexes.Indexes[name] = index
	}
	return indexes
}
<commit_msg>Ensure RUnlock() called directly after read to prevent deadlock within registerValue()<commit_after>package changes

import (
	"reflect"
	"strings"
	"sync"
)

// KeyMapper defines an interface for finding the key name for a given type field.
type KeyMapper interface {
	// KeyIndexes returns the key names, and their location, for comparing. This
	// operation should be heavily cached to avoid runtime performance issues.
	KeyIndexes(reflect.Value) (KeyIndexes, error)
}

// TagMapper is a KeyMapper implements that looks up tags in a sorted order for
// key names, then falls back to the field name.
type TagMapper struct {
	tags  []string
	types map[reflect.Type]KeyIndexes
	sync.RWMutex
}

// KeyIndexes provides an ordered list of keys with their reflection indexes.
type KeyIndexes struct {
	Keys    []string
	Indexes map[string][]int
}

// NewKeyIndexes initialises a new KeyIndexes.
func NewKeyIndexes() KeyIndexes {
	return KeyIndexes{
		Keys:    []string{},
		Indexes: map[string][]int{},
	}
}

// NewTagMapper returns a new TagMapper instance.
func NewTagMapper(tags ...string) *TagMapper {
	return &TagMapper{
		tags:  tags,
		types: map[reflect.Type]KeyIndexes{},
	}
}

// KeyIndexes implements the KeyMapper interface and returns the keys and their
// locations in the value's type.
func (mapper *TagMapper) KeyIndexes(value reflect.Value) (KeyIndexes, error) {
	mapper.RLock()
	typ := value.Type()
	indexes, ok := mapper.types[typ]
	mapper.RUnlock()
	if ok {
		return indexes, nil
	}
	return mapper.registerValue(value)
}

// registerValue will create an index lookup, save it for later use, and return it.
func (mapper *TagMapper) registerValue(value reflect.Value) (KeyIndexes, error) {
	mapper.Lock()
	indexes := mapper.registerPart("", NewKeyIndexes(), value, []int{})
	mapper.types[value.Type()] = indexes
	mapper.Unlock()
	return indexes, nil
}

func (mapper *TagMapper) registerPart(prefix string, indexes KeyIndexes, val reflect.Value, runningIndex []int) KeyIndexes {
	// Get the type of the instance supplied.
	typ := val.Type()

	for i := 0; i < typ.NumField(); i++ {
		// Loop over every field, and add the sideload entity if it exists.
		field := typ.Field(i)

		// Exclude unexported fields.
		if field.PkgPath != "" {
			continue
		}

		fieldType := field.Type
		value := val.Field(i)
		// If this is a pointer, we need to take the pointer field and value.
		if fieldType.Kind() == reflect.Ptr {
			fieldType = fieldType.Elem()
			value = value.Elem()
		}

		// Create the index value.
		index := make([]int, len(runningIndex)+1)
		copy(index, runningIndex)
		index[len(runningIndex)] = i

		/**
		 * Due to the complicity of differentiating between a straight up struct,
		 * and an implementation of a type, we just look at the first level by default.
		 * For example, how do we determine the difference between an embedded struct
		 * and a stdlib struct such as `time.Time`. Chances are we want to compare
		 * the `time.Time` vs the underlying values.
		 * An optional tag can force recursion of a struct. FieldName string `diff:"include"`
		 */
		diffTag := field.Tag.Get("diff")
		if diffTag == "exclude" {
			continue
		}

		// Generate a name for this field, which can be set via a tag.
		var tagName string
		for _, tag := range mapper.tags {
			tagName = field.Tag.Get(tag)
			if tagName != "" {
				break
			}
		}

		var name string
		if tagName != "" {
			name = prefix + tagName
		} else {
			// If recursion of a struct append field name to given prefix.
			name = prefix + field.Name

			// If struct has single field, strip period from prefix and don't append
			// inner field name.
			if typ.NumField() == 1 {
				name = strings.TrimSuffix(prefix, ".")
			}
		}

		// If this is a struct, we can go deeper - but only if it tells us to.
		if fieldType.Kind() == reflect.Struct && diffTag == "include" {
			// IsValid is true if it's not the zero value and CanInterface is true
			// if it's an exported field.
			if !value.IsValid() || !value.CanInterface() {
				continue
			}

			// If this is a struct, recurse down the chain with the interface.
			indexes = mapper.registerPart(name+".", indexes, reflect.ValueOf(value.Interface()), index)
			continue
		}
		indexes.Keys = append(indexes.Keys, name)
		indexes.Indexes[name] = index
	}
	return indexes
}
<|endoftext|>


<commit_before>package spinner

const (
	clockOneOClock = '\U0001F550'
	clockOneThirty = '\U0001F55C'
)

// CharSets contains the available character sets
var CharSets = map[int][]string{
	0:  {"←", "↖", "↑", "↗", "→", "↘", "↓", "↙"},
	1:  {"▁", "▃", "▄", "▅", "▆", "▇", "█", "▇", "▆", "▅", "▄", "▃", "▁"},
	2:  {"▖", "▘", "▝", "▗"},
	3:  {"┤", "┘", "┴", "└", "├", "┌", "┬", "┐"},
	4:  {"◢", "◣", "◤", "◥"},
	5:  {"◰", "◳", "◲", "◱"},
	6:  {"◴", "◷", "◶", "◵"},
	7:  {"◐", "◓", "◑", "◒"},
	8:  {".", "o", "O", "@", "*"},
	9:  {"|", "/", "-", "\\"},
	10: {"◡◡", "⊙⊙", "◠◠"},
	11: {"⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷"},
	12: {">))'>", " >))'>", "  >))'>", "   >))'>", "    >))'>", "   <'((<", "  <'((<", " <'((<"},
	13: {"⠁", "⠂", "⠄", "⡀", "⢀", "⠠", "⠐", "⠈"},
	14: {"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"},
	15: {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"},
	16: {"▉", "▊", "▋", "▌", "▍", "▎", "▏", "▎", "▍", "▌", "▋", "▊", "▉"},
	17: {"■", "□", "▪", "▫"},
	18: {"←", "↑", "→", "↓"},
	19: {"╫", "╪"},
	20: {"⇐", "⇖", "⇑", "⇗", "⇒", "⇘", "⇓", "⇙"},
	21: {"⠁", "⠁", "⠉", "⠙", "⠚", "⠒", "⠂", "⠂", "⠒", "⠲", "⠴", "⠤", "⠄", "⠄", "⠤", "⠠", "⠠", "⠤", "⠦", "⠖", "⠒", "⠐", "⠐", "⠒", "⠓", "⠋", "⠉", "⠈", "⠈"},
	22: {"⠈", "⠉", "⠋", "⠓", "⠒", "⠐", "⠐", "⠒", "⠖", "⠦", "⠤", "⠠", "⠠", "⠤", "⠦", "⠖", "⠒", "⠐", "⠐", "⠒", "⠓", "⠋", "⠉", "⠈"},
	23: {"⠁", "⠉", "⠙", "⠚", "⠒", "⠂", "⠂", "⠒", "⠲", "⠴", "⠤", "⠄", "⠄", "⠤", "⠴", "⠲", "⠒", "⠂", "⠂", "⠒", "⠚", "⠙", "⠉", "⠁"},
	24: {"⠋", "⠙", "⠚", "⠒", "⠂", "⠂", "⠒", "⠲", "⠴", "⠦", "⠖", "⠒", "⠐", "⠐", "⠒", "⠓", "⠋"},
	25: {"ｦ", "ｧ", "ｨ", "ｩ", "ｪ", "ｫ", "ｬ", "ｭ", "ｮ", "ｯ", "ｱ", "ｲ", "ｳ", "ｴ", "ｵ", "ｶ", "ｷ", "ｸ", "ｹ", "ｺ", "ｻ", "ｼ", "ｽ", "ｾ", "ｿ", "ﾀ", "ﾁ", "ﾂ", "ﾃ", "ﾄ", "ﾅ", "ﾆ", "ﾇ", "ﾈ", "ﾉ", "ﾊ", "ﾋ", "ﾌ", "ﾍ", "ﾎ", "ﾏ", "ﾐ", "ﾑ", "ﾒ", "ﾓ", "ﾔ", "ﾕ", "ﾖ", "ﾗ", "ﾘ", "ﾙ", "ﾚ", "ﾛ", "ﾜ", "ﾝ"},
	26: {".", "..", "..."},
	27: {"▁", "▂", "▃", "▄", "▅", "▆", "▇", "█", "▉", "▊", "▋", "▌", "▍", "▎", "▏", "▏", "▎", "▍", "▌", "▋", "▊", "▉", "█", "▇", "▆", "▅", "▄", "▃", "▂", "▁"},
	28: {".", "o", "O", "°", "O", "o", "."},
	29: {"+", "x"},
	30: {"v", "<", "^", ">"},
	31: {">>--->", " >>--->", "  >>--->", "   >>--->", "    >>--->", "    <---<<", "   <---<<", "  <---<<", " <---<<", "<---<<"},
	32: {"|", "||", "|||", "||||", "|||||", "|||||||", "||||||||", "|||||||", "||||||", "|||||", "||||", "|||", "||", "|"},
	33: {"[          ]", "[=         ]", "[==        ]", "[===       ]", "[====      ]", "[=====     ]", "[======    ]", "[=======   ]", "[========  ]", "[========= ]", "[==========]"},
	34: {"(*---------)", "(-*--------)", "(--*-------)", "(---*------)", "(----*-----)", "(-----*----)", "(------*---)", "(-------*--)", "(--------*-)", "(---------*)"},
	35: {"█▒▒▒▒▒▒▒▒▒", "███▒▒▒▒▒▒▒", "█████▒▒▒▒▒", "███████▒▒▒", "██████████"},
	36: {"[                    ]", "[=>                  ]", "[===>                ]", "[=====>              ]", "[======>             ]", "[========>           ]", "[==========>         ]", "[============>       ]", "[==============>     ]", "[================>   ]", "[==================> ]", "[===================>]"},
	39: {"🌍", "🌎", "🌏"},
	40: {"◜", "◝", "◞", "◟"},
	41: {"⬒", "⬔", "⬓", "⬕"},
	42: {"⬖", "⬘", "⬗", "⬙"},
}

func init() {
	for i := rune(0); i < 12; i++ {
		CharSets[37] = append(CharSets[37], string([]rune{clockOneOClock + i}))
		CharSets[38] = append(CharSets[38], string([]rune{clockOneOClock + i}), string([]rune{clockOneThirty + i}))
	}
}
<commit_msg>add portal spinner<commit_after>package spinner

const (
	clockOneOClock = '\U0001F550'
	clockOneThirty = '\U0001F55C'
)

// CharSets contains the available character sets
var CharSets = map[int][]string{
	0:  {"←", "↖", "↑", "↗", "→", "↘", "↓", "↙"},
	1:  {"▁", "▃", "▄", "▅", "▆", "▇", "█", "▇", "▆", "▅", "▄", "▃", "▁"},
	2:  {"▖", "▘", "▝", "▗"},
	3:  {"┤", "┘", "┴", "└", "├", "┌", "┬", "┐"},
	4:  {"◢", "◣", "◤", "◥"},
	5:  {"◰", "◳", "◲", "◱"},
	6:  {"◴", "◷", "◶", "◵"},
	7:  {"◐", "◓", "◑", "◒"},
	8:  {".", "o", "O", "@", "*"},
	9:  {"|", "/", "-", "\\"},
	10: {"◡◡", "⊙⊙", "◠◠"},
	11: {"⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷"},
	12: {">))'>", " >))'>", "  >))'>", "   >))'>", "    >))'>", "   <'((<", "  <'((<", " <'((<"},
	13: {"⠁", "⠂", "⠄", "⡀", "⢀", "⠠", "⠐", "⠈"},
	14: {"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"},
	15: {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"},
	16: {"▉", "▊", "▋", "▌", "▍", "▎", "▏", "▎", "▍", "▌", "▋", "▊", "▉"},
	17: {"■", "□", "▪", "▫"},
	18: {"←", "↑", "→", "↓"},
	19: {"╫", "╪"},
	20: {"⇐", "⇖", "⇑", "⇗", "⇒", "⇘", "⇓", "⇙"},
	21: {"⠁", "⠁", "⠉", "⠙", "⠚", "⠒", "⠂", "⠂", "⠒", "⠲", "⠴", "⠤", "⠄", "⠄", "⠤", "⠠", "⠠", "⠤", "⠦", "⠖", "⠒", "⠐", "⠐", "⠒", "⠓", "⠋", "⠉", "⠈", "⠈"},
	22: {"⠈", "⠉", "⠋", "⠓", "⠒", "⠐", "⠐", "⠒", "⠖", "⠦", "⠤", "⠠", "⠠", "⠤", "⠦", "⠖", "⠒", "⠐", "⠐", "⠒", "⠓", "⠋", "⠉", "⠈"},
	23: {"⠁", "⠉", "⠙", "⠚", "⠒", "⠂", "⠂", "⠒", "⠲", "⠴", "⠤", "⠄", "⠄", "⠤", "⠴", "⠲", "⠒", "⠂", "⠂", "⠒", "⠚", "⠙", "⠉", "⠁"},
	24: {"⠋", "⠙", "⠚", "⠒", "⠂", "⠂", "⠒", "⠲", "⠴", "⠦", "⠖", "⠒", "⠐", "⠐", "⠒", "⠓", "⠋"},
	25: {"ｦ", "ｧ", "ｨ", "ｩ", "ｪ", "ｫ", "ｬ", "ｭ", "ｮ", "ｯ", "ｱ", "ｲ", "ｳ", "ｴ", "ｵ", "ｶ", "ｷ", "ｸ", "ｹ", "ｺ", "ｻ", "ｼ", "ｽ", "ｾ", "ｿ", "ﾀ", "ﾁ", "ﾂ", "ﾃ", "ﾄ", "ﾅ", "ﾆ", "ﾇ", "ﾈ", "ﾉ", "ﾊ", "ﾋ", "ﾌ", "ﾍ", "ﾎ", "ﾏ", "ﾐ", "ﾑ", "ﾒ", "ﾓ", "ﾔ", "ﾕ", "ﾖ", "ﾗ", "ﾘ", "ﾙ", "ﾚ", "ﾛ", "ﾜ", "ﾝ"},
	26: {".", "..", "..."},
	27: {"▁", "▂", "▃", "▄", "▅", "▆", "▇", "█", "▉", "▊", "▋", "▌", "▍", "▎", "▏", "▏", "▎", "▍", "▌", "▋", "▊", "▉", "█", "▇", "▆", "▅", "▄", "▃", "▂", "▁"},
	28: {".", "o", "O", "°", "O", "o", "."},
	29: {"+", "x"},
	30: {"v", "<", "^", ">"},
	31: {">>--->", " >>--->", "  >>--->", "   >>--->", "    >>--->", "    <---<<", "   <---<<", "  <---<<", " <---<<", "<---<<"},
	32: {"|", "||", "|||", "||||", "|||||", "|||||||", "||||||||", "|||||||", "||||||", "|||||", "||||", "|||", "||", "|"},
	33: {"[          ]", "[=         ]", "[==        ]", "[===       ]", "[====      ]", "[=====     ]", "[======    ]", "[=======   ]", "[========  ]", "[========= ]", "[==========]"},
	34: {"(*---------)", "(-*--------)", "(--*-------)", "(---*------)", "(----*-----)", "(-----*----)", "(------*---)", "(-------*--)", "(--------*-)", "(---------*)"},
	35: {"█▒▒▒▒▒▒▒▒▒", "███▒▒▒▒▒▒▒", "█████▒▒▒▒▒", "███████▒▒▒", "██████████"},
	36: {"[                    ]", "[=>                  ]", "[===>                ]", "[=====>              ]", "[======>             ]", "[========>           ]", "[==========>         ]", "[============>       ]", "[==============>     ]", "[================>   ]", "[==================> ]", "[===================>]"},
	39: {"🌍", "🌎", "🌏"},
	40: {"◜", "◝", "◞", "◟"},
	41: {"⬒", "⬔", "⬓", "⬕"},
	42: {"⬖", "⬘", "⬗", "⬙"},
	43: {"[>>>          >]", "[]>>>>        []", "[]  >>>>      []", "[]    >>>>    []", "[]      >>>>  []", "[]        >>>>[]", "[>>          >>]"},
}

func init() {
	for i := rune(0); i < 12; i++ {
		CharSets[37] = append(CharSets[37], string([]rune{clockOneOClock + i}))
		CharSets[38] = append(CharSets[38], string([]rune{clockOneOClock + i}), string([]rune{clockOneThirty + i}))
	}
}
<|endoftext|>


<commit_before>package packet

import (
	"testing"
)

//TestBufferable implements the Bufferable interface,
//and is used to test the PeerPacket type.
type TestBufferable struct {
	GetBytesCalled bool

	Buffer []byte //buffer for testing
}

func (t *TestBufferable) GetBytes() []byte {
	t.GetBytesCalled = true

	return t.Buffer
}

func (t *TestBufferable) Equals(other []byte) bool {
	if len(t.Buffer) != len(other) {
		return false
	}
	for i := 0; i < len(t.Buffer); i++ {
		if t.Buffer[i] != other[i] {
			return false
		}
	}

	return true
}

//AggregateEquals tests to see if one mega-buffer is equal
//to the sum of the following buffers.
func AggregateEquals(megabuf []byte, bufs ...[]byte) bool {
	megabuflen := len(megabuf)

	aggregatelen := 0
	for _, buf := range bufs {
		aggregatelen = aggregatelen + len(buf)
	}
	if megabuflen != aggregatelen {
		return false
	}

	k := 0
	for i := 0; i < len(bufs); i++ {
		for j := 0; j < len(bufs[i]); j++ {
			//			fmt.Printf("megabuf[%v] = %v, bufs[%v][%v] = %v\n", k, megabuf[k], i, j, bufs[i][j])
			if megabuf[k] != bufs[i][j] {
				return false
			}
			k++
		}
	}

	return true
}

func TestCreatePacket(t *testing.T) {
	myBuffer := TestBufferable{false, nil}

	pack := new(PeerPacket)
	if _, err := pack.CreatePacket(&myBuffer); err == nil {
		t.Error("CreatePacket should return an error for a nil buffer.")
	}
	if myBuffer.GetBytesCalled == false {
		t.Error("GetBytes should have been called, but was not.")
	}
}

func TestGetBuffer(t *testing.T) {

	//test a single buffer
	myBuffer := TestBufferable{false, nil}
	myBuffer.Buffer = []byte{0, 1, 2, 3, 4}

	pack := new(PeerPacket)
	newPacket, err := pack.CreatePacket(&myBuffer)

	if err != nil {
		t.Errorf("CreatePacket returned an error: %v", err)
	}
	if myBuffer.GetBytesCalled == false {
		t.Error("GetBytes should have been called, but was not.")
	}

	buf := newPacket.GetBufferization()
	if buf == nil {
		t.Error("GetBuffer returned a nil buffer")
	}
	if !myBuffer.Equals(buf) {
		t.Errorf("Buffers are not equal. %v != %v", myBuffer.Buffer, buf)
	}

	//test two buffers
	buf1 := TestBufferable{false, nil}
	buf2 := TestBufferable{false, nil}

	buf1.Buffer = []byte{0, 1, 2, 3, 4}
	buf2.Buffer = []byte{5, 6, 7, 8, 9}

	pack = new(PeerPacket)
	newPacket, err = pack.CreatePacket(&buf1, &buf2)

	if err != nil {
		t.Errorf("CreatePacket returned an error: %v", err)
	}
	if buf1.GetBytesCalled == false {
		t.Error("GetBytes should have been called, but was not.")
	}
	if buf2.GetBytesCalled == false {
		t.Error("GetBytes should have been called, but was not.")
	}

	buf = newPacket.GetBufferization()
	if buf == nil {
		t.Error("GetBuffer returned a nil buffer")
	}

	if !AggregateEquals(buf, buf1.Buffer, buf2.Buffer) {
		t.Errorf("Aggregation of buffers is not equal: %v != %v + %v", buf, buf1.Buffer, buf2.Buffer)
	}

}

func TestBufferizableString(t *testing.T) {
	s := BufferizableString{Str: "hello there!"}
	slice := s.GetBytes()
	if slice == nil {
		t.Error("Buffer is nil")
	}
}
<commit_msg>Add unit testing for CreatePacketFromBytes.<commit_after>package packet

import (
	"testing"

	"github.com/kodykantor/p2p-gossip/id"
	"github.com/kodykantor/p2p-gossip/ttl"
)

//TestBufferable implements the Bufferable interface,
//and is used to test the PeerPacket type.
type TestBufferable struct {
	GetBytesCalled bool   //tells if the GetBytes() func was called
	Buffer         []byte //buffer for testing
}

func (t *TestBufferable) GetBytes() []byte {
	t.GetBytesCalled = true

	return t.Buffer
}

func TestCreatePacketFromBytes(t *testing.T) {
	myID := id.NewID()
	myTTL := ttl.NewTTL()
	idLen := myID.GetLengthInBytes()
	ttlLen := myTTL.GetLengthInBytes()
	bodyLen := 20

	idbuf0 := make([]byte, idLen)
	idbuf1 := make([]byte, idLen)
	ttlbuf := make([]byte, ttlLen)
	bodybuf := make([]byte, bodyLen) //20 bytes for the body

	for i := 0; i < idLen; i++ {
		idbuf0[i] = byte(i)
	}
	t.Logf("idbuf0 is %v", idbuf0)
	for i := 1; i < idLen; i++ {
		idbuf1[idLen-i] = byte(i)
	}
	t.Logf("idbuf1 is %v", idbuf1)
	for i := 0; i < ttlLen; i++ {
		ttlbuf[i] = byte(i)
	}
	t.Logf("ttlbuf is %v", ttlbuf)
	for i := 0; i < bodyLen; i++ {
		bodybuf[i] = byte(i)
	}
	t.Logf("bodybuf is %v", bodybuf)

	//make a mega buffer (a packet)
	x := append(idbuf0, idbuf1...)
	x = append(x, ttlbuf...)
	x = append(x, bodybuf...)

	testPacket := new(PeerPacket)
	pack, err := testPacket.CreatePacketFromBytes(x)
	if err != nil {
		t.Errorf("Error creating packet from megabuffer: %v", err)
	}

	id0 := pack.(*PeerPacket).ID0
	id1 := pack.(*PeerPacket).ID1
	ttl := pack.(*PeerPacket).TTL
	body := pack.(*PeerPacket).Body

	if id0 == nil || id1 == nil || ttl == nil {
		t.Error("IDs or TTL is nil")
	}

	slice := id0.GetBytes()
	for ind, val := range slice {
		if val != idbuf0[ind] {
			t.Errorf("Expected %v, got %v", idbuf0[ind], val)
		}
	}

	slice = id1.GetBytes()
	for ind, val := range slice {
		if val != idbuf1[ind] {
			t.Errorf("Expected %v, got %v", idbuf1[ind], val)
		}
	}

	//Since ttls are an int represented by a byte slice,
	// we have to massage the data we sent in a little bit.
	slice = ttl.GetBytes()
	newTTL, err := myTTL.CreateFromBytes(ttlbuf)
	if err != nil {
		t.Errorf("Error creating ttl from bytes. Check the ttl functions. %v", err)
	}
	ttlBufAsBytes := newTTL.GetBytes()

	t.Logf("Received %v", slice)
	t.Logf("Comparing to %v", ttlbuf)
	for ind, val := range slice {
		if val != ttlBufAsBytes[ind] {
			t.Errorf("Expected %v, got %v", ttlBufAsBytes[ind], val)
		}
	}

	t.Logf("Received %v", body)
	t.Logf("Comparing to %v", bodybuf)
	for ind, val := range body {
		if val != bodybuf[ind] {
			t.Errorf("Expected %v, got %v", bodybuf[ind], val)
		}
	}

	_, err = testPacket.CreatePacketFromBytes(nil)
	if err == nil {
		t.Error("CreatePacketFromBytes should return an error for nil slice")
	}

}

func (t *TestBufferable) Equals(other []byte) bool {
	if len(t.Buffer) != len(other) {
		return false
	}
	for i := 0; i < len(t.Buffer); i++ {
		if t.Buffer[i] != other[i] {
			return false
		}
	}

	return true
}

//AggregateEquals tests to see if one mega-buffer is equal
//to the sum of the following buffers.
func AggregateEquals(megabuf []byte, bufs ...[]byte) bool {
	megabuflen := len(megabuf)

	aggregatelen := 0
	for _, buf := range bufs {
		aggregatelen = aggregatelen + len(buf)
	}
	if megabuflen != aggregatelen {
		return false
	}

	k := 0
	for i := 0; i < len(bufs); i++ {
		for j := 0; j < len(bufs[i]); j++ {
			//			fmt.Printf("megabuf[%v] = %v, bufs[%v][%v] = %v\n", k, megabuf[k], i, j, bufs[i][j])
			if megabuf[k] != bufs[i][j] {
				return false
			}
			k++
		}
	}

	return true
}

func TestCreatePacket(t *testing.T) {
	myBuffer := TestBufferable{false, nil}

	pack := new(PeerPacket)
	if _, err := pack.CreatePacket(&myBuffer); err == nil {
		t.Error("CreatePacket should return an error for a nil buffer.")
	}
	if myBuffer.GetBytesCalled == false {
		t.Error("GetBytes should have been called, but was not.")
	}
}

func TestGetBuffer(t *testing.T) {

	//test a single buffer
	myBuffer := TestBufferable{false, nil}
	myBuffer.Buffer = []byte{0, 1, 2, 3, 4}

	pack := new(PeerPacket)
	newPacket, err := pack.CreatePacket(&myBuffer)

	if err != nil {
		t.Errorf("CreatePacket returned an error: %v", err)
	}
	if myBuffer.GetBytesCalled == false {
		t.Error("GetBytes should have been called, but was not.")
	}

	buf := newPacket.GetBufferization()
	if buf == nil {
		t.Error("GetBuffer returned a nil buffer")
	}
	if !myBuffer.Equals(buf) {
		t.Errorf("Buffers are not equal. %v != %v", myBuffer.Buffer, buf)
	}

	//test two buffers
	buf1 := TestBufferable{false, nil}
	buf2 := TestBufferable{false, nil}

	buf1.Buffer = []byte{0, 1, 2, 3, 4}
	buf2.Buffer = []byte{5, 6, 7, 8, 9}

	pack = new(PeerPacket)
	newPacket, err = pack.CreatePacket(&buf1, &buf2)

	if err != nil {
		t.Errorf("CreatePacket returned an error: %v", err)
	}
	if buf1.GetBytesCalled == false {
		t.Error("GetBytes should have been called, but was not.")
	}
	if buf2.GetBytesCalled == false {
		t.Error("GetBytes should have been called, but was not.")
	}

	buf = newPacket.GetBufferization()
	if buf == nil {
		t.Error("GetBuffer returned a nil buffer")
	}

	if !AggregateEquals(buf, buf1.Buffer, buf2.Buffer) {
		t.Errorf("Aggregation of buffers is not equal: %v != %v + %v", buf, buf1.Buffer, buf2.Buffer)
	}

}

func TestBufferizableString(t *testing.T) {
	s := BufferizableString{Str: "hello there!"}
	slice := s.GetBytes()
	if slice == nil {
		t.Error("Buffer is nil")
	}
}
<|endoftext|>


<commit_before>/* https://leetcode.com/problems/construct-string-from-binary-tree/#/description
You need to construct a string consists of parenthesis and integers from a binary tree with the preorder traversing way.

The null node needs to be represented by empty parenthesis pair "()".
And you need to omit all the empty parenthesis pairs that don't affect the one-to-one mapping relationship between the string and the original binary tree.

Example 1:
    Input: Binary tree: [1,2,3,4]
           1
         /   \
        2     3
       /
      4

    Output: "1(2(4))(3)"

    Explanation: Originallay it needs to be "1(2(4)())(3()())",
    but you need to omit all the unnecessary empty parenthesis pairs.
    And it will be "1(2(4))(3)".

Example 2:
    Input: Binary tree: [1,2,3,null,4]
           1
         /   \
        2     3
         \
          4

    Output: "1(2()(4))(3)"

    Explanation: Almost the same as the first example,
    except we can't omit the first parenthesis pair to break the one-to-one mapping relationship between the input and the output.
*/

package leetcode

import (
	"bytes"
	"strconv"
)

func tree2str(t *TreeNode) string {
	buffer := new(bytes.Buffer)

	var helper func(node *TreeNode)
	helper = func(node *TreeNode) {
		if node == nil {
			return
		}

		buffer.WriteString(strconv.Itoa(node.Val))
		if node.Left == nil && node.Right == nil {
			return
		}

		buffer.WriteByte('(')
		helper(node.Left)
		buffer.WriteByte(')')

		if node.Right != nil {
			buffer.WriteByte('(')
			helper(node.Right)
			buffer.WriteByte(')')
		}
	}

	helper(t)

	return buffer.String()
}

/*
import "fmt"

func tree2str(t *TreeNode) string {
	if t == nil {
		return ""
	}

	if t.Left == nil && t.Right == nil {
		return fmt.Sprintf("%d", t.Val)
	}

	left, right := "", ""
	if t.Left != nil {
		left = fmt.Sprintf("(%s)", tree2str(t.Left))
	}
	if t.Right != nil {
		right = fmt.Sprintf("(%s)", tree2str(t.Right))
		if left == "" {
			left = "()"
		}
	}

	return fmt.Sprintf("%d%s%s", t.Val, left, right)
}
*/
<commit_msg>enhance code<commit_after>/* https://leetcode.com/problems/construct-string-from-binary-tree/#/description
You need to construct a string consists of parenthesis and integers from a binary tree with the preorder traversing way.

The null node needs to be represented by empty parenthesis pair "()".
And you need to omit all the empty parenthesis pairs that don't affect the one-to-one mapping relationship between the string and the original binary tree.

Example 1:
    Input: Binary tree: [1,2,3,4]
           1
         /   \
        2     3
       /
      4

    Output: "1(2(4))(3)"

    Explanation: Originallay it needs to be "1(2(4)())(3()())",
    but you need to omit all the unnecessary empty parenthesis pairs.
    And it will be "1(2(4))(3)".

Example 2:
    Input: Binary tree: [1,2,3,null,4]
           1
         /   \
        2     3
         \
          4

    Output: "1(2()(4))(3)"

    Explanation: Almost the same as the first example,
    except we can't omit the first parenthesis pair to break the one-to-one mapping relationship between the input and the output.
*/

package leetcode

import (
	"bytes"
	"strconv"
)

func tree2str(t *TreeNode) string {
	buffer := new(bytes.Buffer)

	var helper func(node *TreeNode)
	helper = func(node *TreeNode) {
		if node == nil {
			return
		}

		buffer.WriteString(strconv.Itoa(node.Val))
		if node.Left == nil && node.Right == nil {
			return
		}

		buffer.WriteByte('(')
		helper(node.Left)
		buffer.WriteByte(')')

		if node.Right != nil {
			buffer.WriteByte('(')
			helper(node.Right)
			buffer.WriteByte(')')
		}
	}

	helper(t)

	return buffer.String()
}

/*
import "fmt"

func tree2str(t *TreeNode) string {
	if t == nil {
		return ""
	}

	if t.Left == nil && t.Right == nil {
		return fmt.Sprintf("%d", t.Val)
	}

	left, right := "", ""
	left = fmt.Sprintf("(%s)", tree2str(t.Left))

	if t.Right != nil {
		right = fmt.Sprintf("(%s)", tree2str(t.Right))
	}

	return fmt.Sprintf("%d%s%s", t.Val, left, right)
}
*/
<|endoftext|>


<commit_before>package twitter

import (
	"net/http"
	"time"

	"github.com/dghubble/sling"
)

// DirectMessageEvent is a list direct message event
type DirectMessageEvent struct {
	NextCursor string         `json:"next_cursor"`
	Events     []MessageEvent `json:"events"`
	Apps       string         `json:"apps"`
}

// MessageEvent is a signle direct message sent or received
type MessageEvent struct {
	Type      string   `json:"type"`
	ID        int64    `json:"id"`
	CreatedAt string   `json:"created_timestamp"`
	Message   *Message `json:"message_create"`
}

// Message contains the Sender data as well as the Message contents
type Message struct {
	SenderID string       `json:"sender_id"`
	Target   *Target      `json:"target"`
	Data     *MessageData `json:"message_data"`
}

// Target recipient information
type Target struct {
	RecipientID string `json:"recipient_id"`
}

// MessageData contains the raw text of the message sent or received
type MessageData struct {
	Text     string    `json:"text"`
	Entities *Entities `json:"entitites"`
}

// DirectMessageEventsGetParams are the parameters for DirectMessageEvents.Get
type DirectMessageEventsGetParams struct {
	NextCursor string `url:"cursor,omitempty"`
	Count      int    `url:"count,omitempty"`
}

// DirectMessage is a direct message to a single recipient.
type DirectMessage struct {
	CreatedAt           string    `json:"created_at"`
	Entities            *Entities `json:"entities"`
	ID                  int64     `json:"id"`
	IDStr               string    `json:"id_str"`
	Recipient           *User     `json:"recipient"`
	RecipientID         int64     `json:"recipient_id"`
	RecipientScreenName string    `json:"recipient_screen_name"`
	Sender              *User     `json:"sender"`
	SenderID            int64     `json:"sender_id"`
	SenderScreenName    string    `json:"sender_screen_name"`
	Text                string    `json:"text"`
}

// CreatedAtTime returns the time a Direct Message was created.
func (d DirectMessage) CreatedAtTime() (time.Time, error) {
	return time.Parse(time.RubyDate, d.CreatedAt)
}

// DirectMessageService provides methods for accessing Twitter direct message
// API endpoints.
type DirectMessageService struct {
	baseSling *sling.Sling
	sling     *sling.Sling
}

// newDirectMessageService returns a new DirectMessageService.
func newDirectMessageService(sling *sling.Sling) *DirectMessageService {
	return &DirectMessageService{
		baseSling: sling.New(),
		sling:     sling.Path("direct_messages/"),
	}
}

// directMessageShowParams are the parameters for DirectMessageService.Show
type directMessageShowParams struct {
	ID int64 `url:"id,omitempty"`
}

// Show returns the requested Direct Message.
// Requires a user auth context with DM scope.
// https://dev.twitter.com/rest/reference/get/direct_messages/show
func (s *DirectMessageService) Show(id int64) (*DirectMessage, *http.Response, error) {
	params := &directMessageShowParams{ID: id}
	dm := new(DirectMessage)
	apiError := new(APIError)
	resp, err := s.sling.New().Get("show.json").QueryStruct(params).Receive(dm, apiError)
	return dm, resp, relevantError(err, *apiError)
}

// DirectMessageGetParams are the parameters for DirectMessageService.Get
type DirectMessageGetParams struct {
	SinceID         int64 `url:"since_id,omitempty"`
	MaxID           int64 `url:"max_id,omitempty"`
	Count           int   `url:"count,omitempty"`
	IncludeEntities *bool `url:"include_entities,omitempty"`
	SkipStatus      *bool `url:"skip_status,omitempty"`
}

// Get returns recent Direct Messages received by the authenticated user.
// Requires a user auth context with DM scope.
// https://dev.twitter.com/rest/reference/get/direct_messages
func (s *DirectMessageService) Get(params *DirectMessageGetParams) ([]DirectMessage, *http.Response, error) {
	dms := new([]DirectMessage)
	apiError := new(APIError)
	resp, err := s.baseSling.New().Get("direct_messages.json").QueryStruct(params).Receive(dms, apiError)
	return *dms, resp, relevantError(err, *apiError)
}

// GetEvents returns recent Direct Message Events received or sent by the authenticated user.
// Requires a user auth context with DM scope.
// https://developer.twitter.com/en/docs/direct-messages/sending-and-receiving/api-reference/list-events
func (s *DirectMessageService) GetEvents(params *DirectMessageEventsGetParams) (DirectMessageEvent, *http.Response, error) {
	event := new(DirectMessageEvent)
	apiError := new(APIError)
	resp, err := s.sling.New().Get("events/list.json").QueryStruct(params).Receive(event, apiError)
	return *event, resp, relevantError(err, *apiError)
}

// DirectMessageSentParams are the parameters for DirectMessageService.Sent
type DirectMessageSentParams struct {
	SinceID         int64 `url:"since_id,omitempty"`
	MaxID           int64 `url:"max_id,omitempty"`
	Count           int   `url:"count,omitempty"`
	Page            int   `url:"page,omitempty"`
	IncludeEntities *bool `url:"include_entities,omitempty"`
}

// Sent returns recent Direct Messages sent by the authenticated user.
// Requires a user auth context with DM scope.
// https://dev.twitter.com/rest/reference/get/direct_messages/sent
func (s *DirectMessageService) Sent(params *DirectMessageSentParams) ([]DirectMessage, *http.Response, error) {
	dms := new([]DirectMessage)
	apiError := new(APIError)
	resp, err := s.sling.New().Get("sent.json").QueryStruct(params).Receive(dms, apiError)
	return *dms, resp, relevantError(err, *apiError)
}

// DirectMessageNewParams are the parameters for DirectMessageService.New
type DirectMessageNewParams struct {
	UserID     int64  `url:"user_id,omitempty"`
	ScreenName string `url:"screen_name,omitempty"`
	Text       string `url:"text"`
}

// New sends a new Direct Message to a specified user as the authenticated
// user.
// Requires a user auth context with DM scope.
// https://dev.twitter.com/rest/reference/post/direct_messages/new
func (s *DirectMessageService) New(params *DirectMessageNewParams) (*DirectMessage, *http.Response, error) {
	dm := new(DirectMessage)
	apiError := new(APIError)
	resp, err := s.sling.New().Post("new.json").BodyForm(params).Receive(dm, apiError)
	return dm, resp, relevantError(err, *apiError)
}

// DirectMessageDestroyParams are the parameters for DirectMessageService.Destroy
type DirectMessageDestroyParams struct {
	ID              int64 `url:"id,omitempty"`
	IncludeEntities *bool `url:"include_entities,omitempty"`
}

// Destroy deletes the Direct Message with the given id and returns it if
// successful.
// Requires a user auth context with DM scope.
// https://dev.twitter.com/rest/reference/post/direct_messages/destroy
func (s *DirectMessageService) Destroy(id int64, params *DirectMessageDestroyParams) (*DirectMessage, *http.Response, error) {
	if params == nil {
		params = &DirectMessageDestroyParams{}
	}
	params.ID = id
	dm := new(DirectMessage)
	apiError := new(APIError)
	resp, err := s.sling.New().Post("destroy.json").BodyForm(params).Receive(dm, apiError)
	return dm, resp, relevantError(err, *apiError)
}
<commit_msg>Refactor DirectMessage event structs and markers<commit_after>package twitter

import (
	"net/http"
	"time"

	"github.com/dghubble/sling"
)

// DirectMessageEvents is a list direct message event
type DirectMessageEvents struct {
	NextCursor string               `json:"next_cursor"`
	Events     []DirectMessageEvent `json:"events"`
	Apps       string               `json:"apps"`
}

// DirectMessageEvent is a signle direct message sent or received
type DirectMessageEvent struct {
	Type      string                     `json:"type"`
	ID        int64                      `json:"id,string"`
	CreatedAt string                     `json:"created_timestamp"`
	Message   *DirectMessageEventMessage `json:"message_create"`
}

// DirectMessageEventMessage contains the Sender data as well as the Message contents
type DirectMessageEventMessage struct {
	SenderID int64 `json:"sender_id,string"`
	Target   struct {
		RecipientID int64 `json:"recipient_id,string"`
	} `json:"target"`
	Data *DirectMessageEventMessageData `json:"message_data"`
}

// DirectMessageEventMessageData contains the raw text of the message sent or received
type DirectMessageEventMessageData struct {
	Text     string    `json:"text"`
	Entities *Entities `json:"entitites"`
}

// DirectMessageEventsGetParams are the parameters for DirectMessageEvents.Get
type DirectMessageEventsGetParams struct {
	NextCursor string `url:"cursor,omitempty"`
	Count      int    `url:"count,omitempty"`
}

// DirectMessage is a direct message to a single recipient.
type DirectMessage struct {
	CreatedAt           string    `json:"created_at"`
	Entities            *Entities `json:"entities"`
	ID                  int64     `json:"id"`
	IDStr               string    `json:"id_str"`
	Recipient           *User     `json:"recipient"`
	RecipientID         int64     `json:"recipient_id"`
	RecipientScreenName string    `json:"recipient_screen_name"`
	Sender              *User     `json:"sender"`
	SenderID            int64     `json:"sender_id"`
	SenderScreenName    string    `json:"sender_screen_name"`
	Text                string    `json:"text"`
}

// CreatedAtTime returns the time a Direct Message was created.
func (d DirectMessage) CreatedAtTime() (time.Time, error) {
	return time.Parse(time.RubyDate, d.CreatedAt)
}

// DirectMessageService provides methods for accessing Twitter direct message
// API endpoints.
type DirectMessageService struct {
	baseSling *sling.Sling
	sling     *sling.Sling
}

// newDirectMessageService returns a new DirectMessageService.
func newDirectMessageService(sling *sling.Sling) *DirectMessageService {
	return &DirectMessageService{
		baseSling: sling.New(),
		sling:     sling.Path("direct_messages/"),
	}
}

// directMessageShowParams are the parameters for DirectMessageService.Show
type directMessageShowParams struct {
	ID int64 `url:"id,omitempty"`
}

// Show returns the requested Direct Message.
// Requires a user auth context with DM scope.
// https://dev.twitter.com/rest/reference/get/direct_messages/show
func (s *DirectMessageService) Show(id int64) (*DirectMessage, *http.Response, error) {
	params := &directMessageShowParams{ID: id}
	dm := new(DirectMessage)
	apiError := new(APIError)
	resp, err := s.sling.New().Get("show.json").QueryStruct(params).Receive(dm, apiError)
	return dm, resp, relevantError(err, *apiError)
}

// DirectMessageGetParams are the parameters for DirectMessageService.Get
type DirectMessageGetParams struct {
	SinceID         int64 `url:"since_id,omitempty"`
	MaxID           int64 `url:"max_id,omitempty"`
	Count           int   `url:"count,omitempty"`
	IncludeEntities *bool `url:"include_entities,omitempty"`
	SkipStatus      *bool `url:"skip_status,omitempty"`
}

// Get returns recent Direct Messages received by the authenticated user.
// Requires a user auth context with DM scope.
// https://dev.twitter.com/rest/reference/get/direct_messages
func (s *DirectMessageService) Get(params *DirectMessageGetParams) ([]DirectMessage, *http.Response, error) {
	dms := new([]DirectMessage)
	apiError := new(APIError)
	resp, err := s.baseSling.New().Get("direct_messages.json").QueryStruct(params).Receive(dms, apiError)
	return *dms, resp, relevantError(err, *apiError)
}

// GetEvents returns recent Direct Message Events received or sent by the authenticated user.
// Requires a user auth context with DM scope.
// https://developer.twitter.com/en/docs/direct-messages/sending-and-receiving/api-reference/list-events
func (s *DirectMessageService) GetEvents(params *DirectMessageEventsGetParams) (DirectMessageEvents, *http.Response, error) {
	event := new(DirectMessageEvents)
	apiError := new(APIError)
	resp, err := s.sling.New().Get("events/list.json").QueryStruct(params).Receive(event, apiError)
	return *event, resp, relevantError(err, *apiError)
}

// DirectMessageSentParams are the parameters for DirectMessageService.Sent
type DirectMessageSentParams struct {
	SinceID         int64 `url:"since_id,omitempty"`
	MaxID           int64 `url:"max_id,omitempty"`
	Count           int   `url:"count,omitempty"`
	Page            int   `url:"page,omitempty"`
	IncludeEntities *bool `url:"include_entities,omitempty"`
}

// Sent returns recent Direct Messages sent by the authenticated user.
// Requires a user auth context with DM scope.
// https://dev.twitter.com/rest/reference/get/direct_messages/sent
func (s *DirectMessageService) Sent(params *DirectMessageSentParams) ([]DirectMessage, *http.Response, error) {
	dms := new([]DirectMessage)
	apiError := new(APIError)
	resp, err := s.sling.New().Get("sent.json").QueryStruct(params).Receive(dms, apiError)
	return *dms, resp, relevantError(err, *apiError)
}

// DirectMessageNewParams are the parameters for DirectMessageService.New
type DirectMessageNewParams struct {
	UserID     int64  `url:"user_id,omitempty"`
	ScreenName string `url:"screen_name,omitempty"`
	Text       string `url:"text"`
}

// New sends a new Direct Message to a specified user as the authenticated
// user.
// Requires a user auth context with DM scope.
// https://dev.twitter.com/rest/reference/post/direct_messages/new
func (s *DirectMessageService) New(params *DirectMessageNewParams) (*DirectMessage, *http.Response, error) {
	dm := new(DirectMessage)
	apiError := new(APIError)
	resp, err := s.sling.New().Post("new.json").BodyForm(params).Receive(dm, apiError)
	return dm, resp, relevantError(err, *apiError)
}

// DirectMessageDestroyParams are the parameters for DirectMessageService.Destroy
type DirectMessageDestroyParams struct {
	ID              int64 `url:"id,omitempty"`
	IncludeEntities *bool `url:"include_entities,omitempty"`
}

// Destroy deletes the Direct Message with the given id and returns it if
// successful.
// Requires a user auth context with DM scope.
// https://dev.twitter.com/rest/reference/post/direct_messages/destroy
func (s *DirectMessageService) Destroy(id int64, params *DirectMessageDestroyParams) (*DirectMessage, *http.Response, error) {
	if params == nil {
		params = &DirectMessageDestroyParams{}
	}
	params.ID = id
	dm := new(DirectMessage)
	apiError := new(APIError)
	resp, err := s.sling.New().Post("destroy.json").BodyForm(params).Receive(dm, apiError)
	return dm, resp, relevantError(err, *apiError)
}
<|endoftext|>


<commit_before>package stackengine

import (
	"crypto/tls"
	"crypto/x509"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"path"
	"strings"
)

// Client is an empty wrapper around the StackEngine client
type Client struct {
	client    *http.Client
	token     string
	base      string
	transport *http.Transport
}

// NewStackEngineClient returns a client object with connection information.
func NewStackEngineClient(nodes []string, scheme, cert, key, caCert string, authToken string) (*Client, error) {
	var (
		err  error
		host string
	)

	if len(nodes) > 0 {
		host = nodes[0]
	} else {
		host = "127.0.0.1:8443"
	}

	base := scheme + "://" + host

	tlsConfig := &tls.Config{}
	if cert != "" && key != "" {
		clientCert, err := tls.LoadX509KeyPair(cert, key)
		if err != nil {
			return nil, err
		}
		tlsConfig.Certificates = []tls.Certificate{clientCert}
		tlsConfig.BuildNameToCertificate()
	}
	if caCert != "" {
		ca, err := ioutil.ReadFile(caCert)
		if err != nil {
			return nil, err
		}
		caCertPool := x509.NewCertPool()
		caCertPool.AppendCertsFromPEM(ca)
		tlsConfig.RootCAs = caCertPool
	}
	tlsConfig.InsecureSkipVerify = true
	transport := &http.Transport{
		TLSClientConfig: tlsConfig,
	}

	client := &http.Client{Transport: transport}

	return &Client{client, authToken, base, transport}, err
}

// KVPair is used to represent a single K/V entry
type KVPair struct {
	Key         string
	CreateIndex uint64
	ModifyIndex uint64
	LockIndex   uint64
	Flags       uint64
	Value       []byte
	Session     string
}

// GetValues queries redis for keys prefixed by prefix.
func (c *Client) GetValues(keys []string) (map[string]string, error) {
	vars := make(map[string]string)
	var pairs []KVPair

	for _, key := range keys {
		key := strings.TrimPrefix(key, "/")

		uri := c.base + "/v1/kv/" + key + "?recurse"
		req, err := http.NewRequest("GET", uri, nil)

		bearer := "Bearer " + c.token

		fmt.Println("URI: ", uri, bearer)

		req.Header.Add("Authorization", bearer)
		parseFormErr := req.ParseForm()
		if parseFormErr != nil {
			fmt.Println(parseFormErr)
		}

		// Fetch Request
		resp, err := c.client.Do(req)

		if err != nil {
			fmt.Println("Failure : ", err)
			return nil, err
		}

		// Read Response Body
		respBody, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			fmt.Println("Error reading http response: ", err)
		}

		err = json.Unmarshal(respBody, &pairs)

		if err != nil {
			return vars, err
		}
		for _, p := range pairs {
			vars[path.Join("/", p.Key)] = string(p.Value)
		}
	}

	fmt.Println(vars)

	return vars, nil
}

type watchResponse struct {
	waitIndex uint64
	err       error
}

func (c *Client) WatchPrefix(prefix string, waitIndex uint64, stopChan chan bool) (uint64, error) {
	<-stopChan
	return 0, nil
}
<commit_msg>More debug cleanup<commit_after>package stackengine

import (
	"crypto/tls"
	"crypto/x509"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"path"
	"strings"
)

// Client is an empty wrapper around the StackEngine client
type Client struct {
	client    *http.Client
	token     string
	base      string
	transport *http.Transport
}

// NewStackEngineClient returns a client object with connection information.
func NewStackEngineClient(nodes []string, scheme, cert, key, caCert string, authToken string) (*Client, error) {
	var (
		err  error
		host string
	)

	if len(nodes) > 0 {
		host = nodes[0]
	} else {
		host = "127.0.0.1:8443"
	}

	base := scheme + "://" + host

	tlsConfig := &tls.Config{}
	if cert != "" && key != "" {
		clientCert, err := tls.LoadX509KeyPair(cert, key)
		if err != nil {
			return nil, err
		}
		tlsConfig.Certificates = []tls.Certificate{clientCert}
		tlsConfig.BuildNameToCertificate()
	}
	if caCert != "" {
		ca, err := ioutil.ReadFile(caCert)
		if err != nil {
			return nil, err
		}
		caCertPool := x509.NewCertPool()
		caCertPool.AppendCertsFromPEM(ca)
		tlsConfig.RootCAs = caCertPool
	}
	tlsConfig.InsecureSkipVerify = true
	transport := &http.Transport{
		TLSClientConfig: tlsConfig,
	}

	client := &http.Client{Transport: transport}

	return &Client{client, authToken, base, transport}, err
}

// KVPair is used to represent a single K/V entry
type KVPair struct {
	Key         string
	CreateIndex uint64
	ModifyIndex uint64
	LockIndex   uint64
	Flags       uint64
	Value       []byte
	Session     string
}

// GetValues queries redis for keys prefixed by prefix.
func (c *Client) GetValues(keys []string) (map[string]string, error) {
	vars := make(map[string]string)
	var pairs []KVPair

	for _, key := range keys {
		key := strings.TrimPrefix(key, "/")

		uri := c.base + "/v1/kv/" + key + "?recurse"
		req, err := http.NewRequest("GET", uri, nil)

		bearer := "Bearer " + c.token

		req.Header.Add("Authorization", bearer)
		parseFormErr := req.ParseForm()
		if parseFormErr != nil {
			fmt.Println(parseFormErr)
		}

		// Fetch Request
		resp, err := c.client.Do(req)

		if err != nil {
			fmt.Println("Failure : ", err)
			return nil, err
		}

		// Read Response Body
		respBody, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			fmt.Println("Error reading http response: ", err)
		}

		err = json.Unmarshal(respBody, &pairs)

		if err != nil {
			return vars, err
		}
		for _, p := range pairs {
			vars[path.Join("/", p.Key)] = string(p.Value)
		}
	}

	return vars, nil
}

type watchResponse struct {
	waitIndex uint64
	err       error
}

func (c *Client) WatchPrefix(prefix string, waitIndex uint64, stopChan chan bool) (uint64, error) {
	<-stopChan
	return 0, nil
}
<|endoftext|>


<commit_before>// Copyright 2017, Square, Inc.

// Package config handles config files, -config, and env vars at startup.
package config

import (
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/user"
	"path/filepath"
	"strings"

	"github.com/alexflint/go-arg"
	"github.com/square/spincycle/proto"
	"github.com/square/spincycle/request-manager"
	"gopkg.in/yaml.v2"
)

const (
	DEFAULT_CONFIG_FILES = "/etc/spinc/spinc.yaml,~/.spinc.yaml"
	DEFAULT_TIMEOUT      = 10000 // 10s
)

var (
	ErrUnknownRequest = errors.New("request does not exist")
)

// Options represents typical command line options: --addr, --config, etc.
type Options struct {
	Addr    string `arg:"env" yaml:"addr"`
	Config  string `arg:"env"`
	Debug   bool
	Env     string
	Help    bool
	Ping    bool
	Timeout uint `arg:"env" yaml:"timeout"`
	Version bool
	Verbose bool
}

// Command represents a command (start, stop, etc.) and its values.
type Command struct {
	Cmd  string   `arg:"positional"`
	Args []string `arg:"positional"`
}

// CommandLine represents options (--addr, etc.) and commands (start, etc.).
// The caller is expected to copy and use the embedded structs separately, like:
//
//   var o config.Options = cmdLine.Options
//   var c config.Command = cmdLine.Command
//
// Some commands and options are mutually exclusive, like --ping and --version.
// Others can be used together, like --addr and --timeout with any command.
type CommandLine struct {
	Options
	Command
}

// ParseCommandLine parses the command line and env vars. Command line options
// override env vars. Default options are used unless overridden by env vars or
// command line options. Defaults are usually parsed from config files.
func ParseCommandLine(def Options) CommandLine {
	var c CommandLine
	c.Options = def
	p, err := arg.NewParser(arg.Config{Program: "spinc"}, &c)
	if err != nil {
		fmt.Printf("arg.NewParser: %s", err)
		os.Exit(1)
	}
	if err := p.Parse(os.Args[1:]); err != nil {
		switch err {
		case arg.ErrHelp:
			c.Help = true
		case arg.ErrVersion:
			c.Version = true
		default:
			fmt.Printf("Error parsing command line: %s\n", err)
			os.Exit(1)
		}
	}
	return c
}

// Help prints full help or minimal request help if full is false. An rm.Client
// is used to get the list of available requests from the Request Manager API.
func Help(full bool, rmc rm.Client) {
	if full {
		fmt.Printf("Usage: spinc [flags] <command> [request|id] [args|file]\n\n"+
			"Flags:\n"+
			"  --addr     Address of Request Managers (https://local.domain:8080)\n"+
			"  --config   Config files (default: %s)\n"+
			"  --debug    Print debug to stderr\n"+
			"  --env      Environment (dev, staging, production)\n"+
			"  --help     Print help\n"+
			"  --ping     Ping addr\n"+
			"  --timeout  API timeout, milliseconds (default: %d)\n"+
			"  --version  Print version\n\n"+
			"  -v         verbose ps or status\n\n"+
			"Commands:\n"+
			"  help    <request>  Print request help\n"+
			"  start   <request>  Start new request\n"+
			"  stop    <ID>       Stop request\n"+
			"  status  <ID>       Print status of request\n"+
			"  running <ID>       Exit 0 if request is running, else exit 1\n"+
			"  ps                 Show all running requests and jobs\n\n",
			DEFAULT_CONFIG_FILES, DEFAULT_TIMEOUT)
		printRequestList(rmc)
		fmt.Println("\n'spinc help <request>' for request help")
	} else {
		fmt.Printf("spinc help  <request>\nspinc start <request>\n\n")
		printRequestList(rmc)
		fmt.Println("\n'spinc help' for more")
	}
}

func printRequestList(rmc rm.Client) {
	if rmc == nil {
		fmt.Println("Specify --addr or ADDR environment variable to list all requests")
		return
	}

	fmt.Println("Requests:")
	req, err := rmc.RequestList()
	if err != nil {
		fmt.Println(err)
	} else {
		for _, r := range req {
			fmt.Println("  " + r.Name)
		}
	}
}

func RequestHelp(reqName string, rmc rm.Client) error {
	reqList, err := rmc.RequestList()
	if err != nil {
		return err
	}

	var req *proto.RequestSpec
	for _, r := range reqList {
		if r.Name != reqName {
			continue
		}
		req = &r
		break
	}

	if req == nil {
		return ErrUnknownRequest
	}

	fmt.Printf("%s args (* required)\n\n", req.Name)
	l := 0
	for _, a := range req.Args {
		if len(a.Name) > l {
			l = len(a.Name)
		}
	}
	line := fmt.Sprintf("  %%s %%-%ds  %%s\n", l)
	for _, a := range req.Args {
		help := a.Desc
		star := " "
		if a.Required {
			star = "*"
		} else {
			help += " (default: " + a.Default + ")"
		}
		fmt.Printf(line, star, a.Name, help)
	}
	fmt.Println("\nspinc start shutdown-host # prompt for args\n" +
		"spinc start shutdown-host <args>\n" +
		"spinc start shutdown-host <args-file.yaml>")
	return nil
}

func ParseConfigFiles(files string, debug bool) Options {
	var def Options
	for _, file := range strings.Split(files, ",") {
		// If file starts with ~/, we need to expand this to the user home dir
		// because this is a shell expansion, not something Go knows about.
		if file[:2] == "~/" {
			usr, _ := user.Current()
			file = filepath.Join(usr.HomeDir, file[2:])
		}

		absfile, err := filepath.Abs(file)
		if err != nil {
			if debug {
				log.Printf("filepath.Abs(%s) error: %s", file, err)
			}
			continue
		}

		bytes, err := ioutil.ReadFile(absfile)
		if err != nil {
			if debug {
				log.Printf("Cannot read config file %s: %s", file, err)
			}
			continue
		}

		var o Options
		if err := yaml.Unmarshal(bytes, &o); err != nil {
			if debug {
				log.Printf("Invalid YAML in config file %s: %s", file, err)
			}
			continue
		}

		// Set options from this config file only if they're set
		if debug {
			log.Printf("Applying config file %s (%s)", file, absfile)
		}
		if o.Addr != "" {
			def.Addr = o.Addr
		}
		if o.Timeout != 0 {
			def.Timeout = o.Timeout
		}
	}
	return def
}
<commit_msg>verbosity<commit_after>// Copyright 2017, Square, Inc.

// Package config handles config files, -config, and env vars at startup.
package config

import (
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/user"
	"path/filepath"
	"strings"

	"github.com/alexflint/go-arg"
	"github.com/square/spincycle/proto"
	"github.com/square/spincycle/request-manager"
	"gopkg.in/yaml.v2"
)

const (
	DEFAULT_CONFIG_FILES = "/etc/spinc/spinc.yaml,~/.spinc.yaml"
	DEFAULT_TIMEOUT      = 10000 // 10s
)

var (
	ErrUnknownRequest = errors.New("request does not exist")
)

// Options represents typical command line options: --addr, --config, etc.
type Options struct {
	Addr    string `arg:"env" yaml:"addr"`
	Config  string `arg:"env"`
	Debug   bool
	Env     string
	Help    bool
	Ping    bool
	Timeout uint `arg:"env" yaml:"timeout"`
	Version bool
	Verbose bool `arg:"-v"`
}

// Command represents a command (start, stop, etc.) and its values.
type Command struct {
	Cmd  string   `arg:"positional"`
	Args []string `arg:"positional"`
}

// CommandLine represents options (--addr, etc.) and commands (start, etc.).
// The caller is expected to copy and use the embedded structs separately, like:
//
//   var o config.Options = cmdLine.Options
//   var c config.Command = cmdLine.Command
//
// Some commands and options are mutually exclusive, like --ping and --version.
// Others can be used together, like --addr and --timeout with any command.
type CommandLine struct {
	Options
	Command
}

// ParseCommandLine parses the command line and env vars. Command line options
// override env vars. Default options are used unless overridden by env vars or
// command line options. Defaults are usually parsed from config files.
func ParseCommandLine(def Options) CommandLine {
	var c CommandLine
	c.Options = def
	p, err := arg.NewParser(arg.Config{Program: "spinc"}, &c)
	if err != nil {
		fmt.Printf("arg.NewParser: %s", err)
		os.Exit(1)
	}
	if err := p.Parse(os.Args[1:]); err != nil {
		switch err {
		case arg.ErrHelp:
			c.Help = true
		case arg.ErrVersion:
			c.Version = true
		default:
			fmt.Printf("Error parsing command line: %s\n", err)
			os.Exit(1)
		}
	}
	return c
}

// Help prints full help or minimal request help if full is false. An rm.Client
// is used to get the list of available requests from the Request Manager API.
func Help(full bool, rmc rm.Client) {
	if full {
		fmt.Printf("Usage: spinc [flags] <command> [request|id] [args|file]\n\n"+
			"Flags:\n"+
			"  --addr     Address of Request Managers (https://local.domain:8080)\n"+
			"  --config   Config files (default: %s)\n"+
			"  --debug    Print debug to stderr\n"+
			"  --env      Environment (dev, staging, production)\n"+
			"  --help     Print help\n"+
			"  --ping     Ping addr\n"+
			"  --timeout  API timeout, milliseconds (default: %d)\n"+
			"  --version  Print version\n\n"+
			"  -v         verbose ps or status\n\n"+
			"Commands:\n"+
			"  help    <request>  Print request help\n"+
			"  start   <request>  Start new request\n"+
			"  stop    <ID>       Stop request\n"+
			"  status  <ID>       Print status of request\n"+
			"  running <ID>       Exit 0 if request is running, else exit 1\n"+
			"  ps                 Show all running requests and jobs\n\n",
			DEFAULT_CONFIG_FILES, DEFAULT_TIMEOUT)
		printRequestList(rmc)
		fmt.Println("\n'spinc help <request>' for request help")
	} else {
		fmt.Printf("spinc help  <request>\nspinc start <request>\n\n")
		printRequestList(rmc)
		fmt.Println("\n'spinc help' for more")
	}
}

func printRequestList(rmc rm.Client) {
	if rmc == nil {
		fmt.Println("Specify --addr or ADDR environment variable to list all requests")
		return
	}

	fmt.Println("Requests:")
	req, err := rmc.RequestList()
	if err != nil {
		fmt.Println(err)
	} else {
		for _, r := range req {
			fmt.Println("  " + r.Name)
		}
	}
}

func RequestHelp(reqName string, rmc rm.Client) error {
	reqList, err := rmc.RequestList()
	if err != nil {
		return err
	}

	var req *proto.RequestSpec
	for _, r := range reqList {
		if r.Name != reqName {
			continue
		}
		req = &r
		break
	}

	if req == nil {
		return ErrUnknownRequest
	}

	fmt.Printf("%s args (* required)\n\n", req.Name)
	l := 0
	for _, a := range req.Args {
		if len(a.Name) > l {
			l = len(a.Name)
		}
	}
	line := fmt.Sprintf("  %%s %%-%ds  %%s\n", l)
	for _, a := range req.Args {
		help := a.Desc
		star := " "
		if a.Required {
			star = "*"
		} else {
			help += " (default: " + a.Default + ")"
		}
		fmt.Printf(line, star, a.Name, help)
	}
	fmt.Println("\nspinc start shutdown-host # prompt for args\n" +
		"spinc start shutdown-host <args>\n" +
		"spinc start shutdown-host <args-file.yaml>")
	return nil
}

func ParseConfigFiles(files string, debug bool) Options {
	var def Options
	for _, file := range strings.Split(files, ",") {
		// If file starts with ~/, we need to expand this to the user home dir
		// because this is a shell expansion, not something Go knows about.
		if file[:2] == "~/" {
			usr, _ := user.Current()
			file = filepath.Join(usr.HomeDir, file[2:])
		}

		absfile, err := filepath.Abs(file)
		if err != nil {
			if debug {
				log.Printf("filepath.Abs(%s) error: %s", file, err)
			}
			continue
		}

		bytes, err := ioutil.ReadFile(absfile)
		if err != nil {
			if debug {
				log.Printf("Cannot read config file %s: %s", file, err)
			}
			continue
		}

		var o Options
		if err := yaml.Unmarshal(bytes, &o); err != nil {
			if debug {
				log.Printf("Invalid YAML in config file %s: %s", file, err)
			}
			continue
		}

		// Set options from this config file only if they're set
		if debug {
			log.Printf("Applying config file %s (%s)", file, absfile)
		}
		if o.Addr != "" {
			def.Addr = o.Addr
		}
		if o.Timeout != 0 {
			def.Timeout = o.Timeout
		}
	}
	return def
}
<|endoftext|>


<commit_before>package client

import (
	"context"
	"time"

	"github.com/micro/go-micro/v3/broker"
	"github.com/micro/go-micro/v3/broker/http"
	"github.com/micro/go-micro/v3/codec"
	"github.com/micro/go-micro/v3/router"
	regRouter "github.com/micro/go-micro/v3/router/registry"
	"github.com/micro/go-micro/v3/selector"
	"github.com/micro/go-micro/v3/selector/random"
	"github.com/micro/go-micro/v3/transport"
	thttp "github.com/micro/go-micro/v3/transport/http"
)

type Options struct {
	// Used to select codec
	ContentType string
	// Proxy address to send requests via
	Proxy string

	// Plugged interfaces
	Broker    broker.Broker
	Codecs    map[string]codec.NewCodec
	Router    router.Router
	Selector  selector.Selector
	Transport transport.Transport

	// Lookup used for looking up routes
	Lookup LookupFunc

	// Connection Pool
	PoolSize int
	PoolTTL  time.Duration

	// Response cache
	Cache *Cache

	// Middleware for client
	Wrappers []Wrapper

	// Default Call Options
	CallOptions CallOptions

	// Other options for implementations of the interface
	// can be stored in a context
	Context context.Context
}

type CallOptions struct {
	// Address of remote hosts
	Address []string
	// Backoff func
	Backoff BackoffFunc
	// Duration to cache the response for
	CacheExpiry time.Duration
	// Transport Dial Timeout
	DialTimeout time.Duration
	// Number of Call attempts
	Retries int
	// Check if retriable func
	Retry RetryFunc
	// Request/Response timeout
	RequestTimeout time.Duration
	// Router to use for this call
	Router router.Router
	// Selector to use for the call
	Selector selector.Selector
	// SelectOptions to use when selecting a route
	SelectOptions []selector.SelectOption
	// Stream timeout for the stream
	StreamTimeout time.Duration
	// Use the auth token as the authorization header
	AuthToken bool
	// Network to lookup the route within
	Network string

	// Middleware for low level call func
	CallWrappers []CallWrapper

	// Other options for implementations of the interface
	// can be stored in a context
	Context context.Context
}

type PublishOptions struct {
	// Exchange is the routing exchange for the message
	Exchange string
	// Other options for implementations of the interface
	// can be stored in a context
	Context context.Context
}

type MessageOptions struct {
	ContentType string
}

type RequestOptions struct {
	ContentType string
	Stream      bool

	// Other options for implementations of the interface
	// can be stored in a context
	Context context.Context
}

func NewOptions(options ...Option) Options {
	opts := Options{
		Cache:       NewCache(),
		Context:     context.Background(),
		ContentType: "application/protobuf",
		Codecs:      make(map[string]codec.NewCodec),
		CallOptions: CallOptions{
			Backoff:        DefaultBackoff,
			Retry:          DefaultRetry,
			Retries:        DefaultRetries,
			RequestTimeout: DefaultRequestTimeout,
			DialTimeout:    transport.DefaultDialTimeout,
		},
		Lookup:    LookupRoute,
		PoolSize:  DefaultPoolSize,
		PoolTTL:   DefaultPoolTTL,
		Broker:    http.NewBroker(),
		Router:    regRouter.NewRouter(),
		Selector:  random.NewSelector(),
		Transport: thttp.NewTransport(),
	}

	for _, o := range options {
		o(&opts)
	}

	return opts
}

// Broker to be used for pub/sub
func Broker(b broker.Broker) Option {
	return func(o *Options) {
		o.Broker = b
	}
}

// Codec to be used to encode/decode requests for a given content type
func Codec(contentType string, c codec.NewCodec) Option {
	return func(o *Options) {
		o.Codecs[contentType] = c
	}
}

// Default content type of the client
func ContentType(ct string) Option {
	return func(o *Options) {
		o.ContentType = ct
	}
}

// Proxy sets the proxy address
func Proxy(addr string) Option {
	return func(o *Options) {
		o.Proxy = addr
	}
}

// PoolSize sets the connection pool size
func PoolSize(d int) Option {
	return func(o *Options) {
		o.PoolSize = d
	}
}

// PoolTTL sets the connection pool ttl
func PoolTTL(d time.Duration) Option {
	return func(o *Options) {
		o.PoolTTL = d
	}
}

// Transport to use for communication e.g http, rabbitmq, etc
func Transport(t transport.Transport) Option {
	return func(o *Options) {
		o.Transport = t
	}
}

// Router is used to lookup routes for a service
func Router(r router.Router) Option {
	return func(o *Options) {
		o.Router = r
	}
}

// Selector is used to select a route
func Selector(s selector.Selector) Option {
	return func(o *Options) {
		o.Selector = s
	}
}

// Adds a Wrapper to a list of options passed into the client
func Wrap(w Wrapper) Option {
	return func(o *Options) {
		o.Wrappers = append(o.Wrappers, w)
	}
}

// Adds a Wrapper to the list of CallFunc wrappers
func WrapCall(cw ...CallWrapper) Option {
	return func(o *Options) {
		o.CallOptions.CallWrappers = append(o.CallOptions.CallWrappers, cw...)
	}
}

// Backoff is used to set the backoff function used
// when retrying Calls
func Backoff(fn BackoffFunc) Option {
	return func(o *Options) {
		o.CallOptions.Backoff = fn
	}
}

// Lookup sets the lookup function to use for resolving service names
func Lookup(l LookupFunc) Option {
	return func(o *Options) {
		o.Lookup = l
	}
}

// Number of retries when making the request.
// Should this be a Call Option?
func Retries(i int) Option {
	return func(o *Options) {
		o.CallOptions.Retries = i
	}
}

// Retry sets the retry function to be used when re-trying.
func Retry(fn RetryFunc) Option {
	return func(o *Options) {
		o.CallOptions.Retry = fn
	}
}

// The request timeout.
// Should this be a Call Option?
func RequestTimeout(d time.Duration) Option {
	return func(o *Options) {
		o.CallOptions.RequestTimeout = d
	}
}

// StreamTimeout sets the stream timeout
func StreamTimeout(d time.Duration) Option {
	return func(o *Options) {
		o.CallOptions.StreamTimeout = d
	}
}

// Transport dial timeout
func DialTimeout(d time.Duration) Option {
	return func(o *Options) {
		o.CallOptions.DialTimeout = d
	}
}

// Call Options

// WithExchange sets the exchange to route a message through
func WithExchange(e string) PublishOption {
	return func(o *PublishOptions) {
		o.Exchange = e
	}
}

// PublishContext sets the context in publish options
func PublishContext(ctx context.Context) PublishOption {
	return func(o *PublishOptions) {
		o.Context = ctx
	}
}

// WithAddress sets the remote addresses to use rather than using service discovery
func WithAddress(a ...string) CallOption {
	return func(o *CallOptions) {
		o.Address = a
	}
}

// WithCallWrapper is a CallOption which adds to the existing CallFunc wrappers
func WithCallWrapper(cw ...CallWrapper) CallOption {
	return func(o *CallOptions) {
		o.CallWrappers = append(o.CallWrappers, cw...)
	}
}

// WithBackoff is a CallOption which overrides that which
// set in Options.CallOptions
func WithBackoff(fn BackoffFunc) CallOption {
	return func(o *CallOptions) {
		o.Backoff = fn
	}
}

// WithRetry is a CallOption which overrides that which
// set in Options.CallOptions
func WithRetry(fn RetryFunc) CallOption {
	return func(o *CallOptions) {
		o.Retry = fn
	}
}

// WithRetries is a CallOption which overrides that which
// set in Options.CallOptions
func WithRetries(i int) CallOption {
	return func(o *CallOptions) {
		o.Retries = i
	}
}

// WithRequestTimeout is a CallOption which overrides that which
// set in Options.CallOptions
func WithRequestTimeout(d time.Duration) CallOption {
	return func(o *CallOptions) {
		o.RequestTimeout = d
	}
}

// WithStreamTimeout sets the stream timeout
func WithStreamTimeout(d time.Duration) CallOption {
	return func(o *CallOptions) {
		o.StreamTimeout = d
	}
}

// WithDialTimeout is a CallOption which overrides that which
// set in Options.CallOptions
func WithDialTimeout(d time.Duration) CallOption {
	return func(o *CallOptions) {
		o.DialTimeout = d
	}
}

// WithAuthToken is a CallOption which overrides the
// authorization header with the services own auth token
func WithAuthToken() CallOption {
	return func(o *CallOptions) {
		o.AuthToken = true
	}
}

// WithCache is a CallOption which sets the duration the response
// shoull be cached for
func WithCache(c time.Duration) CallOption {
	return func(o *CallOptions) {
		o.CacheExpiry = c
	}
}

// WithNetwork is a CallOption which sets the network attribute
func WithNetwork(n string) CallOption {
	return func(o *CallOptions) {
		o.Network = n
	}
}

// WithRouter sets the router to use for this call
func WithRouter(r router.Router) CallOption {
	return func(o *CallOptions) {
		o.Router = r
	}
}

// WithSelector sets the selector to use for this call
func WithSelector(s selector.Selector) CallOption {
	return func(o *CallOptions) {
		o.Selector = s
	}
}

// WithSelectOptions sets the options to pass to the selector for this call
func WithSelectOptions(sops ...selector.SelectOption) CallOption {
	return func(o *CallOptions) {
		o.SelectOptions = sops
	}
}

func WithMessageContentType(ct string) MessageOption {
	return func(o *MessageOptions) {
		o.ContentType = ct
	}
}

// Request Options

func WithContentType(ct string) RequestOption {
	return func(o *RequestOptions) {
		o.ContentType = ct
	}
}

func StreamingRequest() RequestOption {
	return func(o *RequestOptions) {
		o.Stream = true
	}
}
<commit_msg>allow setting registry router in client<commit_after>package client

import (
	"context"
	"time"

	"github.com/micro/go-micro/v3/broker"
	"github.com/micro/go-micro/v3/broker/http"
	"github.com/micro/go-micro/v3/codec"
	"github.com/micro/go-micro/v3/registry"
	"github.com/micro/go-micro/v3/router"
	regRouter "github.com/micro/go-micro/v3/router/registry"
	"github.com/micro/go-micro/v3/selector"
	"github.com/micro/go-micro/v3/selector/random"
	"github.com/micro/go-micro/v3/transport"
	thttp "github.com/micro/go-micro/v3/transport/http"
)

type Options struct {
	// Used to select codec
	ContentType string
	// Proxy address to send requests via
	Proxy string

	// Plugged interfaces
	Broker    broker.Broker
	Codecs    map[string]codec.NewCodec
	Router    router.Router
	Selector  selector.Selector
	Transport transport.Transport

	// Lookup used for looking up routes
	Lookup LookupFunc

	// Connection Pool
	PoolSize int
	PoolTTL  time.Duration

	// Response cache
	Cache *Cache

	// Middleware for client
	Wrappers []Wrapper

	// Default Call Options
	CallOptions CallOptions

	// Other options for implementations of the interface
	// can be stored in a context
	Context context.Context
}

type CallOptions struct {
	// Address of remote hosts
	Address []string
	// Backoff func
	Backoff BackoffFunc
	// Duration to cache the response for
	CacheExpiry time.Duration
	// Transport Dial Timeout
	DialTimeout time.Duration
	// Number of Call attempts
	Retries int
	// Check if retriable func
	Retry RetryFunc
	// Request/Response timeout
	RequestTimeout time.Duration
	// Router to use for this call
	Router router.Router
	// Selector to use for the call
	Selector selector.Selector
	// SelectOptions to use when selecting a route
	SelectOptions []selector.SelectOption
	// Stream timeout for the stream
	StreamTimeout time.Duration
	// Use the auth token as the authorization header
	AuthToken bool
	// Network to lookup the route within
	Network string

	// Middleware for low level call func
	CallWrappers []CallWrapper

	// Other options for implementations of the interface
	// can be stored in a context
	Context context.Context
}

type PublishOptions struct {
	// Exchange is the routing exchange for the message
	Exchange string
	// Other options for implementations of the interface
	// can be stored in a context
	Context context.Context
}

type MessageOptions struct {
	ContentType string
}

type RequestOptions struct {
	ContentType string
	Stream      bool

	// Other options for implementations of the interface
	// can be stored in a context
	Context context.Context
}

func NewOptions(options ...Option) Options {
	opts := Options{
		Cache:       NewCache(),
		Context:     context.Background(),
		ContentType: "application/protobuf",
		Codecs:      make(map[string]codec.NewCodec),
		CallOptions: CallOptions{
			Backoff:        DefaultBackoff,
			Retry:          DefaultRetry,
			Retries:        DefaultRetries,
			RequestTimeout: DefaultRequestTimeout,
			DialTimeout:    transport.DefaultDialTimeout,
		},
		Lookup:    LookupRoute,
		PoolSize:  DefaultPoolSize,
		PoolTTL:   DefaultPoolTTL,
		Broker:    http.NewBroker(),
		Router:    regRouter.NewRouter(),
		Selector:  random.NewSelector(),
		Transport: thttp.NewTransport(),
	}

	for _, o := range options {
		o(&opts)
	}

	return opts
}

// Broker to be used for pub/sub
func Broker(b broker.Broker) Option {
	return func(o *Options) {
		o.Broker = b
	}
}

// Codec to be used to encode/decode requests for a given content type
func Codec(contentType string, c codec.NewCodec) Option {
	return func(o *Options) {
		o.Codecs[contentType] = c
	}
}

// Default content type of the client
func ContentType(ct string) Option {
	return func(o *Options) {
		o.ContentType = ct
	}
}

// Proxy sets the proxy address
func Proxy(addr string) Option {
	return func(o *Options) {
		o.Proxy = addr
	}
}

// PoolSize sets the connection pool size
func PoolSize(d int) Option {
	return func(o *Options) {
		o.PoolSize = d
	}
}

// PoolTTL sets the connection pool ttl
func PoolTTL(d time.Duration) Option {
	return func(o *Options) {
		o.PoolTTL = d
	}
}

// Transport to use for communication e.g http, rabbitmq, etc
func Transport(t transport.Transport) Option {
	return func(o *Options) {
		o.Transport = t
	}
}

// Registry sets the routers registry
func Registry(r registry.Registry) Option {
	return func(o *Options) {
		o.Router.Init(router.Registry(r))
	}
}

// Router is used to lookup routes for a service
func Router(r router.Router) Option {
	return func(o *Options) {
		o.Router = r
	}
}

// Selector is used to select a route
func Selector(s selector.Selector) Option {
	return func(o *Options) {
		o.Selector = s
	}
}

// Adds a Wrapper to a list of options passed into the client
func Wrap(w Wrapper) Option {
	return func(o *Options) {
		o.Wrappers = append(o.Wrappers, w)
	}
}

// Adds a Wrapper to the list of CallFunc wrappers
func WrapCall(cw ...CallWrapper) Option {
	return func(o *Options) {
		o.CallOptions.CallWrappers = append(o.CallOptions.CallWrappers, cw...)
	}
}

// Backoff is used to set the backoff function used
// when retrying Calls
func Backoff(fn BackoffFunc) Option {
	return func(o *Options) {
		o.CallOptions.Backoff = fn
	}
}

// Lookup sets the lookup function to use for resolving service names
func Lookup(l LookupFunc) Option {
	return func(o *Options) {
		o.Lookup = l
	}
}

// Number of retries when making the request.
// Should this be a Call Option?
func Retries(i int) Option {
	return func(o *Options) {
		o.CallOptions.Retries = i
	}
}

// Retry sets the retry function to be used when re-trying.
func Retry(fn RetryFunc) Option {
	return func(o *Options) {
		o.CallOptions.Retry = fn
	}
}

// The request timeout.
// Should this be a Call Option?
func RequestTimeout(d time.Duration) Option {
	return func(o *Options) {
		o.CallOptions.RequestTimeout = d
	}
}

// StreamTimeout sets the stream timeout
func StreamTimeout(d time.Duration) Option {
	return func(o *Options) {
		o.CallOptions.StreamTimeout = d
	}
}

// Transport dial timeout
func DialTimeout(d time.Duration) Option {
	return func(o *Options) {
		o.CallOptions.DialTimeout = d
	}
}

// Call Options

// WithExchange sets the exchange to route a message through
func WithExchange(e string) PublishOption {
	return func(o *PublishOptions) {
		o.Exchange = e
	}
}

// PublishContext sets the context in publish options
func PublishContext(ctx context.Context) PublishOption {
	return func(o *PublishOptions) {
		o.Context = ctx
	}
}

// WithAddress sets the remote addresses to use rather than using service discovery
func WithAddress(a ...string) CallOption {
	return func(o *CallOptions) {
		o.Address = a
	}
}

// WithCallWrapper is a CallOption which adds to the existing CallFunc wrappers
func WithCallWrapper(cw ...CallWrapper) CallOption {
	return func(o *CallOptions) {
		o.CallWrappers = append(o.CallWrappers, cw...)
	}
}

// WithBackoff is a CallOption which overrides that which
// set in Options.CallOptions
func WithBackoff(fn BackoffFunc) CallOption {
	return func(o *CallOptions) {
		o.Backoff = fn
	}
}

// WithRetry is a CallOption which overrides that which
// set in Options.CallOptions
func WithRetry(fn RetryFunc) CallOption {
	return func(o *CallOptions) {
		o.Retry = fn
	}
}

// WithRetries is a CallOption which overrides that which
// set in Options.CallOptions
func WithRetries(i int) CallOption {
	return func(o *CallOptions) {
		o.Retries = i
	}
}

// WithRequestTimeout is a CallOption which overrides that which
// set in Options.CallOptions
func WithRequestTimeout(d time.Duration) CallOption {
	return func(o *CallOptions) {
		o.RequestTimeout = d
	}
}

// WithStreamTimeout sets the stream timeout
func WithStreamTimeout(d time.Duration) CallOption {
	return func(o *CallOptions) {
		o.StreamTimeout = d
	}
}

// WithDialTimeout is a CallOption which overrides that which
// set in Options.CallOptions
func WithDialTimeout(d time.Duration) CallOption {
	return func(o *CallOptions) {
		o.DialTimeout = d
	}
}

// WithAuthToken is a CallOption which overrides the
// authorization header with the services own auth token
func WithAuthToken() CallOption {
	return func(o *CallOptions) {
		o.AuthToken = true
	}
}

// WithCache is a CallOption which sets the duration the response
// shoull be cached for
func WithCache(c time.Duration) CallOption {
	return func(o *CallOptions) {
		o.CacheExpiry = c
	}
}

// WithNetwork is a CallOption which sets the network attribute
func WithNetwork(n string) CallOption {
	return func(o *CallOptions) {
		o.Network = n
	}
}

// WithRouter sets the router to use for this call
func WithRouter(r router.Router) CallOption {
	return func(o *CallOptions) {
		o.Router = r
	}
}

// WithSelector sets the selector to use for this call
func WithSelector(s selector.Selector) CallOption {
	return func(o *CallOptions) {
		o.Selector = s
	}
}

// WithSelectOptions sets the options to pass to the selector for this call
func WithSelectOptions(sops ...selector.SelectOption) CallOption {
	return func(o *CallOptions) {
		o.SelectOptions = sops
	}
}

func WithMessageContentType(ct string) MessageOption {
	return func(o *MessageOptions) {
		o.ContentType = ct
	}
}

// Request Options

func WithContentType(ct string) RequestOption {
	return func(o *RequestOptions) {
		o.ContentType = ct
	}
}

func StreamingRequest() RequestOption {
	return func(o *RequestOptions) {
		o.Stream = true
	}
}
<|endoftext|>


<commit_before>package rtmp

import (
	"bytes"
	"io"
)

func (c *Client) receiveLoop() {
	for {
		// Read the next header from the connection
		h, err := ReadHeader(c)
		if err != nil {
			if c.IsAlive() {
				log.Warn("unable to receive next header while connected")
				c.Reset()
			} else {
				log.Debug("client receive: connection closed")
			}
			return
		}

		// Determine whether or not we already have a chunk stream
		// allocated for this ID. If we don't, create one.
		var cs *InboundChunkStream = c.inChunkStreams[h.ChunkStreamId]
		if cs == nil {
			cs = NewInboundChunkStream(h.ChunkStreamId)
			c.inChunkStreams[h.ChunkStreamId] = cs
		}

		var ts uint32
		var m *Message

		if (cs.lastHeader == nil) && (h.Format != HEADER_FORMAT_FULL) {
			log.Warn("unable to find previous header on chunk stream %d", h.ChunkStreamId)
			c.Reset()
			return
		}

		switch h.Format {
		case HEADER_FORMAT_FULL:
			// If it's an entirely new header, replace the reference in
			// the chunk stream and set the working timestamp from
			// the header.
			cs.lastHeader = &h
			ts = h.Timestamp

		case HEADER_FORMAT_SAME_STREAM:
			// If it's the same stream, use the last message stream id,
			// but otherwise use values from the header.
			h.MessageStreamId = cs.lastHeader.MessageStreamId
			cs.lastHeader = &h
			ts = cs.lastInAbsoluteTimestamp + h.Timestamp

		case HEADER_FORMAT_SAME_LENGTH_AND_STREAM:
			// If it's the same length and stream, copy values from the
			// last header and replace it.
			h.MessageStreamId = cs.lastHeader.MessageStreamId
			h.MessageLength = cs.lastHeader.MessageLength
			h.MessageTypeId = cs.lastHeader.MessageTypeId
			cs.lastHeader = &h
			ts = cs.lastInAbsoluteTimestamp + h.Timestamp

		case HEADER_FORMAT_CONTINUATION:
			// A full continuation of the previous stream. Copy all values.
			h.MessageStreamId = cs.lastHeader.MessageStreamId
			h.MessageLength = cs.lastHeader.MessageLength
			h.MessageTypeId = cs.lastHeader.MessageTypeId
			h.Timestamp = cs.lastHeader.Timestamp
			ts = cs.lastInAbsoluteTimestamp + cs.lastHeader.Timestamp

			// If there's a message already started, use it.
			if cs.currentMessage != nil {
				m = cs.currentMessage
			}
		}

		if m == nil {
			m = &Message{
				Type:              h.MessageTypeId,
				ChunkStreamId:     h.ChunkStreamId,
				StreamId:          h.MessageStreamId,
				Timestamp:         h.CalculateTimestamp(),
				AbsoluteTimestamp: ts,
				Length:            h.MessageLength,
				Buffer:            new(bytes.Buffer),
			}
		}

		cs.lastInAbsoluteTimestamp = ts

		rs := m.RemainingBytes()
		if rs > c.inChunkSize {
			rs = c.inChunkSize
		}

		_, err = io.CopyN(m.Buffer, c, int64(rs))
		if err != nil {
			if c.connected {
				log.Warn("unable to copy %d message bytes from buffer", rs)
				c.Reset()
			}

			return
		}

		if m.RemainingBytes() == 0 {
			cs.currentMessage = nil
			log.Trace("receive sending message to router: %#v", m)
			c.inMessages <- m
		} else {
			cs.currentMessage = m
		}
	}
}
<commit_msg>Include error in read warning<commit_after>package rtmp

import (
	"bytes"
	"io"
)

func (c *Client) receiveLoop() {
	for {
		// Read the next header from the connection
		h, err := ReadHeader(c)
		if err != nil {
			if c.IsAlive() {
				log.Warn("unable to receive next header while connected: %s", err)
				c.Reset()
			} else {
				log.Debug("client receive: connection closed")
			}
			return
		}

		// Determine whether or not we already have a chunk stream
		// allocated for this ID. If we don't, create one.
		var cs *InboundChunkStream = c.inChunkStreams[h.ChunkStreamId]
		if cs == nil {
			cs = NewInboundChunkStream(h.ChunkStreamId)
			c.inChunkStreams[h.ChunkStreamId] = cs
		}

		var ts uint32
		var m *Message

		if (cs.lastHeader == nil) && (h.Format != HEADER_FORMAT_FULL) {
			log.Warn("unable to find previous header on chunk stream %d", h.ChunkStreamId)
			c.Reset()
			return
		}

		switch h.Format {
		case HEADER_FORMAT_FULL:
			// If it's an entirely new header, replace the reference in
			// the chunk stream and set the working timestamp from
			// the header.
			cs.lastHeader = &h
			ts = h.Timestamp

		case HEADER_FORMAT_SAME_STREAM:
			// If it's the same stream, use the last message stream id,
			// but otherwise use values from the header.
			h.MessageStreamId = cs.lastHeader.MessageStreamId
			cs.lastHeader = &h
			ts = cs.lastInAbsoluteTimestamp + h.Timestamp

		case HEADER_FORMAT_SAME_LENGTH_AND_STREAM:
			// If it's the same length and stream, copy values from the
			// last header and replace it.
			h.MessageStreamId = cs.lastHeader.MessageStreamId
			h.MessageLength = cs.lastHeader.MessageLength
			h.MessageTypeId = cs.lastHeader.MessageTypeId
			cs.lastHeader = &h
			ts = cs.lastInAbsoluteTimestamp + h.Timestamp

		case HEADER_FORMAT_CONTINUATION:
			// A full continuation of the previous stream. Copy all values.
			h.MessageStreamId = cs.lastHeader.MessageStreamId
			h.MessageLength = cs.lastHeader.MessageLength
			h.MessageTypeId = cs.lastHeader.MessageTypeId
			h.Timestamp = cs.lastHeader.Timestamp
			ts = cs.lastInAbsoluteTimestamp + cs.lastHeader.Timestamp

			// If there's a message already started, use it.
			if cs.currentMessage != nil {
				m = cs.currentMessage
			}
		}

		if m == nil {
			m = &Message{
				Type:              h.MessageTypeId,
				ChunkStreamId:     h.ChunkStreamId,
				StreamId:          h.MessageStreamId,
				Timestamp:         h.CalculateTimestamp(),
				AbsoluteTimestamp: ts,
				Length:            h.MessageLength,
				Buffer:            new(bytes.Buffer),
			}
		}

		cs.lastInAbsoluteTimestamp = ts

		rs := m.RemainingBytes()
		if rs > c.inChunkSize {
			rs = c.inChunkSize
		}

		_, err = io.CopyN(m.Buffer, c, int64(rs))
		if err != nil {
			if c.connected {
				log.Warn("unable to copy %d message bytes from buffer", rs)
				c.Reset()
			}

			return
		}

		if m.RemainingBytes() == 0 {
			cs.currentMessage = nil
			log.Trace("receive sending message to router: %#v", m)
			c.inMessages <- m
		} else {
			cs.currentMessage = m
		}
	}
}
<|endoftext|>