<commit_before>//
// Generate an RSS feed from a PostgreSQL database containing tweets.
//
// The tweet database is the one populated by my twitter-tcl twitter_poll
// program.
//
package main

import (
	"database/sql"
	"flag"
	"fmt"
	"log"
	"os"
	"time"

	_ "github.com/lib/pq"
	"summercat.com/config"
	"summercat.com/gorse/gorselib"
)

// FeedURI is the URI set on the RSS feed's channel element's link element.
// It need not be a real URI but should be unique.
var FeedURI = "https://leviathan.summercat.com/tweets/"

// Tweet describe a tweet pulled from the database.
type Tweet struct {
	Nick    string
	Text    string
	Time    time.Time
	TweetID int64
}

// MyConfig holds configuration values.
type MyConfig struct {
	DBUser string
	DBPass string
	DBName string
	DBHost string
	// the number of recent tweets to put in the xml.
	NumTweets uint64
}

// connectToDB opens a new connection to the database.
func connectToDB(name string, user string, pass string, host string) (*sql.DB,
	error) {
	dsn := fmt.Sprintf("user=%s password=%s dbname=%s host=%s", user, pass, name,
		host)
	db, err := sql.Open("postgres", dsn)
	if err != nil {
		log.Printf("Failed to connect to the database: %s", err.Error())
		return nil, err
	}
	return db, nil
}

// getTweets retrieves tweets from a database.
func getTweets(config *MyConfig) ([]Tweet, error) {
	db, err := connectToDB(config.DBName, config.DBUser, config.DBPass,
		config.DBHost)
	if err != nil {
		return nil, err
	}

	// get most recent tweets.
	sql := `
SELECT nick, text, time, tweet_id
FROM tweet
ORDER BY time DESC
LIMIT $1
`
	rows, err := db.Query(sql, config.NumTweets)
	if err != nil {
		log.Printf("Query failure: %s", err.Error())
		return nil, err
	}

	var tweets []Tweet
	for rows.Next() {
		tweet := Tweet{}
		err = rows.Scan(&tweet.Nick, &tweet.Text, &tweet.Time, &tweet.TweetID)
		if err != nil {
			log.Printf("Failed to scan row: %s", err.Error())
			// TODO: is there anything to clean up?
			return nil, err
		}
		tweets = append(tweets, tweet)
	}

	// I'm adding a close because I see 'unexpected EOF on client connection'
	// in postgresql logs from this. with a close it goes away!
	err = db.Close()
	if err != nil {
		log.Printf("Failed to close database connection: %s", err.Error())
		return nil, err
	}
	return tweets, nil
}

// create a URL to the status.
// apparently this URL is not in the tweet status payload.
// form:
// https://twitter.com/<screenname>/status/<tweetid>
func createStatusURL(screenName string, tweetID int64) string {
	return fmt.Sprintf("https://twitter.com/%s/status/%d",
		screenName, tweetID)
}

// main is the program entry point.
func main() {
	log.SetFlags(log.Ltime | log.Llongfile)

	// command line arguments.
	outputFile := flag.String("output-file", "", "Output XML file to write.")
	configFile := flag.String("config-file", "", "Config file")
	flag.Parse()
	if len(*outputFile) == 0 || len(*configFile) == 0 {
		flag.PrintDefaults()
		os.Exit(1)
	}

	// load up the config.
	var settings MyConfig
	err := config.GetConfig(*configFile, &settings)
	if err != nil {
		log.Printf("Failed to retrieve config: %s", err.Error())
		os.Exit(1)
	}
	// TODO: we could run validation on each config item... but then again,
	//   we can just try to connect to the database!

	// reduce some library logging.
	gorselib.SetQuiet(true)

	// retrieve recent tweets.
	tweets, err := getTweets(&settings)
	if err != nil {
		log.Printf("Failed to retrieve tweets: %s", err.Error())
		os.Exit(1)
	}

	// set up the feed's information.
	rss := gorselib.RssFeed{}
	rss.Name = "Twitreader"
	rss.Uri = FeedURI
	rss.Description = "Twitreader tweets"
	rss.LastUpdateTime = time.Now()

	// build rss items.
	for _, tweet := range tweets {
		item := gorselib.RssItem{
			Title:           fmt.Sprintf("%s", tweet.Nick, tweet.TweetID),
			Uri:             createStatusURL(tweet.Nick, tweet.TweetID),
			Description:     tweet.Text,
			PublicationDate: tweet.Time,
		}
		rss.Items = append(rss.Items, item)
	}

	err = gorselib.WriteFeedXML(&rss, *outputFile)
	if err != nil {
		log.Printf("Failed to write XML: %s", err.Error())
		os.Exit(1)
	}
}
<commit_msg>Update to catch up with changes made to gorselib<commit_after>//
// Generate an RSS feed from a PostgreSQL database containing tweets.
//
// The tweet database is the one populated by my twitter-tcl twitter_poll
// program.
//
package main

import (
	"database/sql"
	"flag"
	"fmt"
	"log"
	"os"
	"time"

	_ "github.com/lib/pq"
	"summercat.com/config"
	"summercat.com/gorse/gorselib"
)

// FeedURI is the URI set on the RSS feed's channel element's link element.
// It need not be a real URI but should be unique.
var FeedURI = "https://leviathan.summercat.com/tweets/"

// Tweet describe a tweet pulled from the database.
type Tweet struct {
	Nick    string
	Text    string
	Time    time.Time
	TweetID int64
}

// MyConfig holds configuration values.
type MyConfig struct {
	DBUser string
	DBPass string
	DBName string
	DBHost string
	// the number of recent tweets to put in the xml.
	NumTweets uint64
}

// connectToDB opens a new connection to the database.
func connectToDB(name string, user string, pass string, host string) (*sql.DB,
	error) {
	dsn := fmt.Sprintf("user=%s password=%s dbname=%s host=%s", user, pass, name,
		host)
	db, err := sql.Open("postgres", dsn)
	if err != nil {
		log.Printf("Failed to connect to the database: %s", err.Error())
		return nil, err
	}
	return db, nil
}

// getTweets retrieves tweets from a database.
func getTweets(config *MyConfig) ([]Tweet, error) {
	db, err := connectToDB(config.DBName, config.DBUser, config.DBPass,
		config.DBHost)
	if err != nil {
		return nil, err
	}

	// get most recent tweets.
	sql := `
SELECT nick, text, time, tweet_id
FROM tweet
ORDER BY time DESC
LIMIT $1
`
	rows, err := db.Query(sql, config.NumTweets)
	if err != nil {
		log.Printf("Query failure: %s", err.Error())
		return nil, err
	}

	var tweets []Tweet
	for rows.Next() {
		tweet := Tweet{}
		err = rows.Scan(&tweet.Nick, &tweet.Text, &tweet.Time, &tweet.TweetID)
		if err != nil {
			log.Printf("Failed to scan row: %s", err.Error())
			// TODO: is there anything to clean up?
			return nil, err
		}
		tweets = append(tweets, tweet)
	}

	// I'm adding a close because I see 'unexpected EOF on client connection'
	// in postgresql logs from this. with a close it goes away!
	err = db.Close()
	if err != nil {
		log.Printf("Failed to close database connection: %s", err.Error())
		return nil, err
	}
	return tweets, nil
}

// create a URL to the status.
// apparently this URL is not in the tweet status payload.
// form:
// https://twitter.com/<screenname>/status/<tweetid>
func createStatusURL(screenName string, tweetID int64) string {
	return fmt.Sprintf("https://twitter.com/%s/status/%d",
		screenName, tweetID)
}

// main is the program entry point.
func main() {
	log.SetFlags(log.Ltime | log.Llongfile)

	// command line arguments.
	outputFile := flag.String("output-file", "", "Output XML file to write.")
	configFile := flag.String("config-file", "", "Config file")
	flag.Parse()
	if len(*outputFile) == 0 || len(*configFile) == 0 {
		flag.PrintDefaults()
		os.Exit(1)
	}

	// load up the config.
	var settings MyConfig
	err := config.GetConfig(*configFile, &settings)
	if err != nil {
		log.Printf("Failed to retrieve config: %s", err.Error())
		os.Exit(1)
	}
	// TODO: we could run validation on each config item... but then again,
	//   we can just try to connect to the database!

	// reduce some library logging.
	gorselib.SetQuiet(true)

	// retrieve recent tweets.
	tweets, err := getTweets(&settings)
	if err != nil {
		log.Printf("Failed to retrieve tweets: %s", err.Error())
		os.Exit(1)
	}

	// set up the feed's information.
	rss := gorselib.RSSFeed{}
	rss.Name = "Twitreader"
	rss.URI = FeedURI
	rss.Description = "Twitreader tweets"
	rss.LastUpdateTime = time.Now()

	// build rss items.
	for _, tweet := range tweets {
		item := gorselib.RSSItem{
			Title:           fmt.Sprintf("%s", tweet.Nick, tweet.TweetID),
			URI:             createStatusURL(tweet.Nick, tweet.TweetID),
			Description:     tweet.Text,
			PublicationDate: tweet.Time,
		}
		rss.Items = append(rss.Items, item)
	}

	err = gorselib.WriteFeedXML(&rss, *outputFile)
	if err != nil {
		log.Printf("Failed to write XML: %s", err.Error())
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package crawler

import (
	"net/url"

	log "github.com/cihub/seelog"
)

// Crawler coordinated crawling a site, and stores completed results
type Crawler struct {
	// Store our results
	Pages map[string]*Page
	Links map[string]*Link

	// completed channel is an inbound queue of completed requests
	// for processing by the main crawler goroutine
	completed chan *Result

	// skipped tracks pages we have skipped
	skipped chan *Result

	// errored tracks pages which errored, which we may then
	// choose to reattempt
	errored chan *Result

	// requestsInFlight tracks how many of requests are outstanding
	requestsInFlight int

	// totalRequests tracks the number of requests we have made
	totalRequests int

	// target stores our original target for comparisons
	target *url.URL
}

type Result struct {
	Url   *url.URL
	Depth int
	Page  *Page
	Error error
}

// Work is our main event loop, coordinating request processing
// This is single threaded and is the only thread that writes into
// our internal maps, so we don't require coordination or locking
// (maps are not threadsafe)
func (c *Crawler) Work(target string, depth int, fetcher Fetcher) {
	var err error

	// Convert our target to a URL
	if c.target, err = url.Parse(target); err != nil {
		log.Errorf("Could not parse target '%s'", target)
		return
	}

	// Initialise channels to track requests
	c.completed = make(chan *Result)
	c.skipped = make(chan *Result)
	c.errored = make(chan *Result)

	// Initialise results containers
	c.Pages = make(map[string]*Page)
	c.Links = make(map[string]*Link)

	// Get our first page & track this
	go c.crawl(c.target, depth, fetcher)
	c.requestsInFlight++
	c.totalRequests++

	// Event loop
	for {
		select {
		case r := <-c.skipped:
			log.Debugf("Page skipped for %s", r.Url)
			c.totalRequests--
		case r := <-c.errored:
			log.Debugf("Page errored for %s: %v", r.Url, r.Error)
		case r := <-c.completed:
			log.Debugf("Page complete for %s", r.Url)
			if r.Page == nil {
				break
			}

			// Process each link
			for _, l := range r.Page.Links {

				// Skip page if not on our target domain
				if l.Target.Host != c.target.Host {
					// log.Debugf("Skipping %s as not on target domain", source.String())
					continue
				}

				// Check if we've already hit this page
				if _, exists := c.Pages[l.Target.String()]; exists {
					// log.Debugf("Skipping %s as already processed", l.Target.String())
					continue
				}

				log.Debugf("Triggering crawl of %s from %s", l.Target.String(), r.Url.String())
				go c.crawl(l.Target, r.Depth-1, fetcher)
				c.requestsInFlight++
				c.totalRequests++
			}
			log.Debugf("Fired %v new requests, %v currently in flight", len(r.Page.Links), c.requestsInFlight)

			c.Pages[r.Url.String()] = r.Page

		}

		// Decrement outstanding requests & and abort if complete
		c.requestsInFlight--
		if c.requestsInFlight == 0 {
			log.Debugf("Complete")
			return
		}
	}
}

// crawl uses fetcher to recursively crawl
// pages starting with url, to a maximum of depth.
func (c *Crawler) crawl(source *url.URL, depth int, fetcher Fetcher) {

	// The result of our crawl
	res := &Result{
		Depth: depth,
		Url:   source,
	}

	// Skip pages if we are at our maximum depth
	if depth <= 0 {
		log.Debugf("Skipping %s as at 0 depth", source.String())
		c.skipped <- res
		return
	}

	// Crawl the page, using our fetcher
	urls, assets, err := fetcher.Fetch(source.String())
	if err != nil {
		res.Error = err
		c.errored <- res
		return
	}

	log.Infof("%v URLs found at %s", len(urls), source.String())

	links := make([]*Link, 0)
	for _, u := range urls {
		links = append(links, &Link{
			Source: source,
			Target: u,
		})
	}

	// Store this page and links into the result
	res.Page = &Page{
		Url:    source,
		Links:  links,
		Assets: assets,
	}

	// 	// Mark this page as complete
	c.completed <- res
}

func (c *Crawler) TotalRequests() int {
	return c.totalRequests
}
<commit_msg>Comments<commit_after>package crawler

import (
	"net/url"

	log "github.com/cihub/seelog"
)

// Crawler coordinated crawling a site, and stores completed results
type Crawler struct {
	// Store our results
	Pages map[string]*Page
	Links map[string]*Link

	// completed channel is an inbound queue of completed requests
	// for processing by the main crawler goroutine
	completed chan *Result

	// skipped tracks pages we have skipped
	skipped chan *Result

	// errored tracks pages which errored, which we may then
	// choose to reattempt
	errored chan *Result

	// requestsInFlight tracks how many of requests are outstanding
	requestsInFlight int

	// totalRequests tracks the number of requests we have made
	totalRequests int

	// target stores our original target for comparisons
	target *url.URL
}

// Result represents the result of a crawl request
type Result struct {
	Url   *url.URL
	Depth int
	Page  *Page
	Error error
}

// Work is our main event loop, coordinating request processing
// This is single threaded and is the only thread that writes into
// our internal maps, so we don't require coordination or locking
// (maps are not threadsafe)
func (c *Crawler) Work(target string, depth int, fetcher Fetcher) {
	var err error

	// Convert our target to a URL
	if c.target, err = url.Parse(target); err != nil {
		log.Errorf("Could not parse target '%s'", target)
		return
	}

	// Initialise channels to track requests
	c.completed = make(chan *Result)
	c.skipped = make(chan *Result)
	c.errored = make(chan *Result)

	// Initialise results containers
	c.Pages = make(map[string]*Page)
	c.Links = make(map[string]*Link)

	// Get our first page & track this
	go c.crawl(c.target, depth, fetcher)
	c.requestsInFlight++
	c.totalRequests++

	// Event loop
	for {
		select {
		case r := <-c.skipped:
			log.Debugf("Page skipped for %s", r.Url)
			c.totalRequests--
		case r := <-c.errored:
			log.Debugf("Page errored for %s: %v", r.Url, r.Error)
		case r := <-c.completed:
			log.Debugf("Page complete for %s", r.Url)
			if r.Page == nil {
				break
			}

			// Process each link
			for _, l := range r.Page.Links {

				// Skip page if not on our target domain
				if l.Target.Host != c.target.Host {
					// log.Debugf("Skipping %s as not on target domain", source.String())
					continue
				}

				// Check if we've already hit this page
				if _, exists := c.Pages[l.Target.String()]; exists {
					// log.Debugf("Skipping %s as already processed", l.Target.String())
					continue
				}

				log.Debugf("Triggering crawl of %s from %s", l.Target.String(), r.Url.String())
				go c.crawl(l.Target, r.Depth-1, fetcher)
				c.requestsInFlight++
				c.totalRequests++
			}
			log.Debugf("Fired %v new requests, %v currently in flight", len(r.Page.Links), c.requestsInFlight)

			c.Pages[r.Url.String()] = r.Page

		}

		// Decrement outstanding requests & and abort if complete
		c.requestsInFlight--
		if c.requestsInFlight == 0 {
			log.Debugf("Complete")
			return
		}
	}
}

// crawl uses fetcher to recursively crawl
// pages starting with url, to a maximum of depth.
func (c *Crawler) crawl(source *url.URL, depth int, fetcher Fetcher) {

	// The result of our crawl
	res := &Result{
		Depth: depth,
		Url:   source,
	}

	// Skip pages if we are at our maximum depth
	if depth <= 0 {
		log.Debugf("Skipping %s as at 0 depth", source.String())
		c.skipped <- res
		return
	}

	// Crawl the page, using our fetcher
	urls, assets, err := fetcher.Fetch(source.String())
	if err != nil {
		res.Error = err
		c.errored <- res
		return
	}

	log.Infof("%v URLs found at %s", len(urls), source.String())

	links := make([]*Link, 0)
	for _, u := range urls {
		links = append(links, &Link{
			Source: source,
			Target: u,
		})
	}

	// Store this page and links into the result
	res.Page = &Page{
		Url:    source,
		Links:  links,
		Assets: assets,
	}

	// 	// Mark this page as complete
	c.completed <- res
}

func (c *Crawler) TotalRequests() int {
	return c.totalRequests
}
<|endoftext|>


<commit_before>// Copyright 2013 Travis Keep. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or
// at http://opensource.org/licenses/BSD-3-Clause.

//This library is a modified version of the library at http://godoc.org/github.com/keep94/sunrise

// Package sunrise computes sunrises and sunsets using wikipedia article
// http://en.wikipedia.org/wiki/Sunrise_equation. Testing at my
// latitude and longitude in California shows that computed sunrises and
// sunsets can vary by as much as 2 minutes from those that NOAA reports
// at http://www.esrl.noaa.gov/gmd/grad/solcalc/sunrise.html.
package sunrise

import (
	"math"
	"time"
)

const (
	jepoch = float64(2451545.0)
	uepoch = int64(946728000.0)
)

//Location gives sunrise and sunset times.
type Location struct {
	location        *time.Location
	latitude        float64
	longitude       float64
	jstar           float64
	solarNoon       float64
	hourAngleInDays float64
}

// NewLocation computes the sunrise and sunset times for latitude and longitude
// around currentTime. Generally, the computed sunrise will be no earlier
// than 24 hours before currentTime and the computed sunset will be no later
// than 24 hours after currentTime. However, these differences may exceed 24
// hours on days with more than 23 hours of daylight.
// The latitude is positive for north and negative for south. Longitude is
// positive for east and negative for west.
func NewLocation(latitude float64, longitude float64) *Location {
	l := &Location{
		location:  time.Now().Location(),
		latitude:  latitude,
		longitude: longitude,
		jstar:     jStar(longitude),
	}

	l.computeSolarNoonHourAngle()

	return l
}

//Today updates instance for calculation of today's sunrise and sunset
func (l *Location) Today() {
	l.jstar = jStar(l.longitude)

	l.computeSolarNoonHourAngle()
}

// AddDays computes the sunrise and sunset numDays after
// (or before if numDays is negative) the current sunrise and sunset at the
// same latitude and longitude.
func (l *Location) AddDays(numDays int) {
	l.jstar += float64(numDays)
	l.computeSolarNoonHourAngle()
}

// Sunrise returns the current computed sunrise. Returned sunrise has the same
// location as the time passed to Around.
func (l *Location) Sunrise() time.Time {
	return goTime(l.solarNoon-l.hourAngleInDays, l.location)
}

// Sunset returns the current computed sunset. Returned sunset has the same
// location as the time passed to Around.
func (l *Location) Sunset() time.Time {
	return goTime(l.solarNoon+l.hourAngleInDays, l.location)
}

func (l *Location) computeSolarNoonHourAngle() {
	ma := mod360(357.5291 + 0.98560028*(l.jstar-jepoch))
	center := 1.9148*sin(ma) + 0.02*sin(2.0*ma) + 0.0003*sin(3.0*ma)
	el := mod360(ma + 102.9372 + center + 180.0)
	l.solarNoon = l.jstar + 0.0053*sin(ma) - 0.0069*sin(2.0*el)
	declination := asin(sin(el) * sin(23.45))
	l.hourAngleInDays = acos((sin(-0.83)-sin(l.latitude)*sin(declination))/(cos(l.latitude)*cos(declination))) / 360.0
}

func julianDay(unix int64) float64 {
	return float64(unix-uepoch)/86400.0 + jepoch
}

func jStar(longitude float64) float64 {
	return math.Floor(
		julianDay(time.Now().Unix())-0.0009+longitude/360.0+0.5) + 0.0009 - longitude/360.0
}

func goTime(julianDay float64, loc *time.Location) time.Time {
	unix := uepoch + int64((julianDay-jepoch)*86400.0)
	return time.Unix(unix, 0).In(loc)
}

func sin(degrees float64) float64 {
	return math.Sin(degrees * math.Pi / 180.0)
}

func cos(degrees float64) float64 {
	return math.Cos(degrees * math.Pi / 180.0)
}

func asin(x float64) float64 {
	return math.Asin(x) * 180.0 / math.Pi
}

func acos(x float64) float64 {
	if x >= 1.0 {
		return 0.0
	}
	if x <= -1.0 {
		return 180.0
	}
	return math.Acos(x) * 180.0 / math.Pi
}

func mod360(x float64) float64 {
	return x - 360.0*math.Floor(x/360.0)
}
<commit_msg>Added String interface<commit_after>// Copyright 2013 Travis Keep. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or
// at http://opensource.org/licenses/BSD-3-Clause.

//This library is a modified version of the library at http://godoc.org/github.com/keep94/sunrise

// Package sunrise computes sunrises and sunsets using wikipedia article
// http://en.wikipedia.org/wiki/Sunrise_equation. Testing at my
// latitude and longitude in California shows that computed sunrises and
// sunsets can vary by as much as 2 minutes from those that NOAA reports
// at http://www.esrl.noaa.gov/gmd/grad/solcalc/sunrise.html.
package sunrise

import (
	"fmt"
	"math"
	"time"
)

const (
	jepoch = float64(2451545.0)
	uepoch = int64(946728000.0)
)

//Location gives sunrise and sunset times.
type Location struct {
	location        *time.Location
	latitude        float64
	longitude       float64
	jstar           float64
	solarNoon       float64
	hourAngleInDays float64
}

// NewLocation computes the sunrise and sunset times for latitude and longitude
// around currentTime. Generally, the computed sunrise will be no earlier
// than 24 hours before currentTime and the computed sunset will be no later
// than 24 hours after currentTime. However, these differences may exceed 24
// hours on days with more than 23 hours of daylight.
// The latitude is positive for north and negative for south. Longitude is
// positive for east and negative for west.
func NewLocation(latitude float64, longitude float64) *Location {
	l := &Location{
		location:  time.Now().Location(),
		latitude:  latitude,
		longitude: longitude,
		jstar:     jStar(longitude),
	}

	l.computeSolarNoonHourAngle()

	return l
}

//String interface to show location details
func (l *Location) String() string {
	return fmt.Sprintf("Calculation Details: Lat %.3f, Long %.3f", l.latitude, l.longitude)
}

//Today updates instance for calculation of today's sunrise and sunset
func (l *Location) Today() {
	l.jstar = jStar(l.longitude)

	l.computeSolarNoonHourAngle()
}

// AddDays computes the sunrise and sunset numDays after
// (or before if numDays is negative) the current sunrise and sunset at the
// same latitude and longitude.
func (l *Location) AddDays(numDays int) {
	l.jstar += float64(numDays)
	l.computeSolarNoonHourAngle()
}

// Sunrise returns the current computed sunrise. Returned sunrise has the same
// location as the time passed to Around.
func (l *Location) Sunrise() time.Time {
	return goTime(l.solarNoon-l.hourAngleInDays, l.location)
}

// Sunset returns the current computed sunset. Returned sunset has the same
// location as the time passed to Around.
func (l *Location) Sunset() time.Time {
	return goTime(l.solarNoon+l.hourAngleInDays, l.location)
}

func (l *Location) computeSolarNoonHourAngle() {
	ma := mod360(357.5291 + 0.98560028*(l.jstar-jepoch))
	center := 1.9148*sin(ma) + 0.02*sin(2.0*ma) + 0.0003*sin(3.0*ma)
	el := mod360(ma + 102.9372 + center + 180.0)
	l.solarNoon = l.jstar + 0.0053*sin(ma) - 0.0069*sin(2.0*el)
	declination := asin(sin(el) * sin(23.45))
	l.hourAngleInDays = acos((sin(-0.83)-sin(l.latitude)*sin(declination))/(cos(l.latitude)*cos(declination))) / 360.0
}

func julianDay(unix int64) float64 {
	return float64(unix-uepoch)/86400.0 + jepoch
}

func jStar(longitude float64) float64 {
	return math.Floor(
		julianDay(time.Now().Unix())-0.0009+longitude/360.0+0.5) + 0.0009 - longitude/360.0
}

func goTime(julianDay float64, loc *time.Location) time.Time {
	unix := uepoch + int64((julianDay-jepoch)*86400.0)
	return time.Unix(unix, 0).In(loc)
}

func sin(degrees float64) float64 {
	return math.Sin(degrees * math.Pi / 180.0)
}

func cos(degrees float64) float64 {
	return math.Cos(degrees * math.Pi / 180.0)
}

func asin(x float64) float64 {
	return math.Asin(x) * 180.0 / math.Pi
}

func acos(x float64) float64 {
	if x >= 1.0 {
		return 0.0
	}
	if x <= -1.0 {
		return 180.0
	}
	return math.Acos(x) * 180.0 / math.Pi
}

func mod360(x float64) float64 {
	return x - 360.0*math.Floor(x/360.0)
}
<|endoftext|>


<commit_before>package iterator

import (
	"context"

	"github.com/cayleygraph/cayley/graph"
)

var _ graph.Iterator = &Not{}

// Not iterator acts like a complement for the primary iterator.
// It will return all the vertices which are not part of the primary iterator.
type Not struct {
	primaryIt graph.Iterator
	allIt     graph.Iterator
	result    graph.Ref
	runstats  graph.IteratorStats
	err       error
}

func NewNot(primaryIt, allIt graph.Iterator) *Not {
	return &Not{
		primaryIt: primaryIt,
		allIt:     allIt,
	}
}

// Reset resets the internal iterators and the iterator itself.
func (it *Not) Reset() {
	it.result = nil
	it.primaryIt.Reset()
	it.allIt.Reset()
}

func (it *Not) TagResults(dst map[string]graph.Ref) {
	if it.primaryIt != nil {
		it.primaryIt.TagResults(dst)
	}
}

// SubIterators returns a slice of the sub iterators.
// The first iterator is the primary iterator, for which the complement
// is generated.
func (it *Not) SubIterators() []graph.Iterator {
	return []graph.Iterator{it.primaryIt, it.allIt}
}

// Next advances the Not iterator. It returns whether there is another valid
// new value. It fetches the next value of the all iterator which is not
// contained by the primary iterator.
func (it *Not) Next(ctx context.Context) bool {
	it.runstats.Next += 1

	for it.allIt.Next(ctx) {
		if curr := it.allIt.Result(); !it.primaryIt.Contains(ctx, curr) {
			it.result = curr
			it.runstats.ContainsNext += 1
			return true
		}
	}
	it.err = it.allIt.Err()
	return false
}

func (it *Not) Err() error {
	return it.err
}

func (it *Not) Result() graph.Ref {
	return it.result
}

// Contains checks whether the passed value is part of the primary iterator's
// complement. For a valid value, it updates the Result returned by the iterator
// to the value itself.
func (it *Not) Contains(ctx context.Context, val graph.Ref) bool {
	it.runstats.Contains += 1

	if it.primaryIt.Contains(ctx, val) {
		return false
	}

	it.err = it.primaryIt.Err()
	if it.err != nil {
		// Explicitly return 'false', since an error occurred.
		return false
	}

	it.result = val
	return true
}

// NextPath checks whether there is another path. Not applicable, hence it will
// return false.
func (it *Not) NextPath(ctx context.Context) bool {
	return false
}

// Close closes the primary and all iterators.  It closes all subiterators
// it can, but returns the first error it encounters.
func (it *Not) Close() error {
	err := it.primaryIt.Close()

	_err := it.allIt.Close()
	if _err != nil && err == nil {
		err = _err
	}

	return err
}

func (it *Not) Optimize() (graph.Iterator, bool) {
	// TODO - consider wrapping the primaryIt with a MaterializeIt
	optimizedPrimaryIt, optimized := it.primaryIt.Optimize()
	if optimized {
		it.primaryIt = optimizedPrimaryIt
	}
	it.primaryIt = NewMaterialize(it.primaryIt)
	return it, false
}

func (it *Not) Stats() graph.IteratorStats {
	primaryStats := it.primaryIt.Stats()
	allStats := it.allIt.Stats()
	return graph.IteratorStats{
		NextCost:     allStats.NextCost + primaryStats.ContainsCost,
		ContainsCost: primaryStats.ContainsCost,
		Size:         allStats.Size - primaryStats.Size,
		ExactSize:    false,
		Next:         it.runstats.Next,
		Contains:     it.runstats.Contains,
		ContainsNext: it.runstats.ContainsNext,
	}
}

func (it *Not) Size() (int64, bool) {
	st := it.Stats()
	return st.Size, st.ExactSize
}

func (it *Not) String() string {
	return "Not"
}
<commit_msg>iterator: rewrite Not<commit_after>package iterator

import (
	"context"

	"github.com/cayleygraph/cayley/graph"
)

var _ graph.IteratorFuture = &Not{}

// Not iterator acts like a complement for the primary iterator.
// It will return all the vertices which are not part of the primary iterator.
type Not struct {
	it *not
	graph.Iterator
}

func NewNot(primaryIt, allIt graph.Iterator) *Not {
	it := &Not{
		it: newNot(graph.As2(primaryIt), graph.As2(allIt)),
	}
	it.Iterator = graph.NewLegacy(it.it)
	return it
}

func (it *Not) As2() graph.Iterator2 {
	it.Close()
	return it.it
}

var _ graph.Iterator2Compat = (*not)(nil)

// Not iterator acts like a complement for the primary iterator.
// It will return all the vertices which are not part of the primary iterator.
type not struct {
	primary graph.Iterator2
	allIt   graph.Iterator2
}

func newNot(primaryIt, allIt graph.Iterator2) *not {
	return &not{
		primary: primaryIt,
		allIt:   allIt,
	}
}

func (it *not) Iterate() graph.Iterator2Next {
	return newNotNext(it.primary.Lookup(), it.allIt.Iterate())
}

func (it *not) Lookup() graph.Iterator2Contains {
	return newNotContains(it.primary.Lookup())
}

func (it *not) AsLegacy() graph.Iterator {
	it2 := &Not{it: it}
	it2.Iterator = graph.NewLegacy(it)
	return it2
}

// SubIterators returns a slice of the sub iterators.
// The first iterator is the primary iterator, for which the complement
// is generated.
func (it *not) SubIterators() []graph.Iterator2 {
	return []graph.Iterator2{it.primary, it.allIt}
}

func (it *not) Optimize() (graph.Iterator2, bool) {
	// TODO - consider wrapping the primary with a MaterializeIt
	optimizedPrimaryIt, optimized := it.primary.Optimize()
	if optimized {
		it.primary = optimizedPrimaryIt
	}
	it.primary = newMaterialize(it.primary)
	return it, false
}

func (it *not) Stats() graph.IteratorStats {
	primaryStats := it.primary.Stats()
	allStats := it.allIt.Stats()
	return graph.IteratorStats{
		NextCost:     allStats.NextCost + primaryStats.ContainsCost,
		ContainsCost: primaryStats.ContainsCost,
		Size:         allStats.Size - primaryStats.Size,
		ExactSize:    false,
	}
}

func (it *not) Size() (int64, bool) {
	st := it.Stats()
	return st.Size, st.ExactSize
}

func (it *not) String() string {
	return "Not"
}

// Not iterator acts like a complement for the primary iterator.
// It will return all the vertices which are not part of the primary iterator.
type notNext struct {
	primaryIt graph.Iterator2Contains
	allIt     graph.Iterator2Next
	result    graph.Ref
}

func newNotNext(primaryIt graph.Iterator2Contains, allIt graph.Iterator2Next) *notNext {
	return &notNext{
		primaryIt: primaryIt,
		allIt:     allIt,
	}
}

func (it *notNext) TagResults(dst map[string]graph.Ref) {
	if it.primaryIt != nil {
		it.primaryIt.TagResults(dst)
	}
}

// Next advances the Not iterator. It returns whether there is another valid
// new value. It fetches the next value of the all iterator which is not
// contained by the primary iterator.
func (it *notNext) Next(ctx context.Context) bool {
	for it.allIt.Next(ctx) {
		if curr := it.allIt.Result(); !it.primaryIt.Contains(ctx, curr) {
			it.result = curr
			return true
		}
	}
	return false
}

func (it *notNext) Err() error {
	if err := it.allIt.Err(); err != nil {
		return err
	}
	if err := it.primaryIt.Err(); err != nil {
		return err
	}
	return nil
}

func (it *notNext) Result() graph.Ref {
	return it.result
}

// NextPath checks whether there is another path. Not applicable, hence it will
// return false.
func (it *notNext) NextPath(ctx context.Context) bool {
	return false
}

// Close closes the primary and all iterators.  It closes all subiterators
// it can, but returns the first error it encounters.
func (it *notNext) Close() error {
	err := it.primaryIt.Close()
	if err2 := it.allIt.Close(); err2 != nil && err == nil {
		err = err2
	}
	return err
}

func (it *notNext) String() string {
	return "NotNext"
}

// Not iterator acts like a complement for the primary iterator.
// It will return all the vertices which are not part of the primary iterator.
type notContains struct {
	primaryIt graph.Iterator2Contains
	result    graph.Ref
	err       error
}

func newNotContains(primaryIt graph.Iterator2Contains) *notContains {
	return &notContains{
		primaryIt: primaryIt,
	}
}

func (it *notContains) TagResults(dst map[string]graph.Ref) {
	if it.primaryIt != nil {
		it.primaryIt.TagResults(dst)
	}
}

func (it *notContains) Err() error {
	return it.err
}

func (it *notContains) Result() graph.Ref {
	return it.result
}

// Contains checks whether the passed value is part of the primary iterator's
// complement. For a valid value, it updates the Result returned by the iterator
// to the value itself.
func (it *notContains) Contains(ctx context.Context, val graph.Ref) bool {
	if it.primaryIt.Contains(ctx, val) {
		return false
	}
	it.err = it.primaryIt.Err()
	if it.err != nil {
		// Explicitly return 'false', since an error occurred.
		return false
	}
	it.result = val
	return true
}

// NextPath checks whether there is another path. Not applicable, hence it will
// return false.
func (it *notContains) NextPath(ctx context.Context) bool {
	return false
}

// Close closes the primary and all iterators.  It closes all subiterators
// it can, but returns the first error it encounters.
func (it *notContains) Close() error {
	return it.primaryIt.Close()
}

func (it *notContains) String() string {
	return "NotContains"
}
<|endoftext|>


<commit_before>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package service

import (
	"os"
	"path/filepath"
	"runtime"
	"strings"

	"golang.org/x/net/context"

	"github.com/keybase/client/go/engine"
	"github.com/keybase/client/go/libkb"
	keybase1 "github.com/keybase/client/go/protocol"
	rpc "github.com/keybase/go-framed-msgpack-rpc"
)

type ConfigHandler struct {
	libkb.Contextified
	xp     rpc.Transporter
	svc    *Service
	connID libkb.ConnectionID
}

func NewConfigHandler(xp rpc.Transporter, i libkb.ConnectionID, g *libkb.GlobalContext, svc *Service) *ConfigHandler {
	return &ConfigHandler{
		Contextified: libkb.NewContextified(g),
		xp:           xp,
		svc:          svc,
		connID:       i,
	}
}

func (h ConfigHandler) GetCurrentStatus(_ context.Context, sessionID int) (res keybase1.GetCurrentStatusRes, err error) {
	var cs libkb.CurrentStatus
	if cs, err = libkb.GetCurrentStatus(h.G()); err == nil {
		res = cs.Export()
	}
	return
}

func getPlatformInfo() keybase1.PlatformInfo {
	return keybase1.PlatformInfo{
		Os:        runtime.GOOS,
		Arch:      runtime.GOARCH,
		GoVersion: runtime.Version(),
	}
}

func (h ConfigHandler) GetExtendedStatus(_ context.Context, sessionID int) (res keybase1.ExtendedStatus, err error) {
	defer h.G().Trace("ConfigHandler::GetExtendedStatus", func() error { return err })()

	res.Standalone = h.G().Env.GetStandalone()
	res.LogDir = h.G().Env.GetLogDir()

	// Should work in standalone mode too
	if h.G().ConnectionManager != nil {
		res.Clients = h.G().ConnectionManager.ListAllLabeledConnections()
	}

	me, err := libkb.LoadMe(libkb.NewLoadUserArg(h.G()))
	if err != nil {
		h.G().Log.Debug("| could not load me user")
	} else {
		device, err := me.GetComputedKeyFamily().GetCurrentDevice(h.G())
		if err != nil {
			h.G().Log.Debug("| GetCurrentDevice failed: %s", err)
		} else {
			res.Device = device.ProtExport()
		}
	}

	h.G().LoginState().Account(func(a *libkb.Account) {
		res.PassphraseStreamCached = a.PassphraseStreamCache().Valid()
		if a.LoginSession() != nil {
			res.Session = a.LoginSession().Status()
		}
	}, "ConfigHandler::GetExtendedStatus")

	// this isn't quite ideal, but if there's a delegated UpdateUI available, then electron is running and connected.
	if h.G().UIRouter != nil {
		updateUI, err := h.G().UIRouter.GetUpdateUI()
		if err == nil && updateUI != nil {
			res.DesktopUIConnected = true
		}
	}

	current, all, err := h.G().GetAllUserNames()
	if err != nil {
		h.G().Log.Debug("| died in GetAllUseranmes()")
		return res, err
	}
	res.DefaultUsername = current.String()
	p := make([]string, len(all))
	for i, u := range all {
		p[i] = u.String()
	}
	res.ProvisionedUsernames = p
	res.PlatformInfo = getPlatformInfo()

	return res, nil
}

func (h ConfigHandler) GetConfig(_ context.Context, sessionID int) (keybase1.Config, error) {
	var c keybase1.Config

	c.ServerURI = h.G().Env.GetServerURI()
	c.RunMode = string(h.G().Env.GetRunMode())
	var err error
	c.SocketFile, err = h.G().Env.GetSocketFile()
	if err != nil {
		return c, err
	}

	gpg := h.G().GetGpgClient()
	canExec, err := gpg.CanExec()
	if err == nil {
		c.GpgExists = canExec
		c.GpgPath = gpg.Path()
	}

	c.Version = libkb.VersionString()
	c.VersionShort = libkb.Version

	var v []string
	libkb.VersionMessage(func(s string) {
		v = append(v, s)
	})
	c.VersionFull = strings.Join(v, "\n")

	dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err == nil {
		c.Path = dir
	}

	c.ConfigPath = h.G().Env.GetConfigFilename()
	c.Label = h.G().Env.GetLabel()
	if h.svc != nil {
		if h.svc.ForkType == keybase1.ForkType_AUTO {
			c.IsAutoForked = true
		}
		c.ForkType = h.svc.ForkType
	}

	return c, nil
}

func (h ConfigHandler) SetUserConfig(_ context.Context, arg keybase1.SetUserConfigArg) (err error) {
	eng := engine.NewUserConfigEngine(&engine.UserConfigEngineArg{
		Key:   arg.Key,
		Value: arg.Value,
	}, h.G())

	ctx := &engine.Context{}
	err = engine.RunEngine(eng, ctx)
	if err != nil {
		return err
	}
	return nil
}

func (h ConfigHandler) SetPath(_ context.Context, arg keybase1.SetPathArg) error {
	svcPath := os.Getenv("PATH")
	h.G().Log.Debug("SetPath: service path = %s", svcPath)
	h.G().Log.Debug("SetPath: client path =  %s", arg.Path)

	pathenv := strings.Split(svcPath, ":")
	var clientAdditions []string
NextDir:
	for _, dir := range strings.Split(arg.Path, ":") {
		for _, x := range pathenv {
			if x == dir {
				continue NextDir
			}
		}
		clientAdditions = append(clientAdditions, dir)
	}

	pathenv = append(pathenv, clientAdditions...)
	combined := strings.Join(pathenv, ":")

	if combined == svcPath {
		return nil
	}

	h.G().Log.Debug("SetPath: setting service path: %s", combined)
	os.Setenv("PATH", combined)

	return nil
}

func (h ConfigHandler) HelloIAm(_ context.Context, arg keybase1.ClientDetails) error {
	return h.G().ConnectionManager.Label(h.connID, arg)
}
<commit_msg>Use map<commit_after>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package service

import (
	"os"
	"path/filepath"
	"runtime"
	"strings"

	"golang.org/x/net/context"

	"github.com/keybase/client/go/engine"
	"github.com/keybase/client/go/libkb"
	keybase1 "github.com/keybase/client/go/protocol"
	rpc "github.com/keybase/go-framed-msgpack-rpc"
)

type ConfigHandler struct {
	libkb.Contextified
	xp     rpc.Transporter
	svc    *Service
	connID libkb.ConnectionID
}

func NewConfigHandler(xp rpc.Transporter, i libkb.ConnectionID, g *libkb.GlobalContext, svc *Service) *ConfigHandler {
	return &ConfigHandler{
		Contextified: libkb.NewContextified(g),
		xp:           xp,
		svc:          svc,
		connID:       i,
	}
}

func (h ConfigHandler) GetCurrentStatus(_ context.Context, sessionID int) (res keybase1.GetCurrentStatusRes, err error) {
	var cs libkb.CurrentStatus
	if cs, err = libkb.GetCurrentStatus(h.G()); err == nil {
		res = cs.Export()
	}
	return
}

func getPlatformInfo() keybase1.PlatformInfo {
	return keybase1.PlatformInfo{
		Os:        runtime.GOOS,
		Arch:      runtime.GOARCH,
		GoVersion: runtime.Version(),
	}
}

func (h ConfigHandler) GetExtendedStatus(_ context.Context, sessionID int) (res keybase1.ExtendedStatus, err error) {
	defer h.G().Trace("ConfigHandler::GetExtendedStatus", func() error { return err })()

	res.Standalone = h.G().Env.GetStandalone()
	res.LogDir = h.G().Env.GetLogDir()

	// Should work in standalone mode too
	if h.G().ConnectionManager != nil {
		res.Clients = h.G().ConnectionManager.ListAllLabeledConnections()
	}

	me, err := libkb.LoadMe(libkb.NewLoadUserArg(h.G()))
	if err != nil {
		h.G().Log.Debug("| could not load me user")
	} else {
		device, err := me.GetComputedKeyFamily().GetCurrentDevice(h.G())
		if err != nil {
			h.G().Log.Debug("| GetCurrentDevice failed: %s", err)
		} else {
			res.Device = device.ProtExport()
		}
	}

	h.G().LoginState().Account(func(a *libkb.Account) {
		res.PassphraseStreamCached = a.PassphraseStreamCache().Valid()
		if a.LoginSession() != nil {
			res.Session = a.LoginSession().Status()
		}
	}, "ConfigHandler::GetExtendedStatus")

	// this isn't quite ideal, but if there's a delegated UpdateUI available, then electron is running and connected.
	if h.G().UIRouter != nil {
		updateUI, err := h.G().UIRouter.GetUpdateUI()
		if err == nil && updateUI != nil {
			res.DesktopUIConnected = true
		}
	}

	current, all, err := h.G().GetAllUserNames()
	if err != nil {
		h.G().Log.Debug("| died in GetAllUseranmes()")
		return res, err
	}
	res.DefaultUsername = current.String()
	p := make([]string, len(all))
	for i, u := range all {
		p[i] = u.String()
	}
	res.ProvisionedUsernames = p
	res.PlatformInfo = getPlatformInfo()

	return res, nil
}

func (h ConfigHandler) GetConfig(_ context.Context, sessionID int) (keybase1.Config, error) {
	var c keybase1.Config

	c.ServerURI = h.G().Env.GetServerURI()
	c.RunMode = string(h.G().Env.GetRunMode())
	var err error
	c.SocketFile, err = h.G().Env.GetSocketFile()
	if err != nil {
		return c, err
	}

	gpg := h.G().GetGpgClient()
	canExec, err := gpg.CanExec()
	if err == nil {
		c.GpgExists = canExec
		c.GpgPath = gpg.Path()
	}

	c.Version = libkb.VersionString()
	c.VersionShort = libkb.Version

	var v []string
	libkb.VersionMessage(func(s string) {
		v = append(v, s)
	})
	c.VersionFull = strings.Join(v, "\n")

	dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err == nil {
		c.Path = dir
	}

	c.ConfigPath = h.G().Env.GetConfigFilename()
	c.Label = h.G().Env.GetLabel()
	if h.svc != nil {
		if h.svc.ForkType == keybase1.ForkType_AUTO {
			c.IsAutoForked = true
		}
		c.ForkType = h.svc.ForkType
	}

	return c, nil
}

func (h ConfigHandler) SetUserConfig(_ context.Context, arg keybase1.SetUserConfigArg) (err error) {
	eng := engine.NewUserConfigEngine(&engine.UserConfigEngineArg{
		Key:   arg.Key,
		Value: arg.Value,
	}, h.G())

	ctx := &engine.Context{}
	err = engine.RunEngine(eng, ctx)
	if err != nil {
		return err
	}
	return nil
}

func (h ConfigHandler) SetPath(_ context.Context, arg keybase1.SetPathArg) error {
	svcPath := os.Getenv("PATH")
	h.G().Log.Debug("SetPath: service path = %s", svcPath)
	h.G().Log.Debug("SetPath: client path =  %s", arg.Path)

	pathenv := strings.Split(svcPath, ":")
	pathset := make(map[string]bool)
	for _, p := range pathenv {
		pathset[p] = true
	}

	var clientAdditions []string
	for _, dir := range strings.Split(arg.Path, ":") {
		if _, ok := pathset[dir]; ok {
			continue
		}
		clientAdditions = append(clientAdditions, dir)
	}

	pathenv = append(pathenv, clientAdditions...)
	combined := strings.Join(pathenv, ":")

	if combined == svcPath {
		return nil
	}

	h.G().Log.Debug("SetPath: setting service path: %s", combined)
	os.Setenv("PATH", combined)

	return nil
}

func (h ConfigHandler) HelloIAm(_ context.Context, arg keybase1.ClientDetails) error {
	return h.G().ConnectionManager.Label(h.connID, arg)
}
<|endoftext|>


<commit_before>package plugins

import "github.com/bwmarrin/discordgo"

type About struct{}

func (a *About) Commands() []string {
	return []string{
		"about",
		"info",
	}
}

func (a *About) Init(session *discordgo.Session) {

}

func (a *About) Action(command string, content string, msg *discordgo.Message, session *discordgo.Session) {
	m := "This is Robyul 2, the successor of our old and still loved Robyul.\nYou can find more about Robyul 2 here: <https://robyul.chat>.\nRobyul 2 is a fork of Karen:\n"

	m += "Hi my name is Karen!\nI'm a :robot: that will make this Discord Server a better place c:\nHere is some information about me:\n```\n"

	m += `
Karen Araragi (阿良々木 火憐, Araragi Karen) is the eldest of Koyomi Araragi's sisters and the older half of
the Tsuganoki 2nd Middle School Fire Sisters (栂の木二中のファイヤーシスターズ, Tsuganoki Ni-chuu no Faiya Shisutazu).

She is a self-proclaimed "hero of justice" who often imitates the personality and
quirks of various characters from tokusatsu series.
Despite this, she is completely uninvolved with the supernatural, until she becomes victim to a certain oddity.
She is the titular protagonist of two arcs: Karen Bee and Karen Ogre. She is also the narrator of Karen Ogre.
`

	m += "\n```"
	m += "BTW: I'm :free:, open-source and built using the Go programming language.\n"
	m += "Visit me at <http://karen.vc> or <https://git.lukas.moe/sn0w/Karen>"

	session.ChannelMessageSend(msg.ChannelID, m)
}
<commit_msg>[about] updates text<commit_after>package plugins

import "github.com/bwmarrin/discordgo"

type About struct{}

func (a *About) Commands() []string {
	return []string{
		"about",
		"info",
	}
}

func (a *About) Init(session *discordgo.Session) {

}

func (a *About) Action(command string, content string, msg *discordgo.Message, session *discordgo.Session) {
	m := "**Hey! I'm Robyul.**\nI'm built using Go, open-source and a fork of Shiro, formerly called Karen, which you can find here: <https://github.com/SubliminalHQ/shiro>.\nYou can find out more about me here: <https://robyul.chat/>.\nSuggestions and discussions are always welcome on the Discord for me: <https://discord.gg/s5qZvUV>."

	session.ChannelMessageSend(msg.ChannelID, m)
}
<|endoftext|>


<commit_before>/*
Package graceful implements graceful shutdown for HTTP servers by closing idle
connections after receiving a signal. By default, this package listens for
interrupts (i.e., SIGINT), but when it detects that it is running under Einhorn
it will additionally listen for SIGUSR2 as well, giving your application
automatic support for graceful upgrades.

It's worth mentioning explicitly that this package is a hack to shim graceful
shutdown behavior into the net/http package provided in Go 1.2. It was written
by carefully reading the sequence of function calls net/http happened to use as
of this writing and finding enough surface area with which to add appropriate
behavior. There's a very good chance that this package will cease to work in
future versions of Go, but with any luck the standard library will add support
of its own by then.

If you're interested in figuring out how this package works, we suggest you read
the documentation for WrapConn() and net.go.
*/
package graceful

import (
	"crypto/tls"
	"net"
	"net/http"
	"time"
)

// Exactly like net/http's Server. In fact, it *is* a net/http Server, just with
// different method implementations
type Server http.Server

// About 200 years, also known as "forever"
const forever time.Duration = 200 * 365 * 24 * time.Hour

/*
You might notice that these methods look awfully similar to the methods of the
same name from the go standard library--that's because they were stolen from
there! If go were more like, say, Ruby, it'd actually be possible to shim just
the Serve() method, since we can do everything we want from there. However, it's
not possible to get the other methods which call Serve() (ListenAndServe(), say)
to call your shimmed copy--they always call the original.

Since I couldn't come up with a better idea, I just copy-and-pasted both
ListenAndServe and ListenAndServeTLS here more-or-less verbatim. "Oh well!"
*/

// Behaves exactly like the net/http function of the same name.
func (srv *Server) Serve(l net.Listener) (err error) {
	go func() {
		<-kill
		l.Close()
	}()
	l = WrapListener(l)

	// Spawn a shadow http.Server to do the actual servering. We do this
	// because we need to sketch on some of the parameters you passed in,
	// and it's nice to keep our sketching to ourselves.
	shadow := *(*http.Server)(srv)

	if shadow.ReadTimeout == 0 {
		shadow.ReadTimeout = forever
	}
	shadow.Handler = Middleware(shadow.Handler)

	err = shadow.Serve(l)

	// We expect an error when we close the listener, so we indiscriminately
	// swallow Serve errors when we're in a shutdown state.
	select {
	case <-kill:
		return nil
	default:
		return err
	}
}

// Behaves exactly like the net/http function of the same name.
func (srv *Server) ListenAndServe() error {
	addr := srv.Addr
	if addr == "" {
		addr = ":http"
	}
	l, e := net.Listen("tcp", addr)
	if e != nil {
		return e
	}
	return srv.Serve(l)
}

// Behaves exactly like the net/http function of the same name.
func (srv *Server) ListenAndServeTLS(certFile, keyFile string) error {
	addr := srv.Addr
	if addr == "" {
		addr = ":https"
	}
	config := &tls.Config{}
	if srv.TLSConfig != nil {
		*config = *srv.TLSConfig
	}
	if config.NextProtos == nil {
		config.NextProtos = []string{"http/1.1"}
	}

	var err error
	config.Certificates = make([]tls.Certificate, 1)
	config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		return err
	}

	conn, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}

	tlsListener := tls.NewListener(conn, config)
	return srv.Serve(tlsListener)
}

// Behaves exactly like the net/http function of the same name.
func ListenAndServe(addr string, handler http.Handler) error {
	server := &Server{Addr: addr, Handler: handler}
	return server.ListenAndServe()
}

// Behaves exactly like the net/http function of the same name.
func ListenAndServeTLS(addr, certfile, keyfile string, handler http.Handler) error {
	server := &Server{Addr: addr, Handler: handler}
	return server.ListenAndServeTLS(certfile, keyfile)
}

// Behaves exactly like the net/http function of the same name.
func Serve(l net.Listener, handler http.Handler) error {
	server := &Server{Handler: handler}
	return server.Serve(l)
}
<commit_msg>Reference graceful shutdown golang issue<commit_after>/*
Package graceful implements graceful shutdown for HTTP servers by closing idle
connections after receiving a signal. By default, this package listens for
interrupts (i.e., SIGINT), but when it detects that it is running under Einhorn
it will additionally listen for SIGUSR2 as well, giving your application
automatic support for graceful upgrades.

It's worth mentioning explicitly that this package is a hack to shim graceful
shutdown behavior into the net/http package provided in Go 1.2. It was written
by carefully reading the sequence of function calls net/http happened to use as
of this writing and finding enough surface area with which to add appropriate
behavior. There's a very good chance that this package will cease to work in
future versions of Go, but with any luck the standard library will add support
of its own by then (https://code.google.com/p/go/issues/detail?id=4674).

If you're interested in figuring out how this package works, we suggest you read
the documentation for WrapConn() and net.go.
*/
package graceful

import (
	"crypto/tls"
	"net"
	"net/http"
	"time"
)

// Exactly like net/http's Server. In fact, it *is* a net/http Server, just with
// different method implementations
type Server http.Server

// About 200 years, also known as "forever"
const forever time.Duration = 200 * 365 * 24 * time.Hour

/*
You might notice that these methods look awfully similar to the methods of the
same name from the go standard library--that's because they were stolen from
there! If go were more like, say, Ruby, it'd actually be possible to shim just
the Serve() method, since we can do everything we want from there. However, it's
not possible to get the other methods which call Serve() (ListenAndServe(), say)
to call your shimmed copy--they always call the original.

Since I couldn't come up with a better idea, I just copy-and-pasted both
ListenAndServe and ListenAndServeTLS here more-or-less verbatim. "Oh well!"
*/

// Behaves exactly like the net/http function of the same name.
func (srv *Server) Serve(l net.Listener) (err error) {
	go func() {
		<-kill
		l.Close()
	}()
	l = WrapListener(l)

	// Spawn a shadow http.Server to do the actual servering. We do this
	// because we need to sketch on some of the parameters you passed in,
	// and it's nice to keep our sketching to ourselves.
	shadow := *(*http.Server)(srv)

	if shadow.ReadTimeout == 0 {
		shadow.ReadTimeout = forever
	}
	shadow.Handler = Middleware(shadow.Handler)

	err = shadow.Serve(l)

	// We expect an error when we close the listener, so we indiscriminately
	// swallow Serve errors when we're in a shutdown state.
	select {
	case <-kill:
		return nil
	default:
		return err
	}
}

// Behaves exactly like the net/http function of the same name.
func (srv *Server) ListenAndServe() error {
	addr := srv.Addr
	if addr == "" {
		addr = ":http"
	}
	l, e := net.Listen("tcp", addr)
	if e != nil {
		return e
	}
	return srv.Serve(l)
}

// Behaves exactly like the net/http function of the same name.
func (srv *Server) ListenAndServeTLS(certFile, keyFile string) error {
	addr := srv.Addr
	if addr == "" {
		addr = ":https"
	}
	config := &tls.Config{}
	if srv.TLSConfig != nil {
		*config = *srv.TLSConfig
	}
	if config.NextProtos == nil {
		config.NextProtos = []string{"http/1.1"}
	}

	var err error
	config.Certificates = make([]tls.Certificate, 1)
	config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		return err
	}

	conn, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}

	tlsListener := tls.NewListener(conn, config)
	return srv.Serve(tlsListener)
}

// Behaves exactly like the net/http function of the same name.
func ListenAndServe(addr string, handler http.Handler) error {
	server := &Server{Addr: addr, Handler: handler}
	return server.ListenAndServe()
}

// Behaves exactly like the net/http function of the same name.
func ListenAndServeTLS(addr, certfile, keyfile string, handler http.Handler) error {
	server := &Server{Addr: addr, Handler: handler}
	return server.ListenAndServeTLS(certfile, keyfile)
}

// Behaves exactly like the net/http function of the same name.
func Serve(l net.Listener, handler http.Handler) error {
	server := &Server{Handler: handler}
	return server.Serve(l)
}
<|endoftext|>


<commit_before>package tunnel

import (
	"fmt"
	"net/http"
	"reflect"
	"sync"
	"time"

	"github.com/gorilla/websocket"
	"github.com/looplab/fsm"
	"github.com/ondevice/ondevice/api"
	"github.com/ondevice/ondevice/config"
	"github.com/ondevice/ondevice/logg"
	"github.com/ondevice/ondevice/util"
)

// Connection -- WebSocket connection
//
// internal state machine:
// - initial (OpenWebsocket() sets the state to 'connecting' pretty soon after creating the state machine, so this state isn't all too relevant)
// - connecting: event: "connect", from: initial
// - open: event: "connected", from: connecting
// - error: event: "error", from: initial, connecting, open
// - closed: event: "close", from: initial, connecting, open, error, timeout
type Connection struct {
	ws    *websocket.Conn
	state *fsm.FSM

	CloseListeners    []func()
	ErrorListeners    []func(err util.APIError)
	MessageListerners []func(int, []byte)

	writeLock sync.Mutex
	done      chan struct{}
}

// OpenWebsocket -- Open a websocket connection
func OpenWebsocket(c *Connection, endpoint string, params map[string]string, onMessage func(int, []byte), auths ...api.Authentication) util.APIError {
	if c.state != nil {
		panic("OpenWebsocket() called twice on a single Connection!")
	}
	c.state = fsm.NewFSM("initial", fsm.Events{
		{Name: "connect", Src: []string{"initial"}, Dst: "connecting"},
		{Name: "connected", Src: []string{"connecting"}, Dst: "open"},
		{Name: "error", Src: []string{"initial", "connecting", "open"}, Dst: "error"},
		{Name: "close", Src: []string{"initial", "connecting", "open", "error"}, Dst: "closed"},
	}, fsm.Callbacks{
		"after_error":  c._onError,
		"enter_closed": c._onClose,
		"enter_state":  c._onStateChange,
	})

	hdr := http.Header{}

	var auth api.Authentication
	if len(auths) == 0 {
		var err error
		if auth, err = api.CreateClientAuth(); err != nil {
			return util.NewAPIError(util.OtherError, err.Error())
		}
	} else {
		auth = auths[0]
	}

	hdr.Add("Authorization", auth.GetAuthHeader())
	hdr.Add("User-agent", fmt.Sprintf("ondevice v%s", config.GetVersion()))

	url := auth.GetURL(endpoint+"/websocket", params, "wss")
	logg.Debugf("Opening websocket connection to '%s' (auth: '%s')", url, auth.GetAuthHeader())

	c.state.Event("connect")
	websocket.DefaultDialer.HandshakeTimeout = 60 * time.Second
	ws, resp, err := websocket.DefaultDialer.Dial(url, hdr)
	if err != nil {
		if resp != nil {
			if resp.StatusCode == 401 {
				return util.NewAPIError(resp.StatusCode, "API server authentication failed")
			}
			return util.NewAPIError(resp.StatusCode, "Error opening websocket: ", err)
		}
		return util.NewAPIError(util.OtherError, "Error opening websocket: ", err)
	}

	c.ws = ws
	c.MessageListerners = append(c.MessageListerners, onMessage)
	c.done = make(chan struct{})

	go c.receive()

	return nil
}

// Close -- Close the underlying WebSocket connection
func (c *Connection) Close() {
	if err := c.state.Event("close"); err != nil {
		// TODO do error handling (and ignore 'already in closed state' error)
	}
}

// IsClosed -- Returns true for closed connections (either being closed normally or due to an error/timeout)
func (c *Connection) IsClosed() bool {
	return c.state != nil && c.state.Is("closed")
}

func (c *Connection) receive() {
	defer c.Close()

	for {
		msgType, msg, err := c.ws.ReadMessage()
		if err != nil {
			if e, ok := err.(*websocket.CloseError); ok {
				if e.Code == 1000 {
					// normal close
				} else {
					logg.Error("Websocket closed abnormally: ", err)
				}
			} else {
				if !c.IsClosed() {
					logg.Errorf("read error (type: %s): %s", reflect.TypeOf(err), err)
					c._error(util.NewAPIError(util.OtherError, err.Error()))
				} else {
					logg.Debug("Connetion.receive() interrupted by error: ", reflect.TypeOf(err), err)
				}
			}
			return
		}
		for _, cb := range c.MessageListerners {
			cb(msgType, msg)
		}
	}
}

// SendBinary -- Send binary WebSocket message
func (c *Connection) SendBinary(data []byte) error {
	c.writeLock.Lock()
	defer c.writeLock.Unlock()

	return c.ws.WriteMessage(websocket.BinaryMessage, data)
}

// SendJSON -- Send a JSON text message to the WebSocket
func (c *Connection) SendJSON(value interface{}) error {
	c.writeLock.Lock()
	defer c.writeLock.Unlock()

	return c.ws.WriteJSON(value)
}

// SendText -- send a raw text websocket messge (use SendJson instead where possible)
func (c *Connection) SendText(msg string) error {
	c.writeLock.Lock()
	defer c.writeLock.Unlock()

	return c.ws.WriteMessage(websocket.TextMessage, []byte(msg))
}

// Wait -- Wait for the connection to close
func (c *Connection) Wait() {
	<-c.done
}

// _error -- Puts the connection into the 'error' state and closes it
func (c *Connection) _error(err util.APIError) {
	c.state.Event("error", err)
}

func (c *Connection) _onClose(ev *fsm.Event) {
	if c.ws != nil { // could be nil if a goroutine called us before we are connected
		c.ws.Close()
	}

	close(c.done)
	for _, cb := range c.CloseListeners {
		cb()
	}
}

func (c *Connection) _onError(ev *fsm.Event) {
	err, ok := ev.Args[0].(util.APIError)
	if !ok {
		panic("Connection._onError() expects an APIError parameter!")
	}
	for _, cb := range c.ErrorListeners {
		cb(err)
	}

	c.Close()
}

func (c *Connection) _onStateChange(ev *fsm.Event) {
	logg.Debugf("Connection state changed: ", ev.Src, " -> ", ev.Dst)
}
<commit_msg>tunnel._onStateChange: fixed debug message (was using Debugf() instead of Debug())<commit_after>package tunnel

import (
	"fmt"
	"net/http"
	"reflect"
	"sync"
	"time"

	"github.com/gorilla/websocket"
	"github.com/looplab/fsm"
	"github.com/ondevice/ondevice/api"
	"github.com/ondevice/ondevice/config"
	"github.com/ondevice/ondevice/logg"
	"github.com/ondevice/ondevice/util"
)

// Connection -- WebSocket connection
//
// internal state machine:
// - initial (OpenWebsocket() sets the state to 'connecting' pretty soon after creating the state machine, so this state isn't all too relevant)
// - connecting: event: "connect", from: initial
// - open: event: "connected", from: connecting
// - error: event: "error", from: initial, connecting, open
// - closed: event: "close", from: initial, connecting, open, error, timeout
type Connection struct {
	ws    *websocket.Conn
	state *fsm.FSM

	CloseListeners    []func()
	ErrorListeners    []func(err util.APIError)
	MessageListerners []func(int, []byte)

	writeLock sync.Mutex
	done      chan struct{}
}

// OpenWebsocket -- Open a websocket connection
func OpenWebsocket(c *Connection, endpoint string, params map[string]string, onMessage func(int, []byte), auths ...api.Authentication) util.APIError {
	if c.state != nil {
		panic("OpenWebsocket() called twice on a single Connection!")
	}
	c.state = fsm.NewFSM("initial", fsm.Events{
		{Name: "connect", Src: []string{"initial"}, Dst: "connecting"},
		{Name: "connected", Src: []string{"connecting"}, Dst: "open"},
		{Name: "error", Src: []string{"initial", "connecting", "open"}, Dst: "error"},
		{Name: "close", Src: []string{"initial", "connecting", "open", "error"}, Dst: "closed"},
	}, fsm.Callbacks{
		"after_error":  c._onError,
		"enter_closed": c._onClose,
		"enter_state":  c._onStateChange,
	})

	hdr := http.Header{}

	var auth api.Authentication
	if len(auths) == 0 {
		var err error
		if auth, err = api.CreateClientAuth(); err != nil {
			return util.NewAPIError(util.OtherError, err.Error())
		}
	} else {
		auth = auths[0]
	}

	hdr.Add("Authorization", auth.GetAuthHeader())
	hdr.Add("User-agent", fmt.Sprintf("ondevice v%s", config.GetVersion()))

	url := auth.GetURL(endpoint+"/websocket", params, "wss")
	logg.Debugf("Opening websocket connection to '%s' (auth: '%s')", url, auth.GetAuthHeader())

	c.state.Event("connect")
	websocket.DefaultDialer.HandshakeTimeout = 60 * time.Second
	ws, resp, err := websocket.DefaultDialer.Dial(url, hdr)
	if err != nil {
		if resp != nil {
			if resp.StatusCode == 401 {
				return util.NewAPIError(resp.StatusCode, "API server authentication failed")
			}
			return util.NewAPIError(resp.StatusCode, "Error opening websocket: ", err)
		}
		return util.NewAPIError(util.OtherError, "Error opening websocket: ", err)
	}

	c.ws = ws
	c.MessageListerners = append(c.MessageListerners, onMessage)
	c.done = make(chan struct{})

	go c.receive()

	return nil
}

// Close -- Close the underlying WebSocket connection
func (c *Connection) Close() {
	if err := c.state.Event("close"); err != nil {
		// TODO do error handling (and ignore 'already in closed state' error)
	}
}

// IsClosed -- Returns true for closed connections (either being closed normally or due to an error/timeout)
func (c *Connection) IsClosed() bool {
	return c.state != nil && c.state.Is("closed")
}

func (c *Connection) receive() {
	defer c.Close()

	for {
		msgType, msg, err := c.ws.ReadMessage()
		if err != nil {
			if e, ok := err.(*websocket.CloseError); ok {
				if e.Code == 1000 {
					// normal close
				} else {
					logg.Error("Websocket closed abnormally: ", err)
				}
			} else {
				if !c.IsClosed() {
					logg.Errorf("read error (type: %s): %s", reflect.TypeOf(err), err)
					c._error(util.NewAPIError(util.OtherError, err.Error()))
				} else {
					logg.Debug("Connetion.receive() interrupted by error: ", reflect.TypeOf(err), err)
				}
			}
			return
		}
		for _, cb := range c.MessageListerners {
			cb(msgType, msg)
		}
	}
}

// SendBinary -- Send binary WebSocket message
func (c *Connection) SendBinary(data []byte) error {
	c.writeLock.Lock()
	defer c.writeLock.Unlock()

	return c.ws.WriteMessage(websocket.BinaryMessage, data)
}

// SendJSON -- Send a JSON text message to the WebSocket
func (c *Connection) SendJSON(value interface{}) error {
	c.writeLock.Lock()
	defer c.writeLock.Unlock()

	return c.ws.WriteJSON(value)
}

// SendText -- send a raw text websocket messge (use SendJson instead where possible)
func (c *Connection) SendText(msg string) error {
	c.writeLock.Lock()
	defer c.writeLock.Unlock()

	return c.ws.WriteMessage(websocket.TextMessage, []byte(msg))
}

// Wait -- Wait for the connection to close
func (c *Connection) Wait() {
	<-c.done
}

// _error -- Puts the connection into the 'error' state and closes it
func (c *Connection) _error(err util.APIError) {
	c.state.Event("error", err)
}

func (c *Connection) _onClose(ev *fsm.Event) {
	if c.ws != nil { // could be nil if a goroutine called us before we are connected
		c.ws.Close()
	}

	close(c.done)
	for _, cb := range c.CloseListeners {
		cb()
	}
}

func (c *Connection) _onError(ev *fsm.Event) {
	err, ok := ev.Args[0].(util.APIError)
	if !ok {
		panic("Connection._onError() expects an APIError parameter!")
	}
	for _, cb := range c.ErrorListeners {
		cb(err)
	}

	c.Close()
}

func (c *Connection) _onStateChange(ev *fsm.Event) {
	logg.Debug("Connection state changed: ", ev.Src, " -> ", ev.Dst)
}
<|endoftext|>


<commit_before>package main

import (
    "appengine"
    "appengine/datastore"
    "strconv"
    "time"
)

type DB struct {
    Context appengine.Context
}

func (db *DB) FetchUser(user string) {
    vineApi := VineRequest{db.Context}
    data, err := vineApi.GetUser(user)

    if data["private"].(float64) == 1.0 {
        return
    }

    var userMeta StoredUserMeta
    var userData StoredUserData

    userId := strconv.FormatFloat(data["userId"].(float64), 'f', -1, 64)

    userMetaTemp, err := db.GetUserMeta(userId)

    if err == datastore.ErrNoSuchEntity {
        userMeta = StoredUserMeta{
            Username: data["username"].(string),
            Location: data["location"].(string),
            Description: data["description"].(string),
            Verified: data["verified"].(float64) == 1.0,
            AvatarUrl: data["avatarUrl"].(string),
        }
        if data["vanityUrls"] != nil {
            userMeta.VanityUrl = data["vanityUrls"].([]interface{})[0].(string)
        }

        if data["background"] != nil {
            userMeta.Background = data["background"].(string)
        }

        if userMeta.Verified {
            userMeta.VerifiedDate = time.Now()
        }

        userData = StoredUserData{
            LastUpdated: time.Now(),
            Followers: []float64{data["followerCount"].(float64),},
            Following: []float64{data["followingCount"].(float64),},
            Loops: []float64{data["loopCount"].(float64),},
            AuthoredPosts: []float64{data["authoredPostCount"].(float64),},
            Reposts: []float64{data["postCount"].(float64) - data["authoredPostCount"].(float64),},
            Likes: []float64{data["likeCount"].(float64),},
            Updated: []time.Time{time.Now(),},
        }

    } else {

        userMeta = userMetaTemp.(StoredUserMeta)

        if userMeta.Location != data["location"].(string) {
            userMeta.Previous.Location = append(userMeta.Previous.Location, PreviousLocation{userMeta.Location, time.Now()})
            userMeta.Location = data["location"].(string)
        }

        if userMeta.Username != data["username"].(string) {
            userMeta.Previous.Username = append(userMeta.Previous.Username, PreviousUsername{userMeta.Username, time.Now()})
            userMeta.Username = data["username"].(string)
        }

        if userMeta.Description != data["description"].(string) {
            userMeta.Previous.Description = append(userMeta.Previous.Description, PreviousDescription{userMeta.Description, time.Now()})
            userMeta.Description = data["description"].(string)
        }

        if userMeta.Background != data["background"].(string) {
            userMeta.Previous.Background = append(userMeta.Previous.Background, PreviousBackground{userMeta.Background, time.Now()})
            userMeta.Background = data["background"].(string)
        }

        userDataTemp, err := db.GetUserData(userId)
        userData = userDataTemp.(StoredUserData)

        if err != datastore.ErrNoSuchEntity {
            userData.LastUpdated = time.Now()
            userData.Followers = append(userData.Followers, data["followerCount"].(float64))
            userData.Following = append(userData.Following, data["followingCount"].(float64))
            userData.Loops = append(userData.Loops, data["loopCount"].(float64))
            userData.AuthoredPosts = append(userData.AuthoredPosts, data["authoredPostCount"].(float64))
            userData.Reposts = append(userData.Reposts, data["postCount"].(float64) - data["authoredPostCount"].(float64))
            userData.Likes = append(userData.Followers, data["likeCount"].(float64))
            userData.Updated = append(userData.Updated, time.Now())
        }
    }

    dataKey := datastore.NewKey(db.Context, "UserData", userId, 0, nil)
    metaKey := datastore.NewKey(db.Context, "UserMeta", userId, 0, nil)

    datastore.Put(db.Context, dataKey, &userData)
    datastore.Put(db.Context, metaKey, &userMeta)
}

func (db *DB) GetUserData(user string) (interface{}, error) {

    var data *StoredUserData

    key := datastore.NewKey(db.Context, "UserMeta", user, 0, nil)
    err := datastore.Get(db.Context, key, &data)

    if err != nil {
        return nil, err
    } else {
        return data, nil
    }
}

func (db *DB) GetUserMeta(user string) (interface{}, error) {

    var meta *StoredUserMeta

	key := datastore.NewKey(db.Context, "UserData", user, 0, nil)
    err := datastore.Get(db.Context, key, &meta)

    if err != nil {
        return nil, err
    } else {
        return meta, nil
    }
}<commit_msg>Fixed datastore keys and kinds<commit_after>package main

import (
    "appengine"
    "appengine/datastore"
    "strconv"
    "time"
)

type DB struct {
    Context appengine.Context
}

func (db *DB) FetchUser(user string) {
    vineApi := VineRequest{db.Context}
    data, err := vineApi.GetUser(user)

    if data["private"].(float64) == 1.0 {
        return
    }

    var userMeta StoredUserMeta
    var userData StoredUserData

    userId := strconv.FormatFloat(data["userId"].(float64), 'f', -1, 64)

    userMetaTemp, err := db.GetUserMeta(userId)

    if err == datastore.ErrNoSuchEntity {
        userMeta = StoredUserMeta{
            Username: data["username"].(string),
            Location: data["location"].(string),
            Description: data["description"].(string),
            Verified: data["verified"].(float64) == 1.0,
            AvatarUrl: data["avatarUrl"].(string),
        }
        if data["vanityUrls"] != nil {
            userMeta.VanityUrl = data["vanityUrls"].([]interface{})[0].(string)
        }

        if data["background"] != nil {
            userMeta.Background = data["background"].(string)
        }

        if userMeta.Verified {
            userMeta.VerifiedDate = time.Now()
        }

        userData = StoredUserData{
            LastUpdated: time.Now(),
            Followers: []float64{data["followerCount"].(float64),},
            Following: []float64{data["followingCount"].(float64),},
            Loops: []float64{data["loopCount"].(float64),},
            AuthoredPosts: []float64{data["authoredPostCount"].(float64),},
            Reposts: []float64{data["postCount"].(float64) - data["authoredPostCount"].(float64),},
            Likes: []float64{data["likeCount"].(float64),},
            Updated: []time.Time{time.Now(),},
        }

    } else {

        userMeta = userMetaTemp.(StoredUserMeta)

        if userMeta.Location != data["location"].(string) {
            userMeta.Previous.Location = append(userMeta.Previous.Location, PreviousLocation{userMeta.Location, time.Now()})
            userMeta.Location = data["location"].(string)
        }

        if userMeta.Username != data["username"].(string) {
            userMeta.Previous.Username = append(userMeta.Previous.Username, PreviousUsername{userMeta.Username, time.Now()})
            userMeta.Username = data["username"].(string)
        }

        if userMeta.Description != data["description"].(string) {
            userMeta.Previous.Description = append(userMeta.Previous.Description, PreviousDescription{userMeta.Description, time.Now()})
            userMeta.Description = data["description"].(string)
        }

        if userMeta.Background != data["background"].(string) {
            userMeta.Previous.Background = append(userMeta.Previous.Background, PreviousBackground{userMeta.Background, time.Now()})
            userMeta.Background = data["background"].(string)
        }

        userDataTemp, err := db.GetUserData(userId)
        userData = userDataTemp.(StoredUserData)

        if err != datastore.ErrNoSuchEntity {
            userData.LastUpdated = time.Now()
            userData.Followers = append(userData.Followers, data["followerCount"].(float64))
            userData.Following = append(userData.Following, data["followingCount"].(float64))
            userData.Loops = append(userData.Loops, data["loopCount"].(float64))
            userData.AuthoredPosts = append(userData.AuthoredPosts, data["authoredPostCount"].(float64))
            userData.Reposts = append(userData.Reposts, data["postCount"].(float64) - data["authoredPostCount"].(float64))
            userData.Likes = append(userData.Followers, data["likeCount"].(float64))
            userData.Updated = append(userData.Updated, time.Now())
        }
    }

    dataKey := datastore.NewKey(db.Context, "UserData", userId, 0, nil)
    metaKey := datastore.NewKey(db.Context, "UserMeta", userId, 0, nil)

    datastore.Put(db.Context, dataKey, &userData)
    datastore.Put(db.Context, metaKey, &userMeta)
}

func (db *DB) GetUserData(user string) (interface{}, error) {

    data := StoredUserData{}

    key := datastore.NewKey(db.Context, "UserData", user, 0, nil)
    err := datastore.Get(db.Context, key, &data)

    if err != nil {
        return nil, err
    } else {
        return data, nil
    }
}

func (db *DB) GetUserMeta(user string) (interface{}, error) {

    meta := StoredUserMeta{}

	key := datastore.NewKey(db.Context, "UserMeta", user, 0, nil)
    err := datastore.Get(db.Context, key, &meta)

    if err != nil {
        return nil, err
    } else {
        return meta, nil
    }
}<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"log"
	"strings"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/iam"
	"github.com/aws/aws-sdk-go/service/rds"

	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsRDSClusterParameterGroup() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsRDSClusterParameterGroupCreate,
		Read:   resourceAwsRDSClusterParameterGroupRead,
		Update: resourceAwsRDSClusterParameterGroupUpdate,
		Delete: resourceAwsRDSClusterParameterGroupDelete,
		Schema: map[string]*schema.Schema{
			"arn": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},
			"name": &schema.Schema{
				Type:         schema.TypeString,
				ForceNew:     true,
				Required:     true,
				ValidateFunc: validateDbParamGroupName,
			},
			"family": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"description": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"parameter": &schema.Schema{
				Type:     schema.TypeSet,
				Optional: true,
				ForceNew: false,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"name": &schema.Schema{
							Type:     schema.TypeString,
							Required: true,
						},
						"value": &schema.Schema{
							Type:     schema.TypeString,
							Required: true,
						},
						"apply_method": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
							Default:  "immediate",
							// this parameter is not actually state, but a
							// meta-parameter describing how the RDS API call
							// to modify the parameter group should be made.
							// Future reads of the resource from AWS don't tell
							// us what we used for apply_method previously, so
							// by squashing state to an empty string we avoid
							// needing to do an update for every future run.
							StateFunc: func(interface{}) string { return "" },
						},
					},
				},
				Set: resourceAwsDbParameterHash,
			},

			"tags": tagsSchema(),
		},
	}
}

func resourceAwsRDSClusterParameterGroupCreate(d *schema.ResourceData, meta interface{}) error {
	rdsconn := meta.(*AWSClient).rdsconn
	tags := tagsFromMapRDS(d.Get("tags").(map[string]interface{}))

	createOpts := rds.CreateDBClusterParameterGroupInput{
		DBClusterParameterGroupName: aws.String(d.Get("name").(string)),
		DBParameterGroupFamily:      aws.String(d.Get("family").(string)),
		Description:                 aws.String(d.Get("description").(string)),
		Tags:                        tags,
	}

	log.Printf("[DEBUG] Create DB Cluster Parameter Group: %#v", createOpts)
	_, err := rdsconn.CreateDBClusterParameterGroup(&createOpts)
	if err != nil {
		return fmt.Errorf("Error creating DB Cluster Parameter Group: %s", err)
	}

	d.Partial(true)
	d.SetPartial("name")
	d.SetPartial("family")
	d.SetPartial("description")
	d.Partial(false)

	d.SetId(*createOpts.DBClusterParameterGroupName)
	log.Printf("[INFO] DB Cluster Parameter Group ID: %s", d.Id())

	return resourceAwsRDSClusterParameterGroupUpdate(d, meta)
}

func resourceAwsRDSClusterParameterGroupRead(d *schema.ResourceData, meta interface{}) error {
	rdsconn := meta.(*AWSClient).rdsconn

	describeOpts := rds.DescribeDBClusterParameterGroupsInput{
		DBClusterParameterGroupName: aws.String(d.Id()),
	}

	describeResp, err := rdsconn.DescribeDBClusterParameterGroups(&describeOpts)
	if err != nil {
		return err
	}

	if len(describeResp.DBClusterParameterGroups) != 1 ||
		*describeResp.DBClusterParameterGroups[0].DBClusterParameterGroupName != d.Id() {
		return fmt.Errorf("Unable to find Cluster Parameter Group: %#v", describeResp.DBClusterParameterGroups)
	}

	d.Set("name", describeResp.DBClusterParameterGroups[0].DBClusterParameterGroupName)
	d.Set("family", describeResp.DBClusterParameterGroups[0].DBParameterGroupFamily)
	d.Set("description", describeResp.DBClusterParameterGroups[0].Description)

	// Only include user customized parameters as there's hundreds of system/default ones
	describeParametersOpts := rds.DescribeDBClusterParametersInput{
		DBClusterParameterGroupName: aws.String(d.Id()),
		Source: aws.String("user"),
	}

	describeParametersResp, err := rdsconn.DescribeDBClusterParameters(&describeParametersOpts)
	if err != nil {
		return err
	}

	d.Set("parameter", flattenParameters(describeParametersResp.Parameters))

	paramGroup := describeResp.DBClusterParameterGroups[0]
	arn, err := buildRDSCPGARN(d, meta)
	if err != nil {
		name := "<empty>"
		if paramGroup.DBClusterParameterGroupName != nil && *paramGroup.DBClusterParameterGroupName != "" {
			name = *paramGroup.DBClusterParameterGroupName
		}
		log.Printf("[DEBUG] Error building ARN for DB Cluster Parameter Group, not setting Tags for Cluster Param Group %s", name)
	} else {
		d.Set("arn", arn)
		resp, err := rdsconn.ListTagsForResource(&rds.ListTagsForResourceInput{
			ResourceName: aws.String(arn),
		})

		if err != nil {
			log.Printf("[DEBUG] Error retrieving tags for ARN: %s", arn)
		}

		var dt []*rds.Tag
		if len(resp.TagList) > 0 {
			dt = resp.TagList
		}
		d.Set("tags", tagsToMapRDS(dt))
	}

	return nil
}

func resourceAwsRDSClusterParameterGroupUpdate(d *schema.ResourceData, meta interface{}) error {
	rdsconn := meta.(*AWSClient).rdsconn

	d.Partial(true)

	if d.HasChange("parameter") {
		o, n := d.GetChange("parameter")
		if o == nil {
			o = new(schema.Set)
		}
		if n == nil {
			n = new(schema.Set)
		}

		os := o.(*schema.Set)
		ns := n.(*schema.Set)

		// Expand the "parameter" set to aws-sdk-go compat []rds.Parameter
		parameters, err := expandParameters(ns.Difference(os).List())
		if err != nil {
			return err
		}

		if len(parameters) > 0 {
			modifyOpts := rds.ModifyDBClusterParameterGroupInput{
				DBClusterParameterGroupName: aws.String(d.Get("name").(string)),
				Parameters:                  parameters,
			}

			log.Printf("[DEBUG] Modify DB Cluster Parameter Group: %s", modifyOpts)
			_, err = rdsconn.ModifyDBClusterParameterGroup(&modifyOpts)
			if err != nil {
				return fmt.Errorf("Error modifying DB Cluster Parameter Group: %s", err)
			}
		}
		d.SetPartial("parameter")
	}

	if arn, err := buildRDSCPGARN(d, meta); err == nil {
		if err := setTagsRDS(rdsconn, d, arn); err != nil {
			return err
		} else {
			d.SetPartial("tags")
		}
	}

	d.Partial(false)

	return resourceAwsRDSClusterParameterGroupRead(d, meta)
}

func resourceAwsRDSClusterParameterGroupDelete(d *schema.ResourceData, meta interface{}) error {
	stateConf := &resource.StateChangeConf{
		Pending:    []string{"pending"},
		Target:     []string{"destroyed"},
		Refresh:    resourceAwsRDSClusterParameterGroupDeleteRefreshFunc(d, meta),
		Timeout:    3 * time.Minute,
		MinTimeout: 1 * time.Second,
	}
	_, err := stateConf.WaitForState()
	return err
}

func resourceAwsRDSClusterParameterGroupDeleteRefreshFunc(
	d *schema.ResourceData,
	meta interface{}) resource.StateRefreshFunc {
	rdsconn := meta.(*AWSClient).rdsconn

	return func() (interface{}, string, error) {

		deleteOpts := rds.DeleteDBClusterParameterGroupInput{
			DBClusterParameterGroupName: aws.String(d.Id()),
		}

		if _, err := rdsconn.DeleteDBClusterParameterGroup(&deleteOpts); err != nil {
			rdserr, ok := err.(awserr.Error)
			if !ok {
				return d, "error", err
			}

			if rdserr.Code() != "DBParameterGroupNotFound" {
				return d, "error", err
			}
		}

		return d, "destroyed", nil
	}
}

func buildRDSCPGARN(d *schema.ResourceData, meta interface{}) (string, error) {
	iamconn := meta.(*AWSClient).iamconn
	region := meta.(*AWSClient).region
	// An zero value GetUserInput{} defers to the currently logged in user
	resp, err := iamconn.GetUser(&iam.GetUserInput{})
	if err != nil {
		return "", err
	}
	userARN := *resp.User.Arn
	accountID := strings.Split(userARN, ":")[4]
	arn := fmt.Sprintf("arn:aws:rds:%s:%s:cluster-pg:%s", region, accountID, d.Id())
	return arn, nil
}
<commit_msg>remove partial mode handling<commit_after>package aws

import (
	"fmt"
	"log"
	"strings"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/iam"
	"github.com/aws/aws-sdk-go/service/rds"

	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsRDSClusterParameterGroup() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsRDSClusterParameterGroupCreate,
		Read:   resourceAwsRDSClusterParameterGroupRead,
		Update: resourceAwsRDSClusterParameterGroupUpdate,
		Delete: resourceAwsRDSClusterParameterGroupDelete,
		Schema: map[string]*schema.Schema{
			"arn": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},
			"name": &schema.Schema{
				Type:         schema.TypeString,
				ForceNew:     true,
				Required:     true,
				ValidateFunc: validateDbParamGroupName,
			},
			"family": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"description": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"parameter": &schema.Schema{
				Type:     schema.TypeSet,
				Optional: true,
				ForceNew: false,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"name": &schema.Schema{
							Type:     schema.TypeString,
							Required: true,
						},
						"value": &schema.Schema{
							Type:     schema.TypeString,
							Required: true,
						},
						"apply_method": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
							Default:  "immediate",
							// this parameter is not actually state, but a
							// meta-parameter describing how the RDS API call
							// to modify the parameter group should be made.
							// Future reads of the resource from AWS don't tell
							// us what we used for apply_method previously, so
							// by squashing state to an empty string we avoid
							// needing to do an update for every future run.
							StateFunc: func(interface{}) string { return "" },
						},
					},
				},
				Set: resourceAwsDbParameterHash,
			},

			"tags": tagsSchema(),
		},
	}
}

func resourceAwsRDSClusterParameterGroupCreate(d *schema.ResourceData, meta interface{}) error {
	rdsconn := meta.(*AWSClient).rdsconn
	tags := tagsFromMapRDS(d.Get("tags").(map[string]interface{}))

	createOpts := rds.CreateDBClusterParameterGroupInput{
		DBClusterParameterGroupName: aws.String(d.Get("name").(string)),
		DBParameterGroupFamily:      aws.String(d.Get("family").(string)),
		Description:                 aws.String(d.Get("description").(string)),
		Tags:                        tags,
	}

	log.Printf("[DEBUG] Create DB Cluster Parameter Group: %#v", createOpts)
	_, err := rdsconn.CreateDBClusterParameterGroup(&createOpts)
	if err != nil {
		return fmt.Errorf("Error creating DB Cluster Parameter Group: %s", err)
	}

	d.SetId(*createOpts.DBClusterParameterGroupName)
	log.Printf("[INFO] DB Cluster Parameter Group ID: %s", d.Id())

	return resourceAwsRDSClusterParameterGroupUpdate(d, meta)
}

func resourceAwsRDSClusterParameterGroupRead(d *schema.ResourceData, meta interface{}) error {
	rdsconn := meta.(*AWSClient).rdsconn

	describeOpts := rds.DescribeDBClusterParameterGroupsInput{
		DBClusterParameterGroupName: aws.String(d.Id()),
	}

	describeResp, err := rdsconn.DescribeDBClusterParameterGroups(&describeOpts)
	if err != nil {
		return err
	}

	if len(describeResp.DBClusterParameterGroups) != 1 ||
		*describeResp.DBClusterParameterGroups[0].DBClusterParameterGroupName != d.Id() {
		return fmt.Errorf("Unable to find Cluster Parameter Group: %#v", describeResp.DBClusterParameterGroups)
	}

	d.Set("name", describeResp.DBClusterParameterGroups[0].DBClusterParameterGroupName)
	d.Set("family", describeResp.DBClusterParameterGroups[0].DBParameterGroupFamily)
	d.Set("description", describeResp.DBClusterParameterGroups[0].Description)

	// Only include user customized parameters as there's hundreds of system/default ones
	describeParametersOpts := rds.DescribeDBClusterParametersInput{
		DBClusterParameterGroupName: aws.String(d.Id()),
		Source: aws.String("user"),
	}

	describeParametersResp, err := rdsconn.DescribeDBClusterParameters(&describeParametersOpts)
	if err != nil {
		return err
	}

	d.Set("parameter", flattenParameters(describeParametersResp.Parameters))

	paramGroup := describeResp.DBClusterParameterGroups[0]
	arn, err := buildRDSCPGARN(d, meta)
	if err != nil {
		name := "<empty>"
		if paramGroup.DBClusterParameterGroupName != nil && *paramGroup.DBClusterParameterGroupName != "" {
			name = *paramGroup.DBClusterParameterGroupName
		}
		log.Printf("[DEBUG] Error building ARN for DB Cluster Parameter Group, not setting Tags for Cluster Param Group %s", name)
	} else {
		d.Set("arn", arn)
		resp, err := rdsconn.ListTagsForResource(&rds.ListTagsForResourceInput{
			ResourceName: aws.String(arn),
		})

		if err != nil {
			log.Printf("[DEBUG] Error retrieving tags for ARN: %s", arn)
		}

		var dt []*rds.Tag
		if len(resp.TagList) > 0 {
			dt = resp.TagList
		}
		d.Set("tags", tagsToMapRDS(dt))
	}

	return nil
}

func resourceAwsRDSClusterParameterGroupUpdate(d *schema.ResourceData, meta interface{}) error {
	rdsconn := meta.(*AWSClient).rdsconn

	d.Partial(true)

	if d.HasChange("parameter") {
		o, n := d.GetChange("parameter")
		if o == nil {
			o = new(schema.Set)
		}
		if n == nil {
			n = new(schema.Set)
		}

		os := o.(*schema.Set)
		ns := n.(*schema.Set)

		// Expand the "parameter" set to aws-sdk-go compat []rds.Parameter
		parameters, err := expandParameters(ns.Difference(os).List())
		if err != nil {
			return err
		}

		if len(parameters) > 0 {
			modifyOpts := rds.ModifyDBClusterParameterGroupInput{
				DBClusterParameterGroupName: aws.String(d.Get("name").(string)),
				Parameters:                  parameters,
			}

			log.Printf("[DEBUG] Modify DB Cluster Parameter Group: %s", modifyOpts)
			_, err = rdsconn.ModifyDBClusterParameterGroup(&modifyOpts)
			if err != nil {
				return fmt.Errorf("Error modifying DB Cluster Parameter Group: %s", err)
			}
		}
		d.SetPartial("parameter")
	}

	if arn, err := buildRDSCPGARN(d, meta); err == nil {
		if err := setTagsRDS(rdsconn, d, arn); err != nil {
			return err
		} else {
			d.SetPartial("tags")
		}
	}

	d.Partial(false)

	return resourceAwsRDSClusterParameterGroupRead(d, meta)
}

func resourceAwsRDSClusterParameterGroupDelete(d *schema.ResourceData, meta interface{}) error {
	stateConf := &resource.StateChangeConf{
		Pending:    []string{"pending"},
		Target:     []string{"destroyed"},
		Refresh:    resourceAwsRDSClusterParameterGroupDeleteRefreshFunc(d, meta),
		Timeout:    3 * time.Minute,
		MinTimeout: 1 * time.Second,
	}
	_, err := stateConf.WaitForState()
	return err
}

func resourceAwsRDSClusterParameterGroupDeleteRefreshFunc(
	d *schema.ResourceData,
	meta interface{}) resource.StateRefreshFunc {
	rdsconn := meta.(*AWSClient).rdsconn

	return func() (interface{}, string, error) {

		deleteOpts := rds.DeleteDBClusterParameterGroupInput{
			DBClusterParameterGroupName: aws.String(d.Id()),
		}

		if _, err := rdsconn.DeleteDBClusterParameterGroup(&deleteOpts); err != nil {
			rdserr, ok := err.(awserr.Error)
			if !ok {
				return d, "error", err
			}

			if rdserr.Code() != "DBParameterGroupNotFound" {
				return d, "error", err
			}
		}

		return d, "destroyed", nil
	}
}

func buildRDSCPGARN(d *schema.ResourceData, meta interface{}) (string, error) {
	iamconn := meta.(*AWSClient).iamconn
	region := meta.(*AWSClient).region
	// An zero value GetUserInput{} defers to the currently logged in user
	resp, err := iamconn.GetUser(&iam.GetUserInput{})
	if err != nil {
		return "", err
	}
	userARN := *resp.User.Arn
	accountID := strings.Split(userARN, ":")[4]
	arn := fmt.Sprintf("arn:aws:rds:%s:%s:cluster-pg:%s", region, accountID, d.Id())
	return arn, nil
}
<|endoftext|>


<commit_before>// Copyright 2013 Andreas Koch. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package pattern

import (
	"regexp"
)

var (
	// Lines which contain nothing but white space characters
	// or no characters at all.
	EmptyLinePattern = regexp.MustCompile(`^\s*$`)

	// Lines which a start with a hash, followed by zero or more
	// white space characters, followed by text.
	TitlePattern = regexp.MustCompile(`^#\s*([\pL\pN\p{Latin}]+.+)`)

	// Lines which start with text
	DescriptionPattern = regexp.MustCompile(`^[\pL\pN\p{Latin}]+.+`)

	// Lines which nothing but dashes
	HorizontalRulePattern = regexp.MustCompile(`^-{3,}$`)

	// Lines with a "key: value" syntax
	SingleLineMetaDataPattern = regexp.MustCompile(`^(\w+[\w\s]+\w+):\s*([\pL\pN\p{Latin}]+.+)$`)

	// Lines with a meta data label in them syntax
	MultiLineTagsPattern = regexp.MustCompile(`(?is)tags:\n{1,2}(\s?-\s?.+\n)+\n`)

	// Lines with a meta data label in them syntax
	MetaDataLabelPattern = regexp.MustCompile(`^(\w+[\w\s]+\w+):`)

	// Meta data list item pattern
	MetaDataListItemPattern = regexp.MustCompile(`^\s?[*-]\s?(.+)$`)

	// Pattern which matches all HTML/XML tags
	HtmlTagPattern = regexp.MustCompile(`\<[^\>]*\>`)
)
<commit_msg>Fixed the multiline meta data tag pattern<commit_after>// Copyright 2013 Andreas Koch. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package pattern

import (
	"regexp"
)

var (
	// Lines which contain nothing but white space characters
	// or no characters at all.
	EmptyLinePattern = regexp.MustCompile(`^\s*$`)

	// Lines which a start with a hash, followed by zero or more
	// white space characters, followed by text.
	TitlePattern = regexp.MustCompile(`^#\s*([\pL\pN\p{Latin}]+.+)`)

	// Lines which start with text
	DescriptionPattern = regexp.MustCompile(`^[\pL\pN\p{Latin}]+.+`)

	// Lines which nothing but dashes
	HorizontalRulePattern = regexp.MustCompile(`^-{3,}$`)

	// Lines with a "key: value" syntax
	SingleLineMetaDataPattern = regexp.MustCompile(`^(\w+[\w\s]+\w+):\s*([\pL\pN\p{Latin}]+.+)$`)

	// Lines with a meta data label in them syntax
	MultiLineTagsPattern = regexp.MustCompile(`(?is)tags:\n{1,2}(\s?-\s?.+\n)+`)

	// Lines with a meta data label in them syntax
	MetaDataLabelPattern = regexp.MustCompile(`^(\w+[\w\s]+\w+):`)

	// Meta data list item pattern
	MetaDataListItemPattern = regexp.MustCompile(`^\s?[*-]\s?(.+)$`)

	// Pattern which matches all HTML/XML tags
	HtmlTagPattern = regexp.MustCompile(`\<[^\>]*\>`)
)
<|endoftext|>


<commit_before>package main

import (
	"appengine"
	"appengine/urlfetch"
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"
	"regexp"
)

type VineRequest struct {
	Context appengine.Context
}

var (
	ErrUserDoesntExist = errors.New("That record doesn't exist.")
)

const (
	VINE_API = "https://api.vineapp.com"
)

func (v *VineRequest) get(url string) (*VineUser, error) {
	if v.Context == nil {
		return nil, errors.New("Google AppEngine Context Required")
	} else {
		c := v.Context
		client := urlfetch.Client(c)
		req, _ := http.NewRequest("GET", VINE_API+url, nil)
		req.Header.Set("x-vine-client", "vinewww/1.0")
		resp, err := client.Do(req)
		if err == nil {
			jsonData, _ := ioutil.ReadAll(resp.Body)
			data := new(VineUserWrapper)
			err = json.Unmarshal(jsonData, &data)
			if data.Success {
				return data.Data, nil
			} else {
				return nil, errors.New(data.Error)
			}
		} else {
			return nil, err
		}
	}
}

func (v *VineRequest) GetUser(userId string) (*VineUser, error) {
	url := "/users/profiles/"
	match, _ := regexp.MatchString("^[0-9]+$", userId)

	if match {
		url += userId
	} else {
		url += "vanity/" + userId
	}

	data, err := v.get(url)
	if err != nil {
		return nil, err
	} else {
		return data, nil
	}
}
<commit_msg>Updated Vine user doesn't exist message.<commit_after>package main

import (
	"appengine"
	"appengine/urlfetch"
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"
	"regexp"
)

type VineRequest struct {
	Context appengine.Context
}

var (
	ErrUserDoesntExist = errors.New("That record does not exist.")
)

const (
	VINE_API = "https://api.vineapp.com"
)

func (v *VineRequest) get(url string) (*VineUser, error) {
	if v.Context == nil {
		return nil, errors.New("Google AppEngine Context Required")
	} else {
		c := v.Context
		client := urlfetch.Client(c)
		req, _ := http.NewRequest("GET", VINE_API+url, nil)
		req.Header.Set("x-vine-client", "vinewww/1.0")
		resp, err := client.Do(req)
		if err == nil {
			jsonData, _ := ioutil.ReadAll(resp.Body)
			data := new(VineUserWrapper)
			err = json.Unmarshal(jsonData, &data)
			if data.Success {
				return data.Data, nil
			} else {
				return nil, errors.New(data.Error)
			}
		} else {
			return nil, err
		}
	}
}

func (v *VineRequest) GetUser(userId string) (*VineUser, error) {
	url := "/users/profiles/"
	match, _ := regexp.MatchString("^[0-9]+$", userId)

	if match {
		url += userId
	} else {
		url += "vanity/" + userId
	}

	data, err := v.get(url)
	if err != nil {
		return nil, err
	} else {
		return data, nil
	}
}
<|endoftext|>