<commit_before>package main

import (
	"bytes"
	"fmt"
	"strconv"
	"time"
	"unicode/utf8"

	"github.com/ikkerens/disgo"
	"github.com/slf4go/logger"
)

const (
	slotKeyCap = '\U000020e3'
	slotCircle = '\U0001f534'
	slotCross  = '\U0000274e'
)

var winConditions = [...][]int{
	// Horizontal
	{0, 1, 2},
	{3, 4, 5},
	{6, 7, 8},

	// Vertical
	{0, 3, 6},
	{1, 4, 7},
	{2, 5, 8},

	// Diagonal
	{0, 4, 8},
	{2, 4, 6},
}

type ticTacToe struct {
	discord                 *disgo.Session
	player1, player2        *disgo.User
	gameOver, draw, expired bool

	board *disgo.Message
	slots [9]rune
	turn  bool

	expire *time.Timer
}

func (game *ticTacToe) start(channel disgo.Snowflake) disgo.Snowflake {
	for i := range game.slots {
		game.slots[i] = 0
	}

	board, err := game.discord.SendEmbed(channel, *game.buildBoard())
	if err != nil {
		logger.ErrorE(err)
		return 0
	}
	game.board = board

	for i := 1; i <= 9; i++ {
		board.AddReaction(strconv.Itoa(i) + string(slotKeyCap))
	}

	game.expire = time.NewTimer(30 * time.Second)
	go func() {
		<-game.expire.C
		game.expired = true
		game.end()
	}()

	return board.ID()
}

func (game *ticTacToe) addReaction(user disgo.Snowflake, emoji string) {
	current, _, piece := game.getCurrentPlayer()
	if current.ID() != user {
		return
	}

	runes := []rune(emoji)
	if utf8.RuneCountInString(emoji) == 2 && runes[1] == slotKeyCap {
		slot, err := strconv.ParseInt(string(runes[0]), 10, 8)
		if err != nil {
			return
		}

		if slot >= 1 && slot <= 9 && game.slots[slot-1] == 0 {
			game.expire.Reset(10 * time.Second)
			game.slots[slot-1] = piece
			game.turn = !game.turn

			if !game.checkForEndOfGame() {
				game.board.EditEmbed(*game.buildBoard())
				game.board.DeleteOwnReaction(emoji)
			}
		}
	}
}

func (game *ticTacToe) checkForEndOfGame() bool {
	// Is there a winner?
	for _, win := range winConditions {
		if game.slots[win[0]] == game.slots[win[1]] && game.slots[win[0]] == game.slots[win[2]] && game.slots[win[0]] != 0 {
			if game.slots[win[0]] == slotCircle {
				game.turn = false
			} else {
				game.turn = true
			}
			game.end()
			return true
		}
	}

	// Are there free slots?
	for _, slot := range game.slots {
		if slot == 0 {
			return false
		}
	}

	// No winner, but also no free slots, must be a draw
	game.draw = true
	game.end()
	return true
}

func (game *ticTacToe) end() {
	if !game.gameOver {
		delete(games, game.board.ID())
		game.expire.Stop()
		game.gameOver = true
		game.board.DeleteAllReactions()
		game.board.EditEmbed(*game.buildBoard())
	}
}

func (game *ticTacToe) buildBoard() *disgo.Embed {
	var field bytes.Buffer

	for y := 0; y < 3; y++ {
		for x := 0; x < 3; x++ {
			slot := game.slots[y*3+x]
			if slot == 0 {
				field.WriteString(strconv.Itoa(y*3 + x + 1))
				field.WriteRune(slotKeyCap)
			} else {
				field.WriteRune(slot)
			}
		}

		if y != 2 {
			field.WriteRune('\n')
		}
	}

	var footer string
	current, color, _ := game.getCurrentPlayer()

	if game.gameOver {
		color = 0x4F545C
		if game.draw {
			footer = "The game ended in a draw."
		} else if game.expired {
			footer = "This game has been cancelled due to player inactivity."
		} else {
			footer = fmt.Sprintf("%s won the game.", current.Username())
		}
	} else {
		footer = fmt.Sprintf("It's currently %s's turn.", current.Username())
	}

	return &disgo.Embed{
		Title:       fmt.Sprintf("Tic Tac Toe: %c %s vs %c %s", slotCircle, game.player1.Username(), slotCross, game.player2.Username()),
		Description: field.String(),
		Color:       color,
		Footer: disgo.EmbedFooter{
			Text:    footer,
			IconURL: current.AvatarURL(),
		},
	}
}

func (game *ticTacToe) getCurrentPlayer() (*disgo.User, int, rune) {
	if game.turn {
		return game.player2, 0x77B255, slotCross
	} else {
		return game.player1, 0xDD2E44, slotCircle
	}
}
<commit_msg>Added game.go to build ignore<commit_after>package main

// +build ignore

import (
	"bytes"
	"fmt"
	"strconv"
	"time"
	"unicode/utf8"

	"github.com/ikkerens/disgo"
	"github.com/slf4go/logger"
)

const (
	slotKeyCap = '\U000020e3'
	slotCircle = '\U0001f534'
	slotCross  = '\U0000274e'
)

var winConditions = [...][]int{
	// Horizontal
	{0, 1, 2},
	{3, 4, 5},
	{6, 7, 8},

	// Vertical
	{0, 3, 6},
	{1, 4, 7},
	{2, 5, 8},

	// Diagonal
	{0, 4, 8},
	{2, 4, 6},
}

type ticTacToe struct {
	discord                 *disgo.Session
	player1, player2        *disgo.User
	gameOver, draw, expired bool

	board *disgo.Message
	slots [9]rune
	turn  bool

	expire *time.Timer
}

func (game *ticTacToe) start(channel disgo.Snowflake) disgo.Snowflake {
	for i := range game.slots {
		game.slots[i] = 0
	}

	board, err := game.discord.SendEmbed(channel, *game.buildBoard())
	if err != nil {
		logger.ErrorE(err)
		return 0
	}
	game.board = board

	for i := 1; i <= 9; i++ {
		board.AddReaction(strconv.Itoa(i) + string(slotKeyCap))
	}

	game.expire = time.NewTimer(30 * time.Second)
	go func() {
		<-game.expire.C
		game.expired = true
		game.end()
	}()

	return board.ID()
}

func (game *ticTacToe) addReaction(user disgo.Snowflake, emoji string) {
	current, _, piece := game.getCurrentPlayer()
	if current.ID() != user {
		return
	}

	runes := []rune(emoji)
	if utf8.RuneCountInString(emoji) == 2 && runes[1] == slotKeyCap {
		slot, err := strconv.ParseInt(string(runes[0]), 10, 8)
		if err != nil {
			return
		}

		if slot >= 1 && slot <= 9 && game.slots[slot-1] == 0 {
			game.expire.Reset(10 * time.Second)
			game.slots[slot-1] = piece
			game.turn = !game.turn

			if !game.checkForEndOfGame() {
				game.board.EditEmbed(*game.buildBoard())
				game.board.DeleteOwnReaction(emoji)
			}
		}
	}
}

func (game *ticTacToe) checkForEndOfGame() bool {
	// Is there a winner?
	for _, win := range winConditions {
		if game.slots[win[0]] == game.slots[win[1]] && game.slots[win[0]] == game.slots[win[2]] && game.slots[win[0]] != 0 {
			if game.slots[win[0]] == slotCircle {
				game.turn = false
			} else {
				game.turn = true
			}
			game.end()
			return true
		}
	}

	// Are there free slots?
	for _, slot := range game.slots {
		if slot == 0 {
			return false
		}
	}

	// No winner, but also no free slots, must be a draw
	game.draw = true
	game.end()
	return true
}

func (game *ticTacToe) end() {
	if !game.gameOver {
		delete(games, game.board.ID())
		game.expire.Stop()
		game.gameOver = true
		game.board.DeleteAllReactions()
		game.board.EditEmbed(*game.buildBoard())
	}
}

func (game *ticTacToe) buildBoard() *disgo.Embed {
	var field bytes.Buffer

	for y := 0; y < 3; y++ {
		for x := 0; x < 3; x++ {
			slot := game.slots[y*3+x]
			if slot == 0 {
				field.WriteString(strconv.Itoa(y*3 + x + 1))
				field.WriteRune(slotKeyCap)
			} else {
				field.WriteRune(slot)
			}
		}

		if y != 2 {
			field.WriteRune('\n')
		}
	}

	var footer string
	current, color, _ := game.getCurrentPlayer()

	if game.gameOver {
		color = 0x4F545C
		if game.draw {
			footer = "The game ended in a draw."
		} else if game.expired {
			footer = "This game has been cancelled due to player inactivity."
		} else {
			footer = fmt.Sprintf("%s won the game.", current.Username())
		}
	} else {
		footer = fmt.Sprintf("It's currently %s's turn.", current.Username())
	}

	return &disgo.Embed{
		Title:       fmt.Sprintf("Tic Tac Toe: %c %s vs %c %s", slotCircle, game.player1.Username(), slotCross, game.player2.Username()),
		Description: field.String(),
		Color:       color,
		Footer: disgo.EmbedFooter{
			Text:    footer,
			IconURL: current.AvatarURL(),
		},
	}
}

func (game *ticTacToe) getCurrentPlayer() (*disgo.User, int, rune) {
	if game.turn {
		return game.player2, 0x77B255, slotCross
	} else {
		return game.player1, 0xDD2E44, slotCircle
	}
}
<|endoftext|>


<commit_before>package raymond

import (
	"fmt"
	"log"
	"reflect"
)

// Arguments provided to helpers
type HelperArg struct {
	// evaluation visitor
	eval *EvalVisitor

	// params
	params []interface{}
	hash   map[string]interface{}
}

// Helper function: can return a string or a SafeString
type Helper func(h *HelperArg) interface{}

// All global helpers
var helpers map[string]Helper

func init() {
	helpers = make(map[string]Helper)

	// register builtin helpers
	RegisterHelper("if", ifHelper)
	RegisterHelper("unless", unlessHelper)
	RegisterHelper("with", withHelper)
	RegisterHelper("each", eachHelper)
	RegisterHelper("log", logHelper)
	RegisterHelper("lookup", lookupHelper)
}

// Registers a new global helper function
func RegisterHelper(name string, helper Helper) {
	if helpers[name] != nil {
		panic(fmt.Errorf("Helper already registered: %s", name))
	}

	helpers[name] = helper
}

// Find a registered global helper function
func FindHelper(name string) Helper {
	return helpers[name]
}

// Instanciates a new HelperArg
func NewHelperArg(eval *EvalVisitor, params []interface{}, hash map[string]interface{}) *HelperArg {
	return &HelperArg{
		eval:   eval,
		params: params,
		hash:   hash,
	}
}

// Instanciates a new empty HelperArg
func NewEmptyHelperArg(eval *EvalVisitor) *HelperArg {
	return &HelperArg{
		eval: eval,
		hash: make(map[string]interface{}),
	}
}

// Returns all parameters
func (h *HelperArg) Params() []interface{} {
	return h.params
}

// Returns parameter at given position
func (h *HelperArg) Param(pos int) interface{} {
	if len(h.params) > pos {
		return h.params[pos]
	} else {
		return nil
	}
}

// Get string representation of parameter at given position
func (h *HelperArg) ParamStr(pos int) string {
	return Str(h.Param(pos))
}

// Returns hash value by name
func (h *HelperArg) Hash(name string) interface{} {
	return h.hash[name]
}

// Returns string representation of hash value by name
func (h *HelperArg) HashStr(name string) string {
	return Str(h.hash[name])
}

// Returns current evaluation context
func (h *HelperArg) Ctx() interface{} {
	return h.eval.curCtx()
}

// Returns current context field value
func (h *HelperArg) Field(name string) interface{} {
	value := h.eval.evalField(h.eval.curCtx(), name, false)
	if !value.IsValid() {
		return nil
	}

	return value.Interface()
}

// Get string representation of input data by name
func (h *HelperArg) FieldStr(name string) string {
	return Str(h.Field(name))
}

// Returns true if first param is truthy
func (h *HelperArg) TruthFirstParam() bool {
	val := h.Param(0)
	if val == nil {
		return false
	}

	thruth, ok := IsTruth(reflect.ValueOf(val))
	if !ok {
		return false
	}

	return thruth
}

// Returns true if 'includeZero' option is set and first param is the number 0
func (h *HelperArg) IsIncludableZero() bool {
	b, ok := h.Hash("includeZero").(bool)
	if ok && b {
		nb, ok := h.Param(0).(int)
		if ok && nb == 0 {
			return true
		}
	}

	return false
}

// Evaluate block with given context, private data and iteration key
func (h *HelperArg) BlockWith(ctx interface{}, data *DataFrame, key interface{}) string {
	result := ""

	if block := h.eval.curBlock(); (block != nil) && (block.Program != nil) {
		result = h.eval.evalProgram(block.Program, ctx, data, key)
	}

	return result
}

// Evaluate block with given context
func (h *HelperArg) BlockWithCtx(ctx interface{}) string {
	return h.BlockWith(ctx, nil, nil)
}

// Evaluate block
func (h *HelperArg) Block() string {
	return h.BlockWith(nil, nil, nil)
}

// Evaluate inverse
func (h *HelperArg) Inverse() string {
	result := ""
	if block := h.eval.curBlock(); (block != nil) && (block.Inverse != nil) {
		result, _ = block.Inverse.Accept(h.eval).(string)
	}

	return result
}

// Evaluate field for given context
func (h *HelperArg) Eval(ctx interface{}, field string) interface{} {
	if ctx == nil {
		return nil
	}

	if field == "" {
		return nil
	}

	val := h.eval.evalField(reflect.ValueOf(ctx), field, false)
	if !val.IsValid() {
		return nil
	}

	return val.Interface()
}

// Push context
func (h *HelperArg) PushCtx(ctx interface{}) {
	h.eval.pushCtx(reflect.ValueOf(ctx))
}

// Pop context
func (h *HelperArg) PopCtx() interface{} {
	var value reflect.Value

	value = h.eval.popCtx()
	if !value.IsValid() {
		return value
	}

	return value.Interface()
}

// Get current private data frame
func (h *HelperArg) DataFrame() *DataFrame {
	return h.eval.dataFrame
}

// Instanciates a new data frame that is a copy of current one
func (h *HelperArg) NewDataFrame() *DataFrame {
	return h.eval.dataFrame.Copy()
}

// Instanciates a new data frame and set iteration specific vars
func (h *HelperArg) NewIterDataFrame(length int, i int, key interface{}) *DataFrame {
	return h.eval.dataFrame.NewIterDataFrame(length, i, key)
}

// Set current data frame
func (h *HelperArg) SetDataFrame(data *DataFrame) {
	h.eval.setDataFrame(data)
}

// Set back parent data frame
func (h *HelperArg) PopDataFrame() {
	h.eval.popDataFrame()
}

//
// Builtin helpers
//

func ifHelper(h *HelperArg) interface{} {
	if h.IsIncludableZero() || h.TruthFirstParam() {
		return h.Block()
	}

	return h.Inverse()
}

func unlessHelper(h *HelperArg) interface{} {
	if h.IsIncludableZero() || h.TruthFirstParam() {
		return h.Inverse()
	}

	return h.Block()
}

func withHelper(h *HelperArg) interface{} {
	if h.TruthFirstParam() {
		return h.BlockWithCtx(h.Param(0))
	}

	return h.Inverse()
}

func eachHelper(h *HelperArg) interface{} {
	if !h.TruthFirstParam() {
		h.Inverse()
		return ""
	}

	result := ""

	val := reflect.ValueOf(h.Param(0))
	switch val.Kind() {
	case reflect.Array, reflect.Slice:
		for i := 0; i < val.Len(); i++ {
			// computes private data
			data := h.NewIterDataFrame(val.Len(), i, nil)

			// evaluates block
			result += h.BlockWith(val.Index(i).Interface(), data, i)
		}
	case reflect.Map:
		// note: a go hash is not ordered, so result may vary, this behaviour differs from the JS implementation
		keys := val.MapKeys()
		for i := 0; i < len(keys); i++ {
			key := keys[i].Interface()
			ctx := val.MapIndex(keys[i]).Interface()

			// computes private data
			data := h.NewIterDataFrame(len(keys), i, key)

			// evaluates block
			result += h.BlockWith(ctx, data, key)
		}
	case reflect.Struct:
		var exportedFields []int

		// collect exported fields only
		for i := 0; i < val.NumField(); i++ {
			if tField := val.Type().Field(i); tField.PkgPath == "" {
				exportedFields = append(exportedFields, i)
			}
		}

		for i, fieldIndex := range exportedFields {
			key := val.Type().Field(fieldIndex).Name
			ctx := val.Field(fieldIndex).Interface()

			// computes private data
			data := h.NewIterDataFrame(len(exportedFields), i, key)

			// evaluates block
			result += h.BlockWith(ctx, data, key)
		}
	}

	return result
}

func logHelper(h *HelperArg) interface{} {
	log.Print(h.ParamStr(0))
	return ""
}

func lookupHelper(h *HelperArg) interface{} {
	return Str(h.Eval(h.Param(0), h.ParamStr(1)))
}
<commit_msg>Cleanups helper.go code<commit_after>package raymond

import (
	"fmt"
	"log"
	"reflect"
)

// Arguments provided to helpers
type HelperArg struct {
	// evaluation visitor
	eval *EvalVisitor

	// params
	params []interface{}
	hash   map[string]interface{}
}

// Helper function: can return a string or a SafeString
type Helper func(h *HelperArg) interface{}

// All global helpers
var helpers map[string]Helper

func init() {
	helpers = make(map[string]Helper)

	// register builtin helpers
	RegisterHelper("if", ifHelper)
	RegisterHelper("unless", unlessHelper)
	RegisterHelper("with", withHelper)
	RegisterHelper("each", eachHelper)
	RegisterHelper("log", logHelper)
	RegisterHelper("lookup", lookupHelper)
}

// Registers a new global helper function
func RegisterHelper(name string, helper Helper) {
	if helpers[name] != nil {
		panic(fmt.Errorf("Helper already registered: %s", name))
	}

	helpers[name] = helper
}

// Find a registered global helper function
func FindHelper(name string) Helper {
	return helpers[name]
}

// Instanciates a new HelperArg
func NewHelperArg(eval *EvalVisitor, params []interface{}, hash map[string]interface{}) *HelperArg {
	return &HelperArg{
		eval:   eval,
		params: params,
		hash:   hash,
	}
}

// Instanciates a new empty HelperArg
func NewEmptyHelperArg(eval *EvalVisitor) *HelperArg {
	return &HelperArg{
		eval: eval,
		hash: make(map[string]interface{}),
	}
}

//
// Getters
//

// Returns all parameters
func (h *HelperArg) Params() []interface{} {
	return h.params
}

// Returns parameter at given position
func (h *HelperArg) Param(pos int) interface{} {
	if len(h.params) > pos {
		return h.params[pos]
	} else {
		return nil
	}
}

// Get string representation of parameter at given position
func (h *HelperArg) ParamStr(pos int) string {
	return Str(h.Param(pos))
}

// Returns hash value by name
func (h *HelperArg) Hash(name string) interface{} {
	return h.hash[name]
}

// Returns string representation of hash value by name
func (h *HelperArg) HashStr(name string) string {
	return Str(h.hash[name])
}

// Returns current evaluation context
func (h *HelperArg) Ctx() interface{} {
	return h.eval.curCtx()
}

// Returns current context field value
func (h *HelperArg) Field(name string) interface{} {
	value := h.eval.evalField(h.eval.curCtx(), name, false)
	if !value.IsValid() {
		return nil
	}

	return value.Interface()
}

// Get string representation of current context field value
func (h *HelperArg) FieldStr(name string) string {
	return Str(h.Field(name))
}

//
// Private data frame
//

// Get current private data frame
func (h *HelperArg) DataFrame() *DataFrame {
	return h.eval.dataFrame
}

// Instanciates a new data frame that is a copy of current one
func (h *HelperArg) NewDataFrame() *DataFrame {
	return h.eval.dataFrame.Copy()
}

// Instanciates a new data frame and set iteration specific vars
func (h *HelperArg) NewIterDataFrame(length int, i int, key interface{}) *DataFrame {
	return h.eval.dataFrame.NewIterDataFrame(length, i, key)
}

// Set current data frame
func (h *HelperArg) SetDataFrame(data *DataFrame) {
	h.eval.setDataFrame(data)
}

// Set back parent data frame
func (h *HelperArg) PopDataFrame() {
	h.eval.popDataFrame()
}

//
// Evaluation
//

// Evaluate block with given context, private data and iteration key
func (h *HelperArg) BlockWith(ctx interface{}, data *DataFrame, key interface{}) string {
	result := ""

	if block := h.eval.curBlock(); (block != nil) && (block.Program != nil) {
		result = h.eval.evalProgram(block.Program, ctx, data, key)
	}

	return result
}

// Evaluate block with given context
func (h *HelperArg) BlockWithCtx(ctx interface{}) string {
	return h.BlockWith(ctx, nil, nil)
}

// Evaluate block
func (h *HelperArg) Block() string {
	return h.BlockWith(nil, nil, nil)
}

// Evaluate inverse
func (h *HelperArg) Inverse() string {
	result := ""
	if block := h.eval.curBlock(); (block != nil) && (block.Inverse != nil) {
		result, _ = block.Inverse.Accept(h.eval).(string)
	}

	return result
}

// Evaluate field for given context
func (h *HelperArg) Eval(ctx interface{}, field string) interface{} {
	if ctx == nil {
		return nil
	}

	if field == "" {
		return nil
	}

	val := h.eval.evalField(reflect.ValueOf(ctx), field, false)
	if !val.IsValid() {
		return nil
	}

	return val.Interface()
}

//
// Misc
//

// Returns true if first param is truthy
func (h *HelperArg) TruthFirstParam() bool {
	val := h.Param(0)
	if val == nil {
		return false
	}

	thruth, ok := IsTruth(reflect.ValueOf(val))
	if !ok {
		return false
	}

	return thruth
}

// Returns true if 'includeZero' option is set and first param is the number 0
func (h *HelperArg) IsIncludableZero() bool {
	b, ok := h.Hash("includeZero").(bool)
	if ok && b {
		nb, ok := h.Param(0).(int)
		if ok && nb == 0 {
			return true
		}
	}

	return false
}

//
// Builtin helpers
//

func ifHelper(h *HelperArg) interface{} {
	if h.IsIncludableZero() || h.TruthFirstParam() {
		return h.Block()
	}

	return h.Inverse()
}

func unlessHelper(h *HelperArg) interface{} {
	if h.IsIncludableZero() || h.TruthFirstParam() {
		return h.Inverse()
	}

	return h.Block()
}

func withHelper(h *HelperArg) interface{} {
	if h.TruthFirstParam() {
		return h.BlockWithCtx(h.Param(0))
	}

	return h.Inverse()
}

func eachHelper(h *HelperArg) interface{} {
	if !h.TruthFirstParam() {
		h.Inverse()
		return ""
	}

	result := ""

	val := reflect.ValueOf(h.Param(0))
	switch val.Kind() {
	case reflect.Array, reflect.Slice:
		for i := 0; i < val.Len(); i++ {
			// computes private data
			data := h.NewIterDataFrame(val.Len(), i, nil)

			// evaluates block
			result += h.BlockWith(val.Index(i).Interface(), data, i)
		}
	case reflect.Map:
		// note: a go hash is not ordered, so result may vary, this behaviour differs from the JS implementation
		keys := val.MapKeys()
		for i := 0; i < len(keys); i++ {
			key := keys[i].Interface()
			ctx := val.MapIndex(keys[i]).Interface()

			// computes private data
			data := h.NewIterDataFrame(len(keys), i, key)

			// evaluates block
			result += h.BlockWith(ctx, data, key)
		}
	case reflect.Struct:
		var exportedFields []int

		// collect exported fields only
		for i := 0; i < val.NumField(); i++ {
			if tField := val.Type().Field(i); tField.PkgPath == "" {
				exportedFields = append(exportedFields, i)
			}
		}

		for i, fieldIndex := range exportedFields {
			key := val.Type().Field(fieldIndex).Name
			ctx := val.Field(fieldIndex).Interface()

			// computes private data
			data := h.NewIterDataFrame(len(exportedFields), i, key)

			// evaluates block
			result += h.BlockWith(ctx, data, key)
		}
	}

	return result
}

func logHelper(h *HelperArg) interface{} {
	log.Print(h.ParamStr(0))
	return ""
}

func lookupHelper(h *HelperArg) interface{} {
	return Str(h.Eval(h.Param(0), h.ParamStr(1)))
}
<|endoftext|>


<commit_before>// +build !linux,!freebsd,!netbsd,!darwin,!solaris

package xattr

import (
	"os"
)

// XATTR_SUPPORTED will be true if the current platform is supported
const XATTR_SUPPORTED = false

func getxattr(path string, name string, data []byte) (int, error) {
	return 0, nil
}

func lgetxattr(path string, name string, data []byte) (int, error) {
	return 0, nil
}

func fgetxattr(f *os.File, name string, data []byte) (int, error) {
	return 0, nil
}

func setxattr(path string, name string, data []byte, flags int) error {
	return nil
}

func lsetxattr(path string, name string, data []byte, flags int) error {
	return nil
}

func fsetxattr(f *os.File, name string, data []byte, flags int) error {
	return nil
}

func removexattr(path string, name string) error {
	return nil
}

func lremovexattr(path string, name string) error {
	return nil
}

func fremovexattr(f *os.File, name string) error {
	return nil
}

func listxattr(path string, data []byte) (int, error) {
	return 0, nil
}

func llistxattr(path string, data []byte) (int, error) {
	return 0, nil
}

func flistxattr(f *os.File, data []byte) (int, error) {
	return 0, nil
}

// dummy
func stringsFromByteSlice(buf []byte) (result []string) {
	return []string{}
}
<commit_msg>add alias to unsupported (#63)<commit_after>//go:build !linux && !freebsd && !netbsd && !darwin && !solaris
// +build !linux,!freebsd,!netbsd,!darwin,!solaris

package xattr

import (
	"os"
	"syscall"
)

const (
	// We need to use the default for non supported operating systems
	ENOATTR = syscall.ENODATA
)

// XATTR_SUPPORTED will be true if the current platform is supported
const XATTR_SUPPORTED = false

func getxattr(path string, name string, data []byte) (int, error) {
	return 0, nil
}

func lgetxattr(path string, name string, data []byte) (int, error) {
	return 0, nil
}

func fgetxattr(f *os.File, name string, data []byte) (int, error) {
	return 0, nil
}

func setxattr(path string, name string, data []byte, flags int) error {
	return nil
}

func lsetxattr(path string, name string, data []byte, flags int) error {
	return nil
}

func fsetxattr(f *os.File, name string, data []byte, flags int) error {
	return nil
}

func removexattr(path string, name string) error {
	return nil
}

func lremovexattr(path string, name string) error {
	return nil
}

func fremovexattr(f *os.File, name string) error {
	return nil
}

func listxattr(path string, data []byte) (int, error) {
	return 0, nil
}

func llistxattr(path string, data []byte) (int, error) {
	return 0, nil
}

func flistxattr(f *os.File, data []byte) (int, error) {
	return 0, nil
}

// dummy
func stringsFromByteSlice(buf []byte) (result []string) {
	return []string{}
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"strings"
	"testing"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/arn"
	"github.com/aws/aws-sdk-go/service/ssoadmin"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
	"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
	"github.com/terraform-providers/terraform-provider-aws/aws/internal/tfawsresource"
)

func TestAccAWSSSOPermissionSet_Basic(t *testing.T) {
	var permissionSet, updatedPermissionSet ssoadmin.PermissionSet
	resourceName := "aws_sso_permission_set.example"
	rName := acctest.RandomWithPrefix("tf-sso-test")

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t); testAccPreCheckAWSSSOInstance(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAWSSSOPermissionSetDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccSSOPermissionSetBasicConfig(rName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSSSOPermissionSetExists(resourceName, &permissionSet),
					resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "1"),
					tfawsresource.TestCheckTypeSetElemAttr(resourceName, "managed_policy_arns.*", "arn:aws:iam::aws:policy/ReadOnlyAccess"), // lintignore:AWSAT005
					resource.TestCheckResourceAttr(resourceName, "name", rName),
					resource.TestCheckResourceAttr(resourceName, "description", "Just a test"),
					resource.TestCheckResourceAttr(resourceName, "tags.%", "0"),
				),
			},
			{
				ResourceName:      resourceName,
				ImportState:       true,
				ImportStateVerify: true,
			},
			{
				Config: testAccSSOPermissionSetBasicConfigUpdated(rName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSSSOPermissionSetExists(resourceName, &updatedPermissionSet),
					resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "2"),
					tfawsresource.TestCheckTypeSetElemAttr(resourceName, "managed_policy_arns.*", "arn:aws:iam::aws:policy/ReadOnlyAccess"),              // lintignore:AWSAT005
					tfawsresource.TestCheckTypeSetElemAttr(resourceName, "managed_policy_arns.*", "arn:aws:iam::aws:policy/job-function/ViewOnlyAccess"), // lintignore:AWSAT005
					resource.TestCheckResourceAttr(resourceName, "name", rName),
					resource.TestCheckResourceAttr(resourceName, "description", "Just a test update"),
					resource.TestCheckResourceAttr(resourceName, "tags.%", "0"),
				),
			},
		},
	})
}

func TestAccAWSSSOPermissionSet_Disappears(t *testing.T) {
	var permissionSet ssoadmin.PermissionSet
	resourceName := "aws_sso_permission_set.example"
	rName := acctest.RandomWithPrefix("tf-sso-test")

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t); testAccPreCheckAWSSSOInstance(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAWSSSOPermissionSetDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccSSOPermissionSetBasicConfig(rName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSSSOPermissionSetExists(resourceName, &permissionSet),
					testAccCheckResourceDisappears(testAccProvider, resourceAwsSsoPermissionSet(), resourceName),
				),
				ExpectNonEmptyPlan: true,
			},
		},
	})
}

func TestAccAWSSSOPermissionSet_Tags(t *testing.T) {
	var permissionSet ssoadmin.PermissionSet
	resourceName := "aws_sso_permission_set.example"
	rName := acctest.RandomWithPrefix("tf-sso-test")

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t); testAccPreCheckAWSSSOInstance(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAWSSSOPermissionSetDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccSSOPermissionSetConfigTagsSingle(rName, "key1", "value1"),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSSSOPermissionSetExists(resourceName, &permissionSet),
					resource.TestCheckResourceAttr(resourceName, "tags.%", "1"),
					resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"),
				),
			},
			{
				ResourceName:      resourceName,
				ImportState:       true,
				ImportStateVerify: true,
			},
			{
				Config: testAccSSOPermissionSetConfigTagsMultiple(rName, "key1", "updatedvalue1", "key2", "value2"),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSSSOPermissionSetExists(resourceName, &permissionSet),
					resource.TestCheckResourceAttr(resourceName, "tags.%", "2"),
					resource.TestCheckResourceAttr(resourceName, "tags.key1", "updatedvalue1"),
					resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"),
				),
			},
			{
				Config: testAccSSOPermissionSetConfigTagsSingle(rName, "key2", "value2"),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSSSOPermissionSetExists(resourceName, &permissionSet),
					resource.TestCheckResourceAttr(resourceName, "tags.%", "1"),
					resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"),
				),
			},
		},
	})
}

func testAccCheckAWSSSOPermissionSetExists(resourceName string, permissionSet *ssoadmin.PermissionSet) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[resourceName]
		if !ok {
			return fmt.Errorf("Not found: %s", resourceName)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("Resource (%s) ID not set", resourceName)
		}

		instanceArn, err := resourceAwsSsoPermissionSetParseID(rs.Primary.ID)

		if err != nil {
			return err
		}

		ssoadminconn := testAccProvider.Meta().(*AWSClient).ssoadminconn

		permissionSetResp, permissionSetErr := ssoadminconn.DescribePermissionSet(&ssoadmin.DescribePermissionSetInput{
			InstanceArn:      aws.String(instanceArn),
			PermissionSetArn: aws.String(rs.Primary.ID),
		})

		if permissionSetErr != nil {
			return permissionSetErr
		}

		if *permissionSetResp.PermissionSet.PermissionSetArn == rs.Primary.ID {
			*permissionSet = *permissionSetResp.PermissionSet
			return nil
		}

		return fmt.Errorf("AWS SSO Permission Set (%s) not found", rs.Primary.ID)
	}
}

func testAccCheckAWSSSOPermissionSetDestroy(s *terraform.State) error {
	ssoadminconn := testAccProvider.Meta().(*AWSClient).ssoadminconn

	for _, rs := range s.RootModule().Resources {
		if rs.Type != "aws_sso_permission_set" {
			continue
		}

		idFormatErr := fmt.Errorf("Unexpected format of ARN (%s), expected arn:${Partition}:sso:::permissionSet/${InstanceId}/${PermissionSetId}", rs.Primary.ID)
		permissionSetArn, err := arn.Parse(rs.Primary.ID)
		if err != nil {
			return err
		}

		resourceParts := strings.Split(permissionSetArn.Resource, "/")
		if len(resourceParts) != 3 || resourceParts[0] != "permissionSet" || resourceParts[1] == "" || resourceParts[2] == "" {
			return idFormatErr
		}

		// resourceParts = ["permissionSet","ins-123456A", "ps-56789B"]
		instanceArn := arn.ARN{
			Partition: permissionSetArn.Partition,
			Service:   permissionSetArn.Service,
			Resource:  fmt.Sprintf("instance/%s", resourceParts[1]),
		}.String()

		input := &ssoadmin.DescribePermissionSetInput{
			InstanceArn:      aws.String(instanceArn),
			PermissionSetArn: aws.String(rs.Primary.ID),
		}

		output, err := ssoadminconn.DescribePermissionSet(input)

		if tfawserr.ErrCodeEquals(err, ssoadmin.ErrCodeResourceNotFoundException) {
			continue
		}

		if err != nil {
			return err
		}

		if output != nil {
			return fmt.Errorf("AWS SSO Permission Set (%s) still exists", rs.Primary.ID)
		}
	}

	return nil
}

func testAccSSOPermissionSetBasicConfig(rName string) string {
	return fmt.Sprintf(`
data "aws_sso_instance" "selected" {}

resource "aws_sso_permission_set" "example" {
  name                = "%s"
  description         = "Just a test"
  instance_arn        = data.aws_sso_instance.selected.arn
  managed_policy_arns = ["arn:aws:iam::aws:policy/ReadOnlyAccess"]
}
`, rName) // lintignore:AWSAT005
}

func testAccSSOPermissionSetBasicConfigUpdated(rName string) string {
	return fmt.Sprintf(`
data "aws_sso_instance" "selected" {}

resource "aws_sso_permission_set" "example" {
  name         = "%s"
  description  = "Just a test update"
  instance_arn = data.aws_sso_instance.selected.arn
  managed_policy_arns = [
    "arn:aws:iam::aws:policy/ReadOnlyAccess",
    "arn:aws:iam::aws:policy/job-function/ViewOnlyAccess"
  ]
}
`, rName) // lintignore:AWSAT005
}

func testAccSSOPermissionSetConfigTagsSingle(rName, tagKey1, tagValue1 string) string {
	return fmt.Sprintf(`
data "aws_sso_instance" "selected" {}

resource "aws_sso_permission_set" "example" {
  name                = "%s"
  description         = "Just a test"
  instance_arn        = data.aws_sso_instance.selected.arn
  managed_policy_arns = ["arn:aws:iam::aws:policy/ReadOnlyAccess"]

  tags = {
    %[2]q = %[3]q
  }
}
`, rName, tagKey1, tagValue1) // lintignore:AWSAT005
}

func testAccSSOPermissionSetConfigTagsMultiple(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string {
	return fmt.Sprintf(`
data "aws_sso_instance" "selected" {}

resource "aws_sso_permission_set" "example" {
  name                = "%s"
  description         = "Just a test"
  instance_arn        = data.aws_sso_instance.selected.arn
  managed_policy_arns = ["arn:aws:iam::aws:policy/ReadOnlyAccess"]

  tags = {
    %[2]q = %[3]q
    %[4]q = %[5]q
  }
}
`, rName, tagKey1, tagValue1, tagKey2, tagValue2) // lintignore:AWSAT005
}
<commit_msg>Update aws/resource_aws_sso_permission_set_test.go<commit_after>package aws

import (
	"fmt"
	"strings"
	"testing"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/arn"
	"github.com/aws/aws-sdk-go/service/ssoadmin"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
	"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
	"github.com/terraform-providers/terraform-provider-aws/aws/internal/tfawsresource"
)

func TestAccAWSSSOPermissionSet_Basic(t *testing.T) {
	var permissionSet, updatedPermissionSet ssoadmin.PermissionSet
	resourceName := "aws_sso_permission_set.example"
	rName := acctest.RandomWithPrefix("tf-sso-test")

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t); testAccPreCheckAWSSSOInstance(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAWSSSOPermissionSetDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccSSOPermissionSetBasicConfig(rName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSSSOPermissionSetExists(resourceName, &permissionSet),
					resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "1"),
					tfawsresource.TestCheckTypeSetElemAttr(resourceName, "managed_policy_arns.*", "arn:aws:iam::aws:policy/ReadOnlyAccess"), // lintignore:AWSAT005
					resource.TestCheckResourceAttr(resourceName, "name", rName),
					resource.TestCheckResourceAttr(resourceName, "description", "Just a test"),
					resource.TestCheckResourceAttr(resourceName, "tags.%", "0"),
				),
			},
			{
				ResourceName:      resourceName,
				ImportState:       true,
				ImportStateVerify: true,
			},
			{
				Config: testAccSSOPermissionSetBasicConfigUpdated(rName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSSSOPermissionSetExists(resourceName, &updatedPermissionSet),
					resource.TestCheckResourceAttr(resourceName, "managed_policy_arns.#", "2"),
					tfawsresource.TestCheckTypeSetElemAttr(resourceName, "managed_policy_arns.*", "arn:aws:iam::aws:policy/ReadOnlyAccess"),              // lintignore:AWSAT005
					tfawsresource.TestCheckTypeSetElemAttr(resourceName, "managed_policy_arns.*", "arn:aws:iam::aws:policy/job-function/ViewOnlyAccess"), // lintignore:AWSAT005
					resource.TestCheckResourceAttr(resourceName, "name", rName),
					resource.TestCheckResourceAttr(resourceName, "description", "Just a test update"),
					resource.TestCheckResourceAttr(resourceName, "tags.%", "0"),
				),
			},
		},
	})
}

func TestAccAWSSSOPermissionSet_Disappears(t *testing.T) {
	var permissionSet ssoadmin.PermissionSet
	resourceName := "aws_sso_permission_set.example"
	rName := acctest.RandomWithPrefix("tf-sso-test")

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t); testAccPreCheckAWSSSOInstance(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAWSSSOPermissionSetDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccSSOPermissionSetBasicConfig(rName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSSSOPermissionSetExists(resourceName, &permissionSet),
					testAccCheckResourceDisappears(testAccProvider, resourceAwsSsoPermissionSet(), resourceName),
				),
				ExpectNonEmptyPlan: true,
			},
		},
	})
}

func TestAccAWSSSOPermissionSet_Tags(t *testing.T) {
	var permissionSet ssoadmin.PermissionSet
	resourceName := "aws_sso_permission_set.example"
	rName := acctest.RandomWithPrefix("tf-sso-test")

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t); testAccPreCheckAWSSSOInstance(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAWSSSOPermissionSetDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccSSOPermissionSetConfigTagsSingle(rName, "key1", "value1"),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSSSOPermissionSetExists(resourceName, &permissionSet),
					resource.TestCheckResourceAttr(resourceName, "tags.%", "1"),
					resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"),
				),
			},
			{
				ResourceName:      resourceName,
				ImportState:       true,
				ImportStateVerify: true,
			},
			{
				Config: testAccSSOPermissionSetConfigTagsMultiple(rName, "key1", "updatedvalue1", "key2", "value2"),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSSSOPermissionSetExists(resourceName, &permissionSet),
					resource.TestCheckResourceAttr(resourceName, "tags.%", "2"),
					resource.TestCheckResourceAttr(resourceName, "tags.key1", "updatedvalue1"),
					resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"),
				),
			},
			{
				Config: testAccSSOPermissionSetConfigTagsSingle(rName, "key2", "value2"),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSSSOPermissionSetExists(resourceName, &permissionSet),
					resource.TestCheckResourceAttr(resourceName, "tags.%", "1"),
					resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"),
				),
			},
		},
	})
}

func testAccCheckAWSSSOPermissionSetExists(resourceName string, permissionSet *ssoadmin.PermissionSet) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[resourceName]
		if !ok {
			return fmt.Errorf("Not found: %s", resourceName)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("Resource (%s) ID not set", resourceName)
		}

		instanceArn, err := resourceAwsSsoPermissionSetParseID(rs.Primary.ID)

		if err != nil {
			return err
		}

		ssoadminconn := testAccProvider.Meta().(*AWSClient).ssoadminconn

		permissionSetResp, permissionSetErr := ssoadminconn.DescribePermissionSet(&ssoadmin.DescribePermissionSetInput{
			InstanceArn:      aws.String(instanceArn),
			PermissionSetArn: aws.String(rs.Primary.ID),
		})

		if permissionSetErr != nil {
			return permissionSetErr
		}

		if permissionSetResp != nil {
		    if arn := aws.StringValue(permissionStRespPermissionSet.PermissionSetArn); arn == rs.Primary.ID {
			return nil
		    }

		return fmt.Errorf("AWS SSO Permission Set (%s) not found", rs.Primary.ID)
	}
}

func testAccCheckAWSSSOPermissionSetDestroy(s *terraform.State) error {
	ssoadminconn := testAccProvider.Meta().(*AWSClient).ssoadminconn

	for _, rs := range s.RootModule().Resources {
		if rs.Type != "aws_sso_permission_set" {
			continue
		}

		idFormatErr := fmt.Errorf("Unexpected format of ARN (%s), expected arn:${Partition}:sso:::permissionSet/${InstanceId}/${PermissionSetId}", rs.Primary.ID)
		permissionSetArn, err := arn.Parse(rs.Primary.ID)
		if err != nil {
			return err
		}

		resourceParts := strings.Split(permissionSetArn.Resource, "/")
		if len(resourceParts) != 3 || resourceParts[0] != "permissionSet" || resourceParts[1] == "" || resourceParts[2] == "" {
			return idFormatErr
		}

		// resourceParts = ["permissionSet","ins-123456A", "ps-56789B"]
		instanceArn := arn.ARN{
			Partition: permissionSetArn.Partition,
			Service:   permissionSetArn.Service,
			Resource:  fmt.Sprintf("instance/%s", resourceParts[1]),
		}.String()

		input := &ssoadmin.DescribePermissionSetInput{
			InstanceArn:      aws.String(instanceArn),
			PermissionSetArn: aws.String(rs.Primary.ID),
		}

		output, err := ssoadminconn.DescribePermissionSet(input)

		if tfawserr.ErrCodeEquals(err, ssoadmin.ErrCodeResourceNotFoundException) {
			continue
		}

		if err != nil {
			return err
		}

		if output != nil {
			return fmt.Errorf("AWS SSO Permission Set (%s) still exists", rs.Primary.ID)
		}
	}

	return nil
}

func testAccSSOPermissionSetBasicConfig(rName string) string {
	return fmt.Sprintf(`
data "aws_sso_instance" "selected" {}

resource "aws_sso_permission_set" "example" {
  name                = "%s"
  description         = "Just a test"
  instance_arn        = data.aws_sso_instance.selected.arn
  managed_policy_arns = ["arn:aws:iam::aws:policy/ReadOnlyAccess"]
}
`, rName) // lintignore:AWSAT005
}

func testAccSSOPermissionSetBasicConfigUpdated(rName string) string {
	return fmt.Sprintf(`
data "aws_sso_instance" "selected" {}

resource "aws_sso_permission_set" "example" {
  name         = "%s"
  description  = "Just a test update"
  instance_arn = data.aws_sso_instance.selected.arn
  managed_policy_arns = [
    "arn:aws:iam::aws:policy/ReadOnlyAccess",
    "arn:aws:iam::aws:policy/job-function/ViewOnlyAccess"
  ]
}
`, rName) // lintignore:AWSAT005
}

func testAccSSOPermissionSetConfigTagsSingle(rName, tagKey1, tagValue1 string) string {
	return fmt.Sprintf(`
data "aws_sso_instance" "selected" {}

resource "aws_sso_permission_set" "example" {
  name                = "%s"
  description         = "Just a test"
  instance_arn        = data.aws_sso_instance.selected.arn
  managed_policy_arns = ["arn:aws:iam::aws:policy/ReadOnlyAccess"]

  tags = {
    %[2]q = %[3]q
  }
}
`, rName, tagKey1, tagValue1) // lintignore:AWSAT005
}

func testAccSSOPermissionSetConfigTagsMultiple(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string {
	return fmt.Sprintf(`
data "aws_sso_instance" "selected" {}

resource "aws_sso_permission_set" "example" {
  name                = "%s"
  description         = "Just a test"
  instance_arn        = data.aws_sso_instance.selected.arn
  managed_policy_arns = ["arn:aws:iam::aws:policy/ReadOnlyAccess"]

  tags = {
    %[2]q = %[3]q
    %[4]q = %[5]q
  }
}
`, rName, tagKey1, tagValue1, tagKey2, tagValue2) // lintignore:AWSAT005
}
<|endoftext|>


<commit_before>// Copyright 2013 Michael Yang. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package id3

import (
	"io"
)

var (
	// V3FrameTypeMap specifies the frame IDs and constructors allowed in ID3v2.3
	V3FrameTypeMap = map[string]FrameType{
		"AENC": FrameType{id: "AENC", description: "Audio encryption", constructor: NewDataFrame},
		"APIC": FrameType{id: "APIC", description: "Attached picture", constructor: NewImageFrame},
		"COMM": FrameType{id: "COMM", description: "Comments", constructor: NewDataFrame},
		"COMR": FrameType{id: "COMR", description: "Commercial frame", constructor: NewDataFrame},
		"ENCR": FrameType{id: "ENCR", description: "Encryption method registration", constructor: NewDataFrame},
		"EQUA": FrameType{id: "EQUA", description: "Equalization", constructor: NewDataFrame},
		"ETCO": FrameType{id: "ETCO", description: "Event timing codes", constructor: NewDataFrame},
		"GEOB": FrameType{id: "GEOB", description: "General encapsulated object", constructor: NewDataFrame},
		"GRID": FrameType{id: "GRID", description: "Group identification registration", constructor: NewDataFrame},
		"IPLS": FrameType{id: "IPLS", description: "Involved people list", constructor: NewDataFrame},
		"LINK": FrameType{id: "LINK", description: "Linked information", constructor: NewDataFrame},
		"MCDI": FrameType{id: "MCDI", description: "Music CD identifier", constructor: NewDataFrame},
		"MLLT": FrameType{id: "MLLT", description: "MPEG location lookup table", constructor: NewDataFrame},
		"OWNE": FrameType{id: "OWNE", description: "Ownership frame", constructor: NewDataFrame},
		"PRIV": FrameType{id: "PRIV", description: "Private frame", constructor: NewDataFrame},
		"PCNT": FrameType{id: "PCNT", description: "Play counter", constructor: NewDataFrame},
		"POPM": FrameType{id: "POPM", description: "Popularimeter", constructor: NewDataFrame},
		"POSS": FrameType{id: "POSS", description: "Position synchronisation frame", constructor: NewDataFrame},
		"RBUF": FrameType{id: "RBUF", description: "Recommended buffer size", constructor: NewDataFrame},
		"RVAD": FrameType{id: "RVAD", description: "Relative volume adjustment", constructor: NewDataFrame},
		"RVRB": FrameType{id: "RVRB", description: "Reverb", constructor: NewDataFrame},
		"SYLT": FrameType{id: "SYLT", description: "Synchronized lyric/text", constructor: NewDataFrame},
		"SYTC": FrameType{id: "SYTC", description: "Synchronized tempo codes", constructor: NewDataFrame},
		"TALB": FrameType{id: "TALB", description: "Album/Movie/Show title", constructor: NewTextFrame},
		"TBPM": FrameType{id: "TBPM", description: "BPM (beats per minute)", constructor: NewTextFrame},
		"TCOM": FrameType{id: "TCOM", description: "Composer", constructor: NewTextFrame},
		"TCON": FrameType{id: "TCON", description: "Content type", constructor: NewTextFrame},
		"TCOP": FrameType{id: "TCOP", description: "Copyright message", constructor: NewTextFrame},
		"TDAT": FrameType{id: "TDAT", description: "Date", constructor: NewTextFrame},
		"TDLY": FrameType{id: "TDLY", description: "Playlist delay", constructor: NewTextFrame},
		"TENC": FrameType{id: "TENC", description: "Encoded by", constructor: NewTextFrame},
		"TEXT": FrameType{id: "TEXT", description: "Lyricist/Text writer", constructor: NewTextFrame},
		"TFLT": FrameType{id: "TFLT", description: "File type", constructor: NewTextFrame},
		"TIME": FrameType{id: "TIME", description: "Time", constructor: NewTextFrame},
		"TIT1": FrameType{id: "TIT1", description: "Content group description", constructor: NewTextFrame},
		"TIT2": FrameType{id: "TIT2", description: "Title/songname/content description", constructor: NewTextFrame},
		"TIT3": FrameType{id: "TIT3", description: "Subtitle/Description refinement", constructor: NewTextFrame},
		"TKEY": FrameType{id: "TKEY", description: "Initial key", constructor: NewTextFrame},
		"TLAN": FrameType{id: "TLAN", description: "Language(s)", constructor: NewTextFrame},
		"TLEN": FrameType{id: "TLEN", description: "Length", constructor: NewTextFrame},
		"TMED": FrameType{id: "TMED", description: "Media type", constructor: NewTextFrame},
		"TOAL": FrameType{id: "TOAL", description: "Original album/movie/show title", constructor: NewTextFrame},
		"TOFN": FrameType{id: "TOFN", description: "Original filename", constructor: NewTextFrame},
		"TOLY": FrameType{id: "TOLY", description: "Original lyricist(s)/text writer(s)", constructor: NewTextFrame},
		"TOPE": FrameType{id: "TOPE", description: "Original artist(s)/performer(s)", constructor: NewTextFrame},
		"TORY": FrameType{id: "TORY", description: "Original release year", constructor: NewTextFrame},
		"TOWN": FrameType{id: "TOWN", description: "File owner/licensee", constructor: NewTextFrame},
		"TPE1": FrameType{id: "TPE1", description: "Lead performer(s)/Soloist(s)", constructor: NewTextFrame},
		"TPE2": FrameType{id: "TPE2", description: "Band/orchestra/accompaniment", constructor: NewTextFrame},
		"TPE3": FrameType{id: "TPE3", description: "Conductor/performer refinement", constructor: NewTextFrame},
		"TPE4": FrameType{id: "TPE4", description: "Interpreted, remixed, or otherwise modified by", constructor: NewTextFrame},
		"TPOS": FrameType{id: "TPOS", description: "Part of a set", constructor: NewTextFrame},
		"TPUB": FrameType{id: "TPUB", description: "Publisher", constructor: NewTextFrame},
		"TRCK": FrameType{id: "TRCK", description: "Track number/Position in set", constructor: NewTextFrame},
		"TRDA": FrameType{id: "TRDA", description: "Recording dates", constructor: NewTextFrame},
		"TRSN": FrameType{id: "TRSN", description: "Internet radio station name", constructor: NewTextFrame},
		"TRSO": FrameType{id: "TRSO", description: "Internet radio station owner", constructor: NewTextFrame},
		"TSIZ": FrameType{id: "TSIZ", description: "Size", constructor: NewTextFrame},
		"TSRC": FrameType{id: "TSRC", description: "ISRC (international standard recording code)", constructor: NewTextFrame},
		"TSSE": FrameType{id: "TSSE", description: "Software/Hardware and settings used for encoding", constructor: NewTextFrame},
		"TYER": FrameType{id: "TYER", description: "Year", constructor: NewTextFrame},
		"TXXX": FrameType{id: "TXXX", description: "User defined text information frame", constructor: NewDescTextFrame},
		"UFID": FrameType{id: "UFID", description: "Unique file identifier", constructor: NewDataFrame},
		"USER": FrameType{id: "USER", description: "Terms of use", constructor: NewDataFrame},
		"USLT": FrameType{id: "USLT", description: "Unsychronized lyric/text transcription", constructor: NewUnsynchTextFrame},
		"WCOM": FrameType{id: "WCOM", description: "Commercial information", constructor: NewDataFrame},
		"WCOP": FrameType{id: "WCOP", description: "Copyright/Legal information", constructor: NewDataFrame},
		"WOAF": FrameType{id: "WOAF", description: "Official audio file webpage", constructor: NewDataFrame},
		"WOAR": FrameType{id: "WOAR", description: "Official artist/performer webpage", constructor: NewDataFrame},
		"WOAS": FrameType{id: "WOAS", description: "Official audio source webpage", constructor: NewDataFrame},
		"WORS": FrameType{id: "WORS", description: "Official internet radio station homepage", constructor: NewDataFrame},
		"WPAY": FrameType{id: "WPAY", description: "Payment", constructor: NewDataFrame},
		"WPUB": FrameType{id: "WPUB", description: "Publishers official webpage", constructor: NewDataFrame},
		"WXXX": FrameType{id: "WXXX", description: "User defined URL link frame", constructor: NewDataFrame},
	}
)

func NewV3Frame(reader io.Reader) Framer {
	data := make([]byte, FrameHeaderSize)
	if n, err := io.ReadFull(reader, data); n < FrameHeaderSize || err != nil {
		return nil
	}

	id := string(data[:4])
	t, ok := V3FrameTypeMap[id]
	if !ok {
		return nil
	}

	size, err := normint(data[4:8])
	if err != nil {
		return nil
	}

	h := FrameHead{
		FrameType:   t,
		statusFlags: data[8],
		formatFlags: data[9],
		size:        size,
	}

	frameData := make([]byte, size)
	if n, err := io.ReadFull(reader, frameData); n < int(size) || err != nil {
		return nil
	}

	return t.constructor(h, frameData)
}

func V3Bytes(f Framer) []byte {
	headBytes := make([]byte, FrameHeaderSize)

	copy(headBytes[:4], []byte(f.Id()))
	copy(headBytes[4:8], normbytes(int32(f.Size())))
	headBytes[8] = f.StatusFlags()
	headBytes[9] = f.FormatFlags()

	return append(headBytes, f.Bytes()...)
}
<commit_msg>Add v2.2 to v2.3 deprecation map<commit_after>// Copyright 2013 Michael Yang. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package id3

import (
	"io"
)

var (
	// V3DeprecatedTypeMap contains deprecated frame IDs from ID3v2.2
	V3DeprecatedTypeMap = map[string]string{
		"BUF": "RBUF", "COM": "COMM", "CRA": "AENC", "EQU": "EQUA",
		"ETC": "ETCO", "GEO": "GEOB", "MCI": "MCDI", "MLL": "MLLT",
		"PIC": "APIC", "POP": "POPM", "REV": "RVRB", "RVA": "RVAD",
		"SLT": "SYLT", "STC": "SYTC", "TAL": "TALB", "TBP": "TBPM",
		"TCM": "TCOM", "TCO": "TCON", "TCR": "TCOP", "TDA": "TDAT",
		"TDY": "TDLY", "TEN": "TENC", "TFT": "TFLT", "TIM": "TIME",
		"TKE": "TKEY", "TLA": "TLAN", "TLE": "TLEN", "TMT": "TMED",
		"TOA": "TOPE", "TOF": "TOFN", "TOL": "TOLY", "TOR": "TORY",
		"TOT": "TOAL", "TP1": "TPE1", "TP2": "TPE2", "TP3": "TPE3",
		"TP4": "TPE4", "TPA": "TPOS", "TPB": "TPUB", "TRC": "TSRC",
		"TRD": "TRDA", "TRK": "TRCK", "TSI": "TSIZ", "TSS": "TSSE",
		"TT1": "TIT1", "TT2": "TIT2", "TT3": "TIT3", "TXT": "TEXT",
		"TXX": "TXXX", "TYE": "TYER", "UFI": "UFID", "ULT": "USLT",
		"WAF": "WOAF", "WAR": "WOAR", "WAS": "WOAS", "WCM": "WCOM",
		"WCP": "WCOP", "WPB": "WPB", "WXX": "WXXX",
	}

	// V3FrameTypeMap specifies the frame IDs and constructors allowed in ID3v2.3
	V3FrameTypeMap = map[string]FrameType{
		"AENC": FrameType{id: "AENC", description: "Audio encryption", constructor: NewDataFrame},
		"APIC": FrameType{id: "APIC", description: "Attached picture", constructor: NewImageFrame},
		"COMM": FrameType{id: "COMM", description: "Comments", constructor: NewDataFrame},
		"COMR": FrameType{id: "COMR", description: "Commercial frame", constructor: NewDataFrame},
		"ENCR": FrameType{id: "ENCR", description: "Encryption method registration", constructor: NewDataFrame},
		"EQUA": FrameType{id: "EQUA", description: "Equalization", constructor: NewDataFrame},
		"ETCO": FrameType{id: "ETCO", description: "Event timing codes", constructor: NewDataFrame},
		"GEOB": FrameType{id: "GEOB", description: "General encapsulated object", constructor: NewDataFrame},
		"GRID": FrameType{id: "GRID", description: "Group identification registration", constructor: NewDataFrame},
		"IPLS": FrameType{id: "IPLS", description: "Involved people list", constructor: NewDataFrame},
		"LINK": FrameType{id: "LINK", description: "Linked information", constructor: NewDataFrame},
		"MCDI": FrameType{id: "MCDI", description: "Music CD identifier", constructor: NewDataFrame},
		"MLLT": FrameType{id: "MLLT", description: "MPEG location lookup table", constructor: NewDataFrame},
		"OWNE": FrameType{id: "OWNE", description: "Ownership frame", constructor: NewDataFrame},
		"PRIV": FrameType{id: "PRIV", description: "Private frame", constructor: NewDataFrame},
		"PCNT": FrameType{id: "PCNT", description: "Play counter", constructor: NewDataFrame},
		"POPM": FrameType{id: "POPM", description: "Popularimeter", constructor: NewDataFrame},
		"POSS": FrameType{id: "POSS", description: "Position synchronisation frame", constructor: NewDataFrame},
		"RBUF": FrameType{id: "RBUF", description: "Recommended buffer size", constructor: NewDataFrame},
		"RVAD": FrameType{id: "RVAD", description: "Relative volume adjustment", constructor: NewDataFrame},
		"RVRB": FrameType{id: "RVRB", description: "Reverb", constructor: NewDataFrame},
		"SYLT": FrameType{id: "SYLT", description: "Synchronized lyric/text", constructor: NewDataFrame},
		"SYTC": FrameType{id: "SYTC", description: "Synchronized tempo codes", constructor: NewDataFrame},
		"TALB": FrameType{id: "TALB", description: "Album/Movie/Show title", constructor: NewTextFrame},
		"TBPM": FrameType{id: "TBPM", description: "BPM (beats per minute)", constructor: NewTextFrame},
		"TCOM": FrameType{id: "TCOM", description: "Composer", constructor: NewTextFrame},
		"TCON": FrameType{id: "TCON", description: "Content type", constructor: NewTextFrame},
		"TCOP": FrameType{id: "TCOP", description: "Copyright message", constructor: NewTextFrame},
		"TDAT": FrameType{id: "TDAT", description: "Date", constructor: NewTextFrame},
		"TDLY": FrameType{id: "TDLY", description: "Playlist delay", constructor: NewTextFrame},
		"TENC": FrameType{id: "TENC", description: "Encoded by", constructor: NewTextFrame},
		"TEXT": FrameType{id: "TEXT", description: "Lyricist/Text writer", constructor: NewTextFrame},
		"TFLT": FrameType{id: "TFLT", description: "File type", constructor: NewTextFrame},
		"TIME": FrameType{id: "TIME", description: "Time", constructor: NewTextFrame},
		"TIT1": FrameType{id: "TIT1", description: "Content group description", constructor: NewTextFrame},
		"TIT2": FrameType{id: "TIT2", description: "Title/songname/content description", constructor: NewTextFrame},
		"TIT3": FrameType{id: "TIT3", description: "Subtitle/Description refinement", constructor: NewTextFrame},
		"TKEY": FrameType{id: "TKEY", description: "Initial key", constructor: NewTextFrame},
		"TLAN": FrameType{id: "TLAN", description: "Language(s)", constructor: NewTextFrame},
		"TLEN": FrameType{id: "TLEN", description: "Length", constructor: NewTextFrame},
		"TMED": FrameType{id: "TMED", description: "Media type", constructor: NewTextFrame},
		"TOAL": FrameType{id: "TOAL", description: "Original album/movie/show title", constructor: NewTextFrame},
		"TOFN": FrameType{id: "TOFN", description: "Original filename", constructor: NewTextFrame},
		"TOLY": FrameType{id: "TOLY", description: "Original lyricist(s)/text writer(s)", constructor: NewTextFrame},
		"TOPE": FrameType{id: "TOPE", description: "Original artist(s)/performer(s)", constructor: NewTextFrame},
		"TORY": FrameType{id: "TORY", description: "Original release year", constructor: NewTextFrame},
		"TOWN": FrameType{id: "TOWN", description: "File owner/licensee", constructor: NewTextFrame},
		"TPE1": FrameType{id: "TPE1", description: "Lead performer(s)/Soloist(s)", constructor: NewTextFrame},
		"TPE2": FrameType{id: "TPE2", description: "Band/orchestra/accompaniment", constructor: NewTextFrame},
		"TPE3": FrameType{id: "TPE3", description: "Conductor/performer refinement", constructor: NewTextFrame},
		"TPE4": FrameType{id: "TPE4", description: "Interpreted, remixed, or otherwise modified by", constructor: NewTextFrame},
		"TPOS": FrameType{id: "TPOS", description: "Part of a set", constructor: NewTextFrame},
		"TPUB": FrameType{id: "TPUB", description: "Publisher", constructor: NewTextFrame},
		"TRCK": FrameType{id: "TRCK", description: "Track number/Position in set", constructor: NewTextFrame},
		"TRDA": FrameType{id: "TRDA", description: "Recording dates", constructor: NewTextFrame},
		"TRSN": FrameType{id: "TRSN", description: "Internet radio station name", constructor: NewTextFrame},
		"TRSO": FrameType{id: "TRSO", description: "Internet radio station owner", constructor: NewTextFrame},
		"TSIZ": FrameType{id: "TSIZ", description: "Size", constructor: NewTextFrame},
		"TSRC": FrameType{id: "TSRC", description: "ISRC (international standard recording code)", constructor: NewTextFrame},
		"TSSE": FrameType{id: "TSSE", description: "Software/Hardware and settings used for encoding", constructor: NewTextFrame},
		"TYER": FrameType{id: "TYER", description: "Year", constructor: NewTextFrame},
		"TXXX": FrameType{id: "TXXX", description: "User defined text information frame", constructor: NewDescTextFrame},
		"UFID": FrameType{id: "UFID", description: "Unique file identifier", constructor: NewDataFrame},
		"USER": FrameType{id: "USER", description: "Terms of use", constructor: NewDataFrame},
		"USLT": FrameType{id: "USLT", description: "Unsychronized lyric/text transcription", constructor: NewUnsynchTextFrame},
		"WCOM": FrameType{id: "WCOM", description: "Commercial information", constructor: NewDataFrame},
		"WCOP": FrameType{id: "WCOP", description: "Copyright/Legal information", constructor: NewDataFrame},
		"WOAF": FrameType{id: "WOAF", description: "Official audio file webpage", constructor: NewDataFrame},
		"WOAR": FrameType{id: "WOAR", description: "Official artist/performer webpage", constructor: NewDataFrame},
		"WOAS": FrameType{id: "WOAS", description: "Official audio source webpage", constructor: NewDataFrame},
		"WORS": FrameType{id: "WORS", description: "Official internet radio station homepage", constructor: NewDataFrame},
		"WPAY": FrameType{id: "WPAY", description: "Payment", constructor: NewDataFrame},
		"WPUB": FrameType{id: "WPUB", description: "Publishers official webpage", constructor: NewDataFrame},
		"WXXX": FrameType{id: "WXXX", description: "User defined URL link frame", constructor: NewDataFrame},
	}
)

func NewV3Frame(reader io.Reader) Framer {
	data := make([]byte, FrameHeaderSize)
	if n, err := io.ReadFull(reader, data); n < FrameHeaderSize || err != nil {
		return nil
	}

	id := string(data[:4])
	t, ok := V3FrameTypeMap[id]
	if !ok {
		return nil
	}

	size, err := normint(data[4:8])
	if err != nil {
		return nil
	}

	h := FrameHead{
		FrameType:   t,
		statusFlags: data[8],
		formatFlags: data[9],
		size:        size,
	}

	frameData := make([]byte, size)
	if n, err := io.ReadFull(reader, frameData); n < int(size) || err != nil {
		return nil
	}

	return t.constructor(h, frameData)
}

func V3Bytes(f Framer) []byte {
	headBytes := make([]byte, FrameHeaderSize)

	copy(headBytes[:4], []byte(f.Id()))
	copy(headBytes[4:8], normbytes(int32(f.Size())))
	headBytes[8] = f.StatusFlags()
	headBytes[9] = f.FormatFlags()

	return append(headBytes, f.Bytes()...)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"text/tabwriter"
	"time"

	"github.com/docker/docker/pkg/units"
)

var cmdImages = &Command{
	Exec:        runImages,
	UsageLine:   "images [OPTIONS]",
	Description: "List images",
	Help:        "List images.",
}

func init() {
	// FIXME: -h
	cmdImages.Flag.BoolVar(&imagesA, []string{"a", "-all"}, false, "Show all iamges")
	cmdImages.Flag.BoolVar(&imagesNoTrunc, []string{"-no-trunc"}, false, "Don't truncate output")
	cmdImages.Flag.BoolVar(&imagesQ, []string{"q", "-quiet"}, false, "Only show numeric IDs")
}

// Flags
var imagesA bool       // -a flag
var imagesQ bool       // -q flag
var imagesNoTrunc bool // -no-trunc flag

func runImages(cmd *Command, args []string) {
	images, err := cmd.API.GetImages()
	if err != nil {
		fmt.Fprintf(os.Stderr, "unable to fetch images from the Scaleway API: %v\n", err)
		os.Exit(1)
	}

	w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
	defer w.Flush()
	if !imagesQ {
		fmt.Fprintf(w, "REPOSITORY\tTAG\tIMAGE ID\tCREATED\tVIRTUAL SIZE\n")
	}
	for _, image := range *images {
		if imagesQ {
			fmt.Fprintf(w, "%s\n", image.Identifier)
		} else {
			tag := "latest"
			virtualSize := units.HumanSize(image.RootVolume.Size)
			short_id := truncIf(image.Identifier, 8, !imagesNoTrunc)
			short_name := truncIf(wordify(image.Name), 25, !imagesNoTrunc)
			creationTime, _ := time.Parse("2006-01-02T15:04:05.000000+00:00", image.CreationDate)
			creationDate := units.HumanDuration(time.Now().UTC().Sub(creationTime))
			fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", short_name, tag, short_id, creationDate, virtualSize)
		}
	}
}
<commit_msg>Fixed compilation of 'scw images'.<commit_after>package main

import (
	"fmt"
	"os"
	"text/tabwriter"
	"time"

	"github.com/docker/docker/pkg/units"
)

var cmdImages = &Command{
	Exec:        runImages,
	UsageLine:   "images [OPTIONS]",
	Description: "List images",
	Help:        "List images.",
}

func init() {
	// FIXME: -h
	cmdImages.Flag.BoolVar(&imagesA, []string{"a", "-all"}, false, "Show all iamges")
	cmdImages.Flag.BoolVar(&imagesNoTrunc, []string{"-no-trunc"}, false, "Don't truncate output")
	cmdImages.Flag.BoolVar(&imagesQ, []string{"q", "-quiet"}, false, "Only show numeric IDs")
}

// Flags
var imagesA bool       // -a flag
var imagesQ bool       // -q flag
var imagesNoTrunc bool // -no-trunc flag

func runImages(cmd *Command, args []string) {
	images, err := cmd.API.GetImages()
	if err != nil {
		fmt.Fprintf(os.Stderr, "unable to fetch images from the Scaleway API: %v\n", err)
		os.Exit(1)
	}

	w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
	defer w.Flush()
	if !imagesQ {
		fmt.Fprintf(w, "REPOSITORY\tTAG\tIMAGE ID\tCREATED\tVIRTUAL SIZE\n")
	}
	for _, image := range *images {
		if imagesQ {
			fmt.Fprintf(w, "%s\n", image.Identifier)
		} else {
			tag := "latest"
			virtualSize := units.HumanSize(float64(image.RootVolume.Size))
			short_id := truncIf(image.Identifier, 8, !imagesNoTrunc)
			short_name := truncIf(wordify(image.Name), 25, !imagesNoTrunc)
			creationTime, _ := time.Parse("2006-01-02T15:04:05.000000+00:00", image.CreationDate)
			creationDate := units.HumanDuration(time.Now().UTC().Sub(creationTime))
			fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", short_name, tag, short_id, creationDate, virtualSize)
		}
	}
}
<|endoftext|>


<commit_before>// Package bot provides the interfaces for creating a chatops
// bot.
package bot

/* bot.go defines core data structures and public methods for startup.
   handler.go has the methods for callbacks from the connector, */

import (
	"fmt"
	"log"
	"math/rand"
	"os"
	"os/signal"
	"regexp"
	"sync"
	"syscall"
	"time"
)

// Version is the current version of Gopherbot
var Version = "v1.1.0"

// mkdist.* creates a temporary commit.go that sets commit to the current
// git commit in an init() function
var commit = "(manual build)"

var globalLock sync.RWMutex
var random *rand.Rand

var connectors = make(map[string]func(Handler, *log.Logger) Connector)

// RegisterConnector should be called in an init function to register a type
// of connector. Currently only Slack is implemented.
func RegisterConnector(name string, connstarter func(Handler, *log.Logger) Connector) {
	if stopRegistrations {
		return
	}
	if connectors[name] != nil {
		log.Fatal("Attempted registration of duplicate connector:", name)
	}
	connectors[name] = connstarter
}

// robot holds all the interal data relevant to the Bot. Most of it is populated
// by loadConfig, other stuff is populated by the connector.
var robot struct {
	Connector                             // Connector interface, implemented by each specific protocol
	configPath           string           // Directory for configuration files overriding defaults / installed
	installPath          string           // Path to the bot's installation directory
	adminUsers           []string         // List of users with access to administrative commands
	alias                rune             // single-char alias for addressing the bot
	name                 string           // e.g. "Gort"
	fullName             string           // e.g. "Robbie Robot"
	adminContact         string           // who to contact for problems with the robot.
	email                string           // the from: when the robot sends email
	mailConf             botMailer        // configuration to use when sending email
	ignoreUsers          []string         // list of users to never listen to, like other bots
	preRegex             *regexp.Regexp   // regex for matching prefixed commands, e.g. "Gort, drop your weapon"
	postRegex            *regexp.Regexp   // regex for matching, e.g. "open the pod bay doors, hal"
	bareRegex            *regexp.Regexp   // regex for matching the robot's bare name, if you forgot it in the previous command
	joinChannels         []string         // list of channels to join
	defaultAllowDirect   bool             // whether plugins are available in DM by default
	defaultMessageFormat MessageFormat    // Raw unless set to Variable or Fixed
	plugChannels         []string         // list of channels where plugins are active by default
	sync.RWMutex                          // for safe updating of bot data structures
	protocol             string           // Name of the protocol, e.g. "slack"
	brainProvider        string           // Type of Brain provider to use
	brain                SimpleBrain      // Interface for robot to Store and Retrieve data
	defaultElevator      string           // Plugin name for performing elevation
	defaultAuthorizer    string           // Plugin name for performing authorization
	externalPlugins      []externalPlugin // List of external plugins to load
	port                 string           // Localhost port to listen on
	logger               *log.Logger      // Where to log to
	stop                 chan struct{}    // stop channel for stopping the connector
	done                 chan struct{}    // channel closed when robot finishes shutting down
	shuttingDown         bool             // to prevent new plugins from starting
	pluginsRunning       int              // a count of how many plugins are currently running
	paused               bool             // it's a Windows thing
	sync.WaitGroup                        // for keeping track of running plugins
}

// initBot sets up the global robot and loads
// configuration.
func initBot(cpath, epath string, logger *log.Logger) {
	globalLock.Lock()
	// Prevent plugin registration after program init
	stopRegistrations = true
	// Seed the pseudo-random number generator, for plugin IDs, RandomString, etc.
	random = rand.New(rand.NewSource(time.Now().UnixNano()))

	globalLock.Unlock()

	robot.Lock()
	robot.configPath = cpath
	robot.installPath = epath
	robot.logger = logger
	robot.stop = make(chan struct{})
	robot.done = make(chan struct{})
	robot.shuttingDown = false
	robot.Unlock()

	handle := handler{}
	bot := &Robot{}
	if err := bot.loadConfig(); err != nil {
		Log(Fatal, fmt.Sprintf("Error loading initial configuration: %v", err))
	}

	if len(robot.brainProvider) > 0 {
		if bprovider, ok := brains[robot.brainProvider]; !ok {
			Log(Fatal, fmt.Sprintf("No provider registered for brain: \"%s\"", robot.brainProvider))
		} else {
			brain := bprovider(handle, logger)
			robot.Lock()
			robot.brain = brain
			robot.Unlock()
		}
	}
}

// set connector sets the connector, which should already be initialized
func setConnector(c Connector) {
	robot.Lock()
	robot.Connector = c
	robot.Unlock()
}

// run starts all the loops and returns a channel that closes when the robot
// shuts down. It should return after the connector loop has started and
// plugins are initialized.
func run() <-chan struct{} {
	robot.RLock()
	port := robot.port
	robot.RUnlock()
	if len(port) > 0 {
		// Only start the HttpListener once, runs for life of process
		botHttpListener.Lock()
		if !botHttpListener.listening {
			botHttpListener.listening = true
			go listenHTTPJSON()
		}
		botHttpListener.Unlock()
	}

	var cl []string
	robot.RLock()
	cl = append(cl, robot.joinChannels...)
	robot.RUnlock()
	for _, channel := range cl {
		robot.JoinChannel(channel)
	}

	// Start the brain loop
	go runBrain()

	// signal handler
	go func() {
		robot.RLock()
		done := robot.done
		robot.RUnlock()
		sigs := make(chan os.Signal, 1)

		signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)

	loop:
		for {
			select {
			case sig := <-sigs:
				robot.Lock()
				if robot.shuttingDown {
					Log(Warn, "Received SIGINT/SIGTERM while shutdown in progress")
					robot.Unlock()
				} else {
					robot.shuttingDown = true
					robot.Unlock()
					signal.Stop(sigs)
					Log(Info, fmt.Sprintf("Exiting on signal: %s", sig))
					stop()
				}
			case <-done:
				break loop
			}
		}
	}()

	// connector loop
	robot.RLock()
	go func(conn Connector, stop <-chan struct{}, done chan<- struct{}) {
		conn.Run(stop)
		close(done)
	}(robot.Connector, robot.stop, robot.done)
	robot.RUnlock()

	initializePlugins()
	robot.RLock()
	defer robot.RUnlock()
	return robot.done
}

// stop is called whenever the robot needs to shut down gracefully. All callers
// should lock the bot and check the value of robot.shuttingDown; see
// builtins.go and win_svc_run.go
func stop() {
	robot.RLock()
	Log(Debug, fmt.Sprintf("stop called with %d plugins running", robot.pluginsRunning))
	stop := robot.stop
	robot.RUnlock()
	robot.Wait()
	brainQuit()
	close(stop)
}
<commit_msg>Open development of 1.1.1-snapshot<commit_after>// Package bot provides the interfaces for creating a chatops
// bot.
package bot

/* bot.go defines core data structures and public methods for startup.
   handler.go has the methods for callbacks from the connector, */

import (
	"fmt"
	"log"
	"math/rand"
	"os"
	"os/signal"
	"regexp"
	"sync"
	"syscall"
	"time"
)

// Version is the current version of Gopherbot
var Version = "v1.1.1-snapshot"

// mkdist.* creates a temporary commit.go that sets commit to the current
// git commit in an init() function
var commit = "(manual build)"

var globalLock sync.RWMutex
var random *rand.Rand

var connectors = make(map[string]func(Handler, *log.Logger) Connector)

// RegisterConnector should be called in an init function to register a type
// of connector. Currently only Slack is implemented.
func RegisterConnector(name string, connstarter func(Handler, *log.Logger) Connector) {
	if stopRegistrations {
		return
	}
	if connectors[name] != nil {
		log.Fatal("Attempted registration of duplicate connector:", name)
	}
	connectors[name] = connstarter
}

// robot holds all the interal data relevant to the Bot. Most of it is populated
// by loadConfig, other stuff is populated by the connector.
var robot struct {
	Connector                             // Connector interface, implemented by each specific protocol
	configPath           string           // Directory for configuration files overriding defaults / installed
	installPath          string           // Path to the bot's installation directory
	adminUsers           []string         // List of users with access to administrative commands
	alias                rune             // single-char alias for addressing the bot
	name                 string           // e.g. "Gort"
	fullName             string           // e.g. "Robbie Robot"
	adminContact         string           // who to contact for problems with the robot.
	email                string           // the from: when the robot sends email
	mailConf             botMailer        // configuration to use when sending email
	ignoreUsers          []string         // list of users to never listen to, like other bots
	preRegex             *regexp.Regexp   // regex for matching prefixed commands, e.g. "Gort, drop your weapon"
	postRegex            *regexp.Regexp   // regex for matching, e.g. "open the pod bay doors, hal"
	bareRegex            *regexp.Regexp   // regex for matching the robot's bare name, if you forgot it in the previous command
	joinChannels         []string         // list of channels to join
	defaultAllowDirect   bool             // whether plugins are available in DM by default
	defaultMessageFormat MessageFormat    // Raw unless set to Variable or Fixed
	plugChannels         []string         // list of channels where plugins are active by default
	sync.RWMutex                          // for safe updating of bot data structures
	protocol             string           // Name of the protocol, e.g. "slack"
	brainProvider        string           // Type of Brain provider to use
	brain                SimpleBrain      // Interface for robot to Store and Retrieve data
	defaultElevator      string           // Plugin name for performing elevation
	defaultAuthorizer    string           // Plugin name for performing authorization
	externalPlugins      []externalPlugin // List of external plugins to load
	port                 string           // Localhost port to listen on
	logger               *log.Logger      // Where to log to
	stop                 chan struct{}    // stop channel for stopping the connector
	done                 chan struct{}    // channel closed when robot finishes shutting down
	shuttingDown         bool             // to prevent new plugins from starting
	pluginsRunning       int              // a count of how many plugins are currently running
	paused               bool             // it's a Windows thing
	sync.WaitGroup                        // for keeping track of running plugins
}

// initBot sets up the global robot and loads
// configuration.
func initBot(cpath, epath string, logger *log.Logger) {
	globalLock.Lock()
	// Prevent plugin registration after program init
	stopRegistrations = true
	// Seed the pseudo-random number generator, for plugin IDs, RandomString, etc.
	random = rand.New(rand.NewSource(time.Now().UnixNano()))

	globalLock.Unlock()

	robot.Lock()
	robot.configPath = cpath
	robot.installPath = epath
	robot.logger = logger
	robot.stop = make(chan struct{})
	robot.done = make(chan struct{})
	robot.shuttingDown = false
	robot.Unlock()

	handle := handler{}
	bot := &Robot{}
	if err := bot.loadConfig(); err != nil {
		Log(Fatal, fmt.Sprintf("Error loading initial configuration: %v", err))
	}

	if len(robot.brainProvider) > 0 {
		if bprovider, ok := brains[robot.brainProvider]; !ok {
			Log(Fatal, fmt.Sprintf("No provider registered for brain: \"%s\"", robot.brainProvider))
		} else {
			brain := bprovider(handle, logger)
			robot.Lock()
			robot.brain = brain
			robot.Unlock()
		}
	}
}

// set connector sets the connector, which should already be initialized
func setConnector(c Connector) {
	robot.Lock()
	robot.Connector = c
	robot.Unlock()
}

// run starts all the loops and returns a channel that closes when the robot
// shuts down. It should return after the connector loop has started and
// plugins are initialized.
func run() <-chan struct{} {
	robot.RLock()
	port := robot.port
	robot.RUnlock()
	if len(port) > 0 {
		// Only start the HttpListener once, runs for life of process
		botHttpListener.Lock()
		if !botHttpListener.listening {
			botHttpListener.listening = true
			go listenHTTPJSON()
		}
		botHttpListener.Unlock()
	}

	var cl []string
	robot.RLock()
	cl = append(cl, robot.joinChannels...)
	robot.RUnlock()
	for _, channel := range cl {
		robot.JoinChannel(channel)
	}

	// Start the brain loop
	go runBrain()

	// signal handler
	go func() {
		robot.RLock()
		done := robot.done
		robot.RUnlock()
		sigs := make(chan os.Signal, 1)

		signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)

	loop:
		for {
			select {
			case sig := <-sigs:
				robot.Lock()
				if robot.shuttingDown {
					Log(Warn, "Received SIGINT/SIGTERM while shutdown in progress")
					robot.Unlock()
				} else {
					robot.shuttingDown = true
					robot.Unlock()
					signal.Stop(sigs)
					Log(Info, fmt.Sprintf("Exiting on signal: %s", sig))
					stop()
				}
			case <-done:
				break loop
			}
		}
	}()

	// connector loop
	robot.RLock()
	go func(conn Connector, stop <-chan struct{}, done chan<- struct{}) {
		conn.Run(stop)
		close(done)
	}(robot.Connector, robot.stop, robot.done)
	robot.RUnlock()

	initializePlugins()
	robot.RLock()
	defer robot.RUnlock()
	return robot.done
}

// stop is called whenever the robot needs to shut down gracefully. All callers
// should lock the bot and check the value of robot.shuttingDown; see
// builtins.go and win_svc_run.go
func stop() {
	robot.RLock()
	Log(Debug, fmt.Sprintf("stop called with %d plugins running", robot.pluginsRunning))
	stop := robot.stop
	robot.RUnlock()
	robot.Wait()
	brainQuit()
	close(stop)
}
<|endoftext|>


<commit_before>// Package bot provides the interfaces for creating a chatops
// bot.
package bot

/* bot.go defines core data structures and public methods for startup.
   handler.go has the methods for callbacks from the connector, */

import (
	"fmt"
	"log"
	"math/rand"
	"regexp"
	"sync"
	"time"
)

// Version is the current version of Gopherbot
var Version = "1.0.0-snapshot"

// mkdist.* creates a temporary commit.go that sets commit to the current
// git commit in an init() function
var commit = "(manual build)"

var globalLock sync.RWMutex
var random *rand.Rand

var connectors = make(map[string]func(Handler, *log.Logger) Connector)

// RegisterConnector should be called in an init function to register a type
// of connector. Currently only Slack is implemented.
func RegisterConnector(name string, connstarter func(Handler, *log.Logger) Connector) {
	if stopRegistrations {
		return
	}
	if connectors[name] != nil {
		log.Fatal("Attempted registration of duplicate connector:", name)
	}
	connectors[name] = connstarter
}

// robot holds all the interal data relevant to the Bot. Most of it is populated
// by loadConfig, other stuff is populated by the connector.
var robot struct {
	Connector                           // Connector interface, implemented by each specific protocol
	localPath          string           // Directory for local files overriding default config
	installPath        string           // Path to the bot's installation directory
	adminUsers         []string         // List of users with access to administrative commands
	alias              rune             // single-char alias for addressing the bot
	name               string           // e.g. "Gort"
	fullName           string           // e.g. "Robbie Robot"
	adminContact       string           // who to contact for problems with the robot.
	email              string           // the from: when the robot sends email
	mailConf           botMailer        // configuration to use when sending email
	ignoreUsers        []string         // list of users to never listen to, like other bots
	preRegex           *regexp.Regexp   // regex for matching prefixed commands, e.g. "Gort, drop your weapon"
	postRegex          *regexp.Regexp   // regex for matching, e.g. "open the pod bay doors, hal"
	joinChannels       []string         // list of channels to join
	defaultAllowDirect bool             // whether plugins are available in DM by default
	plugChannels       []string         // list of channels where plugins are active by default
	sync.RWMutex                        // for safe updating of bot data structures
	protocol           string           // Name of the protocol, e.g. "slack"
	brainProvider      string           // Type of Brain provider to use
	brain              SimpleBrain      // Interface for robot to Store and Retrieve data
	defaultElevator    string           // Plugin name for performing elevation
	defaultAuthorizer  string           // Plugin name for performing authorization
	externalPlugins    []externalPlugin // List of external plugins to load
	port               string           // Localhost port to listen on
	logger             *log.Logger      // Where to log to
}

//var robot *robotcfg

// newBot instantiates the one and only instance of a Gobot, and loads
// configuration.
func newBot(cpath, epath string, logger *log.Logger) error {
	globalLock.Lock()
	// Prevent plugin registration after program init
	stopRegistrations = true
	// Seed the pseudo-random number generator, for plugin IDs, RandomString, etc.
	random = rand.New(rand.NewSource(time.Now().UnixNano()))

	globalLock.Unlock()

	robot.localPath = cpath
	robot.installPath = epath
	robot.logger = logger

	handle := handler{}
	if err := loadConfig(); err != nil {
		return err
	}

	if len(robot.brainProvider) > 0 {
		if bprovider, ok := brains[robot.brainProvider]; !ok {
			Log(Fatal, fmt.Sprintf("No provider registered for brain: \"%s\"", robot.brainProvider))
		} else {
			robot.brain = bprovider(handle, logger)
		}
	}
	return nil
}

// Init is called after the bot is connected.
func botInit(c Connector) {
	robot.Lock()
	if robot.Connector != nil {
		robot.Unlock()
		return
	}
	robot.Connector = c
	robot.Unlock()
	go listenHTTPJSON()
	var cl []string
	robot.RLock()
	cl = append(cl, robot.joinChannels...)
	robot.RUnlock()
	for _, channel := range cl {
		robot.JoinChannel(channel)
	}
	initializePlugins()
}
<commit_msg>Cut 1.0.0 release<commit_after>// Package bot provides the interfaces for creating a chatops
// bot.
package bot

/* bot.go defines core data structures and public methods for startup.
   handler.go has the methods for callbacks from the connector, */

import (
	"fmt"
	"log"
	"math/rand"
	"regexp"
	"sync"
	"time"
)

// Version is the current version of Gopherbot
var Version = "1.0.0"

// mkdist.* creates a temporary commit.go that sets commit to the current
// git commit in an init() function
var commit = "(manual build)"

var globalLock sync.RWMutex
var random *rand.Rand

var connectors = make(map[string]func(Handler, *log.Logger) Connector)

// RegisterConnector should be called in an init function to register a type
// of connector. Currently only Slack is implemented.
func RegisterConnector(name string, connstarter func(Handler, *log.Logger) Connector) {
	if stopRegistrations {
		return
	}
	if connectors[name] != nil {
		log.Fatal("Attempted registration of duplicate connector:", name)
	}
	connectors[name] = connstarter
}

// robot holds all the interal data relevant to the Bot. Most of it is populated
// by loadConfig, other stuff is populated by the connector.
var robot struct {
	Connector                           // Connector interface, implemented by each specific protocol
	localPath          string           // Directory for local files overriding default config
	installPath        string           // Path to the bot's installation directory
	adminUsers         []string         // List of users with access to administrative commands
	alias              rune             // single-char alias for addressing the bot
	name               string           // e.g. "Gort"
	fullName           string           // e.g. "Robbie Robot"
	adminContact       string           // who to contact for problems with the robot.
	email              string           // the from: when the robot sends email
	mailConf           botMailer        // configuration to use when sending email
	ignoreUsers        []string         // list of users to never listen to, like other bots
	preRegex           *regexp.Regexp   // regex for matching prefixed commands, e.g. "Gort, drop your weapon"
	postRegex          *regexp.Regexp   // regex for matching, e.g. "open the pod bay doors, hal"
	joinChannels       []string         // list of channels to join
	defaultAllowDirect bool             // whether plugins are available in DM by default
	plugChannels       []string         // list of channels where plugins are active by default
	sync.RWMutex                        // for safe updating of bot data structures
	protocol           string           // Name of the protocol, e.g. "slack"
	brainProvider      string           // Type of Brain provider to use
	brain              SimpleBrain      // Interface for robot to Store and Retrieve data
	defaultElevator    string           // Plugin name for performing elevation
	defaultAuthorizer  string           // Plugin name for performing authorization
	externalPlugins    []externalPlugin // List of external plugins to load
	port               string           // Localhost port to listen on
	logger             *log.Logger      // Where to log to
}

//var robot *robotcfg

// newBot instantiates the one and only instance of a Gobot, and loads
// configuration.
func newBot(cpath, epath string, logger *log.Logger) error {
	globalLock.Lock()
	// Prevent plugin registration after program init
	stopRegistrations = true
	// Seed the pseudo-random number generator, for plugin IDs, RandomString, etc.
	random = rand.New(rand.NewSource(time.Now().UnixNano()))

	globalLock.Unlock()

	robot.localPath = cpath
	robot.installPath = epath
	robot.logger = logger

	handle := handler{}
	if err := loadConfig(); err != nil {
		return err
	}

	if len(robot.brainProvider) > 0 {
		if bprovider, ok := brains[robot.brainProvider]; !ok {
			Log(Fatal, fmt.Sprintf("No provider registered for brain: \"%s\"", robot.brainProvider))
		} else {
			robot.brain = bprovider(handle, logger)
		}
	}
	return nil
}

// Init is called after the bot is connected.
func botInit(c Connector) {
	robot.Lock()
	if robot.Connector != nil {
		robot.Unlock()
		return
	}
	robot.Connector = c
	robot.Unlock()
	go listenHTTPJSON()
	var cl []string
	robot.RLock()
	cl = append(cl, robot.joinChannels...)
	robot.RUnlock()
	for _, channel := range cl {
		robot.JoinChannel(channel)
	}
	initializePlugins()
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/gamelost/bot3server/module/fight"
	// "github.com/gamelost/bot3server/module/logger"
	"github.com/gamelost/bot3server/server"
	//"github.com/gamelost/bot3server/module/helloworld"
	"github.com/gamelost/bot3server/module/help"
	"github.com/gamelost/bot3server/module/inconceivable"
	"github.com/gamelost/bot3server/module/slap"
	//"github.com/gamelost/bot3server/module/sleep"
	"github.com/gamelost/bot3server/module/cah"
	"github.com/gamelost/bot3server/module/nextwedding"
	// "github.com/gamelost/bot3server/module/panic"
	"github.com/gamelost/bot3server/module/remindme"
	"github.com/gamelost/bot3server/module/zed"
	//"github.com/gamelost/bot3server/module/testcommands"
	"github.com/gamelost/bot3server/module/boulderingtime"
	wuconditions "github.com/gamelost/bot3server/module/weather/conditions"
	wuforecast "github.com/gamelost/bot3server/module/weather/forecast"
	//"github.com/gamelost/bot3server/module/howlongbeforeicanquityouintel"
	iniconf "code.google.com/p/goconf/conf"
	"encoding/json"
	"fmt"
	nsq "github.com/bitly/go-nsq"
	"log"
	"os"
	"os/signal"
	"syscall"
)

const DEFAULT_CONFIG_FILENAME = "bot3server.config"

func main() {

	// the quit channel
	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)

	config, err := iniconf.ReadConfigFile(DEFAULT_CONFIG_FILENAME)
	if err != nil {
		log.Fatal("Unable to read configuration file. Exiting now.")
	}

	bot3apiInput, _ := config.GetString("default", "bot3api-input")

	// set up listener instance
	incomingFromIRC, err := nsq.NewReader(bot3apiInput, "main")
	if err != nil {
		panic(err)
		sigChan <- syscall.SIGINT
	}

	// set up channels
	incomingFromNSQChan := make(chan *nsq.Message)
	outgoingToNSQChan := make(chan *server.BotResponse)

	writer := nsq.NewWriter("127.0.0.1:4150")

	// initialize the handlers
	botApp := &BotApp{Config: config, IncomingChan: incomingFromNSQChan, OutgoingChan: outgoingToNSQChan, NSQOutputWriter: writer}
	botApp.initServices()

	incomingFromIRC.AddHandler(botApp)
	incomingFromIRC.ConnectToLookupd("127.0.0.1:4161")

	go botApp.DoBotService(incomingFromNSQChan, outgoingToNSQChan)

	<-sigChan
}

type BotApp struct {
	Config          *iniconf.ConfigFile
	Handlers        map[string]server.BotHandler
	IncomingChan    chan *nsq.Message
	OutgoingChan    chan *server.BotResponse
	NSQOutputWriter *nsq.Writer
}

func (ba *BotApp) AddHandler(key string, h server.BotHandler) {
	ba.Handlers[key] = h
}

func (ba *BotApp) GetHandler(key string) server.BotHandler {
	return ba.Handlers[key]
}

func (ba *BotApp) initServices() error {

	ba.Handlers = make(map[string]server.BotHandler)

	// implement all services
	ba.AddHandler("fight", (new(fight.FightService)).NewService())
	ba.AddHandler("cah", (new(cah.CahService)).NewService())
	ba.AddHandler("slap", (new(slap.SlapService)).NewService())
	ba.AddHandler("inconceivable", (new(inconceivable.InconceivableService)).NewService())
	ba.AddHandler("help", (new(help.HelpService)).NewService())
	ba.AddHandler("remindme", (new(remindme.RemindMeService)).NewService())
	ba.AddHandler("nextwedding", (new(nextwedding.NextWeddingService)).NewService())
	ba.AddHandler("weather", (new(wuconditions.WeatherConditionsService)).NewService())
	ba.AddHandler("forecast", (new(wuforecast.WeatherForecastService)).NewService())
	ba.AddHandler("zed", (new(zed.ZedsDeadService)).NewService())
	ba.AddHandler("boulderingtime", (new(boulderingtime.BoulderingTimeService)).NewService())
	return nil
}

func (ba *BotApp) DoBotService(incomingFromNSQChan chan *nsq.Message, outgoingToNSQChan chan *server.BotResponse) {

	for {
		select {
		case msg := <-incomingFromNSQChan:
			fmt.Printf("Received incoming message from NSQ bus: %s\n", msg)
			break
		case msg := <-outgoingToNSQChan:
			//fmt.Printf("Received outgoing message for NSQ bus: %s\n", msg)
			val, _ := json.Marshal(msg)
			ba.NSQOutputWriter.Publish("bot3api-output", val)
			break
		}
	}
}

func (ba *BotApp) HandleMessage(message *nsq.Message) error {

	ba.IncomingChan <- message
	var req = &server.BotRequest{}
	json.Unmarshal(message.Body, req)
	go ba.HandleIncoming(req)
	return nil
}

func (ba *BotApp) HandleIncoming(botRequest *server.BotRequest) error {

	// since we dont want the entire botapi to crash if a module throws an error or panic
	// we'll trap it here and log/notify the owner.

	// mabye a good idea to automatically-disable the module if it panics too frequently

	// throwout malformed requests
	fmt.Printf("Line is %s", botRequest)
	if botRequest.RawLine == nil {
		return nil
	}

	// deferred method to protect against panics
	defer func(rawline string) {
		// if module panics, log it and discard
		if x := recover(); x != nil {
			log.Printf("Trapped panic. Rawline is:[%s]: %v", rawline, x)
		}
	}(botRequest.RawLine.Text())

	// log all lines
	// logger.Log(botRequest)

	// check if command before processing
	if botRequest.RequestIsCommand() {

		command := botRequest.Command()

		handler := ba.GetHandler(command)

		if handler != nil {
			fmt.Printf("Handler for: %s\n", command)

			botResponse := &server.BotResponse{Target: botRequest.RawLine.Target()}
			handler.Handle(botRequest, botResponse)
			ba.OutgoingChan <- botResponse
		}
	}

	// return error object (nil for now)
	return nil
}
<commit_msg>renamed bot3api.go to bot3server.go, some refactoring and added dice module<commit_after><|endoftext|>