<commit_before>package main

import (
	"bufio"
	"encoding/binary"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"sort"
	"strings"
	"time"

	"encoding/csv"
	"github.com/bwmarrin/discordgo"
)

var (
	// discordgo session
	discord *discordgo.Session

	// Map of Guild id's to *Play channels, used for queuing and rate-limiting guilds
	queues map[string]chan *Play = make(map[string]chan *Play)

	sounds = []*Sound{}

	soundMap = map[string]*Sound{}

	// Sound encoding settings
	BITRATE        = 128
	MAX_QUEUE_SIZE = 6

	// Owner
	OWNER string

	// Bot token
	token string
)

// Right now, configuration only set to take in a bot token. but we can add in more things in the future.
type Configuration struct {
	Token string
}

// Play represents an individual use of the !airhorn command
type Play struct {
	GuildID   string
	ChannelID string
	UserID    string
	Sound     *Sound
}

// Sound type cribbed from airhornbot.
type Sound struct {
	Name string `csv:"filename"`

	// major difference here is that we want to be able to call each sound explicitly
	Command string `csv:"command"`

	// Really not sure how important this is. let's defa
	PartDelay int `csv:"-"`

	// Buffer to store encoded PCM packets
	buffer [][]byte `csv:"-"`
}

func main() {

	// first lets verify that we've got a token
	confFile, err := os.Open("config/conf.json")
	if err != nil {
		panic(err)
	}
	decoder := json.NewDecoder(confFile)
	configuration := Configuration{}
	err = decoder.Decode(&configuration)
	if err != nil {
		panic(err)
	}
	token = configuration.Token
	if strings.Contains(token, "ADD YOUR DISCORD BOT TOKEN HERE!") {
		fmt.Println("Please set a Discord bot token in config/conf.json.")
		return
	}
	fmt.Println("Retrieved token: " + token)

	// lets load up our sounds
	soundsFile, err := os.OpenFile("config/sounds.csv", os.O_RDWR|os.O_CREATE, os.ModePerm) // should figure out what these os objects are
	if err != nil {
		panic(err)
	}
	defer soundsFile.Close()

	reader := csv.NewReader(soundsFile)
	//Configure reader options Ref http://golang.org/src/pkg/encoding/csv/reader.go?s=#L81
	reader.Comma = ','         //field delimiter
	reader.Comment = '#'       //Comment character
	reader.FieldsPerRecord = 2 //Number of records per record. Set to Negative value for variable
	reader.TrimLeadingSpace = true

	for {
		// read just one record, but we could ReadAll() as well
		record, err := reader.Read()
		// end-of-file is fitted into err
		if err == io.EOF {
			break
		} else if err != nil {
			fmt.Println("Error:", err)
			reader.Read()
			continue
		}
		// record is array of strings Ref http://golang.org/src/pkg/encoding/csv/reader.go?s=#L134
		// Create the play
		sound := &Sound{
			Name:    record[0],
			Command: record[1],
		}
		sounds = append(sounds, sound)
	}

	for _, sound := range sounds {
		// for each sound, load the .dca into memory and store it in the Sound struct
		sound.Load()
		soundMap[sound.Command] = sound
		fmt.Println("Loaded filename", sound.Name, "loaded command", sound.Command)
	}

	// Create a new Discord session using the provided bot token.
	dg, err := discordgo.New("Bot " + token)
	if err != nil {
		fmt.Println("Error creating Discord session: ", err)
		return
	}

	// Register ready as a callback for the ready events.
	dg.AddHandler(ready)

	// Register messageCreate as a callback for the messageCreate events.
	dg.AddHandler(messageCreate)

	// Register guildCreate as a callback for the guildCreate events.
	dg.AddHandler(guildCreate)

	// Open the websocket and begin listening.
	err = dg.Open()
	if err != nil {
		fmt.Println("Error opening Discord session: ", err)
	}

	// Open our Http upload handler
	http.ListenAndServe(":8080", http.HandlerFunc(handleUpload))

	fmt.Println("Discord Soundboard is now running.  Press CTRL-C to exit.")
	// Simple way to keep program running until CTRL-C is pressed.
	<-make(chan struct{})
	return
}

func ready(s *discordgo.Session, event *discordgo.Ready) {
	// Set the playing status.
	_ = s.UpdateStatus(0, "!commands")
}

// This function will be called (due to AddHandler above) every time a new
// message is created on any channel that the autenticated bot has access to.
func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
	if strings.HasPrefix(m.Content, "!") { // we can make the prefix configurable but for right now always look for !
		command := m.Content[1:] //substring starting at index 1

		c, err := s.State.Channel(m.ChannelID)
		if err != nil {
			// Could not find channel.
			return
		}

		// we need to have the channel available to send a message, so do this second.
		if command == "list" || command == "commands" {
			// special case for list command.
			// this code actually sucks but using the reflect stdlib means i have to do some bizarre casting
			keys := make([]string, len(soundMap))
			i := 0
			for k := range soundMap {
				keys[i] = k
				i++
			}
			sort.Strings(keys)
			// discord has a 2000 character limit on message length. we'll need to break up our list if the length gets too long
			commandList := strings.Join(keys, ", ")
			if len(commandList) > 1900 { //lowball for safety
				keyIndex := 0
				for keyIndex < len(keys) {
					outputString := ""
					for len(outputString) < 1900 && keyIndex < len(keys) {
						outputString = outputString + keys[keyIndex] + ", "
						keyIndex++
					}
					outputString = outputString[:len(outputString)-2]                       // remove last chars
					_, _ = s.ChannelMessageSend(c.ID, "**Commands**```"+outputString+"```") // short enough, so we're fine.
				}

			} else {
				_, _ = s.ChannelMessageSend(c.ID, "**Commands**```"+strings.Join(keys, ", ")+"```") // short enough, so we're fine.
			}
			return
		}

		// Find the guild for that channel.
		g, err := s.State.Guild(c.GuildID)
		if err != nil {
			// Could not find guild.
			return
		}

		// get audio channel to play in
		ac := getCurrentVoiceChannel(m.Author, g, s)
		if ac == nil {
			fmt.Println("Failed to find channel to play sound in")
			return
		}

		i, ok := soundMap[command] // look for command in our soundMap
		if ok {                    // we found it, so lets queue the sound
			go enqueuePlay(m.Author, ac, g, i, s)
		}
		return
	}
}

// This function will be called (due to AddHandler above) every time a new
// guild is joined.
func guildCreate(s *discordgo.Session, event *discordgo.GuildCreate) {
	if event.Guild.Unavailable {
		return
	}

	for _, channel := range event.Guild.Channels {
		if channel.ID == event.Guild.ID {
			return
		}
	}
}

// Load attempts to load an encoded sound file from disk
// DCA files are pre-computed sound files that are easy to send to Discord.
// If you would like to create your own DCA files, please use:
// https://github.com/nstafie/dca-rs
// eg: dca-rs --raw -i <input wav file> > <output file>
func (s *Sound) Load() error {
	path := "sounds/" + s.Name

	file, err := os.Open(path)

	if err != nil {
		fmt.Println("error opening dca file :", err)
		return err
	}

	var opuslen int16

	for {
		// read opus frame length from dca file
		err = binary.Read(file, binary.LittleEndian, &opuslen)

		// If this is the end of the file, just return
		if err == io.EOF || err == io.ErrUnexpectedEOF {
			return nil
		}

		if err != nil {
			fmt.Println("error reading from dca file1 :", err)
			return err
		}

		// read encoded pcm from dca file
		InBuf := make([]byte, opuslen)
		err = binary.Read(file, binary.LittleEndian, &InBuf)

		// Should not be any end of file errors
		if err != nil {
			fmt.Println("error reading from dca file2 :", err)
			return err
		}

		// append encoded pcm data to the buffer
		s.buffer = append(s.buffer, InBuf)
	}
}

// Prepares and enqueues a play into the ratelimit/buffer guild queue
func enqueuePlay(user *discordgo.User, channel *discordgo.Channel, guild *discordgo.Guild, sound *Sound, session *discordgo.Session) {
	play := createPlay(user, channel, guild, sound)
	if play == nil {
		return
	}

	// Check if we already have a connection to this guild
	//   yes, this isn't threadsafe, but its "OK" 99% of the time
	_, exists := queues[guild.ID]

	if exists {
		if len(queues[guild.ID]) < MAX_QUEUE_SIZE {
			queues[guild.ID] <- play
		}
	} else {
		queues[guild.ID] = make(chan *Play, MAX_QUEUE_SIZE)
		playSound(play, nil, session)
	}
}

// Prepares a play
func createPlay(user *discordgo.User, channel *discordgo.Channel, guild *discordgo.Guild, sound *Sound) *Play {

	// Create the play
	play := &Play{
		GuildID:   guild.ID,
		ChannelID: channel.ID,
		UserID:    user.ID,
		Sound:     sound,
	}

	return play
}

// Play a sound
func playSound(play *Play, vc *discordgo.VoiceConnection, session *discordgo.Session) (err error) {
	fmt.Println("playing sound " + play.Sound.Name)

	if vc == nil {
		vc, err = session.ChannelVoiceJoin(play.GuildID, play.ChannelID, false, false)
		// vc.Receive = false
		if err != nil {
			fmt.Println("Failed to play sound")
			fmt.Println(err)
		}
	}

	// If we need to change channels, do that now
	if vc.ChannelID != play.ChannelID {
		vc.ChangeChannel(play.ChannelID, false, false)
		time.Sleep(time.Millisecond * 125)
	}

	// Sleep for a specified amount of time before playing the sound
	time.Sleep(time.Millisecond * 32)

	// Play the sound
	play.Sound.Play(vc)

	// If there is another song in the queue, recurse and play that
	if len(queues[play.GuildID]) > 0 {
		play := <-queues[play.GuildID]
		playSound(play, vc, session)
		return nil
	}

	// If the queue is empty, delete it
	time.Sleep(time.Millisecond * time.Duration(play.Sound.PartDelay))
	delete(queues, play.GuildID)
	vc.Disconnect()
	return nil
}

// Plays this sound over the specified VoiceConnection
func (s *Sound) Play(vc *discordgo.VoiceConnection) {
	vc.Speaking(true)
	defer vc.Speaking(false)

	for _, buff := range s.buffer {
		vc.OpusSend <- buff
	}
}

// Attempts to find the current users voice channel inside a given guild
func getCurrentVoiceChannel(user *discordgo.User, guild *discordgo.Guild, session *discordgo.Session) *discordgo.Channel {
	for _, vs := range guild.VoiceStates {
		if vs.UserID == user.ID {
			channel, _ := session.State.Channel(vs.ChannelID)
			return channel
		}
	}
	return nil
}

func handleUpload(w http.ResponseWriter, r *http.Request) {
	//read file from request and save to disk
	file, header, err := r.FormFile("file")

	if err != nil {
		fmt.Fprintln(w, err)
		return
	}

	defer file.Close()

	out, err := os.Create("sounds/" + header.Filename)
	if err != nil {
		fmt.Fprintf(w, "Failed to open the file for writing")
		return
	}

	defer out.Close()
	_, err = io.Copy(out, file)
	if err != nil {
		fmt.Fprintln(w, err)
	}

	//create dca filename
	dcaFilename := strings.TrimSuffix(header.Filename, filepath.Ext(header.Filename)) + ".dca"

	dcaOut, err := os.Create("sounds/" + dcaFilename)
	if err != nil {
		panic(err)
	}
	//    defer dcaOut.Close()

	// convert file to .dca
	cmd := exec.Command("dca-rs", "-i", "sounds/"+header.Filename, "--raw")

	stdoutPipe, err := cmd.StdoutPipe()
	if err != nil {
		panic(err)
	}

	writer := bufio.NewWriter(dcaOut)
	//defer writer.Flush()

	err = cmd.Start()
	if err != nil {
		panic(err)
	}

	io.Copy(writer, stdoutPipe)
	cmd.Wait()

	fmt.Println("No errors from command")
	writer.Flush()
	dcaOut.Close()

	// that was obnoxious. now let's get our command, add the sound to the map as well as our config file.
	sound := &Sound{
		Name:    dcaFilename,
		Command: r.FormValue("command"),
	}

	sound.Load()
	soundMap[sound.Command] = sound
	fmt.Println("Loaded filename", sound.Name, "loaded command", sound.Command)

	f, err := os.OpenFile("config/sounds.csv", os.O_APPEND|os.O_WRONLY, 0600)
	if err != nil {
		panic(err)
	}

	defer f.Close()

	if _, err = f.WriteString("\n" + dcaFilename + "," + r.FormValue("command")); err != nil {
		panic(err)
	}
}
<commit_msg>Soundboard now cleans up messages that it responded to. Hopefully resolved hang when VoiceConnection not closed cleanly<commit_after>package main

import (
	"bufio"
	"encoding/binary"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"sort"
	"strings"
	"time"

	"encoding/csv"
	"github.com/bwmarrin/discordgo"
)

var (
	// discordgo session
	discord *discordgo.Session

	// Map of Guild id's to *Play channels, used for queuing and rate-limiting guilds
	queues map[string]chan *Play = make(map[string]chan *Play)

	sounds = []*Sound{}

	soundMap = map[string]*Sound{}

	// Sound encoding settings
	BITRATE        = 128
	MAX_QUEUE_SIZE = 6

	// Owner
	OWNER string

	// Bot token
	token string
)

// Right now, configuration only set to take in a bot token. but we can add in more things in the future.
type Configuration struct {
	Token string
}

// Play represents an individual use of the !airhorn command
type Play struct {
	GuildID   string
	ChannelID string
	UserID    string
	Sound     *Sound
}

// Sound type cribbed from airhornbot.
type Sound struct {
	Name string `csv:"filename"`

	// major difference here is that we want to be able to call each sound explicitly
	Command string `csv:"command"`

	// Really not sure how important this is. let's defa
	PartDelay int `csv:"-"`

	// Buffer to store encoded PCM packets
	buffer [][]byte `csv:"-"`
}

func main() {

	// first lets verify that we've got a token
	confFile, err := os.Open("config/conf.json")
	if err != nil {
		panic(err)
	}
	decoder := json.NewDecoder(confFile)
	configuration := Configuration{}
	err = decoder.Decode(&configuration)
	if err != nil {
		panic(err)
	}
	token = configuration.Token
	if strings.Contains(token, "ADD YOUR DISCORD BOT TOKEN HERE!") {
		fmt.Println("Please set a Discord bot token in config/conf.json.")
		return
	}
	fmt.Println("Retrieved token: " + token)

	// lets load up our sounds
	soundsFile, err := os.OpenFile("config/sounds.csv", os.O_RDWR|os.O_CREATE, os.ModePerm) // should figure out what these os objects are
	if err != nil {
		panic(err)
	}
	defer soundsFile.Close()

	reader := csv.NewReader(soundsFile)
	//Configure reader options Ref http://golang.org/src/pkg/encoding/csv/reader.go?s=#L81
	reader.Comma = ','         //field delimiter
	reader.Comment = '#'       //Comment character
	reader.FieldsPerRecord = 2 //Number of records per record. Set to Negative value for variable
	reader.TrimLeadingSpace = true

	for {
		// read just one record, but we could ReadAll() as well
		record, err := reader.Read()
		// end-of-file is fitted into err
		if err == io.EOF {
			break
		} else if err != nil {
			fmt.Println("Error:", err)
			reader.Read()
			continue
		}
		// record is array of strings Ref http://golang.org/src/pkg/encoding/csv/reader.go?s=#L134
		// Create the play
		sound := &Sound{
			Name:    record[0],
			Command: record[1],
		}
		sounds = append(sounds, sound)
	}

	for _, sound := range sounds {
		// for each sound, load the .dca into memory and store it in the Sound struct
		sound.Load()
		soundMap[sound.Command] = sound
		fmt.Println("Loaded filename", sound.Name, "loaded command", sound.Command)
	}

	// Create a new Discord session using the provided bot token.
	dg, err := discordgo.New("Bot " + token)
	if err != nil {
		fmt.Println("Error creating Discord session: ", err)
		return
	}

	// Register ready as a callback for the ready events.
	dg.AddHandler(ready)

	// Register messageCreate as a callback for the messageCreate events.
	dg.AddHandler(messageCreate)

	// Register guildCreate as a callback for the guildCreate events.
	dg.AddHandler(guildCreate)

	// Open the websocket and begin listening.
	err = dg.Open()
	if err != nil {
		fmt.Println("Error opening Discord session: ", err)
	}

	// Open our Http upload handler
	http.ListenAndServe(":8080", http.HandlerFunc(handleUpload))

	fmt.Println("Discord Soundboard is now running.  Press CTRL-C to exit.")
	// Simple way to keep program running until CTRL-C is pressed.
	<-make(chan struct{})
	return
}

func ready(s *discordgo.Session, event *discordgo.Ready) {
	// Set the playing status.
	_ = s.UpdateStatus(0, "!commands")
}

// This function will be called (due to AddHandler above) every time a new
// message is created on any channel that the autenticated bot has access to.
func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
	if strings.HasPrefix(m.Content, "!") { // we can make the prefix configurable but for right now always look for !
		command := m.Content[1:] //substring starting at index 1

		c, err := s.State.Channel(m.ChannelID)
		if err != nil {
			// Could not find channel.
			return
		}

		// we need to have the channel available to send a message, so do this second.
		if command == "list" || command == "commands" {
			// special case for list command.
			// this code actually sucks but using the reflect stdlib means i have to do some bizarre casting
			keys := make([]string, len(soundMap))
			i := 0
			for k := range soundMap {
				keys[i] = k
				i++
			}
			sort.Strings(keys)
			// discord has a 2000 character limit on message length. we'll need to break up our list if the length gets too long
			commandList := strings.Join(keys, ", ")
			if len(commandList) > 1900 { //lowball for safety
				keyIndex := 0
				for keyIndex < len(keys) {
					outputString := ""
					for len(outputString) < 1900 && keyIndex < len(keys) {
						outputString = outputString + keys[keyIndex] + ", "
						keyIndex++
					}
					outputString = outputString[:len(outputString)-2]                       // remove last chars
					_, _ = s.ChannelMessageSend(c.ID, "**Commands**```"+outputString+"```") // short enough, so we're fine.
				}

			} else {
				_, _ = s.ChannelMessageSend(c.ID, "**Commands**```"+strings.Join(keys, ", ")+"```") // short enough, so we're fine.
			}
			return
		}

		// Find the guild for that channel.
		g, err := s.State.Guild(c.GuildID)
		if err != nil {
			// Could not find guild.
			return
		}

		// get audio channel to play in
		ac := getCurrentVoiceChannel(m.Author, g, s)
		if ac == nil {
			fmt.Println("Failed to find channel to play sound in")
			return
		}

		i, ok := soundMap[command] // look for command in our soundMap
		if ok {                    // we found it, so lets queue the sound
			go enqueuePlay(m.Author, ac, g, i, s)
			go s.ChannelMessageDelete(m.ChannelID, m.ID) //clean up the command afterwards
		}
		return
	}
}

// This function will be called (due to AddHandler above) every time a new
// guild is joined.
func guildCreate(s *discordgo.Session, event *discordgo.GuildCreate) {
	if event.Guild.Unavailable {
		return
	}

	for _, channel := range event.Guild.Channels {
		if channel.ID == event.Guild.ID {
			return
		}
	}
}

// Load attempts to load an encoded sound file from disk
// DCA files are pre-computed sound files that are easy to send to Discord.
// If you would like to create your own DCA files, please use:
// https://github.com/nstafie/dca-rs
// eg: dca-rs --raw -i <input wav file> > <output file>
func (s *Sound) Load() error {
	path := "sounds/" + s.Name

	file, err := os.Open(path)

	if err != nil {
		fmt.Println("error opening dca file :", err)
		return err
	}

	var opuslen int16

	for {
		// read opus frame length from dca file
		err = binary.Read(file, binary.LittleEndian, &opuslen)

		// If this is the end of the file, just return
		if err == io.EOF || err == io.ErrUnexpectedEOF {
			return nil
		}

		if err != nil {
			fmt.Println("error reading from dca file1 :", err)
			return err
		}

		// read encoded pcm from dca file
		InBuf := make([]byte, opuslen)
		err = binary.Read(file, binary.LittleEndian, &InBuf)

		// Should not be any end of file errors
		if err != nil {
			fmt.Println("error reading from dca file2 :", err)
			return err
		}

		// append encoded pcm data to the buffer
		s.buffer = append(s.buffer, InBuf)
	}
}

// Prepares and enqueues a play into the ratelimit/buffer guild queue
func enqueuePlay(user *discordgo.User, channel *discordgo.Channel, guild *discordgo.Guild, sound *Sound, session *discordgo.Session) {
	play := createPlay(user, channel, guild, sound)
	if play == nil {
		return
	}

	// Check if we already have a connection to this guild
	//   yes, this isn't threadsafe, but its "OK" 99% of the time
	_, exists := queues[guild.ID]

	if exists {
		if len(queues[guild.ID]) < MAX_QUEUE_SIZE {
			queues[guild.ID] <- play
		}
	} else {
		queues[guild.ID] = make(chan *Play, MAX_QUEUE_SIZE)
		playSound(play, nil, session)
	}
}

// Prepares a play
func createPlay(user *discordgo.User, channel *discordgo.Channel, guild *discordgo.Guild, sound *Sound) *Play {

	// Create the play
	play := &Play{
		GuildID:   guild.ID,
		ChannelID: channel.ID,
		UserID:    user.ID,
		Sound:     sound,
	}

	return play
}

// Play a sound
func playSound(play *Play, vc *discordgo.VoiceConnection, session *discordgo.Session) (err error) {
	fmt.Println("playing sound " + play.Sound.Name)

	if vc == nil {
		vc, err = session.ChannelVoiceJoin(play.GuildID, play.ChannelID, false, false)
		// vc.Receive = false
		if err != nil {
			fmt.Println("Failed to retrieve voice connection. Close and retry.")
			// this occurs when the voice connection fails to close. let's close manually?
			vc.Close() // close manually
			vc, _ = session.ChannelVoiceJoin(play.GuildID, play.ChannelID, false, false)
		}
	}

	// If we need to change channels, do that now
	if vc.ChannelID != play.ChannelID {
		vc.ChangeChannel(play.ChannelID, false, false)
		time.Sleep(time.Millisecond * 125)
	}

	// Sleep for a specified amount of time before playing the sound
	time.Sleep(time.Millisecond * 32)

	// Play the sound
	play.Sound.Play(vc)

	// If there is another song in the queue, recurse and play that
	if len(queues[play.GuildID]) > 0 {
		play := <-queues[play.GuildID]
		playSound(play, vc, session)
		return nil
	}

	// If the queue is empty, delete it
	time.Sleep(time.Millisecond * time.Duration(play.Sound.PartDelay))
	delete(queues, play.GuildID)
	vc.Disconnect()
	return nil
}

// Plays this sound over the specified VoiceConnection
func (s *Sound) Play(vc *discordgo.VoiceConnection) {
	vc.Speaking(true)
	defer vc.Speaking(false)

	for _, buff := range s.buffer {
		vc.OpusSend <- buff
	}
}

// Attempts to find the current users voice channel inside a given guild
func getCurrentVoiceChannel(user *discordgo.User, guild *discordgo.Guild, session *discordgo.Session) *discordgo.Channel {
	for _, vs := range guild.VoiceStates {
		if vs.UserID == user.ID {
			channel, _ := session.State.Channel(vs.ChannelID)
			return channel
		}
	}
	return nil
}

func handleUpload(w http.ResponseWriter, r *http.Request) {
	//read file from request and save to disk
	file, header, err := r.FormFile("file")

	if err != nil {
		fmt.Fprintln(w, err)
		return
	}

	defer file.Close()

	out, err := os.Create("sounds/" + header.Filename)
	if err != nil {
		fmt.Fprintf(w, "Failed to open the file for writing")
		return
	}

	defer out.Close()
	_, err = io.Copy(out, file)
	if err != nil {
		fmt.Fprintln(w, err)
	}

	//create dca filename
	dcaFilename := strings.TrimSuffix(header.Filename, filepath.Ext(header.Filename)) + ".dca"

	dcaOut, err := os.Create("sounds/" + dcaFilename)
	if err != nil {
		panic(err)
	}
	//    defer dcaOut.Close()

	// convert file to .dca
	cmd := exec.Command("dca-rs", "-i", "sounds/"+header.Filename, "--raw")

	stdoutPipe, err := cmd.StdoutPipe()
	if err != nil {
		panic(err)
	}

	writer := bufio.NewWriter(dcaOut)
	//defer writer.Flush()

	err = cmd.Start()
	if err != nil {
		panic(err)
	}

	io.Copy(writer, stdoutPipe)
	cmd.Wait()

	fmt.Println("No errors from command")
	writer.Flush()
	dcaOut.Close()

	// that was obnoxious. now let's get our command, add the sound to the map as well as our config file.
	sound := &Sound{
		Name:    dcaFilename,
		Command: r.FormValue("command"),
	}

	sound.Load()
	soundMap[sound.Command] = sound
	fmt.Println("Loaded filename", sound.Name, "loaded command", sound.Command)

	f, err := os.OpenFile("config/sounds.csv", os.O_APPEND|os.O_WRONLY, 0600)
	if err != nil {
		panic(err)
	}

	defer f.Close()

	if _, err = f.WriteString("\n" + dcaFilename + "," + r.FormValue("command")); err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"

	"github.com/jesselucas/executil"
)

// Semantic Version
const VERSION = "0.0.2"

const space = "  " // Used for pretty printing
const br = "\n\n"

func main() {
	// Check for our command line configuration flags
	var (
		versionUsage = "Prints current version" + " (v. " + VERSION + ")"
		versionPtr   = flag.Bool("version", false, versionUsage)
	)

	// Set up short hand flags
	flag.BoolVar(versionPtr, "v", false, versionUsage+" (shorthand)")
	flag.Parse()

	if *versionPtr {
		fmt.Println(VERSION)
		os.Exit(0)
	}

	// usage description
	usage := "protogen /path/to/myService.proto"
	options := "--version (-v)"
	optionsDesc := versionUsage

	// Create help message with usage messaging
	helpMessage := fmt.Sprintf(bold("USAGE:")+"\n%s%v", space, usage)
	// Break between messages
	helpMessage += br
	// Add options messaging
	helpMessage += fmt.Sprintf(bold("OPTIONS:")+"\n%v\n%s%v", options, space, optionsDesc)

	// Check arg for appname to load
	if len(os.Args) != 2 {
		fmt.Println(helpMessage)
		os.Exit(0)
	}

	protoPath := os.Args[1]

	// run protoc command (protoc --go_out=plugins=grpc:. $proto)
	cmd := executil.Command("protoc", "--go_out=plugins=grpc:.", protoPath)
	cmd.OutputPrefix = "protoc"
	err := cmd.StartAndWait()
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	fmt.Println(bold("SUCCESS: ") + protoPath + " pb.go successfully created.")

}

func bold(s string) string {
	return "\033[1m" + s + "\033[0m"
}
<commit_msg>Updating Version<commit_after>package main

import (
	"flag"
	"fmt"
	"os"

	"github.com/jesselucas/executil"
)

// Semantic Version
const Version = "0.0.3"

const space = "  " // Used for pretty printing
const br = "\n\n"

func main() {
	// Check for our command line configuration flags
	var (
		versionUsage = "Prints current version" + " (v. " + Version + ")"
		versionPtr   = flag.Bool("version", false, versionUsage)
	)

	// Set up short hand flags
	flag.BoolVar(versionPtr, "v", false, versionUsage+" (shorthand)")
	flag.Parse()

	if *versionPtr {
		fmt.Println(Version)
		os.Exit(0)
	}

	// usage description
	usage := "protogen /path/to/myService.proto"
	options := "--version (-v)"
	optionsDesc := versionUsage

	// Create help message with usage messaging
	helpMessage := fmt.Sprintf(bold("USAGE:")+"\n%s%v", space, usage)
	// Break between messages
	helpMessage += br
	// Add options messaging
	helpMessage += fmt.Sprintf(bold("OPTIONS:")+"\n%v\n%s%v", options, space, optionsDesc)

	// Check arg for appname to load
	if len(os.Args) != 2 {
		fmt.Println(helpMessage)
		os.Exit(0)
	}

	protoPath := os.Args[1]

	// run protoc command (protoc --go_out=plugins=grpc:. $proto)
	cmd := executil.Command("protoc", "--go_out=plugins=grpc:.", protoPath)
	cmd.OutputPrefix = "protoc"
	err := cmd.StartAndWait()
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	fmt.Println(bold("SUCCESS: ") + protoPath + " pb.go successfully created.")

}

func bold(s string) string {
	return "\033[1m" + s + "\033[0m"
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"strings"
	"net"
	//"golang.org/x/crypto/openpgp"
)

var outputChannel = make(chan chan string, 5)
var peers []Peer
type Peer struct {
	conn net.Conn
	username string
}
func main() {
	go printAll(outputChannel)
	listen()
}
func onMessageReceived(message string, peerFrom Peer) {
	messageChannel := make(chan string, 100)
	outputChannel <- messageChannel
	go func(){
		defer close(messageChannel)
	 	processMessage(message,messageChannel,peerFrom)
	}()
}
func processMessage(message string, messageChannel chan string, peerFrom Peer) {
	messageChannel<-"Hey, a message from "+peerFrom.username+". "
	messageChannel<-"Beginning processsing. "
	messageChannel<-"Done processing. "
	messageChannel<-"Here's the message: "
	messageChannel<-message
}

func handleConn(conn net.Conn, peerChannel chan Peer) {
	fmt.Println("CONNECTION BABE")
	username, err := bufio.NewReader(conn).ReadString('\n')
	if err != nil {
		return
	}
	username=strings.TrimSpace(username)
	fmt.Println("Received username: "+username)
	//here make sure that username is valid
	peerObj:=Peer{conn:conn,username:username}
	peerChannel<-peerObj
}
func onConnClose(peer Peer){
	//remove from list of peers, but idk how to do that in go =(
	fmt.Println("Disconnected from "+peer.username)
}
func peerListen(peer Peer){
	defer peer.conn.Close()
	defer onConnClose(peer)
	conn:=peer.conn
	username:=peer.username
	fmt.Println("Beginning to listen to "+username)
	for {
		message, err := bufio.NewReader(conn).ReadString('\n')
		if err!=nil{
			return
		}
		message=strings.TrimSpace(message)
		onMessageReceived(message,peer)
	}
}
func peerWithName(name string) int{
	for i:=0; i<len(peers); i++{
		if peers[i].username == name{
			return i
		}
	}
	return -1
}
func listen() {
	ln, err := net.Listen("tcp", ":8080")
	if err != nil {
		panic(err)
	}
	defer ln.Close()
	peerChannel := make(chan Peer)
	defer close(peerChannel)
	go func(){
		for{
			peer,ok := <-peerChannel
			if ok{
				if peerWithName(peer.username)==-1{
					peers = append(peers,peer)
					go peerListen(peer)
				}else{
				 	peer.conn.Close()
					fmt.Println("Sadly we are already connected to "+peer.username+". Disconnecting")
				}
			}else{
				return
			}
		}
	}()
	for {
		conn, err := ln.Accept()
		if err != nil {
			panic(err)
		}
		go handleConn(conn,peerChannel)
	}
}

func printAll(stringChanChan <-chan chan string) {
	for {
		strChan := <-stringChanChan
		for{
			str, ok:= <-strChan
			if ok{
				fmt.Printf(str)
			}else{
				break
			}
		}
		fmt.Println()
	}
}
<commit_msg>updated<commit_after>package main

import (
	"bufio"
	"fmt"
	"strings"
	"net"
	//"golang.org/x/crypto/openpgp"
)

var outputChannel = make(chan chan string, 5)
var peers []Peer
var myname string = "leijurv"
type Peer struct {
	conn net.Conn
	username string
}
func main() {
	go printAll(outputChannel)
	listen()
}
func onMessageReceived(message string, peerFrom Peer) {
	messageChannel := make(chan string, 100)
	outputChannel <- messageChannel
	go func(){
		defer close(messageChannel)
	 	processMessage(message,messageChannel,peerFrom)
	}()
}
func processMessage(message string, messageChannel chan string, peerFrom Peer) {
	messageChannel<-"Hey, a message from "+peerFrom.username+". "
	messageChannel<-"Beginning processsing. "
	messageChannel<-"Done processing. "
	messageChannel<-"Here's the message: "
	messageChannel<-message
}

func handleConn(conn net.Conn, peerChannel chan Peer) {
	fmt.Println("CONNECTION BABE. Sending our name")
	conn.Write([]byte(myname+"\n"));
	username, err := bufio.NewReader(conn).ReadString('\n')
	if err != nil {
		return
	}
	username=strings.TrimSpace(username)
	fmt.Println("Received username: "+username)
	//here make sure that username is valid
	peerObj:=Peer{conn:conn,username:username}
	peerChannel<-peerObj
}
func onConnClose(peer Peer){
	//remove from list of peers, but idk how to do that in go =(
	fmt.Println("Disconnected from "+peer.username)
}
func peerListen(peer Peer){
	defer peer.conn.Close()
	defer onConnClose(peer)
	conn:=peer.conn
	username:=peer.username
	fmt.Println("Beginning to listen to "+username)
	for {
		message, err := bufio.NewReader(conn).ReadString('\n')
		if err!=nil{
			return
		}
		message=strings.TrimSpace(message)
		onMessageReceived(message,peer)
	}
}
func peerWithName(name string) int{
	for i:=0; i<len(peers); i++{
		if peers[i].username == name{
			return i
		}
	}
	return -1
}
func listen() {
	ln, err := net.Listen("tcp", ":8080")
	if err != nil {
		panic(err)
	}
	defer ln.Close()
	peerChannel := make(chan Peer)
	defer close(peerChannel)
	go func(){
		for{
			peer,ok := <-peerChannel
			if ok{
				if peerWithName(peer.username)==-1{
					peers = append(peers,peer)
					go peerListen(peer)
				}else{
				 	peer.conn.Close()
					fmt.Println("Sadly we are already connected to "+peer.username+". Disconnecting")
				}
			}else{
				return
			}
		}
	}()
	for {
		conn, err := ln.Accept()
		if err != nil {
			panic(err)
		}
		go handleConn(conn,peerChannel)
	}
}

func printAll(stringChanChan <-chan chan string) {
	for {
		strChan := <-stringChanChan
		for{
			str, ok:= <-strChan
			if ok{
				fmt.Printf(str)
			}else{
				break
			}
		}
		fmt.Println()
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"golang.org/x/net/context"

	"bazil.org/fuse"
	"bazil.org/fuse/fs"

	skicka "github.com/pellaeon/skicka"
	"github.com/pellaeon/skicka/gdrive"

	"github.com/pellaeon/goas/v3/logger"
)

///////////////////////////////////////////////////////////////////////////
// Global Variables

const BLKSIZE = 4096

var progName = filepath.Base(os.Args[0])

type debugging bool

var (
	gd *gdrive.GDrive

	// The key is only set if encryption is needed (i.e. if -encrypt is
	// provided for an upload, or if an encrypted file is encountered
	// during 'download' or 'cat').
	key []byte

	debug   debugging
	verbose debugging
	quiet   bool

	// Configuration read in from the skicka config file.
	config struct {
		Google struct {
			ClientId     string
			ClientSecret string
			// If set, is appended to all http requests via ?key=XXX.
			ApiKey string
		}
		Encryption struct {
			Salt             string
			Passphrase_hash  string
			Encrypted_key    string
			Encrypted_key_iv string
		}
		Upload struct {
			Ignored_Regexp         []string
			Bytes_per_second_limit int
		}
		Download struct {
			Bytes_per_second_limit int
		}
	}

	// Various statistics gathered along the way. These all should be
	// updated using atomic operations since we often have multiple threads
	// working concurrently for uploads and downloads.
	stats struct {
		DiskReadBytes     int64
		DiskWriteBytes    int64
		UploadBytes       int64
		DownloadBytes     int64
		LocalFilesUpdated int64
		DriveFilesUpdated int64
	}

	// Smaller files will be handled with multiple threads going at once;
	// doing so improves bandwidth utilization since round-trips to the
	// Drive APIs take a while.  (However, we don't want too have too many
	// workers; this would both lead to lots of 403 rate limit errors...)
	nWorkers int
)

func usage() {
	fmt.Fprintf(os.Stderr, "Usage of %s:\n", progName)
	fmt.Fprintf(os.Stderr, "  %s MOUNTPOINT\n", progName)
	flag.PrintDefaults()
}

func userHomeDir() string {
	if runtime.GOOS == "windows" {
		home := os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
		if home == "" {
			home = os.Getenv("USERPROFILE")
		}
		return home
	}
	return os.Getenv("HOME")
}

func main() {
	l := logger.NewStandardTimeLogger(os.Stdout, time.StampMicro)
	logger.SetLogger(l)
	logger.SetLevel(logger.LevelDebug)
	log.SetFlags(0)
	log.SetPrefix(progName + ": ")

	// Initialize skicka
	home := userHomeDir()
	tokenCacheFilename := flag.String("tokencache",
		filepath.Join(home, ".skicka.tokencache.json"),
		"OAuth2 token cache file")
	configFilename := flag.String("config",
		filepath.Join(home, ".skicka.config"),
		"Configuration file")
	metadataCacheFilename := flag.String("metadata-cache-file",
		filepath.Join(home, "/.skicka.metadata.cache"),
		"Filename for local cache of Google Drive file metadata")
	nw := flag.Int("num-threads", 4, "Number of threads to use for uploads/downloads")
	vb := flag.Bool("verbose", false, "Enable verbose output")
	dbg := flag.Bool("debug", false, "Enable debugging output")
	qt := flag.Bool("quiet", false, "Suppress non-error messages")
	dumpHTTP := flag.Bool("dump-http", false, "Dump http traffic")
	flakyHTTP := flag.Bool("flaky-http", false, "Add flakiness to http traffic")
	noBrowserAuth := flag.Bool("no-browser-auth", false,
		"Don't try launching browser for authorization")
	flag.Usage = usage
	flag.Parse()

	var mountpoint string
	if flag.NArg() == 1 {
		mountpoint = flag.Arg(0)
	} else {
		mountpoint = *flag.String("mountpoint", "", "Mountpoint")
	}
	if mountpoint == "" {
		usage()
		os.Exit(2)
	}

	nWorkers = *nw

	debug = debugging(*dbg)
	verbose = debugging(*vb || bool(debug))
	quiet = *qt

	skicka.ReadConfigFile(*configFilename)

	// Set up the basic http.Transport.
	transport := http.DefaultTransport
	if tr, ok := transport.(*http.Transport); ok {
		// Increase the default number of open connections per destination host
		// to be enough for the number of goroutines we run concurrently for
		// uploads/downloads; this gives some benefit especially for uploading
		// small files.
		tr.MaxIdleConnsPerHost = 4
	} else {
		skicka.PrintErrorAndExit(fmt.Errorf("DefaultTransport not an *http.Transport?"))
	}
	if *flakyHTTP {
		transport = skicka.NewFlakyTransport(transport)
	}
	if *dumpHTTP {
		//transport = skicka.LoggingTransport{transport: transport}
		// TODO
	}

	// And now upgrade to the OAuth Transport *http.Client.
	client, err := skicka.GetOAuthClient(*tokenCacheFilename, !*noBrowserAuth,
		transport)
	if err != nil {
		skicka.PrintErrorAndExit(fmt.Errorf("error with OAuth2 Authorization: %v ", err))
	}

	// Choose the appropriate callback function for the GDrive object to
	// use for debugging output.
	var dpf func(s string, args ...interface{})
	if debug {
		dpf = skicka.DebugPrint
	} else {
		dpf = skicka.DebugNoPrint
	}

	gd, err = gdrive.New(config.Upload.Bytes_per_second_limit,
		config.Download.Bytes_per_second_limit, dpf, client,
		*metadataCacheFilename, quiet)
	if err != nil {
		log.Fatal(fmt.Errorf("error creating Google Drive "+
			"client: %v", err))
		os.Exit(3)
	}

	c, err := fuse.Mount(mountpoint)
	if err != nil {
		log.Fatalf("Mount failed: %v", err)
	}
	defer c.Close()

	filesys := &FS{
		gd: gd,
	}
	if err := fs.Serve(c, filesys); err != nil {
		log.Fatalf("Serve failed: %v", err)
	}

	// check if the mount process has an error to report
	<-c.Ready
	if err := c.MountError; err != nil {
		log.Fatalf("mount process error: %v", err)
	}
}

type FS struct {
	gd *gdrive.GDrive
}

var _ fs.FS = (*FS)(nil)

func (f *FS) Root() (fs.Node, error) {
	gd_file, err := f.gd.GetFile("/")
	if err != nil {
		logger.Errorf("FS Root() : %v", err)
	}
	return Dir{
		sk_file: gd_file,
		fs:      f,
	}, err
}

var _ fs.FSStatfser = (*FS)(nil)

func (fs *FS) Statfs(ctx context.Context, req *fuse.StatfsRequest, resp *fuse.StatfsResponse) error {
	usage, err := fs.gd.GetDriveUsage()
	if err != nil {
		logger.Errorf("Statfs() error: %v", err)
	}
	resp.Blocks = uint64(usage.Capacity / BLKSIZE)
	resp.Bfree = uint64((usage.Capacity - usage.Used) / BLKSIZE)
	resp.Bavail = uint64((usage.Capacity - usage.Used) / BLKSIZE)
	resp.Files = 9999 //XXX
	resp.Ffree = 9999 //XXX
	resp.Bsize = BLKSIZE
	resp.Namelen = 32767 //http://www.aurelp.com/2014/09/10/what-is-the-maximum-name-length-for-a-file-on-google-drive/
	resp.Frsize = BLKSIZE
	req.Respond(resp)
	return err
}

var _ fs.Node = (*Dir)(nil)

type Dir struct {
	sk_file *gdrive.File
	fs      *FS
}

func (n Dir) Attr(ctx context.Context, attr *fuse.Attr) error {
	var sum uint64
	for _, c := range n.sk_file.Id {
		sum += uint64(c)
	}
	attr.Inode = sum
	attr.Size = 4096 // XXX
	attr.Blocks = 0
	attr.Atime = n.sk_file.ModTime
	attr.Mtime = n.sk_file.ModTime
	attr.Ctime = n.sk_file.ModTime
	attr.Crtime = n.sk_file.ModTime
	attr.Mode = os.ModeDir | 0755
	attr.Nlink = 0

	return nil
}

type DirHandle struct {
	sk_file *gdrive.File
}

var _ fs.HandleReadDirAller = (*DirHandle)(nil)

func (dh *DirHandle) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
	logger.Debugf("ReadDirAll %s", dh.sk_file.Path)
	files_in_folder, err := gd.GetFilesInFolder(dh.sk_file.Path)
	logger.Debugf("ReadDirAll %+v", files_in_folder)
	if err != nil {
		log.Fatalf("ReadDirAll failed: %v", err)
	}
	var res []fuse.Dirent
	for _, file := range files_in_folder {
		var de fuse.Dirent
		de.Name = file.Path[strings.LastIndex(file.Path, "/")+1:]
		if file.IsFolder() {
			de.Type = fuse.DT_Dir
		} else {
			de.Type = fuse.DT_File
		}
		sum := uint64(0)
		for _, c := range file.Id {
			sum += uint64(c)
		}
		de.Inode = sum
		res = append(res, de)
	}
	return res, err
}

var _ fs.Node = (*File)(nil)

type File struct {
	sk_file *gdrive.File
	fs      *FS
}

func (n File) Attr(ctx context.Context, attr *fuse.Attr) error {
	sum := uint64(0)
	for _, c := range n.sk_file.Id {
		sum += uint64(c)
	}
	attr.Inode = sum
	attr.Size = uint64(n.sk_file.FileSize)
	attr.Blocks = uint64(n.sk_file.FileSize / 1024) // XXX: block size 1024 bytes
	attr.Atime = n.sk_file.ModTime
	attr.Mtime = n.sk_file.ModTime
	attr.Ctime = n.sk_file.ModTime
	attr.Crtime = n.sk_file.ModTime
	attr.Mode = os.ModeDir | 0755
	attr.Nlink = 0

	return nil
}

var _ fs.NodeRequestLookuper = (*Dir)(nil)

func (n *Dir) Lookup(ctx context.Context, req *fuse.LookupRequest, resp *fuse.LookupResponse) (fs.Node, error) {
	path := req.Name
	logger.Debugf("req.Name= " + req.Name)
	gd_file, err := n.fs.gd.GetFile(path)
	if err != nil {
		log.Fatalf("Lookup GetFile failed: %v", err)
		return nil, fuse.ENOENT
	}
	if gd_file.IsFolder() {
		return Dir{
			sk_file: gd_file,
			fs:      n.fs,
		}, nil
	} else {
		return File{
			sk_file: gd_file,
			fs:      n.fs,
		}, nil
	}
}

var _ fs.NodeOpener = (*File)(nil)

func (n File) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fs.Handle, error) {
	resp.Flags |= fuse.OpenNonSeekable
	return &FileHandle{
		sk_file: n.sk_file,
	}, nil
}

var _ fs.NodeOpener = (*Dir)(nil)

func (n Dir) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fs.Handle, error) {
	logger.Debugf("Dir.Open()")
	resp.Flags |= fuse.OpenNonSeekable
	return &DirHandle{
		sk_file: n.sk_file,
	}, nil
}

var _ fs.Handle = (*FileHandle)(nil)

type FileHandle struct {
	sk_file *gdrive.File
}

var _ fs.HandleReleaser = (*FileHandle)(nil)

func (fh *FileHandle) Release(ctx context.Context, req *fuse.ReleaseRequest) error {
	return nil
}

var _ fs.HandleReader = (*FileHandle)(nil)

func (fh *FileHandle) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error {
	buf := make([]byte, req.Size)
	reader, err := gd.GetFileContents(fh.sk_file)
	if err != nil {
		log.Fatalf("FileHandle Read GetFileContents failed: %v", err)
	}
	n, err := reader.Read(buf)
	if err != nil {
		log.Fatalf("Filehandle Read reader.Read failed: %v", err)
	}
	resp.Data = buf[:n]
	return err
}
<commit_msg>Debug# add debug messages<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"golang.org/x/net/context"

	"bazil.org/fuse"
	"bazil.org/fuse/fs"

	skicka "github.com/pellaeon/skicka"
	"github.com/pellaeon/skicka/gdrive"

	"github.com/pellaeon/goas/v3/logger"
)

///////////////////////////////////////////////////////////////////////////
// Global Variables

const BLKSIZE = 4096

var progName = filepath.Base(os.Args[0])

type debugging bool

var (
	gd *gdrive.GDrive

	// The key is only set if encryption is needed (i.e. if -encrypt is
	// provided for an upload, or if an encrypted file is encountered
	// during 'download' or 'cat').
	key []byte

	debug   debugging
	verbose debugging
	quiet   bool

	// Configuration read in from the skicka config file.
	config struct {
		Google struct {
			ClientId     string
			ClientSecret string
			// If set, is appended to all http requests via ?key=XXX.
			ApiKey string
		}
		Encryption struct {
			Salt             string
			Passphrase_hash  string
			Encrypted_key    string
			Encrypted_key_iv string
		}
		Upload struct {
			Ignored_Regexp         []string
			Bytes_per_second_limit int
		}
		Download struct {
			Bytes_per_second_limit int
		}
	}

	// Various statistics gathered along the way. These all should be
	// updated using atomic operations since we often have multiple threads
	// working concurrently for uploads and downloads.
	stats struct {
		DiskReadBytes     int64
		DiskWriteBytes    int64
		UploadBytes       int64
		DownloadBytes     int64
		LocalFilesUpdated int64
		DriveFilesUpdated int64
	}

	// Smaller files will be handled with multiple threads going at once;
	// doing so improves bandwidth utilization since round-trips to the
	// Drive APIs take a while.  (However, we don't want too have too many
	// workers; this would both lead to lots of 403 rate limit errors...)
	nWorkers int
)

func usage() {
	fmt.Fprintf(os.Stderr, "Usage of %s:\n", progName)
	fmt.Fprintf(os.Stderr, "  %s MOUNTPOINT\n", progName)
	flag.PrintDefaults()
}

func userHomeDir() string {
	if runtime.GOOS == "windows" {
		home := os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
		if home == "" {
			home = os.Getenv("USERPROFILE")
		}
		return home
	}
	return os.Getenv("HOME")
}

func main() {
	l := logger.NewStandardTimeLogger(os.Stdout, time.StampMicro)
	logger.SetLogger(l)
	logger.SetLevel(logger.LevelDebug)
	log.SetFlags(0)
	log.SetPrefix(progName + ": ")

	// Initialize skicka
	home := userHomeDir()
	tokenCacheFilename := flag.String("tokencache",
		filepath.Join(home, ".skicka.tokencache.json"),
		"OAuth2 token cache file")
	configFilename := flag.String("config",
		filepath.Join(home, ".skicka.config"),
		"Configuration file")
	metadataCacheFilename := flag.String("metadata-cache-file",
		filepath.Join(home, "/.skicka.metadata.cache"),
		"Filename for local cache of Google Drive file metadata")
	nw := flag.Int("num-threads", 4, "Number of threads to use for uploads/downloads")
	vb := flag.Bool("verbose", false, "Enable verbose output")
	dbg := flag.Bool("debug", false, "Enable debugging output")
	qt := flag.Bool("quiet", false, "Suppress non-error messages")
	dumpHTTP := flag.Bool("dump-http", false, "Dump http traffic")
	flakyHTTP := flag.Bool("flaky-http", false, "Add flakiness to http traffic")
	noBrowserAuth := flag.Bool("no-browser-auth", false,
		"Don't try launching browser for authorization")
	flag.Usage = usage
	flag.Parse()

	var mountpoint string
	if flag.NArg() == 1 {
		mountpoint = flag.Arg(0)
	} else {
		mountpoint = *flag.String("mountpoint", "", "Mountpoint")
	}
	if mountpoint == "" {
		usage()
		os.Exit(2)
	}

	nWorkers = *nw

	debug = debugging(*dbg)
	verbose = debugging(*vb || bool(debug))
	quiet = *qt

	skicka.ReadConfigFile(*configFilename)

	// Set up the basic http.Transport.
	transport := http.DefaultTransport
	if tr, ok := transport.(*http.Transport); ok {
		// Increase the default number of open connections per destination host
		// to be enough for the number of goroutines we run concurrently for
		// uploads/downloads; this gives some benefit especially for uploading
		// small files.
		tr.MaxIdleConnsPerHost = 4
	} else {
		skicka.PrintErrorAndExit(fmt.Errorf("DefaultTransport not an *http.Transport?"))
	}
	if *flakyHTTP {
		transport = skicka.NewFlakyTransport(transport)
	}
	if *dumpHTTP {
		//transport = skicka.LoggingTransport{transport: transport}
		// TODO
	}

	// And now upgrade to the OAuth Transport *http.Client.
	client, err := skicka.GetOAuthClient(*tokenCacheFilename, !*noBrowserAuth,
		transport)
	if err != nil {
		skicka.PrintErrorAndExit(fmt.Errorf("error with OAuth2 Authorization: %v ", err))
	}

	// Choose the appropriate callback function for the GDrive object to
	// use for debugging output.
	var dpf func(s string, args ...interface{})
	if debug {
		dpf = skicka.DebugPrint
	} else {
		dpf = skicka.DebugNoPrint
	}

	gd, err = gdrive.New(config.Upload.Bytes_per_second_limit,
		config.Download.Bytes_per_second_limit, dpf, client,
		*metadataCacheFilename, quiet)
	if err != nil {
		log.Fatal(fmt.Errorf("error creating Google Drive "+
			"client: %v", err))
		os.Exit(3)
	}

	c, err := fuse.Mount(mountpoint)
	if err != nil {
		log.Fatalf("Mount failed: %v", err)
	}
	defer c.Close()

	filesys := &FS{
		gd: gd,
	}
	if err := fs.Serve(c, filesys); err != nil {
		log.Fatalf("Serve failed: %v", err)
	}

	// check if the mount process has an error to report
	<-c.Ready
	if err := c.MountError; err != nil {
		log.Fatalf("mount process error: %v", err)
	}
}

type FS struct {
	gd *gdrive.GDrive
}

var _ fs.FS = (*FS)(nil)

func (f *FS) Root() (fs.Node, error) {
	gd_file, err := f.gd.GetFile("/")
	logger.Debugf("Root(): %s", gd_file.Path)
	if err != nil {
		logger.Errorf("FS Root() : %v", err)
	}
	return Dir{
		sk_file: gd_file,
		fs:      f,
	}, err
}

var _ fs.FSStatfser = (*FS)(nil)

func (fs *FS) Statfs(ctx context.Context, req *fuse.StatfsRequest, resp *fuse.StatfsResponse) error {
	usage, err := fs.gd.GetDriveUsage()
	if err != nil {
		logger.Errorf("Statfs() error: %v", err)
	}
	resp.Blocks = uint64(usage.Capacity / BLKSIZE)
	resp.Bfree = uint64((usage.Capacity - usage.Used) / BLKSIZE)
	resp.Bavail = uint64((usage.Capacity - usage.Used) / BLKSIZE)
	resp.Files = 9999 //XXX
	resp.Ffree = 9999 //XXX
	resp.Bsize = BLKSIZE
	resp.Namelen = 32767 //http://www.aurelp.com/2014/09/10/what-is-the-maximum-name-length-for-a-file-on-google-drive/
	resp.Frsize = BLKSIZE
	req.Respond(resp)
	return err
}

var _ fs.Node = (*Dir)(nil)

type Dir struct {
	sk_file *gdrive.File
	fs      *FS
}

func (n Dir) Attr(ctx context.Context, attr *fuse.Attr) error {
	logger.Debugf("%s .Attr()", n.sk_file.Path)
	var sum uint64
	for _, c := range n.sk_file.Id {
		sum += uint64(c)
	}
	logger.Debugf("%s .Attr() Id= %s", n.sk_file.Path, n.sk_file.Id)
	attr.Inode = sum
	attr.Size = 4096 // XXX
	attr.Blocks = 0
	attr.Atime = n.sk_file.ModTime
	attr.Mtime = n.sk_file.ModTime
	attr.Ctime = n.sk_file.ModTime
	attr.Crtime = n.sk_file.ModTime
	attr.Mode = os.ModeDir | 0755
	attr.Nlink = 0

	return nil
}

type DirHandle struct {
	sk_file *gdrive.File
}

var _ fs.HandleReadDirAller = (*DirHandle)(nil)

func (dh *DirHandle) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
	logger.Debugf("ReadDirAll %s", dh.sk_file.Path)
	files_in_folder, err := gd.GetFilesInFolder(dh.sk_file.Path)
	if err != nil {
		log.Fatalf("ReadDirAll failed: %v", err)
	}
	var res []fuse.Dirent
	for _, file := range files_in_folder {
		var de fuse.Dirent
		de.Name = file.Path[strings.LastIndex(file.Path, "/")+1:]
		logger.Debugf("ReadDirAll %s - %s", dh.sk_file.Path, de.Name)
		if file.IsFolder() {
			de.Type = fuse.DT_Dir
		} else {
			de.Type = fuse.DT_File
		}
		sum := uint64(0)
		for _, c := range file.Id {
			sum += uint64(c)
		}
		de.Inode = sum
		res = append(res, de)
	}
	return res, err
}

var _ fs.Node = (*File)(nil)

type File struct {
	sk_file *gdrive.File
	fs      *FS
}

func (n File) Attr(ctx context.Context, attr *fuse.Attr) error {
	logger.Debugf("%s.Attr()", n.sk_file.Path)
	sum := uint64(0)
	for _, c := range n.sk_file.Id {
		sum += uint64(c)
	}
	attr.Inode = sum
	attr.Size = uint64(n.sk_file.FileSize)
	attr.Blocks = uint64(n.sk_file.FileSize / 1024) // XXX: block size 1024 bytes
	attr.Atime = n.sk_file.ModTime
	attr.Mtime = n.sk_file.ModTime
	attr.Ctime = n.sk_file.ModTime
	attr.Crtime = n.sk_file.ModTime
	attr.Mode = os.ModeDir | 0755
	attr.Nlink = 0

	return nil
}

var _ fs.NodeRequestLookuper = (*Dir)(nil)

func (n *Dir) Lookup(ctx context.Context, req *fuse.LookupRequest, resp *fuse.LookupResponse) (fs.Node, error) {
	path := req.Name
	logger.Debugf("req.Name= " + req.Name)
	gd_file, err := n.fs.gd.GetFile(path)
	if err != nil {
		log.Panicf("Lookup GetFile failed: %v", err)
		return nil, fuse.ENOENT
	}
	if gd_file.IsFolder() {
		return Dir{
			sk_file: gd_file,
			fs:      n.fs,
		}, nil
	} else {
		return File{
			sk_file: gd_file,
			fs:      n.fs,
		}, nil
	}
}

var _ fs.NodeOpener = (*File)(nil)

func (n File) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fs.Handle, error) {
	resp.Flags |= fuse.OpenNonSeekable
	return &FileHandle{
		sk_file: n.sk_file,
	}, nil
}

var _ fs.NodeOpener = (*Dir)(nil)

func (n Dir) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fs.Handle, error) {
	logger.Debugf("Dir.Open()")
	resp.Flags |= fuse.OpenNonSeekable
	return &DirHandle{
		sk_file: n.sk_file,
	}, nil
}

var _ fs.Handle = (*FileHandle)(nil)

type FileHandle struct {
	sk_file *gdrive.File
}

var _ fs.HandleReleaser = (*FileHandle)(nil)

func (fh *FileHandle) Release(ctx context.Context, req *fuse.ReleaseRequest) error {
	return nil
}

var _ fs.HandleReader = (*FileHandle)(nil)

func (fh *FileHandle) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error {
	logger.Debugf("FileHandle Read()")
	buf := make([]byte, req.Size)
	reader, err := gd.GetFileContents(fh.sk_file)
	if err != nil {
		log.Panicf("FileHandle Read GetFileContents failed: %v", err)
	}
	n, err := reader.Read(buf)
	if err != nil {
		log.Panicf("Filehandle Read reader.Read failed: %v", err)
	}
	resp.Data = buf[:n]
	return err
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"
	"strings"
	"time"

	"github.com/jrperritt/gophercloud/rackspace"
	rsV1Servers "github.com/jrperritt/gophercloud/rackspace/compute/v1/servers"
	"github.com/rackspace/gophercloud"
	osV2Servers "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
	"github.com/rackspace/gophercloud/pagination"
	rsV2Servers "github.com/rackspace/gophercloud/rackspace/compute/v2/servers"
	"github.com/rackspace/gophercloud/rackspace/identity/v2/tokens"
)

const (
	metadataKey     = "rax:reboot_window"
	metadataTimeFmt = "2006-01-02T15:04:05Z"
)

func main() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [--csv] username apikey\n\n", os.Args[0])

		flag.CommandLine.PrintDefaults()
	}

	outputToCSV := flag.Bool("csv", false,
		"Output a CSV file to 'cs-reboot-info-output.csv' in the current directory.")
	flag.Parse()

	if flag.NArg() != 2 {
		fmt.Println("You must supply a username and API key as the last two arguments.")
		os.Exit(1)
	}

	username, apiKey := flag.Arg(0), flag.Arg(1)

	opts := gophercloud.AuthOptions{
		Username: username,
		APIKey:   apiKey,
	}

	provider, err := rackspace.AuthenticatedClient(opts)
	if err != nil {
		fmt.Printf("Unable to authenticate: %v\n", err)
		os.Exit(1)
	}

	regions, fg := Regions(provider, opts)

	fmt.Printf("Regions with a Cloud Servers endpoint: %s\n", strings.Join(regions, ", "))
	if fg {
		fmt.Println("Found both First and Next Generation endpoints.")
	}

	var entries []entry

	// Iterate through regions with an NG compute endpoint. Collect data about each server.
	for _, region := range regions {
		compute, err := rackspace.NewComputeV2(provider, gophercloud.EndpointOpts{
			Region: region,
		})
		if err != nil {
			fmt.Printf("Unable to locate a Next Gen Cloud Servers endpoint in region %s: %v\n", region, err)
			continue
		}

		err = rsV2Servers.List(compute, nil).EachPage(func(page pagination.Page) (bool, error) {
			s, err := osV2Servers.ExtractServers(page)
			if err != nil {
				return false, err
			}

			for _, server := range s {
				entry, err := ConstructEntry(server, "Next Gen", region)
				if err != nil {
					if strings.Contains(err.Error(), "not present") {
						continue
					}
					fmt.Printf("%s\n", err)
					continue
				} else {
					entries = append(entries, *entry)
				}
			}

			return true, nil
		})
	}

	// Iterate through regions with an FG compute endpoint. Collect data about each server.
	compute, err := rackspace.NewComputeV1(provider, gophercloud.EndpointOpts{
		Availability: gophercloud.AvailabilityPublic,
	})
	if err != nil {
		fmt.Printf("Unable to locate a First Gen Cloud Servers endpoint. Skipping...\n")
	} else {
		err = rsV1Servers.List(compute, rsV1Servers.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
			s, err := osV2Servers.ExtractServers(page)
			if err != nil {
				return false, err
			}

			for _, server := range s {
				entry, err := ConstructEntry(server, "First Gen", "DFW")
				if err != nil {
					if strings.Contains(err.Error(), "not present") {
						continue
					}
					fmt.Printf("%s\n", err)
					continue
				} else {
					entries = append(entries, *entry)
				}
			}

			return true, nil
		})
		if err != nil {
			fmt.Printf("Error listing servers: %+v\n", err)
		}
	}

	if len(entries) > 0 {
		if *outputToCSV {
			outputCSV(entries)
		} else {
			fmt.Printf("The following %d Cloud Servers have an automated reboot scheduled:", len(entries))
			outputTabular(entries)
		}
	}
}

// Regions acquires the service catalog and returns a slice of every region that contains a next-gen
// server endpoint, and a boolean indicating whether or not this customer has access to FG servers.
func Regions(provider *gophercloud.ProviderClient, opts gophercloud.AuthOptions) ([]string, bool) {
	service := rackspace.NewIdentityV2(provider)

	result := tokens.Create(service, tokens.WrapOptions(opts))
	catalog, err := result.ExtractServiceCatalog()
	if err != nil {
		fmt.Printf("Unable to retrieve the service catalog: %v\n", err)
		os.Exit(1)
	}

	var regions []string
	var fg bool
	for _, entry := range catalog.Entries {
		if entry.Type == "compute" {
			for _, endpoint := range entry.Endpoints {
				if endpoint.Region == "" {
					fg = true
				} else {
					regions = append(regions, endpoint.Region)
				}
			}
		}
	}
	return regions, fg
}

// ConstructEntry extracts the metadata key and builds an entry for a server.
func ConstructEntry(server osV2Servers.Server, genType, region string) (*entry, error) {
	window, ok := server.Metadata[metadataKey]
	if !ok {
		return nil, fmt.Errorf("Metadatum %s was not present in the result for server %s", metadataKey, server.ID)
	}

	windowString, ok := window.(string)
	if !ok {
		return nil, fmt.Errorf("Metadatum %s for server %s was not a string: %#v", metadataKey, server.ID, window)
	}

	// Expected format: 2014-01-28T00:00:00Z;2014-01-28T03:00:00Z

	parts := strings.Split(windowString, ";")
	if len(parts) != 2 {
		return nil, fmt.Errorf("Unexpected metadatum format for server %s: %s", server.ID, windowString)
	}

	start, err := time.Parse(metadataTimeFmt, parts[0])
	if err != nil {
		return nil, fmt.Errorf("Unable to parse window start time for server %s: %s", server.ID, parts[0])
	}

	end, err := time.Parse(metadataTimeFmt, parts[1])
	if err != nil {
		return nil, fmt.Errorf("Unable to parse window end time for server %s: %s", server.ID, parts[1])
	}

	e := &entry{
		Server:      server,
		Region:      region,
		GenType:     genType,
		WindowStart: start,
		WindowEnd:   end,
	}
	return e, nil
}
<commit_msg>Add cs-reboot-info/1.0 to the user-agent<commit_after>package main

import (
	"flag"
	"fmt"
	"os"
	"strings"
	"time"

	"github.com/jrperritt/gophercloud/rackspace"
	rsV1Servers "github.com/jrperritt/gophercloud/rackspace/compute/v1/servers"
	"github.com/rackspace/gophercloud"
	osV2Servers "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
	"github.com/rackspace/gophercloud/pagination"
	rsV2Servers "github.com/rackspace/gophercloud/rackspace/compute/v2/servers"
	"github.com/rackspace/gophercloud/rackspace/identity/v2/tokens"
)

const (
	metadataKey     = "rax:reboot_window"
	metadataTimeFmt = "2006-01-02T15:04:05Z"
)

func main() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [--csv] username apikey\n\n", os.Args[0])

		flag.CommandLine.PrintDefaults()
	}

	outputToCSV := flag.Bool("csv", false,
		"Output a CSV file to 'cs-reboot-info-output.csv' in the current directory.")
	flag.Parse()

	if flag.NArg() != 2 {
		fmt.Println("You must supply a username and API key as the last two arguments.")
		os.Exit(1)
	}

	username, apiKey := flag.Arg(0), flag.Arg(1)

	opts := gophercloud.AuthOptions{
		Username: username,
		APIKey:   apiKey,
	}

	provider, err := rackspace.AuthenticatedClient(opts)
	if err != nil {
		fmt.Printf("Unable to authenticate: %v\n", err)
		os.Exit(1)
	}

	provider.UserAgent.Prepend("cs-reboot-info/1.0")

	regions, fg := Regions(provider, opts)

	fmt.Printf("Regions with a Cloud Servers endpoint: %s\n", strings.Join(regions, ", "))
	if fg {
		fmt.Println("Found both First and Next Generation endpoints.")
	}

	var entries []entry

	// Iterate through regions with an NG compute endpoint. Collect data about each server.
	for _, region := range regions {
		compute, err := rackspace.NewComputeV2(provider, gophercloud.EndpointOpts{
			Region: region,
		})
		if err != nil {
			fmt.Printf("Unable to locate a Next Gen Cloud Servers endpoint in region %s: %v\n", region, err)
			continue
		}

		err = rsV2Servers.List(compute, nil).EachPage(func(page pagination.Page) (bool, error) {
			s, err := osV2Servers.ExtractServers(page)
			if err != nil {
				return false, err
			}

			for _, server := range s {
				entry, err := ConstructEntry(server, "Next Gen", region)
				if err != nil {
					if strings.Contains(err.Error(), "not present") {
						continue
					}
					fmt.Printf("%s\n", err)
					continue
				} else {
					entries = append(entries, *entry)
				}
			}

			return true, nil
		})
	}

	// Iterate through regions with an FG compute endpoint. Collect data about each server.
	compute, err := rackspace.NewComputeV1(provider, gophercloud.EndpointOpts{
		Availability: gophercloud.AvailabilityPublic,
	})
	if err != nil {
		fmt.Printf("Unable to locate a First Gen Cloud Servers endpoint. Skipping...\n")
	} else {
		err = rsV1Servers.List(compute, rsV1Servers.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
			s, err := osV2Servers.ExtractServers(page)
			if err != nil {
				return false, err
			}

			for _, server := range s {
				entry, err := ConstructEntry(server, "First Gen", "DFW")
				if err != nil {
					if strings.Contains(err.Error(), "not present") {
						continue
					}
					fmt.Printf("%s\n", err)
					continue
				} else {
					entries = append(entries, *entry)
				}
			}

			return true, nil
		})
		if err != nil {
			fmt.Printf("Error listing servers: %+v\n", err)
		}
	}

	if len(entries) > 0 {
		if *outputToCSV {
			outputCSV(entries)
		} else {
			fmt.Printf("The following %d Cloud Servers have an automated reboot scheduled:", len(entries))
			outputTabular(entries)
		}
	}
}

// Regions acquires the service catalog and returns a slice of every region that contains a next-gen
// server endpoint, and a boolean indicating whether or not this customer has access to FG servers.
func Regions(provider *gophercloud.ProviderClient, opts gophercloud.AuthOptions) ([]string, bool) {
	service := rackspace.NewIdentityV2(provider)

	result := tokens.Create(service, tokens.WrapOptions(opts))
	catalog, err := result.ExtractServiceCatalog()
	if err != nil {
		fmt.Printf("Unable to retrieve the service catalog: %v\n", err)
		os.Exit(1)
	}

	var regions []string
	var fg bool
	for _, entry := range catalog.Entries {
		if entry.Type == "compute" {
			for _, endpoint := range entry.Endpoints {
				if endpoint.Region == "" {
					fg = true
				} else {
					regions = append(regions, endpoint.Region)
				}
			}
		}
	}
	return regions, fg
}

// ConstructEntry extracts the metadata key and builds an entry for a server.
func ConstructEntry(server osV2Servers.Server, genType, region string) (*entry, error) {
	window, ok := server.Metadata[metadataKey]
	if !ok {
		return nil, fmt.Errorf("Metadatum %s was not present in the result for server %s", metadataKey, server.ID)
	}

	windowString, ok := window.(string)
	if !ok {
		return nil, fmt.Errorf("Metadatum %s for server %s was not a string: %#v", metadataKey, server.ID, window)
	}

	// Expected format: 2014-01-28T00:00:00Z;2014-01-28T03:00:00Z

	parts := strings.Split(windowString, ";")
	if len(parts) != 2 {
		return nil, fmt.Errorf("Unexpected metadatum format for server %s: %s", server.ID, windowString)
	}

	start, err := time.Parse(metadataTimeFmt, parts[0])
	if err != nil {
		return nil, fmt.Errorf("Unable to parse window start time for server %s: %s", server.ID, parts[0])
	}

	end, err := time.Parse(metadataTimeFmt, parts[1])
	if err != nil {
		return nil, fmt.Errorf("Unable to parse window end time for server %s: %s", server.ID, parts[1])
	}

	e := &entry{
		Server:      server,
		Region:      region,
		GenType:     genType,
		WindowStart: start,
		WindowEnd:   end,
	}
	return e, nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
)

var streams []stream
var commands []*exec.Cmd

type stream struct {
	Name   string
	Stream string
	Image  string
}

func main() {
	//Read in urls of webcams from configuration file
	data, err := ioutil.ReadFile("streams.json")
	if err != nil {
		panic(err)
	}

	if err := json.Unmarshal(data, &streams); err != nil {
		panic(err)
	}

	//Startup webserver to listen to commands to execute video player
	http.HandleFunc("/", serveIndex)
	http.HandleFunc("/all", serveAll)
	http.HandleFunc("/pick", serveOne)
	log.Fatal(http.ListenAndServe(":2000", nil))
	log.Println("Webserver started")

	//Startup players in default mode (view all)
}

func serveIndex(w http.ResponseWriter, r *http.Request) {
	//Serve up basic website with icons to choose from with "all" option
	io.WriteString(w, "Hello world!")
}

func serveAll(w http.ResponseWriter, r *http.Request) {
	//Close all existing processes, and fire up all the videos
	showAll()
	io.WriteString(w, "success")
}

func serveOne(w http.ResponseWriter, r *http.Request) {
	//Close all existing processes, and fire up the single video passed in
	name := r.FormValue("name")
	log.Println("got name of " + name)
	for _, stream := range streams {
		if stream.Name == name {
			log.Println("Starting stream " + stream.Name)
			showOne(stream)
		}
	}
	io.WriteString(w, "success")
}

func showAll() {
	killAll()
	width := 1900
	height := 1200
	streamCount := len(streams)

	//Determine how many streams we have to make even boxed grids
	boxes := 1
	for ; boxes*boxes < streamCount; boxes++ {
	}

	startWidth := 0
	startHeight := 0
	widthStep := width / boxes
	heightStep := height / boxes
	//We now have a box X box width screen (say 3x3), so split the screen appropriately
	for index, s := range streams {
		endWidth := startWidth + (index * widthStep)
		endHeight := startHeight + (index * heightStep)
		log.Printf("end width is %v and end height is %v\n", endWidth, endHeight)
		cmd := exec.Command("mplayer", s.Stream) //"--win", fmt.Sprintf("%v,%v,%v,%v", startWidth, startHeight, endWidth, endHeight),
		cmd.Stdout = os.Stdout
		cmd.Start()
		commands = append(commands, cmd)
	}
}

func showOne(s stream) {
	killAll()
	//Startup in fullscreen
	cmd := exec.Command("mplayer", s.Stream)
	cmd.Start()
	commands = append(commands, cmd)
}

func killAll() {
	log.Println("killing all existing commands")
	for _, proc := range commands {
		proc.Process.Kill()
	}
}
<commit_msg>hacking together streams management with basic website<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"html/template"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
)

var streams []stream
var commands []*exec.Cmd

type stream struct {
	Name   string
	Stream string
	Image  string
}

func main() {
	//Read in urls of webcams from configuration file
	data, err := ioutil.ReadFile("streams.json")
	if err != nil {
		panic(err)
	}

	if err := json.Unmarshal(data, &streams); err != nil {
		panic(err)
	}

	//Startup webserver to listen to commands to execute video player
	http.HandleFunc("/", serveIndex)
	http.HandleFunc("/all", serveAll)
	http.HandleFunc("/pick", serveOne)
	log.Fatal(http.ListenAndServe(":2000", nil))
	log.Println("Webserver started")

	//Startup players in default mode (view all)
}

func renderWebsite(w http.ResponseWriter) {
	const tpl = `
<!DOCTYPE html>

		<meta charset="UTF-8">
		<title>Zoo Cam Viewer</title>

All

		{{range .Streams}}
{{ .Name }}

{{else}}
no streams
{{end}}

`
	t, err := template.New("webpage").Parse(tpl)
	if err != nil {
		panic(err)
	}
	t.Execute(w, struct{ Streams []stream }{Streams: streams})
}

func serveIndex(w http.ResponseWriter, r *http.Request) {
	//Serve up basic website with icons to choose from with "all" option
	renderWebsite(w)
}

func serveAll(w http.ResponseWriter, r *http.Request) {
	//Close all existing processes, and fire up all the videos
	showAll()
	renderWebsite(w)
}

func serveOne(w http.ResponseWriter, r *http.Request) {
	//Close all existing processes, and fire up the single video passed in
	name := r.FormValue("name")
	log.Println("got stream name of " + name)
	for _, stream := range streams {
		if stream.Name == name {
			log.Println("Starting stream " + stream.Name)
			showOne(stream)
		}
	}
	renderWebsite(w)
}

func showAll() {
	killAll()
	width := 1900
	height := 1200
	streamCount := len(streams)

	//Determine how many streams we have to make even boxed grids
	boxes := 1
	for ; boxes*boxes < streamCount; boxes++ {
	}

	startWidth := 0
	startHeight := 0
	widthStep := width / boxes
	heightStep := height / boxes
	//We now have a box X box width screen (say 3x3), so split the screen appropriately
	for index, s := range streams {
		endWidth := startWidth + (index * widthStep)
		endHeight := startHeight + (index * heightStep)
		log.Printf("end width is %v and end height is %v\n", endWidth, endHeight)
		cmd := exec.Command("omxplayer", "--win", fmt.Sprintf("%v,%v,%v,%v", startWidth, startHeight, endWidth, endHeight), s.Stream)
		cmd.Stdout = os.Stdout
		cmd.Start()
		commands = append(commands, cmd)
	}
}

func showOne(s stream) {
	killAll()
	//Startup in fullscreen
	cmd := exec.Command("mplayer", s.Stream)
	cmd.Start()
	commands = append(commands, cmd)
}

func killAll() {
	log.Println("killing all existing streams")
	for _, proc := range commands {
		proc.Process.Kill()
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"net/http"
	"gopkg.in/alecthomas/kingpin.v2"
	"path"
)

var (
	hostFlag = kingpin.Flag("host", "Set the host of Hoverfly").String()
	adminPortFlag = kingpin.Flag("admin-port", "Set the admin port of Hoverfly").String()
	proxyPortFlag = kingpin.Flag("proxy-port", "Set the admin port of Hoverfly").String()
	verboseFlag = kingpin.Flag("verbose", "Verbose mode.").Short('v').Bool()

	modeCommand = kingpin.Command("mode", "Get Hoverfly's current mode")
	modeNameArg = modeCommand.Arg("name", "Set Hoverfly's mode").String()

	startCommand = kingpin.Command("start", "Start a local instance of Hoverfly")
	stopCommand = kingpin.Command("stop", "Stop a local instance of Hoverfly")

	exportCommand = kingpin.Command("export", "Exports data out of Hoverfly")
	exportNameArg = exportCommand.Arg("name", "Name of exported simulation").Required().String()

	importCommand = kingpin.Command("import", "Imports data into Hoverfly")
	importNameArg = importCommand.Arg("name", "Name of imported simulation").Required().String()

	pushCommand = kingpin.Command("push", "Pushes the data to Specto Hub")
	pushNameArg = pushCommand.Arg("name", "Name of exported simulation").Required().String()

	pullCommand = kingpin.Command("pull", "Pushes the data to Specto Hub")
	pullNameArg = pullCommand.Arg("name", "Name of imported simulation").Required().String()
	pullOverrideHostFlag = pullCommand.Flag("override-host", "Name of the host you want to virtualise").String()

	wipeCommand = kingpin.Command("wipe", "Wipe Hoverfly database")
)

func main() {
	kingpin.Parse()

	config := GetConfig(*hostFlag, *adminPortFlag, *proxyPortFlag)

	hoverflyDirectory := getHoverflyDirectory(config)

	cacheDirectory, err := createCacheDirectory(hoverflyDirectory)
	if err != nil {
		failAndExitWithVerboseLevel("Could not create local cache", err, *verboseFlag)
	}

	localCache := LocalCache{
		Uri: cacheDirectory,
	}

	hoverfly := Hoverfly {
		Host: config.HoverflyHost,
		AdminPort: config.HoverflyAdminPort,
		ProxyPort: config.HoverflyProxyPort,
		httpClient: http.DefaultClient,
	}

	spectoLab := SpectoLab{
		Host: config.SpectoLabHost,
		Port: config.SpectoLabPort,
		ApiKey: config.SpectoLabApiKey,
	}

	switch kingpin.Parse() {
		case modeCommand.FullCommand():
			if *modeNameArg == "" || *modeNameArg == "status"{

				mode, err := hoverfly.GetMode()
				if err == nil {
					fmt.Println("Hoverfly is set to", mode, "mode")
				} else {
					failAndExitWithVerboseLevel("Could not get Hoverfly's mode", err, *verboseFlag)
				}

			} else {

				mode, err := hoverfly.SetMode(*modeNameArg)
				if err == nil {
					fmt.Println("Hoverfly has been set to", mode, "mode")
				} else {
					failAndExitWithVerboseLevel("Could not set Hoverfly's mode", err, *verboseFlag)
				}

			}

		case startCommand.FullCommand():
			err := startHandler(hoverflyDirectory, hoverfly)
			if err != nil {
				failAndExitWithVerboseLevel("Could not start Hoverfly", err, *verboseFlag)
			}

		case stopCommand.FullCommand():
			stopHandler(hoverflyDirectory, hoverfly)

		case exportCommand.FullCommand():
			hoverfile, err := NewHoverfile(*exportNameArg)
			if err != nil {
				failAndExitWithVerboseLevel("Could not export from Hoverfly", err, *verboseFlag)
			}

			exportedData, err := hoverfly.ExportSimulation()

			if err != nil {
				failAndExitWithVerboseLevel("Could not export from Hoverfly", err, *verboseFlag)
			}

			if err = localCache.WriteSimulation(hoverfile, exportedData); err == nil {
				fmt.Println(*exportNameArg, "exported successfully")
			} else {
				failAndExitWithVerboseLevel("Could not write simulation to local cache", err, *verboseFlag)
			}

		case importCommand.FullCommand():
			hoverfile, err := NewHoverfile(*importNameArg)
			if err != nil {
				failAndExitWithVerboseLevel("Could not import into Hoverfly", err, *verboseFlag)
			}

			data, err := localCache.ReadSimulation(hoverfile)
			if err != nil {
				failAndExitWithVerboseLevel("Could not read simulation from local cache", err, *verboseFlag)
			}

			if err = hoverfly.ImportSimulation(string(data)); err == nil {
				fmt.Println(hoverfile.String(), "imported successfully")
			} else {
				failAndExitWithVerboseLevel("Could not import into Hoverfly", err, *verboseFlag)
			}

		case pushCommand.FullCommand():
			hoverfile, err := NewHoverfile(*pushNameArg)
			if err != nil {
				failAndExitWithVerboseLevel("Could not push to Specto Labs", err, *verboseFlag)
			}

			data, err := localCache.ReadSimulation(hoverfile)
			if err != nil {
				failAndExitWithVerboseLevel("Could not read simulation from local cache", err, *verboseFlag)
			}

			statusCode, err := spectoLab.UploadSimulation(hoverfile, data)
			if err != nil {
				failAndExitWithVerboseLevel("Could not upload simulation to Specto Labs", err, *verboseFlag)
			}

			if statusCode == 200 {
				fmt.Println(hoverfile.String(), "has been pushed to the Specto Lab")
			}

		case pullCommand.FullCommand():
			hoverfile, err := NewHoverfile(*pullNameArg)
			if err != nil {
				failAndExitWithVerboseLevel("Could not pull from Specto Labs", err, *verboseFlag)
			}

			data := spectoLab.GetSimulation(hoverfile, *pullOverrideHostFlag)

			if err := localCache.WriteSimulation(hoverfile, data); err == nil {
				fmt.Println(hoverfile.String(), "has been pulled from the Specto Lab")
			} else {
				failAndExitWithVerboseLevel("Could not write simulation to local cache", err, *verboseFlag)
			}

		case wipeCommand.FullCommand():
			if err := hoverfly.Wipe(); err == nil {
				fmt.Println("Hoverfly has been wiped")
			} else {
				failAndExitWithVerboseLevel("Could not wipe Hoverfly", err, *verboseFlag)
			}
	}
}

func failAndExitWithVerboseLevel(message string, err error, verbose bool) {
	fmt.Println(message)
	if verbose {
		fmt.Println(err.Error())
	}
	os.Exit(1)
}

func getHoverflyDirectory(config Config) string {
	if len(config.GetFilepath()) == 0 {
		fmt.Println("Missing a config file")
		fmt.Println("Creating a new  a config file")

		hoverflyDir, err := createHomeDirectory()

		if err != nil {
			failAndExitWithVerboseLevel("Could not get .hoverfly directory", err, *verboseFlag)
		}

		err = config.WriteToFile(hoverflyDir)

		if err != nil {
			failAndExitWithVerboseLevel("Could not write new config to disk", err, *verboseFlag)
		}

		return hoverflyDir
	}

	return path.Dir(config.GetFilepath())
}<commit_msg>Renamed the new way of failing and exitiing<commit_after>package main

import (
	"fmt"
	"os"
	"net/http"
	"gopkg.in/alecthomas/kingpin.v2"
	"path"
)

var (
	hostFlag = kingpin.Flag("host", "Set the host of Hoverfly").String()
	adminPortFlag = kingpin.Flag("admin-port", "Set the admin port of Hoverfly").String()
	proxyPortFlag = kingpin.Flag("proxy-port", "Set the admin port of Hoverfly").String()
	verboseFlag = kingpin.Flag("verbose", "Verbose mode.").Short('v').Bool()

	modeCommand = kingpin.Command("mode", "Get Hoverfly's current mode")
	modeNameArg = modeCommand.Arg("name", "Set Hoverfly's mode").String()

	startCommand = kingpin.Command("start", "Start a local instance of Hoverfly")
	stopCommand = kingpin.Command("stop", "Stop a local instance of Hoverfly")

	exportCommand = kingpin.Command("export", "Exports data out of Hoverfly")
	exportNameArg = exportCommand.Arg("name", "Name of exported simulation").Required().String()

	importCommand = kingpin.Command("import", "Imports data into Hoverfly")
	importNameArg = importCommand.Arg("name", "Name of imported simulation").Required().String()

	pushCommand = kingpin.Command("push", "Pushes the data to Specto Hub")
	pushNameArg = pushCommand.Arg("name", "Name of exported simulation").Required().String()

	pullCommand = kingpin.Command("pull", "Pushes the data to Specto Hub")
	pullNameArg = pullCommand.Arg("name", "Name of imported simulation").Required().String()
	pullOverrideHostFlag = pullCommand.Flag("override-host", "Name of the host you want to virtualise").String()

	wipeCommand = kingpin.Command("wipe", "Wipe Hoverfly database")
)

func main() {
	kingpin.Parse()

	config := GetConfig(*hostFlag, *adminPortFlag, *proxyPortFlag)

	hoverflyDirectory := getHoverflyDirectory(config)

	cacheDirectory, err := createCacheDirectory(hoverflyDirectory)
	if err != nil {
		failAndExit("Could not create local cache", err, *verboseFlag)
	}

	localCache := LocalCache{
		Uri: cacheDirectory,
	}

	hoverfly := Hoverfly {
		Host: config.HoverflyHost,
		AdminPort: config.HoverflyAdminPort,
		ProxyPort: config.HoverflyProxyPort,
		httpClient: http.DefaultClient,
	}

	spectoLab := SpectoLab{
		Host: config.SpectoLabHost,
		Port: config.SpectoLabPort,
		ApiKey: config.SpectoLabApiKey,
	}

	switch kingpin.Parse() {
		case modeCommand.FullCommand():
			if *modeNameArg == "" || *modeNameArg == "status"{

				mode, err := hoverfly.GetMode()
				if err == nil {
					fmt.Println("Hoverfly is set to", mode, "mode")
				} else {
					failAndExit("Could not get Hoverfly's mode", err, *verboseFlag)
				}

			} else {

				mode, err := hoverfly.SetMode(*modeNameArg)
				if err == nil {
					fmt.Println("Hoverfly has been set to", mode, "mode")
				} else {
					failAndExit("Could not set Hoverfly's mode", err, *verboseFlag)
				}

			}

		case startCommand.FullCommand():
			err := startHandler(hoverflyDirectory, hoverfly)
			if err != nil {
				failAndExit("Could not start Hoverfly", err, *verboseFlag)
			}

		case stopCommand.FullCommand():
			stopHandler(hoverflyDirectory, hoverfly)

		case exportCommand.FullCommand():
			hoverfile, err := NewHoverfile(*exportNameArg)
			if err != nil {
				failAndExit("Could not export from Hoverfly", err, *verboseFlag)
			}

			exportedData, err := hoverfly.ExportSimulation()

			if err != nil {
				failAndExit("Could not export from Hoverfly", err, *verboseFlag)
			}

			if err = localCache.WriteSimulation(hoverfile, exportedData); err == nil {
				fmt.Println(*exportNameArg, "exported successfully")
			} else {
				failAndExit("Could not write simulation to local cache", err, *verboseFlag)
			}

		case importCommand.FullCommand():
			hoverfile, err := NewHoverfile(*importNameArg)
			if err != nil {
				failAndExit("Could not import into Hoverfly", err, *verboseFlag)
			}

			data, err := localCache.ReadSimulation(hoverfile)
			if err != nil {
				failAndExit("Could not read simulation from local cache", err, *verboseFlag)
			}

			if err = hoverfly.ImportSimulation(string(data)); err == nil {
				fmt.Println(hoverfile.String(), "imported successfully")
			} else {
				failAndExit("Could not import into Hoverfly", err, *verboseFlag)
			}

		case pushCommand.FullCommand():
			hoverfile, err := NewHoverfile(*pushNameArg)
			if err != nil {
				failAndExit("Could not push to Specto Labs", err, *verboseFlag)
			}

			data, err := localCache.ReadSimulation(hoverfile)
			if err != nil {
				failAndExit("Could not read simulation from local cache", err, *verboseFlag)
			}

			statusCode, err := spectoLab.UploadSimulation(hoverfile, data)
			if err != nil {
				failAndExit("Could not upload simulation to Specto Labs", err, *verboseFlag)
			}

			if statusCode == 200 {
				fmt.Println(hoverfile.String(), "has been pushed to the Specto Lab")
			}

		case pullCommand.FullCommand():
			hoverfile, err := NewHoverfile(*pullNameArg)
			if err != nil {
				failAndExit("Could not pull from Specto Labs", err, *verboseFlag)
			}

			data := spectoLab.GetSimulation(hoverfile, *pullOverrideHostFlag)

			if err := localCache.WriteSimulation(hoverfile, data); err == nil {
				fmt.Println(hoverfile.String(), "has been pulled from the Specto Lab")
			} else {
				failAndExit("Could not write simulation to local cache", err, *verboseFlag)
			}

		case wipeCommand.FullCommand():
			if err := hoverfly.Wipe(); err == nil {
				fmt.Println("Hoverfly has been wiped")
			} else {
				failAndExit("Could not wipe Hoverfly", err, *verboseFlag)
			}
	}
}

func failAndExit(message string, err error, verbose bool) {
	fmt.Println(message)
	if verbose {
		fmt.Println(err.Error())
	}
	os.Exit(1)
}

func getHoverflyDirectory(config Config) string {
	if len(config.GetFilepath()) == 0 {
		fmt.Println("Missing a config file")
		fmt.Println("Creating a new  a config file")

		hoverflyDir, err := createHomeDirectory()

		if err != nil {
			failAndExit("Could not get .hoverfly directory", err, *verboseFlag)
		}

		err = config.WriteToFile(hoverflyDir)

		if err != nil {
			failAndExit("Could not write new config to disk", err, *verboseFlag)
		}

		return hoverflyDir
	}

	return path.Dir(config.GetFilepath())
}<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/ChimeraCoder/anaconda"    // Working at 2002271f2160a4d243f0308af0827893e2868157
	"github.com/darkhelmet/twitterstream" // Working at 4051c41877496d38d54647c35897e768fd34385f
	"io/ioutil"
	"log"
	"net/url"
	"strings"
)

func main() {
	log.Println("Twitterd Started")
	b, e := ioutil.ReadFile("./twittercfg")
	if e != nil {
		log.Fatal("Could not read the ./twittercfg file.")
	}
	twittertemp := string(b)
	twitterbits := strings.Split(twittertemp, "\n")
	if len(twitterbits) != 5 {
		log.Fatal("Not enought things in twitter cfg, Needs to be (seperated by \\n) username, consumerKey, consumerSecret, accessToken, accessSecret")
	}
	Client := twitterstream.NewClient(twitterbits[1], twitterbits[2], twitterbits[3], twitterbits[4])
	Conn, e := Client.Track(fmt.Sprintf("@%s", twitterbits[0]))
	// Streamign API is setup now, now just setup the general purpose one now
	anaconda.SetConsumerKey(twitterbits[1])
	anaconda.SetConsumerSecret(twitterbits[2])
	api := anaconda.NewTwitterApi(twitterbits[3], twitterbits[4])

	if e != nil {
		log.Fatal("could not open a streaming connection to get mentions :(")
	}
	for {
		t, e := Conn.Next()
		if e == nil {
			log.Println("TWEET: %s\n", t.Text)
			log.Println("OWNER @%s\n", strings.ToLower(twitterbits[0]))
			if strings.HasPrefix(strings.ToLower(t.Text), fmt.Sprintf("@%s", strings.ToLower(twitterbits[0]))) {
				v := url.Values{} // I dont even know
				t, e := api.PostTweet(fmt.Sprintf("@%s pong", t.User.ScreenName), v)
				if e == nil {
					fmt.Println(t)
				} else {
					fmt.Println(e)
				}
			} else {
				log.Println("Does not start with @<user> ignoring")
			}
		}
	}

}
<commit_msg>Fix spel<commit_after>package main

import (
	"fmt"
	"github.com/ChimeraCoder/anaconda"    // Working at 2002271f2160a4d243f0308af0827893e2868157
	"github.com/darkhelmet/twitterstream" // Working at 4051c41877496d38d54647c35897e768fd34385f
	"io/ioutil"
	"log"
	"net/url"
	"strings"
)

func main() {
	log.Println("Twitterd Started")
	b, e := ioutil.ReadFile("./twittercfg")
	if e != nil {
		log.Fatal("Could not read the ./twittercfg file.")
	}
	twittertemp := string(b)
	twitterbits := strings.Split(twittertemp, "\n")
	if len(twitterbits) != 5 {
		log.Fatal("Not enought things in twitter cfg, Needs to be (seperated by \\n) username, consumerKey, consumerSecret, accessToken, accessSecret")
	}
	Client := twitterstream.NewClient(twitterbits[1], twitterbits[2], twitterbits[3], twitterbits[4])
	Conn, e := Client.Track(fmt.Sprintf("@%s", twitterbits[0]))
	// Streaming API is setup now, now just setup the general purpose one now
	anaconda.SetConsumerKey(twitterbits[1])
	anaconda.SetConsumerSecret(twitterbits[2])
	api := anaconda.NewTwitterApi(twitterbits[3], twitterbits[4])

	if e != nil {
		log.Fatal("could not open a streaming connection to get mentions :(")
	}
	for {
		t, e := Conn.Next()
		if e == nil {
			log.Println("TWEET: %s\n", t.Text)
			log.Println("OWNER @%s\n", strings.ToLower(twitterbits[0]))
			if strings.HasPrefix(strings.ToLower(t.Text), fmt.Sprintf("@%s", strings.ToLower(twitterbits[0]))) {
				v := url.Values{} // I dont even know
				t, e := api.PostTweet(fmt.Sprintf("@%s pong", t.User.ScreenName), v)
				if e == nil {
					fmt.Println(t)
				} else {
					fmt.Println(e)
				}
			} else {
				log.Println("Does not start with @<user> ignoring")
			}
		}
	}

}
<|endoftext|>


<commit_before>package main

import (
	"github.com/graphql-go/graphql"
	"github.com/graphql-go/handler"
	"github.com/pressly/chi"
	"github.com/sogko/data-gov-sg-graphql-go/lib/datagovsg"
	"github.com/sogko/data-gov-sg-graphql-go/lib/schema"
	"github.com/unrolled/render"
	"golang.org/x/net/context"
	"net/http"
	"os"
)

var R *render.Render
var API_KEY string

func init() {
	// Set data.gov.sg API key
	API_KEY = os.Getenv("DATAGOVSG_API_KEY")
	if API_KEY == "" {
		panic("Set DATAGOVSG_API_KEY environment variable before running test")
	}

	R = render.New(render.Options{
		Directory:     "views",
		IsDevelopment: true,
		Extensions:    []string{".html"},
	})
}

func serveGraphQL(ctx context.Context, w http.ResponseWriter, r *http.Request) {
	// get query
	opts := handler.NewRequestOptions(r)

	// init and store data.gov.sg client
	ctx = context.WithValue(ctx, "client", datagovsg.NewClient(API_KEY))

	// execute graphql query
	params := graphql.Params{
		Schema:         schema.Root,
		RequestString:  opts.Query,
		VariableValues: opts.Variables,
		OperationName:  opts.OperationName,
		Context:        ctx,
	}
	result := graphql.Do(params)

	// render result
	R.JSON(w, http.StatusOK, result)
}
func main() {
	r := chi.NewRouter()

	r.Handle("/graphql", serveGraphQL)
	r.FileServer("/", http.Dir("static"))

	http.ListenAndServe(":3000", r)
}
<commit_msg>Make app openshift-ready<commit_after>package main

import (
	"fmt"
	"github.com/graphql-go/graphql"
	"github.com/graphql-go/handler"
	"github.com/pressly/chi"
	"github.com/sogko/data-gov-sg-graphql-go/lib/datagovsg"
	"github.com/sogko/data-gov-sg-graphql-go/lib/schema"
	"github.com/unrolled/render"
	"golang.org/x/net/context"
	"net/http"
	"os"
)

var R *render.Render
var API_KEY string

var PORT string

func init() {

	// Determine which port to server app from
	PORT := os.Getenv("OPENSHIFT_GO_PORT")
	if PORT == "" {
		PORT = os.Getenv("DATAGOVSG_PORT")
	}
	if PORT == "" {
		PORT = "3000"
	}

	// Set data.gov.sg API key
	API_KEY = os.Getenv("DATAGOVSG_API_KEY")
	if API_KEY == "" {
		panic("Set DATAGOVSG_API_KEY environment variable before running test")
	}

	R = render.New(render.Options{
		Directory:     "views",
		IsDevelopment: true,
		Extensions:    []string{".html"},
	})
}

func serveGraphQL(ctx context.Context, w http.ResponseWriter, r *http.Request) {
	// get query
	opts := handler.NewRequestOptions(r)

	// init and store data.gov.sg client
	ctx = context.WithValue(ctx, "client", datagovsg.NewClient(API_KEY))

	// execute graphql query
	params := graphql.Params{
		Schema:         schema.Root,
		RequestString:  opts.Query,
		VariableValues: opts.Variables,
		OperationName:  opts.OperationName,
		Context:        ctx,
	}
	result := graphql.Do(params)

	// render result
	R.JSON(w, http.StatusOK, result)
}
func main() {
	r := chi.NewRouter()

	r.Handle("/graphql", serveGraphQL)
	r.FileServer("/", http.Dir("static"))

	http.ListenAndServe(fmt.Sprintf(":%v", PORT), r)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"os"
)

func main() {
	err := ScrShot()
	if err != nil {
		log.Fatal(err)
	}
	err = Cutter()
	if err != nil {
		log.Fatal(err)
	}
	s, err := ColorCount()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(s)
	if s > SCORE {
		os.Exit(1)
	} else {
		os.Exit(0)
	}
}
<commit_msg>Delete Test Println<commit_after>package main

import (
	"fmt"
	"log"
	"os"
)

func main() {
	err := ScrShot()
	if err != nil {
		log.Fatal(err)
	}
	err = Cutter()
	if err != nil {
		log.Fatal(err)
	}
	s, err := ColorCount()
	if err != nil {
		log.Fatal(err)
	}
	if s > SCORE {
		os.Exit(1)
	} else {
		os.Exit(0)
	}
}
<|endoftext|>