<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"os"
	"strings"
	"time"

	"golang.org/x/net/websocket"
)

var (
	token              string
	botId, atId, alias string
	loc                *time.Location
)

func sendCommitMessage(m Message, outgoing chan<- Message) {
	resp, err := http.Get("http://whatthecommit.com/index.txt")
	if err != nil {
		log.Println(err)
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Println(err)
	}
	m.Text = strings.TrimSpace(string(body))
	outgoing <- m
}

func sendCode(m Message, outgoing chan<- Message) {
	m.Text = "稍等"
	if rand.Intn(2) > 0 {
		m.Text += "，刚看到"
	}
	if time.Now().In(loc).Hour() > 6 {
		m.Text += "，我在地铁上"
	}
	outgoing <- m
	time.Sleep(1 * time.Second)

	if rand.Intn(2) > 0 {
		m.Text = fmt.Sprintf("%d <@%s>", rand.Intn(9000)+1000, m.User)
	} else {
		m.Text = fmt.Sprintf("<@%s> %d", m.User, rand.Intn(9000)+1000)
	}
	outgoing <- m
}

func isImage(m Message) bool {
	return m.SubType == "file_share" &&
		strings.HasPrefix(m.File.Mimetype, "image")
}

// at in the middle of the message is not supported
func isAt(text string) bool {
	return strings.HasPrefix(text, atId) || strings.HasSuffix(text, atId) ||
		strings.HasPrefix(text, alias) || strings.HasSuffix(text, alias)
}

func handleMessage(incoming <-chan Message, outgoing chan<- Message) {
	for msg := range incoming {
		if msg.Type != "message" {
			continue
		}
		if strings.Contains(msg.Text, "谢谢") {
			msg.Text = "不客气 :blush:"
			outgoing <- msg
		} else if isAt(msg.Text) {
			fields := strings.Fields(msg.Text)
			if len(fields) == 1 {
				sendCode(msg, outgoing)
			} else {
				var commit bool
				log.Println(fields)
				for _, f := range fields {
					if isAt(f) {
						continue
					}
					if strings.Contains(f, "commit") {
						commit = true
					}
				}
				if commit {
					sendCommitMessage(msg, outgoing)
				} else {
					if rand.Intn(2) > 0 {
						msg.Text = "呵呵"
					} else {
						msg.Text = "嘻嘻"
					}
					outgoing <- msg
				}
			}
		} else if isImage(msg) {
			sendCode(msg, outgoing)
		}
	}
}

func readCredentials(file string) (token, alias string) {
	b, err := ioutil.ReadFile("CREDENTIALS")
	if err != nil {
		log.Fatal(err)
	}
	lines := strings.Split(string(b), "\n")
	token, alias = lines[0], lines[1]
	log.Println(token, alias)
	return
}

func startServer() {
	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
	}
	log.Println("listening on " + port)
	log.Fatal(http.ListenAndServe(":"+port, nil))
}

func init() {
	loc, _ = time.LoadLocation("Asia/Shanghai")
	log.SetFlags(log.Lshortfile)
	rand.Seed(time.Now().Unix())
}

func main() {
	token, alias = readCredentials("CREDENTIALS")
	wsurl, id := rtmStart(token)
	botId = id
	atId = "<@" + botId + ">"
	if alias == "" {
		alias = atId
	} else {
		alias = "@" + alias
	}
	log.Println(wsurl, botId)

	ws, err := websocket.Dial(wsurl, "", "https://api.slack.com/")
	if err != nil {
		log.Fatal(err)
	}

	incoming := make(chan Message)
	outgoing := make(chan Message)

	go rtmReceive(ws, incoming)
	go rtmSend(ws, outgoing)
	go handleMessage(incoming, outgoing)

	startServer()
}
<commit_msg>fix time check<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"os"
	"strings"
	"time"

	"golang.org/x/net/websocket"
)

var (
	token              string
	botId, atId, alias string
	loc                *time.Location
)

func sendCommitMessage(m Message, outgoing chan<- Message) {
	resp, err := http.Get("http://whatthecommit.com/index.txt")
	if err != nil {
		log.Println(err)
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Println(err)
	}
	m.Text = strings.TrimSpace(string(body))
	outgoing <- m
}

func sendCode(m Message, outgoing chan<- Message) {
	m.Text = "稍等"
	if rand.Intn(2) > 0 {
		m.Text += "，刚看到"
	}
	h := time.Now().In(loc).Hour()
	if h >= 18 && h <= 20 {
		m.Text += "，我在地铁上"
	}
	outgoing <- m
	time.Sleep(1 * time.Second)

	if rand.Intn(2) > 0 {
		m.Text = fmt.Sprintf("%d <@%s>", rand.Intn(9000)+1000, m.User)
	} else {
		m.Text = fmt.Sprintf("<@%s> %d", m.User, rand.Intn(9000)+1000)
	}
	outgoing <- m
}

func isImage(m Message) bool {
	return m.SubType == "file_share" &&
		strings.HasPrefix(m.File.Mimetype, "image")
}

// at in the middle of the message is not supported
func isAt(text string) bool {
	return strings.HasPrefix(text, atId) || strings.HasSuffix(text, atId) ||
		strings.HasPrefix(text, alias) || strings.HasSuffix(text, alias)
}

func handleMessage(incoming <-chan Message, outgoing chan<- Message) {
	for msg := range incoming {
		if msg.Type != "message" {
			continue
		}
		if strings.Contains(msg.Text, "谢谢") {
			msg.Text = "不客气 :blush:"
			outgoing <- msg
		} else if isAt(msg.Text) {
			fields := strings.Fields(msg.Text)
			if len(fields) == 1 {
				sendCode(msg, outgoing)
			} else {
				var commit bool
				log.Println(fields)
				for _, f := range fields {
					if isAt(f) {
						continue
					}
					if strings.Contains(f, "commit") {
						commit = true
					}
				}
				if commit {
					sendCommitMessage(msg, outgoing)
				} else {
					if rand.Intn(2) > 0 {
						msg.Text = "呵呵"
					} else {
						msg.Text = "嘻嘻"
					}
					outgoing <- msg
				}
			}
		} else if isImage(msg) {
			sendCode(msg, outgoing)
		}
	}
}

func readCredentials(file string) (token, alias string) {
	b, err := ioutil.ReadFile("CREDENTIALS")
	if err != nil {
		log.Fatal(err)
	}
	lines := strings.Split(string(b), "\n")
	token, alias = lines[0], lines[1]
	log.Println(token, alias)
	return
}

func startServer() {
	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
	}
	log.Println("listening on " + port)
	log.Fatal(http.ListenAndServe(":"+port, nil))
}

func init() {
	loc, _ = time.LoadLocation("Asia/Shanghai")
	log.SetFlags(log.Lshortfile)
	rand.Seed(time.Now().Unix())
}

func main() {
	token, alias = readCredentials("CREDENTIALS")
	wsurl, id := rtmStart(token)
	botId = id
	atId = "<@" + botId + ">"
	if alias == "" {
		alias = atId
	} else {
		alias = "@" + alias
	}
	log.Println(wsurl, botId)

	ws, err := websocket.Dial(wsurl, "", "https://api.slack.com/")
	if err != nil {
		log.Fatal(err)
	}

	incoming := make(chan Message)
	outgoing := make(chan Message)

	go rtmReceive(ws, incoming)
	go rtmSend(ws, outgoing)
	go handleMessage(incoming, outgoing)

	startServer()
}
<|endoftext|>


<commit_before>package main

import (
    "fmt"
    "github.com/bwmarrin/discordgo"
    "github.com/getsentry/raven-go"
    "github.com/sn0w/Karen/cache"
    "github.com/sn0w/Karen/helpers"
    Logger "github.com/sn0w/Karen/logger"
    "github.com/sn0w/Karen/metrics"
    "github.com/sn0w/Karen/plugins"
    "math/rand"
    "regexp"
    "strings"
    "time"
)

// BotOnReady gets called after the gateway connected
func BotOnReady(session *discordgo.Session, event *discordgo.Ready) {
    Logger.INFO.L("bot", "Connected to discord!")
    Logger.VERBOSE.L("bot", "Invite link: " + fmt.Sprintf(
        "https://discordapp.com/oauth2/authorize?client_id=%s&scope=bot&permissions=%s",
        helpers.GetConfig().Path("discord.id").Data().(string),
        helpers.GetConfig().Path("discord.perms").Data().(string),
    ))

    cache.SetSession(session)

    // Init plugins
    tmpl := "[PLUG] %s reacts to [ %s]"
    for _, plugin := range plugins.PluginList {
        cmds := ""

        for _, cmd := range plugin.Commands() {
            cmds += cmd + " "
        }

        Logger.INFO.L("bot", fmt.Sprintf(
            tmpl,
            helpers.Typeof(plugin),
            cmds,
        ))

        plugin.Init(session)
    }

    // Init trigger plugins
    tmpl = "[TRIG] Registered %s"
    for _, plugin := range plugins.TriggerPluginList {
        Logger.INFO.L("bot", fmt.Sprintf(
            tmpl,
            helpers.Typeof(plugin),
        ))

        plugin.Init(session)
    }

    go func() {
        time.Sleep(3 * time.Second)

        configName := helpers.GetConfig().Path("bot.name").Data().(string)
        configAvatar := helpers.GetConfig().Path("bot.avatar").Data().(string)

        // Change avatar if desired
        if configAvatar != "" && configAvatar != session.State.User.Avatar {
            session.UserUpdate(
                "",
                "",
                session.State.User.Username,
                configAvatar,
                "",
            )
        }

        // Change name if desired
        if configName != "" && configName != session.State.User.Username {
            session.UserUpdate(
                "",
                "",
                configName,
                session.State.User.Avatar,
                "",
            )
        }
    }()

    // Run async game-changer
    go changeGameInterval(session)

    // Run auto-leaver for non-beta guilds
    go autoLeaver(session)
}

// BotOnMessageCreate gets called after a new message was sent
// This will be called after *every* message on *every* server so it should die as soon as possible
// or spawn costly work inside of coroutines.
func BotOnMessageCreate(session *discordgo.Session, message *discordgo.MessageCreate) {
    // Ignore other bots and @everyone/@here
    if message.Author.Bot || message.MentionEveryone {
        return
    }

    // Get the channel
    // Ignore the event if we cannot resolve the channel
    channel, err := cache.Channel(message.ChannelID)
    if err != nil {
        go raven.CaptureError(err, map[string]string{})
        return
    }

    // We only do things in guilds.
    // Get a friend already and stop chatting with bots
    if channel.IsPrivate {
        return
    }

    // Check if the message contains @mentions for us
    if len(message.Mentions) > 0 && strings.HasPrefix(message.Content, "<@") && message.Mentions[0].ID == session.State.User.ID {
        // Prepare content for editing
        msg := message.Content

        /// Remove our @mention
        msg = strings.Replace(msg, "<@" + session.State.User.ID + ">", "", -1)

        // Trim message
        msg = strings.Trim(msg, " ")

        // Convert to []byte before matching
        bmsg := []byte(msg)

        // Match against common task patterns
        // Send to cleverbot if nothing matches
        switch {
        case regexp.MustCompile("(?i)^HELP.*").Match(bmsg):
            metrics.CommandsExecuted.Add(1)
            sendHelp(message)
            return

        case regexp.MustCompile("(?i)^PREFIX.*").Match(bmsg):
            metrics.CommandsExecuted.Add(1)
            prefix, _ := helpers.GetPrefixForServer(channel.GuildID)
            if prefix == "" {
                cache.GetSession().ChannelMessageSend(
                    channel.ID,
                    "Seems like there is no prefix yet :thinking:\n" +
                        "Admins can set one by typing for example `@Karen set prefix ?`",
                )
            }

            cache.GetSession().ChannelMessageSend(
                channel.ID,
                "The prefix is `" + prefix + "` :smiley:",
            )
            return

        case regexp.MustCompile("(?i)^REFRESH CHAT SESSION$").Match(bmsg):
            metrics.CommandsExecuted.Add(1)
            helpers.RequireAdmin(message.Message, func() {
                // Refresh cleverbot session
                helpers.CleverbotRefreshSession(channel.ID)
                cache.GetSession().ChannelMessageSend(channel.ID, ":cyclone: Refreshed!")
            })
            return

        case regexp.MustCompile("(?i)^SET PREFIX (.){1,25}$").Match(bmsg):
            metrics.CommandsExecuted.Add(1)
            helpers.RequireAdmin(message.Message, func() {
                // Extract prefix
                prefix := strings.Split(
                    regexp.MustCompile("(?i)^SET PREFIX\\s").ReplaceAllString(msg, ""),
                    " ",
                )[0]

                // Set new prefix
                err := helpers.SetPrefixForServer(
                    channel.GuildID,
                    prefix,
                )

                if err != nil {
                    helpers.SendError(message.Message, err)
                } else {
                    cache.GetSession().ChannelMessageSend(channel.ID, ":white_check_mark: Saved! \n The prefix is now `" + prefix + "`")
                }
            })
            return

        default:
            // Track usage
            metrics.CleverbotRequests.Add(1)

            // Send to cleverbot
            session.ChannelTyping(message.ChannelID)

            // Resolve other @mentions before sending the message
            for _, user := range message.Mentions {
                msg = strings.Replace(msg, "<@" + user.ID + ">", user.Username, -1)
            }

            // Remove smileys
            msg = regexp.MustCompile(`:\w+:`).ReplaceAllString(msg, "")

            // Send to cleverbot
            helpers.CleverbotSend(session, channel.ID, msg)
            return
        }
    }

    // Only continue if a prefix is set
    // Else check if any instant-replies match
    prefix, _ := helpers.GetPrefixForServer(channel.GuildID)
    if prefix == "" {
        plugins.CallTriggerPlugins(message.Message)
        return
    }

    // Check if the message is prefixed for us
    // If not exit
    if !strings.HasPrefix(message.Content, prefix) {
        return
    }

    // Split the message into parts
    parts := strings.Split(message.Content, " ")

    // Save a sanitized version of the command (no prefix)
    cmd := strings.Replace(parts[0], prefix, "", 1)

    // Check if the user calls for help
    if cmd == "h" || cmd == "help" {
        metrics.CommandsExecuted.Add(1)
        sendHelp(message)
        return
    }

    // Check if a module matches said command
    // Do nothing otherwise
    plugins.CallBotPlugin(
        cmd,
        strings.Replace(message.Content, prefix + cmd, "", -1),
        message.Message,
    )
}

func sendHelp(message *discordgo.MessageCreate) {
    cache.GetSession().ChannelMessageSend(
        message.ChannelID,
        fmt.Sprintf("<@%s> Check out <http://meetkaren.xyz/commands>", message.Author.ID),
    )
}

// Changes the game interval every 10 seconds after called
func changeGameInterval(session *discordgo.Session) {
    for {
        err := session.UpdateStatus(
            0,
            games[rand.Intn(len(games))],
        )
        if err != nil {
            raven.CaptureError(err, map[string]string{})
        }

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

// List of games to randomly show
var games = []string{
    // Random stuff
    "async is the future!",
    "down with OOP!",
    "spoopy stuff",
    "Planking",

    // Kaomoji
    "ʕ•ᴥ•ʔ",
    "༼ つ ◕_◕ ༽つ",
    "(ﾉ◕ヮ◕)ﾉ*:･ﾟ✧",
    "( ͡° ͜ʖ ͡°)",
    "¯\\_(ツ)_/¯",
    "(ง ͠° ͟ل͜ ͡°)ง",
    "ಠ_ಠ",
    "(╯°□°)╯︵ ʞooqǝɔɐɟ",
    "♪~ ᕕ(ᐛ)ᕗ",
    "\\ (•◡•) /",
    "｡◕‿◕｡",

    // actual games
    "Hearthstone",
    "Overwatch",
    "HuniePop",
    "Candy Crush",
    "Hyperdimension Neptunia",
    "Final Fantasy MCMX",
    "CIV V",
    "Pokemon Go",
    "Simulation Simulator 2016",
    "Half Life 3",
    "Nekopara",

    // software
    "with FFMPEG",
    "with libav",
    "with gophers",
    "with python",
    "with reflections",

    // names
    "with Shinobu-Chan",
    "with Ako-Chan",
    "with Nadeko",
    "with Miku",
    "with you O_o",
    "with cats",
    "with JOHN CENA",
    "with senpai",
    "with Serraniel#8978",
    "with 0xFADED#3237",
    "with C0untLizzi",
    "with moot",
    "with your waifu",
    "with Trump",
}
<commit_msg>Update games list<commit_after>package main

import (
    "fmt"
    "github.com/bwmarrin/discordgo"
    "github.com/getsentry/raven-go"
    "github.com/sn0w/Karen/cache"
    "github.com/sn0w/Karen/helpers"
    Logger "github.com/sn0w/Karen/logger"
    "github.com/sn0w/Karen/metrics"
    "github.com/sn0w/Karen/plugins"
    "math/rand"
    "regexp"
    "strings"
    "time"
)

// BotOnReady gets called after the gateway connected
func BotOnReady(session *discordgo.Session, event *discordgo.Ready) {
    Logger.INFO.L("bot", "Connected to discord!")
    Logger.VERBOSE.L("bot", "Invite link: " + fmt.Sprintf(
        "https://discordapp.com/oauth2/authorize?client_id=%s&scope=bot&permissions=%s",
        helpers.GetConfig().Path("discord.id").Data().(string),
        helpers.GetConfig().Path("discord.perms").Data().(string),
    ))

    cache.SetSession(session)

    // Init plugins
    tmpl := "[PLUG] %s reacts to [ %s]"
    for _, plugin := range plugins.PluginList {
        cmds := ""

        for _, cmd := range plugin.Commands() {
            cmds += cmd + " "
        }

        Logger.INFO.L("bot", fmt.Sprintf(
            tmpl,
            helpers.Typeof(plugin),
            cmds,
        ))

        plugin.Init(session)
    }

    // Init trigger plugins
    tmpl = "[TRIG] Registered %s"
    for _, plugin := range plugins.TriggerPluginList {
        Logger.INFO.L("bot", fmt.Sprintf(
            tmpl,
            helpers.Typeof(plugin),
        ))

        plugin.Init(session)
    }

    go func() {
        time.Sleep(3 * time.Second)

        configName := helpers.GetConfig().Path("bot.name").Data().(string)
        configAvatar := helpers.GetConfig().Path("bot.avatar").Data().(string)

        // Change avatar if desired
        if configAvatar != "" && configAvatar != session.State.User.Avatar {
            session.UserUpdate(
                "",
                "",
                session.State.User.Username,
                configAvatar,
                "",
            )
        }

        // Change name if desired
        if configName != "" && configName != session.State.User.Username {
            session.UserUpdate(
                "",
                "",
                configName,
                session.State.User.Avatar,
                "",
            )
        }
    }()

    // Run async game-changer
    go changeGameInterval(session)

    // Run auto-leaver for non-beta guilds
    go autoLeaver(session)
}

// BotOnMessageCreate gets called after a new message was sent
// This will be called after *every* message on *every* server so it should die as soon as possible
// or spawn costly work inside of coroutines.
func BotOnMessageCreate(session *discordgo.Session, message *discordgo.MessageCreate) {
    // Ignore other bots and @everyone/@here
    if message.Author.Bot || message.MentionEveryone {
        return
    }

    // Get the channel
    // Ignore the event if we cannot resolve the channel
    channel, err := cache.Channel(message.ChannelID)
    if err != nil {
        go raven.CaptureError(err, map[string]string{})
        return
    }

    // We only do things in guilds.
    // Get a friend already and stop chatting with bots
    if channel.IsPrivate {
        return
    }

    // Check if the message contains @mentions for us
    if len(message.Mentions) > 0 && strings.HasPrefix(message.Content, "<@") && message.Mentions[0].ID == session.State.User.ID {
        // Prepare content for editing
        msg := message.Content

        /// Remove our @mention
        msg = strings.Replace(msg, "<@" + session.State.User.ID + ">", "", -1)

        // Trim message
        msg = strings.Trim(msg, " ")

        // Convert to []byte before matching
        bmsg := []byte(msg)

        // Match against common task patterns
        // Send to cleverbot if nothing matches
        switch {
        case regexp.MustCompile("(?i)^HELP.*").Match(bmsg):
            metrics.CommandsExecuted.Add(1)
            sendHelp(message)
            return

        case regexp.MustCompile("(?i)^PREFIX.*").Match(bmsg):
            metrics.CommandsExecuted.Add(1)
            prefix, _ := helpers.GetPrefixForServer(channel.GuildID)
            if prefix == "" {
                cache.GetSession().ChannelMessageSend(
                    channel.ID,
                    "Seems like there is no prefix yet :thinking:\n" +
                        "Admins can set one by typing for example `@Karen set prefix ?`",
                )
            }

            cache.GetSession().ChannelMessageSend(
                channel.ID,
                "The prefix is `" + prefix + "` :smiley:",
            )
            return

        case regexp.MustCompile("(?i)^REFRESH CHAT SESSION$").Match(bmsg):
            metrics.CommandsExecuted.Add(1)
            helpers.RequireAdmin(message.Message, func() {
                // Refresh cleverbot session
                helpers.CleverbotRefreshSession(channel.ID)
                cache.GetSession().ChannelMessageSend(channel.ID, ":cyclone: Refreshed!")
            })
            return

        case regexp.MustCompile("(?i)^SET PREFIX (.){1,25}$").Match(bmsg):
            metrics.CommandsExecuted.Add(1)
            helpers.RequireAdmin(message.Message, func() {
                // Extract prefix
                prefix := strings.Split(
                    regexp.MustCompile("(?i)^SET PREFIX\\s").ReplaceAllString(msg, ""),
                    " ",
                )[0]

                // Set new prefix
                err := helpers.SetPrefixForServer(
                    channel.GuildID,
                    prefix,
                )

                if err != nil {
                    helpers.SendError(message.Message, err)
                } else {
                    cache.GetSession().ChannelMessageSend(channel.ID, ":white_check_mark: Saved! \n The prefix is now `" + prefix + "`")
                }
            })
            return

        default:
            // Track usage
            metrics.CleverbotRequests.Add(1)

            // Send to cleverbot
            session.ChannelTyping(message.ChannelID)

            // Resolve other @mentions before sending the message
            for _, user := range message.Mentions {
                msg = strings.Replace(msg, "<@" + user.ID + ">", user.Username, -1)
            }

            // Remove smileys
            msg = regexp.MustCompile(`:\w+:`).ReplaceAllString(msg, "")

            // Send to cleverbot
            helpers.CleverbotSend(session, channel.ID, msg)
            return
        }
    }

    // Only continue if a prefix is set
    // Else check if any instant-replies match
    prefix, _ := helpers.GetPrefixForServer(channel.GuildID)
    if prefix == "" {
        plugins.CallTriggerPlugins(message.Message)
        return
    }

    // Check if the message is prefixed for us
    // If not exit
    if !strings.HasPrefix(message.Content, prefix) {
        return
    }

    // Split the message into parts
    parts := strings.Split(message.Content, " ")

    // Save a sanitized version of the command (no prefix)
    cmd := strings.Replace(parts[0], prefix, "", 1)

    // Check if the user calls for help
    if cmd == "h" || cmd == "help" {
        metrics.CommandsExecuted.Add(1)
        sendHelp(message)
        return
    }

    // Check if a module matches said command
    // Do nothing otherwise
    plugins.CallBotPlugin(
        cmd,
        strings.Replace(message.Content, prefix + cmd, "", -1),
        message.Message,
    )
}

func sendHelp(message *discordgo.MessageCreate) {
    cache.GetSession().ChannelMessageSend(
        message.ChannelID,
        fmt.Sprintf("<@%s> Check out <http://meetkaren.xyz/commands>", message.Author.ID),
    )
}

// Changes the game interval every 10 seconds after called
func changeGameInterval(session *discordgo.Session) {
    for {
        err := session.UpdateStatus(
            0,
            games[rand.Intn(len(games))],
        )
        if err != nil {
            raven.CaptureError(err, map[string]string{})
        }

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

// List of games to randomly show
var games = []string{
    // Random stuff
    "async is the future!",
    "down with OOP!",
    "spoopy stuff",
    "Planking",

    // Kaomoji
    "ʕ•ᴥ•ʔ",
    "༼ つ ◕_◕ ༽つ",
    "(ﾉ◕ヮ◕)ﾉ*:･ﾟ✧",
    "( ͡° ͜ʖ ͡°)",
    "¯\\_(ツ)_/¯",
    "(ง ͠° ͟ل͜ ͡°)ง",
    "ಠ_ಠ",
    "(╯°□°)╯︵ ʞooqǝɔɐɟ",
    "♪~ ᕕ(ᐛ)ᕗ",
    "\\ (•◡•) /",
    "｡◕‿◕｡",

    // actual games
    "Hearthstone",
    "Overwatch",
    "HuniePop",
    "Candy Crush",
    "Hyperdimension Neptunia",
    "Final Fantasy MCMX",
    "CIV V",
    "Simulation Simulator 2016",
    "Half Life 3",

    // software
    "with FFMPEG",
    "with libav",
    "with gophers",
    "with python",
    "with reflections",

    // Bot names
    "with Shiro <3",        // Rest in Peace :(
    "with Emily",           // ohai kaaz
    "with Shinobu-Chan",    // ohai eddy
    "with Nepgear",         // ohai serraniel
    "with Rem",             // ohai serenity

    // Anime names
    "with Ram",
    "with Rom",
    "with Nadeko",
    "with Miku",

    // Discord names
    "with Serraniel",
    "with Kaaz",
    "with 0xFADED",

    // Other names
    "with you O_o",
    "with cats",
    "with JOHN CENA",
    "with senpai",
    "with moot",
    "with your waifu",
}
<|endoftext|>


<commit_before>package opsgenie2

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"path"
	"sync/atomic"
	"time"

	"github.com/influxdata/kapacitor/alert"
	"github.com/influxdata/kapacitor/keyvalue"
	"github.com/influxdata/kapacitor/models"
	"github.com/pkg/errors"
)

type Diagnostic interface {
	WithContext(ctx ...keyvalue.T) Diagnostic

	Error(msg string, err error)
}

type Service struct {
	configValue atomic.Value
	diag        Diagnostic
}

func NewService(c Config, d Diagnostic) *Service {
	s := &Service{
		diag: d,
	}
	s.configValue.Store(c)
	return s
}

func (s *Service) Open() error {
	return nil
}

func (s *Service) Close() error {
	return nil
}

func (s *Service) config() Config {
	return s.configValue.Load().(Config)
}

func (s *Service) Update(newConfig []interface{}) error {
	if l := len(newConfig); l != 1 {
		return fmt.Errorf("expected only one new config object, got %d", l)
	}
	if c, ok := newConfig[0].(Config); !ok {
		return fmt.Errorf("expected config object to be of type %T, got %T", c, newConfig[0])
	} else {
		s.configValue.Store(c)
	}
	return nil
}

func (s *Service) Global() bool {
	c := s.config()
	return c.Global
}

type testOptions struct {
	Teams          []string `json:"teams"`
	Recipients     []string `json:"recipients"`
	RecoveryAction string   `json:"recovery-action"`
	MessageType    string   `json:"message-type"`
	Message        string   `json:"message"`
	EntityID       string   `json:"entity-id"`
}

func (s *Service) TestOptions() interface{} {
	c := s.config()
	return &testOptions{
		Teams:          c.Teams,
		Recipients:     c.Recipients,
		RecoveryAction: c.RecoveryAction,
		MessageType:    "CRITICAL",
		Message:        "test opsgenie message",
		EntityID:       "testEntityID",
	}
}

func (s *Service) Test(options interface{}) error {
	o, ok := options.(*testOptions)
	if !ok {
		return fmt.Errorf("unexpected options type %T", options)
	}
	var level alert.Level
	if o.MessageType == "RECOVERY" {
		level = alert.OK
	} else {
		l, err := alert.ParseLevel(o.MessageType)
		if err != nil {
			return err
		}
		level = l
	}
	return s.Alert(
		o.Teams,
		o.Recipients,
		o.RecoveryAction,
		level,
		o.Message,
		o.EntityID,
		time.Now(),
		models.Result{},
	)
}

func (s *Service) Alert(teams []string, recipients []string, recoveryAction string, level alert.Level, message, entityID string, t time.Time, details models.Result) error {
	req, err := s.preparePost(teams, recipients, recoveryAction, level, message, entityID, t, details)
	if err != nil {
		return errors.Wrap(err, "failed to prepare API request")
	}

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return errors.Wrap(err, "failed to execute API request")
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusAccepted {
		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return errors.Wrap(err, "failed to read API response")
		}
		type response struct {
			Message string `json:"message"`
		}
		r := &response{Message: fmt.Sprintf("failed to understand OpsGenie response. code: %d content: %s", resp.StatusCode, string(body))}
		b := bytes.NewReader(body)
		dec := json.NewDecoder(b)
		dec.Decode(r)
		return fmt.Errorf("opsgenie error: %s", r.Message)
	}
	return nil
}

func (s *Service) preparePost(teams []string, recipients []string, recoveryAction string, level alert.Level, message, entityID string, t time.Time, details models.Result) (*http.Request, error) {
	c := s.config()
	if !c.Enabled {
		return nil, errors.New("service is not enabled")
	}

	alias := base64.URLEncoding.EncodeToString([]byte(entityID))

	ogData := make(map[string]interface{})
	u := c.URL
	switch level {
	case alert.OK:
		recoveryURL, err := url.Parse(c.URL)
		if err != nil {
			return nil, err
		}
		validRecoveryActions := map[string]bool{
			"close": true,
			"notes": true,
		}
		if !validRecoveryActions[recoveryAction] { // use parameter
			if !validRecoveryActions[c.RecoveryAction] { //use global config
				recoveryAction = DefaultOpsGenieRecoveryAction //use default
			} else {
				recoveryAction = c.RecoveryAction
			}
		}
		recoveryURL.Path = path.Join(recoveryURL.Path, alias, recoveryAction)
		recoveryURL.RawQuery = "identifierType=alias"
		u = recoveryURL.String()
		ogData["note"] = message
	default:
		var priority string
		switch level {
		case alert.Info:
			priority = "P5"
		case alert.Warning:
			priority = "P3"
		case alert.Critical:
			priority = "P1"
		}

		ogData["entity"] = entityID
		ogData["alias"] = alias
		ogData["message"] = message
		ogData["note"] = ""
		ogData["priority"] = priority

		// Encode details as description
		b, err := json.Marshal(details)
		if err != nil {
			return nil, err
		}
		ogData["description"] = string(b)

		//Extra Fields (can be used for filtering)
		ogDetails := make(map[string]string)
		ogDetails["Monitoring Tool"] = "Kapacitor"
		ogDetails["Level"] = level.String()

		if len(details.Series) > 0 {
			row := details.Series[0]
			for k, v := range row.Tags {
				ogDetails[k] = v
			}
			ogDetails["Kapacitor Task Name"] = row.Name
		}

		ogData["details"] = ogDetails

		// Create responders list
		var responders []map[string]string
		if len(teams) == 0 {
			teams = c.Teams
		}
		if len(teams) > 0 {
			for _, t := range teams {
				responders = append(responders, map[string]string{
					"name": t,
					"type": "team",
				})
			}
		}

		if len(recipients) == 0 {
			recipients = c.Recipients
		}
		if len(recipients) > 0 {
			for _, u := range recipients {
				responders = append(responders, map[string]string{
					"username": u,
					"type":     "user",
				})
			}
		}

		if len(responders) > 0 {
			ogData["responders"] = responders
		}
	}

	var post bytes.Buffer
	enc := json.NewEncoder(&post)
	if err := enc.Encode(ogData); err != nil {
		return nil, err
	}

	req, err := http.NewRequest("POST", u, &post)
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", "GenieKey "+c.APIKey)

	return req, nil
}

type HandlerConfig struct {
	// OpsGenie Teams.
	TeamsList []string `mapstructure:"teams-list"`

	// OpsGenie Recipients.
	RecipientsList []string `mapstructure:"recipients-list"`

	// OpsGenie RecoveryAction
	RecoveryAction string `mapstructure:"recovery-action"`
}

type handler struct {
	s    *Service
	c    HandlerConfig
	diag Diagnostic
}

func (s *Service) Handler(c HandlerConfig, ctx ...keyvalue.T) alert.Handler {
	return &handler{
		s:    s,
		c:    c,
		diag: s.diag.WithContext(ctx...),
	}
}

func (h *handler) Handle(event alert.Event) {
	if err := h.s.Alert(
		h.c.TeamsList,
		h.c.RecipientsList,
		h.c.RecoveryAction,
		event.State.Level,
		event.State.Message,
		event.State.ID,
		event.State.Time,
		event.Data.Result,
	); err != nil {
		h.diag.Error("failed to send event to OpsGenie", err)
	}
}
<commit_msg>Fix config tests<commit_after>package opsgenie2

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"path"
	"sync/atomic"
	"time"

	"github.com/influxdata/kapacitor/alert"
	"github.com/influxdata/kapacitor/keyvalue"
	"github.com/influxdata/kapacitor/models"
	"github.com/pkg/errors"
)

type Diagnostic interface {
	WithContext(ctx ...keyvalue.T) Diagnostic

	Error(msg string, err error)
}

type Service struct {
	configValue atomic.Value
	diag        Diagnostic
}

func NewService(c Config, d Diagnostic) *Service {
	s := &Service{
		diag: d,
	}
	s.configValue.Store(c)
	return s
}

func (s *Service) Open() error {
	return nil
}

func (s *Service) Close() error {
	return nil
}

func (s *Service) config() Config {
	return s.configValue.Load().(Config)
}

func (s *Service) Update(newConfig []interface{}) error {
	if l := len(newConfig); l != 1 {
		return fmt.Errorf("expected only one new config object, got %d", l)
	}
	if c, ok := newConfig[0].(Config); !ok {
		return fmt.Errorf("expected config object to be of type %T, got %T", c, newConfig[0])
	} else {
		s.configValue.Store(c)
	}
	return nil
}

func (s *Service) Global() bool {
	c := s.config()
	return c.Global
}

type testOptions struct {
	Teams          []string `json:"teams"`
	Recipients     []string `json:"recipients"`
	RecoveryAction string   `json:"recovery_action"`
	MessageType    string   `json:"message-type"`
	Message        string   `json:"message"`
	EntityID       string   `json:"entity-id"`
}

func (s *Service) TestOptions() interface{} {
	c := s.config()
	return &testOptions{
		Teams:          c.Teams,
		Recipients:     c.Recipients,
		RecoveryAction: c.RecoveryAction,
		MessageType:    "CRITICAL",
		Message:        "test opsgenie message",
		EntityID:       "testEntityID",
	}
}

func (s *Service) Test(options interface{}) error {
	o, ok := options.(*testOptions)
	if !ok {
		return fmt.Errorf("unexpected options type %T", options)
	}
	var level alert.Level
	if o.MessageType == "RECOVERY" {
		level = alert.OK
	} else {
		l, err := alert.ParseLevel(o.MessageType)
		if err != nil {
			return err
		}
		level = l
	}
	return s.Alert(
		o.Teams,
		o.Recipients,
		o.RecoveryAction,
		level,
		o.Message,
		o.EntityID,
		time.Now(),
		models.Result{},
	)
}

func (s *Service) Alert(teams []string, recipients []string, recoveryAction string, level alert.Level, message, entityID string, t time.Time, details models.Result) error {
	req, err := s.preparePost(teams, recipients, recoveryAction, level, message, entityID, t, details)
	if err != nil {
		return errors.Wrap(err, "failed to prepare API request")
	}

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return errors.Wrap(err, "failed to execute API request")
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusAccepted {
		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return errors.Wrap(err, "failed to read API response")
		}
		type response struct {
			Message string `json:"message"`
		}
		r := &response{Message: fmt.Sprintf("failed to understand OpsGenie response. code: %d content: %s", resp.StatusCode, string(body))}
		b := bytes.NewReader(body)
		dec := json.NewDecoder(b)
		dec.Decode(r)
		return fmt.Errorf("opsgenie error: %s", r.Message)
	}
	return nil
}

func (s *Service) preparePost(teams []string, recipients []string, recoveryAction string, level alert.Level, message, entityID string, t time.Time, details models.Result) (*http.Request, error) {
	c := s.config()
	if !c.Enabled {
		return nil, errors.New("service is not enabled")
	}

	alias := base64.URLEncoding.EncodeToString([]byte(entityID))

	ogData := make(map[string]interface{})
	u := c.URL
	switch level {
	case alert.OK:
		recoveryURL, err := url.Parse(c.URL)
		if err != nil {
			return nil, err
		}
		validRecoveryActions := map[string]bool{
			"close": true,
			"notes": true,
		}
		if !validRecoveryActions[recoveryAction] { // use parameter
			if !validRecoveryActions[c.RecoveryAction] { //use global config
				recoveryAction = DefaultOpsGenieRecoveryAction //use default
			} else {
				recoveryAction = c.RecoveryAction
			}
		}
		recoveryURL.Path = path.Join(recoveryURL.Path, alias, recoveryAction)
		recoveryURL.RawQuery = "identifierType=alias"
		u = recoveryURL.String()
		ogData["note"] = message
	default:
		var priority string
		switch level {
		case alert.Info:
			priority = "P5"
		case alert.Warning:
			priority = "P3"
		case alert.Critical:
			priority = "P1"
		}

		ogData["entity"] = entityID
		ogData["alias"] = alias
		ogData["message"] = message
		ogData["note"] = ""
		ogData["priority"] = priority

		// Encode details as description
		b, err := json.Marshal(details)
		if err != nil {
			return nil, err
		}
		ogData["description"] = string(b)

		//Extra Fields (can be used for filtering)
		ogDetails := make(map[string]string)
		ogDetails["Monitoring Tool"] = "Kapacitor"
		ogDetails["Level"] = level.String()

		if len(details.Series) > 0 {
			row := details.Series[0]
			for k, v := range row.Tags {
				ogDetails[k] = v
			}
			ogDetails["Kapacitor Task Name"] = row.Name
		}

		ogData["details"] = ogDetails

		// Create responders list
		var responders []map[string]string
		if len(teams) == 0 {
			teams = c.Teams
		}
		if len(teams) > 0 {
			for _, t := range teams {
				responders = append(responders, map[string]string{
					"name": t,
					"type": "team",
				})
			}
		}

		if len(recipients) == 0 {
			recipients = c.Recipients
		}
		if len(recipients) > 0 {
			for _, u := range recipients {
				responders = append(responders, map[string]string{
					"username": u,
					"type":     "user",
				})
			}
		}

		if len(responders) > 0 {
			ogData["responders"] = responders
		}
	}

	var post bytes.Buffer
	enc := json.NewEncoder(&post)
	if err := enc.Encode(ogData); err != nil {
		return nil, err
	}

	req, err := http.NewRequest("POST", u, &post)
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", "GenieKey "+c.APIKey)

	return req, nil
}

type HandlerConfig struct {
	// OpsGenie Teams.
	TeamsList []string `mapstructure:"teams-list"`

	// OpsGenie Recipients.
	RecipientsList []string `mapstructure:"recipients-list"`

	// OpsGenie RecoveryAction
	RecoveryAction string `mapstructure:"recovery_action"`
}

type handler struct {
	s    *Service
	c    HandlerConfig
	diag Diagnostic
}

func (s *Service) Handler(c HandlerConfig, ctx ...keyvalue.T) alert.Handler {
	return &handler{
		s:    s,
		c:    c,
		diag: s.diag.WithContext(ctx...),
	}
}

func (h *handler) Handle(event alert.Event) {
	if err := h.s.Alert(
		h.c.TeamsList,
		h.c.RecipientsList,
		h.c.RecoveryAction,
		event.State.Level,
		event.State.Message,
		event.State.ID,
		event.State.Time,
		event.Data.Result,
	); err != nil {
		h.diag.Error("failed to send event to OpsGenie", err)
	}
}
<|endoftext|>


<commit_before>package types

import (
	"encoding/json"
	"strings"
	"testing"
)

func TestLabels(t *testing.T) {
	tests := []struct {
		in        string
		errPrefix string
	}{
		{
			`[{"name": "os", "value": "linux"}, {"name": "arch", "value": "amd64"}]`,
			"",
		},
		{
			`[{"name": "os", "value": "linux"}, {"name": "arch", "value": "aarch64"}]`,
			"",
		},
		{
			`[{"name": "os", "value": "linux"}, {"name": "arch", "value": "aarch64_be"}]`,
			"",
		},
		{
			`[{"name": "os", "value": "linux"}, {"name": "arch", "value": "armv7l"}]`,
			"",
		},
		{
			`[{"name": "os", "value": "linux"}, {"name": "arch", "value": "armv7b"}]`,
			"",
		},
		{
			`[{"name": "os", "value": "freebsd"}, {"name": "arch", "value": "amd64"}]`,
			"",
		},
		{
			`[{"name": "os", "value": "OS/360"}, {"name": "arch", "value": "S/360"}]`,
			`bad os "OS/360"`,
		},
		{
			`[{"name": "os", "value": "freebsd"}, {"name": "arch", "value": "armv7b"}]`,
			`bad arch "armv7b" for freebsd`,
		},
		{
			`[{"name": "os", "value": "linux"}, {"name": "arch", "value": "arm"}]`,
			`bad arch "arm" for linux`,
		},
		{
			`[{"name": "name"}]`,
			`invalid label name: "name"`,
		},
		{
			`[{"name": "os", "value": "linux"}, {"name": "os", "value": "freebsd"}]`,
			`duplicate labels of name "os"`,
		},
		{
			`[{"name": "arch", "value": "amd64"}, {"name": "os", "value": "freebsd"}, {"name": "arch", "value": "x86_64"}]`,
			`duplicate labels of name "arch"`,
		},
		{
			`[]`,
			"",
		},
	}
	for i, tt := range tests {
		var l Labels
		if err := json.Unmarshal([]byte(tt.in), &l); err != nil {
			if tt.errPrefix == "" {
				t.Errorf("#%d: got err=%v, expected no error", i, err)
			} else if !strings.HasPrefix(err.Error(), tt.errPrefix) {
				t.Errorf("#%d: got err=%v, expected prefix %#v", i, err, tt.errPrefix)
			}
		} else {
			t.Log(l)
			if tt.errPrefix != "" {
				t.Errorf("#%d: got no err, expected prefix %#v", i, tt.errPrefix)
			}
		}
	}
}
<commit_msg>arm64: Add schema label checks<commit_after>package types

import (
	"encoding/json"
	"strings"
	"testing"
)

func TestLabels(t *testing.T) {
	tests := []struct {
		in        string
		errPrefix string
	}{
		{
			`[{"name": "os", "value": "linux"}, {"name": "arch", "value": "amd64"}]`,
			"",
		},
		{
			`[{"name": "os", "value": "linux"}, {"name": "arch", "value": "aarch64"}]`,
			"",
		},
		{
			`[{"name": "os", "value": "linux"}, {"name": "arch", "value": "arm64"}]`,
			`bad arch "arm64" for linux`,
		},
		{
			`[{"name": "os", "value": "linux"}, {"name": "arch", "value": "aarch64_be"}]`,
			"",
		},
		{
			`[{"name": "os", "value": "linux"}, {"name": "arch", "value": "arm64_be"}]`,
			`bad arch "arm64_be" for linux`,
		},
		{
			`[{"name": "os", "value": "linux"}, {"name": "arch", "value": "armv7l"}]`,
			"",
		},
		{
			`[{"name": "os", "value": "linux"}, {"name": "arch", "value": "armv7b"}]`,
			"",
		},
		{
			`[{"name": "os", "value": "freebsd"}, {"name": "arch", "value": "amd64"}]`,
			"",
		},
		{
			`[{"name": "os", "value": "OS/360"}, {"name": "arch", "value": "S/360"}]`,
			`bad os "OS/360"`,
		},
		{
			`[{"name": "os", "value": "freebsd"}, {"name": "arch", "value": "armv7b"}]`,
			`bad arch "armv7b" for freebsd`,
		},
		{
			`[{"name": "os", "value": "linux"}, {"name": "arch", "value": "arm"}]`,
			`bad arch "arm" for linux`,
		},
		{
			`[{"name": "name"}]`,
			`invalid label name: "name"`,
		},
		{
			`[{"name": "os", "value": "linux"}, {"name": "os", "value": "freebsd"}]`,
			`duplicate labels of name "os"`,
		},
		{
			`[{"name": "arch", "value": "amd64"}, {"name": "os", "value": "freebsd"}, {"name": "arch", "value": "x86_64"}]`,
			`duplicate labels of name "arch"`,
		},
		{
			`[]`,
			"",
		},
	}
	for i, tt := range tests {
		var l Labels
		if err := json.Unmarshal([]byte(tt.in), &l); err != nil {
			if tt.errPrefix == "" {
				t.Errorf("#%d: got err=%v, expected no error", i, err)
			} else if !strings.HasPrefix(err.Error(), tt.errPrefix) {
				t.Errorf("#%d: got err=%v, expected prefix %#v", i, err, tt.errPrefix)
			}
		} else {
			t.Log(l)
			if tt.errPrefix != "" {
				t.Errorf("#%d: got no err, expected prefix %#v", i, tt.errPrefix)
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"os/exec"
)

func OutputToNotificationCenter(pokemon PokemonMeta, config Config) error {
    pokemonIcon, _ := DownloadIcon(pokemon.PokedexPokemon.Index)

    exec.Command("notify-send", "-i", pokemonIcon, pokemon.Name + " located near " + pokemon.Location, "Expires in " + HumanTime(pokemon.ExpiresAt)).Run()
    return nil
}
<commit_msg>Added distance in linux notification<commit_after>package main

import (
    "os/exec"
    "strconv"
)

func OutputToNotificationCenter(pokemon PokemonMeta, config Config) error {
    pokemonIcon, _ := DownloadIcon(pokemon.PokedexPokemon.Index)

    exec.Command("notify-send", "-i", pokemonIcon, pokemon.Name + " - " + strconv.Itoa(pokemon.Distance) + "m from " + pokemon.Location, "Expires in " + HumanTime(pokemon.ExpiresAt)).Run()
    return nil
}
<|endoftext|>


<commit_before>package blog

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"testing"
	"time"

	"github.com/ezbuy/ezorm/db"
	"gopkg.in/mgo.v2"
)

var (
	listenPort = ":8080"
)

func dbInit() {
	conf := new(db.MongoConfig)
	conf.DBName = "ezorm"
	conf.MongoDB = "mongodb://127.0.0.1"
	db.Setup(conf)

	http.HandleFunc("/blog", func(w http.ResponseWriter, r *http.Request) {
		k := r.URL.Query().Get("key")

		p, err := BlogMgr.FindByID(k)
		if err != nil {
			if err != mgo.ErrNotFound {
				panic(err.Error())
			}
			w.WriteHeader(http.StatusNotFound)
			w.Write([]byte(err.Error()))
			return
		}

		data, err := json.Marshal(p)
		if err != nil {
			panic(err)
		}
		w.Write(data)
	})

	http.HandleFunc("/blog/delete", func(w http.ResponseWriter, r *http.Request) {
		k := r.URL.Query().Get("key")

		err := BlogMgr.RemoveByID(k)
		if err != nil {
			panic(err.Error())
		}

		w.Write([]byte("ok"))
	})

	fmt.Printf("start listening on port [%s]\n", listenPort)
	go http.ListenAndServe(listenPort, nil)
}

func TestBlog(t *testing.T) {
	dbInit()
	p := BlogMgr.NewBlog()
	p.Title = "I like ezorm"
	p.Slug = fmt.Sprintf("ezorm_%d", time.Now().Nanosecond())
	_, err := p.Save()
	if err != nil {
		t.Fatal(err)
	}

	id := p.Id()

	b, err := getBlogByID(id)
	if err != nil {
		// handle error
		t.Fatal(err.Error())
	}

	fmt.Printf("get blog ok: %#v", b)

	b, err = getBlogByID(id)
	if err != nil {
		// handle error
		t.Fatal(err.Error())
	}
	fmt.Printf("get blog ok: %#v", b)
}

func getBlogByID(id string) (*Blog, error) {
	u := fmt.Sprintf("http://localhost%s/blog?key=%s", listenPort, id)
	println("request url", u)
	resp, err := http.Get(u)
	if err != nil {
		fmt.Printf("http response error:%s\n", err.Error())
		return nil, err
	}

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	if resp.StatusCode != http.StatusOK {
		return nil, errors.New(string(body))
	}
	var b Blog
	err = json.Unmarshal(body, &b)
	return &b, err
}
<commit_msg>Update mongo test<commit_after>package blog

import (
	"fmt"
	"testing"
	"time"

	"github.com/ezbuy/ezorm/db"
)

func init() {
	conf := new(db.MongoConfig)
	conf.DBName = "ezorm"
	conf.MongoDB = "mongodb://127.0.0.1"
	db.Setup(conf)
}

func TestBlogSave(t *testing.T) {
	p := BlogMgr.NewBlog()
	p.Title = "I like ezorm"
	p.Slug = fmt.Sprintf("ezorm_%d", time.Now().Nanosecond())

	_, err := p.Save()
	if err != nil {
		t.Fatal(err)
	}

	id := p.Id()

	b, err := BlogMgr.FindByID(id)
	if err != nil {
		t.Fatal(err.Error())
	}

	fmt.Printf("get blog ok: %#v", b)
}
<|endoftext|>


<commit_before>package main

import (
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"os"
	"testing"
)

func newTestPostServer(expectedBody string, resp string, statusCode int, t *testing.T) *httptest.Server {
	return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		body, _ := ioutil.ReadAll(r.Body)
		flagParams := string(body)
		if flagParams != expectedBody {
			t.Errorf("fetchRource hit wrong endpoint (wanted %v but got %v)", expectedBody, flagParams)
		}
		w.WriteHeader(statusCode)
		w.Write([]byte(flagParams))
	}))
}

func TestRunByTags(t *testing.T) {
	expectedBody := `{"tags":["foo","bar"]}`
	sitesResp := "Post Request Successful"
	ts := newTestPostServer(expectedBody, sitesResp, 200, t)
	defer ts.Close()
	baseURL = ts.URL
	tempOsArgs := os.Args
	os.Args = []string{"rainforest-cli", "-tags=foo,bar", "run"}
	main()
	os.Args = tempOsArgs
}

// func TestRunBySmartID(t *testing.T) {
// 	sitesResp := "Post Request Successful"
// 	ts := newTestServer("/runs.json?tags=foo%2Cbar", sitesResp, 200, t)
// 	defer ts.Close()
// 	baseURL = ts.URL
// 	tempOsArgs := os.Args
// 	os.Args = []string{"rainforest-cli", "-tags=foo,bar", "run"}
// 	main()
// 	os.Args = tempOsArgs
// }
<commit_msg>site_id + smart_folder_id tested<commit_after>package main

import (
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"os"
	"testing"
)

func newTestPostServer(expectedBody string, resp string, statusCode int, t *testing.T) *httptest.Server {
	return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		body, _ := ioutil.ReadAll(r.Body)
		flagParams := string(body)
		if flagParams != expectedBody {
			t.Errorf("fetchRource hit wrong endpoint (wanted %v but got %v)", expectedBody, flagParams)
		}
		w.WriteHeader(statusCode)
		w.Write([]byte(flagParams))
	}))
}

func TestRunByTags(t *testing.T) {
	expectedBody := `{"tags":["foo","bar"]}`
	sitesResp := "Post Request Successful"
	ts := newTestPostServer(expectedBody, sitesResp, 200, t)
	defer ts.Close()
	baseURL = ts.URL
	tempOsArgs := os.Args
	os.Args = []string{"rainforest-cli", "-tags=foo,bar", "run"}
	main()
	os.Args = tempOsArgs
}

func TestRunBySmartFolder(t *testing.T) {
	expectedBody := `{"tags":[""],"smart_folder_id":700}`
	sitesResp := "Post Request Successful"
	ts := newTestPostServer(expectedBody, sitesResp, 200, t)
	defer ts.Close()
	baseURL = ts.URL
	tempOsArgs := os.Args
	os.Args = []string{"rainforest-cli", "-smart_folder_id=700", "run"}
	main()
	os.Args = tempOsArgs
}

func TestRunBySiteId(t *testing.T) {
	expectedBody := `{"tags":[""],"site_id":800}`
	sitesResp := "Post Request Successful"
	ts := newTestPostServer(expectedBody, sitesResp, 200, t)
	defer ts.Close()
	baseURL = ts.URL
	tempOsArgs := os.Args
	os.Args = []string{"rainforest-cli", "-site_id=800", "run"}
	main()
	os.Args = tempOsArgs
}
<|endoftext|>


<commit_before>// Copyright 2020 The LUCI Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package invocations

import (
	"testing"
	"time"

	"github.com/golang/protobuf/ptypes"
	"github.com/golang/protobuf/ptypes/timestamp"

	"go.chromium.org/luci/common/clock/testclock"
	"go.chromium.org/luci/server/span"

	"go.chromium.org/luci/resultdb/internal/spanutil"
	"go.chromium.org/luci/resultdb/internal/testutil"
	pb "go.chromium.org/luci/resultdb/proto/v1"

	. "github.com/smartystreets/goconvey/convey"
)

// historyAccumulator has a method that can be passed as a callback to ByTimestamp.
type historyAccumulator struct {
	invs []ID
	b    spanutil.Buffer
}

func (ia *historyAccumulator) accumulate(inv ID, ts *timestamp.Timestamp) error {
	// We only need the invocationID in these tests.
	_ = ts
	ia.invs = append(ia.invs, inv)
	return nil
}

func newHistoryAccumulator(capacity int) historyAccumulator {
	return historyAccumulator{
		invs: make([]ID, 0, capacity),
	}
}

func TestByTimestamp(t *testing.T) {
	Convey(`ByTimestamp`, t, func() {
		ctx := testutil.SpannerTestContext(t)
		realm := "testproject:bytimestamprealm"

		start := testclock.TestRecentTimeUTC
		middle := start.Add(time.Hour)
		end := middle.Add(time.Hour)

		// Insert some Invocations.
		testutil.MustApply(ctx,
			insertInvocation("first", map[string]interface{}{
				"CreateTime":  start,
				"HistoryTime": start,
				"Realm":       realm,
			}),
			insertInvocation("second", map[string]interface{}{
				"CreateTime":  middle,
				"HistoryTime": middle,
				"Realm":       realm,
			}),
			insertInvocation("secondWrongRealm", map[string]interface{}{
				"CreateTime":  middle,
				"HistoryTime": middle,
				"Realm":       "testproject:wrongrealm",
			}),
			insertInvocation("secondUnindexed", map[string]interface{}{
				"CreateTime": middle,
				// No HistoryTime.
				"Realm": realm,
			}),
			insertInvocation("third", map[string]interface{}{
				"CreateTime":  end,
				"HistoryTime": end,
				"Realm":       realm,
			}),
		)
		ctx, cancel := span.ReadOnlyTransaction(ctx)
		defer cancel()

		beforePB, _ := ptypes.TimestampProto(start.Add(-1 * time.Hour))
		justBeforePB, _ := ptypes.TimestampProto(start.Add(-1 * time.Second))
		startPB, _ := ptypes.TimestampProto(start)
		middlePB, _ := ptypes.TimestampProto(middle)
		afterPB, _ := ptypes.TimestampProto(end.Add(time.Hour))
		muchLaterPB, _ := ptypes.TimestampProto(end.Add(24 * time.Hour))

		Convey(`all but the most recent`, func() {
			ac := newHistoryAccumulator(2)
			err := ByTimestamp(ctx, realm, &pb.TimeRange{Earliest: startPB, Latest: middlePB}, ac.accumulate)
			So(err, ShouldBeNil)
			So(ac.invs, ShouldHaveLength, 2)
			So(string(ac.invs[0]), ShouldEndWith, "second")
			So(string(ac.invs[1]), ShouldEndWith, "first")
		})

		Convey(`all but the oldest`, func() {
			ac := newHistoryAccumulator(2)
			err := ByTimestamp(ctx, realm, &pb.TimeRange{Earliest: middlePB, Latest: afterPB}, ac.accumulate)
			So(err, ShouldBeNil)
			So(ac.invs, ShouldHaveLength, 2)
			So(string(ac.invs[0]), ShouldEndWith, "third")
			So(string(ac.invs[1]), ShouldEndWith, "second")
		})

		Convey(`all results`, func() {
			ac := newHistoryAccumulator(2)
			err := ByTimestamp(ctx, realm, &pb.TimeRange{Earliest: startPB, Latest: afterPB}, ac.accumulate)
			So(err, ShouldBeNil)
			So(ac.invs, ShouldHaveLength, 3)
			So(string(ac.invs[0]), ShouldEndWith, "third")
			So(string(ac.invs[1]), ShouldEndWith, "second")
			So(string(ac.invs[2]), ShouldEndWith, "first")
		})

		Convey(`before first result`, func() {
			ac := newHistoryAccumulator(1)
			err := ByTimestamp(ctx, realm, &pb.TimeRange{Earliest: beforePB, Latest: justBeforePB}, ac.accumulate)
			So(err, ShouldBeNil)
			So(ac.invs, ShouldHaveLength, 0)
		})

		Convey(`after last result`, func() {
			ac := newHistoryAccumulator(1)
			err := ByTimestamp(ctx, realm, &pb.TimeRange{Earliest: afterPB, Latest: muchLaterPB}, ac.accumulate)
			So(err, ShouldBeNil)
			So(ac.invs, ShouldHaveLength, 0)
		})
	})
}
<commit_msg>[resultdb] Disable history test.<commit_after>// Copyright 2020 The LUCI Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package invocations

import (
	"testing"
	"time"

	"github.com/golang/protobuf/ptypes"
	"github.com/golang/protobuf/ptypes/timestamp"

	"go.chromium.org/luci/common/clock/testclock"
	"go.chromium.org/luci/server/span"

	"go.chromium.org/luci/resultdb/internal/spanutil"
	"go.chromium.org/luci/resultdb/internal/testutil"
	pb "go.chromium.org/luci/resultdb/proto/v1"

	. "github.com/smartystreets/goconvey/convey"
)

// historyAccumulator has a method that can be passed as a callback to ByTimestamp.
type historyAccumulator struct {
	invs []ID
	b    spanutil.Buffer
}

func (ia *historyAccumulator) accumulate(inv ID, ts *timestamp.Timestamp) error {
	// We only need the invocationID in these tests.
	_ = ts
	ia.invs = append(ia.invs, inv)
	return nil
}

func newHistoryAccumulator(capacity int) historyAccumulator {
	return historyAccumulator{
		invs: make([]ID, 0, capacity),
	}
}

func TestByTimestamp(t *testing.T) {
	SkipConvey(`ByTimestamp`, t, func() {
		ctx := testutil.SpannerTestContext(t)
		realm := "testproject:bytimestamprealm"

		start := testclock.TestRecentTimeUTC
		middle := start.Add(time.Hour)
		end := middle.Add(time.Hour)

		// Insert some Invocations.
		testutil.MustApply(ctx,
			insertInvocation("first", map[string]interface{}{
				"CreateTime":  start,
				"HistoryTime": start,
				"Realm":       realm,
			}),
			insertInvocation("second", map[string]interface{}{
				"CreateTime":  middle,
				"HistoryTime": middle,
				"Realm":       realm,
			}),
			insertInvocation("secondWrongRealm", map[string]interface{}{
				"CreateTime":  middle,
				"HistoryTime": middle,
				"Realm":       "testproject:wrongrealm",
			}),
			insertInvocation("secondUnindexed", map[string]interface{}{
				"CreateTime": middle,
				// No HistoryTime.
				"Realm": realm,
			}),
			insertInvocation("third", map[string]interface{}{
				"CreateTime":  end,
				"HistoryTime": end,
				"Realm":       realm,
			}),
		)
		ctx, cancel := span.ReadOnlyTransaction(ctx)
		defer cancel()

		beforePB, _ := ptypes.TimestampProto(start.Add(-1 * time.Hour))
		justBeforePB, _ := ptypes.TimestampProto(start.Add(-1 * time.Second))
		startPB, _ := ptypes.TimestampProto(start)
		middlePB, _ := ptypes.TimestampProto(middle)
		afterPB, _ := ptypes.TimestampProto(end.Add(time.Hour))
		muchLaterPB, _ := ptypes.TimestampProto(end.Add(24 * time.Hour))

		Convey(`all but the most recent`, func() {
			ac := newHistoryAccumulator(2)
			err := ByTimestamp(ctx, realm, &pb.TimeRange{Earliest: startPB, Latest: middlePB}, ac.accumulate)
			So(err, ShouldBeNil)
			So(ac.invs, ShouldHaveLength, 2)
			So(string(ac.invs[0]), ShouldEndWith, "second")
			So(string(ac.invs[1]), ShouldEndWith, "first")
		})

		Convey(`all but the oldest`, func() {
			ac := newHistoryAccumulator(2)
			err := ByTimestamp(ctx, realm, &pb.TimeRange{Earliest: middlePB, Latest: afterPB}, ac.accumulate)
			So(err, ShouldBeNil)
			So(ac.invs, ShouldHaveLength, 2)
			So(string(ac.invs[0]), ShouldEndWith, "third")
			So(string(ac.invs[1]), ShouldEndWith, "second")
		})

		Convey(`all results`, func() {
			ac := newHistoryAccumulator(2)
			err := ByTimestamp(ctx, realm, &pb.TimeRange{Earliest: startPB, Latest: afterPB}, ac.accumulate)
			So(err, ShouldBeNil)
			So(ac.invs, ShouldHaveLength, 3)
			So(string(ac.invs[0]), ShouldEndWith, "third")
			So(string(ac.invs[1]), ShouldEndWith, "second")
			So(string(ac.invs[2]), ShouldEndWith, "first")
		})

		Convey(`before first result`, func() {
			ac := newHistoryAccumulator(1)
			err := ByTimestamp(ctx, realm, &pb.TimeRange{Earliest: beforePB, Latest: justBeforePB}, ac.accumulate)
			So(err, ShouldBeNil)
			So(ac.invs, ShouldHaveLength, 0)
		})

		Convey(`after last result`, func() {
			ac := newHistoryAccumulator(1)
			err := ByTimestamp(ctx, realm, &pb.TimeRange{Earliest: afterPB, Latest: muchLaterPB}, ac.accumulate)
			So(err, ShouldBeNil)
			So(ac.invs, ShouldHaveLength, 0)
		})
	})
}
<|endoftext|>


<commit_before>package v2action

import (
	"archive/zip"
	"crypto/sha1"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv2"
	"code.cloudfoundry.org/ykk"
	ignore "github.com/sabhiram/go-gitignore"
	log "github.com/sirupsen/logrus"
)

const (
	DefaultFolderPermissions      = 0755
	DefaultArchiveFilePermissions = 0744
	MaxResourceMatchChunkSize     = 1000
)

type FileChangedError struct {
	Filename string
}

func (e FileChangedError) Error() string {
	return fmt.Sprint("SHA1 mismatch for:", e.Filename)
}

type EmptyDirectoryError struct {
	Path string
}

func (e EmptyDirectoryError) Error() string {
	return fmt.Sprint(e.Path, "is empty")
}

type Resource ccv2.Resource

// GatherArchiveResources returns a list of resources for an archive.
func (actor Actor) GatherArchiveResources(archivePath string) ([]Resource, error) {
	var resources []Resource

	archive, err := os.Open(archivePath)
	if err != nil {
		return nil, err
	}
	defer archive.Close()

	reader, err := actor.newArchiveReader(archive)
	if err != nil {
		return nil, err
	}

	for _, archivedFile := range reader.File {
		resource := Resource{Filename: filepath.ToSlash(archivedFile.Name)}
		if archivedFile.FileInfo().IsDir() {
			resource.Mode = DefaultFolderPermissions
		} else {
			fileReader, err := archivedFile.Open()
			if err != nil {
				return nil, err
			}
			defer fileReader.Close()

			hash := sha1.New()

			_, err = io.Copy(hash, fileReader)
			if err != nil {
				return nil, err
			}

			resource.Mode = DefaultArchiveFilePermissions
			resource.SHA1 = fmt.Sprintf("%x", hash.Sum(nil))
			resource.Size = archivedFile.FileInfo().Size()
		}
		resources = append(resources, resource)
	}
	return resources, nil
}

// GatherDirectoryResources returns a list of resources for a directory.
func (Actor) GatherDirectoryResources(sourceDir string) ([]Resource, error) {
	var (
		resources []Resource
		gitIgnore *ignore.GitIgnore
	)

	pathToCFIgnore := filepath.Join(sourceDir, ".cfignore")
	var gitIgnoreErr error
	if _, err := os.Stat(pathToCFIgnore); !os.IsNotExist(err) {
		gitIgnore, gitIgnoreErr = ignore.CompileIgnoreFileAndLines(pathToCFIgnore, ".cfignore", os.Getenv("CF_TRACE"))
	} else {
		gitIgnore, gitIgnoreErr = ignore.CompileIgnoreLines(os.Getenv("CF_TRACE"))
	}
	if gitIgnoreErr != nil {
		log.Errorln("reading .cfignore file:", gitIgnoreErr)
		return nil, gitIgnoreErr
	}

	walkErr := filepath.Walk(sourceDir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		// if file ignored contine to the next file
		if gitIgnore.MatchesPath(path) {
			return nil
		}

		relPath, err := filepath.Rel(sourceDir, path)
		if err != nil {
			return err
		}

		if relPath == "." {
			return nil
		}

		resource := Resource{
			Filename: filepath.ToSlash(relPath),
		}

		if info.IsDir() {
			resource.Mode = DefaultFolderPermissions
		} else {
			file, err := os.Open(path)
			if err != nil {
				return err
			}
			defer file.Close()

			sum := sha1.New()
			_, err = io.Copy(sum, file)
			if err != nil {
				return err
			}

			resource.Mode = fixMode(info.Mode())
			resource.SHA1 = fmt.Sprintf("%x", sum.Sum(nil))
			resource.Size = info.Size()
		}
		resources = append(resources, resource)
		return nil
	})

	if len(resources) == 0 {
		return nil, EmptyDirectoryError{Path: sourceDir}
	}

	return resources, walkErr
}

// ResourceMatch returns a set of matched resources and unmatched resources in
// the order they were given in allResources.
func (actor Actor) ResourceMatch(allResources []Resource) ([]Resource, []Resource, Warnings, error) {
	resourcesToSend := [][]ccv2.Resource{{}}
	var currentList, sendCount int
	for _, resource := range allResources {
		if resource.Size == 0 {
			continue
		}

		resourcesToSend[currentList] = append(
			resourcesToSend[currentList],
			ccv2.Resource(resource),
		)
		sendCount += 1

		if len(resourcesToSend[currentList]) == MaxResourceMatchChunkSize {
			currentList += 1
			resourcesToSend = append(resourcesToSend, []ccv2.Resource{})
		}
	}

	log.WithFields(log.Fields{
		"total_resources":    len(allResources),
		"resources_to_match": sendCount,
		"chunks":             len(resourcesToSend),
	}).Debug("sending resource match stats")

	matchedCCResources := map[string]ccv2.Resource{}
	var allWarnings Warnings
	for _, chunk := range resourcesToSend {
		if len(chunk) == 0 {
			log.Debug("chunk size 0, stopping resource match requests")
			break
		}

		returnedResources, warnings, err := actor.CloudControllerClient.ResourceMatch(chunk)
		allWarnings = append(allWarnings, warnings...)

		if err != nil {
			log.Errorln("during resource matching", err)
			return nil, nil, allWarnings, err
		}

		for _, resource := range returnedResources {
			matchedCCResources[resource.SHA1] = resource
		}
	}
	log.WithField("matched_resource_count", len(matchedCCResources)).Debug("total number of matched resources")

	var matchedResources, unmatchedResources []Resource
	for _, resource := range allResources {
		if _, ok := matchedCCResources[resource.SHA1]; ok {
			matchedResources = append(matchedResources, resource)
		} else {
			unmatchedResources = append(unmatchedResources, resource)
		}
	}

	return matchedResources, unmatchedResources, allWarnings, nil
}

// ZipArchiveResources zips an archive and a sorted (based on full
// path/filename) list of resources and returns the location. On Windows, the
// filemode for user is forced to be readable and executable.
func (actor Actor) ZipArchiveResources(sourceArchivePath string, filesToInclude []Resource) (string, error) {
	log.WithField("sourceArchive", sourceArchivePath).Info("zipping source files from archive")
	zipFile, err := ioutil.TempFile("", "cf-cli-")
	if err != nil {
		return "", err
	}
	defer zipFile.Close()

	writer := zip.NewWriter(zipFile)
	defer writer.Close()

	source, err := os.Open(sourceArchivePath)
	if err != nil {
		return "", err
	}
	defer source.Close()

	reader, err := actor.newArchiveReader(source)
	if err != nil {
		return "", err
	}

	for _, archiveFile := range reader.File {
		resource, ok := actor.findInResources(archiveFile.Name, filesToInclude)
		if !ok {
			log.WithField("archiveFileName", archiveFile.Name).Debug("skipping file")
			continue
		}

		log.WithField("archiveFileName", archiveFile.Name).Debug("zipping file")
		reader, openErr := archiveFile.Open()
		if openErr != nil {
			log.WithField("archiveFile", archiveFile.Name).Errorln("opening path in dir:", openErr)
			return "", openErr
		}

		err = actor.addFileToZipFromFileSystem(
			resource.Filename, reader, archiveFile.FileInfo(),
			resource.Filename, resource.SHA1, resource.Mode, writer,
		)
		if err != nil {
			log.WithField("archiveFileName", archiveFile.Name).Errorln("zipping file:", err)
			return "", err
		}
	}

	log.WithFields(log.Fields{
		"zip_file_location": zipFile.Name(),
		"zipped_file_count": len(filesToInclude),
	}).Info("zip file created")
	return zipFile.Name(), nil
}

// ZipDirectoryResources zips a directory and a sorted (based on full
// path/filename) list of resources and returns the location. On Windows, the
// filemode for user is forced to be readable and executable.
func (actor Actor) ZipDirectoryResources(sourceDir string, filesToInclude []Resource) (string, error) {
	log.WithField("sourceDir", sourceDir).Info("zipping source files from directory")
	zipFile, err := ioutil.TempFile("", "cf-cli-")
	if err != nil {
		return "", err
	}
	defer zipFile.Close()

	writer := zip.NewWriter(zipFile)
	defer writer.Close()

	for _, resource := range filesToInclude {
		fullPath := filepath.Join(sourceDir, resource.Filename)
		log.WithField("fullPath", fullPath).Debug("zipping file")

		srcFile, err := os.Open(fullPath)
		if err != nil {
			log.WithField("fullPath", fullPath).Errorln("opening path in dir:", err)
			return "", err
		}

		fileInfo, err := srcFile.Stat()
		if err != nil {
			log.WithField("fullPath", fullPath).Errorln("stat error in dir:", err)
			return "", err
		}

		err = actor.addFileToZipFromFileSystem(
			fullPath, srcFile, fileInfo,
			resource.Filename, resource.SHA1, resource.Mode, writer,
		)
		if err != nil {
			log.WithField("fullPath", fullPath).Errorln("zipping file:", err)
			return "", err
		}
	}

	log.WithFields(log.Fields{
		"zip_file_location": zipFile.Name(),
		"zipped_file_count": len(filesToInclude),
	}).Info("zip file created")
	return zipFile.Name(), nil
}

func (Actor) actorToCCResources(resources []Resource) []ccv2.Resource {
	apiResources := make([]ccv2.Resource, 0, len(resources)) // Explicitly done to prevent nils

	for _, resource := range resources {
		apiResources = append(apiResources, ccv2.Resource(resource))
	}

	return apiResources
}

func (Actor) addFileToZipFromFileSystem(
	srcPath string, srcFile io.ReadCloser, fileInfo os.FileInfo,
	destPath string, sha1Sum string, mode os.FileMode, zipFile *zip.Writer,
) error {
	defer srcFile.Close()

	header, err := zip.FileInfoHeader(fileInfo)
	if err != nil {
		log.WithField("srcPath", srcPath).Errorln("getting file info in dir:", err)
		return err
	}

	// An extra '/' indicates that this file is a directory
	if fileInfo.IsDir() && !strings.HasSuffix(destPath, "/") {
		destPath += "/"
	}

	header.Name = destPath
	header.Method = zip.Deflate

	header.SetMode(mode)
	log.WithFields(log.Fields{
		"srcPath":  srcPath,
		"destPath": destPath,
		"mode":     mode,
	}).Debug("setting mode for file")

	destFileWriter, err := zipFile.CreateHeader(header)
	if err != nil {
		log.Errorln("creating header:", err)
		return err
	}

	if !fileInfo.IsDir() {
		sum := sha1.New()

		multi := io.MultiWriter(sum, destFileWriter)
		if _, err := io.Copy(multi, srcFile); err != nil {
			log.WithField("srcPath", srcPath).Errorln("copying data in dir:", err)
			return err
		}

		if currentSum := fmt.Sprintf("%x", sum.Sum(nil)); sha1Sum != currentSum {
			log.WithFields(log.Fields{
				"expected":   sha1Sum,
				"currentSum": currentSum,
			}).Error("setting mode for file")
			return FileChangedError{Filename: srcPath}
		}
	}

	return nil
}

func (Actor) findInResources(path string, filesToInclude []Resource) (Resource, bool) {
	for _, resource := range filesToInclude {
		if resource.Filename == filepath.ToSlash(path) {
			log.WithField("resource", resource.Filename).Debug("found resource in files to include")
			return resource, true
		}
	}

	log.WithField("path", path).Debug("did not find resource in files to include")
	return Resource{}, false
}

func (Actor) newArchiveReader(archive *os.File) (*zip.Reader, error) {
	info, err := archive.Stat()
	if err != nil {
		return nil, err
	}

	return ykk.NewReader(archive, info.Size())
}
<commit_msg>fix windows units<commit_after>package v2action

import (
	"archive/zip"
	"crypto/sha1"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv2"
	"code.cloudfoundry.org/ykk"
	ignore "github.com/sabhiram/go-gitignore"
	log "github.com/sirupsen/logrus"
)

const (
	DefaultFolderPermissions      = 0755
	DefaultArchiveFilePermissions = 0744
	MaxResourceMatchChunkSize     = 1000
)

type FileChangedError struct {
	Filename string
}

func (e FileChangedError) Error() string {
	return fmt.Sprint("SHA1 mismatch for:", e.Filename)
}

type EmptyDirectoryError struct {
	Path string
}

func (e EmptyDirectoryError) Error() string {
	return fmt.Sprint(e.Path, "is empty")
}

type Resource ccv2.Resource

// GatherArchiveResources returns a list of resources for an archive.
func (actor Actor) GatherArchiveResources(archivePath string) ([]Resource, error) {
	var resources []Resource

	archive, err := os.Open(archivePath)
	if err != nil {
		return nil, err
	}
	defer archive.Close()

	reader, err := actor.newArchiveReader(archive)
	if err != nil {
		return nil, err
	}

	for _, archivedFile := range reader.File {
		resource := Resource{Filename: filepath.ToSlash(archivedFile.Name)}
		if archivedFile.FileInfo().IsDir() {
			resource.Mode = DefaultFolderPermissions
		} else {
			fileReader, err := archivedFile.Open()
			if err != nil {
				return nil, err
			}
			defer fileReader.Close()

			hash := sha1.New()

			_, err = io.Copy(hash, fileReader)
			if err != nil {
				return nil, err
			}

			resource.Mode = DefaultArchiveFilePermissions
			resource.SHA1 = fmt.Sprintf("%x", hash.Sum(nil))
			resource.Size = archivedFile.FileInfo().Size()
		}
		resources = append(resources, resource)
	}
	return resources, nil
}

// GatherDirectoryResources returns a list of resources for a directory.
func (Actor) GatherDirectoryResources(sourceDir string) ([]Resource, error) {
	var (
		resources []Resource
		gitIgnore *ignore.GitIgnore
	)

	pathToCFIgnore := filepath.Join(sourceDir, ".cfignore")
	var gitIgnoreErr error
	if _, err := os.Stat(pathToCFIgnore); !os.IsNotExist(err) {
		gitIgnore, gitIgnoreErr = ignore.CompileIgnoreFileAndLines(pathToCFIgnore, ".cfignore")
	} else {
		gitIgnore, gitIgnoreErr = ignore.CompileIgnoreLines()
	}

	if gitIgnoreErr != nil {
		log.Errorln("reading .cfignore file:", gitIgnoreErr)
		return nil, gitIgnoreErr
	}

	walkErr := filepath.Walk(sourceDir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		// if file ignored contine to the next file
		if gitIgnore.MatchesPath(path) {
			return nil
		}

		if path == os.Getenv("CF_TRACE") {
			return nil
		}

		relPath, err := filepath.Rel(sourceDir, path)
		if err != nil {
			return err
		}

		if relPath == "." {
			return nil
		}

		resource := Resource{
			Filename: filepath.ToSlash(relPath),
		}

		if info.IsDir() {
			resource.Mode = DefaultFolderPermissions
		} else {
			file, err := os.Open(path)
			if err != nil {
				return err
			}
			defer file.Close()

			sum := sha1.New()
			_, err = io.Copy(sum, file)
			if err != nil {
				return err
			}

			resource.Mode = fixMode(info.Mode())
			resource.SHA1 = fmt.Sprintf("%x", sum.Sum(nil))
			resource.Size = info.Size()
		}
		resources = append(resources, resource)
		return nil
	})

	if len(resources) == 0 {
		return nil, EmptyDirectoryError{Path: sourceDir}
	}

	return resources, walkErr
}

// ResourceMatch returns a set of matched resources and unmatched resources in
// the order they were given in allResources.
func (actor Actor) ResourceMatch(allResources []Resource) ([]Resource, []Resource, Warnings, error) {
	resourcesToSend := [][]ccv2.Resource{{}}
	var currentList, sendCount int
	for _, resource := range allResources {
		if resource.Size == 0 {
			continue
		}

		resourcesToSend[currentList] = append(
			resourcesToSend[currentList],
			ccv2.Resource(resource),
		)
		sendCount += 1

		if len(resourcesToSend[currentList]) == MaxResourceMatchChunkSize {
			currentList += 1
			resourcesToSend = append(resourcesToSend, []ccv2.Resource{})
		}
	}

	log.WithFields(log.Fields{
		"total_resources":    len(allResources),
		"resources_to_match": sendCount,
		"chunks":             len(resourcesToSend),
	}).Debug("sending resource match stats")

	matchedCCResources := map[string]ccv2.Resource{}
	var allWarnings Warnings
	for _, chunk := range resourcesToSend {
		if len(chunk) == 0 {
			log.Debug("chunk size 0, stopping resource match requests")
			break
		}

		returnedResources, warnings, err := actor.CloudControllerClient.ResourceMatch(chunk)
		allWarnings = append(allWarnings, warnings...)

		if err != nil {
			log.Errorln("during resource matching", err)
			return nil, nil, allWarnings, err
		}

		for _, resource := range returnedResources {
			matchedCCResources[resource.SHA1] = resource
		}
	}
	log.WithField("matched_resource_count", len(matchedCCResources)).Debug("total number of matched resources")

	var matchedResources, unmatchedResources []Resource
	for _, resource := range allResources {
		if _, ok := matchedCCResources[resource.SHA1]; ok {
			matchedResources = append(matchedResources, resource)
		} else {
			unmatchedResources = append(unmatchedResources, resource)
		}
	}

	return matchedResources, unmatchedResources, allWarnings, nil
}

// ZipArchiveResources zips an archive and a sorted (based on full
// path/filename) list of resources and returns the location. On Windows, the
// filemode for user is forced to be readable and executable.
func (actor Actor) ZipArchiveResources(sourceArchivePath string, filesToInclude []Resource) (string, error) {
	log.WithField("sourceArchive", sourceArchivePath).Info("zipping source files from archive")
	zipFile, err := ioutil.TempFile("", "cf-cli-")
	if err != nil {
		return "", err
	}
	defer zipFile.Close()

	writer := zip.NewWriter(zipFile)
	defer writer.Close()

	source, err := os.Open(sourceArchivePath)
	if err != nil {
		return "", err
	}
	defer source.Close()

	reader, err := actor.newArchiveReader(source)
	if err != nil {
		return "", err
	}

	for _, archiveFile := range reader.File {
		resource, ok := actor.findInResources(archiveFile.Name, filesToInclude)
		if !ok {
			log.WithField("archiveFileName", archiveFile.Name).Debug("skipping file")
			continue
		}

		log.WithField("archiveFileName", archiveFile.Name).Debug("zipping file")
		reader, openErr := archiveFile.Open()
		if openErr != nil {
			log.WithField("archiveFile", archiveFile.Name).Errorln("opening path in dir:", openErr)
			return "", openErr
		}

		err = actor.addFileToZipFromFileSystem(
			resource.Filename, reader, archiveFile.FileInfo(),
			resource.Filename, resource.SHA1, resource.Mode, writer,
		)
		if err != nil {
			log.WithField("archiveFileName", archiveFile.Name).Errorln("zipping file:", err)
			return "", err
		}
	}

	log.WithFields(log.Fields{
		"zip_file_location": zipFile.Name(),
		"zipped_file_count": len(filesToInclude),
	}).Info("zip file created")
	return zipFile.Name(), nil
}

// ZipDirectoryResources zips a directory and a sorted (based on full
// path/filename) list of resources and returns the location. On Windows, the
// filemode for user is forced to be readable and executable.
func (actor Actor) ZipDirectoryResources(sourceDir string, filesToInclude []Resource) (string, error) {
	log.WithField("sourceDir", sourceDir).Info("zipping source files from directory")
	zipFile, err := ioutil.TempFile("", "cf-cli-")
	if err != nil {
		return "", err
	}
	defer zipFile.Close()

	writer := zip.NewWriter(zipFile)
	defer writer.Close()

	for _, resource := range filesToInclude {
		fullPath := filepath.Join(sourceDir, resource.Filename)
		log.WithField("fullPath", fullPath).Debug("zipping file")

		srcFile, err := os.Open(fullPath)
		if err != nil {
			log.WithField("fullPath", fullPath).Errorln("opening path in dir:", err)
			return "", err
		}

		fileInfo, err := srcFile.Stat()
		if err != nil {
			log.WithField("fullPath", fullPath).Errorln("stat error in dir:", err)
			return "", err
		}

		err = actor.addFileToZipFromFileSystem(
			fullPath, srcFile, fileInfo,
			resource.Filename, resource.SHA1, resource.Mode, writer,
		)
		if err != nil {
			log.WithField("fullPath", fullPath).Errorln("zipping file:", err)
			return "", err
		}
	}

	log.WithFields(log.Fields{
		"zip_file_location": zipFile.Name(),
		"zipped_file_count": len(filesToInclude),
	}).Info("zip file created")
	return zipFile.Name(), nil
}

func (Actor) actorToCCResources(resources []Resource) []ccv2.Resource {
	apiResources := make([]ccv2.Resource, 0, len(resources)) // Explicitly done to prevent nils

	for _, resource := range resources {
		apiResources = append(apiResources, ccv2.Resource(resource))
	}

	return apiResources
}

func (Actor) addFileToZipFromFileSystem(
	srcPath string, srcFile io.ReadCloser, fileInfo os.FileInfo,
	destPath string, sha1Sum string, mode os.FileMode, zipFile *zip.Writer,
) error {
	defer srcFile.Close()

	header, err := zip.FileInfoHeader(fileInfo)
	if err != nil {
		log.WithField("srcPath", srcPath).Errorln("getting file info in dir:", err)
		return err
	}

	// An extra '/' indicates that this file is a directory
	if fileInfo.IsDir() && !strings.HasSuffix(destPath, "/") {
		destPath += "/"
	}

	header.Name = destPath
	header.Method = zip.Deflate

	header.SetMode(mode)
	log.WithFields(log.Fields{
		"srcPath":  srcPath,
		"destPath": destPath,
		"mode":     mode,
	}).Debug("setting mode for file")

	destFileWriter, err := zipFile.CreateHeader(header)
	if err != nil {
		log.Errorln("creating header:", err)
		return err
	}

	if !fileInfo.IsDir() {
		sum := sha1.New()

		multi := io.MultiWriter(sum, destFileWriter)
		if _, err := io.Copy(multi, srcFile); err != nil {
			log.WithField("srcPath", srcPath).Errorln("copying data in dir:", err)
			return err
		}

		if currentSum := fmt.Sprintf("%x", sum.Sum(nil)); sha1Sum != currentSum {
			log.WithFields(log.Fields{
				"expected":   sha1Sum,
				"currentSum": currentSum,
			}).Error("setting mode for file")
			return FileChangedError{Filename: srcPath}
		}
	}

	return nil
}

func (Actor) findInResources(path string, filesToInclude []Resource) (Resource, bool) {
	for _, resource := range filesToInclude {
		if resource.Filename == filepath.ToSlash(path) {
			log.WithField("resource", resource.Filename).Debug("found resource in files to include")
			return resource, true
		}
	}

	log.WithField("path", path).Debug("did not find resource in files to include")
	return Resource{}, false
}

func (Actor) newArchiveReader(archive *os.File) (*zip.Reader, error) {
	info, err := archive.Stat()
	if err != nil {
		return nil, err
	}

	return ykk.NewReader(archive, info.Size())
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"path/filepath"

	"strings"

	jsonnet "github.com/google/go-jsonnet"
)

func main() {
	// Read all Jsonnet files in the hooks dir.
	files, err := ioutil.ReadDir("./hooks")
	if err != nil {
		log.Fatal(err)
	}
	for _, file := range files {
		filename := file.Name()
		if !strings.HasSuffix(filename, ".jsonnet") {
			continue
		}

		hookname := strings.TrimSuffix(filename, ".jsonnet")
		filepath := filepath.Join("./hooks", filename)
		filedata, err := ioutil.ReadFile(filepath)
		if err != nil {
			log.Fatalf("can't read %q: %v", filepath, err)
		}
		hookcode := string(filedata)

		// Serve the Jsonnet file as a webhook.
		http.HandleFunc("/"+hookname, func(w http.ResponseWriter, r *http.Request) {
			// Read POST body as jsonnet input.
			if r.Method != http.MethodPost {
				http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
				return
			}
			body, err := ioutil.ReadAll(r.Body)
			if err != nil {
				http.Error(w, "Can't read body", http.StatusInternalServerError)
				return
			}

			// Evaluate Jsonnet hook, passing request body as a top-level argument.
			vm := jsonnet.MakeVM()
			vm.TLACode("request", string(body))
			result, err := vm.EvaluateSnippet(filename, hookcode)
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
			w.Header().Set("Content-Type", "application/json")
			fmt.Fprint(w, result)
		})
	}

	log.Fatal(http.ListenAndServe(":80", nil))
}
<commit_msg>jsonnetd: Look in current working dir for hooks.<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"

	"strings"

	jsonnet "github.com/google/go-jsonnet"
)

func main() {
	// Read all Jsonnet files in the working dir.
	files, err := ioutil.ReadDir(".")
	if err != nil {
		log.Fatal(err)
	}
	for _, file := range files {
		filename := file.Name()
		if !strings.HasSuffix(filename, ".jsonnet") {
			continue
		}

		hookname := strings.TrimSuffix(filename, ".jsonnet")
		filedata, err := ioutil.ReadFile(filename)
		if err != nil {
			log.Fatalf("can't read %q: %v", filename, err)
		}
		hookcode := string(filedata)

		// Serve the Jsonnet file as a webhook.
		http.HandleFunc("/"+hookname, func(w http.ResponseWriter, r *http.Request) {
			// Read POST body as jsonnet input.
			if r.Method != http.MethodPost {
				http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
				return
			}
			body, err := ioutil.ReadAll(r.Body)
			if err != nil {
				http.Error(w, "Can't read body", http.StatusInternalServerError)
				return
			}

			// Evaluate Jsonnet hook, passing request body as a top-level argument.
			vm := jsonnet.MakeVM()
			vm.TLACode("request", string(body))
			result, err := vm.EvaluateSnippet(filename, hookcode)
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
			w.Header().Set("Content-Type", "application/json")
			fmt.Fprint(w, result)
		})
	}

	log.Fatal(http.ListenAndServe(":80", nil))
}
<|endoftext|>