<commit_before>package main

import (
	"fmt"
	"os"

	"github.com/codegangsta/cli"
	"github.com/nlopes/slack"
	"github.com/pkg/errors"
)

func doAnnotateWithSlack(c *cli.Context) error {
	inputFilename := c.String("input-filename")
	outputFilename := c.String("output-filename")
	channelID := c.String("channel")
	filterStatusCodeOk := c.Bool("filter-status-code-ok")

	if inputFilename == "" {
		_ = cli.ShowCommandHelp(c, "slack")
		return cli.NewExitError("`input-filename` is a required field.", 1)
	}

	if outputFilename == "" {
		_ = cli.ShowCommandHelp(c, "slack")
		return cli.NewExitError("`output-filename` is a required field.", 1)
	}

	if channelID == "" {
		_ = cli.ShowCommandHelp(c, "slack")
		return cli.NewExitError("`channel` is a required field.", 1)
	}

	cacheFilename := CacheFilename
	cache, err := LoadCache(cacheFilename)
	if err != nil {
		fmt.Fprintln(os.Stderr, err.Error())
	}

	examples, err := ReadExamples(inputFilename)
	if err != nil {
		return err
	}

	AttachMetaData(cache, examples)
	if filterStatusCodeOk {
		examples = FilterStatusCodeOkExamples(examples)
	}
	model := TrainedModel(examples)

	api := slack.New(os.Getenv("SLACK_TOKEN"))
	rtm := api.NewRTM()
	go rtm.ManageConnection()

	example := NextExampleToBeAnnotated(model, examples)

	rtm.SendMessage(rtm.NewOutgoingMessage("Ready to annotate!", channelID))
	rtm.SendMessage(rtm.NewOutgoingMessage(example.Url, channelID))
	prevTimestamp := ""

annotationLoop:
	for {
		select {
		case msg := <-rtm.IncomingEvents:
			switch ev := msg.Data.(type) {
			case *slack.AckMessage:
				prevTimestamp = ev.Timestamp
			case *slack.MessageEvent:
				text := ev.Text
				if len(text) > 1 || len(text) == 0 {
					break
				}
				r := []rune(text)[0]
				act := rune2ActionType(r)

				switch act {
				case LABEL_AS_POSITIVE:
					example.Annotate(POSITIVE)
					model = TrainedModel(examples)
					rtm.AddReaction("heavy_plus_sign", slack.NewRefToMessage(channelID, ev.Timestamp))
				case LABEL_AS_NEGATIVE:
					example.Annotate(NEGATIVE)
					model = TrainedModel(examples)
					rtm.AddReaction("heavy_minus_sign", slack.NewRefToMessage(channelID, prevTimestamp))
				case SKIP:
					rtm.SendMessage(rtm.NewOutgoingMessage("Skiped this example", channelID))
					break
				case SAVE:
					rtm.SendMessage(rtm.NewOutgoingMessage("Saved labeld examples", channelID))
					WriteExamples(examples, outputFilename)
				case HELP:
					rtm.SendMessage(rtm.NewOutgoingMessage(ActionHelpDoc, channelID))
				case EXIT:
					rtm.SendMessage(rtm.NewOutgoingMessage("EXIT", channelID))
					break annotationLoop
				default:
					break annotationLoop
				}
				example = NextExampleToBeAnnotated(model, examples)
				rtm.SendMessage(rtm.NewOutgoingMessage(example.Url, channelID))
			case *slack.InvalidAuthEvent:
				return errors.New("Invalid credentials")
			default:
			}
		}
	}
	return nil
}
<commit_msg>エラーハンドリング<commit_after>package main

import (
	"fmt"
	"os"

	"github.com/codegangsta/cli"
	"github.com/nlopes/slack"
	"github.com/pkg/errors"
)

func doAnnotateWithSlack(c *cli.Context) error {
	inputFilename := c.String("input-filename")
	outputFilename := c.String("output-filename")
	channelID := c.String("channel")
	filterStatusCodeOk := c.Bool("filter-status-code-ok")

	if inputFilename == "" {
		_ = cli.ShowCommandHelp(c, "slack")
		return cli.NewExitError("`input-filename` is a required field.", 1)
	}

	if outputFilename == "" {
		_ = cli.ShowCommandHelp(c, "slack")
		return cli.NewExitError("`output-filename` is a required field.", 1)
	}

	if channelID == "" {
		_ = cli.ShowCommandHelp(c, "slack")
		return cli.NewExitError("`channel` is a required field.", 1)
	}

	cacheFilename := CacheFilename
	cache, err := LoadCache(cacheFilename)
	if err != nil {
		fmt.Fprintln(os.Stderr, err.Error())
	}

	examples, err := ReadExamples(inputFilename)
	if err != nil {
		return err
	}

	AttachMetaData(cache, examples)
	if filterStatusCodeOk {
		examples = FilterStatusCodeOkExamples(examples)
	}
	model := TrainedModel(examples)

	api := slack.New(os.Getenv("SLACK_TOKEN"))
	rtm := api.NewRTM()
	go rtm.ManageConnection()

	example := NextExampleToBeAnnotated(model, examples)
	if example == nil {
		return errors.New("No example to annotate")
	}

	rtm.SendMessage(rtm.NewOutgoingMessage("Ready to annotate!", channelID))
	rtm.SendMessage(rtm.NewOutgoingMessage(example.Url, channelID))
	prevTimestamp := ""

annotationLoop:
	for {
		select {
		case msg := <-rtm.IncomingEvents:
			switch ev := msg.Data.(type) {
			case *slack.AckMessage:
				prevTimestamp = ev.Timestamp
			case *slack.MessageEvent:
				text := ev.Text
				if len(text) > 1 || len(text) == 0 {
					break
				}
				r := []rune(text)[0]
				act := rune2ActionType(r)

				switch act {
				case LABEL_AS_POSITIVE:
					example.Annotate(POSITIVE)
					model = TrainedModel(examples)
					rtm.AddReaction("heavy_plus_sign", slack.NewRefToMessage(channelID, ev.Timestamp))
				case LABEL_AS_NEGATIVE:
					example.Annotate(NEGATIVE)
					model = TrainedModel(examples)
					rtm.AddReaction("heavy_minus_sign", slack.NewRefToMessage(channelID, prevTimestamp))
				case SKIP:
					rtm.SendMessage(rtm.NewOutgoingMessage("Skiped this example", channelID))
					break
				case SAVE:
					rtm.SendMessage(rtm.NewOutgoingMessage("Saved labeld examples", channelID))
					WriteExamples(examples, outputFilename)
				case HELP:
					rtm.SendMessage(rtm.NewOutgoingMessage(ActionHelpDoc, channelID))
				case EXIT:
					rtm.SendMessage(rtm.NewOutgoingMessage("EXIT", channelID))
					break annotationLoop
				default:
					break annotationLoop
				}
				example = NextExampleToBeAnnotated(model, examples)
				if example == nil {
					return errors.New("No example to annotate")
				}
				rtm.SendMessage(rtm.NewOutgoingMessage(example.Url, channelID))
			case *slack.InvalidAuthEvent:
				return errors.New("Invalid credentials")
			default:
			}
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package mpb

import (
	"io"
	"sync"
	"time"
)

// Bar represents a progress Bar
type Bar struct {
	total int
	width int
	alpha float64

	fill     byte
	empty    byte
	tip      byte
	leftEnd  byte
	rightEnd byte

	lastFrame []byte

	incrCh       chan int
	redrawReqCh  chan chan []byte
	currentReqCh chan chan int
	statusReqCh  chan chan int
	decoratorCh  chan *decorator
	flushedCh    chan struct{}
	stopCh       chan struct{}
	done         chan struct{}
}

type Statistics struct {
	Total, Current                   int
	TimeElapsed, TimePerItemEstimate time.Duration
}

func (s *Statistics) eta() time.Duration {
	return time.Duration(s.Total-s.Current) * s.TimePerItemEstimate
}

func newBar(total, width int, wg *sync.WaitGroup) *Bar {
	b := &Bar{
		fill:         '=',
		empty:        '-',
		tip:          '>',
		leftEnd:      '[',
		rightEnd:     ']',
		alpha:        0.25,
		total:        total,
		width:        width,
		incrCh:       make(chan int),
		redrawReqCh:  make(chan chan []byte),
		currentReqCh: make(chan chan int),
		statusReqCh:  make(chan chan int),
		decoratorCh:  make(chan *decorator),
		flushedCh:    make(chan struct{}),
		stopCh:       make(chan struct{}),
		done:         make(chan struct{}),
	}
	go b.server(wg)
	return b
}

// SetWidth sets width of the bar
func (b *Bar) SetWidth(n int) *Bar {
	if n <= 0 {
		return b
	}
	b.width = n
	return b
}

// SetFill sets character representing completed progress.
// Defaults to '='
func (b *Bar) SetFill(c byte) *Bar {
	b.fill = c
	return b
}

// SetTip sets character representing tip of progress.
// Defaults to '>'
func (b *Bar) SetTip(c byte) *Bar {
	b.tip = c
	return b
}

// SetEmpty sets character representing the empty progress
// Defaults to '-'
func (b *Bar) SetEmpty(c byte) *Bar {
	b.empty = c
	return b
}

// SetLeftEnd sets character representing the left most border
// Defaults to '['
func (b *Bar) SetLeftEnd(c byte) *Bar {
	b.leftEnd = c
	return b
}

// SetRightEnd sets character representing the right most border
// Defaults to ']'
func (b *Bar) SetRightEnd(c byte) *Bar {
	b.rightEnd = c
	return b
}

// SetEtaAlpha sets alfa for exponential-weighted-moving-average ETA estimator
// Defaults to 0.25
// Normally you shouldn't touch this
func (b *Bar) SetEtaAlpha(a float64) *Bar {
	b.alpha = a
	return b
}

func (b *Bar) ProxyReader(r io.Reader) *Reader {
	return &Reader{r, b}
}

// Incr increments progress bar
func (b *Bar) Incr(n int) {
	if !b.isDone() {
		b.incrCh <- n
	}
}

// Current returns the actual current.
// If bar was stopped by Stop(), subsequent calls to Current will return -1
func (b *Bar) Current() int {
	if !b.isDone() {
		respCh := make(chan int)
		b.currentReqCh <- respCh
		return <-respCh
	}
	return -1
}

// Stop stops rendering the bar
func (b *Bar) Stop() {
	if !b.isDone() {
		b.stopCh <- struct{}{}
	}
}

func (b *Bar) InProgress() bool {
	return !b.isDone()
}

func (b *Bar) PrependFunc(f DecoratorFunc) *Bar {
	b.decoratorCh <- &decorator{decoratorPrepend, f}
	return b
}

func (b *Bar) AppendFunc(f DecoratorFunc) *Bar {
	b.decoratorCh <- &decorator{decoratorAppend, f}
	return b
}

// String returns the string representation of the bar
func (b *Bar) String() string {
	if !b.isDone() {
		respCh := make(chan []byte)
		b.redrawReqCh <- respCh
		return string(<-respCh)
	}
	return string(b.lastFrame)
}

func (b *Bar) server(wg *sync.WaitGroup) {
	timeStarted := time.Now()
	blockStartTime := timeStarted
	buf := make([]byte, b.width, b.width+24)
	var tpie time.Duration
	var timeElapsed time.Duration
	var appendFuncs []DecoratorFunc
	var prependFuncs []DecoratorFunc
	var completed bool
	var current int
	for {
		select {
		case i := <-b.incrCh:
			n := current + i
			if n > b.total {
				current = b.total
				completed = true
				break
			}
			timeElapsed = time.Since(timeStarted)
			tpie = calcTimePerItemEstimate(tpie, blockStartTime, b.alpha, i)
			blockStartTime = time.Now()
			current = n
			if current == b.total && !completed {
				completed = true
			}
		case d := <-b.decoratorCh:
			switch d.kind {
			case decoratorAppend:
				appendFuncs = append(appendFuncs, d.f)
			case decoratorPrepend:
				prependFuncs = append(prependFuncs, d.f)
			}
		case respCh := <-b.currentReqCh:
			respCh <- current
		case respCh := <-b.redrawReqCh:
			stat := &Statistics{b.total, current, timeElapsed, tpie}
			respCh <- b.draw(stat, buf, appendFuncs, prependFuncs)
		case respCh := <-b.statusReqCh:
			respCh <- int(100 * float64(current) / float64(b.total))
		case <-b.flushedCh:
			if completed && !b.isDone() {
				stat := &Statistics{b.total, current, timeElapsed, tpie}
				b.lastFrame = b.draw(stat, buf, appendFuncs, prependFuncs)
				close(b.done)
				wg.Done()
				return
			}
		case <-b.stopCh:
			close(b.done)
			if !completed {
				wg.Done()
			}
			return
		}
	}
}

func (b *Bar) draw(stat *Statistics, buf []byte, appendFuncs, prependFuncs []DecoratorFunc) []byte {
	completedWidth := stat.Current * b.width / b.total

	for i := 0; i < completedWidth; i++ {
		buf[i] = b.fill
	}
	for i := completedWidth; i < b.width; i++ {
		buf[i] = b.empty
	}
	// set tip bit
	if completedWidth > 0 && completedWidth < b.width {
		buf[completedWidth-1] = b.tip
	}

	// set left and right ends bits
	buf[0], buf[len(buf)-1] = b.leftEnd, b.rightEnd

	// render append functions to the right of the bar
	for _, f := range appendFuncs {
		buf = append(buf, ' ')
		buf = append(buf, []byte(f(stat))...)
	}

	// render prepend functions to the left of the bar
	for _, f := range prependFuncs {
		args := []byte(f(stat))
		args = append(args, ' ')
		buf = append(args, buf...)
	}
	return buf
}

func (b *Bar) isDone() bool {
	select {
	case <-b.done:
		return true
	default:
		return false
	}
}

func (b *Bar) status() int {
	respCh := make(chan int)
	b.statusReqCh <- respCh
	return <-respCh
}

// SortableBarSlice satisfies sort interface
type SortableBarSlice []*Bar

func (p SortableBarSlice) Len() int { return len(p) }

func (p SortableBarSlice) Less(i, j int) bool { return p[i].status() < p[j].status() }

func (p SortableBarSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }

func calcTimePerItemEstimate(tpie time.Duration, blockStartTime time.Time, alpha float64, items int) time.Duration {
	lastBlockTime := time.Since(blockStartTime)
	lastItemEstimate := float64(lastBlockTime) / float64(items)
	return time.Duration((alpha * lastItemEstimate) + (1-alpha)*float64(tpie))
}
<commit_msg>non-blocking status() method<commit_after>package mpb

import (
	"io"
	"sync"
	"time"
)

// Bar represents a progress Bar
type Bar struct {
	// total int
	width int
	alpha float64

	fill     byte
	empty    byte
	tip      byte
	leftEnd  byte
	rightEnd byte

	lastFrame  []byte
	lastStatus int

	incrCh       chan int
	redrawReqCh  chan chan []byte
	currentReqCh chan chan int
	statusReqCh  chan chan int
	decoratorCh  chan *decorator
	flushedCh    chan struct{}
	stopCh       chan struct{}
	done         chan struct{}
}

type Statistics struct {
	Total, Current                   int
	TimeElapsed, TimePerItemEstimate time.Duration
}

func (s *Statistics) eta() time.Duration {
	return time.Duration(s.Total-s.Current) * s.TimePerItemEstimate
}

func newBar(total, width int, wg *sync.WaitGroup) *Bar {
	b := &Bar{
		fill:     '=',
		empty:    '-',
		tip:      '>',
		leftEnd:  '[',
		rightEnd: ']',
		alpha:    0.25,
		// total:        total,
		width:        width,
		incrCh:       make(chan int),
		redrawReqCh:  make(chan chan []byte),
		currentReqCh: make(chan chan int),
		statusReqCh:  make(chan chan int),
		decoratorCh:  make(chan *decorator),
		flushedCh:    make(chan struct{}),
		stopCh:       make(chan struct{}),
		done:         make(chan struct{}),
	}
	go b.server(wg, total)
	return b
}

// SetWidth sets width of the bar
func (b *Bar) SetWidth(n int) *Bar {
	if n <= 0 {
		return b
	}
	b.width = n
	return b
}

// SetFill sets character representing completed progress.
// Defaults to '='
func (b *Bar) SetFill(c byte) *Bar {
	b.fill = c
	return b
}

// SetTip sets character representing tip of progress.
// Defaults to '>'
func (b *Bar) SetTip(c byte) *Bar {
	b.tip = c
	return b
}

// SetEmpty sets character representing the empty progress
// Defaults to '-'
func (b *Bar) SetEmpty(c byte) *Bar {
	b.empty = c
	return b
}

// SetLeftEnd sets character representing the left most border
// Defaults to '['
func (b *Bar) SetLeftEnd(c byte) *Bar {
	b.leftEnd = c
	return b
}

// SetRightEnd sets character representing the right most border
// Defaults to ']'
func (b *Bar) SetRightEnd(c byte) *Bar {
	b.rightEnd = c
	return b
}

// SetEtaAlpha sets alfa for exponential-weighted-moving-average ETA estimator
// Defaults to 0.25
// Normally you shouldn't touch this
func (b *Bar) SetEtaAlpha(a float64) *Bar {
	b.alpha = a
	return b
}

func (b *Bar) ProxyReader(r io.Reader) *Reader {
	return &Reader{r, b}
}

// Incr increments progress bar
func (b *Bar) Incr(n int) {
	if !b.isDone() {
		b.incrCh <- n
	}
}

// Current returns the actual current.
// If bar was stopped by Stop(), subsequent calls to Current will return -1
func (b *Bar) Current() int {
	if !b.isDone() {
		respCh := make(chan int)
		b.currentReqCh <- respCh
		return <-respCh
	}
	return -1
}

// Stop stops rendering the bar
func (b *Bar) Stop() {
	if !b.isDone() {
		b.stopCh <- struct{}{}
	}
}

func (b *Bar) InProgress() bool {
	return !b.isDone()
}

func (b *Bar) PrependFunc(f DecoratorFunc) *Bar {
	b.decoratorCh <- &decorator{decoratorPrepend, f}
	return b
}

func (b *Bar) AppendFunc(f DecoratorFunc) *Bar {
	b.decoratorCh <- &decorator{decoratorAppend, f}
	return b
}

// String returns the string representation of the bar
func (b *Bar) String() string {
	if !b.isDone() {
		respCh := make(chan []byte)
		b.redrawReqCh <- respCh
		return string(<-respCh)
	}
	return string(b.lastFrame)
}

func (b *Bar) server(wg *sync.WaitGroup, total int) {
	timeStarted := time.Now()
	blockStartTime := timeStarted
	buf := make([]byte, b.width, b.width+24)
	var tpie time.Duration
	var timeElapsed time.Duration
	var appendFuncs []DecoratorFunc
	var prependFuncs []DecoratorFunc
	var completed bool
	var current int
	for {
		select {
		case i := <-b.incrCh:
			n := current + i
			if n > total {
				current = total
				completed = true
				break
			}
			timeElapsed = time.Since(timeStarted)
			tpie = calcTimePerItemEstimate(tpie, blockStartTime, b.alpha, i)
			blockStartTime = time.Now()
			current = n
			if current == total && !completed {
				completed = true
			}
		case d := <-b.decoratorCh:
			switch d.kind {
			case decoratorAppend:
				appendFuncs = append(appendFuncs, d.f)
			case decoratorPrepend:
				prependFuncs = append(prependFuncs, d.f)
			}
		case respCh := <-b.currentReqCh:
			respCh <- current
		case respCh := <-b.redrawReqCh:
			stat := &Statistics{total, current, timeElapsed, tpie}
			respCh <- b.draw(stat, buf, appendFuncs, prependFuncs)
		case respCh := <-b.statusReqCh:
			respCh <- percentage(total, current, 100)
		case <-b.flushedCh:
			if completed && !b.isDone() {
				stat := &Statistics{total, current, timeElapsed, tpie}
				b.lastFrame = b.draw(stat, buf, appendFuncs, prependFuncs)
				b.lastStatus = percentage(total, current, 100)
				close(b.done)
				wg.Done()
				return
			}
		case <-b.stopCh:
			close(b.done)
			if !completed {
				wg.Done()
			}
			return
		}
	}
}

func (b *Bar) draw(stat *Statistics, buf []byte, appendFuncs, prependFuncs []DecoratorFunc) []byte {
	completedWidth := percentage(stat.Total, stat.Current, b.width)

	for i := 0; i < completedWidth; i++ {
		buf[i] = b.fill
	}
	for i := completedWidth; i < b.width; i++ {
		buf[i] = b.empty
	}
	// set tip bit
	if completedWidth > 0 && completedWidth < b.width {
		buf[completedWidth-1] = b.tip
	}

	// set left and right ends bits
	buf[0], buf[len(buf)-1] = b.leftEnd, b.rightEnd

	// render append functions to the right of the bar
	for _, f := range appendFuncs {
		buf = append(buf, ' ')
		buf = append(buf, []byte(f(stat))...)
	}

	// render prepend functions to the left of the bar
	for _, f := range prependFuncs {
		args := []byte(f(stat))
		args = append(args, ' ')
		buf = append(args, buf...)
	}
	return buf
}

func (b *Bar) isDone() bool {
	select {
	case <-b.done:
		return true
	default:
		return false
	}
}

func (b *Bar) status() int {
	if !b.isDone() {
		respCh := make(chan int)
		b.statusReqCh <- respCh
		return <-respCh
	}
	return b.lastStatus
}

// SortableBarSlice satisfies sort interface
type SortableBarSlice []*Bar

func (p SortableBarSlice) Len() int { return len(p) }

func (p SortableBarSlice) Less(i, j int) bool { return p[i].status() < p[j].status() }

func (p SortableBarSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }

func calcTimePerItemEstimate(tpie time.Duration, blockStartTime time.Time, alpha float64, items int) time.Duration {
	lastBlockTime := time.Since(blockStartTime)
	lastItemEstimate := float64(lastBlockTime) / float64(items)
	return time.Duration((alpha * lastItemEstimate) + (1-alpha)*float64(tpie))
}

func percentage(total, current, ratio int) int {
	if total == 0 {
		return 0
	}
	return int(float64(ratio) * float64(current) / float64(total))
}
<|endoftext|>


<commit_before>// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
// Use of this source code is governed by a MIT license that can
// be found in the LICENSE file.

package termui

import "fmt"

// BarChart creates multiple bars in a widget:
/*
   bc := termui.NewBarChart()
   data := []int{3, 2, 5, 3, 9, 5}
   bclabels := []string{"S0", "S1", "S2", "S3", "S4", "S5"}
   bc.Border.Label = "Bar Chart"
   bc.Data = data
   bc.Width = 26
   bc.Height = 10
   bc.DataLabels = bclabels
   bc.TextColor = termui.ColorGreen
   bc.BarColor = termui.ColorRed
   bc.NumColor = termui.ColorYellow
*/
type BarChart struct {
	Block
	BarColor   Attribute
	TextColor  Attribute
	NumColor   Attribute
	Data       []int
	DataLabels []string
	BarWidth   int
	BarGap     int
	labels     [][]rune
	dataNum    [][]rune
	numBar     int
	scale      float64
	max        int
}

// NewBarChart returns a new *BarChart with current theme.
func NewBarChart() *BarChart {
	bc := &BarChart{Block: *NewBlock()}
	bc.BarColor = theme.BarChartBar
	bc.NumColor = theme.BarChartNum
	bc.TextColor = theme.BarChartText
	bc.BarGap = 1
	bc.BarWidth = 3
	return bc
}

func (bc *BarChart) layout() {
	bc.numBar = bc.innerWidth / (bc.BarGap + bc.BarWidth)
	bc.labels = make([][]rune, bc.numBar)
	bc.dataNum = make([][]rune, len(bc.Data))

	for i := 0; i < bc.numBar && i < len(bc.DataLabels) && i < len(bc.Data); i++ {
		bc.labels[i] = trimStr2Runes(bc.DataLabels[i], bc.BarWidth)
		n := bc.Data[i]
		s := fmt.Sprint(n)
		bc.dataNum[i] = trimStr2Runes(s, bc.BarWidth)
	}

	bc.max = bc.Data[0] //  what if Data is nil?
	for i := 0; i < len(bc.Data); i++ {
		if bc.max < bc.Data[i] {
			bc.max = bc.Data[i]
		}
	}
	bc.scale = float64(bc.max) / float64(bc.innerHeight-1)
}

// Buffer implements Bufferer interface.
func (bc *BarChart) Buffer() []Point {
	ps := bc.Block.Buffer()
	bc.layout()

	for i := 0; i < bc.numBar && i < len(bc.Data) && i < len(bc.DataLabels); i++ {
		h := int(float64(bc.Data[i]) / bc.scale)
		oftX := i * (bc.BarWidth + bc.BarGap)
		// plot bar
		for j := 0; j < bc.BarWidth; j++ {
			for k := 0; k < h; k++ {
				p := Point{}
				p.Ch = ' '
				p.Bg = bc.BarColor
				if bc.BarColor == ColorDefault { // when color is default, space char treated as transparent!
					p.Bg |= AttrReverse
				}
				p.X = bc.innerX + i*(bc.BarWidth+bc.BarGap) + j
				p.Y = bc.innerY + bc.innerHeight - 2 - k
				ps = append(ps, p)
			}
		}
		// plot text
		for j, k := 0, 0; j < len(bc.labels[i]); j++ {
			w := charWidth(bc.labels[i][j])
			p := Point{}
			p.Ch = bc.labels[i][j]
			p.Bg = bc.BgColor
			p.Fg = bc.TextColor
			p.Y = bc.innerY + bc.innerHeight - 1
			p.X = bc.innerX + oftX + k
			ps = append(ps, p)
			k += w
		}
		// plot num
		for j := 0; j < len(bc.dataNum[i]); j++ {
			p := Point{}
			p.Ch = bc.dataNum[i][j]
			p.Fg = bc.NumColor
			p.Bg = bc.BarColor
			if bc.BarColor == ColorDefault { // the same as above
				p.Bg |= AttrReverse
			}
			if h == 0 {
				p.Bg = bc.BgColor
			}
			p.X = bc.innerX + oftX + (bc.BarWidth-len(bc.dataNum[i]))/2 + j
			p.Y = bc.innerY + bc.innerHeight - 2
			ps = append(ps, p)
		}
	}

	return bc.Block.chopOverflow(ps)
}
<commit_msg>Fix an OutofIndex Panic on empty bargraphs and introduce SetMax()<commit_after>// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
// Use of this source code is governed by a MIT license that can
// be found in the LICENSE file.

package termui

import "fmt"

// BarChart creates multiple bars in a widget:
/*
   bc := termui.NewBarChart()
   data := []int{3, 2, 5, 3, 9, 5}
   bclabels := []string{"S0", "S1", "S2", "S3", "S4", "S5"}
   bc.Border.Label = "Bar Chart"
   bc.Data = data
   bc.Width = 26
   bc.Height = 10
   bc.DataLabels = bclabels
   bc.TextColor = termui.ColorGreen
   bc.BarColor = termui.ColorRed
   bc.NumColor = termui.ColorYellow
*/
type BarChart struct {
	Block
	BarColor   Attribute
	TextColor  Attribute
	NumColor   Attribute
	Data       []int
	DataLabels []string
	BarWidth   int
	BarGap     int
	labels     [][]rune
	dataNum    [][]rune
	numBar     int
	scale      float64
	max        int
}

// NewBarChart returns a new *BarChart with current theme.
func NewBarChart() *BarChart {
	bc := &BarChart{Block: *NewBlock()}
	bc.BarColor = theme.BarChartBar
	bc.NumColor = theme.BarChartNum
	bc.TextColor = theme.BarChartText
	bc.BarGap = 1
	bc.BarWidth = 3
	return bc
}

func (bc *BarChart) layout() {
	bc.numBar = bc.innerWidth / (bc.BarGap + bc.BarWidth)
	bc.labels = make([][]rune, bc.numBar)
	bc.dataNum = make([][]rune, len(bc.Data))

	for i := 0; i < bc.numBar && i < len(bc.DataLabels) && i < len(bc.Data); i++ {
		bc.labels[i] = trimStr2Runes(bc.DataLabels[i], bc.BarWidth)
		n := bc.Data[i]
		s := fmt.Sprint(n)
		bc.dataNum[i] = trimStr2Runes(s, bc.BarWidth)
	}

	//bc.max = bc.Data[0] //  what if Data is nil? Sometimes when bar graph is nill it produces panic with panic: runtime error: index out of range
	// Asign a negative value to get maxvalue auto-populates
	if bc.max == 0 {
		bc.max = -1
	}
	for i := 0; i < len(bc.Data); i++ {
		if bc.max < bc.Data[i] {
			bc.max = bc.Data[i]
		}
	}
	bc.scale = float64(bc.max) / float64(bc.innerHeight-1)
}

func (bc *BarChart) SetMax(max int) {

	if max > 0 {
		bc.max = max
	}
}

// Buffer implements Bufferer interface.
func (bc *BarChart) Buffer() []Point {
	ps := bc.Block.Buffer()
	bc.layout()

	for i := 0; i < bc.numBar && i < len(bc.Data) && i < len(bc.DataLabels); i++ {
		h := int(float64(bc.Data[i]) / bc.scale)
		oftX := i * (bc.BarWidth + bc.BarGap)
		// plot bar
		for j := 0; j < bc.BarWidth; j++ {
			for k := 0; k < h; k++ {
				p := Point{}
				p.Ch = ' '
				p.Bg = bc.BarColor
				if bc.BarColor == ColorDefault { // when color is default, space char treated as transparent!
					p.Bg |= AttrReverse
				}
				p.X = bc.innerX + i*(bc.BarWidth+bc.BarGap) + j
				p.Y = bc.innerY + bc.innerHeight - 2 - k
				ps = append(ps, p)
			}
		}
		// plot text
		for j, k := 0, 0; j < len(bc.labels[i]); j++ {
			w := charWidth(bc.labels[i][j])
			p := Point{}
			p.Ch = bc.labels[i][j]
			p.Bg = bc.BgColor
			p.Fg = bc.TextColor
			p.Y = bc.innerY + bc.innerHeight - 1
			p.X = bc.innerX + oftX + k
			ps = append(ps, p)
			k += w
		}
		// plot num
		for j := 0; j < len(bc.dataNum[i]); j++ {
			p := Point{}
			p.Ch = bc.dataNum[i][j]
			p.Fg = bc.NumColor
			p.Bg = bc.BarColor
			if bc.BarColor == ColorDefault { // the same as above
				p.Bg |= AttrReverse
			}
			if h == 0 {
				p.Bg = bc.BgColor
			}
			p.X = bc.innerX + oftX + (bc.BarWidth-len(bc.dataNum[i]))/2 + j
			p.Y = bc.innerY + bc.innerHeight - 2
			ps = append(ps, p)
		}
	}

	return bc.Block.chopOverflow(ps)
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/go-telegram-bot-api/telegram-bot-api"
	"log"
	"net/http"
	"net/url"
)

func InitBot(token string) (*tgbotapi.BotAPI, error) {
	bot, err := tgbotapi.NewBotAPI(token)
	if err != nil {
		return nil, err
	}

	bot.Debug = true
	log.Println("Account: ", bot.Self.UserName)

	return bot, nil
}

func InitWebHook(bot *tgbotapi.BotAPI, config Config) (tgbotapi.UpdatesChannel, error) {
	URL, err := url.Parse(config.Url)
	if err != nil {
		return nil, err
	}

	bot.SetWebhook(tgbotapi.WebhookConfig{URL: URL})

	updates := bot.ListenForWebhook(URL.Path)

	go http.ListenAndServe("localhost:"+config.Port, nil)
	if err != nil {
		return nil, err
	}

	return updates, nil
}

func ProcessUpdates(updates tgbotapi.UpdatesChannel, bot *tgbotapi.BotAPI) {

	for update := range updates {
		if update.Message == nil {
			continue
		}

		log.Println(update.Message.Chat.ID, update.Message.Text)
	}
}
<commit_msg>test commit<commit_after>package main

import (
	"github.com/go-telegram-bot-api/telegram-bot-api"
	"log"
	"net/http"
	"net/url"
)

func InitBot(token string) (*tgbotapi.BotAPI, error) {
	bot, err := tgbotapi.NewBotAPI(token)
	if err != nil {
		return nil, err
	}

	bot.Debug = true
	log.Println("Account: ", bot.Self.UserName)

	return bot, nil
}

func InitWebHook(bot *tgbotapi.BotAPI, config Config) (tgbotapi.UpdatesChannel, error) {
	URL, err := url.Parse(config.Url)
	if err != nil {
		return nil, err
	}

	bot.SetWebhook(tgbotapi.WebhookConfig{URL: URL})

	updates := bot.ListenForWebhook(URL.Path)

	go http.ListenAndServe("localhost:"+config.Port, nil)
	if err != nil {
		return nil, err
	}

	return updates, nil
}

func ProcessUpdates(updates tgbotapi.UpdatesChannel, bot *tgbotapi.BotAPI) {

	for update := range updates {
		if update.Message == nil {
			continue
		}

		log.Println(update.Message.Chat.ID, update.Message.Text, "olol")
	}
}
<|endoftext|>


<commit_before>package game

import (
	"image"
	"image/color"
	"math"

	"github.com/Bredgren/game1/game/camera"
	"github.com/Bredgren/game1/game/util"
	"github.com/Bredgren/geo"
	"github.com/hajimehoshi/ebiten"
)

type background struct {
	skycolor1      color.Color
	skyclor1       color.Color
	maxHeight      float64
	clouds         []*ebiten.Image
	cloudScaleMin  geo.Vec
	cloudScaleMax  geo.Vec
	cloudMinHight  float64
	cloudThickness float64
	padding        float64

	groundColor color.Color

	// cloudTest *ebiten.Image
	// cloudW    int
	// cloudH    int
	// cloudX    float64
	// cloudY    float64
}

func NewBackground() *background {
	b := &background{
		skycolor1: color.NRGBA{255, 140, 68, 255},
		skyclor1:  color.NRGBA{0, 0, 10, 255},
		maxHeight: 700, // When the background becomes dark
		clouds: []*ebiten.Image{
			// These were found manually with the cloudFinder method below
			makeCloud(192, 90, 2.24, 0.77),
			makeCloud(80, 80, -6.38, -0.55),
			makeCloud(73, 64, -8.57, -10.46),
			makeCloud(84, 97, -10.27, -1.56),
			makeCloud(147, 124, -14.71, 2.8),
			makeCloud(140, 153, 2.85, 14.22),
			makeCloud(130, 157, 13.3, 21.91),
			makeCloud(105, 65, 13.66, 23.44),
			makeCloud(94, 184, 27.74, 28.81),
			makeCloud(104, 84, 34.29, 32.91),
		},
		cloudScaleMin:  geo.VecXY(0.5, 0.5),
		cloudScaleMax:  geo.VecXY(10, 2),
		cloudMinHight:  150, // Lowest a cloud can be
		cloudThickness: 700, // Vertical size of the area a cloud can be

		groundColor: color.NRGBA{158, 37, 140, 255},

		// cloudTest: makeCloud(100, 100, 0, 0),
		// cloudW:    100,
		// cloudH:    100,
		// cloudX:    0,
		// cloudY:    0,
	}

	// To make sure clouds don't suddenly appear on screen we select a padding equal to
	// the maximum size of a cloud and we will draw clouds off screen up to that distance.
	for _, cloud := range b.clouds {
		w, h := geo.I2F2(cloud.Size())
		diagonal := math.Hypot(w, h)
		max := diagonal * math.Max(b.cloudScaleMax.X, b.cloudScaleMax.Y)
		b.padding = math.Max(max, b.padding)
	}

	return b
}

func (b *background) Draw(dst *ebiten.Image, cam *camera.Camera) {
	height := geo.Clamp(-cam.Center().Y, 0, b.maxHeight) / b.maxHeight
	dst.Fill(util.LerpColor(b.skycolor1, b.skyclor1, height))

	// b.cloudFinder(dst, cam)

	b.drawClouds(dst, cam)
}

func (b *background) drawClouds(dst *ebiten.Image, cam *camera.Camera) {
	topLeft := cam.WorldCoords(geo.VecXY(-b.padding, -b.padding))
	screenSize := geo.VecXYi(dst.Size())
	bottomRight := cam.WorldCoords(geo.VecXY(b.padding, b.padding).Plus(screenSize))
	area := geo.RectCornersVec(topLeft, bottomRight)

	// cutoff is used to create some cloudless gaps
	cutoff := 0.6
	// gap is the spacing between clouds, lowering creates more/thicker clouds
	gap := 50

	opts := ebiten.DrawImageOptions{}
	// Round to nearest mutliple of gap becuase we need the x values to be consistent.
	left := float64(((int(area.Left()) + gap/2) / gap) * gap)
	right := float64(((int(area.Right()) + gap/2) / gap) * gap)
	for x := left; x < right; x += float64(gap) {
		noise := geo.Perlin(x*0.5, 0.12345, 0.678901)
		if noise < cutoff {
			continue
		}
		y := geo.Map(noise, cutoff, 1, -b.cloudMinHight, -b.cloudMinHight-b.cloudThickness)

		noise2 := geo.Perlin(x, 0.678901, 0.12345)

		opts.GeoM.Reset()

		opts.GeoM.Rotate(noise2 * 2 * math.Pi)

		xScale := geo.Map(noise2, 0, 1, b.cloudScaleMin.X, b.cloudScaleMax.X)
		yScale := geo.Map(noise2, 0, 1, b.cloudScaleMin.Y, b.cloudScaleMax.Y)
		opts.GeoM.Scale(xScale, yScale)

		pos := cam.ScreenCoords(geo.VecXY(x, y))
		opts.GeoM.Translate(pos.XY())

		cloudIndex := int(math.Floor(noise2 * float64(len(b.clouds)+1)))
		dst.DrawImage(b.clouds[cloudIndex], &opts)
	}
}

func makeCloud(width, height int, xOff, yOff float64) *ebiten.Image {
	pix := image.NewRGBA(image.Rect(0, 0, width, height))
	for i := 0; i < width*height; i++ {
		x, y := float64(i%width), float64(i/width)
		// By filtering out values < 0.5 then rescaling between 0 and 1 we get more isolated
		// clouds. This way we can more easily contain a cloud in a rectangle and not cutoff
		// others at the edges of the rectangle.
		val := geo.Map(filter(geo.PerlinOctave(x*0.01+xOff, y*0.01+yOff, 0, 3, 0.5), 0.5), 0.5, 1, 0, 1)
		pix.Pix[4*i+3] = uint8(0xff * val)
	}

	img, _ := ebiten.NewImage(width, height, ebiten.FilterLinear)
	img.ReplacePixels(pix.Pix)
	return img
}

func filter(n, min float64) float64 {
	if n > min {
		return n
	}
	return 0
}

// func (b *background) cloudFinder(dst *ebiten.Image, cam *camera.Camera) {
// 	speed := 1.0
// 	if ebiten.IsKeyPressed(ebiten.KeyShift) {
// 		speed = 5
// 	}
//
// 	change := false
// 	if ebiten.IsKeyPressed(ebiten.KeyLeft) {
// 		b.cloudX -= 0.01 * speed
// 		change = true
// 	}
// 	if ebiten.IsKeyPressed(ebiten.KeyRight) {
// 		b.cloudX += 0.01 * speed
// 		change = true
// 	}
// 	if ebiten.IsKeyPressed(ebiten.KeyUp) {
// 		b.cloudY -= 0.01 * speed
// 		change = true
// 	}
// 	if ebiten.IsKeyPressed(ebiten.KeyDown) {
// 		b.cloudY += 0.01 * speed
// 		change = true
// 	}
// 	if ebiten.IsKeyPressed(ebiten.KeyJ) {
// 		b.cloudW -= 1 * int(speed)
// 		change = true
// 	}
// 	if ebiten.IsKeyPressed(ebiten.KeyL) {
// 		b.cloudW += 1 * int(speed)
// 		change = true
// 	}
// 	if ebiten.IsKeyPressed(ebiten.KeyI) {
// 		b.cloudH -= 1 * int(speed)
// 		change = true
// 	}
// 	if ebiten.IsKeyPressed(ebiten.KeyK) {
// 		b.cloudH += 1 * int(speed)
// 		change = true
// 	}
//
// 	if change {
// 		log.Println(b.cloudW, b.cloudH, b.cloudX, b.cloudY)
// 		b.cloudTest = makeCloud(b.cloudW, b.cloudH, b.cloudX, b.cloudY)
// 	}
//
// 	pos := cam.ScreenCoords(geo.VecXY(-50, 0))
// 	opts := ebiten.DrawImageOptions{}
// 	opts.GeoM.Scale(1, 1)
// 	opts.GeoM.Translate(pos.XY())
// 	dst.DrawImage(b.cloudTest, &opts)
// }
<commit_msg>Draw ground<commit_after>package game

import (
	"image"
	"image/color"
	"math"

	"github.com/Bredgren/game1/game/camera"
	"github.com/Bredgren/game1/game/util"
	"github.com/Bredgren/geo"
	"github.com/hajimehoshi/ebiten"
)

type background struct {
	skycolor1      color.Color
	skyclor1       color.Color
	maxHeight      float64
	clouds         []*ebiten.Image
	cloudScaleMin  geo.Vec
	cloudScaleMax  geo.Vec
	cloudMinHight  float64
	cloudThickness float64
	padding        float64

	groundColor color.Color
	groundImg   *ebiten.Image

	// cloudTest *ebiten.Image
	// cloudW    int
	// cloudH    int
	// cloudX    float64
	// cloudY    float64
}

func NewBackground() *background {
	b := &background{
		skycolor1: color.NRGBA{255, 140, 68, 255},
		skyclor1:  color.NRGBA{0, 0, 10, 255},
		maxHeight: 700, // When the background becomes dark
		clouds: []*ebiten.Image{
			// These were found manually with the cloudFinder method below
			makeCloud(192, 90, 2.24, 0.77),
			makeCloud(80, 80, -6.38, -0.55),
			makeCloud(73, 64, -8.57, -10.46),
			makeCloud(84, 97, -10.27, -1.56),
			makeCloud(147, 124, -14.71, 2.8),
			makeCloud(140, 153, 2.85, 14.22),
			makeCloud(130, 157, 13.3, 21.91),
			makeCloud(105, 65, 13.66, 23.44),
			makeCloud(94, 184, 27.74, 28.81),
			makeCloud(104, 84, 34.29, 32.91),
		},
		cloudScaleMin:  geo.VecXY(0.5, 0.5),
		cloudScaleMax:  geo.VecXY(10, 2),
		cloudMinHight:  150, // Lowest a cloud can be
		cloudThickness: 700, // Vertical size of the area a cloud can be

		groundColor: color.NRGBA{60, 60, 60, 255},

		// cloudTest: makeCloud(100, 100, 0, 0),
		// cloudW:    100,
		// cloudH:    100,
		// cloudX:    0,
		// cloudY:    0,
	}

	// To make sure clouds don't suddenly appear on screen we select a padding equal to
	// the maximum size of a cloud and we will draw clouds off screen up to that distance.
	for _, cloud := range b.clouds {
		w, h := geo.I2F2(cloud.Size())
		diagonal := math.Hypot(w, h)
		max := diagonal * math.Max(b.cloudScaleMax.X, b.cloudScaleMax.Y)
		b.padding = math.Max(max, b.padding)
	}

	b.groundImg, _ = ebiten.NewImage(1, 1, ebiten.FilterNearest)
	b.groundImg.Fill(b.groundColor)

	return b
}

func (b *background) Draw(dst *ebiten.Image, cam *camera.Camera) {
	height := geo.Clamp(-cam.Center().Y, 0, b.maxHeight) / b.maxHeight
	dst.Fill(util.LerpColor(b.skycolor1, b.skyclor1, height))

	// b.cloudFinder(dst, cam)

	b.drawClouds(dst, cam)
	b.drawGround(dst, cam)
}

func (b *background) drawClouds(dst *ebiten.Image, cam *camera.Camera) {
	topLeft := cam.WorldCoords(geo.VecXY(-b.padding, -b.padding))
	screenSize := geo.VecXYi(dst.Size())
	bottomRight := cam.WorldCoords(geo.VecXY(b.padding, b.padding).Plus(screenSize))
	area := geo.RectCornersVec(topLeft, bottomRight)

	// cutoff is used to create some cloudless gaps
	cutoff := 0.6
	// gap is the spacing between clouds, lowering creates more/thicker clouds
	gap := 50

	opts := ebiten.DrawImageOptions{}
	// Round to nearest mutliple of gap becuase we need the x values to be consistent.
	left := float64(((int(area.Left()) + gap/2) / gap) * gap)
	right := float64(((int(area.Right()) + gap/2) / gap) * gap)
	for x := left; x < right; x += float64(gap) {
		noise := geo.Perlin(x*0.5, 0.12345, 0.678901)
		if noise < cutoff {
			continue
		}
		y := geo.Map(noise, cutoff, 1, -b.cloudMinHight, -b.cloudMinHight-b.cloudThickness)

		noise2 := geo.Perlin(x, 0.678901, 0.12345)

		opts.GeoM.Reset()

		opts.GeoM.Rotate(noise2 * 2 * math.Pi)

		xScale := geo.Map(noise2, 0, 1, b.cloudScaleMin.X, b.cloudScaleMax.X)
		yScale := geo.Map(noise2, 0, 1, b.cloudScaleMin.Y, b.cloudScaleMax.Y)
		opts.GeoM.Scale(xScale, yScale)

		pos := cam.ScreenCoords(geo.VecXY(x, y))
		opts.GeoM.Translate(pos.XY())

		cloudIndex := int(math.Floor(noise2 * float64(len(b.clouds)+1)))
		dst.DrawImage(b.clouds[cloudIndex], &opts)
	}
}

func (b *background) drawGround(dst *ebiten.Image, cam *camera.Camera) {
	dstSize := geo.VecXYi(dst.Size())
	cameraBottomRight := cam.WorldCoords(dstSize)
	if cameraBottomRight.Y < 0 {
		return // Ground is not visible
	}

	cameraTopLeft := cam.WorldCoords(geo.Vec0)
	groundTopLeft := geo.VecXY(cameraTopLeft.X, 0)
	topLeft := cam.ScreenCoords(groundTopLeft)

	groundArea := geo.RectCornersVec(topLeft, dstSize)

	opts := ebiten.DrawImageOptions{}
	opts.GeoM.Scale(groundArea.Size())
	opts.GeoM.Translate(groundArea.TopLeft())

	dst.DrawImage(b.groundImg, &opts)
}

func makeCloud(width, height int, xOff, yOff float64) *ebiten.Image {
	pix := image.NewRGBA(image.Rect(0, 0, width, height))
	for i := 0; i < width*height; i++ {
		x, y := float64(i%width), float64(i/width)
		// By filtering out values < 0.5 then rescaling between 0 and 1 we get more isolated
		// clouds. This way we can more easily contain a cloud in a rectangle and not cutoff
		// others at the edges of the rectangle.
		val := geo.Map(filter(geo.PerlinOctave(x*0.01+xOff, y*0.01+yOff, 0, 3, 0.5), 0.5), 0.5, 1, 0, 1)
		pix.Pix[4*i+3] = uint8(0xff * val)
	}

	img, _ := ebiten.NewImage(width, height, ebiten.FilterLinear)
	img.ReplacePixels(pix.Pix)
	return img
}

func filter(n, min float64) float64 {
	if n > min {
		return n
	}
	return 0
}

// func (b *background) cloudFinder(dst *ebiten.Image, cam *camera.Camera) {
// 	speed := 1.0
// 	if ebiten.IsKeyPressed(ebiten.KeyShift) {
// 		speed = 5
// 	}
//
// 	change := false
// 	if ebiten.IsKeyPressed(ebiten.KeyLeft) {
// 		b.cloudX -= 0.01 * speed
// 		change = true
// 	}
// 	if ebiten.IsKeyPressed(ebiten.KeyRight) {
// 		b.cloudX += 0.01 * speed
// 		change = true
// 	}
// 	if ebiten.IsKeyPressed(ebiten.KeyUp) {
// 		b.cloudY -= 0.01 * speed
// 		change = true
// 	}
// 	if ebiten.IsKeyPressed(ebiten.KeyDown) {
// 		b.cloudY += 0.01 * speed
// 		change = true
// 	}
// 	if ebiten.IsKeyPressed(ebiten.KeyJ) {
// 		b.cloudW -= 1 * int(speed)
// 		change = true
// 	}
// 	if ebiten.IsKeyPressed(ebiten.KeyL) {
// 		b.cloudW += 1 * int(speed)
// 		change = true
// 	}
// 	if ebiten.IsKeyPressed(ebiten.KeyI) {
// 		b.cloudH -= 1 * int(speed)
// 		change = true
// 	}
// 	if ebiten.IsKeyPressed(ebiten.KeyK) {
// 		b.cloudH += 1 * int(speed)
// 		change = true
// 	}
//
// 	if change {
// 		log.Println(b.cloudW, b.cloudH, b.cloudX, b.cloudY)
// 		b.cloudTest = makeCloud(b.cloudW, b.cloudH, b.cloudX, b.cloudY)
// 	}
//
// 	pos := cam.ScreenCoords(geo.VecXY(-50, 0))
// 	opts := ebiten.DrawImageOptions{}
// 	opts.GeoM.Scale(1, 1)
// 	opts.GeoM.Translate(pos.XY())
// 	dst.DrawImage(b.cloudTest, &opts)
// }
<|endoftext|>


<commit_before>// Package ganglia has convenience functions for using ganglia.
//
// This package uses the vlog utility package for logging various messages
// if the vlog.Verbose boolean is set to true.
package ganglia

import (
	"flag"
	"fmt"
	"html"
	"io/ioutil"
	"log"
	"net"
	"os"
	"regexp"
	"runtime"
	"strconv"
	"sync"
	"syscall"
	"time"

	"github.com/fastly/go-utils/debug"
	"github.com/fastly/go-utils/stopper"
	"github.com/fastly/go-utils/vlog"
	"github.com/jbuchbinder/go-gmetric/gmetric"
)

const (
	String = gmetric.VALUE_STRING
	Ushort = gmetric.VALUE_UNSIGNED_SHORT
	Short  = gmetric.VALUE_SHORT
	Uint   = gmetric.VALUE_UNSIGNED_INT
	Int    = gmetric.VALUE_INT
	Float  = gmetric.VALUE_FLOAT
	Double = gmetric.VALUE_DOUBLE
)

var (
	GmondConfig string
	Interval    time.Duration

	gmondChannelRe  = regexp.MustCompile("udp_send_channel\\s*{([^}]+)}")
	gmondHostPortRe = regexp.MustCompile("(host|port)\\s*=\\s*(\\S+)")

	globalReporter struct {
		sync.Once
		*Reporter
	}
)

func init() {
	flag.StringVar(&GmondConfig, "gmond-config", "/etc/ganglia/gmond.conf", "location of gmond.conf")
	flag.DurationVar(&Interval, "ganglia-interval", 9*time.Second, "time between gmetric updates")
}

type gmetricSample struct {
	value interface{}
	when  time.Time
}
type Reporter struct {
	*stopper.ChanStopper
	prefix    string
	callbacks []ReporterCallback
	previous  map[string]gmetricSample
	groupName string
	dmax      uint32
}

// MetricSender takes the following parameters:
//   name: an arbitrary metric name
//   value: the metric's current value
//   metricType: one of GmetricString, GmetricUshort, GmetricShort, GmetricUint, GmetricInt, GmetricFloat, or GmetricDouble
//   units: a label to include on the metric's Y axis
//   rate: if true, send the rate relative to the last sample instead of an absolute value
type MetricSender func(name string, value string, metricType uint32, units string, rate bool)

type ReporterCallback func(MetricSender)

// Gmetric returns a global Reporter that clients may hook into by
// calling AddCallback.
func Gmetric() *Reporter {
	globalReporter.Do(func() {
		globalReporter.Reporter = NewGangliaReporter(Interval)
		globalReporter.AddCallback(CommonGmetrics)
	})
	return globalReporter.Reporter
}

// Configure sets group name and prefix of a reporter and returns the reporter.
func (gr *Reporter) Configure(groupName, prefix string) *Reporter {
	if gr == nil {
		return nil
	}
	gr.prefix = html.EscapeString(prefix)
	gr.groupName = html.EscapeString(groupName)
	return gr
}

// SetDmax configures the amount of time that metrics are valid for in the
// tsdb. The default of 0 means forever. Time resolution is only respected to
// the second.
func (gr *Reporter) SetDmax(dmax time.Duration) *Reporter {
	if gr == nil {
		return nil
	}
	gr.dmax = uint32(dmax.Seconds())
	return gr
}

// Convenience wrapper for Gmetric().AddCallback():
//
//   AddGmetrics(func(gmetric MetricSender) {
// 	   gmetric("profit", "1000000.00", GmetricFloat, "dollars", true)
//   })
func AddGmetrics(callback ReporterCallback) {
	Gmetric().AddCallback(callback)
}

func NewGmetric() (*gmetric.Gmetric, error) {
	b, err := ioutil.ReadFile(GmondConfig)
	if err != nil {
		return nil, err
	}
	stanzas := gmondChannelRe.FindAllStringSubmatch(string(b), -1)
	if len(stanzas) == 0 {
		return nil, fmt.Errorf("No udp_send_channel stanzas found in %s", GmondConfig)
	}

	servers := make([]gmetric.Server, 0)
	for _, stanza := range stanzas {
		var host, port string
		for _, match := range gmondHostPortRe.FindAllStringSubmatch(stanza[1], 2) {
			if match[1] == "host" {
				host = match[2]
			} else if match[1] == "port" {
				port = match[2]
			}
		}
		if host == "" || port == "" {
			return nil, fmt.Errorf("Missing host or port from %s stanza %q", GmondConfig, stanza[0])
		}
		portNum, err := strconv.Atoi(port)
		if err != nil {
			return nil, err
		}
		ips, err := net.LookupIP(host)
		if err != nil {
			return nil, err
		}
		for _, ip := range ips {
			vlog.VLogf("Reporting to Ganglia server at %s:%d", ip, portNum)
			servers = append(servers, gmetric.Server{ip, portNum})
		}
	}

	// see http://sourceforge.net/apps/trac/ganglia/wiki/gmetric_spoofing
	hostname, _ := os.Hostname()
	spoofName := fmt.Sprintf("%s:%s", hostname, hostname)

	gm := gmetric.Gmetric{Spoof: spoofName}
	for _, server := range servers {
		gm.AddServer(server)
	}
	return &gm, nil
}

// NewGangliaReporter returns a Reporter object which calls callback every
// interval with the given group name. callback is passed a Gmetric whose
// servers are initialized from the hosts gmond.conf. Calling Stop on the
// Reporter will cease its operation.
func NewGangliaReporter(interval time.Duration) *Reporter {
	return NewGangliaReporterWithOptions(interval, "")
}

// NewGangliaReporterWithOptions is NewGangliaReporter with the groupName.
func NewGangliaReporterWithOptions(interval time.Duration, groupName string) *Reporter {
	gm, err := NewGmetric()
	if err != nil {
		vlog.VLogfQuiet("ganglia", "Couldn't start Ganglia reporter: %s", err)
		return nil
	} else if gm == nil {
		return nil
	}
	stopper := stopper.NewChanStopper()
	gr := &Reporter{
		ChanStopper: stopper,
		prefix:      "",
		callbacks:   []ReporterCallback{},
		previous:    make(map[string]gmetricSample),
		groupName:   groupName,
		dmax:        0,
	}
	go func() {
		defer stopper.Done()
		for {
			select {
			case <-stopper.Chan:
				return
			case <-time.After(interval):
				go func() {
					// SendMetric "opens" and "closes" UDP connections each
					// time, but since we expect the callback to send several
					// metrics at once, avoid that here.
					conns := gm.OpenConnections()
					n := 0
					sender := func(name string, value string, metricType uint32, units string, rate bool) {
						v := value
						if rate {
							prev, exists := gr.previous[name]
							units += "/sec"

							now := time.Now()

							switch metricType {
							case Ushort, Short, Uint, Int:
								i, err := strconv.Atoi(value)
								if err != nil {
									vlog.VLogfQuiet(name, "Value %q doesn't look like an int: %s", value, err)
									return
								}
								gr.previous[name] = gmetricSample{i, now}
								if !exists {
									return
								}
								delta := i - prev.value.(int)
								elapsed := time.Now().Sub(prev.when).Seconds()
								v = fmt.Sprint(float64(delta) / elapsed)
								// upgrade to a float to avoid loss of precision
								metricType = Float

							case Float, Double:
								f, err := strconv.ParseFloat(value, 64)
								if err != nil {
									vlog.VLogfQuiet(name, "Value %q doesn't look like a float: %s", value, err)
									return
								}
								gr.previous[name] = gmetricSample{f, now}
								if !exists {
									return
								}
								delta := f - prev.value.(float64)
								elapsed := time.Now().Sub(prev.when).Seconds()
								v = fmt.Sprint(delta / elapsed)

							case String:
								vlog.VLogfQuiet(name, "Can't compute deltas for string metric %q", value)
								return
							}
						}

						// gmetad fails to escape quotes, eventually generating
						// invalid xml. do it here as a workaround.
						v = html.EscapeString(v)
						name = html.EscapeString(name)
						units = html.EscapeString(units)

						n++
						gm.SendMetricPackets(
							gr.prefix+name, v, metricType, units,
							gmetric.SLOPE_BOTH,
							uint32(interval.Seconds()), // tmax is the expected reporting interval
							gr.dmax,
							gr.groupName,
							gmetric.PACKET_BOTH, conns,
						)
						if debug.On() {
							if rate {
								log.Printf("gmetric: name=%q, rate=%q, value=%q, type=%d, units=%q, slope=%d, tmax=%d, dmax=%v, group=%q, packet=%d",
									gr.prefix+name, v, value, metricType, units, gmetric.SLOPE_BOTH,
									uint32(interval.Seconds()), gr.dmax, gr.groupName, gmetric.PACKET_BOTH,
								)
							} else {
								log.Printf("gmetric: name=%q, value=%q, type=%d, units=%q, slope=%d, tmax=%d, dmax=%v, group=%q, packet=%d",
									gr.prefix+name, v, metricType, units, gmetric.SLOPE_BOTH,
									uint32(interval.Seconds()), gr.dmax, gr.groupName, gmetric.PACKET_BOTH,
								)
							}
						}
					}
					defer gm.CloseConnections(conns)
					for _, callback := range gr.callbacks {
						callback(sender)
					}
					if debug.On() {
						log.Printf("Published %d metrics to Ganglia", n)
					}
				}()
			}
		}
	}()
	return gr
}

func (gr *Reporter) AddCallback(callback ReporterCallback) {
	if gr == nil {
		return
	}
	gr.callbacks = append(gr.callbacks, callback)
}

func (g *Reporter) Stop() {
	if g == nil {
		return
	}
	g.ChanStopper.Stop()
}

func CommonGmetrics(gmetric MetricSender) {
	gmetric("goroutines", fmt.Sprintf("%d", runtime.NumGoroutine()), Uint, "num", false)

	var mem runtime.MemStats
	runtime.ReadMemStats(&mem)
	gmetric("mem_alloc", fmt.Sprintf("%d", mem.Alloc), Uint, "bytes", false)
	gmetric("mem_sys", fmt.Sprintf("%d", mem.Sys), Uint, "bytes", false)
	gmetric("mem_gc_pause_last", fmt.Sprintf("%.6f", float64(mem.PauseNs[(mem.NumGC+255)%256])/1e6), Float, "ms", false)
	var gcPauseMax uint64
	for _, v := range mem.PauseNs {
		if v > gcPauseMax {
			gcPauseMax = v
		}
	}
	gmetric("mem_gc_pause_max", fmt.Sprintf("%.6f", float64(gcPauseMax)/1e6), Float, "ms", false)
	gmetric("mem_gc_pause_total", fmt.Sprintf("%.6f", float64(mem.PauseTotalNs)/1e6), Float, "ms", true)
	since := time.Now().Sub(time.Unix(0, int64(mem.LastGC))).Seconds()
	gmetric("mem_gc_pause_since", fmt.Sprintf("%.6f", since), Float, "sec", false)

	var r syscall.Rusage
	if syscall.Getrusage(syscall.RUSAGE_SELF, &r) == nil {
		gmetric("rusage_utime", fmt.Sprintf("%.6f", float64(r.Utime.Nano())/1e9), Float, "cpusecs", true)
		gmetric("rusage_stime", fmt.Sprintf("%.6f", float64(r.Stime.Nano())/1e9), Float, "cpusecs", true)
		gmetric("cpu_pct", fmt.Sprintf("%.4f", 100*float64((r.Utime.Nano()+r.Stime.Nano()))/1e9), Float, "%", true)
	}
}
<commit_msg>Fix race in map access<commit_after>// Package ganglia has convenience functions for using ganglia.
//
// This package uses the vlog utility package for logging various messages
// if the vlog.Verbose boolean is set to true.
package ganglia

import (
	"flag"
	"fmt"
	"html"
	"io/ioutil"
	"log"
	"net"
	"os"
	"regexp"
	"runtime"
	"strconv"
	"sync"
	"syscall"
	"time"

	"github.com/fastly/go-utils/debug"
	"github.com/fastly/go-utils/stopper"
	"github.com/fastly/go-utils/vlog"
	"github.com/jbuchbinder/go-gmetric/gmetric"
)

const (
	String = gmetric.VALUE_STRING
	Ushort = gmetric.VALUE_UNSIGNED_SHORT
	Short  = gmetric.VALUE_SHORT
	Uint   = gmetric.VALUE_UNSIGNED_INT
	Int    = gmetric.VALUE_INT
	Float  = gmetric.VALUE_FLOAT
	Double = gmetric.VALUE_DOUBLE
)

var (
	GmondConfig string
	Interval    time.Duration

	gmondChannelRe  = regexp.MustCompile("udp_send_channel\\s*{([^}]+)}")
	gmondHostPortRe = regexp.MustCompile("(host|port)\\s*=\\s*(\\S+)")

	globalReporter struct {
		sync.Once
		*Reporter
	}
)

func init() {
	flag.StringVar(&GmondConfig, "gmond-config", "/etc/ganglia/gmond.conf", "location of gmond.conf")
	flag.DurationVar(&Interval, "ganglia-interval", 9*time.Second, "time between gmetric updates")
}

type gmetricSample struct {
	value interface{}
	when  time.Time
}
type Reporter struct {
	*stopper.ChanStopper
	prefix    string
	callbacks []ReporterCallback
	mu        sync.Mutex // guards previous
	previous  map[string]gmetricSample
	groupName string
	dmax      uint32
}

// MetricSender takes the following parameters:
//   name: an arbitrary metric name
//   value: the metric's current value
//   metricType: one of GmetricString, GmetricUshort, GmetricShort, GmetricUint, GmetricInt, GmetricFloat, or GmetricDouble
//   units: a label to include on the metric's Y axis
//   rate: if true, send the rate relative to the last sample instead of an absolute value
type MetricSender func(name string, value string, metricType uint32, units string, rate bool)

type ReporterCallback func(MetricSender)

// Gmetric returns a global Reporter that clients may hook into by
// calling AddCallback.
func Gmetric() *Reporter {
	globalReporter.Do(func() {
		globalReporter.Reporter = NewGangliaReporter(Interval)
		globalReporter.AddCallback(CommonGmetrics)
	})
	return globalReporter.Reporter
}

// Configure sets group name and prefix of a reporter and returns the reporter.
func (gr *Reporter) Configure(groupName, prefix string) *Reporter {
	if gr == nil {
		return nil
	}
	gr.prefix = html.EscapeString(prefix)
	gr.groupName = html.EscapeString(groupName)
	return gr
}

// SetDmax configures the amount of time that metrics are valid for in the
// tsdb. The default of 0 means forever. Time resolution is only respected to
// the second.
func (gr *Reporter) SetDmax(dmax time.Duration) *Reporter {
	if gr == nil {
		return nil
	}
	gr.dmax = uint32(dmax.Seconds())
	return gr
}

// Convenience wrapper for Gmetric().AddCallback():
//
//   AddGmetrics(func(gmetric MetricSender) {
// 	   gmetric("profit", "1000000.00", GmetricFloat, "dollars", true)
//   })
func AddGmetrics(callback ReporterCallback) {
	Gmetric().AddCallback(callback)
}

func NewGmetric() (*gmetric.Gmetric, error) {
	b, err := ioutil.ReadFile(GmondConfig)
	if err != nil {
		return nil, err
	}
	stanzas := gmondChannelRe.FindAllStringSubmatch(string(b), -1)
	if len(stanzas) == 0 {
		return nil, fmt.Errorf("No udp_send_channel stanzas found in %s", GmondConfig)
	}

	servers := make([]gmetric.Server, 0)
	for _, stanza := range stanzas {
		var host, port string
		for _, match := range gmondHostPortRe.FindAllStringSubmatch(stanza[1], 2) {
			if match[1] == "host" {
				host = match[2]
			} else if match[1] == "port" {
				port = match[2]
			}
		}
		if host == "" || port == "" {
			return nil, fmt.Errorf("Missing host or port from %s stanza %q", GmondConfig, stanza[0])
		}
		portNum, err := strconv.Atoi(port)
		if err != nil {
			return nil, err
		}
		ips, err := net.LookupIP(host)
		if err != nil {
			return nil, err
		}
		for _, ip := range ips {
			vlog.VLogf("Reporting to Ganglia server at %s:%d", ip, portNum)
			servers = append(servers, gmetric.Server{ip, portNum})
		}
	}

	// see http://sourceforge.net/apps/trac/ganglia/wiki/gmetric_spoofing
	hostname, _ := os.Hostname()
	spoofName := fmt.Sprintf("%s:%s", hostname, hostname)

	gm := gmetric.Gmetric{Spoof: spoofName}
	for _, server := range servers {
		gm.AddServer(server)
	}
	return &gm, nil
}

// NewGangliaReporter returns a Reporter object which calls callback every
// interval with the given group name. callback is passed a Gmetric whose
// servers are initialized from the hosts gmond.conf. Calling Stop on the
// Reporter will cease its operation.
func NewGangliaReporter(interval time.Duration) *Reporter {
	return NewGangliaReporterWithOptions(interval, "")
}

// NewGangliaReporterWithOptions is NewGangliaReporter with the groupName.
func NewGangliaReporterWithOptions(interval time.Duration, groupName string) *Reporter {
	gm, err := NewGmetric()
	if err != nil {
		vlog.VLogfQuiet("ganglia", "Couldn't start Ganglia reporter: %s", err)
		return nil
	} else if gm == nil {
		return nil
	}
	stopper := stopper.NewChanStopper()
	gr := &Reporter{
		ChanStopper: stopper,
		prefix:      "",
		callbacks:   []ReporterCallback{},
		previous:    make(map[string]gmetricSample),
		groupName:   groupName,
		dmax:        0,
	}
	go func() {
		defer stopper.Done()
		for {
			select {
			case <-stopper.Chan:
				return
			case <-time.After(interval):
				go func() {
					// SendMetric "opens" and "closes" UDP connections each
					// time, but since we expect the callback to send several
					// metrics at once, avoid that here.
					conns := gm.OpenConnections()
					n := 0
					sender := func(name string, value string, metricType uint32, units string, rate bool) {
						v := value
						if rate {
							gr.mu.Lock()
							defer gr.mu.Unlock()
							prev, exists := gr.previous[name]
							units += "/sec"

							now := time.Now()

							switch metricType {
							case Ushort, Short, Uint, Int:
								i, err := strconv.Atoi(value)
								if err != nil {
									vlog.VLogfQuiet(name, "Value %q doesn't look like an int: %s", value, err)
									return
								}
								gr.previous[name] = gmetricSample{i, now}
								if !exists {
									return
								}
								delta := i - prev.value.(int)
								elapsed := time.Now().Sub(prev.when).Seconds()
								v = fmt.Sprint(float64(delta) / elapsed)
								// upgrade to a float to avoid loss of precision
								metricType = Float

							case Float, Double:
								f, err := strconv.ParseFloat(value, 64)
								if err != nil {
									vlog.VLogfQuiet(name, "Value %q doesn't look like a float: %s", value, err)
									return
								}
								gr.previous[name] = gmetricSample{f, now}
								if !exists {
									return
								}
								delta := f - prev.value.(float64)
								elapsed := time.Now().Sub(prev.when).Seconds()
								v = fmt.Sprint(delta / elapsed)

							case String:
								vlog.VLogfQuiet(name, "Can't compute deltas for string metric %q", value)
								return
							}
						}

						// gmetad fails to escape quotes, eventually generating
						// invalid xml. do it here as a workaround.
						v = html.EscapeString(v)
						name = html.EscapeString(name)
						units = html.EscapeString(units)

						n++
						gm.SendMetricPackets(
							gr.prefix+name, v, metricType, units,
							gmetric.SLOPE_BOTH,
							uint32(interval.Seconds()), // tmax is the expected reporting interval
							gr.dmax,
							gr.groupName,
							gmetric.PACKET_BOTH, conns,
						)
						if debug.On() {
							if rate {
								log.Printf("gmetric: name=%q, rate=%q, value=%q, type=%d, units=%q, slope=%d, tmax=%d, dmax=%v, group=%q, packet=%d",
									gr.prefix+name, v, value, metricType, units, gmetric.SLOPE_BOTH,
									uint32(interval.Seconds()), gr.dmax, gr.groupName, gmetric.PACKET_BOTH,
								)
							} else {
								log.Printf("gmetric: name=%q, value=%q, type=%d, units=%q, slope=%d, tmax=%d, dmax=%v, group=%q, packet=%d",
									gr.prefix+name, v, metricType, units, gmetric.SLOPE_BOTH,
									uint32(interval.Seconds()), gr.dmax, gr.groupName, gmetric.PACKET_BOTH,
								)
							}
						}
					}
					defer gm.CloseConnections(conns)
					for _, callback := range gr.callbacks {
						callback(sender)
					}
					if debug.On() {
						log.Printf("Published %d metrics to Ganglia", n)
					}
				}()
			}
		}
	}()
	return gr
}

func (gr *Reporter) AddCallback(callback ReporterCallback) {
	if gr == nil {
		return
	}
	gr.callbacks = append(gr.callbacks, callback)
}

func (g *Reporter) Stop() {
	if g == nil {
		return
	}
	g.ChanStopper.Stop()
}

func CommonGmetrics(gmetric MetricSender) {
	gmetric("goroutines", fmt.Sprintf("%d", runtime.NumGoroutine()), Uint, "num", false)

	var mem runtime.MemStats
	runtime.ReadMemStats(&mem)
	gmetric("mem_alloc", fmt.Sprintf("%d", mem.Alloc), Uint, "bytes", false)
	gmetric("mem_sys", fmt.Sprintf("%d", mem.Sys), Uint, "bytes", false)
	gmetric("mem_gc_pause_last", fmt.Sprintf("%.6f", float64(mem.PauseNs[(mem.NumGC+255)%256])/1e6), Float, "ms", false)
	var gcPauseMax uint64
	for _, v := range mem.PauseNs {
		if v > gcPauseMax {
			gcPauseMax = v
		}
	}
	gmetric("mem_gc_pause_max", fmt.Sprintf("%.6f", float64(gcPauseMax)/1e6), Float, "ms", false)
	gmetric("mem_gc_pause_total", fmt.Sprintf("%.6f", float64(mem.PauseTotalNs)/1e6), Float, "ms", true)
	since := time.Now().Sub(time.Unix(0, int64(mem.LastGC))).Seconds()
	gmetric("mem_gc_pause_since", fmt.Sprintf("%.6f", since), Float, "sec", false)

	var r syscall.Rusage
	if syscall.Getrusage(syscall.RUSAGE_SELF, &r) == nil {
		gmetric("rusage_utime", fmt.Sprintf("%.6f", float64(r.Utime.Nano())/1e9), Float, "cpusecs", true)
		gmetric("rusage_stime", fmt.Sprintf("%.6f", float64(r.Stime.Nano())/1e9), Float, "cpusecs", true)
		gmetric("cpu_pct", fmt.Sprintf("%.4f", 100*float64((r.Utime.Nano()+r.Stime.Nano()))/1e9), Float, "%", true)
	}
}
<|endoftext|>


<commit_before>package gateway

import (
	"bytes"
	"fmt"
	"github.com/42wim/matterbridge/bridge"
	"github.com/42wim/matterbridge/bridge/config"
	log "github.com/Sirupsen/logrus"
	//	"github.com/davecgh/go-spew/spew"
	"crypto/sha1"
	"github.com/hashicorp/golang-lru"
	"github.com/peterhellberg/emojilib"
	"net/http"
	"regexp"
	"strings"
	"time"
)

type Gateway struct {
	*config.Config
	Router         *Router
	MyConfig       *config.Gateway
	Bridges        map[string]*bridge.Bridge
	Channels       map[string]*config.ChannelInfo
	ChannelOptions map[string]config.ChannelOptions
	Message        chan config.Message
	Name           string
	Messages       *lru.Cache
}

type BrMsgID struct {
	br        *bridge.Bridge
	ID        string
	ChannelID string
}

func New(cfg config.Gateway, r *Router) *Gateway {
	gw := &Gateway{Channels: make(map[string]*config.ChannelInfo), Message: r.Message,
		Router: r, Bridges: make(map[string]*bridge.Bridge), Config: r.Config}
	cache, _ := lru.New(5000)
	gw.Messages = cache
	gw.AddConfig(&cfg)
	return gw
}

func (gw *Gateway) AddBridge(cfg *config.Bridge) error {
	br := gw.Router.getBridge(cfg.Account)
	if br == nil {
		br = bridge.New(gw.Config, cfg, gw.Message)
	}
	gw.mapChannelsToBridge(br)
	gw.Bridges[cfg.Account] = br
	return nil
}

func (gw *Gateway) AddConfig(cfg *config.Gateway) error {
	gw.Name = cfg.Name
	gw.MyConfig = cfg
	gw.mapChannels()
	for _, br := range append(gw.MyConfig.In, append(gw.MyConfig.InOut, gw.MyConfig.Out...)...) {
		err := gw.AddBridge(&br)
		if err != nil {
			return err
		}
	}
	return nil
}

func (gw *Gateway) mapChannelsToBridge(br *bridge.Bridge) {
	for ID, channel := range gw.Channels {
		if br.Account == channel.Account {
			br.Channels[ID] = *channel
		}
	}
}

func (gw *Gateway) reconnectBridge(br *bridge.Bridge) {
	br.Disconnect()
	time.Sleep(time.Second * 5)
RECONNECT:
	log.Infof("Reconnecting %s", br.Account)
	err := br.Connect()
	if err != nil {
		log.Errorf("Reconnection failed: %s. Trying again in 60 seconds", err)
		time.Sleep(time.Second * 60)
		goto RECONNECT
	}
	br.Joined = make(map[string]bool)
	br.JoinChannels()
}

func (gw *Gateway) mapChannelConfig(cfg []config.Bridge, direction string) {
	for _, br := range cfg {
		if isApi(br.Account) {
			br.Channel = "api"
		}
		ID := br.Channel + br.Account
		if _, ok := gw.Channels[ID]; !ok {
			channel := &config.ChannelInfo{Name: br.Channel, Direction: direction, ID: ID, Options: br.Options, Account: br.Account,
				SameChannel: make(map[string]bool)}
			channel.SameChannel[gw.Name] = br.SameChannel
			gw.Channels[channel.ID] = channel
		} else {
			// if we already have a key and it's not our current direction it means we have a bidirectional inout
			if gw.Channels[ID].Direction != direction {
				gw.Channels[ID].Direction = "inout"
			}
		}
		gw.Channels[ID].SameChannel[gw.Name] = br.SameChannel
	}
}

func (gw *Gateway) mapChannels() error {
	gw.mapChannelConfig(gw.MyConfig.In, "in")
	gw.mapChannelConfig(gw.MyConfig.Out, "out")
	gw.mapChannelConfig(gw.MyConfig.InOut, "inout")
	return nil
}

func (gw *Gateway) getDestChannel(msg *config.Message, dest bridge.Bridge) []config.ChannelInfo {
	var channels []config.ChannelInfo

	// for messages received from the api check that the gateway is the specified one
	if msg.Protocol == "api" && gw.Name != msg.Gateway {
		return channels
	}

	// if source channel is in only, do nothing
	for _, channel := range gw.Channels {
		// lookup the channel from the message
		if channel.ID == getChannelID(*msg) {
			// we only have destinations if the original message is from an "in" (sending) channel
			if !strings.Contains(channel.Direction, "in") {
				return channels
			}
			continue
		}
	}
	for _, channel := range gw.Channels {
		if _, ok := gw.Channels[getChannelID(*msg)]; !ok {
			continue
		}

		// do samechannelgateway logic
		if channel.SameChannel[msg.Gateway] {
			if msg.Channel == channel.Name && msg.Account != dest.Account {
				channels = append(channels, *channel)
			}
			continue
		}
		if strings.Contains(channel.Direction, "out") && channel.Account == dest.Account && gw.validGatewayDest(msg, channel) {
			channels = append(channels, *channel)
		}
	}
	return channels
}

func (gw *Gateway) handleMessage(msg config.Message, dest *bridge.Bridge) []*BrMsgID {
	var brMsgIDs []*BrMsgID

	// TODO refactor
	// only slack now, check will have to be done in the different bridges.
	// we need to check if we can't use fallback or text in other bridges
	if msg.Extra != nil {
		if dest.Protocol != "discord" &&
			dest.Protocol != "slack" &&
			dest.Protocol != "mattermost" &&
			dest.Protocol != "telegram" &&
			dest.Protocol != "matrix" {
			if msg.Text == "" {
				return brMsgIDs
			}
		}
	}
	// only relay join/part when configged
	if msg.Event == config.EVENT_JOIN_LEAVE && !gw.Bridges[dest.Account].Config.ShowJoinPart {
		return brMsgIDs
	}
	// broadcast to every out channel (irc QUIT)
	if msg.Channel == "" && msg.Event != config.EVENT_JOIN_LEAVE {
		log.Debug("empty channel")
		return brMsgIDs
	}
	originchannel := msg.Channel
	origmsg := msg
	channels := gw.getDestChannel(&msg, *dest)
	for _, channel := range channels {
		// do not send to ourself
		if channel.ID == getChannelID(origmsg) {
			continue
		}
		log.Debugf("Sending %#v from %s (%s) to %s (%s)", msg, msg.Account, originchannel, dest.Account, channel.Name)
		msg.Channel = channel.Name
		msg.Avatar = gw.modifyAvatar(origmsg, dest)
		msg.Username = gw.modifyUsername(origmsg, dest)
		msg.ID = ""
		if res, ok := gw.Messages.Get(origmsg.ID); ok {
			IDs := res.([]*BrMsgID)
			for _, id := range IDs {
				// check protocol, bridge name and channelname
				// for people that reuse the same bridge multiple times. see #342
				if dest.Protocol == id.br.Protocol && dest.Name == id.br.Name && channel.ID == id.ChannelID {
					msg.ID = id.ID
				}
			}
		}
		// for api we need originchannel as channel
		if dest.Protocol == "api" {
			msg.Channel = originchannel
		}
		mID, err := dest.Send(msg)
		if err != nil {
			fmt.Println(err)
		}
		// append the message ID (mID) from this bridge (dest) to our brMsgIDs slice
		if mID != "" {
			brMsgIDs = append(brMsgIDs, &BrMsgID{dest, mID, channel.ID})
		}
	}
	return brMsgIDs
}

func (gw *Gateway) ignoreMessage(msg *config.Message) bool {
	// if we don't have the bridge, ignore it
	if _, ok := gw.Bridges[msg.Account]; !ok {
		return true
	}
	if msg.Text == "" {
		// we have an attachment or actual bytes
		if msg.Extra != nil && (msg.Extra["attachments"] != nil || len(msg.Extra["file"]) > 0) {
			return false
		}
		log.Debugf("ignoring empty message %#v from %s", msg, msg.Account)
		return true
	}
	for _, entry := range strings.Fields(gw.Bridges[msg.Account].Config.IgnoreNicks) {
		if msg.Username == entry {
			log.Debugf("ignoring %s from %s", msg.Username, msg.Account)
			return true
		}
	}
	// TODO do not compile regexps everytime
	for _, entry := range strings.Fields(gw.Bridges[msg.Account].Config.IgnoreMessages) {
		if entry != "" {
			re, err := regexp.Compile(entry)
			if err != nil {
				log.Errorf("incorrect regexp %s for %s", entry, msg.Account)
				continue
			}
			if re.MatchString(msg.Text) {
				log.Debugf("matching %s. ignoring %s from %s", entry, msg.Text, msg.Account)
				return true
			}
		}
	}
	return false
}

func (gw *Gateway) modifyUsername(msg config.Message, dest *bridge.Bridge) string {
	br := gw.Bridges[msg.Account]
	msg.Protocol = br.Protocol
	if gw.Config.General.StripNick || dest.Config.StripNick {
		re := regexp.MustCompile("[^a-zA-Z0-9]+")
		msg.Username = re.ReplaceAllString(msg.Username, "")
	}
	nick := dest.Config.RemoteNickFormat
	if nick == "" {
		nick = gw.Config.General.RemoteNickFormat
	}

	// loop to replace nicks
	for _, outer := range br.Config.ReplaceNicks {
		search := outer[0]
		replace := outer[1]
		// TODO move compile to bridge init somewhere
		re, err := regexp.Compile(search)
		if err != nil {
			log.Errorf("regexp in %s failed: %s", msg.Account, err)
			break
		}
		msg.Username = re.ReplaceAllString(msg.Username, replace)
	}

	if len(msg.Username) > 0 {
		// fix utf-8 issue #193
		i := 0
		for index := range msg.Username {
			if i == 1 {
				i = index
				break
			}
			i++
		}
		nick = strings.Replace(nick, "{NOPINGNICK}", msg.Username[:i]+""+msg.Username[i:], -1)
	}
	nick = strings.Replace(nick, "{BRIDGE}", br.Name, -1)
	nick = strings.Replace(nick, "{PROTOCOL}", br.Protocol, -1)
	nick = strings.Replace(nick, "{NICK}", msg.Username, -1)
	return nick
}

func (gw *Gateway) modifyAvatar(msg config.Message, dest *bridge.Bridge) string {
	iconurl := gw.Config.General.IconURL
	if iconurl == "" {
		iconurl = dest.Config.IconURL
	}
	iconurl = strings.Replace(iconurl, "{NICK}", msg.Username, -1)
	if msg.Avatar == "" {
		msg.Avatar = iconurl
	}
	return msg.Avatar
}

func (gw *Gateway) modifyMessage(msg *config.Message) {
	// replace :emoji: to unicode
	msg.Text = emojilib.Replace(msg.Text)
	br := gw.Bridges[msg.Account]
	// loop to replace messages
	for _, outer := range br.Config.ReplaceMessages {
		search := outer[0]
		replace := outer[1]
		// TODO move compile to bridge init somewhere
		re, err := regexp.Compile(search)
		if err != nil {
			log.Errorf("regexp in %s failed: %s", msg.Account, err)
			break
		}
		msg.Text = re.ReplaceAllString(msg.Text, replace)
	}

	// messages from api have Gateway specified, don't overwrite
	if msg.Protocol != "api" {
		msg.Gateway = gw.Name
	}
}

func (gw *Gateway) handleFiles(msg *config.Message) {
	if msg.Extra == nil || gw.Config.General.MediaServerUpload == "" {
		return
	}
	if len(msg.Extra["file"]) > 0 {
		client := &http.Client{
			Timeout: time.Second * 5,
		}
		for i, f := range msg.Extra["file"] {
			fi := f.(config.FileInfo)
			sha1sum := fmt.Sprintf("%x", sha1.Sum(*fi.Data))
			reader := bytes.NewReader(*fi.Data)
			url := gw.Config.General.MediaServerUpload + "/" + sha1sum + "/" + fi.Name
			durl := gw.Config.General.MediaServerDownload + "/" + sha1sum + "/" + fi.Name
			extra := msg.Extra["file"][i].(config.FileInfo)
			extra.URL = durl
			msg.Extra["file"][i] = extra
			req, _ := http.NewRequest("PUT", url, reader)
			req.Header.Set("Content-Type", "binary/octet-stream")
			_, err := client.Do(req)
			if err != nil {
				log.Errorf("mediaserver upload failed: %#v", err)
			}
			log.Debugf("mediaserver download URL = %s", durl)
		}
	}
}

func getChannelID(msg config.Message) string {
	return msg.Channel + msg.Account
}

func (gw *Gateway) validGatewayDest(msg *config.Message, channel *config.ChannelInfo) bool {
	return msg.Gateway == gw.Name
}

func isApi(account string) bool {
	return strings.HasPrefix(account, "api.")
}
<commit_msg>Allow xmpp to receive the extra messages when text is empty. #295<commit_after>package gateway

import (
	"bytes"
	"fmt"
	"github.com/42wim/matterbridge/bridge"
	"github.com/42wim/matterbridge/bridge/config"
	log "github.com/Sirupsen/logrus"
	//	"github.com/davecgh/go-spew/spew"
	"crypto/sha1"
	"github.com/hashicorp/golang-lru"
	"github.com/peterhellberg/emojilib"
	"net/http"
	"regexp"
	"strings"
	"time"
)

type Gateway struct {
	*config.Config
	Router         *Router
	MyConfig       *config.Gateway
	Bridges        map[string]*bridge.Bridge
	Channels       map[string]*config.ChannelInfo
	ChannelOptions map[string]config.ChannelOptions
	Message        chan config.Message
	Name           string
	Messages       *lru.Cache
}

type BrMsgID struct {
	br        *bridge.Bridge
	ID        string
	ChannelID string
}

func New(cfg config.Gateway, r *Router) *Gateway {
	gw := &Gateway{Channels: make(map[string]*config.ChannelInfo), Message: r.Message,
		Router: r, Bridges: make(map[string]*bridge.Bridge), Config: r.Config}
	cache, _ := lru.New(5000)
	gw.Messages = cache
	gw.AddConfig(&cfg)
	return gw
}

func (gw *Gateway) AddBridge(cfg *config.Bridge) error {
	br := gw.Router.getBridge(cfg.Account)
	if br == nil {
		br = bridge.New(gw.Config, cfg, gw.Message)
	}
	gw.mapChannelsToBridge(br)
	gw.Bridges[cfg.Account] = br
	return nil
}

func (gw *Gateway) AddConfig(cfg *config.Gateway) error {
	gw.Name = cfg.Name
	gw.MyConfig = cfg
	gw.mapChannels()
	for _, br := range append(gw.MyConfig.In, append(gw.MyConfig.InOut, gw.MyConfig.Out...)...) {
		err := gw.AddBridge(&br)
		if err != nil {
			return err
		}
	}
	return nil
}

func (gw *Gateway) mapChannelsToBridge(br *bridge.Bridge) {
	for ID, channel := range gw.Channels {
		if br.Account == channel.Account {
			br.Channels[ID] = *channel
		}
	}
}

func (gw *Gateway) reconnectBridge(br *bridge.Bridge) {
	br.Disconnect()
	time.Sleep(time.Second * 5)
RECONNECT:
	log.Infof("Reconnecting %s", br.Account)
	err := br.Connect()
	if err != nil {
		log.Errorf("Reconnection failed: %s. Trying again in 60 seconds", err)
		time.Sleep(time.Second * 60)
		goto RECONNECT
	}
	br.Joined = make(map[string]bool)
	br.JoinChannels()
}

func (gw *Gateway) mapChannelConfig(cfg []config.Bridge, direction string) {
	for _, br := range cfg {
		if isApi(br.Account) {
			br.Channel = "api"
		}
		ID := br.Channel + br.Account
		if _, ok := gw.Channels[ID]; !ok {
			channel := &config.ChannelInfo{Name: br.Channel, Direction: direction, ID: ID, Options: br.Options, Account: br.Account,
				SameChannel: make(map[string]bool)}
			channel.SameChannel[gw.Name] = br.SameChannel
			gw.Channels[channel.ID] = channel
		} else {
			// if we already have a key and it's not our current direction it means we have a bidirectional inout
			if gw.Channels[ID].Direction != direction {
				gw.Channels[ID].Direction = "inout"
			}
		}
		gw.Channels[ID].SameChannel[gw.Name] = br.SameChannel
	}
}

func (gw *Gateway) mapChannels() error {
	gw.mapChannelConfig(gw.MyConfig.In, "in")
	gw.mapChannelConfig(gw.MyConfig.Out, "out")
	gw.mapChannelConfig(gw.MyConfig.InOut, "inout")
	return nil
}

func (gw *Gateway) getDestChannel(msg *config.Message, dest bridge.Bridge) []config.ChannelInfo {
	var channels []config.ChannelInfo

	// for messages received from the api check that the gateway is the specified one
	if msg.Protocol == "api" && gw.Name != msg.Gateway {
		return channels
	}

	// if source channel is in only, do nothing
	for _, channel := range gw.Channels {
		// lookup the channel from the message
		if channel.ID == getChannelID(*msg) {
			// we only have destinations if the original message is from an "in" (sending) channel
			if !strings.Contains(channel.Direction, "in") {
				return channels
			}
			continue
		}
	}
	for _, channel := range gw.Channels {
		if _, ok := gw.Channels[getChannelID(*msg)]; !ok {
			continue
		}

		// do samechannelgateway logic
		if channel.SameChannel[msg.Gateway] {
			if msg.Channel == channel.Name && msg.Account != dest.Account {
				channels = append(channels, *channel)
			}
			continue
		}
		if strings.Contains(channel.Direction, "out") && channel.Account == dest.Account && gw.validGatewayDest(msg, channel) {
			channels = append(channels, *channel)
		}
	}
	return channels
}

func (gw *Gateway) handleMessage(msg config.Message, dest *bridge.Bridge) []*BrMsgID {
	var brMsgIDs []*BrMsgID

	// TODO refactor
	// only slack now, check will have to be done in the different bridges.
	// we need to check if we can't use fallback or text in other bridges
	if msg.Extra != nil {
		if dest.Protocol != "discord" &&
			dest.Protocol != "slack" &&
			dest.Protocol != "mattermost" &&
			dest.Protocol != "telegram" &&
			dest.Protocol != "matrix" &&
			dest.Protocol != "xmpp" {
			if msg.Text == "" {
				return brMsgIDs
			}
		}
	}
	// only relay join/part when configged
	if msg.Event == config.EVENT_JOIN_LEAVE && !gw.Bridges[dest.Account].Config.ShowJoinPart {
		return brMsgIDs
	}
	// broadcast to every out channel (irc QUIT)
	if msg.Channel == "" && msg.Event != config.EVENT_JOIN_LEAVE {
		log.Debug("empty channel")
		return brMsgIDs
	}
	originchannel := msg.Channel
	origmsg := msg
	channels := gw.getDestChannel(&msg, *dest)
	for _, channel := range channels {
		// do not send to ourself
		if channel.ID == getChannelID(origmsg) {
			continue
		}
		log.Debugf("Sending %#v from %s (%s) to %s (%s)", msg, msg.Account, originchannel, dest.Account, channel.Name)
		msg.Channel = channel.Name
		msg.Avatar = gw.modifyAvatar(origmsg, dest)
		msg.Username = gw.modifyUsername(origmsg, dest)
		msg.ID = ""
		if res, ok := gw.Messages.Get(origmsg.ID); ok {
			IDs := res.([]*BrMsgID)
			for _, id := range IDs {
				// check protocol, bridge name and channelname
				// for people that reuse the same bridge multiple times. see #342
				if dest.Protocol == id.br.Protocol && dest.Name == id.br.Name && channel.ID == id.ChannelID {
					msg.ID = id.ID
				}
			}
		}
		// for api we need originchannel as channel
		if dest.Protocol == "api" {
			msg.Channel = originchannel
		}
		mID, err := dest.Send(msg)
		if err != nil {
			fmt.Println(err)
		}
		// append the message ID (mID) from this bridge (dest) to our brMsgIDs slice
		if mID != "" {
			brMsgIDs = append(brMsgIDs, &BrMsgID{dest, mID, channel.ID})
		}
	}
	return brMsgIDs
}

func (gw *Gateway) ignoreMessage(msg *config.Message) bool {
	// if we don't have the bridge, ignore it
	if _, ok := gw.Bridges[msg.Account]; !ok {
		return true
	}
	if msg.Text == "" {
		// we have an attachment or actual bytes
		if msg.Extra != nil && (msg.Extra["attachments"] != nil || len(msg.Extra["file"]) > 0) {
			return false
		}
		log.Debugf("ignoring empty message %#v from %s", msg, msg.Account)
		return true
	}
	for _, entry := range strings.Fields(gw.Bridges[msg.Account].Config.IgnoreNicks) {
		if msg.Username == entry {
			log.Debugf("ignoring %s from %s", msg.Username, msg.Account)
			return true
		}
	}
	// TODO do not compile regexps everytime
	for _, entry := range strings.Fields(gw.Bridges[msg.Account].Config.IgnoreMessages) {
		if entry != "" {
			re, err := regexp.Compile(entry)
			if err != nil {
				log.Errorf("incorrect regexp %s for %s", entry, msg.Account)
				continue
			}
			if re.MatchString(msg.Text) {
				log.Debugf("matching %s. ignoring %s from %s", entry, msg.Text, msg.Account)
				return true
			}
		}
	}
	return false
}

func (gw *Gateway) modifyUsername(msg config.Message, dest *bridge.Bridge) string {
	br := gw.Bridges[msg.Account]
	msg.Protocol = br.Protocol
	if gw.Config.General.StripNick || dest.Config.StripNick {
		re := regexp.MustCompile("[^a-zA-Z0-9]+")
		msg.Username = re.ReplaceAllString(msg.Username, "")
	}
	nick := dest.Config.RemoteNickFormat
	if nick == "" {
		nick = gw.Config.General.RemoteNickFormat
	}

	// loop to replace nicks
	for _, outer := range br.Config.ReplaceNicks {
		search := outer[0]
		replace := outer[1]
		// TODO move compile to bridge init somewhere
		re, err := regexp.Compile(search)
		if err != nil {
			log.Errorf("regexp in %s failed: %s", msg.Account, err)
			break
		}
		msg.Username = re.ReplaceAllString(msg.Username, replace)
	}

	if len(msg.Username) > 0 {
		// fix utf-8 issue #193
		i := 0
		for index := range msg.Username {
			if i == 1 {
				i = index
				break
			}
			i++
		}
		nick = strings.Replace(nick, "{NOPINGNICK}", msg.Username[:i]+""+msg.Username[i:], -1)
	}
	nick = strings.Replace(nick, "{BRIDGE}", br.Name, -1)
	nick = strings.Replace(nick, "{PROTOCOL}", br.Protocol, -1)
	nick = strings.Replace(nick, "{NICK}", msg.Username, -1)
	return nick
}

func (gw *Gateway) modifyAvatar(msg config.Message, dest *bridge.Bridge) string {
	iconurl := gw.Config.General.IconURL
	if iconurl == "" {
		iconurl = dest.Config.IconURL
	}
	iconurl = strings.Replace(iconurl, "{NICK}", msg.Username, -1)
	if msg.Avatar == "" {
		msg.Avatar = iconurl
	}
	return msg.Avatar
}

func (gw *Gateway) modifyMessage(msg *config.Message) {
	// replace :emoji: to unicode
	msg.Text = emojilib.Replace(msg.Text)
	br := gw.Bridges[msg.Account]
	// loop to replace messages
	for _, outer := range br.Config.ReplaceMessages {
		search := outer[0]
		replace := outer[1]
		// TODO move compile to bridge init somewhere
		re, err := regexp.Compile(search)
		if err != nil {
			log.Errorf("regexp in %s failed: %s", msg.Account, err)
			break
		}
		msg.Text = re.ReplaceAllString(msg.Text, replace)
	}

	// messages from api have Gateway specified, don't overwrite
	if msg.Protocol != "api" {
		msg.Gateway = gw.Name
	}
}

func (gw *Gateway) handleFiles(msg *config.Message) {
	if msg.Extra == nil || gw.Config.General.MediaServerUpload == "" {
		return
	}
	if len(msg.Extra["file"]) > 0 {
		client := &http.Client{
			Timeout: time.Second * 5,
		}
		for i, f := range msg.Extra["file"] {
			fi := f.(config.FileInfo)
			sha1sum := fmt.Sprintf("%x", sha1.Sum(*fi.Data))
			reader := bytes.NewReader(*fi.Data)
			url := gw.Config.General.MediaServerUpload + "/" + sha1sum + "/" + fi.Name
			durl := gw.Config.General.MediaServerDownload + "/" + sha1sum + "/" + fi.Name
			extra := msg.Extra["file"][i].(config.FileInfo)
			extra.URL = durl
			msg.Extra["file"][i] = extra
			req, _ := http.NewRequest("PUT", url, reader)
			req.Header.Set("Content-Type", "binary/octet-stream")
			_, err := client.Do(req)
			if err != nil {
				log.Errorf("mediaserver upload failed: %#v", err)
			}
			log.Debugf("mediaserver download URL = %s", durl)
		}
	}
}

func getChannelID(msg config.Message) string {
	return msg.Channel + msg.Account
}

func (gw *Gateway) validGatewayDest(msg *config.Message, channel *config.ChannelInfo) bool {
	return msg.Gateway == gw.Name
}

func isApi(account string) bool {
	return strings.HasPrefix(account, "api.")
}
<|endoftext|>


<commit_before><commit_msg>Add rudimentary Delete prompt<commit_after><|endoftext|>


<commit_before>// +build windows

package main

import (
	"io"
	"os"

	"github.com/gorilla/websocket"
	"github.com/mattn/go-colorable"

	"github.com/lxc/lxd"
	"github.com/lxc/lxd/shared"
)

// Windows doesn't process ANSI sequences natively, so we wrap
// os.Stdout for improved user experience for Windows client
type WrappedWriteCloser struct {
	io.Closer
	wrapper io.Writer
}

func (wwc *WrappedWriteCloser) Write(p []byte) (int, error) {
	return wwc.wrapper.Write(p)
}

func (c *execCmd) getStdout() io.WriteCloser {
	return &WrappedWriteCloser{os.Stdout, colorable.NewColorableStdout()}
}

func (c *execCmd) controlSocketHandler(d *lxd.Client, control *websocket.Conn) {
	// TODO: figure out what the equivalent of signal.SIGWINCH is on
	// windows and use that; for now if you resize your terminal it just
	// won't work quite correctly.
	err := c.sendTermSize(control)
	if err != nil {
		shared.Debugf("error setting term size %s", err)
	}
}
<commit_msg>lxc/exec_windows: Debugf() --> LogDebugf()<commit_after>// +build windows

package main

import (
	"io"
	"os"

	"github.com/gorilla/websocket"
	"github.com/mattn/go-colorable"

	"github.com/lxc/lxd"
	"github.com/lxc/lxd/shared"
)

// Windows doesn't process ANSI sequences natively, so we wrap
// os.Stdout for improved user experience for Windows client
type WrappedWriteCloser struct {
	io.Closer
	wrapper io.Writer
}

func (wwc *WrappedWriteCloser) Write(p []byte) (int, error) {
	return wwc.wrapper.Write(p)
}

func (c *execCmd) getStdout() io.WriteCloser {
	return &WrappedWriteCloser{os.Stdout, colorable.NewColorableStdout()}
}

func (c *execCmd) controlSocketHandler(d *lxd.Client, control *websocket.Conn) {
	// TODO: figure out what the equivalent of signal.SIGWINCH is on
	// windows and use that; for now if you resize your terminal it just
	// won't work quite correctly.
	err := c.sendTermSize(control)
	if err != nil {
		shared.LogDebugf("error setting term size %s", err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"net/http"

	"github.com/gorilla/mux"

	"github.com/lxc/lxd/lxd/network/acl"
	"github.com/lxc/lxd/lxd/project"
	"github.com/lxc/lxd/lxd/response"
	"github.com/lxc/lxd/lxd/util"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/version"
)

var networkACLsCmd = APIEndpoint{
	Path: "network-acls",

	Get:  APIEndpointAction{Handler: networkACLsGet, AccessHandler: allowProjectPermission("networks", "view")},
	Post: APIEndpointAction{Handler: networkACLsPost, AccessHandler: allowProjectPermission("networks", "manage-networks")},
}

var networkACLCmd = APIEndpoint{
	Path: "network-acls/{name}",

	Delete: APIEndpointAction{Handler: networkACLDelete, AccessHandler: allowProjectPermission("networks", "manage-networks")},
	Get:    APIEndpointAction{Handler: networkACLGet, AccessHandler: allowProjectPermission("networks", "view")},
	Put:    APIEndpointAction{Handler: networkACLPut, AccessHandler: allowProjectPermission("networks", "manage-networks")},
	Patch:  APIEndpointAction{Handler: networkACLPut, AccessHandler: allowProjectPermission("networks", "manage-networks")},
	Post:   APIEndpointAction{Handler: networkACLPost, AccessHandler: allowProjectPermission("networks", "manage-networks")},
}

// API endpoints.

// List Network ACLs.
func networkACLsGet(d *Daemon, r *http.Request) response.Response {
	projectName, _, err := project.NetworkProject(d.State().Cluster, projectParam(r))
	if err != nil {
		return response.SmartError(err)
	}

	recursion := util.IsRecursionRequest(r)

	// Get list of Network ACLs.
	aclNames, err := d.cluster.GetNetworkACLs(projectName)
	if err != nil {
		return response.InternalError(err)
	}

	resultString := []string{}
	resultMap := []api.NetworkACL{}
	for _, aclName := range aclNames {
		if !recursion {
			resultString = append(resultString, fmt.Sprintf("/%s/network-acls/%s", version.APIVersion, aclName))
		} else {
			netACL, err := acl.LoadByName(d.State(), projectName, aclName)
			if err != nil {
				continue
			}

			netACLInfo := netACL.Info()
			netACLInfo.UsedBy, _ = netACL.UsedBy() // Ignore errors in UsedBy, will return nil.

			resultMap = append(resultMap, *netACLInfo)
		}
	}

	if !recursion {
		return response.SyncResponse(true, resultString)
	}

	return response.SyncResponse(true, resultMap)
}

// Create Network ACL.
func networkACLsPost(d *Daemon, r *http.Request) response.Response {
	projectName, _, err := project.NetworkProject(d.State().Cluster, projectParam(r))
	if err != nil {
		return response.SmartError(err)
	}

	req := api.NetworkACLsPost{}

	// Parse the request into a record.
	err = json.NewDecoder(r.Body).Decode(&req)
	if err != nil {
		return response.BadRequest(err)
	}

	_, err = acl.LoadByName(d.State(), projectName, req.Name)
	if err == nil {
		return response.BadRequest(fmt.Errorf("The network ACL already exists"))
	}

	err = acl.Create(d.State(), projectName, &req)
	if err != nil {
		return response.SmartError(err)
	}

	url := fmt.Sprintf("/%s/network-acls/%s", version.APIVersion, req.Name)
	return response.SyncResponseLocation(true, nil, url)
}

// Delete Network ACL.
func networkACLDelete(d *Daemon, r *http.Request) response.Response {
	return response.NotImplemented(nil)
}

// Show Network ACL.
func networkACLGet(d *Daemon, r *http.Request) response.Response {
	projectName, _, err := project.NetworkProject(d.State().Cluster, projectParam(r))
	if err != nil {
		return response.SmartError(err)
	}

	netACL, err := acl.LoadByName(d.State(), projectName, mux.Vars(r)["name"])
	if err != nil {
		return response.SmartError(err)
	}

	info := netACL.Info()
	info.UsedBy, err = netACL.UsedBy()
	if err != nil {
		return response.SmartError(err)
	}

	return response.SyncResponseETag(true, info, netACL.Etag())
}

// Update Network ACL.
func networkACLPut(d *Daemon, r *http.Request) response.Response {
	projectName, _, err := project.NetworkProject(d.State().Cluster, projectParam(r))
	if err != nil {
		return response.SmartError(err)
	}

	// Get the existing Network ACL.
	netACL, err := acl.LoadByName(d.State(), projectName, mux.Vars(r)["name"])
	if err != nil {
		return response.SmartError(err)
	}

	// Validate the ETag.
	err = util.EtagCheck(r, netACL.Etag())
	if err != nil {
		return response.PreconditionFailed(err)
	}

	req := api.NetworkACLPut{}

	// Decode the request.
	err = json.NewDecoder(r.Body).Decode(&req)
	if err != nil {
		return response.BadRequest(err)
	}

	if r.Method == http.MethodPatch {
		// If config being updated via "patch" method, then merge all existing config with the keys that
		// are present in the request config.
		for k, v := range netACL.Info().Config {
			_, ok := req.Config[k]
			if !ok {
				req.Config[k] = v
			}
		}
	}

	err = netACL.Update(&req)
	if err != nil {
		return response.SmartError(err)
	}

	return response.EmptySyncResponse
}

// Rename Network ACL.
func networkACLPost(d *Daemon, r *http.Request) response.Response {
	projectName, _, err := project.NetworkProject(d.State().Cluster, projectParam(r))
	if err != nil {
		return response.SmartError(err)
	}

	req := api.NetworkACLPost{}

	// Parse the request.
	err = json.NewDecoder(r.Body).Decode(&req)
	if err != nil {
		return response.BadRequest(err)
	}

	// Get the existing Network ACL.
	netACL, err := acl.LoadByName(d.State(), projectName, mux.Vars(r)["name"])
	if err != nil {
		return response.SmartError(err)
	}

	err = netACL.Rename(req.Name)
	if err != nil {
		return response.SmartError(err)
	}

	url := fmt.Sprintf("/%s/network-acls/%s", version.APIVersion, req.Name)
	return response.SyncResponseLocation(true, nil, url)
}
<commit_msg>lxd/network/acls: Implements networkACLDelete function<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"net/http"

	"github.com/gorilla/mux"

	"github.com/lxc/lxd/lxd/network/acl"
	"github.com/lxc/lxd/lxd/project"
	"github.com/lxc/lxd/lxd/response"
	"github.com/lxc/lxd/lxd/util"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/version"
)

var networkACLsCmd = APIEndpoint{
	Path: "network-acls",

	Get:  APIEndpointAction{Handler: networkACLsGet, AccessHandler: allowProjectPermission("networks", "view")},
	Post: APIEndpointAction{Handler: networkACLsPost, AccessHandler: allowProjectPermission("networks", "manage-networks")},
}

var networkACLCmd = APIEndpoint{
	Path: "network-acls/{name}",

	Delete: APIEndpointAction{Handler: networkACLDelete, AccessHandler: allowProjectPermission("networks", "manage-networks")},
	Get:    APIEndpointAction{Handler: networkACLGet, AccessHandler: allowProjectPermission("networks", "view")},
	Put:    APIEndpointAction{Handler: networkACLPut, AccessHandler: allowProjectPermission("networks", "manage-networks")},
	Patch:  APIEndpointAction{Handler: networkACLPut, AccessHandler: allowProjectPermission("networks", "manage-networks")},
	Post:   APIEndpointAction{Handler: networkACLPost, AccessHandler: allowProjectPermission("networks", "manage-networks")},
}

// API endpoints.

// List Network ACLs.
func networkACLsGet(d *Daemon, r *http.Request) response.Response {
	projectName, _, err := project.NetworkProject(d.State().Cluster, projectParam(r))
	if err != nil {
		return response.SmartError(err)
	}

	recursion := util.IsRecursionRequest(r)

	// Get list of Network ACLs.
	aclNames, err := d.cluster.GetNetworkACLs(projectName)
	if err != nil {
		return response.InternalError(err)
	}

	resultString := []string{}
	resultMap := []api.NetworkACL{}
	for _, aclName := range aclNames {
		if !recursion {
			resultString = append(resultString, fmt.Sprintf("/%s/network-acls/%s", version.APIVersion, aclName))
		} else {
			netACL, err := acl.LoadByName(d.State(), projectName, aclName)
			if err != nil {
				continue
			}

			netACLInfo := netACL.Info()
			netACLInfo.UsedBy, _ = netACL.UsedBy() // Ignore errors in UsedBy, will return nil.

			resultMap = append(resultMap, *netACLInfo)
		}
	}

	if !recursion {
		return response.SyncResponse(true, resultString)
	}

	return response.SyncResponse(true, resultMap)
}

// Create Network ACL.
func networkACLsPost(d *Daemon, r *http.Request) response.Response {
	projectName, _, err := project.NetworkProject(d.State().Cluster, projectParam(r))
	if err != nil {
		return response.SmartError(err)
	}

	req := api.NetworkACLsPost{}

	// Parse the request into a record.
	err = json.NewDecoder(r.Body).Decode(&req)
	if err != nil {
		return response.BadRequest(err)
	}

	_, err = acl.LoadByName(d.State(), projectName, req.Name)
	if err == nil {
		return response.BadRequest(fmt.Errorf("The network ACL already exists"))
	}

	err = acl.Create(d.State(), projectName, &req)
	if err != nil {
		return response.SmartError(err)
	}

	url := fmt.Sprintf("/%s/network-acls/%s", version.APIVersion, req.Name)
	return response.SyncResponseLocation(true, nil, url)
}

// Delete Network ACL.
func networkACLDelete(d *Daemon, r *http.Request) response.Response {
	projectName, _, err := project.NetworkProject(d.State().Cluster, projectParam(r))
	if err != nil {
		return response.SmartError(err)
	}

	netACL, err := acl.LoadByName(d.State(), projectName, mux.Vars(r)["name"])
	if err != nil {
		return response.SmartError(err)
	}

	err = netACL.Delete()
	if err != nil {
		return response.SmartError(err)
	}

	return response.EmptySyncResponse
}

// Show Network ACL.
func networkACLGet(d *Daemon, r *http.Request) response.Response {
	projectName, _, err := project.NetworkProject(d.State().Cluster, projectParam(r))
	if err != nil {
		return response.SmartError(err)
	}

	netACL, err := acl.LoadByName(d.State(), projectName, mux.Vars(r)["name"])
	if err != nil {
		return response.SmartError(err)
	}

	info := netACL.Info()
	info.UsedBy, err = netACL.UsedBy()
	if err != nil {
		return response.SmartError(err)
	}

	return response.SyncResponseETag(true, info, netACL.Etag())
}

// Update Network ACL.
func networkACLPut(d *Daemon, r *http.Request) response.Response {
	projectName, _, err := project.NetworkProject(d.State().Cluster, projectParam(r))
	if err != nil {
		return response.SmartError(err)
	}

	// Get the existing Network ACL.
	netACL, err := acl.LoadByName(d.State(), projectName, mux.Vars(r)["name"])
	if err != nil {
		return response.SmartError(err)
	}

	// Validate the ETag.
	err = util.EtagCheck(r, netACL.Etag())
	if err != nil {
		return response.PreconditionFailed(err)
	}

	req := api.NetworkACLPut{}

	// Decode the request.
	err = json.NewDecoder(r.Body).Decode(&req)
	if err != nil {
		return response.BadRequest(err)
	}

	if r.Method == http.MethodPatch {
		// If config being updated via "patch" method, then merge all existing config with the keys that
		// are present in the request config.
		for k, v := range netACL.Info().Config {
			_, ok := req.Config[k]
			if !ok {
				req.Config[k] = v
			}
		}
	}

	err = netACL.Update(&req)
	if err != nil {
		return response.SmartError(err)
	}

	return response.EmptySyncResponse
}

// Rename Network ACL.
func networkACLPost(d *Daemon, r *http.Request) response.Response {
	projectName, _, err := project.NetworkProject(d.State().Cluster, projectParam(r))
	if err != nil {
		return response.SmartError(err)
	}

	req := api.NetworkACLPost{}

	// Parse the request.
	err = json.NewDecoder(r.Body).Decode(&req)
	if err != nil {
		return response.BadRequest(err)
	}

	// Get the existing Network ACL.
	netACL, err := acl.LoadByName(d.State(), projectName, mux.Vars(r)["name"])
	if err != nil {
		return response.SmartError(err)
	}

	err = netACL.Rename(req.Name)
	if err != nil {
		return response.SmartError(err)
	}

	url := fmt.Sprintf("/%s/network-acls/%s", version.APIVersion, req.Name)
	return response.SyncResponseLocation(true, nil, url)
}
<|endoftext|>