<commit_before>package plugins

import (
	"strings"
	"time"

	"github.com/Seklfreak/Robyul2/cache"
	"github.com/Seklfreak/Robyul2/helpers"
	"github.com/bwmarrin/discordgo"
	rethink "github.com/gorethink/gorethink"
	"github.com/olebedev/when"
	"github.com/olebedev/when/rules/common"
	"github.com/olebedev/when/rules/en"
)

type Reminders struct {
	parser *when.Parser
}

type DB_Reminders struct {
	Id        string        `gorethink:"id,omitempty"`
	UserID    string        `gorethink:"userid"`
	Reminders []DB_Reminder `gorethink:"reminders"`
}

type DB_Reminder struct {
	Message   string `gorethink:"message"`
	ChannelID string `gorethink:"channelID"`
	GuildID   string `gorethink:"guildID"`
	Timestamp int64  `gorethink:"timestamp"`
}

func (r *Reminders) Commands() []string {
	return []string{
		"remind",
		"remindme",
		"rm",
		"reminders",
		"rms",
	}
}

func (r *Reminders) Init(session *discordgo.Session) {
	r.parser = when.New(nil)
	r.parser.Add(en.All...)
	r.parser.Add(common.All...)

	go func() {
		defer helpers.Recover()

		for {
			reminderBucket := make([]DB_Reminders, 0)
			cursor, err := rethink.Table("reminders").Run(helpers.GetDB())
			helpers.Relax(err)

			err = cursor.All(&reminderBucket)
			helpers.Relax(err)

			for _, reminders := range reminderBucket {
				changes := false

				// Downward loop for in-loop element removal
				for idx := len(reminders.Reminders) - 1; idx >= 0; idx-- {
					reminder := reminders.Reminders[idx]

					if reminder.Timestamp <= time.Now().Unix() {
						dmChannel, err := session.UserChannelCreate(reminders.UserID)
						helpers.Relax(err)

						content := ":alarm_clock: You wanted me to remind you about this:\n" + "```" + helpers.ZERO_WIDTH_SPACE + reminder.Message + "```"
						if reminder.Message == "" {
							content = ":alarm_clock: You wanted me to remind you about something, but you didn't tell me about what. <:blobthinking:317028940885524490>"
						}

						helpers.SendMessage(
							dmChannel.ID,
							content,
						)

						reminders.Reminders = append(reminders.Reminders[:idx], reminders.Reminders[idx+1:]...)
						changes = true
					}
				}

				if changes {
					setReminders(reminders.UserID, reminders)
				}
			}

			time.Sleep(10 * time.Second)
		}
	}()

	cache.GetLogger().WithField("module", "reminders").Info("Started reminder loop (10s)")
}

func (r *Reminders) Action(command string, content string, msg *discordgo.Message, session *discordgo.Session) {
	if !helpers.ModuleIsAllowed(msg.ChannelID, msg.ID, msg.Author.ID, helpers.ModulePermReminders) {
		return
	}

	switch command {
	case "rm", "remind", "remindme":
		session.ChannelTyping(msg.ChannelID)

		channel, err := helpers.GetChannel(msg.ChannelID)
		helpers.Relax(err)

		parts := strings.Fields(content)

		if len(parts) < 3 {
			helpers.SendMessage(msg.ChannelID, ":x: Please check if the format is correct")
			return
		}

		r, err := r.parser.Parse(content, time.Now())
		helpers.Relax(err)
		if r == nil {
			helpers.SendMessage(msg.ChannelID, ":x: Please check if the format is correct")
			return
		}

		reminders := getReminders(msg.Author.ID)
		reminders.Reminders = append(reminders.Reminders, DB_Reminder{
			Message:   strings.Replace(content, r.Text, "", 1),
			ChannelID: channel.ID,
			GuildID:   channel.GuildID,
			Timestamp: r.Time.Unix(),
		})
		setReminders(msg.Author.ID, reminders)

		helpers.SendMessage(msg.ChannelID, "Ok I'll remind you <:blobokhand:317032017164238848>")
		break

	case "rms", "reminders": // TODO: better interface
		session.ChannelTyping(msg.ChannelID)

		reminders := getReminders(msg.Author.ID)
		var embedFields []*discordgo.MessageEmbedField

		for _, reminder := range reminders.Reminders {
			ts := time.Unix(reminder.Timestamp, 0)
			channel := "?"
			guild := "?"

			chanRef, err := helpers.GetChannel(reminder.ChannelID)
			if err == nil {
				channel = chanRef.Name
			}

			guildRef, err := helpers.GetGuild(reminder.GuildID)
			if err == nil {
				guild = guildRef.Name
			}

			embedFields = append(embedFields, &discordgo.MessageEmbedField{
				Inline: false,
				Name:   reminder.Message,
				Value:  "At " + ts.String() + " in #" + channel + " of " + guild,
			})
		}

		if len(embedFields) == 0 {
			helpers.SendMessage(msg.ChannelID, helpers.GetText("plugins.reminders.empty"))
			return
		}

		helpers.SendEmbed(msg.ChannelID, &discordgo.MessageEmbed{
			Title:  "Pending reminders",
			Fields: embedFields,
			Color:  0x0FADED,
		})
		break
	}
}

func getReminders(uid string) DB_Reminders {
	var reminderBucket DB_Reminders
	listCursor, err := rethink.Table("reminders").Filter(
		rethink.Row.Field("userid").Eq(uid),
	).Run(helpers.GetDB())
	if err != nil {
		panic(err)
	}
	defer listCursor.Close()
	err = listCursor.One(&reminderBucket)

	// If user has no DB entries create an empty document
	if err == rethink.ErrEmptyResult {
		_, e := rethink.Table("reminders").Insert(DB_Reminders{
			UserID:    uid,
			Reminders: make([]DB_Reminder, 0),
		}).RunWrite(helpers.GetDB())

		// If the creation was successful read the document
		if e != nil {
			panic(e)
		} else {
			return getReminders(uid)
		}
	} else if err != nil {
		panic(err)
	}

	return reminderBucket
}

func setReminders(uid string, reminders DB_Reminders) {
	_, err := rethink.Table("reminders").Update(reminders).Run(helpers.GetDB())
	helpers.Relax(err)
}
<commit_msg>[reminders] adds custom messages for a few servers<commit_after>package plugins

import (
	"strings"
	"time"

	"github.com/Seklfreak/Robyul2/cache"
	"github.com/Seklfreak/Robyul2/helpers"
	"github.com/bwmarrin/discordgo"
	rethink "github.com/gorethink/gorethink"
	"github.com/olebedev/when"
	"github.com/olebedev/when/rules/common"
	"github.com/olebedev/when/rules/en"
)

type Reminders struct {
	parser *when.Parser
}

type DB_Reminders struct {
	Id        string        `gorethink:"id,omitempty"`
	UserID    string        `gorethink:"userid"`
	Reminders []DB_Reminder `gorethink:"reminders"`
}

type DB_Reminder struct {
	Message   string `gorethink:"message"`
	ChannelID string `gorethink:"channelID"`
	GuildID   string `gorethink:"guildID"`
	Timestamp int64  `gorethink:"timestamp"`
}

// maps guildid => custom message
var customReminderMsgMap map[string]string

func (r *Reminders) Commands() []string {
	return []string{
		"remind",
		"remindme",
		"rm",
		"reminders",
		"rms",
	}
}

func (r *Reminders) Init(session *discordgo.Session) {
	r.parser = when.New(nil)
	r.parser.Add(en.All...)
	r.parser.Add(common.All...)

	go func() {
		defer helpers.Recover()

		for {
			reminderBucket := make([]DB_Reminders, 0)
			cursor, err := rethink.Table("reminders").Run(helpers.GetDB())
			helpers.Relax(err)

			err = cursor.All(&reminderBucket)
			helpers.Relax(err)

			for _, reminders := range reminderBucket {
				changes := false

				// Downward loop for in-loop element removal
				for idx := len(reminders.Reminders) - 1; idx >= 0; idx-- {
					reminder := reminders.Reminders[idx]

					if reminder.Timestamp <= time.Now().Unix() {
						dmChannel, err := session.UserChannelCreate(reminders.UserID)
						helpers.Relax(err)

						content := ":alarm_clock: You wanted me to remind you about this:\n" + "```" + helpers.ZERO_WIDTH_SPACE + reminder.Message + "```"
						if reminder.Message == "" {
							content = ":alarm_clock: You wanted me to remind you about something, but you didn't tell me about what. <:blobthinking:317028940885524490>"
						}

						helpers.SendMessage(
							dmChannel.ID,
							content,
						)

						reminders.Reminders = append(reminders.Reminders[:idx], reminders.Reminders[idx+1:]...)
						changes = true
					}
				}

				if changes {
					setReminders(reminders.UserID, reminders)
				}
			}

			time.Sleep(10 * time.Second)
		}
	}()

	// Setup custom reminder messages.
	//  Could eventually be loaded from a db if we wanted guilds to set up there own. not an important enough plugin to need that atm
	customReminderMsgMap = map[string]string{
		"339227598544568340": "Ok I'll remind you <:nayoungok:424683077793611777>", // nayoung cord
		"403003926720413699": "Ok I'll remind you <:nayoungok:424683077793611777>", // snakeyesz dev
	}

	cache.GetLogger().WithField("module", "reminders").Info("Started reminder loop (10s)")
}

func (r *Reminders) Action(command string, content string, msg *discordgo.Message, session *discordgo.Session) {
	if !helpers.ModuleIsAllowed(msg.ChannelID, msg.ID, msg.Author.ID, helpers.ModulePermReminders) {
		return
	}

	switch command {
	case "rm", "remind", "remindme":
		session.ChannelTyping(msg.ChannelID)

		channel, err := helpers.GetChannel(msg.ChannelID)
		helpers.Relax(err)

		parts := strings.Fields(content)

		if len(parts) < 3 {
			helpers.SendMessage(msg.ChannelID, ":x: Please check if the format is correct")
			return
		}

		r, err := r.parser.Parse(content, time.Now())
		helpers.Relax(err)
		if r == nil {
			helpers.SendMessage(msg.ChannelID, ":x: Please check if the format is correct")
			return
		}

		reminders := getReminders(msg.Author.ID)
		reminders.Reminders = append(reminders.Reminders, DB_Reminder{
			Message:   strings.Replace(content, r.Text, "", 1),
			ChannelID: channel.ID,
			GuildID:   channel.GuildID,
			Timestamp: r.Time.Unix(),
		})
		setReminders(msg.Author.ID, reminders)

		// Check if guild has a custom message set
		if customMsg, ok := customReminderMsgMap[channel.GuildID]; ok {
			helpers.SendMessage(msg.ChannelID, customMsg)
		} else {
			helpers.SendMessage(msg.ChannelID, "Ok I'll remind you <:blobokhand:317032017164238848>")
		}
		break

	case "rms", "reminders": // TODO: better interface
		session.ChannelTyping(msg.ChannelID)

		reminders := getReminders(msg.Author.ID)
		var embedFields []*discordgo.MessageEmbedField

		for _, reminder := range reminders.Reminders {
			ts := time.Unix(reminder.Timestamp, 0)
			channel := "?"
			guild := "?"

			chanRef, err := helpers.GetChannel(reminder.ChannelID)
			if err == nil {
				channel = chanRef.Name
			}

			guildRef, err := helpers.GetGuild(reminder.GuildID)
			if err == nil {
				guild = guildRef.Name
			}

			embedFields = append(embedFields, &discordgo.MessageEmbedField{
				Inline: false,
				Name:   reminder.Message,
				Value:  "At " + ts.String() + " in #" + channel + " of " + guild,
			})
		}

		if len(embedFields) == 0 {
			helpers.SendMessage(msg.ChannelID, helpers.GetText("plugins.reminders.empty"))
			return
		}

		helpers.SendEmbed(msg.ChannelID, &discordgo.MessageEmbed{
			Title:  "Pending reminders",
			Fields: embedFields,
			Color:  0x0FADED,
		})
		break
	}
}

func getReminders(uid string) DB_Reminders {
	var reminderBucket DB_Reminders
	listCursor, err := rethink.Table("reminders").Filter(
		rethink.Row.Field("userid").Eq(uid),
	).Run(helpers.GetDB())
	if err != nil {
		panic(err)
	}
	defer listCursor.Close()
	err = listCursor.One(&reminderBucket)

	// If user has no DB entries create an empty document
	if err == rethink.ErrEmptyResult {
		_, e := rethink.Table("reminders").Insert(DB_Reminders{
			UserID:    uid,
			Reminders: make([]DB_Reminder, 0),
		}).RunWrite(helpers.GetDB())

		// If the creation was successful read the document
		if e != nil {
			panic(e)
		} else {
			return getReminders(uid)
		}
	} else if err != nil {
		panic(err)
	}

	return reminderBucket
}

func setReminders(uid string, reminders DB_Reminders) {
	_, err := rethink.Table("reminders").Update(reminders).Run(helpers.GetDB())
	helpers.Relax(err)
}
<|endoftext|>


<commit_before>package command

import (
	"encoding/json"
	"encoding/xml"
	"fmt"
	"io/ioutil"
	"jadebot/search"
	"math/rand"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/bwmarrin/discordgo"
	"github.com/oct2pus/bot/bot"
	"github.com/oct2pus/bot/embed"
)

// Avatar gets the first mentioned users Avatar.
func Avatar(bot bot.Bot,
	message *discordgo.MessageCreate,
	input []string) {
	var emb *discordgo.MessageEmbed

	// should be functionally the same if its empty or nil, if something broke
	// here assume it has to do with the nil/empty slice distinction
	if len(message.Mentions) == 0 {
		emb = embed.ImageEmbed("Avatar",
			"",
			message.Author.AvatarURL("1024"),
			"User: "+message.Author.Username+"#"+
				message.Message.Author.Discriminator,
			bot.Color)
	} else {
		emb = embed.ImageEmbed("Avatar", "",
			message.Message.Mentions[0].AvatarURL("1024"),
			message.Message.Mentions[0].Username+
				"#"+message.Message.Mentions[0].Discriminator,
			bot.Color)
	}

	embed.SendEmbededMessage(bot.Session, message.ChannelID, emb)
}

// Booru searches the MSPABooru and returns the result.
func Booru(bot bot.Bot,
	message *discordgo.MessageCreate,
	input []string) {
	url := "http://mspabooru.com//index.php?page=dapi&s=post&q=index"
	pid := "0"    // page id, aka what page you are on, a page is 25 images
	limit := "25" // how many images to get

	url += "&pid=" + pid + "&limit=" + limit

	if len(input) > 0 {
		url += "&tags="
		for _, ele := range input {
			url += ele + "+"
		}
		url = strings.TrimSuffix(url, "+")
	}

	// hardcoded 'do not use' tags, allowing these outside of nsfw chats is
	// against ToS, remove stuff at your own peril.
	url += "+-*cest+-gore+-erasure+-vomit+-bondage+-dubcon+-mind_control+" +
		"-undergarments+-rating:questionable+-rating:explicit+-3d+-deleteme"

	response, err := http.Get(url)
	if err != nil {
		embed.SendMessage(bot.Session, message.ChannelID,
			"please don't enter gibberish to try and break me :(")
		return
	}

	data, err := ioutil.ReadAll(response.Body)
	if err != nil {
		embed.SendMessage(bot.Session, message.ChannelID,
			"please stop trying to hurt me :(")
		return
	}

	var booruSearch search.Booru
	xml.Unmarshal(data, &booruSearch)

	if len(booruSearch.Posts) <= 0 {
		embed.SendMessage(bot.Session, message.ChannelID,
			"no posts found :(\n"+
				"if you were trying to find a ship, make sure your shipname"+
				" was entered correctly :o\n here is a list of all ship names"+
				" on the booru"+
				"\n<https://docs.google.com/spreadsheets/d/1IR5mmxNxgwAqH0_VEN"+
				"C0KOaTgSXE_azPts8qwqz9xMk>")
		return
	}

	// randomly pick a result
	rand.Seed(time.Now().UnixNano())

	randNum := rand.Intn(len(booruSearch.Posts))

	embed.SendEmbededMessage(bot.Session, message.ChannelID,
		embed.ImageEmbed("Source", booruSearch.Posts[randNum].Source,
			booruSearch.Posts[randNum].FileURL,
			"Warning: Some sources will be broken or NSFW", bot.Color))
}

// Credits accreditates users for their contributions.
func Credits(bot bot.Bot,
	message *discordgo.MessageCreate,
	input []string) {

	embed.SendEmbededMessage(bot.Session, message.ChannelID,
		embed.CreditsEmbed("Jadebot",
			"Chuchumi ( http://chuchumi.tumblr.com/ )",
			"sun gun#0373 ( http://taiyoooh.tumblr.com )",
			"Dzuk#1671 ( https://noct.zone/ )",
			"https://raw.githubusercontent.com/oct2pus/jadebot/origin/art/"+
				"jadebot.png",
			bot.Color))
}

// Discord returns my discord guild.
func Discord(bot bot.Bot,
	message *discordgo.MessageCreate,
	input []string) {

	embed.SendMessage(bot.Session, message.ChannelID,
		"https://discord.gg/PGVh2M8")
}

// Dog gets a picture from dog.ceo
func Dog(bot bot.Bot,
	message *discordgo.MessageCreate,
	input []string) {

	for i, ele := range input {
		input[i] = strings.ToLower(ele)
	}

	var url string

	switch len(input) {
	case 0:
		url = "https://dog.ceo/api/breeds/image/random"
	case 1:
		url = "https://dog.ceo/api/breed/" + input[0] + "/images/random"
	default:
		url = "https://dog.ceo/api/breed/" + input[1] + "/" + input[0] +
			"/images/random"
	}

	var doge search.Dog

	response, err := http.Get(url)
	if err != nil {
		embed.SendMessage(bot.Session, message.ChannelID,
			"something horrible went wrong when i was"+
				" searching for pups, try again")
		return
	}

	data, err := ioutil.ReadAll(response.Body)
	if err != nil {
		// TODO: Write an actual error message here
		embed.SendMessage(bot.Session, message.ChannelID,
			"something really, really bad happened")
		return
	}

	json.Unmarshal(data, &doge)

	if doge.Status != "success" {
		embed.SendMessage(bot.Session, message.ChannelID,
			"i could not find that breed :(\n"+
				"here is a list of breeds i can find!\n"+
				"<https://dog.ceo/dog-api/breeds-list>")
		return
	}

	embed.SendEmbededMessage(bot.Session, message.ChannelID,
		embed.ImageEmbed(
			"Source", doge.Message, doge.Message,
			strings.Join(input, " "), bot.Color))
}

// Doge is a joke command, it just calls Dog() with a Shiba preset.
func Doge(bot bot.Bot,
	message *discordgo.MessageCreate,
	input []string) {

	Dog(bot, message, []string{"shiba"})
}

// Help returns a list of commands.
func Help(bot bot.Bot, message *discordgo.MessageCreate, input []string) {

	embed.SendMessage(bot.Session, message.ChannelID,
		"my commands currently are\n-`avatar`\n-`mspa`, `booru`\n"+
			"-`dog`\n-`otp`, `ship`\n-`discord`\n-`wiki`\n-`invite`\n-`help`,"+
			" `commands`, `command`\n-`help`\n-`about`, `credits`")
}

// Invite returns a bot invite.
func Invite(bot bot.Bot,
	message *discordgo.MessageCreate,
	input []string) {

	embed.SendMessage(bot.Session, message.ChannelID,
		"<https://discordapp.com/oauth2/authorize?cli"+
			"ent_id=331204502277586945&scope=bot&permissions=379968>",
	)
}

// OTP returns a number, its very arbitrary but people like it.
func OTP(bot bot.Bot,
	message *discordgo.MessageCreate,
	input []string) {

	asString := strings.Join(input, " ")
	percent := ang(asString, 11)
	result := "I think " + asString + " has a **" + strconv.Itoa(int(percent)) +
		"/10** chance of being canon!"

	embed.SendMessage(bot.Session, message.ChannelID, result)
}

// Wiki gets article contents and displays them in an embed.
func Wiki(bot bot.Bot, message *discordgo.MessageCreate, input []string) {
	// gotcha with no input
	if len(input) <= 0 {
		embed.SendMessage(bot.Session, message.ChannelID,
			"what article do you want :?")
		return
	}
	minQuality := "25"
	inputs := strings.Join(input, " ")
	// perform list
	url := "https://mspaintadventures.fandom.com/api/v1/"
	listQuery := "Search/List?query=" + inputs + "&limit=1" +
		"&minArticleQuality=" + minQuality + "&batch=1&namespaces=0%2C14"
	listData, err := getJSON(url + listQuery)
	if err != nil {
		embed.SendMessage(bot.Session, message.ChannelID,
			"i cant seem to reach the wiki :(")
		// might also be unreadable
		return
	}
	var list search.WikiList
	json.Unmarshal(listData, &list)
	if len(list.Items) <= 0 {
		embed.SendMessage(bot.Session, message.ChannelID,
			"i cant find that article :o")
		return
	}
	// perform "simple" (big airquotes)
	id := strconv.Itoa(list.Items[0].ID)
	simpleQuery := "Articles/AsSimpleJson?id=" + id
	simpleData, err := getJSON(url + simpleQuery)
	if err != nil {
		embed.SendMessage(bot.Session, message.ChannelID,
			"i cant seem to reach the wiki :(")
		// might also be unreadable
		return
	}
	var simple search.WikiSimple
	json.Unmarshal(simpleData, &simple)
	if len(simple.Sections) <= 0 ||
		len(simple.Sections[0].Content) <= 0 ||
		simple.Sections[0].Content[0].Text == "" {
		// debug message
		fmt.Printf("\nsimple struct: %v\nlistQuery: %v\nsimpleQuery: %v",
			simple, url+listQuery, url+simpleQuery)
		embed.SendMessage(bot.Session, message.ChannelID,
			"i cant read that article :?")
		return
	}
	// present embed to user
	embed.SendEmbededMessage(bot.Session, message.ChannelID,
		embed.TextEmbed(list.Items[0].Title,
			"Summary",
			simple.Sections[0].Content[0].Text,
			list.Items[0].URL,
			list.Items[0].URL,
			bot.Color))
}

// ang stands for Arbitrary Number Generator
func ang(s string, m int32) int32 {
	runes := []rune(s)
	var res int32

	for _, ele := range runes {
		res += ele
	}

	return res % m
}

func getJSON(url string) ([]byte, error) {
	response, err := http.Get(url)
	if err != nil {
		return []byte{}, err
	}
	data, err := ioutil.ReadAll(response.Body)

	return data, err
}
<commit_msg>use bot.Name instead of hardcoded value<commit_after>package command

import (
	"encoding/json"
	"encoding/xml"
	"fmt"
	"io/ioutil"
	"jadebot/search"
	"math/rand"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/bwmarrin/discordgo"
	"github.com/oct2pus/bot/bot"
	"github.com/oct2pus/bot/embed"
)

// Avatar gets the first mentioned users Avatar.
func Avatar(bot bot.Bot,
	message *discordgo.MessageCreate,
	input []string) {
	var emb *discordgo.MessageEmbed

	// should be functionally the same if its empty or nil, if something broke
	// here assume it has to do with the nil/empty slice distinction
	if len(message.Mentions) == 0 {
		emb = embed.ImageEmbed("Avatar",
			"",
			message.Author.AvatarURL("1024"),
			"User: "+message.Author.Username+"#"+
				message.Message.Author.Discriminator,
			bot.Color)
	} else {
		emb = embed.ImageEmbed("Avatar", "",
			message.Message.Mentions[0].AvatarURL("1024"),
			message.Message.Mentions[0].Username+
				"#"+message.Message.Mentions[0].Discriminator,
			bot.Color)
	}

	embed.SendEmbededMessage(bot.Session, message.ChannelID, emb)
}

// Booru searches the MSPABooru and returns the result.
func Booru(bot bot.Bot,
	message *discordgo.MessageCreate,
	input []string) {
	url := "http://mspabooru.com//index.php?page=dapi&s=post&q=index"
	pid := "0"    // page id, aka what page you are on, a page is 25 images
	limit := "25" // how many images to get

	url += "&pid=" + pid + "&limit=" + limit

	if len(input) > 0 {
		url += "&tags="
		for _, ele := range input {
			url += ele + "+"
		}
		url = strings.TrimSuffix(url, "+")
	}

	// hardcoded 'do not use' tags, allowing these outside of nsfw chats is
	// against ToS, remove stuff at your own peril.
	url += "+-*cest+-gore+-erasure+-vomit+-bondage+-dubcon+-mind_control+" +
		"-undergarments+-rating:questionable+-rating:explicit+-3d+-deleteme"

	response, err := http.Get(url)
	if err != nil {
		embed.SendMessage(bot.Session, message.ChannelID,
			"please don't enter gibberish to try and break me :(")
		return
	}

	data, err := ioutil.ReadAll(response.Body)
	if err != nil {
		embed.SendMessage(bot.Session, message.ChannelID,
			"please stop trying to hurt me :(")
		return
	}

	var booruSearch search.Booru
	xml.Unmarshal(data, &booruSearch)

	if len(booruSearch.Posts) <= 0 {
		embed.SendMessage(bot.Session, message.ChannelID,
			"no posts found :(\n"+
				"if you were trying to find a ship, make sure your shipname"+
				" was entered correctly :o\n here is a list of all ship names"+
				" on the booru"+
				"\n<https://docs.google.com/spreadsheets/d/1IR5mmxNxgwAqH0_VEN"+
				"C0KOaTgSXE_azPts8qwqz9xMk>")
		return
	}

	// randomly pick a result
	rand.Seed(time.Now().UnixNano())

	randNum := rand.Intn(len(booruSearch.Posts))

	embed.SendEmbededMessage(bot.Session, message.ChannelID,
		embed.ImageEmbed("Source", booruSearch.Posts[randNum].Source,
			booruSearch.Posts[randNum].FileURL,
			"Warning: Some sources will be broken or NSFW", bot.Color))
}

// Credits accreditates users for their contributions.
func Credits(bot bot.Bot,
	message *discordgo.MessageCreate,
	input []string) {

	embed.SendEmbededMessage(bot.Session, message.ChannelID,
		embed.CreditsEmbed(bot.Name,
			"Chuchumi ( http://chuchumi.tumblr.com/ )",
			"sun gun#0373 ( http://taiyoooh.tumblr.com )",
			"Dzuk#1671 ( https://noct.zone/ )",
			"https://raw.githubusercontent.com/oct2pus/jadebot/origin/art/"+
				"jadebot.png",
			bot.Color))
}

// Discord returns my discord guild.
func Discord(bot bot.Bot,
	message *discordgo.MessageCreate,
	input []string) {

	embed.SendMessage(bot.Session, message.ChannelID,
		"https://discord.gg/PGVh2M8")
}

// Dog gets a picture from dog.ceo
func Dog(bot bot.Bot,
	message *discordgo.MessageCreate,
	input []string) {

	for i, ele := range input {
		input[i] = strings.ToLower(ele)
	}

	var url string

	switch len(input) {
	case 0:
		url = "https://dog.ceo/api/breeds/image/random"
	case 1:
		url = "https://dog.ceo/api/breed/" + input[0] + "/images/random"
	default:
		url = "https://dog.ceo/api/breed/" + input[1] + "/" + input[0] +
			"/images/random"
	}

	var doge search.Dog

	response, err := http.Get(url)
	if err != nil {
		embed.SendMessage(bot.Session, message.ChannelID,
			"something horrible went wrong when i was"+
				" searching for pups, try again")
		return
	}

	data, err := ioutil.ReadAll(response.Body)
	if err != nil {
		// TODO: Write an actual error message here
		embed.SendMessage(bot.Session, message.ChannelID,
			"something really, really bad happened")
		return
	}

	json.Unmarshal(data, &doge)

	if doge.Status != "success" {
		embed.SendMessage(bot.Session, message.ChannelID,
			"i could not find that breed :(\n"+
				"here is a list of breeds i can find!\n"+
				"<https://dog.ceo/dog-api/breeds-list>")
		return
	}

	embed.SendEmbededMessage(bot.Session, message.ChannelID,
		embed.ImageEmbed(
			"Source", doge.Message, doge.Message,
			strings.Join(input, " "), bot.Color))
}

// Doge is a joke command, it just calls Dog() with a Shiba preset.
func Doge(bot bot.Bot,
	message *discordgo.MessageCreate,
	input []string) {

	Dog(bot, message, []string{"shiba"})
}

// Help returns a list of commands.
func Help(bot bot.Bot, message *discordgo.MessageCreate, input []string) {

	embed.SendMessage(bot.Session, message.ChannelID,
		"my commands currently are\n-`avatar`\n-`mspa`, `booru`\n"+
			"-`dog`\n-`otp`, `ship`\n-`discord`\n-`wiki`\n-`invite`\n-`help`,"+
			" `commands`, `command`\n-`help`\n-`about`, `credits`")
}

// Invite returns a bot invite.
func Invite(bot bot.Bot,
	message *discordgo.MessageCreate,
	input []string) {

	embed.SendMessage(bot.Session, message.ChannelID,
		"<https://discordapp.com/oauth2/authorize?cli"+
			"ent_id=331204502277586945&scope=bot&permissions=379968>",
	)
}

// OTP returns a number, its very arbitrary but people like it.
func OTP(bot bot.Bot,
	message *discordgo.MessageCreate,
	input []string) {

	asString := strings.Join(input, " ")
	percent := ang(asString, 11)
	result := "I think " + asString + " has a **" + strconv.Itoa(int(percent)) +
		"/10** chance of being canon!"

	embed.SendMessage(bot.Session, message.ChannelID, result)
}

// Wiki gets article contents and displays them in an embed.
func Wiki(bot bot.Bot, message *discordgo.MessageCreate, input []string) {
	// gotcha with no input
	if len(input) <= 0 {
		embed.SendMessage(bot.Session, message.ChannelID,
			"what article do you want :?")
		return
	}
	minQuality := "25"
	inputs := strings.Join(input, " ")
	// perform list
	url := "https://mspaintadventures.fandom.com/api/v1/"
	listQuery := "Search/List?query=" + inputs + "&limit=1" +
		"&minArticleQuality=" + minQuality + "&batch=1&namespaces=0%2C14"
	listData, err := getJSON(url + listQuery)
	if err != nil {
		embed.SendMessage(bot.Session, message.ChannelID,
			"i cant seem to reach the wiki :(")
		// might also be unreadable
		return
	}
	var list search.WikiList
	json.Unmarshal(listData, &list)
	if len(list.Items) <= 0 {
		embed.SendMessage(bot.Session, message.ChannelID,
			"i cant find that article :o")
		return
	}
	// perform "simple" (big airquotes)
	id := strconv.Itoa(list.Items[0].ID)
	simpleQuery := "Articles/AsSimpleJson?id=" + id
	simpleData, err := getJSON(url + simpleQuery)
	if err != nil {
		embed.SendMessage(bot.Session, message.ChannelID,
			"i cant seem to reach the wiki :(")
		// might also be unreadable
		return
	}
	var simple search.WikiSimple
	json.Unmarshal(simpleData, &simple)
	if len(simple.Sections) <= 0 ||
		len(simple.Sections[0].Content) <= 0 ||
		simple.Sections[0].Content[0].Text == "" {
		// debug message
		fmt.Printf("\nsimple struct: %v\nlistQuery: %v\nsimpleQuery: %v",
			simple, url+listQuery, url+simpleQuery)
		embed.SendMessage(bot.Session, message.ChannelID,
			"i cant read that article :?")
		return
	}
	// present embed to user
	embed.SendEmbededMessage(bot.Session, message.ChannelID,
		embed.TextEmbed(list.Items[0].Title,
			"Summary",
			simple.Sections[0].Content[0].Text,
			list.Items[0].URL,
			list.Items[0].URL,
			bot.Color))
}

// ang stands for Arbitrary Number Generator
func ang(s string, m int32) int32 {
	runes := []rune(s)
	var res int32

	for _, ele := range runes {
		res += ele
	}

	return res % m
}

func getJSON(url string) ([]byte, error) {
	response, err := http.Get(url)
	if err != nil {
		return []byte{}, err
	}
	data, err := ioutil.ReadAll(response.Body)

	return data, err
}
<|endoftext|>


<commit_before>package pachyderm

import (
	"bytes"
	"fmt"
	"log"
	"os"
	"path"
	"strings"
	"testing"

	"golang.org/x/net/context"

	"github.com/pachyderm/pachyderm/src/pfs"
	"github.com/pachyderm/pachyderm/src/pfs/pfsutil"
	"github.com/pachyderm/pachyderm/src/pkg/require"
	"github.com/pachyderm/pachyderm/src/pkg/uuid"
	"github.com/pachyderm/pachyderm/src/pps"
	"github.com/pachyderm/pachyderm/src/pps/ppsutil"
	"google.golang.org/grpc"
)

func TestJob(t *testing.T) {
	dataRepo := uniqueString("TestJob.data")
	pfsClient := getPfsClient(t)
	require.NoError(t, pfsutil.CreateRepo(pfsClient, dataRepo))
	commit, err := pfsutil.StartCommit(pfsClient, dataRepo, "")
	require.NoError(t, err)
	_, err = pfsutil.PutFile(pfsClient, dataRepo, commit.Id, "file", 0, strings.NewReader("foo"))
	require.NoError(t, err)
	require.NoError(t, pfsutil.FinishCommit(pfsClient, dataRepo, commit.Id))
	ppsClient := getPpsClient(t)
	job, err := ppsutil.CreateJob(
		ppsClient,
		"",
		[]string{"cp", path.Join("/pfs", dataRepo, "file"), "/pfs/out/file"},
		"",
		1,
		[]*pfs.Commit{commit},
		"",
	)
	require.NoError(t, err)
	listCommitRequest := &pfs.ListCommitRequest{
		Repo:       []*pfs.Repo{pps.JobRepo(job)},
		CommitType: pfs.CommitType_COMMIT_TYPE_READ,
		Block:      true,
	}
	listCommitResponse, err := pfsClient.ListCommit(
		context.Background(),
		listCommitRequest,
	)
	require.NoError(t, err)
	outCommits := listCommitResponse.CommitInfo
	require.Equal(t, 1, len(outCommits))
	var buffer bytes.Buffer
	require.NoError(t, pfsutil.GetFile(pfsClient, pps.JobRepo(job).Name, outCommits[0].Commit.Id, "file", 0, 0, nil, &buffer))
	require.Equal(t, "foo", buffer.String())
}

func TestGrep(t *testing.T) {
	t.Skip()
	dataRepo := uniqueString("pachyderm.TestGrep.data")
	pfsClient := getPfsClient(t)
	require.NoError(t, pfsutil.CreateRepo(pfsClient, dataRepo))
	commit, err := pfsutil.StartCommit(pfsClient, dataRepo, "")
	require.NoError(t, err)
	for i := 0; i < 100; i++ {
		_, err = pfsutil.PutFile(pfsClient, dataRepo, commit.Id, fmt.Sprintf("file%d", i), 0, strings.NewReader("foo\nbar\nfizz\nbuzz\n"))
		require.NoError(t, err)
	}
	require.NoError(t, pfsutil.FinishCommit(pfsClient, dataRepo, commit.Id))
	ppsClient := getPpsClient(t)
	_, err = ppsutil.CreateJob(
		ppsClient,
		"",
		[]string{"sh"},
		fmt.Sprintf("grep foo /pfs/%s/* >/pfs/out/foo", dataRepo),
		1,
		[]*pfs.Commit{commit},
		"",
	)
	require.NoError(t, err)
}

func TestPipeline(t *testing.T) {
	t.Skip()
	pfsClient := getPfsClient(t)
	ppsClient := getPpsClient(t)
	// create repos
	dataRepo := uniqueString("TestPipeline.data")
	require.NoError(t, pfsutil.CreateRepo(pfsClient, dataRepo))
	// create pipeline
	pipelineName := uniqueString("pipeline")
	outRepo := pps.PipelineRepo(ppsutil.NewPipeline(pipelineName))
	require.NoError(t, ppsutil.CreatePipeline(
		ppsClient,
		pipelineName,
		"",
		[]string{"cp", path.Join("/pfs", dataRepo, "file"), "/pfs/out/file"},
		"",
		1,
		[]*pfs.Repo{&pfs.Repo{Name: dataRepo}},
	))
	// Do first commit to repo
	log.Printf("Do first commit.")
	commit1, err := pfsutil.StartCommit(pfsClient, dataRepo, "")
	require.NoError(t, err)
	_, err = pfsutil.PutFile(pfsClient, dataRepo, commit1.Id, "file", 0, strings.NewReader("foo"))
	require.NoError(t, err)
	require.NoError(t, pfsutil.FinishCommit(pfsClient, dataRepo, commit1.Id))
	listCommitRequest := &pfs.ListCommitRequest{
		Repo:       []*pfs.Repo{outRepo},
		CommitType: pfs.CommitType_COMMIT_TYPE_READ,
		Block:      true,
	}
	listCommitResponse, err := pfsClient.ListCommit(
		context.Background(),
		listCommitRequest,
	)
	require.NoError(t, err)
	outCommits := listCommitResponse.CommitInfo
	require.Equal(t, 1, len(outCommits))
	var buffer bytes.Buffer
	log.Printf("First commit: %+v", outCommits[0])
	require.NoError(t, pfsutil.GetFile(pfsClient, outRepo.Name, outCommits[0].Commit.Id, "file", 0, 0, nil, &buffer))
	require.Equal(t, "foo", buffer.String())
	// Do second commit to repo
	log.Printf("Do second commit.")
	commit2, err := pfsutil.StartCommit(pfsClient, dataRepo, commit1.Id)
	require.NoError(t, err)
	_, err = pfsutil.PutFile(pfsClient, dataRepo, commit2.Id, "file", 0, strings.NewReader("bar"))
	require.NoError(t, err)
	require.NoError(t, pfsutil.FinishCommit(pfsClient, dataRepo, commit2.Id))
	listCommitRequest = &pfs.ListCommitRequest{
		Repo:       []*pfs.Repo{outRepo},
		FromCommit: []*pfs.Commit{outCommits[0].Commit},
		CommitType: pfs.CommitType_COMMIT_TYPE_READ,
		Block:      true,
	}
	listCommitResponse, err = pfsClient.ListCommit(
		context.Background(),
		listCommitRequest,
	)
	require.NoError(t, err)
	outCommits = listCommitResponse.CommitInfo
	require.Equal(t, 1, len(outCommits))
	buffer = bytes.Buffer{}
	log.Printf("Second commit: %+v", outCommits[0])
	require.NoError(t, pfsutil.GetFile(pfsClient, outRepo.Name, outCommits[0].Commit.Id, "file", 0, 0, nil, &buffer))
	require.Equal(t, "foobar", buffer.String())
}

func getPfsClient(t *testing.T) pfs.APIClient {
	pfsdAddr := os.Getenv("PFSD_PORT_650_TCP_ADDR")
	if pfsdAddr == "" {
		t.Error("PFSD_PORT_650_TCP_ADDR not set")
	}
	clientConn, err := grpc.Dial(fmt.Sprintf("%s:650", pfsdAddr), grpc.WithInsecure())
	require.NoError(t, err)
	return pfs.NewAPIClient(clientConn)
}

func getPpsClient(t *testing.T) pps.APIClient {
	ppsdAddr := os.Getenv("PPSD_PORT_651_TCP_ADDR")
	if ppsdAddr == "" {
		t.Error("PPSD_PORT_651_TCP_ADDR not set")
	}
	clientConn, err := grpc.Dial(fmt.Sprintf("%s:651", ppsdAddr), grpc.WithInsecure())
	require.NoError(t, err)
	return pps.NewAPIClient(clientConn)
}

func uniqueString(prefix string) string {
	return prefix + "." + uuid.NewWithoutDashes()[0:12]
}
<commit_msg>Wait for job to finish not commit.<commit_after>package pachyderm

import (
	"bytes"
	"fmt"
	"log"
	"os"
	"path"
	"strings"
	"testing"

	"golang.org/x/net/context"

	"github.com/pachyderm/pachyderm/src/pfs"
	"github.com/pachyderm/pachyderm/src/pfs/pfsutil"
	"github.com/pachyderm/pachyderm/src/pkg/require"
	"github.com/pachyderm/pachyderm/src/pkg/uuid"
	"github.com/pachyderm/pachyderm/src/pps"
	"github.com/pachyderm/pachyderm/src/pps/ppsutil"
	"google.golang.org/grpc"
)

func TestJob(t *testing.T) {
	dataRepo := uniqueString("TestJob.data")
	pfsClient := getPfsClient(t)
	require.NoError(t, pfsutil.CreateRepo(pfsClient, dataRepo))
	commit, err := pfsutil.StartCommit(pfsClient, dataRepo, "")
	require.NoError(t, err)
	_, err = pfsutil.PutFile(pfsClient, dataRepo, commit.Id, "file", 0, strings.NewReader("foo"))
	require.NoError(t, err)
	require.NoError(t, pfsutil.FinishCommit(pfsClient, dataRepo, commit.Id))
	ppsClient := getPpsClient(t)
	job, err := ppsutil.CreateJob(
		ppsClient,
		"",
		[]string{"cp", path.Join("/pfs", dataRepo, "file"), "/pfs/out/file"},
		"",
		1,
		[]*pfs.Commit{commit},
		"",
	)
	require.NoError(t, err)
	inspectJobRequest := &pps.InspectJobRequest{
		Job:         job,
		BlockOutput: true,
		BlockState:  true,
	}
	jobInfo, err := ppsClient.InspectJob(context.Background(), inspectJobRequest)
	require.NoError(t, err)
	var buffer bytes.Buffer
	require.NoError(t, pfsutil.GetFile(pfsClient, jobInfo.OutputCommit.Repo.Name, jobInfo.OutputCommit.Id, "file", 0, 0, nil, &buffer))
	require.Equal(t, "foo", buffer.String())
}

func TestGrep(t *testing.T) {
	t.Skip()
	dataRepo := uniqueString("pachyderm.TestGrep.data")
	pfsClient := getPfsClient(t)
	require.NoError(t, pfsutil.CreateRepo(pfsClient, dataRepo))
	commit, err := pfsutil.StartCommit(pfsClient, dataRepo, "")
	require.NoError(t, err)
	for i := 0; i < 100; i++ {
		_, err = pfsutil.PutFile(pfsClient, dataRepo, commit.Id, fmt.Sprintf("file%d", i), 0, strings.NewReader("foo\nbar\nfizz\nbuzz\n"))
		require.NoError(t, err)
	}
	require.NoError(t, pfsutil.FinishCommit(pfsClient, dataRepo, commit.Id))
	ppsClient := getPpsClient(t)
	_, err = ppsutil.CreateJob(
		ppsClient,
		"",
		[]string{"sh"},
		fmt.Sprintf("grep foo /pfs/%s/* >/pfs/out/foo", dataRepo),
		1,
		[]*pfs.Commit{commit},
		"",
	)
	require.NoError(t, err)
}

func TestPipeline(t *testing.T) {
	t.Skip()
	pfsClient := getPfsClient(t)
	ppsClient := getPpsClient(t)
	// create repos
	dataRepo := uniqueString("TestPipeline.data")
	require.NoError(t, pfsutil.CreateRepo(pfsClient, dataRepo))
	// create pipeline
	pipelineName := uniqueString("pipeline")
	outRepo := pps.PipelineRepo(ppsutil.NewPipeline(pipelineName))
	require.NoError(t, ppsutil.CreatePipeline(
		ppsClient,
		pipelineName,
		"",
		[]string{"cp", path.Join("/pfs", dataRepo, "file"), "/pfs/out/file"},
		"",
		1,
		[]*pfs.Repo{&pfs.Repo{Name: dataRepo}},
	))
	// Do first commit to repo
	log.Printf("Do first commit.")
	commit1, err := pfsutil.StartCommit(pfsClient, dataRepo, "")
	require.NoError(t, err)
	_, err = pfsutil.PutFile(pfsClient, dataRepo, commit1.Id, "file", 0, strings.NewReader("foo"))
	require.NoError(t, err)
	require.NoError(t, pfsutil.FinishCommit(pfsClient, dataRepo, commit1.Id))
	listCommitRequest := &pfs.ListCommitRequest{
		Repo:       []*pfs.Repo{outRepo},
		CommitType: pfs.CommitType_COMMIT_TYPE_READ,
		Block:      true,
	}
	listCommitResponse, err := pfsClient.ListCommit(
		context.Background(),
		listCommitRequest,
	)
	require.NoError(t, err)
	outCommits := listCommitResponse.CommitInfo
	require.Equal(t, 1, len(outCommits))
	var buffer bytes.Buffer
	log.Printf("First commit: %+v", outCommits[0])
	require.NoError(t, pfsutil.GetFile(pfsClient, outRepo.Name, outCommits[0].Commit.Id, "file", 0, 0, nil, &buffer))
	require.Equal(t, "foo", buffer.String())
	// Do second commit to repo
	log.Printf("Do second commit.")
	commit2, err := pfsutil.StartCommit(pfsClient, dataRepo, commit1.Id)
	require.NoError(t, err)
	_, err = pfsutil.PutFile(pfsClient, dataRepo, commit2.Id, "file", 0, strings.NewReader("bar"))
	require.NoError(t, err)
	require.NoError(t, pfsutil.FinishCommit(pfsClient, dataRepo, commit2.Id))
	listCommitRequest = &pfs.ListCommitRequest{
		Repo:       []*pfs.Repo{outRepo},
		FromCommit: []*pfs.Commit{outCommits[0].Commit},
		CommitType: pfs.CommitType_COMMIT_TYPE_READ,
		Block:      true,
	}
	listCommitResponse, err = pfsClient.ListCommit(
		context.Background(),
		listCommitRequest,
	)
	require.NoError(t, err)
	outCommits = listCommitResponse.CommitInfo
	require.Equal(t, 1, len(outCommits))
	buffer = bytes.Buffer{}
	log.Printf("Second commit: %+v", outCommits[0])
	require.NoError(t, pfsutil.GetFile(pfsClient, outRepo.Name, outCommits[0].Commit.Id, "file", 0, 0, nil, &buffer))
	require.Equal(t, "foobar", buffer.String())
}

func getPfsClient(t *testing.T) pfs.APIClient {
	pfsdAddr := os.Getenv("PFSD_PORT_650_TCP_ADDR")
	if pfsdAddr == "" {
		t.Error("PFSD_PORT_650_TCP_ADDR not set")
	}
	clientConn, err := grpc.Dial(fmt.Sprintf("%s:650", pfsdAddr), grpc.WithInsecure())
	require.NoError(t, err)
	return pfs.NewAPIClient(clientConn)
}

func getPpsClient(t *testing.T) pps.APIClient {
	ppsdAddr := os.Getenv("PPSD_PORT_651_TCP_ADDR")
	if ppsdAddr == "" {
		t.Error("PPSD_PORT_651_TCP_ADDR not set")
	}
	clientConn, err := grpc.Dial(fmt.Sprintf("%s:651", ppsdAddr), grpc.WithInsecure())
	require.NoError(t, err)
	return pps.NewAPIClient(clientConn)
}

func uniqueString(prefix string) string {
	return prefix + "." + uuid.NewWithoutDashes()[0:12]
}
<|endoftext|>


<commit_before>package triggers

type Donators struct{}

func (d *Donators) Triggers() []string {
    return []string{
        "donators",
        "donations",
        "donate",
        "supporters",
        "support",
        "patreon",
        "patreons",
        "credits",
    }
}

func (d *Donators) Response(trigger string, content string) string {
    return "<:robyulblush:327206930437373952> **These awesome people support me:**\nKakkela 💕\nSunny 💓\nsomicidal minaiac 💞\nOokami 🖤\nKeldra 💗\nTN 💝\nseulguille 💘\nSlenn 💜\nFugu ❣\nWoori 💞\nhikari 💙\nAshton 💖\nKay 💝\njamie 💓\nHomeboywill 💘\nRimbol 💕\nGenisphere 💖\nThank you so much!\n_You want to be in this list? <https://www.patreon.com/sekl>!_"
}
<commit_msg>[donators] adds ekgus!<commit_after>package triggers

type Donators struct{}

func (d *Donators) Triggers() []string {
    return []string{
        "donators",
        "donations",
        "donate",
        "supporters",
        "support",
        "patreon",
        "patreons",
        "credits",
    }
}

func (d *Donators) Response(trigger string, content string) string {
    return "<:robyulblush:327206930437373952> **These awesome people support me:**\nKakkela 💕\nSunny 💓\nsomicidal minaiac 💞\nOokami 🖤\nKeldra 💗\nTN 💝\nseulguille 💘\nSlenn 💜\nFugu ❣\nWoori 💞\nhikari 💙\nAshton 💖\nKay 💝\njamie 💓\nHomeboywill 💘\nRimbol 💕\nGenisphere 💖\nekgus 💗\nThank you so much!\n_You want to be in this list? <https://www.patreon.com/sekl>!_"
}
<|endoftext|>


<commit_before>package station

import (
	"bytes"
	"html/template"
	"net/http"
	"log"

	"MediaServer/internal/song"
	"github.com/gorilla/websocket"
	"MediaServer/websrv/msg"
	"MediaServer/urlgen"
)

type Playlist []song.Info

type Data struct {
	Name     string `json:"name"`
	Url      string `json:"url"`
	Playlist `json:"playlist"`
	connections []Connection
	broadcast chan msg.Data
}

func New(w http.ResponseWriter, r *http.Request) Data {
	// TODO read station name from request json
	// Also maybe make .Name == .Url
	ret := Data{
		Name: "Station",
		Url: urlgen.Gen(),
		broadcast: make(chan msg.Data, 10),
	}

	ret.Add(w, r)

	// TODO launch a goroutine for handling station-wide messages

	return ret
}

func (d *Data) Add(w http.ResponseWriter, r *http.Request) {
	up := websocket.Upgrader{}
	ws, err := up.Upgrade(w, r, nil)
	if err != nil {
		log.Println("WebSocket upgrade error:", err)
		return
	}

	d.connections = append(d.connections, Connection{
		Name: "placeholder", // JSON encoded in the request
		In: make(chan msg.Data, 10),
		Out: d.broadcast,
	})

	go d.connections[len(d.connections) - 1].Work(ws)
}

type Page struct {
	template *template.Template
	length int
}

func Load(data []byte) Page {
	return Page{
		template: template.Must(template.New("stationPage").Parse(string(data))),
		length: len(data),
	}
}

func (p Page) Generate(data Data) ([]byte, error) {
	ret := make([]byte, 0, p.length)
	err := p.template.Execute(bytes.NewBuffer(ret), data)
	return ret, err
}
<commit_msg>type Page is not specific to stations. removed.<commit_after>package station

import (
	"log"
	"net/http"

	"MediaServer/internal/song"
	"MediaServer/websrv/msg"
	"github.com/gorilla/websocket"
)

type Data struct {
	Name        string      `json:"name"`
	Playlist    []song.Info `json:"playlist"`
	connections []Connection
	broadcast   chan msg.Data
}

func New(w http.ResponseWriter, r *http.Request) Data {
	// TODO read station name from request json
	// Also maybe make .Name == .Url
	ret := Data{
		Name:      "Station",
		broadcast: make(chan msg.Data, 10),
	}

	// TODO launch a goroutine for handling station-wide messages

	return ret
}

func (d *Data) Add(w http.ResponseWriter, r *http.Request) {
	up := websocket.Upgrader{}
	ws, err := up.Upgrade(w, r, nil)
	if err != nil {
		log.Println("WebSocket upgrade error:", err)
		return
	}

	d.connections = append(d.connections, Connection{
		Name: "placeholder", // TODO json encoded in the request
		In:   make(chan msg.Data, 10),
		Out:  d.broadcast,
	})

	go d.connections[len(d.connections)-1].Work(ws)
}
<|endoftext|>


<commit_before>package commands

import (
	"errors"

	"github.com/concourse/fly/rc"
)

type LogoutCommand struct {
	All bool `short:"a" long:"all" description:"Logout of all targets"`
}

func (command *LogoutCommand) Execute(args []string) error {

	if Fly.Target != "" && !command.All {
		err = rc.DeleteTarget(Fly.Target)
	} else if Fly.Target == "" && command.All {
		flyYAML, err := rc.LoadTargets()
		if err != nil {
			return err
		}

		for targetName, _ := range flyYAML.Targets {
			if err = rc.DeleteTarget(targetName); err != nil {
				return err
			}
		}

	} else {
		return errors.New("must specify either a target (--target/-t) or the all flag (--all/-a)")
	}
}
<commit_msg>Changed error message.<commit_after>package commands

import (
	"errors"

	"github.com/concourse/fly/rc"
)

type LogoutCommand struct {
	All bool `short:"a" long:"all" description:"Logout of all targets"`
}

func (command *LogoutCommand) Execute(args []string) error {

	if Fly.Target != "" && !command.All {
		err = rc.DeleteTarget(Fly.Target)
	} else if Fly.Target == "" && command.All {
		flyYAML, err := rc.LoadTargets()
		if err != nil {
			return err
		}

		for targetName, _ := range flyYAML.Targets {
			if err = rc.DeleteTarget(targetName); err != nil {
				return err
			}
		}

	} else {
		return errors.New("must specify either --target or --all")
	}
}
<|endoftext|>


<commit_before>package commands

import (
	"fmt"
	"strings"

	"github.com/github/hub/cmd"
	"github.com/github/hub/git"
	"github.com/github/hub/ui"
	"github.com/kballard/go-shellquote"
)

type Runner struct {
	commands map[string]*Command
}

func NewRunner() *Runner {
	return &Runner{
		commands: make(map[string]*Command),
	}
}

func (r *Runner) All() map[string]*Command {
	return r.commands
}

func (r *Runner) Use(command *Command, aliases ...string) {
	r.commands[command.Name()] = command
	if len(aliases) > 0 {
		r.commands[aliases[0]] = command
	}
}

func (r *Runner) Lookup(name string) *Command {
	return r.commands[name]
}

func (r *Runner) Execute(cliArgs []string) error {
	args := NewArgs(cliArgs[1:])
	args.ProgramPath = cliArgs[0]
	forceFail := false

	if args.Command == "" && len(args.GlobalFlags) == 0 {
		args.Command = "help"
		forceFail = true
	}

	cmdName := args.Command
	if strings.Contains(cmdName, "=") {
		cmdName = strings.SplitN(cmdName, "=", 2)[0]
	}

	git.GlobalFlags = args.GlobalFlags // preserve git global flags
	if !isBuiltInHubCommand(cmdName) {
		expandAlias(args)
		cmdName = args.Command
	}

	cmd := r.Lookup(cmdName)
	if cmd != nil && cmd.Runnable() {
		err := callRunnableCommand(cmd, args)
		if err == nil && forceFail {
			err = fmt.Errorf("")
		}
		return err
	}

	gitArgs := []string{}
	if args.Command != "" {
		gitArgs = append(gitArgs, args.Command)
	}
	gitArgs = append(gitArgs, args.Params...)

	return git.Run(gitArgs...)
}

func callRunnableCommand(cmd *Command, args *Args) error {
	err := cmd.Call(args)
	if err != nil {
		return err
	}

	cmds := args.Commands()
	if args.Noop {
		printCommands(cmds)
	} else if err = executeCommands(cmds, len(args.Callbacks) == 0); err != nil {
		return err
	}

	for _, fn := range args.Callbacks {
		if err = fn(); err != nil {
			return err
		}
	}

	return nil
}

func printCommands(cmds []*cmd.Cmd) {
	for _, c := range cmds {
		ui.Println(c)
	}
}

func executeCommands(cmds []*cmd.Cmd, execFinal bool) error {
	for i, c := range cmds {
		var err error
		// Run with `Exec` for the last command in chain
		if execFinal && i == len(cmds)-1 {
			err = c.Run()
		} else {
			err = c.Spawn()
		}

		if err != nil {
			return err
		}
	}

	return nil
}

func expandAlias(args *Args) {
	cmd := args.Command
	expandedCmd, err := git.Alias(cmd)

	if err == nil && expandedCmd != "" && !git.IsBuiltInGitCommand(cmd) {
		words, e := splitAliasCmd(expandedCmd)
		if e == nil {
			args.Command = words[0]
			args.PrependParams(words[1:]...)
		}
	}
}

func isBuiltInHubCommand(command string) bool {
	for hubCommand, _ := range CmdRunner.All() {
		if hubCommand == command {
			return true
		}
	}
	return false
}

func splitAliasCmd(cmd string) ([]string, error) {
	if cmd == "" {
		return nil, fmt.Errorf("alias can't be empty")
	}

	if strings.HasPrefix(cmd, "!") {
		return nil, fmt.Errorf("alias starting with ! can't be split")
	}

	words, err := shellquote.Split(cmd)
	if err != nil {
		return nil, err
	}

	return words, nil
}
<commit_msg>Fix error message on hub --paginate<commit_after>package commands

import (
	"fmt"
	"strings"

	"github.com/github/hub/cmd"
	"github.com/github/hub/git"
	"github.com/github/hub/ui"
	"github.com/kballard/go-shellquote"
)

type Runner struct {
	commands map[string]*Command
}

func NewRunner() *Runner {
	return &Runner{
		commands: make(map[string]*Command),
	}
}

func (r *Runner) All() map[string]*Command {
	return r.commands
}

func (r *Runner) Use(command *Command, aliases ...string) {
	r.commands[command.Name()] = command
	if len(aliases) > 0 {
		r.commands[aliases[0]] = command
	}
}

func (r *Runner) Lookup(name string) *Command {
	return r.commands[name]
}

func (r *Runner) Execute(cliArgs []string) error {
	args := NewArgs(cliArgs[1:])
	args.ProgramPath = cliArgs[0]
	forceFail := false

	if args.Command == "" && len(args.GlobalFlags) == 0 {
		args.Command = "help"
		forceFail = true
	}

	cmdName := args.Command
	if strings.Contains(cmdName, "=") {
		cmdName = strings.SplitN(cmdName, "=", 2)[0]
	}

	git.GlobalFlags = args.GlobalFlags // preserve git global flags
	if !isBuiltInHubCommand(cmdName) {
		expandAlias(args)
		cmdName = args.Command
	}

	cmd := r.Lookup(cmdName)
	if cmd != nil && cmd.Runnable() {
		err := callRunnableCommand(cmd, args)
		if err == nil && forceFail {
			err = fmt.Errorf("")
		}
		return err
	}

	gitArgs := []string{}
	if args.Command != "" {
		gitArgs = append(gitArgs, args.Command)
	}
	gitArgs = append(gitArgs, args.Params...)

	return git.Run(gitArgs...)
}

func callRunnableCommand(cmd *Command, args *Args) error {
	err := cmd.Call(args)
	if err != nil {
		return err
	}

	cmds := args.Commands()
	if args.Noop {
		printCommands(cmds)
	} else if err = executeCommands(cmds, len(args.Callbacks) == 0); err != nil {
		return err
	}

	for _, fn := range args.Callbacks {
		if err = fn(); err != nil {
			return err
		}
	}

	return nil
}

func printCommands(cmds []*cmd.Cmd) {
	for _, c := range cmds {
		ui.Println(c)
	}
}

func executeCommands(cmds []*cmd.Cmd, execFinal bool) error {
	for i, c := range cmds {
		var err error
		// Run with `Exec` for the last command in chain
		if execFinal && i == len(cmds)-1 {
			err = c.Run()
		} else {
			err = c.Spawn()
		}

		if err != nil {
			return err
		}
	}

	return nil
}

func expandAlias(args *Args) {
	cmd := args.Command
	if cmd == "" {
		return
	}
	expandedCmd, err := git.Alias(cmd)

	if err == nil && expandedCmd != "" && !git.IsBuiltInGitCommand(cmd) {
		words, e := splitAliasCmd(expandedCmd)
		if e == nil {
			args.Command = words[0]
			args.PrependParams(words[1:]...)
		}
	}
}

func isBuiltInHubCommand(command string) bool {
	for hubCommand, _ := range CmdRunner.All() {
		if hubCommand == command {
			return true
		}
	}
	return false
}

func splitAliasCmd(cmd string) ([]string, error) {
	if cmd == "" {
		return nil, fmt.Errorf("alias can't be empty")
	}

	if strings.HasPrefix(cmd, "!") {
		return nil, fmt.Errorf("alias starting with ! can't be split")
	}

	words, err := shellquote.Split(cmd)
	if err != nil {
		return nil, err
	}

	return words, nil
}
<|endoftext|>


<commit_before>// Copyright © 2014 Steve Francia <spf@spf13.com>.
//
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.

package commands

import (
	"fmt"
	"html"
	"html/template"
	"log"
	"net/http"
	"os"
	"strings"

	"labix.org/v2/mgo/bson"

	"github.com/GeertJohan/go.rice"
	"github.com/gin-gonic/gin"
	"github.com/pilu/fresh/runner/runnerutils"
	"github.com/spf13/cast"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

const pLimit = 10

var serverCmd = &cobra.Command{
	Use:   "server",
	Short: "Server for feeds",
	Long:  `Dagobah will serve all feeds listed in the config file.`,
	Run:   serverRun,
}

func init() {
	serverCmd.Flags().Int("port", 1138, "Port to run Dagobah server on")
	viper.BindPFlag("port", serverCmd.Flags().Lookup("port"))
}

func serverRun(cmd *cobra.Command, args []string) {
	Server()
}

func Server() {
	port := viper.GetString("port")

	r := gin.Default()

	if os.Getenv("DEV") != "" {
		r.Use(RunnerMiddleware())
	}

	templates := loadTemplates("home.html", "channels.html", "items.html", "main.html")
	r.HTMLTemplates = templates

	r.GET("/ping", func(c *gin.Context) {
		c.String(200, "pong")
	})

	r.GET("/", homeRoute)
	r.GET("/post/*key", postRoute)
	//r.GET("/search/*query", searchRoute)
	r.GET("/static/*filepath", staticServe)
	r.GET("/channel/*key", channelRoute)
	r.Run(":" + port)
}

func staticServe(c *gin.Context) {
	static, err := rice.FindBox("static")
	if err != nil {
		log.Fatal(err)
	}
	original := c.Req.URL.Path
	c.Req.URL.Path = c.Params.ByName("filepath")
	http.FileServer(static.HTTPBox()).ServeHTTP(c.Writer, c.Req)
	c.Req.URL.Path = original
}

func RunnerMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		if runnerutils.HasErrors() {
			runnerutils.RenderError(c.Writer)
			c.Abort(500)
		}
	}
}

func loadTemplates(list ...string) *template.Template {
	templateBox, err := rice.FindBox("templates")
	if err != nil {
		log.Fatal(err)
	}

	templates := template.New("")

	for _, x := range list {
		templateString, err := templateBox.String(x)
		if err != nil {
			log.Fatal(err)
		}

		// get file contents as string
		_, err = templates.New(x).Parse(templateString)
		if err != nil {
			log.Fatal(err)
		}
	}

	funcMap := template.FuncMap{
		"html":  ProperHtml,
		"title": func(a string) string { return strings.Title(a) },
	}

	templates.Funcs(funcMap)

	return templates
}

func ProperHtml(text string) template.HTML {
	if strings.Contains(text, "content:encoded>") || strings.Contains(text, "content/:encoded>") {
		text = html.UnescapeString(text)
	}
	return template.HTML(html.UnescapeString(template.HTMLEscapeString(text)))
}

func postRoute(c *gin.Context) {

	key := c.Params.ByName("key")

	if len(key) < 2 {
		c.String(404, "Invalid Channel")
	}

	key = key[1:]

	// TODO Need to find posts before and after this... not just the first ones
	var posts []Itm
	results := Items().Find(bson.M{}).Sort("-date").Limit(pLimit)
	results.All(&posts)

	var post Itm
	Items().Find(bson.M{"key": key}).Sort("-date").One(&post)

	channels := AllChannels()

	obj := gin.H{"title": post.Title, "post": post, "items": posts, "channels": channels}

	if strings.ToLower(c.Req.Header.Get("X-Requested-With")) == "xmlhttprequest" {
		c.HTML(200, "main.html", obj)
	} else {
		c.HTML(200, "home.html", obj)
	}
}

func Offset(c *gin.Context) int {
	curPage := cast.ToInt(c.Req.FormValue("p")) - 1
	if curPage < 1 {
		return 0
	}
	return pLimit * curPage
}

func homeRoute(c *gin.Context) {

	channels := AllChannels()

	var posts []Itm
	results := Items().Find(bson.M{}).Skip(Offset(c)).Sort("-date").Limit(pLimit)
	results.All(&posts)

	if len(posts) == 0 {
		c.HTML(404, "home.html", gin.H{"message": "No Articles"})
		return
	}

	obj := gin.H{"title": "Go Rules", "items": posts, "post": posts[0], "channels": channels}
	c.HTML(200, "home.html", obj)
}

func channelRoute(c *gin.Context) {
	key := c.Params.ByName("key")
	if len(key) < 2 {

		c.HTML(404, "home.html", gin.H{"message": "Channel Not Found"})
		return
	}

	key = key[1:]

	var posts []Itm
	results := Items().Find(bson.M{"channelkey": key}).Skip(Offset(c)).Sort("-date").Limit(pLimit)
	results.All(&posts)

	if len(posts) == 0 {
		c.HTML(404, "home.html", gin.H{"message": "No Articles"})
		return
	}

	channels := AllChannels()

	var currentChannel Chnl
	err := Channels().Find(bson.M{"key": key}).One(&currentChannel)
	if err != nil {
		if string(err.Error()) == "not found" {
			c.HTML(404, "home.html", gin.H{"message": "Channel Not Found"})
			return
		} else {
			fmt.Println(err)
		}
	}

	obj := gin.H{"title": currentChannel.Title, "header": currentChannel.Title, "post": posts[0], "items": posts, "channels": channels}

	if strings.ToLower(c.Req.Header.Get("X-Requested-With")) == "xmlhttprequest" {
		c.HTML(200, "channels.html", obj)
	} else {
		c.HTML(200, "home.html", obj)
	}
}
<commit_msg>Better feedback when server is running.<commit_after>// Copyright © 2014 Steve Francia <spf@spf13.com>.
//
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.

package commands

import (
	"fmt"
	"html"
	"html/template"
	"log"
	"net/http"
	"os"
	"strings"

	"labix.org/v2/mgo/bson"

	"github.com/GeertJohan/go.rice"
	"github.com/gin-gonic/gin"
	"github.com/pilu/fresh/runner/runnerutils"
	"github.com/spf13/cast"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

const pLimit = 10

var serverCmd = &cobra.Command{
	Use:   "server",
	Short: "Server for feeds",
	Long:  `Dagobah will serve all feeds listed in the config file.`,
	Run:   serverRun,
}

func init() {
	serverCmd.Flags().Int("port", 1138, "Port to run Dagobah server on")
	viper.BindPFlag("port", serverCmd.Flags().Lookup("port"))
}

func serverRun(cmd *cobra.Command, args []string) {
	Server()
}

func Server() {
	port := viper.GetString("port")

	r := gin.Default()

	if os.Getenv("DEV") != "" {
		r.Use(RunnerMiddleware())
	}

	templates := loadTemplates("home.html", "channels.html", "items.html", "main.html")
	r.HTMLTemplates = templates

	r.GET("/ping", func(c *gin.Context) {
		c.String(200, "pong")
	})

	r.GET("/", homeRoute)
	r.GET("/post/*key", postRoute)
	//r.GET("/search/*query", searchRoute)
	r.GET("/static/*filepath", staticServe)
	r.GET("/channel/*key", channelRoute)
	fmt.Println("Running on port:", port)
	r.Run(":" + port)
}

func staticServe(c *gin.Context) {
	static, err := rice.FindBox("static")
	if err != nil {
		log.Fatal(err)
	}
	original := c.Req.URL.Path
	c.Req.URL.Path = c.Params.ByName("filepath")
	http.FileServer(static.HTTPBox()).ServeHTTP(c.Writer, c.Req)
	c.Req.URL.Path = original
}

func RunnerMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		if runnerutils.HasErrors() {
			runnerutils.RenderError(c.Writer)
			c.Abort(500)
		}
	}
}

func loadTemplates(list ...string) *template.Template {
	templateBox, err := rice.FindBox("templates")
	if err != nil {
		log.Fatal(err)
	}

	templates := template.New("")

	for _, x := range list {
		templateString, err := templateBox.String(x)
		if err != nil {
			log.Fatal(err)
		}

		// get file contents as string
		_, err = templates.New(x).Parse(templateString)
		if err != nil {
			log.Fatal(err)
		}
	}

	funcMap := template.FuncMap{
		"html":  ProperHtml,
		"title": func(a string) string { return strings.Title(a) },
	}

	templates.Funcs(funcMap)

	return templates
}

func ProperHtml(text string) template.HTML {
	if strings.Contains(text, "content:encoded>") || strings.Contains(text, "content/:encoded>") {
		text = html.UnescapeString(text)
	}
	return template.HTML(html.UnescapeString(template.HTMLEscapeString(text)))
}

func postRoute(c *gin.Context) {

	key := c.Params.ByName("key")

	if len(key) < 2 {
		c.String(404, "Invalid Channel")
	}

	key = key[1:]

	// TODO Need to find posts before and after this... not just the first ones
	var posts []Itm
	results := Items().Find(bson.M{}).Sort("-date").Limit(pLimit)
	results.All(&posts)

	var post Itm
	Items().Find(bson.M{"key": key}).Sort("-date").One(&post)

	channels := AllChannels()

	obj := gin.H{"title": post.Title, "post": post, "items": posts, "channels": channels}

	if strings.ToLower(c.Req.Header.Get("X-Requested-With")) == "xmlhttprequest" {
		c.HTML(200, "main.html", obj)
	} else {
		c.HTML(200, "home.html", obj)
	}
}

func Offset(c *gin.Context) int {
	curPage := cast.ToInt(c.Req.FormValue("p")) - 1
	if curPage < 1 {
		return 0
	}
	return pLimit * curPage
}

func homeRoute(c *gin.Context) {

	channels := AllChannels()

	var posts []Itm
	results := Items().Find(bson.M{}).Skip(Offset(c)).Sort("-date").Limit(pLimit)
	results.All(&posts)

	if len(posts) == 0 {
		c.HTML(404, "home.html", gin.H{"message": "No Articles"})
		return
	}

	obj := gin.H{"title": "Go Rules", "items": posts, "post": posts[0], "channels": channels}
	c.HTML(200, "home.html", obj)
}

func channelRoute(c *gin.Context) {
	key := c.Params.ByName("key")
	if len(key) < 2 {

		c.HTML(404, "home.html", gin.H{"message": "Channel Not Found"})
		return
	}

	key = key[1:]

	var posts []Itm
	results := Items().Find(bson.M{"channelkey": key}).Skip(Offset(c)).Sort("-date").Limit(pLimit)
	results.All(&posts)

	if len(posts) == 0 {
		c.HTML(404, "home.html", gin.H{"message": "No Articles"})
		return
	}

	channels := AllChannels()

	var currentChannel Chnl
	err := Channels().Find(bson.M{"key": key}).One(&currentChannel)
	if err != nil {
		if string(err.Error()) == "not found" {
			c.HTML(404, "home.html", gin.H{"message": "Channel Not Found"})
			return
		} else {
			fmt.Println(err)
		}
	}

	obj := gin.H{"title": currentChannel.Title, "header": currentChannel.Title, "post": posts[0], "items": posts, "channels": channels}

	if strings.ToLower(c.Req.Header.Get("X-Requested-With")) == "xmlhttprequest" {
		c.HTML(200, "channels.html", obj)
	} else {
		c.HTML(200, "home.html", obj)
	}
}
<|endoftext|>


<commit_before>package commands

import (
	"archive/zip"
	"fmt"
	updater "github.com/inconshreveable/go-update"
	"github.com/jingweno/gh/github"
	"github.com/jingweno/gh/utils"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"runtime"
	"strings"
)

var cmdUpdate = &Command{
	Run:   update,
	Usage: "update",
	Short: "Update gh",
	Long: `Update gh with the latest version.

Examples:
  git update
`,
}

func update(cmd *Command, args *Args) {
	err := doUpdate()
	utils.Check(err)
	os.Exit(0)
}

func doUpdate() (err error) {
	client := github.NewClient(github.GitHubHost)
	releases, err := client.Releases(github.NewProject("jingweno", "gh", github.GitHubHost))
	if err != nil {
		err = fmt.Errorf("Error fetching releases: %s", err)
		return
	}

	latestRelease := releases[0]
	tagName := latestRelease.TagName
	version := strings.TrimPrefix(tagName, "v")

	fmt.Printf("Updating gh to release %s...\n", tagName)
	downloadURL := fmt.Sprintf("https://github.com/jingweno/gh/releases/download/%s/gh_%s-snapshot_%s_%s.zip", tagName, version, runtime.GOOS, runtime.GOARCH)
	path, err := downloadFile(downloadURL)
	if err != nil {
		err = fmt.Errorf("Can't download update %s to %s", downloadURL, path)
		return
	}

	exec, err := unzipExecutable(path)
	if err != nil {
		err = fmt.Errorf("Can't unzip gh executable: %s", err)
		return
	}

	err, _ = updater.FromFile(exec)
	if err == nil {
		fmt.Println("Done!")
	}

	return
}

func unzipExecutable(path string) (exec string, err error) {
	rc, err := zip.OpenReader(path)
	if err != nil {
		err = fmt.Errorf("Can't open zip file %s: %s", path, err)
		return
	}
	defer rc.Close()

	dir := filepath.Dir(path)
	for _, file := range rc.File {
		frc, e := file.Open()
		if e != nil {
			err = fmt.Errorf("Can't open zip entry %s when reading: %s", file.Name, err)
			return
		}
		defer frc.Close()

		if !strings.HasPrefix(file.Name, "gh") {
			continue
		}

		dest := filepath.Join(dir, filepath.Base(file.Name))
		f, e := os.Create(dest)
		if e != nil {
			err = e
			return
		}
		defer f.Close()

		copied, e := io.Copy(f, frc)
		if e != nil {
			err = e
			return
		}

		if uint32(copied) != file.UncompressedSize {
			err = fmt.Errorf("Zip entry %s is corrupted", file.Name)
			return
		}

		exec = f.Name()

		break
	}

	if exec == "" {
		err = fmt.Errorf("No gh executable is found")
	}

	return
}

func downloadFile(url string) (path string, err error) {
	dir, err := ioutil.TempDir("", "gh-update")
	if err != nil {
		return
	}

	file, err := os.Create(filepath.Join(dir, filepath.Base(url)))
	if err != nil {
		return
	}
	defer file.Close()

	resp, err := http.Get(url)
	defer resp.Body.Close()

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

	path = file.Name()

	return
}
<commit_msg>Wording the message<commit_after>package commands

import (
	"archive/zip"
	"fmt"
	updater "github.com/inconshreveable/go-update"
	"github.com/jingweno/gh/github"
	"github.com/jingweno/gh/utils"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"runtime"
	"strings"
)

var cmdUpdate = &Command{
	Run:   update,
	Usage: "update",
	Short: "Update gh",
	Long: `Update gh to the latest version.

Examples:
  git update
`,
}

func update(cmd *Command, args *Args) {
	err := doUpdate()
	utils.Check(err)
	os.Exit(0)
}

func doUpdate() (err error) {
	client := github.NewClient(github.GitHubHost)
	releases, err := client.Releases(github.NewProject("jingweno", "gh", github.GitHubHost))
	if err != nil {
		err = fmt.Errorf("Error fetching releases: %s", err)
		return
	}

	latestRelease := releases[0]
	tagName := latestRelease.TagName
	version := strings.TrimPrefix(tagName, "v")

	fmt.Printf("Updating gh to release %s...\n", tagName)
	downloadURL := fmt.Sprintf("https://github.com/jingweno/gh/releases/download/%s/gh_%s-snapshot_%s_%s.zip", tagName, version, runtime.GOOS, runtime.GOARCH)
	path, err := downloadFile(downloadURL)
	if err != nil {
		err = fmt.Errorf("Can't download update from %s to %s", downloadURL, path)
		return
	}

	exec, err := unzipExecutable(path)
	if err != nil {
		err = fmt.Errorf("Can't unzip gh executable: %s", err)
		return
	}

	err, _ = updater.FromFile(exec)
	if err == nil {
		fmt.Println("Done!")
	}

	return
}

func unzipExecutable(path string) (exec string, err error) {
	rc, err := zip.OpenReader(path)
	if err != nil {
		err = fmt.Errorf("Can't open zip file %s: %s", path, err)
		return
	}
	defer rc.Close()

	dir := filepath.Dir(path)
	for _, file := range rc.File {
		frc, e := file.Open()
		if e != nil {
			err = fmt.Errorf("Can't open zip entry %s when reading: %s", file.Name, err)
			return
		}
		defer frc.Close()

		if !strings.HasPrefix(file.Name, "gh") {
			continue
		}

		dest := filepath.Join(dir, filepath.Base(file.Name))
		f, e := os.Create(dest)
		if e != nil {
			err = e
			return
		}
		defer f.Close()

		copied, e := io.Copy(f, frc)
		if e != nil {
			err = e
			return
		}

		if uint32(copied) != file.UncompressedSize {
			err = fmt.Errorf("Zip entry %s is corrupted", file.Name)
			return
		}

		exec = f.Name()

		break
	}

	if exec == "" {
		err = fmt.Errorf("No gh executable is found")
	}

	return
}

func downloadFile(url string) (path string, err error) {
	dir, err := ioutil.TempDir("", "gh-update")
	if err != nil {
		return
	}

	file, err := os.Create(filepath.Join(dir, filepath.Base(url)))
	if err != nil {
		return
	}
	defer file.Close()

	resp, err := http.Get(url)
	defer resp.Body.Close()

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

	path = file.Name()

	return
}
<|endoftext|>


<commit_before>package nameserver

import (
	"fmt"
	"github.com/miekg/dns"
	. "github.com/zettio/weave/common"
	"io"
	"log"
	"net"
	"net/http"
	"github.com/gorilla/mux"
)

func httpErrorAndLog(level *log.Logger, w http.ResponseWriter, msg string,
	status int, logmsg string, logargs ...interface{}) {
	http.Error(w, msg, status)
	level.Printf("[http] "+logmsg, logargs...)
}

func ListenHttp(domain string, db Zone, port int) {

	muxRouter := mux.NewRouter()

	muxRouter.Methods("GET").Path("/status").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		io.WriteString(w, "ok")
	})

	muxRouter.Methods("PUT").Path("/name/{identifier:.+}/{ip:.+}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		reqError := func(msg string, logmsg string, logargs ...interface{}) {
			httpErrorAndLog(Warning, w, msg, http.StatusBadRequest, logmsg, logargs...)
		}

		vars := mux.Vars(r)
		ident := vars["identifier"]
		ipStr := vars["ip"]
		name := r.FormValue("fqdn")

		if name == "" {
			reqError("Invalid FQDN", "Invalid FQDN in request: %s, %s", r.URL, r.Form)
			return
		}

		ip := net.ParseIP(ipStr)
		if ip == nil {
			reqError("Invalid IP", "Invalid IP in request: %s", ipStr)
			return
		}

		if dns.IsSubDomain(domain, name) {
			Info.Printf("[http] Adding %s -> %s", name, ipStr)
			if err := db.AddRecord(ident, name, ip); err != nil {
				if _, ok := err.(DuplicateError); !ok {
					httpErrorAndLog(
						Error, w, "Internal error", http.StatusInternalServerError,
						"Unexpected error from DB: %s", err)
					return
				} // oh, I already know this. whatever.
			}
		} else {
			Info.Printf("[http] Ignoring name %s, not in %s", name, domain)
		}
	})

	muxRouter.Methods("DELETE").Path("/name/{identifier:.+}/{ip:.+}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		reqError := func(msg string, logmsg string, logargs ...interface{}) {
			httpErrorAndLog(Warning, w, msg, http.StatusBadRequest, logmsg, logargs...)
		}

		vars := mux.Vars(r)
		ident := vars["identifier"]
		ipStr := vars["ip"]

		ip := net.ParseIP(ipStr)
		if ip == nil {
			reqError("Invalid IP in request", "Invalid IP in request: %s", ipStr)
			return
		}
		Info.Printf("[http] Deleting %s (%s)", ident, ipStr)
		if err := db.DeleteRecord(ident, ip); err != nil {
			if _, ok := err.(LookupError); !ok {
				httpErrorAndLog(
					Error, w, "Internal error", http.StatusInternalServerError,
					"Unexpected error from DB: %s", err)
				return
			}
		}
	})

	http.Handle("/", muxRouter)

	address := fmt.Sprintf(":%d", port)
	if err := http.ListenAndServe(address, nil); err != nil {
		Error.Fatal("[http] Unable to create http listener: ", err)
	}
}
<commit_msg>Rename URL parameter<commit_after>package nameserver

import (
	"fmt"
	"github.com/miekg/dns"
	. "github.com/zettio/weave/common"
	"io"
	"log"
	"net"
	"net/http"
	"github.com/gorilla/mux"
)

func httpErrorAndLog(level *log.Logger, w http.ResponseWriter, msg string,
	status int, logmsg string, logargs ...interface{}) {
	http.Error(w, msg, status)
	level.Printf("[http] "+logmsg, logargs...)
}

func ListenHttp(domain string, db Zone, port int) {

	muxRouter := mux.NewRouter()

	muxRouter.Methods("GET").Path("/status").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		io.WriteString(w, "ok")
	})

	muxRouter.Methods("PUT").Path("/name/{id:.+}/{ip:.+}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		reqError := func(msg string, logmsg string, logargs ...interface{}) {
			httpErrorAndLog(Warning, w, msg, http.StatusBadRequest, logmsg, logargs...)
		}

		vars := mux.Vars(r)
		idStr := vars["id"]
		ipStr := vars["ip"]
		name := r.FormValue("fqdn")

		if name == "" {
			reqError("Invalid FQDN", "Invalid FQDN in request: %s, %s", r.URL, r.Form)
			return
		}

		ip := net.ParseIP(ipStr)
		if ip == nil {
			reqError("Invalid IP", "Invalid IP in request: %s", ipStr)
			return
		}

		if dns.IsSubDomain(domain, name) {
			Info.Printf("[http] Adding %s -> %s", name, ipStr)
			if err := db.AddRecord(idStr, name, ip); err != nil {
				if _, ok := err.(DuplicateError); !ok {
					httpErrorAndLog(
						Error, w, "Internal error", http.StatusInternalServerError,
						"Unexpected error from DB: %s", err)
					return
				} // oh, I already know this. whatever.
			}
		} else {
			Info.Printf("[http] Ignoring name %s, not in %s", name, domain)
		}
	})

	muxRouter.Methods("DELETE").Path("/name/{id:.+}/{ip:.+}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		reqError := func(msg string, logmsg string, logargs ...interface{}) {
			httpErrorAndLog(Warning, w, msg, http.StatusBadRequest, logmsg, logargs...)
		}

		vars := mux.Vars(r)
		idStr := vars["id"]
		ipStr := vars["ip"]

		ip := net.ParseIP(ipStr)
		if ip == nil {
			reqError("Invalid IP in request", "Invalid IP in request: %s", ipStr)
			return
		}
		Info.Printf("[http] Deleting %s (%s)", idStr, ipStr)
		if err := db.DeleteRecord(idStr, ip); err != nil {
			if _, ok := err.(LookupError); !ok {
				httpErrorAndLog(
					Error, w, "Internal error", http.StatusInternalServerError,
					"Unexpected error from DB: %s", err)
				return
			}
		}
	})

	http.Handle("/", muxRouter)

	address := fmt.Sprintf(":%d", port)
	if err := http.ListenAndServe(address, nil); err != nil {
		Error.Fatal("[http] Unable to create http listener: ", err)
	}
}
<|endoftext|>


<commit_before>package geo_skeleton

import (
	"fmt"
	"log"
	"math"
	"os"
	"time"
)

import (
	"github.com/paulmach/go.geojson"
	"github.com/sjsafranek/DiffDB/diff_store"
	"github.com/sjsafranek/SkeletonDB"
)

var (
	COMMIT_LOG_FILE string = "geo_skeleton_commit.log"
)

const (
	DEFAULT_PRECISION        int = 8
)

// https://gist.github.com/DavidVaini/10308388
func Round(f float64) float64 {
	return math.Floor(f + .5)
}

func RoundToPrecision(f float64, places int) float64 {
	shift := math.Pow(10, float64(places))
	return Round(f*shift) / shift
}

func NewGeoSkeletonDB(db_file string) Database {
	var geoDb = Database{
		File:  db_file,
		Table: "GeoJsonDatasources",
		DB:    skeleton.Database{File: db_file}}
	geoDb.Init()
	return geoDb
}

// Database strust for application.
type Database struct {
	Table            string
	File             string
	commit_log_queue chan string
	Precision        int
	DB              skeleton.Database
}

func (self Database) Init() {

	self.DB.Init()

	// start commit log
	go self.StartCommitLog()

	// default table
	if "" == self.Table {
		self.Table = "GeoJSONLayers"
	}

	conn := self.DB.Connect()
	defer conn.Close()

	err := self.DB.CreateTable(conn, self.Table)
	if nil != err {
		panic(err)
	}

	err = self.DB.CreateTable(conn, "GeoTimeseriesData")
	if nil != err {
		panic(err)
	}
}

func (self Database) getPrecision() int {
	if 1 > self.Precision {
		return DEFAULT_PRECISION
	}
	return self.Precision
}

// Starts Database commit log
func (self *Database) StartCommitLog() {
	self.commit_log_queue = make(chan string, 10000)
	// open file to write database commit log
	COMMIT_LOG, err := os.OpenFile(COMMIT_LOG_FILE, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
	if err != nil {
		log.Println(err)
	}
	defer COMMIT_LOG.Close()
	// read from chan and write to file
	for {
		if len(self.commit_log_queue) > 0 {
			line := <-self.commit_log_queue
			if _, err := COMMIT_LOG.WriteString(line + "\n"); err != nil {
				panic(err)
			}
		} else {
			time.Sleep(1000 * time.Millisecond)
		}
	}
}

// CommitQueueLength returns length of database commit_log_queue
// @returns int
func (self *Database) CommitQueueLength() int {
	return len(self.commit_log_queue)
}

// NewLayer creates new datasource layer
// @returns string - datasource id
// @returns Error
// TODO: RENAME TO NewDatasource
func (self *Database) NewLayer() (string, error) {
	// create geojson
	datasource_id, _ := NewUUID()
	geojs := geojson.NewFeatureCollection()
	// convert to bytes
	value, err := geojs.MarshalJSON()
	if err != nil {
		return "", nil
	}
	self.commit_log_queue <- `{"method": "create_datasource", "data": { "datasource": "` + datasource_id + `", "layer": ` + string(value) + `}}`
	// Insert layer into database
	err = self.DB.Insert(self.Table, datasource_id, value)
	if err != nil {
		panic(err)
	}
	return datasource_id, err
}

// InsertLayer inserts layer into database
// @param datasource {string}
// @param geojs {Geojson}
// @returns Error
func (self *Database) InsertLayer(datasource_id string, geojs *geojson.FeatureCollection) error {
	// convert to bytes
	value, err := geojs.MarshalJSON()
	if err != nil {
		return err
	}

	err = self.DB.Insert(self.Table, datasource_id, value)
	if err != nil {
		panic(err)
	}

	go self.UpdateTimeseriesDatasource(datasource_id, value)

	return err
}

// GetLayer returns layer from database
// @param datasource {string}
// @returns Geojson
// @returns Error
func (self *Database) GetLayer(datasource_id string) (*geojson.FeatureCollection, error) {
	val, err := self.DB.Select(self.Table, datasource_id)
	if err != nil {
		return nil, err
	}
	if "" == string(val) {
		return nil, fmt.Errorf("Datasource not found")
	}
	// Read to struct
	geojs, err := geojson.UnmarshalFeatureCollection(val)
	if err != nil {
		return geojs, err
	}
	return geojs, nil
}

// GetLayers returns all datasource_ids from database
// @param datasource {string}
// @returns Geojson
// @returns Error
func (self *Database) GetLayers() ([]string, error) {
	val, err := self.DB.SelectAll(self.Table)
	if err != nil {
		return nil, err
	}
	return val, nil
}

// DeleteLayer deletes layer from database
// @param datasource {string}
// @returns Error
func (self *Database) DeleteLayer(datasource_id string) error {
	self.commit_log_queue <- `{"method": "delete_layer", "data": { "datasource": "` + datasource_id + `"}}`
	err := self.DB.Remove(datasource_id, self.Table)
	return err
}

func (self *Database) normalizeGeometry(feat *geojson.Feature) (*geojson.Feature, error) {
	// FIT TO 7 - 8 DECIMAL PLACES OF PRECISION
	if nil == feat.Geometry {
		return nil, fmt.Errorf("Feature has no geometry!")
	}

	precision := self.getPrecision()

	switch feat.Geometry.Type {

	case geojson.GeometryPoint:
		// []float64
		feat.Geometry.Point[0] = RoundToPrecision(feat.Geometry.Point[0], precision)
		feat.Geometry.Point[1] = RoundToPrecision(feat.Geometry.Point[1], precision)

	case geojson.GeometryMultiPoint:
		// [][]float64
		for i := range feat.Geometry.MultiPoint {
			for j := range feat.Geometry.MultiPoint[i] {
				feat.Geometry.MultiPoint[i][j] = RoundToPrecision(feat.Geometry.MultiPoint[i][j], precision)
			}
		}

	case geojson.GeometryLineString:
		// [][]float64
		for i := range feat.Geometry.LineString {
			for j := range feat.Geometry.LineString[i] {
				feat.Geometry.LineString[i][j] = RoundToPrecision(feat.Geometry.LineString[i][j], precision)
			}
		}

	case geojson.GeometryMultiLineString:
		// [][][]float64
		for i := range feat.Geometry.MultiLineString {
			for j := range feat.Geometry.MultiLineString[i] {
				for k := range feat.Geometry.MultiLineString[i][j] {
					feat.Geometry.MultiLineString[i][j][k] = RoundToPrecision(feat.Geometry.MultiLineString[i][j][k], precision)
				}
			}
		}

	case geojson.GeometryPolygon:
		// [][][]float64
		for i := range feat.Geometry.Polygon {
			for j := range feat.Geometry.Polygon[i] {
				for k := range feat.Geometry.Polygon[i][j] {
					feat.Geometry.Polygon[i][j][k] = RoundToPrecision(feat.Geometry.Polygon[i][j][k], precision)
				}
			}
		}

	case geojson.GeometryMultiPolygon:
		// [][][][]float64
		for i := range feat.Geometry.MultiPolygon {
			log.Printf("%v\n", feat.Geometry.MultiPolygon[i])
		}

	}

	/*
		//case GeometryCollection:
		//	geo.Geometries = g.Geometries
		//	// log.Printf("%v\n", feat.Geometry.Geometries)

	*/
	return feat, nil
}

func (self *Database) normalizeProperties(feat *geojson.Feature, featCollection *geojson.FeatureCollection) *geojson.Feature {

	// check if nil map
	if nil == feat.Properties {
		feat.Properties = make(map[string]interface{})
	}

	if 0 == len(featCollection.Features) {
		return feat
	}
	// Standardize properties for new feature
	for j := range featCollection.Features[0].Properties {
		if _, ok := feat.Properties[j]; !ok {
			feat.Properties[j] = ""
		}
	}

	// Standardize properties for existing features
	for i := range featCollection.Features {
		for j := range feat.Properties {
			if _, ok := featCollection.Features[i].Properties[j]; !ok {
				featCollection.Features[i].Properties[j] = ""
			}
		}
	}

	return feat
}

// InsertFeature adds feature to layer. Updates layer in Database
// @param datasource {string}
// @param feat {Geojson Feature}
// @returns Error
func (self *Database) InsertFeature(datasource_id string, feat *geojson.Feature) error {

	if nil == feat {
		return fmt.Errorf("feature value is <nil>!")
	}

	// Get layer from database
	featCollection, err := self.GetLayer(datasource_id)
	if err != nil {
		return err
	}

	// Apply required columns
	now := time.Now().Unix()

	// check if nil map
	if nil == feat.Properties {
		feat.Properties = make(map[string]interface{})
	}

	feat.Properties["is_active"] = true
	feat.Properties["is_deleted"] = false
	feat.Properties["date_created"] = now
	feat.Properties["date_modified"] = now
	feat.Properties["geo_id"] = fmt.Sprintf("%v", now)

	feat, err = self.normalizeGeometry(feat)
	if nil != err {
		return err
	}

	feat = self.normalizeProperties(feat, featCollection)

	// Write to commit log
	value, err := feat.MarshalJSON()
	if err != nil {
		return err
	}
	self.commit_log_queue <- `{"method": "insert_feature", "data": { "datasource": "` + datasource_id + `", "feature": ` + string(value) + `}}`

	// Add new feature to layer
	featCollection.AddFeature(feat)

	// insert layer
	err = self.InsertLayer(datasource_id, featCollection)
	if err != nil {
		panic(err)
	}
	return err
}

// EditFeature Edits feature in layer. Updates layer in Database
// @param datasource {string}
// @param geo_id {string}
// @param feat {Geojson Feature}
// @returns Error
func (self *Database) EditFeature(datasource_id string, geo_id string, feat *geojson.Feature) error {

	// Get layer from database
	featCollection, err := self.GetLayer(datasource_id)
	if err != nil {
		return err
	}

	feature_exists := false

	for i := range featCollection.Features {
		if geo_id == fmt.Sprintf("%v", featCollection.Features[i].Properties["geo_id"]) {

			now := time.Now().Unix()
			feat.Properties["date_modified"] = now

			feat, err = self.normalizeGeometry(feat)
			if nil != err {
				return err
			}

			feat = self.normalizeProperties(feat, featCollection)
			featCollection.Features[i] = feat
			// Write to commit log
			value, err := feat.MarshalJSON()
			if err != nil {
				return err
			}
			self.commit_log_queue <- `{"method": "edit_feature", "data": { "datasource": "` + datasource_id + `", "geo_id": "` + geo_id + `", "feature": ` + string(value) + `}}`
			feature_exists = true
		}
	}

	if !feature_exists {
		return fmt.Errorf("feature not found!")
	}

	// insert layer
	err = self.InsertLayer(datasource_id, featCollection)
	if err != nil {
		panic(err)
	}
	return err
}

func (self *Database) InsertTimeseriesDatasource(datasource_id string, enc []byte) (error) {
	err := self.DB.Insert("GeoTimeseriesData", datasource_id, enc)
	return err
}

func (self *Database) SelectTimeseriesDatasource(datasource_id string) (diff_store.DiffStore, error) {
	var ddata diff_store.DiffStore
	data, err := self.DB.Select("GeoTimeseriesData", datasource_id)
	ddata.Decode(data)
	return ddata, err
}

func (self *Database) UpdateTimeseriesDatasource(datasource_id string, value []byte) error {

	update_value := string(value)

	//var ddata diff_store.DiffStore
	ddata, err := self.SelectTimeseriesDatasource(datasource_id)
	if nil != err {
		if err.Error() == "Not found" {
			// create new diffstore if key not found in database
			ddata = diff_store.NewDiffStore(datasource_id)
		} else {
			panic(err)
		}
	}

	// update diffstore
	ddata.Update(update_value)

	// save to database
	enc, err := ddata.Encode()
	if nil != err {
		panic(err)
	}

	ddata.Name = datasource_id
	err = self.InsertTimeseriesDatasource(string(ddata.Name), enc)

	return err
}<commit_msg>changed select timeseries datasource<commit_after>package geo_skeleton

import (
	"fmt"
	"log"
	"math"
	"os"
	"time"
)

import (
	"github.com/paulmach/go.geojson"
	"github.com/sjsafranek/DiffDB/diff_store"
	"github.com/sjsafranek/SkeletonDB"
)

var (
	COMMIT_LOG_FILE string = "geo_skeleton_commit.log"
)

const (
	DEFAULT_PRECISION        int = 8
)

// https://gist.github.com/DavidVaini/10308388
func Round(f float64) float64 {
	return math.Floor(f + .5)
}

func RoundToPrecision(f float64, places int) float64 {
	shift := math.Pow(10, float64(places))
	return Round(f*shift) / shift
}

func NewGeoSkeletonDB(db_file string) Database {
	var geoDb = Database{
		File:  db_file,
		Table: "GeoJsonDatasources",
		DB:    skeleton.Database{File: db_file}}
	geoDb.Init()
	return geoDb
}

// Database strust for application.
type Database struct {
	Table            string
	File             string
	commit_log_queue chan string
	Precision        int
	DB              skeleton.Database
}

func (self Database) Init() {

	self.DB.Init()

	// start commit log
	go self.StartCommitLog()

	// default table
	if "" == self.Table {
		self.Table = "GeoJSONLayers"
	}

	conn := self.DB.Connect()
	defer conn.Close()

	err := self.DB.CreateTable(conn, self.Table)
	if nil != err {
		panic(err)
	}

	err = self.DB.CreateTable(conn, "GeoTimeseriesData")
	if nil != err {
		panic(err)
	}
}

func (self Database) getPrecision() int {
	if 1 > self.Precision {
		return DEFAULT_PRECISION
	}
	return self.Precision
}

// Starts Database commit log
func (self *Database) StartCommitLog() {
	self.commit_log_queue = make(chan string, 10000)
	// open file to write database commit log
	COMMIT_LOG, err := os.OpenFile(COMMIT_LOG_FILE, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
	if err != nil {
		log.Println(err)
	}
	defer COMMIT_LOG.Close()
	// read from chan and write to file
	for {
		if len(self.commit_log_queue) > 0 {
			line := <-self.commit_log_queue
			if _, err := COMMIT_LOG.WriteString(line + "\n"); err != nil {
				panic(err)
			}
		} else {
			time.Sleep(1000 * time.Millisecond)
		}
	}
}

// CommitQueueLength returns length of database commit_log_queue
// @returns int
func (self *Database) CommitQueueLength() int {
	return len(self.commit_log_queue)
}

// NewLayer creates new datasource layer
// @returns string - datasource id
// @returns Error
// TODO: RENAME TO NewDatasource
func (self *Database) NewLayer() (string, error) {
	// create geojson
	datasource_id, _ := NewUUID()
	geojs := geojson.NewFeatureCollection()
	// convert to bytes
	value, err := geojs.MarshalJSON()
	if err != nil {
		return "", nil
	}
	self.commit_log_queue <- `{"method": "create_datasource", "data": { "datasource": "` + datasource_id + `", "layer": ` + string(value) + `}}`
	// Insert layer into database
	err = self.DB.Insert(self.Table, datasource_id, value)
	if err != nil {
		panic(err)
	}
	return datasource_id, err
}

// InsertLayer inserts layer into database
// @param datasource {string}
// @param geojs {Geojson}
// @returns Error
func (self *Database) InsertLayer(datasource_id string, geojs *geojson.FeatureCollection) error {
	// convert to bytes
	value, err := geojs.MarshalJSON()
	if err != nil {
		return err
	}

	err = self.DB.Insert(self.Table, datasource_id, value)
	if err != nil {
		panic(err)
	}

	go self.UpdateTimeseriesDatasource(datasource_id, value)

	return err
}

// GetLayer returns layer from database
// @param datasource {string}
// @returns Geojson
// @returns Error
func (self *Database) GetLayer(datasource_id string) (*geojson.FeatureCollection, error) {
	val, err := self.DB.Select(self.Table, datasource_id)
	if err != nil {
		return nil, err
	}
	if "" == string(val) {
		return nil, fmt.Errorf("Datasource not found")
	}
	// Read to struct
	geojs, err := geojson.UnmarshalFeatureCollection(val)
	if err != nil {
		return geojs, err
	}
	return geojs, nil
}

// GetLayers returns all datasource_ids from database
// @param datasource {string}
// @returns Geojson
// @returns Error
func (self *Database) GetLayers() ([]string, error) {
	val, err := self.DB.SelectAll(self.Table)
	if err != nil {
		return nil, err
	}
	return val, nil
}

// DeleteLayer deletes layer from database
// @param datasource {string}
// @returns Error
func (self *Database) DeleteLayer(datasource_id string) error {
	self.commit_log_queue <- `{"method": "delete_layer", "data": { "datasource": "` + datasource_id + `"}}`
	err := self.DB.Remove(datasource_id, self.Table)
	return err
}

func (self *Database) normalizeGeometry(feat *geojson.Feature) (*geojson.Feature, error) {
	// FIT TO 7 - 8 DECIMAL PLACES OF PRECISION
	if nil == feat.Geometry {
		return nil, fmt.Errorf("Feature has no geometry!")
	}

	precision := self.getPrecision()

	switch feat.Geometry.Type {

	case geojson.GeometryPoint:
		// []float64
		feat.Geometry.Point[0] = RoundToPrecision(feat.Geometry.Point[0], precision)
		feat.Geometry.Point[1] = RoundToPrecision(feat.Geometry.Point[1], precision)

	case geojson.GeometryMultiPoint:
		// [][]float64
		for i := range feat.Geometry.MultiPoint {
			for j := range feat.Geometry.MultiPoint[i] {
				feat.Geometry.MultiPoint[i][j] = RoundToPrecision(feat.Geometry.MultiPoint[i][j], precision)
			}
		}

	case geojson.GeometryLineString:
		// [][]float64
		for i := range feat.Geometry.LineString {
			for j := range feat.Geometry.LineString[i] {
				feat.Geometry.LineString[i][j] = RoundToPrecision(feat.Geometry.LineString[i][j], precision)
			}
		}

	case geojson.GeometryMultiLineString:
		// [][][]float64
		for i := range feat.Geometry.MultiLineString {
			for j := range feat.Geometry.MultiLineString[i] {
				for k := range feat.Geometry.MultiLineString[i][j] {
					feat.Geometry.MultiLineString[i][j][k] = RoundToPrecision(feat.Geometry.MultiLineString[i][j][k], precision)
				}
			}
		}

	case geojson.GeometryPolygon:
		// [][][]float64
		for i := range feat.Geometry.Polygon {
			for j := range feat.Geometry.Polygon[i] {
				for k := range feat.Geometry.Polygon[i][j] {
					feat.Geometry.Polygon[i][j][k] = RoundToPrecision(feat.Geometry.Polygon[i][j][k], precision)
				}
			}
		}

	case geojson.GeometryMultiPolygon:
		// [][][][]float64
		for i := range feat.Geometry.MultiPolygon {
			log.Printf("%v\n", feat.Geometry.MultiPolygon[i])
		}

	}

	/*
		//case GeometryCollection:
		//	geo.Geometries = g.Geometries
		//	// log.Printf("%v\n", feat.Geometry.Geometries)

	*/
	return feat, nil
}

func (self *Database) normalizeProperties(feat *geojson.Feature, featCollection *geojson.FeatureCollection) *geojson.Feature {

	// check if nil map
	if nil == feat.Properties {
		feat.Properties = make(map[string]interface{})
	}

	if 0 == len(featCollection.Features) {
		return feat
	}
	// Standardize properties for new feature
	for j := range featCollection.Features[0].Properties {
		if _, ok := feat.Properties[j]; !ok {
			feat.Properties[j] = ""
		}
	}

	// Standardize properties for existing features
	for i := range featCollection.Features {
		for j := range feat.Properties {
			if _, ok := featCollection.Features[i].Properties[j]; !ok {
				featCollection.Features[i].Properties[j] = ""
			}
		}
	}

	return feat
}

// InsertFeature adds feature to layer. Updates layer in Database
// @param datasource {string}
// @param feat {Geojson Feature}
// @returns Error
func (self *Database) InsertFeature(datasource_id string, feat *geojson.Feature) error {

	if nil == feat {
		return fmt.Errorf("feature value is <nil>!")
	}

	// Get layer from database
	featCollection, err := self.GetLayer(datasource_id)
	if err != nil {
		return err
	}

	// Apply required columns
	now := time.Now().Unix()

	// check if nil map
	if nil == feat.Properties {
		feat.Properties = make(map[string]interface{})
	}

	feat.Properties["is_active"] = true
	feat.Properties["is_deleted"] = false
	feat.Properties["date_created"] = now
	feat.Properties["date_modified"] = now
	feat.Properties["geo_id"] = fmt.Sprintf("%v", now)

	feat, err = self.normalizeGeometry(feat)
	if nil != err {
		return err
	}

	feat = self.normalizeProperties(feat, featCollection)

	// Write to commit log
	value, err := feat.MarshalJSON()
	if err != nil {
		return err
	}
	self.commit_log_queue <- `{"method": "insert_feature", "data": { "datasource": "` + datasource_id + `", "feature": ` + string(value) + `}}`

	// Add new feature to layer
	featCollection.AddFeature(feat)

	// insert layer
	err = self.InsertLayer(datasource_id, featCollection)
	if err != nil {
		panic(err)
	}
	return err
}

// EditFeature Edits feature in layer. Updates layer in Database
// @param datasource {string}
// @param geo_id {string}
// @param feat {Geojson Feature}
// @returns Error
func (self *Database) EditFeature(datasource_id string, geo_id string, feat *geojson.Feature) error {

	// Get layer from database
	featCollection, err := self.GetLayer(datasource_id)
	if err != nil {
		return err
	}

	feature_exists := false

	for i := range featCollection.Features {
		if geo_id == fmt.Sprintf("%v", featCollection.Features[i].Properties["geo_id"]) {

			now := time.Now().Unix()
			feat.Properties["date_modified"] = now

			feat, err = self.normalizeGeometry(feat)
			if nil != err {
				return err
			}

			feat = self.normalizeProperties(feat, featCollection)
			featCollection.Features[i] = feat
			// Write to commit log
			value, err := feat.MarshalJSON()
			if err != nil {
				return err
			}
			self.commit_log_queue <- `{"method": "edit_feature", "data": { "datasource": "` + datasource_id + `", "geo_id": "` + geo_id + `", "feature": ` + string(value) + `}}`
			feature_exists = true
		}
	}

	if !feature_exists {
		return fmt.Errorf("feature not found!")
	}

	// insert layer
	err = self.InsertLayer(datasource_id, featCollection)
	if err != nil {
		panic(err)
	}
	return err
}

func (self *Database) InsertTimeseriesDatasource(datasource_id string, ddata diff_store.DiffStore) (error) {
	// save to database
	enc, err := ddata.Encode()
	if nil != err {
		panic(err)
	}

	//ddata.Name = datasource_id
	if datasource_id != ddata.Name {
		panic("DATASOURCE IDS DO NOT MATCH")
	}

	err := self.DB.Insert("GeoTimeseriesData", datasource_id, enc)
	return err
}

func (self *Database) SelectTimeseriesDatasource(datasource_id string) (diff_store.DiffStore, error) {
	var ddata diff_store.DiffStore
	data, err := self.DB.Select("GeoTimeseriesData", datasource_id)
	ddata.Decode(data)
	return ddata, err
}

func (self *Database) UpdateTimeseriesDatasource(datasource_id string, value []byte) error {

	update_value := string(value)

	//var ddata diff_store.DiffStore
	ddata, err := self.SelectTimeseriesDatasource(datasource_id)
	if nil != err {
		if err.Error() == "Not found" {
			// create new diffstore if key not found in database
			ddata = diff_store.NewDiffStore(datasource_id)
		} else {
			panic(err)
		}
	}

	// update diffstore
	ddata.Update(update_value)

	// write to database
	err = self.InsertTimeseriesDatasource(datasource_id, ddata)
	return err
}<|endoftext|>