<commit_before>package twse

import (
	"encoding/csv"
	"errors"
	"fmt"
	"net/url"
	"strconv"
	"strings"
	"time"

	"github.com/toomore/gogrs/utils"
)

// TWSECLASS is a class list of TWSE.
var TWSECLASS = map[string]string{
	"MS":         "大盤統計資訊",
	"0049":       "封閉式基金",
	"0099P":      "ETF",
	"01":         "水泥工業",
	"019919T":    "受益證券",
	"02":         "食品工業",
	"03":         "塑膠工業",
	"04":         "紡織纖維",
	"05":         "電機機械",
	"06":         "電器電纜",
	"07":         "化學生技醫療",
	"08":         "玻璃陶瓷",
	"09":         "造紙工業",
	"0999":       "認購權證", //(不含牛證)
	"0999B":      "熊證",
	"0999C":      "牛證",
	"0999G9":     "認股權憑證",
	"0999GA":     "附認股權特別股",
	"0999GD":     "附認股權公司債",
	"0999P":      "認售權證", //(不含熊證)
	"0999X":      "可展延牛證",
	"0999Y":      "可展延熊證",
	"10":         "鋼鐵工業",
	"11":         "橡膠工業",
	"12":         "汽車工業",
	"13":         "電子工業",
	"14":         "建材營造",
	"15":         "航運業",
	"16":         "觀光事業",
	"17":         "金融保險",
	"18":         "貿易百貨",
	"19":         "綜合",
	"20":         "其他",
	"21":         "化學工業",
	"22":         "生技醫療業",
	"23":         "油電燃氣業",
	"24":         "半導體業",
	"25":         "電腦及週邊設備業",
	"26":         "光電業",
	"27":         "通信網路業",
	"28":         "電子零組件業",
	"29":         "電子通路業",
	"30":         "資訊服務業",
	"31":         "其他電子業",
	"9299":       "存託憑證",
	"ALL":        "全部",
	"ALLBUT0999": "全部(不含權證、牛熊證、可展延牛熊證)",
	"CB":         "可轉換公司債",
}

// StockInfo is simple stock info for no, name.
type StockInfo struct {
	No   string
	Name string
}

// Lists is to get TWSE list.
type Lists struct {
	Date            time.Time
	FmtData         map[string]FmtListData
	categoryRawData map[string][][]string
	categoryNoList  map[string][]StockInfo
}

// Get is to get csv data.
func (l *Lists) Get(category string) ([][]string, error) {
	if TWSECLASS[category] == "" {
		return nil, errors.New("Not support.")
	}

	year, month, day := l.Date.Date()
	data, err := hCache.PostForm(fmt.Sprintf("%s%s", utils.TWSEHOST, utils.TWSELISTCSV),
		url.Values{"download": {"csv"}, "selectType": {category},
			"qdate": {fmt.Sprintf("%d/%02d/%02d", year-1911, month, day)}})

	if err != nil {
		return nil, fmt.Errorf("Network fail: %s", err)
	}
	if l.categoryRawData == nil {
		l.categoryRawData = make(map[string][][]string)
	}

	csvArrayContent := strings.Split(string(data), "\n")

	var csvReader *csv.Reader
	switch category {
	case "MS":
		if len(csvArrayContent) > 6 {
			csvReader = csv.NewReader(strings.NewReader(strings.Join(csvArrayContent[4:51], "\n")))
		}
	case "ALLBUT0999":
		if len(csvArrayContent) > 155 {
			csvReader = csv.NewReader(strings.NewReader(strings.Join(csvArrayContent[154:len(csvArrayContent)-9], "\n")))
		}
	default:
		if len(csvArrayContent) > 9 {
			csvReader = csv.NewReader(strings.NewReader(strings.Join(csvArrayContent[4:len(csvArrayContent)-7], "\n")))
		}
	}
	if csvReader != nil {
		returnData, err := csvReader.ReadAll()
		switch category {
		default:
			if err == nil {
				l.categoryRawData[category] = returnData
				l.formatData(category)
			}
		case "MS":
		}
		return returnData, err
	}
	return nil, errors.New("Not enough data.")
}

// GetCategoryList 取得分類的股票代碼與名稱列表
func (l Lists) GetCategoryList(category string) []StockInfo {
	if _, ok := l.categoryNoList[category]; !ok {
		l.Get(category)
	}
	return l.categoryNoList[category]
}

// FmtListData 格式化個股的資料資訊
type FmtListData struct {
	No             string
	Name           string
	Volume         uint64  //成交股數
	TotalPrice     uint64  //成交金額
	Open           float64 //開盤價
	High           float64 //最高價
	Low            float64 //最低價
	Price          float64 //收盤價
	Range          float64 //漲跌價差
	Totalsale      uint64  //成交筆數
	LastBuyPrice   float64 //最後揭示買價
	LastBuyVolume  uint64  //最後揭示買量
	LastSellPrice  float64 //最後揭示賣價
	LastSellVolume uint64  //最後揭示賣量
	PERatio        float64 //本益比
}

func (l *Lists) formatData(categoryNo string) {
	if l.FmtData == nil {
		l.FmtData = make(map[string]FmtListData)
	}

	if l.categoryNoList == nil {
		l.categoryNoList = make(map[string][]StockInfo)
	}

	if _, ok := l.categoryNoList[categoryNo]; !ok {
		l.categoryNoList[categoryNo] = make([]StockInfo, len(l.categoryRawData[categoryNo]))
	}

	for i, v := range l.categoryRawData[categoryNo] {
		var data FmtListData
		data.No = strings.Trim(v[0], " ")
		data.Name = strings.Trim(v[1], " ")
		data.Volume, _ = strconv.ParseUint(v[2], 10, 32)
		data.Totalsale, _ = strconv.ParseUint(v[3], 10, 32)
		data.TotalPrice, _ = strconv.ParseUint(v[4], 10, 32)
		data.Open, _ = strconv.ParseFloat(v[5], 64)
		data.High, _ = strconv.ParseFloat(v[6], 64)
		data.Low, _ = strconv.ParseFloat(v[7], 64)
		data.Price, _ = strconv.ParseFloat(v[8], 64)
		data.Range, _ = strconv.ParseFloat(fmt.Sprintf("%s%s", v[9], v[10]), 64)
		data.LastBuyPrice, _ = strconv.ParseFloat(v[11], 64)
		data.LastBuyVolume, _ = strconv.ParseUint(v[12], 10, 32)
		data.LastSellPrice, _ = strconv.ParseFloat(v[13], 64)
		data.LastSellVolume, _ = strconv.ParseUint(v[14], 10, 32)
		data.PERatio, _ = strconv.ParseFloat(v[15], 64)

		l.FmtData[data.No] = data
		l.categoryNoList[categoryNo][i] = StockInfo{No: data.No, Name: data.Name}
	}
}
<commit_msg>Using regexp.<commit_after>package twse

import (
	"encoding/csv"
	"errors"
	"fmt"
	"net/url"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/toomore/gogrs/utils"
)

// TWSECLASS is a class list of TWSE.
var TWSECLASS = map[string]string{
	"MS":         "大盤統計資訊",
	"0049":       "封閉式基金",
	"0099P":      "ETF",
	"01":         "水泥工業",
	"019919T":    "受益證券",
	"02":         "食品工業",
	"03":         "塑膠工業",
	"04":         "紡織纖維",
	"05":         "電機機械",
	"06":         "電器電纜",
	"07":         "化學生技醫療",
	"08":         "玻璃陶瓷",
	"09":         "造紙工業",
	"0999":       "認購權證", //(不含牛證)
	"0999B":      "熊證",
	"0999C":      "牛證",
	"0999G9":     "認股權憑證",
	"0999GA":     "附認股權特別股",
	"0999GD":     "附認股權公司債",
	"0999P":      "認售權證", //(不含熊證)
	"0999X":      "可展延牛證",
	"0999Y":      "可展延熊證",
	"10":         "鋼鐵工業",
	"11":         "橡膠工業",
	"12":         "汽車工業",
	"13":         "電子工業",
	"14":         "建材營造",
	"15":         "航運業",
	"16":         "觀光事業",
	"17":         "金融保險",
	"18":         "貿易百貨",
	"19":         "綜合",
	"20":         "其他",
	"21":         "化學工業",
	"22":         "生技醫療業",
	"23":         "油電燃氣業",
	"24":         "半導體業",
	"25":         "電腦及週邊設備業",
	"26":         "光電業",
	"27":         "通信網路業",
	"28":         "電子零組件業",
	"29":         "電子通路業",
	"30":         "資訊服務業",
	"31":         "其他電子業",
	"9299":       "存託憑證",
	"ALL":        "全部",
	"ALLBUT0999": "全部(不含權證、牛熊證、可展延牛熊證)",
	"CB":         "可轉換公司債",
}

// StockInfo is simple stock info for no, name.
type StockInfo struct {
	No   string
	Name string
}

// Lists is to get TWSE list.
type Lists struct {
	Date            time.Time
	FmtData         map[string]FmtListData
	categoryRawData map[string][][]string
	categoryNoList  map[string][]StockInfo
}

// Get is to get csv data.
func (l *Lists) Get(category string) ([][]string, error) {
	if TWSECLASS[category] == "" {
		return nil, errors.New("Not support.")
	}

	year, month, day := l.Date.Date()
	data, err := hCache.PostForm(fmt.Sprintf("%s%s", utils.TWSEHOST, utils.TWSELISTCSV),
		url.Values{"download": {"csv"}, "selectType": {category},
			"qdate": {fmt.Sprintf("%d/%02d/%02d", year-1911, month, day)}})

	if err != nil {
		return nil, fmt.Errorf("Network fail: %s", err)
	}
	if l.categoryRawData == nil {
		l.categoryRawData = make(map[string][][]string)
	}

	csvArrayContent := strings.Split(string(data), "\n")

	var csvReader *csv.Reader
	switch category {
	case "MS":
		if len(csvArrayContent) > 6 {
			csvReader = csv.NewReader(strings.NewReader(strings.Join(csvArrayContent[4:51], "\n")))
		}
	case "ALLBUT0999", "ALL":
		if len(csvArrayContent) > 155 {
			re := regexp.MustCompile("^=?[\"]{1}[0-9A-Z]{4,}")
			pickdata := make([]string, 0)
			for _, v := range csvArrayContent {
				if re.MatchString(v) {
					if v[0] == 61 {
						pickdata = append(pickdata, v[1:])
					} else {
						pickdata = append(pickdata, v)
					}
				}
			}
			csvReader = csv.NewReader(strings.NewReader(strings.Join(pickdata, "\n")))
		}
	default:
		if len(csvArrayContent) > 9 {
			csvReader = csv.NewReader(strings.NewReader(strings.Join(csvArrayContent[4:len(csvArrayContent)-7], "\n")))
		}
	}
	if csvReader != nil {
		returnData, err := csvReader.ReadAll()
		switch category {
		default:
			if err == nil {
				l.categoryRawData[category] = returnData
				l.formatData(category)
			}
		case "MS":
		}
		return returnData, err
	}
	return nil, errors.New("Not enough data.")
}

// GetCategoryList 取得分類的股票代碼與名稱列表
func (l Lists) GetCategoryList(category string) []StockInfo {
	if _, ok := l.categoryNoList[category]; !ok {
		l.Get(category)
	}
	return l.categoryNoList[category]
}

// FmtListData 格式化個股的資料資訊
type FmtListData struct {
	No             string
	Name           string
	Volume         uint64  //成交股數
	TotalPrice     uint64  //成交金額
	Open           float64 //開盤價
	High           float64 //最高價
	Low            float64 //最低價
	Price          float64 //收盤價
	Range          float64 //漲跌價差
	Totalsale      uint64  //成交筆數
	LastBuyPrice   float64 //最後揭示買價
	LastBuyVolume  uint64  //最後揭示買量
	LastSellPrice  float64 //最後揭示賣價
	LastSellVolume uint64  //最後揭示賣量
	PERatio        float64 //本益比
}

func (l *Lists) formatData(categoryNo string) {
	if l.FmtData == nil {
		l.FmtData = make(map[string]FmtListData)
	}

	if l.categoryNoList == nil {
		l.categoryNoList = make(map[string][]StockInfo)
	}

	if _, ok := l.categoryNoList[categoryNo]; !ok {
		l.categoryNoList[categoryNo] = make([]StockInfo, len(l.categoryRawData[categoryNo]))
	}

	for i, v := range l.categoryRawData[categoryNo] {
		var data FmtListData
		data.No = strings.Trim(v[0], " ")
		data.Name = strings.Trim(v[1], " ")
		data.Volume, _ = strconv.ParseUint(v[2], 10, 32)
		data.Totalsale, _ = strconv.ParseUint(v[3], 10, 32)
		data.TotalPrice, _ = strconv.ParseUint(v[4], 10, 32)
		data.Open, _ = strconv.ParseFloat(v[5], 64)
		data.High, _ = strconv.ParseFloat(v[6], 64)
		data.Low, _ = strconv.ParseFloat(v[7], 64)
		data.Price, _ = strconv.ParseFloat(v[8], 64)
		data.Range, _ = strconv.ParseFloat(fmt.Sprintf("%s%s", v[9], v[10]), 64)
		data.LastBuyPrice, _ = strconv.ParseFloat(v[11], 64)
		data.LastBuyVolume, _ = strconv.ParseUint(v[12], 10, 32)
		data.LastSellPrice, _ = strconv.ParseFloat(v[13], 64)
		data.LastSellVolume, _ = strconv.ParseUint(v[14], 10, 32)
		data.PERatio, _ = strconv.ParseFloat(v[15], 64)

		l.FmtData[data.No] = data
		l.categoryNoList[categoryNo][i] = StockInfo{No: data.No, Name: data.Name}
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 Eleme Inc. All rights reserved.

// Package alerter implements an alerter to send sms/email messages
// on anomalies found.
package alerter

import (
	"encoding/json"
	"os/exec"
	"sync/atomic"
	"time"

	"github.com/eleme/banshee/config"
	"github.com/eleme/banshee/models"
	"github.com/eleme/banshee/storage"
	"github.com/eleme/banshee/util/log"
	"github.com/eleme/banshee/util/safemap"
)

// Limit for buffered detected metric results, further results will be dropped
// if this limit is reached.
const bufferedMetricResultsLimit = 10 * 1024

// Alerter alerts on anomalies detected.
type Alerter struct {
	// Storage
	db *storage.DB
	// Config
	cfg *config.Config
	// Input
	In chan *models.Metric
	// Alertings stamps
	m *safemap.SafeMap
	// Alertings counters
	c *safemap.SafeMap
}

// Alerting message.
type msg struct {
	Project *models.Project `json:"project"`
	Metric  *models.Metric  `json:"metric"`
	User    *models.User    `json:"user"`
}

// New creates a alerter.
func New(cfg *config.Config, db *storage.DB) *Alerter {
	al := new(Alerter)
	al.cfg = cfg
	al.db = db
	al.In = make(chan *models.Metric, bufferedMetricResultsLimit)
	al.m = safemap.New()
	al.c = safemap.New()
	return al
}

// Start several goroutines to wait for detected metrics, then check each
// metric with all the rules, the configured shell command will be executed
// once a rule is hit.
func (al *Alerter) Start() {
	log.Info("start %d alerter workers..", al.cfg.Alerter.Workers)
	for i := 0; i < al.cfg.Alerter.Workers; i++ {
		go al.work()
	}
	go func() {
		ticker := time.NewTicker(time.Hour * 24)
		for _ = range ticker.C {
			al.c.Clear()
		}
	}()
}

// work waits for detected metrics, then check each metric with all the
// rules, the configured shell command will be executed once a rule is hit.
func (al *Alerter) work() {
	for {
		metric := <-al.In
		// Check interval.
		v, ok := al.m.Get(metric.Name)
		if ok && metric.Stamp-v.(uint32) < al.cfg.Alerter.Interval {
			return
		}
		// Check alert times in one day
		v, ok = al.c.Get(metric.Name)
		if ok && atomic.LoadUint32(v.(*uint32)) > al.cfg.Alerter.OneDayLimit {
			return
		}
		if !ok {
			var newCounter uint32
			newCounter = 1
			al.c.Set(metric.Name, &newCounter)
		} else {
			atomic.AddUint32(v.(*uint32), 1)
		}
		// Universals
		var univs []models.User
		if err := al.db.Admin.DB().Where("universal = ?", true).Find(&univs); err != nil {
			log.Error("get universal users: %v, skiping..", err)
			continue
		}
		for _, rule := range metric.TestedRules {
			// Project
			proj := &models.Project{}
			if err := al.db.Admin.DB().Model(rule).Related(proj); err != nil {
				log.Error("project not found, %v, skiping..", err)
				continue
			}
			// Users
			var users []models.User
			if err := al.db.Admin.DB().Model(proj).Related(&users, "Users"); err != nil {
				log.Error("get users: %v, skiping..", err)
				continue
			}
			users = append(users, univs...)
			// Send
			for _, user := range users {
				d := &msg{
					Project: proj,
					Metric:  metric,
					User:    &user,
				}
				// Exec
				if len(al.cfg.Alerter.Command) == 0 {
					log.Warn("alert command not configured")
					continue
				}
				b, _ := json.Marshal(d)
				cmd := exec.Command(al.cfg.Alerter.Command, string(b))
				if err := cmd.Run(); err != nil {
					log.Error("exec %s: %v", al.cfg.Alerter.Command, err)
				}
			}
			if len(users) != 0 {
				al.m.Set(metric.Name, metric.Stamp)
			}
		}
	}
}
<commit_msg>Fix getting error from gorm in alerter<commit_after>// Copyright 2015 Eleme Inc. All rights reserved.

// Package alerter implements an alerter to send sms/email messages
// on anomalies found.
package alerter

import (
	"encoding/json"
	"os/exec"
	"sync/atomic"
	"time"

	"github.com/eleme/banshee/config"
	"github.com/eleme/banshee/models"
	"github.com/eleme/banshee/storage"
	"github.com/eleme/banshee/util/log"
	"github.com/eleme/banshee/util/safemap"
)

// Limit for buffered detected metric results, further results will be dropped
// if this limit is reached.
const bufferedMetricResultsLimit = 10 * 1024

// Alerter alerts on anomalies detected.
type Alerter struct {
	// Storage
	db *storage.DB
	// Config
	cfg *config.Config
	// Input
	In chan *models.Metric
	// Alertings stamps
	m *safemap.SafeMap
	// Alertings counters
	c *safemap.SafeMap
}

// Alerting message.
type msg struct {
	Project *models.Project `json:"project"`
	Metric  *models.Metric  `json:"metric"`
	User    *models.User    `json:"user"`
}

// New creates a alerter.
func New(cfg *config.Config, db *storage.DB) *Alerter {
	al := new(Alerter)
	al.cfg = cfg
	al.db = db
	al.In = make(chan *models.Metric, bufferedMetricResultsLimit)
	al.m = safemap.New()
	al.c = safemap.New()
	return al
}

// Start several goroutines to wait for detected metrics, then check each
// metric with all the rules, the configured shell command will be executed
// once a rule is hit.
func (al *Alerter) Start() {
	log.Info("start %d alerter workers..", al.cfg.Alerter.Workers)
	for i := 0; i < al.cfg.Alerter.Workers; i++ {
		go al.work()
	}
	go func() {
		ticker := time.NewTicker(time.Hour * 24)
		for _ = range ticker.C {
			al.c.Clear()
		}
	}()
}

// work waits for detected metrics, then check each metric with all the
// rules, the configured shell command will be executed once a rule is hit.
func (al *Alerter) work() {
	for {
		metric := <-al.In
		// Check interval.
		v, ok := al.m.Get(metric.Name)
		if ok && metric.Stamp-v.(uint32) < al.cfg.Alerter.Interval {
			return
		}
		// Check alert times in one day
		v, ok = al.c.Get(metric.Name)
		if ok && atomic.LoadUint32(v.(*uint32)) > al.cfg.Alerter.OneDayLimit {
			return
		}
		if !ok {
			var newCounter uint32
			newCounter = 1
			al.c.Set(metric.Name, &newCounter)
		} else {
			atomic.AddUint32(v.(*uint32), 1)
		}
		// Universals
		var univs []models.User
		if err := al.db.Admin.DB().Where("universal = ?", true).Find(&univs).Error; err != nil {
			log.Error("get universal users: %v, skiping..", err)
			continue
		}
		for _, rule := range metric.TestedRules {
			// Project
			proj := &models.Project{}
			if err := al.db.Admin.DB().Model(rule).Related(proj).Error; err != nil {
				log.Error("project not found, %v, skiping..", err)
				continue
			}
			// Users
			var users []models.User
			if err := al.db.Admin.DB().Model(proj).Related(&users, "Users").Error; err != nil {
				log.Error("get users: %v, skiping..", err)
				continue
			}
			users = append(users, univs...)
			// Send
			for _, user := range users {
				d := &msg{
					Project: proj,
					Metric:  metric,
					User:    &user,
				}
				// Exec
				if len(al.cfg.Alerter.Command) == 0 {
					log.Warn("alert command not configured")
					continue
				}
				b, _ := json.Marshal(d)
				cmd := exec.Command(al.cfg.Alerter.Command, string(b))
				if err := cmd.Run(); err != nil {
					log.Error("exec %s: %v", al.cfg.Alerter.Command, err)
				}
			}
			if len(users) != 0 {
				al.m.Set(metric.Name, metric.Stamp)
			}
		}
	}
}
<|endoftext|>


<commit_before>// Copyright © 2016 Thomas Rabaix <thomas.rabaix@gmail.com>.
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

package api

import (
	"encoding/json"

	log "github.com/Sirupsen/logrus"
	"github.com/rande/goapp"
	"github.com/rande/pkgmirror"
	"goji.io"
	"goji.io/pat"
)

func ConfigureApp(config *pkgmirror.Config, l *goapp.Lifecycle) {

	l.Register(func(app *goapp.App) error {
		app.Set("pkgmirror.channel.state", func(app *goapp.App) interface{} {
			return make(chan pkgmirror.State)
		})

		app.Set("pkgmirror.sse.broker", func(app *goapp.App) interface{} {
			return pkgmirror.NewSseBroker()
		})

		return nil
	})

	l.Prepare(func(app *goapp.App) error {
		mux := app.Get("mux").(*goji.Mux)
		mux.HandleFuncC(pat.Get("/api/mirrors"), Api_GET_MirrorServices(app))
		mux.HandleFunc(pat.Get("/api/sse"), Api_GET_Sse(app))
		mux.HandleFuncC(pat.Get("/api/ping"), Api_GET_Ping(app))

		return nil
	})

	l.Run(func(app *goapp.App, state *goapp.GoroutineState) error {
		ch := app.Get("pkgmirror.channel.state").(chan pkgmirror.State)
		brk := app.Get("pkgmirror.sse.broker").(*pkgmirror.SseBroker)
		logger := app.Get("logger").(*log.Logger)

		logger.Info("Start the SSE Broker")
		// start the broken
		go brk.Listen()

		states := map[string]pkgmirror.State{}

		// send the current state
		brk.OnConnect(func() {
			for _, s := range states {
				data, _ := json.Marshal(&s)

				brk.Notifier <- data
			}
		})

		for {
			select {
			case s := <-ch:
				logger.WithFields(log.Fields{
					"id":      s.Id,
					"message": s.Message,
					"status":  s.Status,
				}).Debug("Receive message")

				states[s.Id] = s

				data, _ := json.Marshal(&s)

				brk.Notifier <- data
			case <-state.In:
				return nil // exit
			}
		}
	})

}
<commit_msg>fix(sse): fix race condition with update status<commit_after>// Copyright © 2016 Thomas Rabaix <thomas.rabaix@gmail.com>.
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

package api

import (
	"encoding/json"
	"sync"

	log "github.com/Sirupsen/logrus"
	"github.com/rande/goapp"
	"github.com/rande/pkgmirror"
	"goji.io"
	"goji.io/pat"
)

func ConfigureApp(config *pkgmirror.Config, l *goapp.Lifecycle) {

	l.Register(func(app *goapp.App) error {
		app.Set("pkgmirror.channel.state", func(app *goapp.App) interface{} {
			return make(chan pkgmirror.State)
		})

		app.Set("pkgmirror.sse.broker", func(app *goapp.App) interface{} {
			return pkgmirror.NewSseBroker()
		})

		return nil
	})

	l.Prepare(func(app *goapp.App) error {
		mux := app.Get("mux").(*goji.Mux)
		mux.HandleFuncC(pat.Get("/api/mirrors"), Api_GET_MirrorServices(app))
		mux.HandleFunc(pat.Get("/api/sse"), Api_GET_Sse(app))
		mux.HandleFuncC(pat.Get("/api/ping"), Api_GET_Ping(app))

		return nil
	})

	l.Run(func(app *goapp.App, state *goapp.GoroutineState) error {
		ch := app.Get("pkgmirror.channel.state").(chan pkgmirror.State)
		brk := app.Get("pkgmirror.sse.broker").(*pkgmirror.SseBroker)
		logger := app.Get("logger").(*log.Logger)

		logger.Info("Start the SSE Broker")
		// start the broken
		go brk.Listen()

		states := map[string]pkgmirror.State{}

		l := sync.Mutex{}

		// send the current state
		brk.OnConnect(func() {
			l.Lock()
			for _, s := range states {
				data, _ := json.Marshal(&s)

				brk.Notifier <- data
			}
			l.Unlock()
		})

		for {
			select {
			case s := <-ch:
				logger.WithFields(log.Fields{
					"id":      s.Id,
					"message": s.Message,
					"status":  s.Status,
				}).Debug("Receive message")

				l.Lock()
				states[s.Id] = s
				l.Unlock()

				data, _ := json.Marshal(&s)

				brk.Notifier <- data
			case <-state.In:
				return nil // exit
			}
		}
	})

}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Knative Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// This file contains logic to encapsulate flags which are needed to specify
// what cluster, etc. to use for e2e tests.

package test

import (
	"flag"
	"fmt"
	"strings"

	"log"

	"knative.dev/eventing/test/common"
	pkgTest "knative.dev/pkg/test"
	testLogging "knative.dev/pkg/test/logging"
)

// EventingFlags holds the command line flags specific to knative/eventing.
var EventingFlags = initializeEventingFlags()

// Channels holds the Channels we want to run test against.
type Channels []string

func (channels *Channels) String() string {
	return fmt.Sprint(*channels)
}

// Set converts the input string to Channels.
// The default Channel we will test against is InMemoryChannel.
func (channels *Channels) Set(value string) error {
	for _, channel := range strings.Split(value, ",") {
		channel := strings.TrimSpace(channel)
		if !isValid(channel) {
			log.Fatalf("The given channel name %q is invalid, tests cannot be run.\n", channel)
		}

		*channels = append(*channels, channel)
	}
	return nil
}

// Check if the channel name is valid.
func isValid(channel string) bool {
	return strings.HasSuffix(channel, "Channel")
}

// EventingEnvironmentFlags holds the e2e flags needed only by the eventing repo.
type EventingEnvironmentFlags struct {
	Channels
}

func initializeEventingFlags() *EventingEnvironmentFlags {
	f := EventingEnvironmentFlags{}

	flag.Var(&f.Channels, "channels", "The names of the channels, which are separated by comma.")

	flag.Parse()

	// If no channel is passed through the flag, initialize it as the DefaultChannel.
	if f.Channels == nil || len(f.Channels) == 0 {
		f.Channels = []string{common.DefaultChannel}
	}

	testLogging.InitializeLogger(pkgTest.Flags.LogVerbose)
	if pkgTest.Flags.EmitMetrics {
		testLogging.InitializeMetricExporter("eventing")
	}

	return &f
}
<commit_msg>eventing test work with go1.13 (#1830)<commit_after>/*
Copyright 2018 The Knative Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// This file contains logic to encapsulate flags which are needed to specify
// what cluster, etc. to use for e2e tests.

package test

import (
	"flag"
	"fmt"
	"strings"

	"log"

	"knative.dev/eventing/test/common"
	pkgTest "knative.dev/pkg/test"
	testLogging "knative.dev/pkg/test/logging"
)

// EventingFlags holds the command line flags specific to knative/eventing.
var EventingFlags = initializeEventingFlags()

// Channels holds the Channels we want to run test against.
type Channels []string

func (channels *Channels) String() string {
	return fmt.Sprint(*channels)
}

// Set converts the input string to Channels.
// The default Channel we will test against is InMemoryChannel.
func (channels *Channels) Set(value string) error {
	for _, channel := range strings.Split(value, ",") {
		channel := strings.TrimSpace(channel)
		if !isValid(channel) {
			log.Fatalf("The given channel name %q is invalid, tests cannot be run.\n", channel)
		}

		*channels = append(*channels, channel)
	}
	return nil
}

// Check if the channel name is valid.
func isValid(channel string) bool {
	return strings.HasSuffix(channel, "Channel")
}

// EventingEnvironmentFlags holds the e2e flags needed only by the eventing repo.
type EventingEnvironmentFlags struct {
	Channels
}

// initializeEventingFlags registers flags used by e2e tests, calling flag.Parse() here would fail in
// go1.13+, see https://github.com/knative/test-infra/issues/1329 for details
func initializeEventingFlags() *EventingEnvironmentFlags {
	f := EventingEnvironmentFlags{}

	flag.Var(&f.Channels, "channels", "The names of the channels, which are separated by comma.")

	// If no channel is passed through the flag, initialize it as the DefaultChannel.
	if f.Channels == nil || len(f.Channels) == 0 {
		f.Channels = []string{common.DefaultChannel}
	}

	testLogging.InitializeLogger(pkgTest.Flags.LogVerbose)
	if pkgTest.Flags.EmitMetrics {
		testLogging.InitializeMetricExporter("eventing")
	}

	return &f
}
<|endoftext|>


<commit_before>package whois

import (
	"os"
	"testing"

	"github.com/domainr/whoistest"
	"github.com/nbio/st"
)

func TestReadMIME(t *testing.T) {
	fns, err := whoistest.ResponseFiles()
	st.Assert(t, err, nil)
	for _, fn := range fns {
		// fmt.Printf("%s\n", fn)
		res, err := readMIMEFile(fn)
		st.Refute(t, res, nil)
		st.Assert(t, err, nil)
		// fmt.Printf("%#v\n\n", res)
	}
}

func readMIMEFile(fn string) (*Response, error) {
	f, err := os.Open(fn)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	return ReadMIME(f)
}

func TestPIRRateLimitText(t *testing.T) {
	req, err := Resolve("google.org")
	st.Assert(t, err, nil)
	res, err := Fetch(req)
	st.Assert(t, err, nil)
	st.Expect(t, res.MediaType, "text/plain")
	st.Expect(t, res.Charset, "iso-8859-1")
	res.Body = []byte("WHOIS LIMIT EXCEEDED - SEE WWW.PIR.ORG/WHOIS FOR DETAILS\n")
	res.DetectContentType("")
	st.Expect(t, res.MediaType, "text/plain")
	st.Expect(t, res.Charset, "windows-1252")
	h := res.Header()
	st.Expect(t, h.Get("Content-Type"), "text/plain; charset=windows-1252")
}
<commit_msg>Fix test.<commit_after>package whois

import (
	"os"
	"testing"

	"github.com/domainr/whoistest"
	"github.com/nbio/st"
)

func TestReadMIME(t *testing.T) {
	fns, err := whoistest.ResponseFiles()
	st.Assert(t, err, nil)
	for _, fn := range fns {
		// fmt.Printf("%s\n", fn)
		res, err := readMIMEFile(fn)
		st.Refute(t, res, nil)
		st.Assert(t, err, nil)
		// fmt.Printf("%#v\n\n", res)
	}
}

func readMIMEFile(fn string) (*Response, error) {
	f, err := os.Open(fn)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	return ReadMIME(f)
}

func TestPIRRateLimitText(t *testing.T) {
	req, err := Resolve("google.org")
	st.Assert(t, err, nil)
	res, err := req.Fetch()
	st.Assert(t, err, nil)
	st.Expect(t, res.MediaType, "text/plain")
	st.Expect(t, res.Charset, "iso-8859-1")
	res.Body = []byte("WHOIS LIMIT EXCEEDED - SEE WWW.PIR.ORG/WHOIS FOR DETAILS\n")
	res.DetectContentType("")
	st.Expect(t, res.MediaType, "text/plain")
	st.Expect(t, res.Charset, "windows-1252")
	h := res.Header()
	st.Expect(t, h.Get("Content-Type"), "text/plain; charset=windows-1252")
}
<|endoftext|>


<commit_before><commit_msg>Use the reset/renew on the ro txns rather than abort/new.<commit_after><|endoftext|>


<commit_before><commit_msg>Improve error logging<commit_after><|endoftext|>


<commit_before>package joystick

import (
	"testing"

	"github.com/hybridgroup/gobot"
)

func initTestJoystickAdaptor() *JoystickAdaptor {
	a := NewJoystickAdaptor("bot")
	a.connect = func(j *JoystickAdaptor) (err error) {
		j.joystick = &testJoystick{}
		return nil
	}
	return a
}

func TestJoystickAdaptorConnect(t *testing.T) {
	a := initTestJoystickAdaptor()
	gobot.Assert(t, a.Connect(), nil)
}

func TestJoystickAdaptorFinalize(t *testing.T) {
	a := initTestJoystickAdaptor()
	a.Connect()
	gobot.Assert(t, a.Finalize(), nil)
}
<commit_msg>Increase joystick test coverage<commit_after>package joystick

import (
	"errors"
	"testing"

	"github.com/hybridgroup/gobot"
)

func initTestJoystickAdaptor() *JoystickAdaptor {
	a := NewJoystickAdaptor("bot")
	a.connect = func(j *JoystickAdaptor) (err error) {
		j.joystick = &testJoystick{}
		return nil
	}
	return a
}

func TestJoystickAdaptorConnect(t *testing.T) {
	a := initTestJoystickAdaptor()
	gobot.Assert(t, a.Connect(), nil)

	a = NewJoystickAdaptor("bot")
	gobot.Assert(t, a.Connect(), errors.New("No joystick available"))
}

func TestJoystickAdaptorFinalize(t *testing.T) {
	a := initTestJoystickAdaptor()
	a.Connect()
	gobot.Assert(t, a.Finalize(), nil)
}
<|endoftext|>


<commit_before>package main

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

	"github.com/google/uuid"
	"github.com/gorilla/sessions"
	"github.com/gorilla/websocket"
	garbler "github.com/michaelbironneau/garbler/lib"
	"github.com/tejpbit/talarlista/backend/messages"
)

const SESSION_KEY = "talarlista_session"
const UUID_KEY = "uuid"

const (
	NoUserForSession   = "The provided session have no corresponding user in the state."
	NoUUIDInSession    = "The provided session does not contain a UUID for the user."
	NoUserForUUID      = "The UUID provided from the session does not correspond to a user in the state."
	UserIsNotAdmin     = "User is not admin"
	UserAlreadyInList  = "User already in list. Cannot be added twice."
	UserNotFoundInList = "User not in current list"
	NoListForUUID      = "The UUID does not correspond to any list"
)

type Hub struct {
	Users             map[uuid.UUID]*User
	AdminCreatedUsers map[uuid.UUID]*User
	SpeakerLists      []*SpeakerList
	connectedUsers    map[uuid.UUID]*User
	oneTimePasswords  []string
	input             chan UserEvent
	messageHandlers   map[string]MessageHandler
}

var reqs = garbler.PasswordStrengthRequirements{
	MinimumTotalLength: 8,
	MaximumTotalLength: 8,
	Uppercase:          3,
	Digits:             2,
}

var store = sessions.NewCookieStore([]byte("this is the secret stuff"))

func CreateHub() Hub {
	store.Options = &sessions.Options{
		MaxAge:   86400,
		HttpOnly: true,
	}

	speakerLists := []*SpeakerList{}

	hub := Hub{
		Users:             make(map[uuid.UUID]*User),
		AdminCreatedUsers: make(map[uuid.UUID]*User),
		SpeakerLists:      speakerLists,
		connectedUsers:    make(map[uuid.UUID]*User),
	}

	hub.generateNewPassword()
	hub.generateNewPassword()
	hub.generateNewPassword()

	hub.messageHandlers = CreateHandlers(&hub)
	return hub
}

func (hub *Hub) Start() error {
	if hub.input != nil {
		return errors.New("Hub already running")
	}
	hub.input = make(chan UserEvent, 10)
	for _, user := range hub.Users {
		user.hubChannel = hub.input
	}

	go hub.listenForUserEvents()
	return nil
}

func (hub *Hub) Broadcast(sendEvent messages.SendEvent) {
	for _, user := range hub.Users {
		user.input <- sendEvent
	}
}

func (hub *Hub) AdminBroadcast(sendEvent messages.SendEvent) {
	for _, user := range hub.Users {
		if user.IsAdmin {
			user.input <- sendEvent
		}
	}
}

func (hub *Hub) listenForUserEvents() {
	for {
		event := <-hub.input
		handler, ok := hub.messageHandlers[event.messageType]
		if !ok {
			sendError(event.user.input, fmt.Sprintf("No handler for the topic '%s'.", event.messageType))
			log.Printf("No handler for the topic '%s'", event.messageType)
			continue
		}
		handler.handle(event)
	}
}

func (hub *Hub) addUserToList(id uuid.UUID, user *User) error {
	listIndex := -1
	for i, list := range hub.SpeakerLists {
		if list.Id == id {
			listIndex = i
			break
		}
	}
	if listIndex == -1 {
		return errors.New("Could not find list for provided id")
	}
	list := hub.SpeakerLists[listIndex]
	ok := list.AddUser(user)
	if !ok {
		return errors.New("User already in list")
	}
	return nil
}

func (hub *Hub) removeUserFromList(id uuid.UUID, user *User) error {
	listIndex := -1
	for i, list := range hub.SpeakerLists {
		if list.Id == id {
			listIndex = i
			break
		}
	}
	if listIndex == -1 {
		return errors.New("Could not find list for provided id")
	}
	list := hub.SpeakerLists[listIndex]
	ok := list.RemoveUser(user)
	if !ok {
		return errors.New("User not in list")
	}
	return nil
}

func (hub *Hub) deleteList(id uuid.UUID) error {
	i := -1
	for index, list := range hub.SpeakerLists {
		if list.Id == id {
			i = index
		}
	}
	if i == -1 {
		return errors.New("List not found")
	}

	copy(hub.SpeakerLists[i:], hub.SpeakerLists[i+1:])
	hub.SpeakerLists[len(hub.SpeakerLists)-1] = nil
	hub.SpeakerLists = hub.SpeakerLists[:len(hub.SpeakerLists)-1]
	return nil
}

func (hub *Hub) getList(id uuid.UUID) (*SpeakerList, error) {

	for _, list := range hub.SpeakerLists {
		if id == list.Id {
			return list, nil
		}
	}
	return nil, errors.New(NoListForUUID)
}

func (hub Hub) getUserFromRequest(req *http.Request) (*User, error) {
	session, err := store.Get(req, SESSION_KEY)
	if err != nil {
		return nil, errors.New("Could not get session from storage")
	}
	return hub.getUserFromSession(session)
}

func (hub Hub) getUserFromSession(session *sessions.Session) (*User, error) {
	id, err := getUUIDfromSession(session)

	if err != nil {
		return nil, err
	}

	return hub.getUser(id)
}

func (hub Hub) getUser(id uuid.UUID) (*User, error) {
	user, ok := hub.Users[id]

	if !ok {
		return nil, errors.New(NoUserForUUID)
	}
	return user, nil
}

func (hub Hub) isUserNickTaken(nick string) bool {
	for _, user := range hub.Users {
		if user.Nick == nick {
			return true
		}
	}
	for _, user := range hub.AdminCreatedUsers {
		if user.Nick == nick {
			return true
		}
	}
	return false
}

func (hub Hub) addUser(user *User) bool {
	_, ok := hub.Users[user.Id]
	if ok {
		return false
	}
	hub.Users[user.Id] = user
	return true
}

func (hub Hub) updateUser(updatedUser *User) (success bool) {
	user, inUsers := hub.Users[updatedUser.Id]
	adminCreatedUser, inAdminCreatedUsers := hub.AdminCreatedUsers[updatedUser.Id]
	success = inUsers != inAdminCreatedUsers
	if !success {
		return
	}

	if inUsers {
		user.Nick = updatedUser.Nick
	} else if inAdminCreatedUsers {
		adminCreatedUser.Nick = updatedUser.Nick
	}

	return
}

func (hub Hub) deleteUser(user *User) {
	for _, list := range hub.SpeakerLists {
		list.RemoveUser(user)
	}
	delete(hub.AdminCreatedUsers, user.Id)
	delete(hub.Users, user.Id)
	delete(hub.connectedUsers, user.Id)
}

func (hub Hub) addAdminCreatedUser(user *User) bool {
	_, ok := hub.AdminCreatedUsers[user.Id]
	if ok {
		return false
	}
	user.Id = uuid.New()
	hub.AdminCreatedUsers[user.Id] = user
	return true
}

func (hub *Hub) tryAdminLogin(user *User, password string) bool {
	passwordIndex := -1
	for i, k := range hub.oneTimePasswords {
		if k == password {
			passwordIndex = i
			break
		}
	}

	ok := passwordIndex != -1
	if ok {
		user.IsAdmin = true
		hub.oneTimePasswords = append(hub.oneTimePasswords[:passwordIndex], hub.oneTimePasswords[passwordIndex+1:]...)
	}
	return ok
}

type PasswordListResponse struct {
	Passwords []string `json:"passwords"`
}

func (hub *Hub) generateNewPassword() {
	newPassword, err := garbler.NewPassword(&reqs)
	if err != nil {
		log.Panicf("Could not generate password: %v", err)
	}
	log.Printf("Generated password: %s", newPassword)
	hub.oneTimePasswords = append(hub.oneTimePasswords, newPassword)
}

func createPasswordListResponse(passwordList []string) (messages.SendEvent, error) {
	response := PasswordListResponse{passwordList}
	jsonObj, err := json.Marshal(response)
	if err != nil {
		return messages.SendEvent{}, err
	} else {
		return messages.SendEvent{messages.ADMIN_UPDATE_PASSWORD_LIST, jsonObj}, nil
	}
}

func getUUIDfromSession(session *sessions.Session) (uuid.UUID, error) {
	storedValue, ok := session.Values[UUID_KEY]
	if !ok {
		return uuid.UUID{}, errors.New(NoUUIDInSession)
	}

	stringId, ok := storedValue.(string)
	if !ok {
		return uuid.UUID{}, errors.New("Could not cast stored value to string")
	}

	id, err := uuid.Parse(stringId)
	if err != nil {
		return uuid.UUID{}, errors.New("Could not parse stored string into uuid.UUID")
	}

	return id, nil
}

func (hub *Hub) ServeWs(w http.ResponseWriter, r *http.Request) {
	session, err := store.Get(r, SESSION_KEY)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	if session.IsNew {
		newUser := CreateUser()
		session.Values[UUID_KEY] = newUser.Id.String()
		newUser.hubChannel = hub.input
		hub.addUser(newUser)

		err = session.Save(r, w)

		if err != nil {
			log.Printf("Error when saving session to storage: %v\n", err)
		}
	}

	conn, err := upgrader.Upgrade(w, r, w.Header())
	if err != nil {
		log.Println(err)
		return
	}

	user, err := hub.getUserFromRequest(r)
	if err != nil {
		errorResp, err := json.Marshal(messages.JsonMessage{Message: err.Error()})

		if err != nil {
			errorEvent := messages.SendEvent{Topic: messages.ERROR, Content: []byte(err.Error())}
			conn.WriteMessage(websocket.TextMessage, []byte(errorEvent.String()))
		} else {
			errorEvent := messages.SendEvent{Topic: messages.ERROR, Content: errorResp}
			conn.WriteMessage(websocket.TextMessage, []byte(errorEvent.String()))
		}
		conn.Close()
		return
	}

	user.ServeWS(conn)
}

func createListsResponse(lists []*SpeakerList) (messages.SendEvent, error) {
	listsObj, err := json.Marshal(lists)
	if err != nil {
		return messages.SendEvent{}, err
	} else {
		return messages.SendEvent{messages.LISTS_UPDATE, listsObj}, nil
	}
}

type UsersResponse struct {
	Users             []*User `json:"users"`
	AdminCreatedUsers []*User `json:"adminCreatedUsers"`
}

func (hub *Hub) createUsersResponse() (messages.SendEvent, error) {
	var users, adminCreatedUsers []*User
	for _, u := range hub.Users {
		users = append(users, u)
	}
	for _, u := range hub.AdminCreatedUsers {
		adminCreatedUsers = append(adminCreatedUsers, u)
	}

	usersUbj, err := json.Marshal(UsersResponse{Users: users, AdminCreatedUsers: adminCreatedUsers})
	if err != nil {
		return messages.SendEvent{}, err
	} else {
		return messages.SendEvent{messages.USERS_UPDATE, usersUbj}, nil
	}
}

func createListResponse(list *SpeakerList) (messages.SendEvent, error) {
	listObj, err := json.Marshal(list)
	if err != nil {
		return messages.SendEvent{}, err
	} else {
		return messages.SendEvent{messages.LIST_UPDATE, listObj}, nil
	}
}

func sendNotification(userChannel chan messages.SendEvent, topic, message string) {
	respObj, err := json.Marshal(messages.JsonMessage{message})
	if err != nil {
		log.Printf("Could not marshal error message: %v", err)
	}
	userChannel <- messages.SendEvent{topic, respObj}
}

func sendError(userChannel chan messages.SendEvent, message string) {
	sendNotification(userChannel, messages.ERROR, message)
}

func sendSuccess(userChannel chan messages.SendEvent, message string) {
	sendNotification(userChannel, messages.SUCCESS, message)
}

var upgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
	CheckOrigin: func(r *http.Request) bool {
		return true
	},
}
<commit_msg>Generate uuid and append to cookie name. fixes #15<commit_after>package main

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

	"github.com/google/uuid"
	"github.com/gorilla/sessions"
	"github.com/gorilla/websocket"
	garbler "github.com/michaelbironneau/garbler/lib"
	"github.com/tejpbit/talarlista/backend/messages"
)

const SESSION_KEY = "talarlista_session"
const UUID_KEY = "uuid"

const (
	NoUserForSession   = "The provided session have no corresponding user in the state."
	NoUUIDInSession    = "The provided session does not contain a UUID for the user."
	NoUserForUUID      = "The UUID provided from the session does not correspond to a user in the state."
	UserIsNotAdmin     = "User is not admin"
	UserAlreadyInList  = "User already in list. Cannot be added twice."
	UserNotFoundInList = "User not in current list"
	NoListForUUID      = "The UUID does not correspond to any list"
)

type Hub struct {
	ID                string
	Users             map[uuid.UUID]*User
	AdminCreatedUsers map[uuid.UUID]*User
	SpeakerLists      []*SpeakerList
	connectedUsers    map[uuid.UUID]*User
	oneTimePasswords  []string
	input             chan UserEvent
	messageHandlers   map[string]MessageHandler
}

var reqs = garbler.PasswordStrengthRequirements{
	MinimumTotalLength: 8,
	MaximumTotalLength: 8,
	Uppercase:          3,
	Digits:             2,
}

var store = sessions.NewCookieStore([]byte("this is the secret stuff"))

func CreateHub() Hub {
	store.Options = &sessions.Options{
		MaxAge:   86400,
		HttpOnly: true,
	}

	speakerLists := []*SpeakerList{}

	hub := Hub{
		ID:                fmt.Sprintf("%s_%s", SESSION_KEY, uuid.New().String()),
		Users:             make(map[uuid.UUID]*User),
		AdminCreatedUsers: make(map[uuid.UUID]*User),
		SpeakerLists:      speakerLists,
		connectedUsers:    make(map[uuid.UUID]*User),
	}

	hub.generateNewPassword()
	hub.generateNewPassword()
	hub.generateNewPassword()

	hub.messageHandlers = CreateHandlers(&hub)
	return hub
}

func (hub *Hub) Start() error {
	if hub.input != nil {
		return errors.New("Hub already running")
	}
	hub.input = make(chan UserEvent, 10)
	for _, user := range hub.Users {
		user.hubChannel = hub.input
	}

	go hub.listenForUserEvents()
	return nil
}

func (hub *Hub) Broadcast(sendEvent messages.SendEvent) {
	for _, user := range hub.Users {
		user.input <- sendEvent
	}
}

func (hub *Hub) AdminBroadcast(sendEvent messages.SendEvent) {
	for _, user := range hub.Users {
		if user.IsAdmin {
			user.input <- sendEvent
		}
	}
}

func (hub *Hub) listenForUserEvents() {
	for {
		event := <-hub.input
		handler, ok := hub.messageHandlers[event.messageType]
		if !ok {
			sendError(event.user.input, fmt.Sprintf("No handler for the topic '%s'.", event.messageType))
			log.Printf("No handler for the topic '%s'", event.messageType)
			continue
		}
		handler.handle(event)
	}
}

func (hub *Hub) addUserToList(id uuid.UUID, user *User) error {
	listIndex := -1
	for i, list := range hub.SpeakerLists {
		if list.Id == id {
			listIndex = i
			break
		}
	}
	if listIndex == -1 {
		return errors.New("Could not find list for provided id")
	}
	list := hub.SpeakerLists[listIndex]
	ok := list.AddUser(user)
	if !ok {
		return errors.New("User already in list")
	}
	return nil
}

func (hub *Hub) removeUserFromList(id uuid.UUID, user *User) error {
	listIndex := -1
	for i, list := range hub.SpeakerLists {
		if list.Id == id {
			listIndex = i
			break
		}
	}
	if listIndex == -1 {
		return errors.New("Could not find list for provided id")
	}
	list := hub.SpeakerLists[listIndex]
	ok := list.RemoveUser(user)
	if !ok {
		return errors.New("User not in list")
	}
	return nil
}

func (hub *Hub) deleteList(id uuid.UUID) error {
	i := -1
	for index, list := range hub.SpeakerLists {
		if list.Id == id {
			i = index
		}
	}
	if i == -1 {
		return errors.New("List not found")
	}

	copy(hub.SpeakerLists[i:], hub.SpeakerLists[i+1:])
	hub.SpeakerLists[len(hub.SpeakerLists)-1] = nil
	hub.SpeakerLists = hub.SpeakerLists[:len(hub.SpeakerLists)-1]
	return nil
}

func (hub *Hub) getList(id uuid.UUID) (*SpeakerList, error) {

	for _, list := range hub.SpeakerLists {
		if id == list.Id {
			return list, nil
		}
	}
	return nil, errors.New(NoListForUUID)
}

func (hub Hub) getUserFromRequest(req *http.Request) (*User, error) {
	session, err := store.Get(req, hub.ID)
	if err != nil {
		return nil, errors.New("Could not get session from storage")
	}
	return hub.getUserFromSession(session)
}

func (hub Hub) getUserFromSession(session *sessions.Session) (*User, error) {
	id, err := getUUIDfromSession(session)

	if err != nil {
		return nil, err
	}

	return hub.getUser(id)
}

func (hub Hub) getUser(id uuid.UUID) (*User, error) {
	user, ok := hub.Users[id]

	if !ok {
		return nil, errors.New(NoUserForUUID)
	}
	return user, nil
}

func (hub Hub) isUserNickTaken(nick string) bool {
	for _, user := range hub.Users {
		if user.Nick == nick {
			return true
		}
	}
	for _, user := range hub.AdminCreatedUsers {
		if user.Nick == nick {
			return true
		}
	}
	return false
}

func (hub Hub) addUser(user *User) bool {
	_, ok := hub.Users[user.Id]
	if ok {
		return false
	}
	hub.Users[user.Id] = user
	return true
}

func (hub Hub) updateUser(updatedUser *User) (success bool) {
	user, inUsers := hub.Users[updatedUser.Id]
	adminCreatedUser, inAdminCreatedUsers := hub.AdminCreatedUsers[updatedUser.Id]
	success = inUsers != inAdminCreatedUsers
	if !success {
		return
	}

	if inUsers {
		user.Nick = updatedUser.Nick
	} else if inAdminCreatedUsers {
		adminCreatedUser.Nick = updatedUser.Nick
	}

	return
}

func (hub Hub) deleteUser(user *User) {
	for _, list := range hub.SpeakerLists {
		list.RemoveUser(user)
	}
	delete(hub.AdminCreatedUsers, user.Id)
	delete(hub.Users, user.Id)
	delete(hub.connectedUsers, user.Id)
}

func (hub Hub) addAdminCreatedUser(user *User) bool {
	_, ok := hub.AdminCreatedUsers[user.Id]
	if ok {
		return false
	}
	user.Id = uuid.New()
	hub.AdminCreatedUsers[user.Id] = user
	return true
}

func (hub *Hub) tryAdminLogin(user *User, password string) bool {
	passwordIndex := -1
	for i, k := range hub.oneTimePasswords {
		if k == password {
			passwordIndex = i
			break
		}
	}

	ok := passwordIndex != -1
	if ok {
		user.IsAdmin = true
		hub.oneTimePasswords = append(hub.oneTimePasswords[:passwordIndex], hub.oneTimePasswords[passwordIndex+1:]...)
	}
	return ok
}

type PasswordListResponse struct {
	Passwords []string `json:"passwords"`
}

func (hub *Hub) generateNewPassword() {
	newPassword, err := garbler.NewPassword(&reqs)
	if err != nil {
		log.Panicf("Could not generate password: %v", err)
	}
	log.Printf("Generated password: %s", newPassword)
	hub.oneTimePasswords = append(hub.oneTimePasswords, newPassword)
}

func createPasswordListResponse(passwordList []string) (messages.SendEvent, error) {
	response := PasswordListResponse{passwordList}
	jsonObj, err := json.Marshal(response)
	if err != nil {
		return messages.SendEvent{}, err
	} else {
		return messages.SendEvent{messages.ADMIN_UPDATE_PASSWORD_LIST, jsonObj}, nil
	}
}

func getUUIDfromSession(session *sessions.Session) (uuid.UUID, error) {
	storedValue, ok := session.Values[UUID_KEY]
	if !ok {
		return uuid.UUID{}, errors.New(NoUUIDInSession)
	}

	stringId, ok := storedValue.(string)
	if !ok {
		return uuid.UUID{}, errors.New("Could not cast stored value to string")
	}

	id, err := uuid.Parse(stringId)
	if err != nil {
		return uuid.UUID{}, errors.New("Could not parse stored string into uuid.UUID")
	}

	return id, nil
}

func (hub *Hub) ServeWs(w http.ResponseWriter, r *http.Request) {
	session, err := store.Get(r, hub.ID)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	if session.IsNew {
		newUser := CreateUser()
		session.Values[UUID_KEY] = newUser.Id.String()
		newUser.hubChannel = hub.input
		hub.addUser(newUser)

		err = session.Save(r, w)

		if err != nil {
			log.Printf("Error when saving session to storage: %v\n", err)
		}
	}

	conn, err := upgrader.Upgrade(w, r, w.Header())
	if err != nil {
		log.Println(err)
		return
	}

	user, err := hub.getUserFromRequest(r)
	if err != nil {
		errorResp, err := json.Marshal(messages.JsonMessage{Message: err.Error()})

		if err != nil {
			errorEvent := messages.SendEvent{Topic: messages.ERROR, Content: []byte(err.Error())}
			conn.WriteMessage(websocket.TextMessage, []byte(errorEvent.String()))
		} else {
			errorEvent := messages.SendEvent{Topic: messages.ERROR, Content: errorResp}
			conn.WriteMessage(websocket.TextMessage, []byte(errorEvent.String()))
		}
		conn.Close()
		return
	}

	user.ServeWS(conn)
}

func createListsResponse(lists []*SpeakerList) (messages.SendEvent, error) {
	listsObj, err := json.Marshal(lists)
	if err != nil {
		return messages.SendEvent{}, err
	} else {
		return messages.SendEvent{messages.LISTS_UPDATE, listsObj}, nil
	}
}

type UsersResponse struct {
	Users             []*User `json:"users"`
	AdminCreatedUsers []*User `json:"adminCreatedUsers"`
}

func (hub *Hub) createUsersResponse() (messages.SendEvent, error) {
	var users, adminCreatedUsers []*User
	for _, u := range hub.Users {
		users = append(users, u)
	}
	for _, u := range hub.AdminCreatedUsers {
		adminCreatedUsers = append(adminCreatedUsers, u)
	}

	usersUbj, err := json.Marshal(UsersResponse{Users: users, AdminCreatedUsers: adminCreatedUsers})
	if err != nil {
		return messages.SendEvent{}, err
	} else {
		return messages.SendEvent{messages.USERS_UPDATE, usersUbj}, nil
	}
}

func createListResponse(list *SpeakerList) (messages.SendEvent, error) {
	listObj, err := json.Marshal(list)
	if err != nil {
		return messages.SendEvent{}, err
	} else {
		return messages.SendEvent{messages.LIST_UPDATE, listObj}, nil
	}
}

func sendNotification(userChannel chan messages.SendEvent, topic, message string) {
	respObj, err := json.Marshal(messages.JsonMessage{message})
	if err != nil {
		log.Printf("Could not marshal error message: %v", err)
	}
	userChannel <- messages.SendEvent{topic, respObj}
}

func sendError(userChannel chan messages.SendEvent, message string) {
	sendNotification(userChannel, messages.ERROR, message)
}

func sendSuccess(userChannel chan messages.SendEvent, message string) {
	sendNotification(userChannel, messages.SUCCESS, message)
}

var upgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
	CheckOrigin: func(r *http.Request) bool {
		return true
	},
}
<|endoftext|>


<commit_before><commit_msg>add SectionReader, ReaderAt.<commit_after><|endoftext|>


<commit_before><commit_msg>update go-sample-0 expected output<commit_after><|endoftext|>


<commit_before>package interchange

import (
	"code.google.com/p/go.net/context"
	"errors"
	"fmt"
	"sort"
	"strings"
)

const (
	// Delimiter between topic elements.
	topicDelimeter = "."

	// Name of the Arke hub's root topic.
	rootName = "."
)

func copyTopic(topic []string) []string {
	cp := make([]string, len(topic))
	copy(cp, topic)
	return cp
}

// isValidTopic determines whether the provided topic array is valid.
func isValidTopic(topic []string) bool {
	if len(topic) == 0 {
		return false
	}

	for i := range topic {
		token := topic[i]
		switch token {
		case "", rootName:
			return false
		}
	}

	return true
}

// A topicNode represents a vertex in the topic trie.
//
// Topic nodes occur when there exists a subscriber on a topic.  You can think
// of a topic as a word whose "characters" are delimited by periods. The topic
// trie as such acts as a trie over these tokens The root is implicitly named
// '.' and acts specially as it doesn't collapse, the following example will
// show this in practice.
//
// Example: for the trie consisting of subscribers on foo.bar.baz and
// foo.bar.qux there will be four topic nodes: one for the root (whose name is
// '.'), one for the branching node whose name is foo.bar and one node for each
// of baz and qux.
type topicNode struct {
	ctx      context.Context
	deadSubs int

	Cancel      context.CancelFunc
	Name        []string
	Children    []*topicNode
	Subscribers []*subscriber
}

func newTopicNode(ctx context.Context, cancel context.CancelFunc, name []string) *topicNode {
	return &topicNode{
		ctx:         ctx,
		Cancel:      cancel,
		Name:        copyTopic(name),
		Children:    make([]*topicNode, 0, 10),
		Subscribers: make([]*subscriber, 0, 10),
	}
}

type byName []*topicNode

func (n byName) Len() int      { return len(n) }
func (n byName) Swap(i, j int) { n[i], n[j] = n[j], n[i] }
func (n byName) Less(i, j int) bool {
	return strings.Join(n[i].Name, ".") < strings.Join(n[j].Name, ".")
}

// AddSub creates a subscriber, attaches it to this topicNode and starts it.
//
// Parameters:
//  sub: The subscription from which to build the new Subscriber
//  cleanup: The channel via which the subscriber's 'death' will notify the hub.
func (t *topicNode) AddSub(sub *subscription, cleanup chan<- []string) *subscriber {
	ctx, _ := context.WithDeadline(t.ctx, sub.Deadline)

	new_subscriber := createSubscriber(sub, ctx)

	// When the subscriber is done, notify the hub for garbage collection.
	go func(topic []string, notify chan<- []string) {
		<-ctx.Done()
		notify <- topic
	}(sub.Topic, cleanup)

	t.Subscribers = append(t.Subscribers, new_subscriber)

	return new_subscriber
}

// MaybeFindTopic searches the given topic trie for the provided topic.
//
// An empty `rest` indicates success.
//
// If the topic isn't found, it returns the closest ancestor node to what would
// be the parent of the topic if it did exist.  This returned node may require
// realigning its name, e.g. `rest` may be foo.bar.baz where the name of the
// returned node is foo.bar.qux.
//
// In the failure case, rest will be the remainder of the name string that
// wasn't found.
//
// Assumes `topic` is in canonical form (e.g. no empty elements or those of the
// form of topicDelimeter except in the case of a root topic).
// If a non-canonical topic is passed, no matching topic will be found.
func (t *topicNode) MaybeFindTopic(topic []string) (nearestTopic *topicNode, rest, overlap []string) {
	if len(topic) < 1 || t == nil {
		return t, []string{}, []string{}
	}

	nearestTopic = t
	rest = topic
	overlap = []string{}

	// Determine overlap between provided topic and t.Name
	if t.Name[0] != rootName {
		i := 0
		for i < len(t.Name) && i < len(topic) {
			if topic[i] != t.Name[i] {
				break
			}
			i += 1
		}
		overlap = topic[:i]
	} else if len(t.Name) > 1 {
		// The root node must have a name of at most length 1.
		panic(
			errors.New(
				fmt.Sprintf(
					"Hub root has name longer than 1 component: %q", t.Name)))
	}

	// If the provided topic is _us_, return.
	if len(overlap) == len(topic) {
		return nearestTopic, []string{}, []string{}
	}

	// Even after discounting t.Name, topic has components not consumed
	// Search in t.Children.
	index := sort.Search(
		len(t.Children),
		func(i int) bool { return t.Children[i].Name[0] >= topic[len(overlap)] })
	if index < len(t.Children) {
		candidate := t.Children[index]
		if candidate.Name[0] == topic[len(overlap)] {
			nearestTopic = candidate
			rest = topic[len(overlap):]
			overlap = []string{candidate.Name[0]}

			for i := 1; i < len(nearestTopic.Name) && i < len(rest); i += 1 {
				// Names partially overlap
				if rest[i] != nearestTopic.Name[i] {
					overlap = rest[:i]
					return nearestTopic, copyTopic(rest), copyTopic(overlap)
				}
			}

			// The current child's name has been consumed and we should look at
			// its children to find more of our topic.
			if len(rest)-len(overlap) != 0 {
				return nearestTopic.MaybeFindTopic(rest)
			} else {
				return nearestTopic, []string{}, []string{}
			}
		}
	}

	return nearestTopic, copyTopic(rest), copyTopic(overlap)
}

// CreateChild ensures there exists a topicNode at subTopic in the topic trie.
//
// If this child already exists, it's considered a no-op and CreateChild
// returns successfully with newTopic being the existing child.
//
// CreateChild has four resulting cases:
// 1) Error on invalid subTopic
// 2) Return existing topicNode
// 3) Create new topicNode without modifying the trie
// 4) Modify the trie structure and then create a new topicNode
//
// Case 4 happens when we have an existing node with an overlapping run from our
// desired new topic name.  E.g. we have a foo.bar.baz node in the trie but we
// want to create a node foo.bar.qux.  In this case, we must create a foo.bar
// node with foo.bar.baz being renamed baz and assigned as a child of foo.bar.
// This requires rebinding a new context that derives from the new foo.bar for
// baz, its subscribers, and the subtrie beneath baz.
// We then add a qux node that derives from foo.bar.
func (t *topicNode) CreateChild(subTopic []string) (newTopic *topicNode, err error) {
	if !isValidTopic(subTopic) {
		return nil, errors.New(fmt.Sprintf(
			"Malformed topicName (%s) provided to CreateChild of topicNode (%s)",
			subTopic, t.Name))
	}

	// Empty sub-topic name -> return self
	if len(subTopic) == 0 {
		return t, nil
	}

	// Sub-topic already exists -> return sub-topic
	candidate, rest, overlap := t.MaybeFindTopic(subTopic)
	if len(rest) == 0 {
		return candidate, nil
	}

	parent := candidate
	if len(overlap) > 0 && len(overlap) < len(candidate.Name) {
		// Partial overlap exists, so we must split the found candidate.
		ctx, cancel := context.WithCancel(parent.ctx)
		new_child := newTopicNode(ctx, cancel, parent.Name[len(overlap):])
		parent.Name = copyTopic(overlap)

		new_child.Children = parent.Children
		parent.Children = make([]*topicNode, 0, 10)

		new_child.Subscribers = parent.Subscribers
		parent.Subscribers = make([]*subscriber, 0, 10)

		reseatTopicNode(parent, new_child)
	}

	child_ctx, cancel_child := context.WithCancel(parent.ctx)
	new_topic_node := newTopicNode(child_ctx, cancel_child, rest[len(overlap):])
	parent.Children = append(parent.Children, new_topic_node)
	sort.Sort(byName(parent.Children))

	return new_topic_node, nil
}

// reseatTopicNode moves `to_be_reseated` from its current parent onto `parent`.
func reseatTopicNode(parent, to_be_reseated *topicNode) {
	parent.Children = append(parent.Children, to_be_reseated)
	to_be_reseated.Apply(parent, func(parent, child *topicNode) {
		child.ctx, child.Cancel = context.WithCancel(parent.ctx)
		for i := range child.Subscribers {
			sub := child.Subscribers[i]
			deadline, _ := sub.ctx.Deadline()
			sub.ctx, _ = context.WithDeadline(child.ctx, deadline)
		}
	})
}

// Collapse is the effective garbage collection operation for a topic trie.
//
// It culls dead subscribers and topicNodes which no longer have any
// subscribers.  Recursively collapses children and cleans up empty trie
// branches.
func (t *topicNode) Collapse() {
	t.CollapseSubscribers()

	// If no children, skip.
	for i := 0; i < len(t.Children); i += 1 {
		child := t.Children[i]
		child.Collapse()

		if len(child.Subscribers) == 0 && len(child.Children) == 0 {
			last := uint(len(t.Children) - 1)

			t.Children[i] = t.Children[last]
			// Remove the array's ref to this child so it can be collected.
			t.Children[last] = nil
			// Truncate slice to exclude now-defunct final element.
			t.Children = t.Children[0:last]

			// We've now moved an unseen element to this index... we should
			// properly evaluate it.
			i -= 1
		}
	}

	// Collapse unnecessary runs of subscriber-free nodes.
	// Leave the root node strictly named ["."].
	//
	// Note that reseating is unnecessary here as the context-hierarchy is
	// preserved in the elision of an intervening descendant; e.g. the removal
	// of B in A->B->C => A->C still preserves the property that cancellation of
	// A results in the cancellation of C.
	if t.Name[0] != rootName && len(t.Subscribers) == 0 && len(t.Children) == 1 {
		child := t.Children[0]
		t.Name = append(t.Name, child.Name...)
		t.Subscribers = child.Subscribers
		t.Children = child.Children
	}

	// Sort order must be preserved for topic finding to work efficiently.
	sort.Sort(byName(t.Children))
}

func (t *topicNode) CollapseSubscribers() {
	// Note: List length may change during iteration.
	// If no subscribers, skip as we're completely collapsed.
	for i := 0; i < len(t.Subscribers); i += 1 {
		select {
		case <-t.Subscribers[i].Done():
			var last uint
			last = uint(len(t.Subscribers) - 1)

			t.Subscribers[i] = t.Subscribers[last]
			// Remove the array's ref to this subscriber so it can be collected.
			t.Subscribers[last] = nil
			// Truncate slice to exclude now-defunct final element.
			t.Subscribers = t.Subscribers[0:last]

			// We've now moved an unseen element to this index... we should
			// properly evaluate it.
			i -= 1
		default:
		}
	}
}

// Apply recursively pre-applies a function to all topicNodes in a
// topic trie rooted at the callee.
func (t *topicNode) Apply(parent *topicNode, f func(parent, child *topicNode)) {
	f(parent, t)
	for i := range t.Children {
		t.Children[i].Apply(t, f)
	}
}

func (t *topicNode) render(indentation string) string {
	var tokens []string

	tokens = append(tokens, indentation+strings.Join(t.Name, topicDelimeter)+topicDelimeter)

	// Partition children by "structural type"
	var leaves []*topicNode
	var sub_tries []*topicNode
	for i := range t.Children {
		child := t.Children[i]
		if len(child.Children) > 0 {
			sub_tries = append(sub_tries, child)
		} else {
			leaves = append(leaves, child)
		}
	}

	// Render leaves on one line
	if len(leaves) > 0 {
		var leaf_line []string
		for i := range leaves {
			leaf_line = append(leaf_line, strings.Join(leaves[i].Name, topicDelimeter))
		}
		tokens = append(tokens, indentation+"\t"+strings.Join(leaf_line, ", "))
	}

	// Render non-leaf children recursively
	for i := range sub_tries {
		tokens = append(tokens, sub_tries[i].render(indentation+"\t"))
	}

	return strings.Join(tokens, "\n")
}

func (t *topicNode) RenderTrie() string {
	return t.render("") + "\n"
}
<commit_msg>Improve trie.render() readability.<commit_after>package interchange

import (
	"code.google.com/p/go.net/context"
	"errors"
	"fmt"
	"sort"
	"strings"
)

const (
	// Delimiter between topic elements.
	topicDelimeter = "."

	// Name of the Arke hub's root topic.
	rootName = "."
)

func copyTopic(topic []string) []string {
	cp := make([]string, len(topic))
	copy(cp, topic)
	return cp
}

// isValidTopic determines whether the provided topic array is valid.
func isValidTopic(topic []string) bool {
	if len(topic) == 0 {
		return false
	}

	for i := range topic {
		token := topic[i]
		switch token {
		case "", rootName:
			return false
		}
	}

	return true
}

// A topicNode represents a vertex in the topic trie.
//
// Topic nodes occur when there exists a subscriber on a topic.  You can think
// of a topic as a word whose "characters" are delimited by periods. The topic
// trie as such acts as a trie over these tokens The root is implicitly named
// '.' and acts specially as it doesn't collapse, the following example will
// show this in practice.
//
// Example: for the trie consisting of subscribers on foo.bar.baz and
// foo.bar.qux there will be four topic nodes: one for the root (whose name is
// '.'), one for the branching node whose name is foo.bar and one node for each
// of baz and qux.
type topicNode struct {
	ctx      context.Context
	deadSubs int

	Cancel      context.CancelFunc
	Name        []string
	Children    []*topicNode
	Subscribers []*subscriber
}

func newTopicNode(ctx context.Context, cancel context.CancelFunc, name []string) *topicNode {
	return &topicNode{
		ctx:         ctx,
		Cancel:      cancel,
		Name:        copyTopic(name),
		Children:    make([]*topicNode, 0, 10),
		Subscribers: make([]*subscriber, 0, 10),
	}
}

type byName []*topicNode

func (n byName) Len() int      { return len(n) }
func (n byName) Swap(i, j int) { n[i], n[j] = n[j], n[i] }
func (n byName) Less(i, j int) bool {
	return strings.Join(n[i].Name, ".") < strings.Join(n[j].Name, ".")
}

// AddSub creates a subscriber, attaches it to this topicNode and starts it.
//
// Parameters:
//  sub: The subscription from which to build the new Subscriber
//  cleanup: The channel via which the subscriber's 'death' will notify the hub.
func (t *topicNode) AddSub(sub *subscription, cleanup chan<- []string) *subscriber {
	ctx, _ := context.WithDeadline(t.ctx, sub.Deadline)

	new_subscriber := createSubscriber(sub, ctx)

	// When the subscriber is done, notify the hub for garbage collection.
	go func(topic []string, notify chan<- []string) {
		<-ctx.Done()
		notify <- topic
	}(sub.Topic, cleanup)

	t.Subscribers = append(t.Subscribers, new_subscriber)

	return new_subscriber
}

// MaybeFindTopic searches the given topic trie for the provided topic.
//
// An empty `rest` indicates success.
//
// If the topic isn't found, it returns the closest ancestor node to what would
// be the parent of the topic if it did exist.  This returned node may require
// realigning its name, e.g. `rest` may be foo.bar.baz where the name of the
// returned node is foo.bar.qux.
//
// In the failure case, rest will be the remainder of the name string that
// wasn't found.
//
// Assumes `topic` is in canonical form (e.g. no empty elements or those of the
// form of topicDelimeter except in the case of a root topic).
// If a non-canonical topic is passed, no matching topic will be found.
func (t *topicNode) MaybeFindTopic(topic []string) (nearestTopic *topicNode, rest, overlap []string) {
	if len(topic) < 1 || t == nil {
		return t, []string{}, []string{}
	}

	nearestTopic = t
	rest = topic
	overlap = []string{}

	// Determine overlap between provided topic and t.Name
	if t.Name[0] != rootName {
		i := 0
		for i < len(t.Name) && i < len(topic) {
			if topic[i] != t.Name[i] {
				break
			}
			i += 1
		}
		overlap = topic[:i]
	} else if len(t.Name) > 1 {
		// The root node must have a name of at most length 1.
		panic(
			errors.New(
				fmt.Sprintf(
					"Hub root has name longer than 1 component: %q", t.Name)))
	}

	// If the provided topic is _us_, return.
	if len(overlap) == len(topic) {
		return nearestTopic, []string{}, []string{}
	}

	// Even after discounting t.Name, topic has components not consumed
	// Search in t.Children.
	index := sort.Search(
		len(t.Children),
		func(i int) bool { return t.Children[i].Name[0] >= topic[len(overlap)] })
	if index < len(t.Children) {
		candidate := t.Children[index]
		if candidate.Name[0] == topic[len(overlap)] {
			nearestTopic = candidate
			rest = topic[len(overlap):]
			overlap = []string{candidate.Name[0]}

			for i := 1; i < len(nearestTopic.Name) && i < len(rest); i += 1 {
				// Names partially overlap
				if rest[i] != nearestTopic.Name[i] {
					overlap = rest[:i]
					return nearestTopic, copyTopic(rest), copyTopic(overlap)
				}
			}

			// The current child's name has been consumed and we should look at
			// its children to find more of our topic.
			if len(rest)-len(overlap) != 0 {
				return nearestTopic.MaybeFindTopic(rest)
			} else {
				return nearestTopic, []string{}, []string{}
			}
		}
	}

	return nearestTopic, copyTopic(rest), copyTopic(overlap)
}

// CreateChild ensures there exists a topicNode at subTopic in the topic trie.
//
// If this child already exists, it's considered a no-op and CreateChild
// returns successfully with newTopic being the existing child.
//
// CreateChild has four resulting cases:
// 1) Error on invalid subTopic
// 2) Return existing topicNode
// 3) Create new topicNode without modifying the trie
// 4) Modify the trie structure and then create a new topicNode
//
// Case 4 happens when we have an existing node with an overlapping run from our
// desired new topic name.  E.g. we have a foo.bar.baz node in the trie but we
// want to create a node foo.bar.qux.  In this case, we must create a foo.bar
// node with foo.bar.baz being renamed baz and assigned as a child of foo.bar.
// This requires rebinding a new context that derives from the new foo.bar for
// baz, its subscribers, and the subtrie beneath baz.
// We then add a qux node that derives from foo.bar.
func (t *topicNode) CreateChild(subTopic []string) (newTopic *topicNode, err error) {
	if !isValidTopic(subTopic) {
		return nil, errors.New(fmt.Sprintf(
			"Malformed topicName (%s) provided to CreateChild of topicNode (%s)",
			subTopic, t.Name))
	}

	// Empty sub-topic name -> return self
	if len(subTopic) == 0 {
		return t, nil
	}

	// Sub-topic already exists -> return sub-topic
	candidate, rest, overlap := t.MaybeFindTopic(subTopic)
	if len(rest) == 0 {
		return candidate, nil
	}

	parent := candidate
	if len(overlap) > 0 && len(overlap) < len(candidate.Name) {
		// Partial overlap exists, so we must split the found candidate.
		ctx, cancel := context.WithCancel(parent.ctx)
		new_child := newTopicNode(ctx, cancel, parent.Name[len(overlap):])
		parent.Name = copyTopic(overlap)

		new_child.Children = parent.Children
		parent.Children = make([]*topicNode, 0, 10)

		new_child.Subscribers = parent.Subscribers
		parent.Subscribers = make([]*subscriber, 0, 10)

		reseatTopicNode(parent, new_child)
	}

	child_ctx, cancel_child := context.WithCancel(parent.ctx)
	new_topic_node := newTopicNode(child_ctx, cancel_child, rest[len(overlap):])
	parent.Children = append(parent.Children, new_topic_node)
	sort.Sort(byName(parent.Children))

	return new_topic_node, nil
}

// reseatTopicNode moves `to_be_reseated` from its current parent onto `parent`.
func reseatTopicNode(parent, to_be_reseated *topicNode) {
	parent.Children = append(parent.Children, to_be_reseated)
	to_be_reseated.Apply(parent, func(parent, child *topicNode) {
		child.ctx, child.Cancel = context.WithCancel(parent.ctx)
		for i := range child.Subscribers {
			sub := child.Subscribers[i]
			deadline, _ := sub.ctx.Deadline()
			sub.ctx, _ = context.WithDeadline(child.ctx, deadline)
		}
	})
}

// Collapse is the effective garbage collection operation for a topic trie.
//
// It culls dead subscribers and topicNodes which no longer have any
// subscribers.  Recursively collapses children and cleans up empty trie
// branches.
func (t *topicNode) Collapse() {
	t.CollapseSubscribers()

	// If no children, skip.
	for i := 0; i < len(t.Children); i += 1 {
		child := t.Children[i]
		child.Collapse()

		if len(child.Subscribers) == 0 && len(child.Children) == 0 {
			last := uint(len(t.Children) - 1)

			t.Children[i] = t.Children[last]
			// Remove the array's ref to this child so it can be collected.
			t.Children[last] = nil
			// Truncate slice to exclude now-defunct final element.
			t.Children = t.Children[0:last]

			// We've now moved an unseen element to this index... we should
			// properly evaluate it.
			i -= 1
		}
	}

	// Collapse unnecessary runs of subscriber-free nodes.
	// Leave the root node strictly named ["."].
	//
	// Note that reseating is unnecessary here as the context-hierarchy is
	// preserved in the elision of an intervening descendant; e.g. the removal
	// of B in A->B->C => A->C still preserves the property that cancellation of
	// A results in the cancellation of C.
	if t.Name[0] != rootName && len(t.Subscribers) == 0 && len(t.Children) == 1 {
		child := t.Children[0]
		t.Name = append(t.Name, child.Name...)
		t.Subscribers = child.Subscribers
		t.Children = child.Children
	}

	// Sort order must be preserved for topic finding to work efficiently.
	sort.Sort(byName(t.Children))
}

func (t *topicNode) CollapseSubscribers() {
	// Note: List length may change during iteration.
	// If no subscribers, skip as we're completely collapsed.
	for i := 0; i < len(t.Subscribers); i += 1 {
		select {
		case <-t.Subscribers[i].Done():
			var last uint
			last = uint(len(t.Subscribers) - 1)

			t.Subscribers[i] = t.Subscribers[last]
			// Remove the array's ref to this subscriber so it can be collected.
			t.Subscribers[last] = nil
			// Truncate slice to exclude now-defunct final element.
			t.Subscribers = t.Subscribers[0:last]

			// We've now moved an unseen element to this index... we should
			// properly evaluate it.
			i -= 1
		default:
		}
	}
}

// Apply recursively pre-applies a function to all topicNodes in a
// topic trie rooted at the callee.
func (t *topicNode) Apply(parent *topicNode, f func(parent, child *topicNode)) {
	f(parent, t)
	for i := range t.Children {
		t.Children[i].Apply(t, f)
	}
}

func (t *topicNode) render(indentation string) string {
	var tokens []string
	child_indentation := indentation + "\t"

	tokens = append(tokens, indentation+strings.Join(t.Name, topicDelimeter)+topicDelimeter)

	// Partition children by "structural type"
	var leaves []*topicNode
	var sub_tries []*topicNode
	for i := range t.Children {
		child := t.Children[i]
		if len(child.Children) > 0 {
			sub_tries = append(sub_tries, child)
		} else {
			leaves = append(leaves, child)
		}
	}

	// Render leaves on one line
	if len(leaves) > 0 {
		var leaf_line []string
		for i := range leaves {
			leaf_line = append(leaf_line, strings.Join(leaves[i].Name, topicDelimeter))
		}
		tokens = append(tokens, child_indentation+strings.Join(leaf_line, ", "))
	}

	// Render non-leaf children recursively
	for i := range sub_tries {
		tokens = append(tokens, sub_tries[i].render(child_indentation))
	}

	return strings.Join(tokens, "\n")
}

func (t *topicNode) RenderTrie() string {
	return t.render("") + "\n"
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"os"

	"github.com/coreos/go-systemd/dbus"
	"gopkg.in/gin-gonic/gin.v1"
)

func main() {
	r := gin.Default()
	r.GET("/.well-known/onesie/status.json", func(c *gin.Context) {
		conn, err := dbus.New()
		if err != nil {
			log.Fatalf("Error openning connection: %+v", err)
		}
		defer conn.Close()

		units, err := conn.ListUnits()
		if err != nil {
			log.Fatalf("Error listing units: %+v", err)
		}
		log.Printf("Units: %+v", units)

		c.JSON(200, gin.H{
			"message": "ok",
		})
	})

	port := "9090"
	if os.Getenv("PORT") != "" {
		port = os.Getenv("PORT")
	}

	r.Run(fmt.Sprintf(":%s", port))
}
<commit_msg>new connection<commit_after>package main

import (
	"fmt"
	"log"
	"os"

	"github.com/coreos/go-systemd/dbus"
	"gopkg.in/gin-gonic/gin.v1"
)

func main() {
	r := gin.Default()
	r.GET("/.well-known/onesie/status.json", func(c *gin.Context) {
		conn, err := dbus.NewSystemdConnection()
		if err != nil {
			log.Fatalf("Error openning connection: %+v", err)
		}
		defer conn.Close()

		units, err := conn.ListUnits()
		if err != nil {
			log.Fatalf("Error listing units: %+v", err)
		}
		log.Printf("Units: %+v", units)

		c.JSON(200, gin.H{
			"message": "ok",
		})
	})

	port := "9090"
	if os.Getenv("PORT") != "" {
		port = os.Getenv("PORT")
	}

	r.Run(fmt.Sprintf(":%s", port))
}
<|endoftext|>


<commit_before>package baja

import (
	"bufio"
	"bytes"
	"encoding/base64"
	"encoding/csv"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"strings"
	// "text/template"

	"github.com/yeo/betterdev.link/baja/dao"
	"github.com/yeo/betterdev.link/baja/dts"
	"github.com/yeo/betterdev.link/baja/server"

	"crypto/sha256"
	"github.com/PuerkitoBio/goquery"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/ses"
	"github.com/jaytaylor/html2text"
)

func customEmail(doc *goquery.Document, issue, email string) (string, error) {
	// We just want to obfuscato email
	h := sha256.New()
	h.Write([]byte(email))

	doc.Find("a").Each(func(_ int, link *goquery.Selection) {
		href, ok := link.Attr("href")

		linkId := base64.URLEncoding.WithPadding(base64.NoPadding).EncodeToString([]byte(href))
		if ok {
			link.SetAttr("href", fmt.Sprintf("https://betterdev.link/links/%s?issue=%s&email=%x", linkId, issue, h.Sum(nil)))
		}
	})

	body, err := goquery.OuterHtml(doc.Selection)
	if err != nil {
		return "", err
	}

	//TODO: Refactor
	body = strings.Replace(body, "*|EMAIL_HASH|*", fmt.Sprintf("%x", h.Sum(nil)), -1)
	return body, nil
}

func sendTo(svc *ses.SES, issue, subject, email string) (*ses.SendEmailOutput, error) {
	buf := bytes.NewBuffer(nil)
	f, _ := os.Open(fmt.Sprintf("./public/issues/%s/email/index.html", issue)) // Error handling elided for brevity.
	io.Copy(buf, f)                                                            // Error handling elided for brevity.
	f.Close()

	doc, err := goquery.NewDocumentFromReader(bytes.NewReader(buf.Bytes()))
	if err != nil {
		log.Fatal("Fail to parse email", err)
	}

	emailBody, err := customEmail(doc, issue, email)
	if err != nil {
		log.Fatal("Fail to build custom email")
	}

	emailText, err := html2text.FromString(emailBody, html2text.Options{PrettyTables: true})

	if err != nil {
		log.Fatal("Cannot convert HTML email to Text")
	}

	input := &ses.SendEmailInput{
		Destination: &ses.Destination{
			//CcAddresses: []*string{
			//	aws.String("vinh@noty.im"),
			//},
			ToAddresses: []*string{
				aws.String(email),
			},
		},
		Message: &ses.Message{
			Body: &ses.Body{
				Html: &ses.Content{
					Charset: aws.String("UTF-8"),
					Data:    aws.String(emailBody),
				},
				Text: &ses.Content{
					Charset: aws.String("UTF-8"),
					Data:    aws.String(emailText),
				},
			},
			Subject: &ses.Content{
				Charset: aws.String("UTF-8"),
				Data:    aws.String(subject),
			},
		},
		//ReturnPath:    aws.String(""),
		//ReturnPathArn: aws.String(""),
		Source: aws.String("vinh@yeo.space"),
		//SourceArn:     aws.String(""),
	}

	result, err := svc.SendEmail(input)
	if err != nil {
		if aerr, ok := err.(awserr.Error); ok {
			switch aerr.Code() {
			case ses.ErrCodeMessageRejected:
				fmt.Println(ses.ErrCodeMessageRejected, aerr.Error())
			case ses.ErrCodeMailFromDomainNotVerifiedException:
				fmt.Println(ses.ErrCodeMailFromDomainNotVerifiedException, aerr.Error())
			case ses.ErrCodeConfigurationSetDoesNotExistException:
				fmt.Println(ses.ErrCodeConfigurationSetDoesNotExistException, aerr.Error())
			default:
				log.Println("Fail to send", email, aerr.Error())
			}
		} else {
			// Print the error, cast err to awserr.Error to get the Code and
			// Message from an error.
			log.Println("Fail to send", email, err.Error())
		}
		return nil, err
	}

	return result, nil
}

func Fanout(issueNumber string, mode string, confirm bool) {
	// Quick and dirty sent tracking
	// All of this crapy need to refactor
	config := server.LoadConfigFromEnv()
	db := dao.Connect(config.MongoURI)
	tracker := dts.TrackerService{db}
	// TODO: Close db

	log.Printf("Start to parse export contact from %s\n", mode)
	contacts, err := ioutil.ReadFile(fmt.Sprintf("./content/%s.csv", mode))
	if err != nil {
		log.Fatal("Cannot read file", err)
	}

	f, err := os.Stat(fmt.Sprintf("./content/issues/%s.yml", issueNumber))
	if err != nil {
		log.Fatal("Cannot open issue file")
	}
	issue, err := loadIssue(f)
	if err != nil {
		log.Fatal("Cannot parse issue content")
	}

	r := csv.NewReader(bytes.NewReader(contacts))
	subject := fmt.Sprintf("BetterDev #%s - %s", issueNumber, issue.Subject)
	if mode == "dev" {
		subject = "[Test]" + subject
	}

	svc := ses.New(session.New())

	// Activity user
	file, err := os.Open(fmt.Sprintf("./content/%s", "no_activity.user"))
	var cleanUser map[string]bool
	cleanUser = make(map[string]bool)
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		cleanUser[scanner.Text()] = true
	}
	log.Println(cleanUser)

	// Iterator throught the list and fanout email
	count := 0
	for {
		count = count + 1
		record, err := r.Read()
		if count <= 1 {
			log.Println("Skip header")
			continue
		}

		if err == io.EOF {
			break
		}
		log.Printf("Process at %d %v\n", count, record)

		if err != nil {
			log.Fatal(err)
		}

		if cleanUser[record[0]] == true {
			log.Println(record[0], "is ignored because user has not click any link in last 6 months")
			continue
		}

		if confirm == true {
			if tracker.AlreadySent(issueNumber, record[0]) == true {
				log.Println("Already fanout", issueNumber, "to", record)
				continue
			}

			result, err := sendTo(svc, issueNumber, subject, record[0])
			if err == nil {
				tracker.LogSent(issueNumber, record[0])

				log.Println("Send to", record[0], "succesfully", result)
			} else {
				log.Println("Fail to fanout", record[0], err)
			}
		} else {
			log.Println("DRY Fanout", issueNumber, subject, "to", record[0])
		}
	}
}
<commit_msg>Fallback to legacy<commit_after>package baja

import (
	"bufio"
	"bytes"
	"encoding/base64"
	"encoding/csv"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"strings"
	// "text/template"

	"github.com/yeo/betterdev.link/baja/dao"
	"github.com/yeo/betterdev.link/baja/dts"
	"github.com/yeo/betterdev.link/baja/server"

	"crypto/sha256"
	"github.com/PuerkitoBio/goquery"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/ses"
	"github.com/jaytaylor/html2text"
)

func customEmail(doc *goquery.Document, issue, email string) (string, error) {
	// We just want to obfuscato email
	h := sha256.New()
	h.Write([]byte(email))

	doc.Find("a").Each(func(_ int, link *goquery.Selection) {
		href, ok := link.Attr("href")

		linkId := base64.URLEncoding.WithPadding(base64.NoPadding).EncodeToString([]byte(href))
		if ok {
			link.SetAttr("href", fmt.Sprintf("https://betterdev.link/links/%s?issue=%s&email=%x", linkId, issue, h.Sum(nil)))
		}
	})

	body, err := goquery.OuterHtml(doc.Selection)
	if err != nil {
		return "", err
	}

	//TODO: Refactor
	body = strings.Replace(body, "*|EMAIL_HASH|*", fmt.Sprintf("%x", h.Sum(nil)), -1)
	return body, nil
}

func sendTo(svc *ses.SES, issue, subject, email string) (*ses.SendEmailOutput, error) {
	buf := bytes.NewBuffer(nil)
	f, _ := os.Open(fmt.Sprintf("./public/issues/%s/email/index.html", issue)) // Error handling elided for brevity.
	io.Copy(buf, f)                                                            // Error handling elided for brevity.
	f.Close()

	doc, err := goquery.NewDocumentFromReader(bytes.NewReader(buf.Bytes()))
	if err != nil {
		log.Fatal("Fail to parse email", err)
	}

	emailBody, err := customEmail(doc, issue, email)
	if err != nil {
		log.Fatal("Fail to build custom email")
	}

	emailText, err := html2text.FromString(emailBody, html2text.Options{PrettyTables: true})

	if err != nil {
		log.Fatal("Cannot convert HTML email to Text")
	}

	input := &ses.SendEmailInput{
		Destination: &ses.Destination{
			//CcAddresses: []*string{
			//	aws.String("vinh@noty.im"),
			//},
			ToAddresses: []*string{
				aws.String(email),
			},
		},
		Message: &ses.Message{
			Body: &ses.Body{
				Html: &ses.Content{
					Charset: aws.String("UTF-8"),
					Data:    aws.String(emailBody),
				},
				Text: &ses.Content{
					Charset: aws.String("UTF-8"),
					Data:    aws.String(emailText),
				},
			},
			Subject: &ses.Content{
				Charset: aws.String("UTF-8"),
				Data:    aws.String(subject),
			},
		},
		//ReturnPath:    aws.String(""),
		//ReturnPathArn: aws.String(""),
		Source: aws.String("vinh@yeo.space"),
		//SourceArn:     aws.String(""),
	}

	result, err := svc.SendEmail(input)
	if err != nil {
		if aerr, ok := err.(awserr.Error); ok {
			switch aerr.Code() {
			case ses.ErrCodeMessageRejected:
				fmt.Println(ses.ErrCodeMessageRejected, aerr.Error())
			case ses.ErrCodeMailFromDomainNotVerifiedException:
				fmt.Println(ses.ErrCodeMailFromDomainNotVerifiedException, aerr.Error())
			case ses.ErrCodeConfigurationSetDoesNotExistException:
				fmt.Println(ses.ErrCodeConfigurationSetDoesNotExistException, aerr.Error())
			default:
				log.Println("Fail to send", email, aerr.Error())
			}
		} else {
			// Print the error, cast err to awserr.Error to get the Code and
			// Message from an error.
			log.Println("Fail to send", email, err.Error())
		}
		return nil, err
	}

	return result, nil
}

func Fanout(issueNumber string, mode string, confirm bool) {
	// Quick and dirty sent tracking
	// All of this crapy need to refactor
	config := server.LoadConfigFromEnv()
	db := dao.Connect(config.MongoURI)
	tracker := dts.TrackerService{db}
	// TODO: Close db

	log.Printf("Start to parse export contact from %s\n", mode)
	contacts, err := ioutil.ReadFile(fmt.Sprintf("./content/%s.csv", mode))
	if err != nil {
		log.Fatal("Cannot read file", err)
	}

	f, err := os.Stat(fmt.Sprintf("./content/issues/%s.yml", issueNumber))
	if err != nil {
		log.Fatal("Cannot open issue file")
	}
	issue, err := loadIssue(f)
	if err != nil {
		log.Fatal("Cannot parse issue content")
	}

	r := csv.NewReader(bytes.NewReader(contacts))
	subject := fmt.Sprintf("BetterDev #%s", issueNumber)
	if issue.Subject != "" {
		subject = fmt.Sprintf("%s - %s", subject, issue.Subject)
	}

	if mode == "dev" {
		subject = "[Test]" + subject
	}

	svc := ses.New(session.New())

	// Activity user
	file, err := os.Open(fmt.Sprintf("./content/%s", "no_activity.user"))
	var cleanUser map[string]bool
	cleanUser = make(map[string]bool)
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		cleanUser[scanner.Text()] = true
	}
	log.Println(cleanUser)

	// Iterator throught the list and fanout email
	count := 0
	for {
		count = count + 1
		record, err := r.Read()
		if count <= 1 {
			log.Println("Skip header")
			continue
		}

		if err == io.EOF {
			break
		}
		log.Printf("Process at %d %v\n", count, record)

		if err != nil {
			log.Fatal(err)
		}

		if cleanUser[record[0]] == true {
			log.Println(record[0], "is ignored because user has not click any link in last 6 months")
			continue
		}

		if confirm == true {
			if tracker.AlreadySent(issueNumber, record[0]) == true {
				log.Println("Already fanout", issueNumber, "to", record)
				continue
			}

			result, err := sendTo(svc, issueNumber, subject, record[0])
			if err == nil {
				tracker.LogSent(issueNumber, record[0])

				log.Println("Send to", record[0], "succesfully", result)
			} else {
				log.Println("Fail to fanout", record[0], err)
			}
		} else {
			log.Println("DRY Fanout", issueNumber, subject, "to", record[0])
		}
	}
}
<|endoftext|>


<commit_before>package controller

import (
	"net/http"
	"time"

	"github.com/utahta/momoclo-channel/appengine/lib/crawler"
	"github.com/utahta/momoclo-channel/appengine/lib/reminder"
	"github.com/utahta/momoclo-channel/appengine/lib/ustream"
	"golang.org/x/net/context"
)

// Notify reminder
func CronReminder(w http.ResponseWriter, req *http.Request) {
	ctx := getContext(req)

	if err := reminder.Notify(ctx); err != nil {
		newError(err, http.StatusInternalServerError).Handle(ctx, w)
		return
	}
}

// Notify ustream
func CronUstream(w http.ResponseWriter, req *http.Request) {
	ctx := getContext(req)

	if err := ustream.Notify(ctx); err != nil {
		newError(err, http.StatusInternalServerError).Handle(ctx, w)
		return
	}
}

// Crawling
func CronCrawl(w http.ResponseWriter, req *http.Request) {
	ctx, cancel := context.WithTimeout(getContext(req), 55*time.Second)
	defer cancel()

	if err := crawler.Crawl(ctx); err != nil {
		newError(err, http.StatusInternalServerError).Handle(ctx, w)
		return
	}
}
<commit_msg>Change timeout 55s to 30s<commit_after>package controller

import (
	"net/http"
	"time"

	"github.com/utahta/momoclo-channel/appengine/lib/crawler"
	"github.com/utahta/momoclo-channel/appengine/lib/reminder"
	"github.com/utahta/momoclo-channel/appengine/lib/ustream"
	"golang.org/x/net/context"
)

// Notify reminder
func CronReminder(w http.ResponseWriter, req *http.Request) {
	ctx := getContext(req)

	if err := reminder.Notify(ctx); err != nil {
		newError(err, http.StatusInternalServerError).Handle(ctx, w)
		return
	}
}

// Notify ustream
func CronUstream(w http.ResponseWriter, req *http.Request) {
	ctx := getContext(req)

	if err := ustream.Notify(ctx); err != nil {
		newError(err, http.StatusInternalServerError).Handle(ctx, w)
		return
	}
}

// Crawling
func CronCrawl(w http.ResponseWriter, req *http.Request) {
	ctx, cancel := context.WithTimeout(getContext(req), 30*time.Second)
	defer cancel()

	if err := crawler.Crawl(ctx); err != nil {
		newError(err, http.StatusInternalServerError).Handle(ctx, w)
		return
	}
}
<|endoftext|>