<commit_before>//
// Copyright 2017, Sander van Harmelen
//
// 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 gitlab

import (
	"fmt"
	"net/url"
)

// BranchesService handles communication with the branch related methods
// of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/branches.html
type BranchesService struct {
	client *Client
}

// Branch represents a GitLab branch.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/branches.html
type Branch struct {
	Commit             *Commit `json:"commit"`
	Name               string  `json:"name"`
	Protected          bool    `json:"protected"`
	Merged             bool    `json:"merged"`
	Default            bool    `json:"default"`
	DevelopersCanPush  bool    `json:"developers_can_push"`
	DevelopersCanMerge bool    `json:"developers_can_merge"`
}

func (b Branch) String() string {
	return Stringify(b)
}

// ListBranchesOptions represents the available ListBranches() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/branches.html#list-repository-branches
type ListBranchesOptions struct {
	ListOptions
	Search string `url:"search,omitempty" json:"search,omitempty"`
}

// ListBranches gets a list of repository branches from a project, sorted by
// name alphabetically.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/branches.html#list-repository-branches
func (s *BranchesService) ListBranches(pid interface{}, opts *ListBranchesOptions, options ...OptionFunc) ([]*Branch, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/branches", pathEscape(project))

	req, err := s.client.NewRequest("GET", u, opts, options)
	if err != nil {
		return nil, nil, err
	}

	var b []*Branch
	resp, err := s.client.Do(req, &b)
	if err != nil {
		return nil, resp, err
	}

	return b, resp, err
}

// GetBranch gets a single project repository branch.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/branches.html#get-single-repository-branch
func (s *BranchesService) GetBranch(pid interface{}, branch string, options ...OptionFunc) (*Branch, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/branches/%s", pathEscape(project), url.PathEscape(branch))

	req, err := s.client.NewRequest("GET", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	b := new(Branch)
	resp, err := s.client.Do(req, b)
	if err != nil {
		return nil, resp, err
	}

	return b, resp, err
}

// ProtectBranchOptions represents the available ProtectBranch() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/branches.html#protect-repository-branch
type ProtectBranchOptions struct {
	DevelopersCanPush  *bool `url:"developers_can_push,omitempty" json:"developers_can_push,omitempty"`
	DevelopersCanMerge *bool `url:"developers_can_merge,omitempty" json:"developers_can_merge,omitempty"`
}

// ProtectBranch protects a single project repository branch. This is an
// idempotent function, protecting an already protected repository branch
// still returns a 200 OK status code.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/branches.html#protect-repository-branch
func (s *BranchesService) ProtectBranch(pid interface{}, branch string, opts *ProtectBranchOptions, options ...OptionFunc) (*Branch, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/branches/%s/protect", pathEscape(project), url.PathEscape(branch))

	req, err := s.client.NewRequest("PUT", u, opts, options)
	if err != nil {
		return nil, nil, err
	}

	b := new(Branch)
	resp, err := s.client.Do(req, b)
	if err != nil {
		return nil, resp, err
	}

	return b, resp, err
}

// UnprotectBranch unprotects a single project repository branch. This is an
// idempotent function, unprotecting an already unprotected repository branch
// still returns a 200 OK status code.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/branches.html#unprotect-repository-branch
func (s *BranchesService) UnprotectBranch(pid interface{}, branch string, options ...OptionFunc) (*Branch, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/branches/%s/unprotect", pathEscape(project), url.PathEscape(branch))

	req, err := s.client.NewRequest("PUT", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	b := new(Branch)
	resp, err := s.client.Do(req, b)
	if err != nil {
		return nil, resp, err
	}

	return b, resp, err
}

// CreateBranchOptions represents the available CreateBranch() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/branches.html#create-repository-branch
type CreateBranchOptions struct {
	Branch *string `url:"branch,omitempty" json:"branch,omitempty"`
	Ref    *string `url:"ref,omitempty" json:"ref,omitempty"`
}

// CreateBranch creates branch from commit SHA or existing branch.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/branches.html#create-repository-branch
func (s *BranchesService) CreateBranch(pid interface{}, opt *CreateBranchOptions, options ...OptionFunc) (*Branch, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/branches", pathEscape(project))

	req, err := s.client.NewRequest("POST", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	b := new(Branch)
	resp, err := s.client.Do(req, b)
	if err != nil {
		return nil, resp, err
	}

	return b, resp, err
}

// DeleteBranch deletes an existing branch.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/branches.html#delete-repository-branch
func (s *BranchesService) DeleteBranch(pid interface{}, branch string, options ...OptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/branches/%s", pathEscape(project), url.PathEscape(branch))

	req, err := s.client.NewRequest("DELETE", u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}

// DeleteMergedBranches deletes all branches that are merged into the project's default branch.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/branches.html#delete-merged-branches
func (s *BranchesService) DeleteMergedBranches(pid interface{}, options ...OptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/merged_branches", pathEscape(project))

	req, err := s.client.NewRequest("DELETE", u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}
<commit_msg>Make 'Search' field to be a pointer<commit_after>//
// Copyright 2017, Sander van Harmelen
//
// 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 gitlab

import (
	"fmt"
	"net/url"
)

// BranchesService handles communication with the branch related methods
// of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/branches.html
type BranchesService struct {
	client *Client
}

// Branch represents a GitLab branch.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/branches.html
type Branch struct {
	Commit             *Commit `json:"commit"`
	Name               string  `json:"name"`
	Protected          bool    `json:"protected"`
	Merged             bool    `json:"merged"`
	Default            bool    `json:"default"`
	DevelopersCanPush  bool    `json:"developers_can_push"`
	DevelopersCanMerge bool    `json:"developers_can_merge"`
}

func (b Branch) String() string {
	return Stringify(b)
}

// ListBranchesOptions represents the available ListBranches() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/branches.html#list-repository-branches
type ListBranchesOptions struct {
	ListOptions
	Search *string `url:"search,omitempty" json:"search,omitempty"`
}

// ListBranches gets a list of repository branches from a project, sorted by
// name alphabetically.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/branches.html#list-repository-branches
func (s *BranchesService) ListBranches(pid interface{}, opts *ListBranchesOptions, options ...OptionFunc) ([]*Branch, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/branches", pathEscape(project))

	req, err := s.client.NewRequest("GET", u, opts, options)
	if err != nil {
		return nil, nil, err
	}

	var b []*Branch
	resp, err := s.client.Do(req, &b)
	if err != nil {
		return nil, resp, err
	}

	return b, resp, err
}

// GetBranch gets a single project repository branch.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/branches.html#get-single-repository-branch
func (s *BranchesService) GetBranch(pid interface{}, branch string, options ...OptionFunc) (*Branch, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/branches/%s", pathEscape(project), url.PathEscape(branch))

	req, err := s.client.NewRequest("GET", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	b := new(Branch)
	resp, err := s.client.Do(req, b)
	if err != nil {
		return nil, resp, err
	}

	return b, resp, err
}

// ProtectBranchOptions represents the available ProtectBranch() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/branches.html#protect-repository-branch
type ProtectBranchOptions struct {
	DevelopersCanPush  *bool `url:"developers_can_push,omitempty" json:"developers_can_push,omitempty"`
	DevelopersCanMerge *bool `url:"developers_can_merge,omitempty" json:"developers_can_merge,omitempty"`
}

// ProtectBranch protects a single project repository branch. This is an
// idempotent function, protecting an already protected repository branch
// still returns a 200 OK status code.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/branches.html#protect-repository-branch
func (s *BranchesService) ProtectBranch(pid interface{}, branch string, opts *ProtectBranchOptions, options ...OptionFunc) (*Branch, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/branches/%s/protect", pathEscape(project), url.PathEscape(branch))

	req, err := s.client.NewRequest("PUT", u, opts, options)
	if err != nil {
		return nil, nil, err
	}

	b := new(Branch)
	resp, err := s.client.Do(req, b)
	if err != nil {
		return nil, resp, err
	}

	return b, resp, err
}

// UnprotectBranch unprotects a single project repository branch. This is an
// idempotent function, unprotecting an already unprotected repository branch
// still returns a 200 OK status code.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/branches.html#unprotect-repository-branch
func (s *BranchesService) UnprotectBranch(pid interface{}, branch string, options ...OptionFunc) (*Branch, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/branches/%s/unprotect", pathEscape(project), url.PathEscape(branch))

	req, err := s.client.NewRequest("PUT", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	b := new(Branch)
	resp, err := s.client.Do(req, b)
	if err != nil {
		return nil, resp, err
	}

	return b, resp, err
}

// CreateBranchOptions represents the available CreateBranch() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/branches.html#create-repository-branch
type CreateBranchOptions struct {
	Branch *string `url:"branch,omitempty" json:"branch,omitempty"`
	Ref    *string `url:"ref,omitempty" json:"ref,omitempty"`
}

// CreateBranch creates branch from commit SHA or existing branch.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/branches.html#create-repository-branch
func (s *BranchesService) CreateBranch(pid interface{}, opt *CreateBranchOptions, options ...OptionFunc) (*Branch, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/branches", pathEscape(project))

	req, err := s.client.NewRequest("POST", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	b := new(Branch)
	resp, err := s.client.Do(req, b)
	if err != nil {
		return nil, resp, err
	}

	return b, resp, err
}

// DeleteBranch deletes an existing branch.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/branches.html#delete-repository-branch
func (s *BranchesService) DeleteBranch(pid interface{}, branch string, options ...OptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/branches/%s", pathEscape(project), url.PathEscape(branch))

	req, err := s.client.NewRequest("DELETE", u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}

// DeleteMergedBranches deletes all branches that are merged into the project's default branch.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/branches.html#delete-merged-branches
func (s *BranchesService) DeleteMergedBranches(pid interface{}, options ...OptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("projects/%s/repository/merged_branches", pathEscape(project))

	req, err := s.client.NewRequest("DELETE", u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}
<|endoftext|>


<commit_before>// Brunobot notifies a Discord channel when Professional Dota 2 games are live.
// This should be run via cron every minute to ensure only one notification.
package main

import (
    "bytes"
    "encoding/json"
    "flag"
    "fmt"
    "net/http"
    "os"
    "strconv"
    "strings"
    "time"
)

// Payload to be sent to discord
type Payload struct {
    Content string `json:"content"`
}

// Config file
type Configuration struct {
    Teams     []string
    Webhook   string
    Whitelist bool
}

// JSON Struct to store data from the game api
type API struct {
    Matches []struct {
        Team1 struct {
            TeamName string `json:"team_name"`
        } `json:"team1"`
        Link          string `json:"link"`
        StarttimeUnix string `json:"starttime_unix"`
        Team2         struct {
            TeamName string `json:"team_name"`
        } `json:"team2"`
    } `json:"matches"`
}

func getMatches(url string, target interface{}) error {
    req, err := http.NewRequest("GET", url, nil)
    req.Header.Set("Content-Type", "application/json")

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()

    return json.NewDecoder(resp.Body).Decode(target)
}

func sendNotification(text string, webhook string) {
    payload := Payload{text}
    blob, err := json.Marshal(payload)
    if err != nil {
        panic(err)
    }
    req, err := http.NewRequest("POST", webhook, bytes.NewBuffer(blob))
    req.Header.Set("Content-Type", "application/json")

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()
}

func usage() {
    fmt.Println("Usage: brunobot --config=/path/to/config.json")
    os.Exit(1)
}

func whitelisted(team1 string, team2 string, whitelist []string) bool {
    a := strings.ToLower(team1)
    b := strings.ToLower(team2)
    for _, team := range whitelist {
        team = strings.ToLower(team)
        if a == team || b == team {
            return true
        }
    }
    return false
}

// Webhook must be set in configuration
func loadConfiguration(path string, config *Configuration) {
    file, err := os.Open(path)
    if err != nil {
        fmt.Println("Configuration file not found.. exiting")
        usage()
    }
    decoder := json.NewDecoder(file)

    err = decoder.Decode(config)
    if err != nil {
        panic(err)
    }

    if config.Webhook == "" {
        fmt.Println("Webhook is not set.. exiting")
        usage()
    }
}

func main() {
    configFile := flag.String("config", "", "Configuration file")
    flag.Parse()
    if *configFile == "" {
        fmt.Println("Configuration file not found.. exiting")
        usage()
    }
    configuration := &Configuration{}
    loadConfiguration(*configFile, configuration)

    api := new(API)
    getMatches("http://dailydota2.com/match-api", api)
    if len(api.Matches) != 0 {
        for _, match := range api.Matches {
            team1 := match.Team1.TeamName
            team2 := match.Team2.TeamName
            send := false

            if configuration.Whitelist {
                send = whitelisted(team1, team2, configuration.Teams)
            } else {
                send = true
            }

            // Prevent multiple notifications by checking time difference
            startTime, _ := strconv.Atoi(match.StarttimeUnix)
            delta := time.Now().Unix() - int64(startTime)
            if send && delta <= 60 {
                // Link points to stream but the stats page is nicer, so replace
                // with the stats URL instead
                url := strings.Replace(match.Link, "match", "stats", 1)
                notification := fmt.Sprintf("%s vs %s :: %s", team1, team2, url)
                sendNotification(notification, configuration.Webhook)
            }
        }
    }
}
<commit_msg>Simplify code with gofmt<commit_after>// Brunobot notifies a Discord channel when Professional Dota 2 games are live.
// This should be run via cron every minute to ensure only one notification.
package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"
)

// Payload to be sent to discord
type Payload struct {
	Content string `json:"content"`
}

// Config file
type Configuration struct {
	Teams     []string
	Webhook   string
	Whitelist bool
}

// JSON Struct to store data from the game api
type API struct {
	Matches []struct {
		Team1 struct {
			TeamName string `json:"team_name"`
		} `json:"team1"`
		Link          string `json:"link"`
		StarttimeUnix string `json:"starttime_unix"`
		Team2         struct {
			TeamName string `json:"team_name"`
		} `json:"team2"`
	} `json:"matches"`
}

func getMatches(url string, target interface{}) error {
	req, err := http.NewRequest("GET", url, nil)
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()

	return json.NewDecoder(resp.Body).Decode(target)
}

func sendNotification(text string, webhook string) {
	payload := Payload{text}
	blob, err := json.Marshal(payload)
	if err != nil {
		panic(err)
	}
	req, err := http.NewRequest("POST", webhook, bytes.NewBuffer(blob))
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
}

func usage() {
	fmt.Println("Usage: brunobot --config=/path/to/config.json")
	os.Exit(1)
}

func whitelisted(team1 string, team2 string, whitelist []string) bool {
	a := strings.ToLower(team1)
	b := strings.ToLower(team2)
	for _, team := range whitelist {
		team = strings.ToLower(team)
		if a == team || b == team {
			return true
		}
	}
	return false
}

// Webhook must be set in configuration
func loadConfiguration(path string, config *Configuration) {
	file, err := os.Open(path)
	if err != nil {
		fmt.Println("Configuration file not found.. exiting")
		usage()
	}
	decoder := json.NewDecoder(file)

	err = decoder.Decode(config)
	if err != nil {
		panic(err)
	}

	if config.Webhook == "" {
		fmt.Println("Webhook is not set.. exiting")
		usage()
	}
}

func main() {
	configFile := flag.String("config", "", "Configuration file")
	flag.Parse()
	if *configFile == "" {
		fmt.Println("Configuration file not found.. exiting")
		usage()
	}
	configuration := &Configuration{}
	loadConfiguration(*configFile, configuration)

	api := new(API)
	getMatches("http://dailydota2.com/match-api", api)
	if len(api.Matches) != 0 {
		for _, match := range api.Matches {
			team1 := match.Team1.TeamName
			team2 := match.Team2.TeamName
			send := false

			if configuration.Whitelist {
				send = whitelisted(team1, team2, configuration.Teams)
			} else {
				send = true
			}

			// Prevent multiple notifications by checking time difference
			startTime, _ := strconv.Atoi(match.StarttimeUnix)
			delta := time.Now().Unix() - int64(startTime)
			if send && delta <= 60 {
				// Link points to stream but the stats page is nicer, so replace
				// with the stats URL instead
				url := strings.Replace(match.Link, "match", "stats", 1)
				notification := fmt.Sprintf("%s vs %s :: %s", team1, team2, url)
				sendNotification(notification, configuration.Webhook)
			}
		}
	}
}
<|endoftext|>


<commit_before>package api

import (
	"encoding/base64"
	"encoding/json"
	"net/http"
	"strconv"
	"strings"

	"github.com/MyHomeworkSpace/api-server/data"
	"github.com/MyHomeworkSpace/api-server/email"
	"github.com/labstack/echo"
)

type FeedbacksResponse struct {
	Status    string          `json:"status"`
	Feedbacks []data.Feedback `json:"feedbacks"`
}

type UserCountResponse struct {
	Status string `json:"status"`
	Count  int    `json:"count"`
}

func routeAdminGetAllFeedback(w http.ResponseWriter, r *http.Request, ec echo.Context, c RouteContext) {
	rows, err := DB.Query("SELECT feedback.id, feedback.userId, feedback.type, feedback.text, feedback.screenshot, feedback.timestamp, users.name, users.email FROM feedback INNER JOIN users ON feedback.userId = users.id")
	if err != nil {
		ErrorLog_LogError("getting all feedback", err)
		ec.JSON(http.StatusInternalServerError, ErrorResponse{"error", "internal_server_error"})
		return
	}

	feedbacks := []data.Feedback{}
	for rows.Next() {
		resp := data.Feedback{-1, -1, "", "", "", "", "", false}
		var screenshot string
		rows.Scan(&resp.ID, &resp.UserID, &resp.Type, &resp.Text, &screenshot, &resp.Timestamp, &resp.UserName, &resp.UserEmail)
		if screenshot != "" {
			resp.HasScreenshot = true
		}
		feedbacks = append(feedbacks, resp)
	}

	ec.JSON(http.StatusOK, FeedbacksResponse{"ok", feedbacks})
}

func routeAdminGetFeedbackScreenshot(w http.ResponseWriter, r *http.Request, ec echo.Context, c RouteContext) {
	id, err := strconv.Atoi(ec.Param("id"))
	if err != nil {
		ec.JSON(http.StatusBadRequest, ErrorResponse{"error", "invalid_paramas"})
		return
	}

	rows, err := DB.Query("SELECT screenshot FROM feedback WHERE id = ?", id)
	if err != nil {
		ErrorLog_LogError("getting screenshot", err)
		ec.JSON(http.StatusInternalServerError, ErrorResponse{"error", "internal_server_error"})
		return
	}

	if !rows.Next() {
		ec.JSON(http.StatusNotFound, ErrorResponse{"error", "not_found"})
		return
	}

	var screenshot64 string
	err = rows.Scan(&screenshot64)
	if err != nil {
		ErrorLog_LogError("getting screenshot", err)
		ec.JSON(http.StatusInternalServerError, ErrorResponse{"error", "internal_server_error"})
		return
	}

	rows.Close()

	if screenshot64 == "" {
		ec.JSON(http.StatusNotFound, ErrorResponse{"error", "no_screenshot"})
		return
	}

	screenshot64 = strings.Replace(screenshot64, "data:image/png;base64,", "", 1)

	screenshot, err := base64.StdEncoding.DecodeString(screenshot64)
	if err != nil {
		ErrorLog_LogError("getting screenshot", err)
		ec.JSON(http.StatusInternalServerError, ErrorResponse{"error", "internal_server_error"})
		return
	}

	ec.Blob(http.StatusOK, "image/png;base64", screenshot)
}

func routeAdminGetUserCount(w http.ResponseWriter, r *http.Request, ec echo.Context, c RouteContext) {
	rows, err := DB.Query("SELECT COUNT(*) FROM users")
	if err != nil {
		ErrorLog_LogError("getting user count", err)
		ec.JSON(http.StatusInternalServerError, ErrorResponse{"error", "internal_server_error"})
		return
	}

	rows.Next()
	count := -1
	rows.Scan(&count)

	ec.JSON(http.StatusOK, UserCountResponse{"ok", count})
}

func routeAdminSendEmail(w http.ResponseWriter, r *http.Request, ec echo.Context, c RouteContext) {
	if ec.FormValue("template") == "" || ec.FormValue("data") == "" {
		ec.JSON(http.StatusBadRequest, ErrorResponse{"error", "missing_params"})
		return
	}

	data := map[string]interface{}{}
	err := json.Unmarshal([]byte(ec.FormValue("data")), &data)
	if err != nil {
		ec.JSON(http.StatusBadRequest, ErrorResponse{"error", "invalid_params"})
		return
	}

	err = email.Send("", c.User, ec.FormValue("template"), data)
	if err != nil {
		ErrorLog_LogError("sending email", err)
		ec.JSON(http.StatusInternalServerError, ErrorResponse{"error", "internal_server_error"})
		return
	}

	ec.JSON(http.StatusOK, StatusResponse{"ok"})
}
<commit_msg>allow setting a user ID for sendEmail endpoint<commit_after>package api

import (
	"encoding/base64"
	"encoding/json"
	"net/http"
	"strconv"
	"strings"

	"github.com/MyHomeworkSpace/api-server/data"
	"github.com/MyHomeworkSpace/api-server/email"
	"github.com/labstack/echo"
)

type FeedbacksResponse struct {
	Status    string          `json:"status"`
	Feedbacks []data.Feedback `json:"feedbacks"`
}

type UserCountResponse struct {
	Status string `json:"status"`
	Count  int    `json:"count"`
}

func routeAdminGetAllFeedback(w http.ResponseWriter, r *http.Request, ec echo.Context, c RouteContext) {
	rows, err := DB.Query("SELECT feedback.id, feedback.userId, feedback.type, feedback.text, feedback.screenshot, feedback.timestamp, users.name, users.email FROM feedback INNER JOIN users ON feedback.userId = users.id")
	if err != nil {
		ErrorLog_LogError("getting all feedback", err)
		ec.JSON(http.StatusInternalServerError, ErrorResponse{"error", "internal_server_error"})
		return
	}

	feedbacks := []data.Feedback{}
	for rows.Next() {
		resp := data.Feedback{-1, -1, "", "", "", "", "", false}
		var screenshot string
		rows.Scan(&resp.ID, &resp.UserID, &resp.Type, &resp.Text, &screenshot, &resp.Timestamp, &resp.UserName, &resp.UserEmail)
		if screenshot != "" {
			resp.HasScreenshot = true
		}
		feedbacks = append(feedbacks, resp)
	}

	ec.JSON(http.StatusOK, FeedbacksResponse{"ok", feedbacks})
}

func routeAdminGetFeedbackScreenshot(w http.ResponseWriter, r *http.Request, ec echo.Context, c RouteContext) {
	id, err := strconv.Atoi(ec.Param("id"))
	if err != nil {
		ec.JSON(http.StatusBadRequest, ErrorResponse{"error", "invalid_paramas"})
		return
	}

	rows, err := DB.Query("SELECT screenshot FROM feedback WHERE id = ?", id)
	if err != nil {
		ErrorLog_LogError("getting screenshot", err)
		ec.JSON(http.StatusInternalServerError, ErrorResponse{"error", "internal_server_error"})
		return
	}

	if !rows.Next() {
		ec.JSON(http.StatusNotFound, ErrorResponse{"error", "not_found"})
		return
	}

	var screenshot64 string
	err = rows.Scan(&screenshot64)
	if err != nil {
		ErrorLog_LogError("getting screenshot", err)
		ec.JSON(http.StatusInternalServerError, ErrorResponse{"error", "internal_server_error"})
		return
	}

	rows.Close()

	if screenshot64 == "" {
		ec.JSON(http.StatusNotFound, ErrorResponse{"error", "no_screenshot"})
		return
	}

	screenshot64 = strings.Replace(screenshot64, "data:image/png;base64,", "", 1)

	screenshot, err := base64.StdEncoding.DecodeString(screenshot64)
	if err != nil {
		ErrorLog_LogError("getting screenshot", err)
		ec.JSON(http.StatusInternalServerError, ErrorResponse{"error", "internal_server_error"})
		return
	}

	ec.Blob(http.StatusOK, "image/png;base64", screenshot)
}

func routeAdminGetUserCount(w http.ResponseWriter, r *http.Request, ec echo.Context, c RouteContext) {
	rows, err := DB.Query("SELECT COUNT(*) FROM users")
	if err != nil {
		ErrorLog_LogError("getting user count", err)
		ec.JSON(http.StatusInternalServerError, ErrorResponse{"error", "internal_server_error"})
		return
	}

	rows.Next()
	count := -1
	rows.Scan(&count)

	ec.JSON(http.StatusOK, UserCountResponse{"ok", count})
}

func routeAdminSendEmail(w http.ResponseWriter, r *http.Request, ec echo.Context, c RouteContext) {
	if ec.FormValue("template") == "" || ec.FormValue("data") == "" {
		ec.JSON(http.StatusBadRequest, ErrorResponse{"error", "missing_params"})
		return
	}

	user := c.User

	if ec.FormValue("userID") != "" {
		userID, err := strconv.Atoi(ec.FormValue("userID"))
		if err != nil {
			ec.JSON(http.StatusBadRequest, ErrorResponse{"error", "invalid_params"})
			return
		}

		userStruct, err := data.GetUserByID(userID)
		if err == data.ErrNotFound {
			ec.JSON(http.StatusBadRequest, ErrorResponse{"error", "invalid_params"})
			return
		} else if err != nil {
			ErrorLog_LogError("sending email", err)
			ec.JSON(http.StatusInternalServerError, ErrorResponse{"error", "internal_server_error"})
			return
		}

		user = &userStruct
	}

	data := map[string]interface{}{}
	err := json.Unmarshal([]byte(ec.FormValue("data")), &data)
	if err != nil {
		ec.JSON(http.StatusBadRequest, ErrorResponse{"error", "invalid_params"})
		return
	}

	err = email.Send("", user, ec.FormValue("template"), data)
	if err != nil {
		ErrorLog_LogError("sending email", err)
		ec.JSON(http.StatusInternalServerError, ErrorResponse{"error", "internal_server_error"})
		return
	}

	ec.JSON(http.StatusOK, StatusResponse{"ok"})
}
<|endoftext|>


<commit_before>package db

import (
	"database/sql"
	"errors"
	"fmt"
	"strings"
	"time"

	"github.com/muesli/polly/api/config"

	"github.com/lib/pq"
	"github.com/muesli/cache2go"
	uuid "github.com/nu7hatch/gouuid"
)

var (
	pgDB   *sql.DB
	pgConn config.PostgreSQLConnection

	proposalsCache = cache2go.Cache("track")
	usersCache     = cache2go.Cache("user")

	// ErrInvalidID is the error returned when encountering an invalid database ID
	ErrInvalidID = errors.New("Invalid id")
)

// SetupPostgres sets the db configuration
func SetupPostgres(pc config.PostgreSQLConnection) {
	pgConn = pc
}

// GetDatabase connects to the database on first run and returns the existing
// connection on further calls
func GetDatabase() *sql.DB {
	if pgDB == nil {
		var err error
		pgDB, err = sql.Open("postgres", pgConn.Marshal())
		if err != nil {
			panic(err)
		}

		tables := []string{
			`CREATE TABLE IF NOT EXISTS users
				(
				  id          	bigserial 	PRIMARY KEY,
				  username    	text      	NOT NULL,
				  password		text		NOT NULL,
				  about       	text,
				  email       	text		NOT NULL,
				  activated   	bool		DEFAULT false,
				  authtoken   	text      	NOT NULL,
				  CONSTRAINT  	uk_username	UNIQUE (username),
				  CONSTRAINT  	uk_email 	UNIQUE (email)
				)`,
			`CREATE TABLE IF NOT EXISTS proposals
				(
				  id          	bigserial 	PRIMARY KEY,
				  userid      	bigserial 	NOT NULL,
				  title       	text      	NOT NULL,
				  description	text      	NOT NULL,
				  recipient		text		NOT NULL,
				  value			int			NOT NULL,
				  ends			timestamp	NOT NULL,
				  votes	      	int       	DEFAULT 0,
				  moderated     bool        DEFAULT false,
				  CONSTRAINT  	fk_user		FOREIGN KEY (userid) REFERENCES users (id) MATCH SIMPLE ON UPDATE CASCADE ON DELETE CASCADE
				)`,
			`CREATE TABLE IF NOT EXISTS votes
				(
				  id          	bigserial			PRIMARY KEY,
				  userid    	bigserial			NOT NULL,
				  proposalid   	bigserial			NOT NULL,
				  vote			bool				NOT NULL,
				  CONSTRAINT  	uk_user_proposal	UNIQUE (userid, proposalid),
				  CONSTRAINT  	fk_user				FOREIGN KEY (userid) REFERENCES users (id) MATCH SIMPLE ON UPDATE CASCADE ON DELETE CASCADE,
				  CONSTRAINT  	fk_proposal			FOREIGN KEY (proposalid) REFERENCES proposals (id) MATCH SIMPLE ON UPDATE CASCADE ON DELETE CASCADE
				)`,
		}

		// FIXME: add IF NOT EXISTS to CREATE INDEX statements (coming in v9.5)
		// See: http://www.postgresql.org/docs/devel/static/sql-createindex.html
		indexes := []string{
			`CREATE INDEX idx_users_email ON users(email)`,
			`CREATE INDEX idx_proposals_moderated ON proposals(moderated)`,
			`CREATE INDEX idx_proposals_value ON proposals(value)`,
			`CREATE INDEX idx_proposals_userid ON proposals(userid)`,
			`CREATE INDEX idx_proposals_ends ON proposals(ends)`,
			`CREATE INDEX idx_votes_userid ON votes(userid)`,
			`CREATE INDEX idx_votes_proposalid ON votes(proposalid)`,
		}

		for _, v := range tables {
			fmt.Println("Creating table:", v)
			_, err = pgDB.Exec(v)
			if err != nil {
				panic(err)
			}
		}
		for _, v := range indexes {
			fmt.Println("Creating index:", v)
			_, err = pgDB.Exec(v)
			if err != nil && strings.Index(err.Error(), "already exists") < 0 {
				fmt.Println("Error:", err)
			}
		}

	}

	return pgDB
}

// WipeDatabase drops all database tables - use carefully!
func WipeDatabase() {
	// Commented out to prevent accidental usage

	/*
		drops := []string{
			`DROP TABLE votes`,
			`DROP TABLE proposals`,
			`DROP TABLE users`,
		}

		for _, v := range drops {
			fmt.Println("Dropping table:", v)
			_, err := pgDB.Exec(v)
			if err != nil {
				panic(err)
			}
		}
	*/
}

func init() {
	fmt.Println("db.init")
	initCaches()

	negativeInf := time.Time{}
	positiveInf, _ := time.Parse("2006", "3000")

	pq.EnableInfinityTs(negativeInf, positiveInf)
}

// UUID returns a new unique identifier
func UUID() (string, error) {
	u, err := uuid.NewV4()
	if err != nil {
		return "", err
	}

	uuid := strings.Join(strings.Split(u.String(), "-"), "")
	return uuid, nil
}

func initCaches() {
	usersCache.SetAddedItemCallback(func(item *cache2go.CacheItem) {
		// fmt.Println("Now in users-cache:", item.Key().(string), item.Data().(*DbUser).Username)
	})
	usersCache.SetAboutToDeleteItemCallback(func(item *cache2go.CacheItem) {
		// fmt.Println("Deleting from users-cache:", item.Key().(string), item.Data().(*DbUser).Username, item.CreatedOn())
	})
	usersCache.SetDataLoader(func(key interface{}, args ...interface{}) *cache2go.CacheItem {
		if len(args) == 1 {
			if context, ok := args[0].(*PollyContext); ok {
				user, err := context.LoadUserByID(key.(int64))
				if err != nil {
					fmt.Println("usersCache ERROR for key", key, ":", err)
					return nil
				}

				entry := cache2go.CreateCacheItem(key, 10*time.Minute, &user)
				return &entry
			}
		}
		fmt.Println("Got no APIContext passed in")
		return nil
	})

	proposalsCache.SetAddedItemCallback(func(item *cache2go.CacheItem) {
		// fmt.Println("Now in proposals-cache:", item.Key().(string), item.Data().(*DbProposal).Title)
	})
	proposalsCache.SetAboutToDeleteItemCallback(func(item *cache2go.CacheItem) {
		// fmt.Println("Deleting from proposals-cache:", item.Key().(string), item.Data().(*DbProposal).Title, item.CreatedOn())
	})
	proposalsCache.SetDataLoader(func(key interface{}, args ...interface{}) *cache2go.CacheItem {
		if len(args) == 1 {
			if context, ok := args[0].(*PollyContext); ok {
				proposal, err := context.LoadProposalByID(key.(int64))
				if err != nil {
					fmt.Println("proposalsCache ERROR for key", key, ":", err)
					return nil
				}

				entry := cache2go.CreateCacheItem(key, 10*time.Minute, &proposal)
				return &entry
			}
		}
		fmt.Println("Got no APIContext passed in")
		return nil
	})
}
<commit_msg>Adapted to latest cache2go API<commit_after>package db

import (
	"database/sql"
	"errors"
	"fmt"
	"strings"
	"time"

	"github.com/muesli/polly/api/config"

	"github.com/lib/pq"
	"github.com/muesli/cache2go"
	uuid "github.com/nu7hatch/gouuid"
)

var (
	pgDB   *sql.DB
	pgConn config.PostgreSQLConnection

	proposalsCache = cache2go.Cache("track")
	usersCache     = cache2go.Cache("user")

	// ErrInvalidID is the error returned when encountering an invalid database ID
	ErrInvalidID = errors.New("Invalid id")
)

// SetupPostgres sets the db configuration
func SetupPostgres(pc config.PostgreSQLConnection) {
	pgConn = pc
}

// GetDatabase connects to the database on first run and returns the existing
// connection on further calls
func GetDatabase() *sql.DB {
	if pgDB == nil {
		var err error
		pgDB, err = sql.Open("postgres", pgConn.Marshal())
		if err != nil {
			panic(err)
		}

		tables := []string{
			`CREATE TABLE IF NOT EXISTS users
				(
				  id          	bigserial 	PRIMARY KEY,
				  username    	text      	NOT NULL,
				  password		text		NOT NULL,
				  about       	text,
				  email       	text		NOT NULL,
				  activated   	bool		DEFAULT false,
				  authtoken   	text      	NOT NULL,
				  CONSTRAINT  	uk_username	UNIQUE (username),
				  CONSTRAINT  	uk_email 	UNIQUE (email)
				)`,
			`CREATE TABLE IF NOT EXISTS proposals
				(
				  id          	bigserial 	PRIMARY KEY,
				  userid      	bigserial 	NOT NULL,
				  title       	text      	NOT NULL,
				  description	text      	NOT NULL,
				  recipient		text		NOT NULL,
				  value			int			NOT NULL,
				  ends			timestamp	NOT NULL,
				  votes	      	int       	DEFAULT 0,
				  moderated     bool        DEFAULT false,
				  CONSTRAINT  	fk_user		FOREIGN KEY (userid) REFERENCES users (id) MATCH SIMPLE ON UPDATE CASCADE ON DELETE CASCADE
				)`,
			`CREATE TABLE IF NOT EXISTS votes
				(
				  id          	bigserial			PRIMARY KEY,
				  userid    	bigserial			NOT NULL,
				  proposalid   	bigserial			NOT NULL,
				  vote			bool				NOT NULL,
				  CONSTRAINT  	uk_user_proposal	UNIQUE (userid, proposalid),
				  CONSTRAINT  	fk_user				FOREIGN KEY (userid) REFERENCES users (id) MATCH SIMPLE ON UPDATE CASCADE ON DELETE CASCADE,
				  CONSTRAINT  	fk_proposal			FOREIGN KEY (proposalid) REFERENCES proposals (id) MATCH SIMPLE ON UPDATE CASCADE ON DELETE CASCADE
				)`,
		}

		// FIXME: add IF NOT EXISTS to CREATE INDEX statements (coming in v9.5)
		// See: http://www.postgresql.org/docs/devel/static/sql-createindex.html
		indexes := []string{
			`CREATE INDEX idx_users_email ON users(email)`,
			`CREATE INDEX idx_proposals_moderated ON proposals(moderated)`,
			`CREATE INDEX idx_proposals_value ON proposals(value)`,
			`CREATE INDEX idx_proposals_userid ON proposals(userid)`,
			`CREATE INDEX idx_proposals_ends ON proposals(ends)`,
			`CREATE INDEX idx_votes_userid ON votes(userid)`,
			`CREATE INDEX idx_votes_proposalid ON votes(proposalid)`,
		}

		for _, v := range tables {
			fmt.Println("Creating table:", v)
			_, err = pgDB.Exec(v)
			if err != nil {
				panic(err)
			}
		}
		for _, v := range indexes {
			fmt.Println("Creating index:", v)
			_, err = pgDB.Exec(v)
			if err != nil && strings.Index(err.Error(), "already exists") < 0 {
				fmt.Println("Error:", err)
			}
		}

	}

	return pgDB
}

// WipeDatabase drops all database tables - use carefully!
func WipeDatabase() {
	// Commented out to prevent accidental usage

	/*
		drops := []string{
			`DROP TABLE votes`,
			`DROP TABLE proposals`,
			`DROP TABLE users`,
		}

		for _, v := range drops {
			fmt.Println("Dropping table:", v)
			_, err := pgDB.Exec(v)
			if err != nil {
				panic(err)
			}
		}
	*/
}

func init() {
	fmt.Println("db.init")
	initCaches()

	negativeInf := time.Time{}
	positiveInf, _ := time.Parse("2006", "3000")

	pq.EnableInfinityTs(negativeInf, positiveInf)
}

// UUID returns a new unique identifier
func UUID() (string, error) {
	u, err := uuid.NewV4()
	if err != nil {
		return "", err
	}

	uuid := strings.Join(strings.Split(u.String(), "-"), "")
	return uuid, nil
}

func initCaches() {
	usersCache.SetAddedItemCallback(func(item *cache2go.CacheItem) {
		// fmt.Println("Now in users-cache:", item.Key().(string), item.Data().(*DbUser).Username)
	})
	usersCache.SetAboutToDeleteItemCallback(func(item *cache2go.CacheItem) {
		// fmt.Println("Deleting from users-cache:", item.Key().(string), item.Data().(*DbUser).Username, item.CreatedOn())
	})
	usersCache.SetDataLoader(func(key interface{}, args ...interface{}) *cache2go.CacheItem {
		if len(args) == 1 {
			if context, ok := args[0].(*PollyContext); ok {
				user, err := context.LoadUserByID(key.(int64))
				if err != nil {
					fmt.Println("usersCache ERROR for key", key, ":", err)
					return nil
				}

				entry := cache2go.NewCacheItem(key, 10*time.Minute, &user)
				return entry
			}
		}
		fmt.Println("Got no APIContext passed in")
		return nil
	})

	proposalsCache.SetAddedItemCallback(func(item *cache2go.CacheItem) {
		// fmt.Println("Now in proposals-cache:", item.Key().(string), item.Data().(*DbProposal).Title)
	})
	proposalsCache.SetAboutToDeleteItemCallback(func(item *cache2go.CacheItem) {
		// fmt.Println("Deleting from proposals-cache:", item.Key().(string), item.Data().(*DbProposal).Title, item.CreatedOn())
	})
	proposalsCache.SetDataLoader(func(key interface{}, args ...interface{}) *cache2go.CacheItem {
		if len(args) == 1 {
			if context, ok := args[0].(*PollyContext); ok {
				proposal, err := context.LoadProposalByID(key.(int64))
				if err != nil {
					fmt.Println("proposalsCache ERROR for key", key, ":", err)
					return nil
				}

				entry := cache2go.NewCacheItem(key, 10*time.Minute, &proposal)
				return entry
			}
		}
		fmt.Println("Got no APIContext passed in")
		return nil
	})
}
<|endoftext|>


<commit_before>// Copyright 2013 Prometheus Team
// 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 web

import (
	"code.google.com/p/gorest"
	"flag"
	"fmt"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/exp"
	"github.com/prometheus/prometheus/web/api"
	"github.com/prometheus/prometheus/web/blob"
	"html/template"
	"log"
	"net/http"
	"net/http/pprof"
)

// Commandline flags.
var (
	listenAddress  = flag.String("listenAddress", ":9090", "Address to listen on for web interface.")
	useLocalAssets = flag.Bool("useLocalAssets", false, "Read assets/templates from file instead of binary.")
)

type WebService struct {
	StatusHandler  *StatusHandler
	MetricsHandler *api.MetricsService
}

func (w WebService) ServeForever() error {
	gorest.RegisterService(w.MetricsHandler)

	// TODO(julius): This will need to be rewritten once the exp package provides
	// the coarse mux behaviors via a wrapper function.
	exp.Handle("/debug/pprof/", http.HandlerFunc(pprof.Index))
	exp.Handle("/debug/pprof/cmdline", http.HandlerFunc(pprof.Cmdline))
	exp.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile))
	exp.Handle("/debug/pprof/symbol", http.HandlerFunc(pprof.Symbol))

	exp.Handle("/", w.StatusHandler)
	exp.HandleFunc("/graph", graphHandler)

	exp.Handle("/api/", gorest.Handle())
	exp.Handle("/metrics.json", prometheus.DefaultHandler)
	if *useLocalAssets {
		exp.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("web/static"))))
	} else {
		exp.Handle("/static/", http.StripPrefix("/static/", new(blob.Handler)))
	}

	log.Printf("listening on %s", *listenAddress)

	return http.ListenAndServe(*listenAddress, exp.DefaultCoarseMux)
}

func getTemplate(name string) (t *template.Template, err error) {
	if *useLocalAssets {
		return template.ParseFiles("web/templates/_base.html", fmt.Sprintf("web/templates/%s.html", name))
	}

	t = template.New("_base")

	file, err := blob.GetFile(blob.TemplateFiles, "_base.html")
	if err != nil {
		log.Printf("Could not read base template: %s", err)
		return nil, err
	}
	t.Parse(string(file))

	file, err = blob.GetFile(blob.TemplateFiles, name+".html")
	if err != nil {
		log.Printf("Could not read %s template: %s", name, err)
		return nil, err
	}
	t.Parse(string(file))

	return
}

func executeTemplate(w http.ResponseWriter, name string, data interface{}) {
	tpl, err := getTemplate(name)
	if err != nil {
		log.Printf("Error preparing layout template: %s", err)
		return
	}
	err = tpl.Execute(w, data)
	if err != nil {
		log.Printf("Error executing template: %s", err)
	}
}
<commit_msg>Web handler returns 404 for favicon requests<commit_after>// Copyright 2013 Prometheus Team
// 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 web

import (
	"code.google.com/p/gorest"
	"flag"
	"fmt"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/exp"
	"github.com/prometheus/prometheus/web/api"
	"github.com/prometheus/prometheus/web/blob"
	"html/template"
	"log"
	"net/http"
	"net/http/pprof"
)

// Commandline flags.
var (
	listenAddress  = flag.String("listenAddress", ":9090", "Address to listen on for web interface.")
	useLocalAssets = flag.Bool("useLocalAssets", false, "Read assets/templates from file instead of binary.")
)

type WebService struct {
	StatusHandler  *StatusHandler
	MetricsHandler *api.MetricsService
}

func (w WebService) ServeForever() error {
	gorest.RegisterService(w.MetricsHandler)

	exp.Handle("/favicon.ico", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, "", 404)
	}))

	// TODO(julius): This will need to be rewritten once the exp package provides
	// the coarse mux behaviors via a wrapper function.
	exp.Handle("/debug/pprof/", http.HandlerFunc(pprof.Index))
	exp.Handle("/debug/pprof/cmdline", http.HandlerFunc(pprof.Cmdline))
	exp.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile))
	exp.Handle("/debug/pprof/symbol", http.HandlerFunc(pprof.Symbol))

	exp.Handle("/", w.StatusHandler)
	exp.HandleFunc("/graph", graphHandler)

	exp.Handle("/api/", gorest.Handle())
	exp.Handle("/metrics.json", prometheus.DefaultHandler)
	if *useLocalAssets {
		exp.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("web/static"))))
	} else {
		exp.Handle("/static/", http.StripPrefix("/static/", new(blob.Handler)))
	}

	log.Printf("listening on %s", *listenAddress)

	return http.ListenAndServe(*listenAddress, exp.DefaultCoarseMux)
}

func getTemplate(name string) (t *template.Template, err error) {
	if *useLocalAssets {
		return template.ParseFiles("web/templates/_base.html", fmt.Sprintf("web/templates/%s.html", name))
	}

	t = template.New("_base")

	file, err := blob.GetFile(blob.TemplateFiles, "_base.html")
	if err != nil {
		log.Printf("Could not read base template: %s", err)
		return nil, err
	}
	t.Parse(string(file))

	file, err = blob.GetFile(blob.TemplateFiles, name+".html")
	if err != nil {
		log.Printf("Could not read %s template: %s", name, err)
		return nil, err
	}
	t.Parse(string(file))

	return
}

func executeTemplate(w http.ResponseWriter, name string, data interface{}) {
	tpl, err := getTemplate(name)
	if err != nil {
		log.Printf("Error preparing layout template: %s", err)
		return
	}
	err = tpl.Execute(w, data)
	if err != nil {
		log.Printf("Error executing template: %s", err)
	}
}
<|endoftext|>


<commit_before>// Package bus_test runs full end-to-end tests of the bus system by
// running the loader against a small subset of live data and hitting
// the API, checking for sane results. Most settings will be read
// from the environment like the normal application, but
// $BUS_GTFS_URLS and $BUS_ROUTE_FILTER will be overidden by
// the tests.
package bus_test

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httptest"
	"net/url"
	"os"
	"testing"
	"time"

	"github.com/brnstz/bus/api"
	"github.com/brnstz/bus/internal/conf"
	"github.com/brnstz/bus/internal/etc"
	"github.com/brnstz/bus/loader"
	"github.com/kelseyhightower/envconfig"
)

var (
	serverURL string
)

// TestMain initializes/loads the database and starts an HTTP server to test
// against.
func TestMain(m *testing.M) {
	var err error

	err = envconfig.Process("bus", &conf.DB)
	if err != nil {
		log.Fatal(err)
	}

	err = envconfig.Process("bus", &conf.Loader)
	if err != nil {
		log.Fatal(err)
	}

	err = envconfig.Process("bus", &conf.API)
	if err != nil {
		log.Fatal(err)
	}

	// Load the just subway and Brooklyn bus files
	conf.Loader.GTFSURLs = []string{
		"http://web.mta.info/developers/data/nyct/subway/google_transit.zip",
		"http://web.mta.info/developers/data/nyct/bus/google_transit_brooklyn.zip",
	}

	// Filter on a few routes for our tests
	conf.Loader.RouteFilter = []string{
		"G", "L", "B62", "B43", "B32",
	}

	etc.DBConn = etc.MustDB()

	loader.LoadOnce()

	server := httptest.NewServer(api.NewHandler())
	defer server.Close()
	serverURL = server.URL

	os.Exit(m.Run())
}

type departure struct {
	Desc string
	Time time.Time
}

type stopResponse []struct {
	RouteID   string `json:"route_id"`
	StopName  string `json:"stop_name"`
	Scheduled []departure
	Live      []departure
}

func getJSON(v interface{}, u string) error {
	resp, err := http.Get(u)
	if err != nil {
		return err
	}

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

	err = json.Unmarshal(b, v)
	if err != nil {
		return err
	}

	return nil
}

func TestScheduledSubway(t *testing.T) {
	var res stopResponse
	var err error

	params := url.Values{}
	now := time.Now()

	expectedStop := "Greenpoint Av"
	expectedRoute := "G"

	// Manhattan Av. and Greenpoint Av. in Brooklyn
	params.Set("lat", "40.730202")
	params.Set("lon", "-73.9564682")
	params.Set("miles", "0.1")
	params.Set("filter", "subway")

	err = getJSON(&res, serverURL+"/api/v1/stops?"+params.Encode())
	if err != nil {
		t.Fatal("can't get API response for G train test", err)
	}

	if len(res) != 2 {
		t.Fatalf("expected %v results but got %v", 2, len(res))
	}

	// Check each result
	for _, v := range res {
		if v.StopName != expectedStop {
			t.Errorf("expected %v stop_name but got %v", expectedStop, v.StopName)
		}

		if v.RouteID != expectedRoute {
			t.Errorf("expected %v route_id but got %v", expectedRoute, v.RouteID)
		}

		if len(v.Scheduled) < 1 {
			t.Errorf("expected at least one scheduled departure but got none in %#v", v)
		}

		// Check that scheduled times are in the future
		for _, d := range v.Scheduled {
			if d.Time.Before(now) {
				t.Errorf("expected scheduled time %v would be after or equal to %v but it was not", v.Scheduled, now)
			}
		}
	}
}

func TestLiveSubway(t *testing.T) {
	var res stopResponse
	var err error

	params := url.Values{}
	now := time.Now()

	expectedStop := "Bedford Av"
	expectedRoute := "L"

	// Bedford Av. and N. 7th St. in Brooklyn
	params.Set("lat", "40.717304")
	params.Set("lon", "-73.956872")
	params.Set("miles", "0.01")

	err = getJSON(&res, serverURL+"/api/v1/stops?"+params.Encode())
	if err != nil {
		t.Fatal("can't get API response for L train test", err)
	}

	if len(res) != 2 {
		t.Fatalf("expected %v results but got %v", 2, len(res))
	}

	// Check each result
	for _, v := range res {
		if v.StopName != expectedStop {
			t.Errorf("expected %v stop_name but got %v", expectedStop, v.StopName)
		}

		if v.RouteID != expectedRoute {
			t.Errorf("expected %v route_id but got %v", expectedRoute, v.RouteID)
		}

		if len(v.Live) < 1 {
			t.Errorf("expected at least one live departure but got none in %#v", v)
		}

		// Check that live times are in the future
		for _, d := range v.Live {
			if d.Time.Before(now) {
				t.Errorf("expected live time %v would be after or equal to %v but it was not", v.Live, now)
			}
		}
	}
}

func TestLiveBus(t *testing.T) {
	var res stopResponse
	var err error

	params := url.Values{}

	// Jackson Av. and 11th St. in Queens
	params.Set("lat", "40.7422511")
	params.Set("lon", "-73.9515471")
	params.Set("miles", "0.1")

	err = getJSON(&res, serverURL+"/api/v1/stops?"+params.Encode())
	if err != nil {
		t.Fatal("can't get API response for B62, B32 bus test", err)
	}

	// We should get results for both B62 and B32
	if len(res) != 4 {
		t.Fatalf("expected %v results but got %v", 4, len(res))
	}

	// Check each result
	for _, v := range res {

		// FIXME: some bus routes won't always have live departures, may need to
		// pick different bus or make this a warning
		/*
			if len(v.Live) < 1 {
				t.Errorf("expected at least one live departure but got none in %#v", v)
			}
		*/

		// Check that description field is filled in
		for _, d := range v.Live {
			if len(d.Desc) < 1 {
				t.Errorf("empty description identified in live departure in %#v", v)
			}
		}
	}

}

func TestScheduledBus(t *testing.T) {
	var res stopResponse
	var err error

	params := url.Values{}
	now := time.Now()

	expectedStop := "BOX ST/MANHATTAN AV"
	expectedRoute := "B43"

	// Box St. and Manhattan Av. in Brooklyn
	params.Set("lat", "40.7373215")
	params.Set("lon", "-73.9563212")
	params.Set("miles", "0.1")

	err = getJSON(&res, serverURL+"/api/v1/stops?"+params.Encode())
	if err != nil {
		t.Fatal("can't get API response for B43 bus test", err)
	}

	// Check each result
	for _, v := range res {
		if v.StopName != expectedStop {
			t.Errorf("expected %v stop_name but got %v", expectedStop, v.StopName)
		}

		if v.RouteID != expectedRoute {
			t.Errorf("expected %v route_id but got %v", expectedRoute, v.RouteID)
		}

		// Check that scheduled times are in the future
		for _, d := range v.Scheduled {
			if d.Time.Before(now) {
				t.Errorf("expected scheduled time %v would be after or equal to %v but it was not", v.Scheduled, now)
			}
		}
	}
}
<commit_msg>give 5 minute window when comparing to live results<commit_after>// Package bus_test runs full end-to-end tests of the bus system by
// running the loader against a small subset of live data and hitting
// the API, checking for sane results. Most settings will be read
// from the environment like the normal application, but
// $BUS_GTFS_URLS and $BUS_ROUTE_FILTER will be overidden by
// the tests.
package bus_test

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httptest"
	"net/url"
	"os"
	"testing"
	"time"

	"github.com/brnstz/bus/api"
	"github.com/brnstz/bus/internal/conf"
	"github.com/brnstz/bus/internal/etc"
	"github.com/brnstz/bus/loader"
	"github.com/kelseyhightower/envconfig"
)

var (
	serverURL string
)

// TestMain initializes/loads the database and starts an HTTP server to test
// against.
func TestMain(m *testing.M) {
	var err error

	err = envconfig.Process("bus", &conf.DB)
	if err != nil {
		log.Fatal(err)
	}

	err = envconfig.Process("bus", &conf.Loader)
	if err != nil {
		log.Fatal(err)
	}

	err = envconfig.Process("bus", &conf.API)
	if err != nil {
		log.Fatal(err)
	}

	// Load the just subway and Brooklyn bus files
	conf.Loader.GTFSURLs = []string{
		"http://web.mta.info/developers/data/nyct/subway/google_transit.zip",
		"http://web.mta.info/developers/data/nyct/bus/google_transit_brooklyn.zip",
	}

	// Filter on a few routes for our tests
	conf.Loader.RouteFilter = []string{
		"G", "L", "B62", "B43", "B32",
	}

	etc.DBConn = etc.MustDB()

	loader.LoadOnce()

	server := httptest.NewServer(api.NewHandler())
	defer server.Close()
	serverURL = server.URL

	os.Exit(m.Run())
}

type departure struct {
	Desc string
	Time time.Time
}

type stopResponse []struct {
	RouteID   string `json:"route_id"`
	StopName  string `json:"stop_name"`
	Scheduled []departure
	Live      []departure
}

func getJSON(v interface{}, u string) error {
	resp, err := http.Get(u)
	if err != nil {
		return err
	}

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

	err = json.Unmarshal(b, v)
	if err != nil {
		return err
	}

	return nil
}

func TestScheduledSubway(t *testing.T) {
	var res stopResponse
	var err error

	params := url.Values{}
	now := time.Now().Add(-time.Minute * 5)

	expectedStop := "Greenpoint Av"
	expectedRoute := "G"

	// Manhattan Av. and Greenpoint Av. in Brooklyn
	params.Set("lat", "40.730202")
	params.Set("lon", "-73.9564682")
	params.Set("miles", "0.1")
	params.Set("filter", "subway")

	err = getJSON(&res, serverURL+"/api/v1/stops?"+params.Encode())
	if err != nil {
		t.Fatal("can't get API response for G train test", err)
	}

	if len(res) != 2 {
		t.Fatalf("expected %v results but got %v", 2, len(res))
	}

	// Check each result
	for _, v := range res {
		if v.StopName != expectedStop {
			t.Errorf("expected %v stop_name but got %v", expectedStop, v.StopName)
		}

		if v.RouteID != expectedRoute {
			t.Errorf("expected %v route_id but got %v", expectedRoute, v.RouteID)
		}

		if len(v.Scheduled) < 1 {
			t.Errorf("expected at least one scheduled departure but got none in %#v", v)
		}

		// Check that scheduled times are in the future
		for _, d := range v.Scheduled {
			if d.Time.Before(now) {
				t.Errorf("expected scheduled time %v would be after or equal to %v but it was not", v.Scheduled, now)
			}
		}
	}
}

func TestLiveSubway(t *testing.T) {
	var res stopResponse
	var err error

	params := url.Values{}
	now := time.Now().Add(-time.Minute * 5)

	expectedStop := "Bedford Av"
	expectedRoute := "L"

	// Bedford Av. and N. 7th St. in Brooklyn
	params.Set("lat", "40.717304")
	params.Set("lon", "-73.956872")
	params.Set("miles", "0.01")

	err = getJSON(&res, serverURL+"/api/v1/stops?"+params.Encode())
	if err != nil {
		t.Fatal("can't get API response for L train test", err)
	}

	if len(res) != 2 {
		t.Fatalf("expected %v results but got %v", 2, len(res))
	}

	// Check each result
	for _, v := range res {
		if v.StopName != expectedStop {
			t.Errorf("expected %v stop_name but got %v", expectedStop, v.StopName)
		}

		if v.RouteID != expectedRoute {
			t.Errorf("expected %v route_id but got %v", expectedRoute, v.RouteID)
		}

		if len(v.Live) < 1 {
			t.Errorf("expected at least one live departure but got none in %#v", v)
		}

		// Check that live times are in the future
		for _, d := range v.Live {
			if d.Time.Before(now) {
				t.Errorf("expected live time %v would be after or equal to %v but it was not", v.Live, now)
			}
		}
	}
}

func TestLiveBus(t *testing.T) {
	var res stopResponse
	var err error

	params := url.Values{}

	// Jackson Av. and 11th St. in Queens
	params.Set("lat", "40.7422511")
	params.Set("lon", "-73.9515471")
	params.Set("miles", "0.1")

	err = getJSON(&res, serverURL+"/api/v1/stops?"+params.Encode())
	if err != nil {
		t.Fatal("can't get API response for B62, B32 bus test", err)
	}

	// We should get results for both B62 and B32
	if len(res) != 4 {
		t.Fatalf("expected %v results but got %v", 4, len(res))
	}

	// Check each result
	for _, v := range res {

		// FIXME: some bus routes won't always have live departures, may need to
		// pick different bus or make this a warning
		/*
			if len(v.Live) < 1 {
				t.Errorf("expected at least one live departure but got none in %#v", v)
			}
		*/

		// Check that description field is filled in
		for _, d := range v.Live {
			if len(d.Desc) < 1 {
				t.Errorf("empty description identified in live departure in %#v", v)
			}
		}
	}

}

func TestScheduledBus(t *testing.T) {
	var res stopResponse
	var err error

	params := url.Values{}
	now := time.Now().Add(-time.Minute * 5)

	expectedStop := "BOX ST/MANHATTAN AV"
	expectedRoute := "B43"

	// Box St. and Manhattan Av. in Brooklyn
	params.Set("lat", "40.7373215")
	params.Set("lon", "-73.9563212")
	params.Set("miles", "0.1")

	err = getJSON(&res, serverURL+"/api/v1/stops?"+params.Encode())
	if err != nil {
		t.Fatal("can't get API response for B43 bus test", err)
	}

	// Check each result
	for _, v := range res {
		if v.StopName != expectedStop {
			t.Errorf("expected %v stop_name but got %v", expectedStop, v.StopName)
		}

		if v.RouteID != expectedRoute {
			t.Errorf("expected %v route_id but got %v", expectedRoute, v.RouteID)
		}

		// Check that scheduled times are in the future
		for _, d := range v.Scheduled {
			if d.Time.Before(now) {
				t.Errorf("expected scheduled time %v would be after or equal to %v but it was not", v.Scheduled, now)
			}
		}
	}
}
<|endoftext|>


<commit_before>// BlockByHeightRawResponse returns the raw data from the api call.
// Copyright 2016 Factom Foundation
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

package factom

import (
	"encoding/json"
	"fmt"
)

type JStruct struct {
	data []byte
}

func (e *JStruct) MarshalJSON() ([]byte, error) {
	return e.data, nil
}

func (e *JStruct) UnmarshalJSON(b []byte) error {
	e.data = b
	return nil
}

type BlockByHeightRawResponse struct {
	//TODO: implement all of the blocks as proper structures

	DBlock  *JStruct `json:"dblock,omitempty"`
	ABlock  *JStruct `json:"ablock,omitempty"`
	FBlock  *JStruct `json:"fblock,omitempty"`
	ECBlock *JStruct `json:"ecblock,omitempty"`

	RawData string `json:"rawdata,omitempty"`
}

func (f *BlockByHeightRawResponse) String() string {
	var s string
	if f.DBlock != nil {
		j, _ := f.DBlock.MarshalJSON()
		s += fmt.Sprintln("DBlock:", string(j))
	} else if f.ABlock != nil {
		j, _ := f.ABlock.MarshalJSON()
		s += fmt.Sprintln("ABlock:", string(j))
	} else if f.FBlock != nil {
		j, _ := f.FBlock.MarshalJSON()
		s += fmt.Sprintln("FBlock:", string(j))
	} else if f.ECBlock != nil {
		j, _ := f.ECBlock.MarshalJSON()
		s += fmt.Sprintln("ECBlock:", string(j))
	}

	return s
}

// Deprecated: use ablock, dblock, eblock, ecblock and fblock instead.
func GetBlockByHeightRaw(blockType string, height int64) (*BlockByHeightRawResponse, error) {
	params := heightRequest{Height: height}
	req := NewJSON2Request(fmt.Sprintf("%vblock-by-height", blockType), APICounter(), params)
	resp, err := factomdRequest(req)
	if err != nil {
		return nil, err
	}
	if resp.Error != nil {
		return nil, resp.Error
	}

	block := new(BlockByHeightRawResponse)
	if err := json.Unmarshal(resp.JSONResult(), block); err != nil {
		return nil, err
	}

	return block, nil
}
<commit_msg>fixed deprecation notices<commit_after>// Copyright 2016 Factom Foundation
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

package factom

import (
	"encoding/json"
	"fmt"
)

type JStruct struct {
	data []byte
}

func (e *JStruct) MarshalJSON() ([]byte, error) {
	return e.data, nil
}

func (e *JStruct) UnmarshalJSON(b []byte) error {
	e.data = b
	return nil
}

type BlockByHeightRawResponse struct {
	//TODO: implement all of the blocks as proper structures

	DBlock  *JStruct `json:"dblock,omitempty"`
	ABlock  *JStruct `json:"ablock,omitempty"`
	FBlock  *JStruct `json:"fblock,omitempty"`
	ECBlock *JStruct `json:"ecblock,omitempty"`

	RawData string `json:"rawdata,omitempty"`
}

func (f *BlockByHeightRawResponse) String() string {
	var s string
	if f.DBlock != nil {
		j, _ := f.DBlock.MarshalJSON()
		s += fmt.Sprintln("DBlock:", string(j))
	} else if f.ABlock != nil {
		j, _ := f.ABlock.MarshalJSON()
		s += fmt.Sprintln("ABlock:", string(j))
	} else if f.FBlock != nil {
		j, _ := f.FBlock.MarshalJSON()
		s += fmt.Sprintln("FBlock:", string(j))
	} else if f.ECBlock != nil {
		j, _ := f.ECBlock.MarshalJSON()
		s += fmt.Sprintln("ECBlock:", string(j))
	}

	return s
}

// GetBlockByHeightRaw fetches the specified block type by height
// Deprecated: use ablock, dblock, eblock, ecblock and fblock instead.
func GetBlockByHeightRaw(blockType string, height int64) (*BlockByHeightRawResponse, error) {
	params := heightRequest{Height: height}
	req := NewJSON2Request(fmt.Sprintf("%vblock-by-height", blockType), APICounter(), params)
	resp, err := factomdRequest(req)
	if err != nil {
		return nil, err
	}
	if resp.Error != nil {
		return nil, resp.Error
	}

	block := new(BlockByHeightRawResponse)
	if err := json.Unmarshal(resp.JSONResult(), block); err != nil {
		return nil, err
	}

	return block, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"strings"

	"github.com/rainforestapp/rainforest-cli/rainforest"
	"github.com/urfave/cli"
)

type branchAPI interface {
	GetBranches(...string) ([]rainforest.Branch, error)
	CreateBranch(*rainforest.Branch) error
	MergeBranch(int) error
	DeleteBranch(int) error
}

func newBranch(c cliContext, api branchAPI) error {
	name := c.Args().First()
	name = strings.TrimSpace(name)

	if name == "" {
		return cli.NewExitError("Branch name cannot be blank", 1)
	}

	branch := rainforest.Branch{
		Name: name,
	}

	err := api.CreateBranch(&branch)

	if err != nil {
		return cli.NewExitError(err.Error(), 1)
	}

	fmt.Printf("Created branch %q.\n", name)
	return nil
}

func mergeBranch(c cliContext, api branchAPI) error {
	name := c.Args().First()
	name = strings.TrimSpace(name)

	if name == "" {
		return cli.NewExitError("Branch name cannot be blank", 1)
	}

	branches, err := api.GetBranches(name)

	if err != nil {
		return cli.NewExitError(err.Error(), 1)
	}

	if len(branches) == 0 {
		return cli.NewExitError("Cannot find branch", 1)
	}

	branch := branches[0]

	err = api.MergeBranch(branch.ID)

	if err != nil {
		return cli.NewExitError(err.Error(), 1)
	}

	fmt.Printf("Merged branch %q into main.\n", name)
	return nil
}

func deleteBranch(c cliContext, api branchAPI) error {
	name := c.Args().First()
	name = strings.TrimSpace(name)

	if name == "" {
		return cli.NewExitError("Branch name cannot be blank", 1)
	}

	branches, err := api.GetBranches(name)

	if err != nil {
		return cli.NewExitError(err.Error(), 1)
	}

	if len(branches) == 0 {
		return cli.NewExitError("Cannot find branch", 1)
	}

	branch := branches[0]

	err = api.DeleteBranch(branch.ID)

	if err != nil {
		return cli.NewExitError(err.Error(), 1)
	}

	fmt.Printf("Deleted branch %q.\n", name)
	return nil
}
<commit_msg>Extract single function to get branchID from name<commit_after>package main

import (
	"errors"
	"fmt"
	"strings"

	"github.com/rainforestapp/rainforest-cli/rainforest"
	"github.com/urfave/cli"
)

type branchAPI interface {
	GetBranches(...string) ([]rainforest.Branch, error)
	CreateBranch(*rainforest.Branch) error
	MergeBranch(int) error
	DeleteBranch(int) error
}

func newBranch(c cliContext, api branchAPI) error {
	name := c.Args().First()
	name = strings.TrimSpace(name)

	if name == "" {
		return cli.NewExitError("Branch name cannot be blank", 1)
	}

	branch := rainforest.Branch{
		Name: name,
	}

	err := api.CreateBranch(&branch)

	if err != nil {
		return cli.NewExitError(err.Error(), 1)
	}

	fmt.Printf("Created branch %q.\n", name)
	return nil
}

func mergeBranch(c cliContext, api branchAPI) error {
	name := c.Args().First()
	name = strings.TrimSpace(name)

	if name == "" {
		return cli.NewExitError("Branch name cannot be blank", 1)
	}

	branchID, err := getBranchID(name, api)

	if err != nil {
		return cli.NewExitError(err.Error(), 1)
	}

	err = api.MergeBranch(branchID)

	if err != nil {
		return cli.NewExitError(err.Error(), 1)
	}

	fmt.Printf("Merged branch %q into main.\n", name)
	return nil
}

func deleteBranch(c cliContext, api branchAPI) error {
	name := c.Args().First()
	name = strings.TrimSpace(name)

	if name == "" {
		return cli.NewExitError("Branch name cannot be blank", 1)
	}

	branchID, err := getBranchID(name, api)

	if err != nil {
		return cli.NewExitError(err.Error(), 1)
	}

	err = api.DeleteBranch(branchID)

	if err != nil {
		return cli.NewExitError(err.Error(), 1)
	}

	fmt.Printf("Deleted branch %q.\n", name)
	return nil
}

// getBranchID gets branchID by using the branch name to query the API
func getBranchID(name string, api branchAPI) (int, error) {
	branches, err := api.GetBranches(name)

	if err != nil {
		return 0, err
	}

	if len(branches) == 0 {
		return 0, errors.New("Cannot find branch")
	}

	branch := branches[0]

	return branch.ID, nil
}
<|endoftext|>


<commit_before>package gitgo

import (
	"compress/zlib"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"strings"
)

type KeyType string

const (
	TreeKey      KeyType = "tree"
	ParentKey            = "parent"
	AuthorKey            = "author"
	CommitterKey         = "committer"
)

type GitObject struct {
	Type string

	Tree      string
	Parents   []string
	Author    string
	Committer string
	Message   string
	Size      string
}

func CatFile(inputSha string) (result string, err error) {

	filename := path.Join(".git", "objects", inputSha[:2], inputSha[2:])

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

	r, err := zlib.NewReader(f)
	if err != nil {
		return
	}

	bts, err := ioutil.ReadAll(r)
	if err != nil {
		return
	}

	return string(bts), nil
}

func parseObj(obj string) (result GitObject, err error) {

	parts := strings.Split(obj, "\x00")
	parts = strings.Fields(parts[0])
	result.Type = parts[0]
	result.Size = parts[1]
	nullIndex := strings.Index(obj, "\x00")

	obj = obj[nullIndex+1:]
	lines := strings.Split(obj, "\n")

	for i, line := range lines {
		// The next line is the commit message
		if len(strings.Fields(line)) == 0 {
			result.Message = strings.Join(lines[i+1:], "\n")
			break
		}
		parts := strings.Fields(line)
		key := parts[0]
		switch KeyType(key) {
		case TreeKey:
			result.Tree = parts[1]
		case ParentKey:
			result.Parents = append(result.Parents, parts[1])
		case AuthorKey:
			result.Author = strings.Join(parts[1:], " ")
		case CommitterKey:
			result.Committer = strings.Join(parts[1:], " ")
		default:
			err = fmt.Errorf("Encounterd unknown field in commit: %s", key)
			return
		}
	}
	return
}
<commit_msg>Add NewObject file for constructing an Object struct from a sha<commit_after>package gitgo

import (
	"compress/zlib"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"strings"
)

type KeyType string

const (
	TreeKey      KeyType = "tree"
	ParentKey            = "parent"
	AuthorKey            = "author"
	CommitterKey         = "committer"
)

type GitObject struct {
	Type string

	Tree      string
	Parents   []string
	Author    string
	Committer string
	Message   string
	Size      string
}

func NewObject(inputSha string) (obj GitObject, err error) {
	str, err := CatFile(inputSha)
	if err != nil {
		return
	}
	return parseObj(str)
}

func CatFile(inputSha string) (result string, err error) {

	filename := path.Join(".git", "objects", inputSha[:2], inputSha[2:])

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

	r, err := zlib.NewReader(f)
	if err != nil {
		return
	}

	bts, err := ioutil.ReadAll(r)
	if err != nil {
		return
	}

	return string(bts), nil
}

func parseObj(obj string) (result GitObject, err error) {

	parts := strings.Split(obj, "\x00")
	parts = strings.Fields(parts[0])
	result.Type = parts[0]
	result.Size = parts[1]
	nullIndex := strings.Index(obj, "\x00")

	obj = obj[nullIndex+1:]
	lines := strings.Split(obj, "\n")

	for i, line := range lines {
		// The next line is the commit message
		if len(strings.Fields(line)) == 0 {
			result.Message = strings.Join(lines[i+1:], "\n")
			break
		}
		parts := strings.Fields(line)
		key := parts[0]
		switch KeyType(key) {
		case TreeKey:
			result.Tree = parts[1]
		case ParentKey:
			result.Parents = append(result.Parents, parts[1])
		case AuthorKey:
			result.Author = strings.Join(parts[1:], " ")
		case CommitterKey:
			result.Committer = strings.Join(parts[1:], " ")
		default:
			err = fmt.Errorf("Encounterd unknown field in commit: %s", key)
			return
		}
	}
	return
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"log"
	"net"
	"time"
	"sync"
	"os/signal"
	"syscall"
	"bufio"
	"strings"
	"strconv"
	"regexp"
	"utils"
	"encoding/json"
	"github.com/gorilla/websocket"
)

type AlmazServer struct {
	sync.RWMutex
	acceptance_regexen []*regexp.Regexp
	storage *Storage
	persist_path string
	subscribers []*StreamSubscriber
	totals *Totals
}

type StreamSubscriber struct {
	conn *websocket.Conn
}

func NewAlmazServer(persist_path string) *AlmazServer {
	s := new(AlmazServer)
	s.acceptance_regexen = make([]*regexp.Regexp, 0)
	s.storage = NewStorage()
	s.persist_path = persist_path
	s.subscribers = make([]*StreamSubscriber, 0)
	s.totals = NewTotals()
	return s
}

func NewStreamSubscriber(conn *websocket.Conn) *StreamSubscriber {
	s := &StreamSubscriber{}
	s.conn = conn
	return s
}

func (self *AlmazServer) AddSubscriber(sub *StreamSubscriber) {
	self.Lock()
	defer self.Unlock()
	self.subscribers = append(self.subscribers, sub)
}

func (self *AlmazServer) RemoveSubscriber(removed_sub *StreamSubscriber) {
	self.Lock()
	defer self.Unlock()
	subs := make([]*StreamSubscriber, 0, len(self.subscribers))
	for _, sub := range(self.subscribers) {
		if sub != removed_sub {
			subs = append(subs, sub)
		}
	}
	self.subscribers = subs
}

func (self *AlmazServer) GetSubscribers() []*StreamSubscriber {
	self.RLock()
	defer self.RUnlock()
	subs := make([]*StreamSubscriber, 0, len(self.subscribers))
	for _, sub := range(self.subscribers) {
		subs = append(subs, sub)
	}
	return subs
}

func (self *AlmazServer) AddAcceptanceRegex(re string) {
	rx := regexp.MustCompile(re)
	log.Printf("storing only metrics that match %s", re)
	self.acceptance_regexen = append(self.acceptance_regexen, rx)
}

func (self *AlmazServer) StartGraphite(bindAddress string) {
	listener, err := net.Listen("tcp", bindAddress)
	if err != nil {
		log.Fatalf("failed to listen: %s", err)
	}
	log.Printf("listening on %s", bindAddress)
	for {
		conn, err := listener.Accept()
		if err != nil {
			log.Printf(err.Error())
			continue
		}
		go self.handleGraphiteConnection(conn)
	}
}

type MetricUpdate struct {
	Metric string `json:"metric"`
	Value int `json:"value"`
	TotalValue int `json:"total_value"`
}

func NewMetricUpdate(metric string, value float64, total int) *MetricUpdate {
	upd := &MetricUpdate{}
	upd.Metric = metric
	upd.Value = int(value)
	upd.TotalValue = total
	return upd
}

func (self *AlmazServer) handleGraphiteConnection(conn net.Conn) {
	defer conn.Close()
	self.RLock()
	defer self.RUnlock()
	t1 := time.Now()

	var fwd_conn net.Conn = nil
	var err error

	metric_updates := make([]*MetricUpdate, 0)

	if *fwdAddress != "" {
		fwd_conn, err = net.Dial("tcp", *fwdAddress)
		if err != nil {
			//log.Printf("forward conn error: %s", err)
		} else {
			defer fwd_conn.Close()
		}
	}

	scanner := bufio.NewScanner(conn)
	for scanner.Scan() {
		trimmedString := scanner.Text()
		parts := strings.Split(trimmedString, " ")
		if len(parts) == 3 {
			metric := parts[0]
			value, err1 := strconv.ParseFloat(parts[1], 32)
			ts, err2 := strconv.ParseInt(parts[2], 10, 64)
			if err1 != nil || err2 != nil {
				log.Printf("parse error: %s %s", err1, err2)
				continue
			}

			accepted := false
			if len(self.acceptance_regexen) == 0 {
				accepted = true
			} else {
				for _, rx := range(self.acceptance_regexen) {
					if rx.MatchString(metric) {
						accepted = true
						break
					}
				}
			}
			if accepted && value > 0 {
				self.storage.StoreMetric(metric, value, ts)
				self.totals.Increment(metric, int(value))
				upd := NewMetricUpdate(metric, value, 0)
				metric_updates = append(metric_updates, upd)
			}
		}
		if fwd_conn != nil {
			fwd_conn.Write([]byte(trimmedString + "\n"))
		}
	}
	t2 := time.Now()
	dt := t2.Sub(t1)
	go self.PushUpstream(metric_updates)
	if *debug {
		log.Printf("Processed metrics batch in %s; storing %d metrics now",
			dt.String(), self.storage.MetricCount())
	}
}

func (self *AlmazServer) PushUpstream(metric_updates []*MetricUpdate) {
	subscribers := self.GetSubscribers()
	for _, upd := range(metric_updates) {
		if upd.Value == 0 {
			continue
		}
		upd.TotalValue = self.totals.Get(upd.Metric)
		json_bytes, err := json.Marshal(upd)
		if err != nil {
			log.Printf("json encode error: %s", err)
			continue
		}
		for _, sub := range(subscribers) {
			if sub.conn == nil {
				continue
			}
			err = sub.conn.WriteMessage(websocket.TextMessage, json_bytes)
			if err != nil {
				log.Printf("WriteMessage error: %s", err)
				sub.conn = nil
				self.RemoveSubscriber(sub)
			}
		}
	}
}

func (self *AlmazServer) AuditLoop() {
	for {
		time.Sleep(33 * time.Second)
		self.PruneOld()
		log.Printf("Audit: metric number = %d", self.storage.MetricCount())
	}
}

func (self *AlmazServer) PruneOld() {
	self.Lock()
	defer self.Unlock()
	ts := time.Now().Unix()

	to_remove := make([]string, 0)

	for name, metric := range(self.storage.metrics) {
		if metric.Age() < ts {
			to_remove = append(to_remove, name)
		}
	}

	for _, name := range(to_remove) {
		self.storage.RemoveMetric(name)
		self.totals.RemoveMetric(name)
	}
	if len(to_remove) > 0 {
		log.Printf("%d old metrics pruned", len(to_remove))
	}
}

func (self *AlmazServer) LoadFromDisk() {
	self.Lock()
	defer self.Unlock()
	log.Printf("Restoring from disk...")
	t1 := time.Now()
	err := self.storage.LoadFromFile(self.persist_path)
	if err != nil {
		log.Printf("Error while loading from disk: %s", err)
	} else {
		t2 := time.Now()
		dt := t2.Sub(t1)
		log.Printf("Done loading (%s)", dt)
	}
}

func (self *AlmazServer) SaveToDisk() {
	self.Lock()
	defer self.Unlock()
	log.Printf("Saving to disk...")
	t1 := time.Now()
	err := self.storage.SaveToFile(self.persist_path)
	if err != nil {
		log.Printf("Error while saving to disk: %s", err)
	} else {
		t2 := time.Now()
		dt := t2.Sub(t1)
		log.Printf("Done saving (%s)", dt)
	}
}

func (self *AlmazServer) ForkAndSaveToDisk() {
	if utils.DoubleFork() > 0 {
		return
	}
	self.SaveToDisk()
	os.Exit(0)
}

func (self *AlmazServer) BgsaveLoop(interval_seconds int) {
	for {
		time.Sleep(time.Duration(interval_seconds) * time.Second)
		self.SaveToDisk()
	}
}

func (self *AlmazServer) WaitForTermination(persist_on_exit bool, bgsave_interval int) {
	var bgsave_int_duration = time.Duration(bgsave_interval) * time.Second
	if !persist_on_exit || bgsave_int_duration <= 0 {
		bgsave_int_duration = time.Duration(60) * time.Second
	}

	impeding_death := make(chan os.Signal, 1)
	signal.Notify(impeding_death, syscall.SIGINT, syscall.SIGTERM)

	bgsave_ticker := time.NewTicker(bgsave_int_duration)

	for {
		select {
			case <-bgsave_ticker.C:
				if persist_on_exit && bgsave_interval > 0 {
					self.ForkAndSaveToDisk()
				}
			case s := <-impeding_death:
				log.Printf("Got signal:", s)
				if persist_on_exit {
					self.SaveToDisk()
				}
				return
		}
	}
}
<commit_msg>doing batch updates<commit_after>package main

import (
	"os"
	"log"
	"net"
	"time"
	"sync"
	"os/signal"
	"syscall"
	"bufio"
	"strings"
	"strconv"
	"regexp"
	"utils"
	"encoding/json"
	"github.com/gorilla/websocket"
)

type AlmazServer struct {
	sync.RWMutex
	acceptance_regexen []*regexp.Regexp
	storage *Storage
	persist_path string
	subscribers []*StreamSubscriber
	totals *Totals
}

type StreamSubscriber struct {
	conn *websocket.Conn
}

func NewAlmazServer(persist_path string) *AlmazServer {
	s := new(AlmazServer)
	s.acceptance_regexen = make([]*regexp.Regexp, 0)
	s.storage = NewStorage()
	s.persist_path = persist_path
	s.subscribers = make([]*StreamSubscriber, 0)
	s.totals = NewTotals()
	return s
}

func NewStreamSubscriber(conn *websocket.Conn) *StreamSubscriber {
	s := &StreamSubscriber{}
	s.conn = conn
	return s
}

func (self *AlmazServer) AddSubscriber(sub *StreamSubscriber) {
	self.Lock()
	defer self.Unlock()
	self.subscribers = append(self.subscribers, sub)
}

func (self *AlmazServer) RemoveSubscriber(removed_sub *StreamSubscriber) {
	self.Lock()
	defer self.Unlock()
	subs := make([]*StreamSubscriber, 0, len(self.subscribers))
	for _, sub := range(self.subscribers) {
		if sub != removed_sub {
			subs = append(subs, sub)
		}
	}
	self.subscribers = subs
}

func (self *AlmazServer) GetSubscribers() []*StreamSubscriber {
	self.RLock()
	defer self.RUnlock()
	subs := make([]*StreamSubscriber, 0, len(self.subscribers))
	for _, sub := range(self.subscribers) {
		subs = append(subs, sub)
	}
	return subs
}

func (self *AlmazServer) AddAcceptanceRegex(re string) {
	rx := regexp.MustCompile(re)
	log.Printf("storing only metrics that match %s", re)
	self.acceptance_regexen = append(self.acceptance_regexen, rx)
}

func (self *AlmazServer) StartGraphite(bindAddress string) {
	listener, err := net.Listen("tcp", bindAddress)
	if err != nil {
		log.Fatalf("failed to listen: %s", err)
	}
	log.Printf("listening on %s", bindAddress)
	for {
		conn, err := listener.Accept()
		if err != nil {
			log.Printf(err.Error())
			continue
		}
		go self.handleGraphiteConnection(conn)
	}
}

type MetricUpdate struct {
	Metric string `json:"metric"`
	Value int `json:"value"`
	TotalValue int `json:"total_value"`
}

func NewMetricUpdate(metric string, value float64, total int) *MetricUpdate {
	upd := &MetricUpdate{}
	upd.Metric = metric
	upd.Value = int(value)
	upd.TotalValue = total
	return upd
}

func (self *AlmazServer) handleGraphiteConnection(conn net.Conn) {
	defer conn.Close()
	self.RLock()
	defer self.RUnlock()
	t1 := time.Now()

	var fwd_conn net.Conn = nil
	var err error

	metric_updates := make([]*MetricUpdate, 0)

	if *fwdAddress != "" {
		fwd_conn, err = net.Dial("tcp", *fwdAddress)
		if err != nil {
			//log.Printf("forward conn error: %s", err)
		} else {
			defer fwd_conn.Close()
		}
	}

	scanner := bufio.NewScanner(conn)
	for scanner.Scan() {
		trimmedString := scanner.Text()
		parts := strings.Split(trimmedString, " ")
		if len(parts) == 3 {
			metric := parts[0]
			value, err1 := strconv.ParseFloat(parts[1], 32)
			ts, err2 := strconv.ParseInt(parts[2], 10, 64)
			if err1 != nil || err2 != nil {
				log.Printf("parse error: %s %s", err1, err2)
				continue
			}

			accepted := false
			if len(self.acceptance_regexen) == 0 {
				accepted = true
			} else {
				for _, rx := range(self.acceptance_regexen) {
					if rx.MatchString(metric) {
						accepted = true
						break
					}
				}
			}
			if accepted && value > 0 {
				self.storage.StoreMetric(metric, value, ts)
				self.totals.Increment(metric, int(value))
				upd := NewMetricUpdate(metric, value, 0)
				metric_updates = append(metric_updates, upd)
			}
		}
		if fwd_conn != nil {
			fwd_conn.Write([]byte(trimmedString + "\n"))
		}
	}
	t2 := time.Now()
	dt := t2.Sub(t1)
	go self.PushUpstream(metric_updates)
	if *debug {
		log.Printf("Processed metrics batch in %s; storing %d metrics now",
			dt.String(), self.storage.MetricCount())
	}
}

func (self *AlmazServer) PushUpstream(metric_updates []*MetricUpdate) {
	subscribers := self.GetSubscribers()
	updates_to_push := make([]*MetricUpdate, 0, len(metric_updates))
	for _, upd := range(metric_updates) {
		if upd.Value == 0 {
			continue
		}
		upd.TotalValue = self.totals.Get(upd.Metric)
		updates_to_push = append(updates_to_push, upd)
	}

	json_bytes, err := json.Marshal(updates_to_push)
	if err != nil {
		log.Printf("json encode error: %s", err)
		return
	}
	for _, sub := range(subscribers) {
		if sub.conn == nil {
			continue
		}
		err = sub.conn.WriteMessage(websocket.TextMessage, json_bytes)
		if err != nil {
			log.Printf("WriteMessage error: %s", err)
			sub.conn = nil
			self.RemoveSubscriber(sub)
		}
	}
}

func (self *AlmazServer) AuditLoop() {
	for {
		time.Sleep(33 * time.Second)
		self.PruneOld()
		log.Printf("Audit: metric number = %d", self.storage.MetricCount())
	}
}

func (self *AlmazServer) PruneOld() {
	self.Lock()
	defer self.Unlock()
	ts := time.Now().Unix()

	to_remove := make([]string, 0)

	for name, metric := range(self.storage.metrics) {
		if metric.Age() < ts {
			to_remove = append(to_remove, name)
		}
	}

	for _, name := range(to_remove) {
		self.storage.RemoveMetric(name)
		self.totals.RemoveMetric(name)
	}
	if len(to_remove) > 0 {
		log.Printf("%d old metrics pruned", len(to_remove))
	}
}

func (self *AlmazServer) LoadFromDisk() {
	self.Lock()
	defer self.Unlock()
	log.Printf("Restoring from disk...")
	t1 := time.Now()
	err := self.storage.LoadFromFile(self.persist_path)
	if err != nil {
		log.Printf("Error while loading from disk: %s", err)
	} else {
		t2 := time.Now()
		dt := t2.Sub(t1)
		log.Printf("Done loading (%s)", dt)
	}
}

func (self *AlmazServer) SaveToDisk() {
	self.Lock()
	defer self.Unlock()
	log.Printf("Saving to disk...")
	t1 := time.Now()
	err := self.storage.SaveToFile(self.persist_path)
	if err != nil {
		log.Printf("Error while saving to disk: %s", err)
	} else {
		t2 := time.Now()
		dt := t2.Sub(t1)
		log.Printf("Done saving (%s)", dt)
	}
}

func (self *AlmazServer) ForkAndSaveToDisk() {
	if utils.DoubleFork() > 0 {
		return
	}
	self.SaveToDisk()
	os.Exit(0)
}

func (self *AlmazServer) BgsaveLoop(interval_seconds int) {
	for {
		time.Sleep(time.Duration(interval_seconds) * time.Second)
		self.SaveToDisk()
	}
}

func (self *AlmazServer) WaitForTermination(persist_on_exit bool, bgsave_interval int) {
	var bgsave_int_duration = time.Duration(bgsave_interval) * time.Second
	if !persist_on_exit || bgsave_int_duration <= 0 {
		bgsave_int_duration = time.Duration(60) * time.Second
	}

	impeding_death := make(chan os.Signal, 1)
	signal.Notify(impeding_death, syscall.SIGINT, syscall.SIGTERM)

	bgsave_ticker := time.NewTicker(bgsave_int_duration)

	for {
		select {
			case <-bgsave_ticker.C:
				if persist_on_exit && bgsave_interval > 0 {
					self.ForkAndSaveToDisk()
				}
			case s := <-impeding_death:
				log.Printf("Got signal:", s)
				if persist_on_exit {
					self.SaveToDisk()
				}
				return
		}
	}
}
<|endoftext|>


<commit_before>package boss

import (
	"bufio"
	"fmt"
	"log"
	"net"
	"net/textproto"
	"strings"
	"sync"
	"time"

	"github.com/pajlada/pajbot2/redismanager"
	"github.com/pajlada/pajbot2/sqlmanager"

	"github.com/pajlada/pajbot2/bot"
	"github.com/pajlada/pajbot2/common"
	"github.com/pajlada/pajbot2/helper"
	"github.com/pajlada/pajbot2/modules"
)

/*
The Irc object contains all data xD
*/
type Irc struct {
	sync.Mutex
	server   string
	port     string
	pass     string
	nick     string
	readConn map[net.Conn][]string
	sendConn map[net.Conn][]int
	ReadChan chan string
	SendChan chan string
	channels map[string]net.Conn
	bots     map[string]chan common.Msg
	redis    *redismanager.RedisManager
	sql      *sqlmanager.SQLManager
	parser   *parse
	quit     chan string
}

/*
SendRaw sends a raw message to the given connection.
The only thing it appends is \r\n
*/
func (irc *Irc) SendRaw(s net.Conn, line string) {
	fmt.Fprint(s, line+"\r\n")
}

func (irc *Irc) newConn(send bool) {
	conn, err := net.Dial("tcp", irc.server+":"+irc.port)
	if err != nil {
		fmt.Println("Error connecting to the IRC servers:", err)
		return
	}
	if irc.pass != "" {
		irc.SendRaw(conn, "PASS "+irc.pass)
	}
	irc.SendRaw(conn, "NICK "+irc.nick)
	irc.SendRaw(conn, "CAP REQ twitch.tv/tags")
	/*
		TODO: Fix so you don't receive multiple of the same whisper
		if you have more than one connection open
	*/
	irc.SendRaw(conn, "CAP REQ twitch.tv/commands")
	irc.Lock()
	defer irc.Unlock()
	// wait for connection, this should be done better but we're gonna use
	// relaybroker anyways so it should be fine for now
	time.Sleep(500 * time.Millisecond)
	if send {
		irc.sendConn[conn] = make([]int, 30)
		go irc.keepAlive(conn)
	} else {
		irc.readConn[conn] = make([]string, 0)
		go irc.readConnection(conn)
	}
	fmt.Println("connected")
}

func (irc *Irc) getSendConn() net.Conn {
	var conn net.Conn
	for c := range irc.sendConn {
		if helper.Sum(irc.sendConn[c]) < 15 {
			conn = c
			break
		}
	}
	if conn == nil {
		irc.newConn(true)
		conn = irc.getSendConn()
	}
	return conn
}

func (irc *Irc) send() {
	for {
		msg := <-irc.SendChan
		conn := irc.getSendConn()
		irc.SendRaw(conn, msg)
		fmt.Println("sent: " + msg)
		irc.Lock()
		irc.sendConn[conn][29]++
		irc.Unlock()
	}
}

func (irc *Irc) rateLimit() {
	for {
		for conn, s := range irc.sendConn {
			newS := append(s[1:], 0)
			irc.Lock()
			irc.sendConn[conn] = newS
			irc.Unlock()
		}
		time.Sleep(1 * time.Second)
	}
}

func (irc *Irc) keepAlive(conn net.Conn) {
	reader := bufio.NewReader(conn)
	tp := textproto.NewReader(reader)
	for {
		line, err := tp.ReadLine()
		if err != nil {
			log.Println("connection died", err)
			delete(irc.sendConn, conn)
			return
		}
		if strings.HasPrefix(line, "PING") {
			irc.SendRaw(conn, strings.Replace(line, "PING", "PONG", 1))
		}
	}
}

// GetGlobalUser fills in the global user in the message from redis
func (irc *Irc) GetGlobalUser(m *common.Msg) {
	u := &common.GlobalUser{}
	irc.redis.GetGlobalUser(m.Channel, &m.User, u)
	if m.Type == common.MsgWhisper {
		m.Channel = u.Channel
	}
}

func (irc *Irc) readConnection(conn net.Conn) {
	reader := bufio.NewReader(conn)
	tp := textproto.NewReader(reader)
	readChan := make(chan string)
	running := true
	go func() {
		var line string
		for running {
			line = <-readChan
			if strings.HasPrefix(line, "PING") {
				irc.SendRaw(conn, strings.Replace(line, "PING", "PONG", 1))
			} else {
				m := irc.parser.Parse(line)
				// throw away its own and other useless msgs
				if m.User.Name == irc.nick {
					// Throw away its own messages
					continue
				}
				log.Println(m.Type)
				switch m.Type {
				case common.MsgPrivmsg, common.MsgWhisper:
					irc.GetGlobalUser(&m)
					if m.Channel != "" {
						irc.bots[m.Channel] <- m
					} else {
						log.Println("No channel for message")
					}
				case common.MsgThrowAway:
					// Do nothing
					break
				default:
					log.Printf("Unhandled message[%d]: %s\n", m.Type, m.Message)
				}
			}
		}
	}()
	defer func() {
		running = false
		close(readChan)
	}()
	for {
		line, err := tp.ReadLine()
		if err != nil {
			log.Println("connection died", err)
			irc.newConn(false)
			irc.JoinChannels(irc.readConn[conn])
			delete(irc.readConn, conn)
			return
		}
		readChan <- line
	}
}

// NewBot creates a new bot in the given channel
func (irc *Irc) NewBot(channel string) {
	read := make(chan common.Msg)
	newbot := bot.Config{
		Quit:     irc.quit,
		Channel:  channel,
		ReadChan: read,
		SendChan: irc.SendChan,
		Redis:    irc.redis,
	}
	irc.bots[channel] = read
	commandModule := &modules.Command{}
	// TODO: This should be generalized (and optional if possible)
	// Could that be based on module type?
	// If module.@type == 'NeedsInit' { (cast)module.Init() }
	commandModule.Init(irc.sql)
	_modules := []bot.Module{
		&modules.Banphrase{},
		commandModule,
		&modules.Pyramid{},
		&modules.Quit{},
	}
	b := bot.NewBot(newbot, _modules)
	go b.Init()
}

/*
JoinChannel joins a twitch chat and creates a new bot if there isnt already one
*/
func (irc *Irc) JoinChannel(channel string) {
	conn := irc.getReadconn()
	irc.SendRaw(conn, "JOIN #"+channel)
	irc.Lock()
	defer irc.Unlock()
	if _, ok := irc.bots[channel]; !ok {
		irc.readConn[conn] = append(irc.readConn[conn], channel)
		irc.NewBot(channel)
	}

}

/*
JoinChannels joins a list of channels, given as a string slice
*/
func (irc *Irc) JoinChannels(channels []string) {
	for i := range channels {
		irc.JoinChannel(channels[i])
		time.Sleep(300 * time.Millisecond)
	}
}

func (irc *Irc) getReadconn() net.Conn {
	var conn net.Conn
	for c, channels := range irc.readConn {
		if len(channels) < 50 {
			conn = c
			break
		}
	}
	if conn == nil {
		irc.newConn(false)
		conn = irc.getReadconn()
	}
	return conn
}

/*
Init initalizes shit.

TODO: This should just create the Irc object. You should have to call
irc.Run() manually I think. or irc.Start()?
*/
func Init(config *common.Config) *Irc {
	irc := &Irc{
		server:   "irc.chat.twitch.tv",
		port:     "80",
		pass:     config.Pass,
		nick:     config.Nick,
		readConn: make(map[net.Conn][]string),
		sendConn: make(map[net.Conn][]int),
		ReadChan: make(chan string, 10),
		SendChan: make(chan string, 10),
		bots:     make(map[string]chan common.Msg),
		redis:    redismanager.Init(config),
		sql:      sqlmanager.Init(config),
		parser:   &parse{},
		quit:     config.Quit,
	}
	irc.newConn(true)
	irc.newConn(false)
	go irc.send()
	go irc.rateLimit()
	go irc.JoinChannels(config.Channels)
	return irc
}
<commit_msg>removed ratelimiting, use relaybroker instead<commit_after>package boss

import (
	"bufio"
	"fmt"
	"log"
	"net"
	"net/textproto"
	"strings"
	"sync"

	"github.com/pajlada/pajbot2/redismanager"
	"github.com/pajlada/pajbot2/sqlmanager"

	"github.com/pajlada/pajbot2/bot"
	"github.com/pajlada/pajbot2/common"
	"github.com/pajlada/pajbot2/modules"
)

/*
The Irc object contains all data xD
*/
type Irc struct {
	sync.Mutex
	server   string
	port     string
	pass     string
	nick     string
	conn     net.Conn
	ReadChan chan string
	SendChan chan string
	bots     map[string]chan common.Msg
	redis    *redismanager.RedisManager
	sql      *sqlmanager.SQLManager
	parser   *parse
	quit     chan string
}

/*
SendRaw sends a raw message to the given connection.
The only thing it appends is \r\n
*/
func (irc *Irc) SendRaw(s net.Conn, line string) {
	fmt.Fprint(s, line+"\r\n")
}

func (irc *Irc) newConn() {
	if irc.conn != nil {
		return
	}
	conn, err := net.Dial("tcp", irc.server+":"+irc.port)
	if err != nil {
		fmt.Println("Error connecting to the IRC servers:", err)
		return
	}
	if irc.pass != "" {
		irc.SendRaw(conn, "PASS "+irc.pass)
	}
	irc.SendRaw(conn, "NICK "+irc.nick)
	go irc.readConnection(conn)
	irc.conn = conn
	fmt.Println("connected")
}

func (irc *Irc) send() {
	for {
		msg := <-irc.SendChan
		irc.SendRaw(irc.conn, msg)
		fmt.Println("sent: " + msg)
	}
}

// GetGlobalUser fills in the global user in the message from redis
func (irc *Irc) GetGlobalUser(m *common.Msg) {
	u := &common.GlobalUser{}
	irc.redis.GetGlobalUser(m.Channel, &m.User, u)
	if m.Type == common.MsgWhisper {
		m.Channel = u.Channel
	}
}

func (irc *Irc) readConnection(conn net.Conn) {
	reader := bufio.NewReader(conn)
	tp := textproto.NewReader(reader)
	readChan := make(chan string)
	running := true
	go func() {
		var line string
		for running {
			line = <-readChan
			if strings.HasPrefix(line, "PING") {
				irc.SendRaw(conn, strings.Replace(line, "PING", "PONG", 1))
			} else {
				m := irc.parser.Parse(line)
				// throw away its own and other useless msgs
				if m.User.Name == irc.nick {
					// Throw away its own messages
					continue
				}
				log.Println(m.Type)
				switch m.Type {
				case common.MsgPrivmsg, common.MsgWhisper:
					irc.GetGlobalUser(&m)
					if m.Channel != "" {
						irc.bots[m.Channel] <- m
					} else {
						log.Println("No channel for message")
					}
				case common.MsgThrowAway:
					// Do nothing
					break
				default:
					log.Printf("Unhandled message[%d]: %s\n", m.Type, m.Message)
				}
			}
		}
	}()
	defer func() {
		running = false
		close(readChan)
	}()
	for {
		line, err := tp.ReadLine()
		if err != nil {
			log.Println("connection died", err)
			irc.newConn()
			//irc.JoinChannels(irc.readConn[conn])
			return
		}
		readChan <- line
	}
}

// NewBot creates a new bot in the given channel
func (irc *Irc) NewBot(channel string) {
	read := make(chan common.Msg)
	newbot := bot.Config{
		Quit:     irc.quit,
		Channel:  channel,
		ReadChan: read,
		SendChan: irc.SendChan,
		Redis:    irc.redis,
	}
	irc.bots[channel] = read
	commandModule := &modules.Command{}
	// TODO: This should be generalized (and optional if possible)
	// Could that be based on module type?
	// If module.@type == 'NeedsInit' { (cast)module.Init() }
	commandModule.Init(irc.sql)
	_modules := []bot.Module{
		&modules.Banphrase{},
		commandModule,
		&modules.Pyramid{},
		&modules.Quit{},
	}
	b := bot.NewBot(newbot, _modules)
	go b.Init()
}

/*
JoinChannel joins a twitch chat and creates a new bot if there isnt already one
*/
func (irc *Irc) JoinChannel(channel string) {
	irc.Lock()
	defer irc.Unlock()
	if _, ok := irc.bots[channel]; !ok {
		irc.NewBot(channel)
		irc.SendRaw(irc.conn, "JOIN #"+channel)
	}
}

/*
JoinChannels joins a list of channels, given as a string slice
*/
func (irc *Irc) JoinChannels(channels []string) {
	for _, channel := range channels {
		irc.JoinChannel(channel)
	}
}

/*
Init initalizes shit.

TODO: This should just create the Irc object. You should have to call
irc.Run() manually I think. or irc.Start()?
*/
func Init(config *common.Config) *Irc {
	server := "irc.chat.twitch.tv"
	port := "80"
	//usingBroker := false
	if config.BrokerPort != "" {
		server = "localhost"
		port = config.BrokerPort
		//usingBroker = true
	}
	irc := &Irc{
		server:   server,
		port:     port,
		pass:     config.Pass,
		nick:     config.Nick,
		ReadChan: make(chan string, 10),
		SendChan: make(chan string, 10),
		bots:     make(map[string]chan common.Msg),
		redis:    redismanager.Init(config),
		sql:      sqlmanager.Init(config),
		parser:   &parse{},
		quit:     config.Quit,
	}
	irc.newConn()
	go irc.send()
	go irc.JoinChannels(config.Channels)
	return irc
}
<|endoftext|>