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

// Binary client for xDS interop tests.
package main

import (
	"context"
	"flag"
	"fmt"
	"net"
	"sync"
	"time"

	"google.golang.org/grpc"
	"google.golang.org/grpc/grpclog"
	testpb "google.golang.org/grpc/interop/grpc_testing"
	"google.golang.org/grpc/peer"
	_ "google.golang.org/grpc/xds/experimental"
)

type statsWatcherKey struct {
	startID int32
	endID   int32
}

type statsWatcher struct {
	rpcsByPeer    map[string]int32
	numFailures   int32
	remainingRpcs int32
	c             chan *testpb.SimpleResponse
}

var (
	failOnFailedRPC = flag.Bool("fail_on_failed_rpc", false, "Fail client if any RPCs fail")
	numChannels     = flag.Int("num_channels", 1, "Num of channels")
	printResponse   = flag.Bool("print_response", false, "Write RPC response to stdout")
	qps             = flag.Int("qps", 1, "QPS per channel")
	rpcTimeout      = flag.Duration("rpc_timeout", 10*time.Second, "Per RPC timeout")
	server          = flag.String("server", "localhost:8080", "Address of server to connect to")
	statsPort       = flag.Int("stats_port", 8081, "Port to expose peer distribution stats service")

	mu               sync.Mutex
	currentRequestID int32
	watchers         = make(map[statsWatcherKey]*statsWatcher)
)

type statsService struct{}

// Wait for the next LoadBalancerStatsRequest.GetNumRpcs to start and complete,
// and return the distribution of remote peers. This is essentially a clientside
// LB reporting mechanism that is designed to be queried by an external test
// driver when verifying that the client is distributing RPCs as expected.
func (s *statsService) GetClientStats(ctx context.Context, in *testpb.LoadBalancerStatsRequest) (*testpb.LoadBalancerStatsResponse, error) {
	mu.Lock()
	watcherKey := statsWatcherKey{currentRequestID, currentRequestID + in.GetNumRpcs()}
	watcher, ok := watchers[watcherKey]
	if !ok {
		watcher = &statsWatcher{
			rpcsByPeer:    make(map[string]int32),
			numFailures:   0,
			remainingRpcs: in.GetNumRpcs(),
			c:             make(chan *testpb.SimpleResponse),
		}
		watchers[watcherKey] = watcher
	}
	mu.Unlock()

	ctx, cancel := context.WithTimeout(ctx, time.Duration(in.GetTimeoutSec())*time.Second)
	defer cancel()

	defer func() {
		mu.Lock()
		delete(watchers, watcherKey)
		mu.Unlock()
	}()

	// Wait until the requested RPCs have all been recorded or timeout occurs.
	for {
		select {
		case r := <-watcher.c:
			if r != nil {
				watcher.rpcsByPeer[(*r).GetHostname()]++
			} else {
				watcher.numFailures++
			}
			watcher.remainingRpcs--
			if watcher.remainingRpcs == 0 {
				return &testpb.LoadBalancerStatsResponse{NumFailures: watcher.numFailures + watcher.remainingRpcs, RpcsByPeer: watcher.rpcsByPeer}, nil
			}
		case <-ctx.Done():
			grpclog.Info("Timed out, returning partial stats")
			return &testpb.LoadBalancerStatsResponse{NumFailures: watcher.numFailures + watcher.remainingRpcs, RpcsByPeer: watcher.rpcsByPeer}, nil
		}
	}
}

func main() {
	flag.Parse()

	lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *statsPort))
	if err != nil {
		grpclog.Fatalf("failed to listen: %v", err)
	}
	s := grpc.NewServer()
	defer s.Stop()
	testpb.RegisterLoadBalancerStatsServiceServer(s, &statsService{})
	go s.Serve(lis)

	clients := make([]testpb.TestServiceClient, *numChannels)
	for i := 0; i < *numChannels; i++ {
		conn, err := grpc.DialContext(context.Background(), *server, grpc.WithInsecure())
		if err != nil {
			grpclog.Fatalf("Fail to dial: %v", err)
		}
		defer conn.Close()
		clients[i] = testpb.NewTestServiceClient(conn)
	}
	ticker := time.NewTicker(time.Second / time.Duration(*qps**numChannels))
	defer ticker.Stop()
	sendRPCs(clients, ticker)
}

func sendRPCs(clients []testpb.TestServiceClient, ticker *time.Ticker) {
	var i int
	for range ticker.C {
		go func(i int) {
			c := clients[i]
			ctx, cancel := context.WithTimeout(context.Background(), *rpcTimeout)
			p := new(peer.Peer)
			mu.Lock()
			savedRequestID := currentRequestID
			currentRequestID++
			savedWatchers := []*statsWatcher{}
			for key, value := range watchers {
				if key.startID <= savedRequestID && savedRequestID < key.endID {
					savedWatchers = append(savedWatchers, value)
				}
			}
			mu.Unlock()
			r, err := c.UnaryCall(ctx, &testpb.SimpleRequest{FillServerId: true}, grpc.Peer(p))

			success := err == nil
			cancel()

			for _, watcher := range savedWatchers {
				watcher.c <- r
			}

			if err != nil && *failOnFailedRPC {
				grpclog.Fatalf("RPC failed: %v", err)
			}
			if success && *printResponse {
				fmt.Printf("Greeting: Hello world, this is %s, from %v\n", r.GetHostname(), p.Addr)
			}
		}(i)
		i = (i + 1) % len(clients)
	}
}
<commit_msg>interop: increase xds test client rpc timeout (#3579)<commit_after>/*
 *
 * Copyright 2020 gRPC authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

// Binary client for xDS interop tests.
package main

import (
	"context"
	"flag"
	"fmt"
	"net"
	"sync"
	"time"

	"google.golang.org/grpc"
	"google.golang.org/grpc/grpclog"
	testpb "google.golang.org/grpc/interop/grpc_testing"
	"google.golang.org/grpc/peer"
	_ "google.golang.org/grpc/xds/experimental"
)

type statsWatcherKey struct {
	startID int32
	endID   int32
}

type statsWatcher struct {
	rpcsByPeer    map[string]int32
	numFailures   int32
	remainingRpcs int32
	c             chan *testpb.SimpleResponse
}

var (
	failOnFailedRPC = flag.Bool("fail_on_failed_rpc", false, "Fail client if any RPCs fail")
	numChannels     = flag.Int("num_channels", 1, "Num of channels")
	printResponse   = flag.Bool("print_response", false, "Write RPC response to stdout")
	qps             = flag.Int("qps", 1, "QPS per channel")
	rpcTimeout      = flag.Duration("rpc_timeout", 20*time.Second, "Per RPC timeout")
	server          = flag.String("server", "localhost:8080", "Address of server to connect to")
	statsPort       = flag.Int("stats_port", 8081, "Port to expose peer distribution stats service")

	mu               sync.Mutex
	currentRequestID int32
	watchers         = make(map[statsWatcherKey]*statsWatcher)
)

type statsService struct{}

// Wait for the next LoadBalancerStatsRequest.GetNumRpcs to start and complete,
// and return the distribution of remote peers. This is essentially a clientside
// LB reporting mechanism that is designed to be queried by an external test
// driver when verifying that the client is distributing RPCs as expected.
func (s *statsService) GetClientStats(ctx context.Context, in *testpb.LoadBalancerStatsRequest) (*testpb.LoadBalancerStatsResponse, error) {
	mu.Lock()
	watcherKey := statsWatcherKey{currentRequestID, currentRequestID + in.GetNumRpcs()}
	watcher, ok := watchers[watcherKey]
	if !ok {
		watcher = &statsWatcher{
			rpcsByPeer:    make(map[string]int32),
			numFailures:   0,
			remainingRpcs: in.GetNumRpcs(),
			c:             make(chan *testpb.SimpleResponse),
		}
		watchers[watcherKey] = watcher
	}
	mu.Unlock()

	ctx, cancel := context.WithTimeout(ctx, time.Duration(in.GetTimeoutSec())*time.Second)
	defer cancel()

	defer func() {
		mu.Lock()
		delete(watchers, watcherKey)
		mu.Unlock()
	}()

	// Wait until the requested RPCs have all been recorded or timeout occurs.
	for {
		select {
		case r := <-watcher.c:
			if r != nil {
				watcher.rpcsByPeer[(*r).GetHostname()]++
			} else {
				watcher.numFailures++
			}
			watcher.remainingRpcs--
			if watcher.remainingRpcs == 0 {
				return &testpb.LoadBalancerStatsResponse{NumFailures: watcher.numFailures + watcher.remainingRpcs, RpcsByPeer: watcher.rpcsByPeer}, nil
			}
		case <-ctx.Done():
			grpclog.Info("Timed out, returning partial stats")
			return &testpb.LoadBalancerStatsResponse{NumFailures: watcher.numFailures + watcher.remainingRpcs, RpcsByPeer: watcher.rpcsByPeer}, nil
		}
	}
}

func main() {
	flag.Parse()

	lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *statsPort))
	if err != nil {
		grpclog.Fatalf("failed to listen: %v", err)
	}
	s := grpc.NewServer()
	defer s.Stop()
	testpb.RegisterLoadBalancerStatsServiceServer(s, &statsService{})
	go s.Serve(lis)

	clients := make([]testpb.TestServiceClient, *numChannels)
	for i := 0; i < *numChannels; i++ {
		conn, err := grpc.DialContext(context.Background(), *server, grpc.WithInsecure())
		if err != nil {
			grpclog.Fatalf("Fail to dial: %v", err)
		}
		defer conn.Close()
		clients[i] = testpb.NewTestServiceClient(conn)
	}
	ticker := time.NewTicker(time.Second / time.Duration(*qps**numChannels))
	defer ticker.Stop()
	sendRPCs(clients, ticker)
}

func sendRPCs(clients []testpb.TestServiceClient, ticker *time.Ticker) {
	var i int
	for range ticker.C {
		go func(i int) {
			c := clients[i]
			ctx, cancel := context.WithTimeout(context.Background(), *rpcTimeout)
			p := new(peer.Peer)
			mu.Lock()
			savedRequestID := currentRequestID
			currentRequestID++
			savedWatchers := []*statsWatcher{}
			for key, value := range watchers {
				if key.startID <= savedRequestID && savedRequestID < key.endID {
					savedWatchers = append(savedWatchers, value)
				}
			}
			mu.Unlock()
			r, err := c.UnaryCall(ctx, &testpb.SimpleRequest{FillServerId: true}, grpc.Peer(p))

			success := err == nil
			cancel()

			for _, watcher := range savedWatchers {
				watcher.c <- r
			}

			if err != nil && *failOnFailedRPC {
				grpclog.Fatalf("RPC failed: %v", err)
			}
			if success && *printResponse {
				fmt.Printf("Greeting: Hello world, this is %s, from %v\n", r.GetHostname(), p.Addr)
			}
		}(i)
		i = (i + 1) % len(clients)
	}
}
<|endoftext|>


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

package gitlab

import (
	"fmt"
	"time"
)

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

// PipelineVariable represents a pipeline variable.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/pipelines.html
type PipelineVariable struct {
	Key   string `json:"key"`
	Value string `json:"value"`
}

// Pipeline represents a GitLab pipeline.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/pipelines.html
type Pipeline struct {
	ID         int    `json:"id"`
	Status     string `json:"status"`
	Ref        string `json:"ref"`
	SHA        string `json:"sha"`
	BeforeSHA  string `json:"before_sha"`
	Tag        bool   `json:"tag"`
	YamlErrors string `json:"yaml_errors"`
	User       struct {
		Name      string `json:"name"`
		Username  string `json:"username"`
		ID        int    `json:"id"`
		State     string `json:"state"`
		AvatarURL string `json:"avatar_url"`
		WebURL    string `json:"web_url"`
	}
	UpdatedAt      *time.Time      `json:"updated_at"`
	CreatedAt      *time.Time      `json:"created_at"`
	StartedAt      *time.Time      `json:"started_at"`
	FinishedAt     *time.Time      `json:"finished_at"`
	CommittedAt    *time.Time      `json:"committed_at"`
	Duration       int             `json:"duration"`
	Coverage       string          `json:"coverage"`
	WebURL         string          `json:"web_url"`
	DetailedStatus *DetailedStatus `json:"detailed_status"`
}

// DetailedStatus contains detailed information about the status of a pipeline
type DetailedStatus struct {
	Icon         string `json:"icon"`
	Text         string `json:"text"`
	Label        string `json:"label"`
	Group        string `json:"group"`
	Tooltip      string `json:"tooltip"`
	HasDetails   bool   `json:"has_details"`
	DetailsPath  string `json:"details_path"`
	Illustration struct {
		Image string `json:"image"`
	} `json:"illustration"`
	Favicon string `json:"favicon"`
}

func (i Pipeline) String() string {
	return Stringify(i)
}

// PipelineList represents a GitLab list project pipelines
//
// GitLab API docs: https://docs.gitlab.com/ce/api/pipelines.html#list-project-pipelines
type PipelineList []*PipelineInfo

// PipelineInfo shows the basic entities of a pipeline, mostly used as fields
// on other assets, like Commit.
type PipelineInfo struct {
	ID     int    `json:"id"`
	Status string `json:"status"`
	Ref    string `json:"ref"`
	SHA    string `json:"sha"`
	WebURL string `json:"web_url"`
}

func (i PipelineList) String() string {
	return Stringify(i)
}

// ListProjectPipelinesOptions represents the available ListProjectPipelines() options.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/pipelines.html#list-project-pipelines
type ListProjectPipelinesOptions struct {
	ListOptions
	Scope      *string          `url:"scope,omitempty" json:"scope,omitempty"`
	Status     *BuildStateValue `url:"status,omitempty" json:"status,omitempty"`
	Ref        *string          `url:"ref,omitempty" json:"ref,omitempty"`
	SHA        *string          `url:"sha,omitempty" json:"sha,omitempty"`
	YamlErrors *bool            `url:"yaml_errors,omitempty" json:"yaml_errors,omitempty"`
	Name       *string          `url:"name,omitempty" json:"name,omitempty"`
	Username   *string          `url:"username,omitempty" json:"username,omitempty"`
	OrderBy    *string          `url:"order_by,omitempty" json:"order_by,omitempty"`
	Sort       *string          `url:"sort,omitempty" json:"sort,omitempty"`
}

// ListProjectPipelines gets a list of project piplines.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/pipelines.html#list-project-pipelines
func (s *PipelinesService) ListProjectPipelines(pid interface{}, opt *ListProjectPipelinesOptions, options ...OptionFunc) (PipelineList, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/pipelines", pathEscape(project))

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

	var p PipelineList
	resp, err := s.client.Do(req, &p)
	if err != nil {
		return nil, resp, err
	}
	return p, resp, err
}

// GetPipeline gets a single project pipeline.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/pipelines.html#get-a-single-pipeline
func (s *PipelinesService) GetPipeline(pid interface{}, pipeline int, options ...OptionFunc) (*Pipeline, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/pipelines/%d", pathEscape(project), pipeline)

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

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

	return p, resp, err
}

// CreatePipelineOptions represents the available CreatePipeline() options.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/pipelines.html#create-a-new-pipeline
type CreatePipelineOptions struct {
	Ref       *string             `url:"ref" json:"ref"`
	Variables []*PipelineVariable `url:"variables,omitempty" json:"variables,omitempty"`
}

// CreatePipeline creates a new project pipeline.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/pipelines.html#create-a-new-pipeline
func (s *PipelinesService) CreatePipeline(pid interface{}, opt *CreatePipelineOptions, options ...OptionFunc) (*Pipeline, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/pipeline", pathEscape(project))

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

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

	return p, resp, err
}

// RetryPipelineBuild retries failed builds in a pipeline
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/pipelines.html#retry-failed-builds-in-a-pipeline
func (s *PipelinesService) RetryPipelineBuild(pid interface{}, pipeline int, options ...OptionFunc) (*Pipeline, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/pipelines/%d/retry", project, pipeline)

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

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

	return p, resp, err
}

// CancelPipelineBuild cancels a pipeline builds
//
// GitLab API docs:
//https://docs.gitlab.com/ce/api/pipelines.html#cancel-a-pipelines-builds
func (s *PipelinesService) CancelPipelineBuild(pid interface{}, pipeline int, options ...OptionFunc) (*Pipeline, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/pipelines/%d/cancel", project, pipeline)

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

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

	return p, resp, err
}

// DeletePipeline deletes an existing pipeline.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/pipelines.html#delete-a-pipeline
func (s *PipelinesService) DeletePipeline(pid interface{}, pipeline int, options ...OptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("projects/%s/pipelines/%d", project, pipeline)

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

	return s.client.Do(req, nil)
}
<commit_msg>Path escape project name on missing Pipelines API methods<commit_after>//
// Copyright 2017, Igor Varavko
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"time"
)

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

// PipelineVariable represents a pipeline variable.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/pipelines.html
type PipelineVariable struct {
	Key   string `json:"key"`
	Value string `json:"value"`
}

// Pipeline represents a GitLab pipeline.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/pipelines.html
type Pipeline struct {
	ID         int    `json:"id"`
	Status     string `json:"status"`
	Ref        string `json:"ref"`
	SHA        string `json:"sha"`
	BeforeSHA  string `json:"before_sha"`
	Tag        bool   `json:"tag"`
	YamlErrors string `json:"yaml_errors"`
	User       struct {
		Name      string `json:"name"`
		Username  string `json:"username"`
		ID        int    `json:"id"`
		State     string `json:"state"`
		AvatarURL string `json:"avatar_url"`
		WebURL    string `json:"web_url"`
	}
	UpdatedAt      *time.Time      `json:"updated_at"`
	CreatedAt      *time.Time      `json:"created_at"`
	StartedAt      *time.Time      `json:"started_at"`
	FinishedAt     *time.Time      `json:"finished_at"`
	CommittedAt    *time.Time      `json:"committed_at"`
	Duration       int             `json:"duration"`
	Coverage       string          `json:"coverage"`
	WebURL         string          `json:"web_url"`
	DetailedStatus *DetailedStatus `json:"detailed_status"`
}

// DetailedStatus contains detailed information about the status of a pipeline
type DetailedStatus struct {
	Icon         string `json:"icon"`
	Text         string `json:"text"`
	Label        string `json:"label"`
	Group        string `json:"group"`
	Tooltip      string `json:"tooltip"`
	HasDetails   bool   `json:"has_details"`
	DetailsPath  string `json:"details_path"`
	Illustration struct {
		Image string `json:"image"`
	} `json:"illustration"`
	Favicon string `json:"favicon"`
}

func (i Pipeline) String() string {
	return Stringify(i)
}

// PipelineList represents a GitLab list project pipelines
//
// GitLab API docs: https://docs.gitlab.com/ce/api/pipelines.html#list-project-pipelines
type PipelineList []*PipelineInfo

// PipelineInfo shows the basic entities of a pipeline, mostly used as fields
// on other assets, like Commit.
type PipelineInfo struct {
	ID     int    `json:"id"`
	Status string `json:"status"`
	Ref    string `json:"ref"`
	SHA    string `json:"sha"`
	WebURL string `json:"web_url"`
}

func (i PipelineList) String() string {
	return Stringify(i)
}

// ListProjectPipelinesOptions represents the available ListProjectPipelines() options.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/pipelines.html#list-project-pipelines
type ListProjectPipelinesOptions struct {
	ListOptions
	Scope      *string          `url:"scope,omitempty" json:"scope,omitempty"`
	Status     *BuildStateValue `url:"status,omitempty" json:"status,omitempty"`
	Ref        *string          `url:"ref,omitempty" json:"ref,omitempty"`
	SHA        *string          `url:"sha,omitempty" json:"sha,omitempty"`
	YamlErrors *bool            `url:"yaml_errors,omitempty" json:"yaml_errors,omitempty"`
	Name       *string          `url:"name,omitempty" json:"name,omitempty"`
	Username   *string          `url:"username,omitempty" json:"username,omitempty"`
	OrderBy    *string          `url:"order_by,omitempty" json:"order_by,omitempty"`
	Sort       *string          `url:"sort,omitempty" json:"sort,omitempty"`
}

// ListProjectPipelines gets a list of project piplines.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/pipelines.html#list-project-pipelines
func (s *PipelinesService) ListProjectPipelines(pid interface{}, opt *ListProjectPipelinesOptions, options ...OptionFunc) (PipelineList, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/pipelines", pathEscape(project))

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

	var p PipelineList
	resp, err := s.client.Do(req, &p)
	if err != nil {
		return nil, resp, err
	}
	return p, resp, err
}

// GetPipeline gets a single project pipeline.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/pipelines.html#get-a-single-pipeline
func (s *PipelinesService) GetPipeline(pid interface{}, pipeline int, options ...OptionFunc) (*Pipeline, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/pipelines/%d", pathEscape(project), pipeline)

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

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

	return p, resp, err
}

// CreatePipelineOptions represents the available CreatePipeline() options.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/pipelines.html#create-a-new-pipeline
type CreatePipelineOptions struct {
	Ref       *string             `url:"ref" json:"ref"`
	Variables []*PipelineVariable `url:"variables,omitempty" json:"variables,omitempty"`
}

// CreatePipeline creates a new project pipeline.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/pipelines.html#create-a-new-pipeline
func (s *PipelinesService) CreatePipeline(pid interface{}, opt *CreatePipelineOptions, options ...OptionFunc) (*Pipeline, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/pipeline", pathEscape(project))

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

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

	return p, resp, err
}

// RetryPipelineBuild retries failed builds in a pipeline
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/pipelines.html#retry-failed-builds-in-a-pipeline
func (s *PipelinesService) RetryPipelineBuild(pid interface{}, pipeline int, options ...OptionFunc) (*Pipeline, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/pipelines/%d/retry", pathEscape(project), pipeline)

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

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

	return p, resp, err
}

// CancelPipelineBuild cancels a pipeline builds
//
// GitLab API docs:
//https://docs.gitlab.com/ce/api/pipelines.html#cancel-a-pipelines-builds
func (s *PipelinesService) CancelPipelineBuild(pid interface{}, pipeline int, options ...OptionFunc) (*Pipeline, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/pipelines/%d/cancel", pathEscape(project), pipeline)

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

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

	return p, resp, err
}

// DeletePipeline deletes an existing pipeline.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/pipelines.html#delete-a-pipeline
func (s *PipelinesService) DeletePipeline(pid interface{}, pipeline int, options ...OptionFunc) (*Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("projects/%s/pipelines/%d", pathEscape(project), pipeline)

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

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


<commit_before>// Copyright 2017 Frédéric Guillot. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.

package scraper // import "miniflux.app/reader/scraper"

// List of predefined scraper rules (alphabetically sorted)
// domain => CSS selectors
var predefinedRules = map[string]string{
	"bbc.co.uk":           "div.vxp-column--single, div.story-body__inner, ul.gallery-images__list",
	"cbc.ca":              ".story-content",
	"darkreading.com":     "#article-main:not(header)",
	"developpez.com":      "div[itemprop=articleBody]",
	"financialsamurai.com": "article",
	"francetvinfo.fr":     ".text",
	"github.com":          "article.entry-content",
	"heise.de":            "header .article-content__lead, header .article-image, div.article-layout__content.article-content",
	"igen.fr":             "section.corps",
	"ing.dk":              "section.body",
	"lapresse.ca":         ".amorce, .entry",
	"lemonde.fr":          "article",
	"lepoint.fr":          ".art-text",
	"lesjoiesducode.fr":   ".blog-post-content img",
	"lesnumeriques.com":   ".text",
	"linux.com":           "div.content, div[property]",
	"medium.com":          ".section-content",
	"mac4ever.com":        "div[itemprop=articleBody]",
	"monwindows.com":      ".blog-post-body",
	"npr.org":             "#storytext",
	"oneindia.com":        ".io-article-body",
	"opensource.com":      "div[property]",
	"osnews.com":          "div.newscontent1",
	"phoronix.com":        "div.content",
	"pseudo-sciences.org": "#art_main",
	"raywenderlich.com":   "article",
	"slate.fr":            ".field-items",
	"techcrunch.com":      "div.article-entry",
	"theoatmeal.com":      "div#comic",
	"theregister.co.uk":   "#body",
	"universfreebox.com":  "#corps_corps",
	"version2.dk":         "section.body",
	"wdwnt.com":           "div.entry-content",
	"wired.com":           "main figure, article",
	"zeit.de":             ".summary, .article-body",
	"zdnet.com":           "div.storyBody",
	"openingsource.org":   "article.suxing-popup-gallery",
}
<commit_msg>Added scraper rule for dilbert.com and turnoff.us<commit_after>// Copyright 2017 Frédéric Guillot. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.

package scraper // import "miniflux.app/reader/scraper"

// List of predefined scraper rules (alphabetically sorted)
// domain => CSS selectors
var predefinedRules = map[string]string{
	"bbc.co.uk":           "div.vxp-column--single, div.story-body__inner, ul.gallery-images__list",
	"cbc.ca":              ".story-content",
	"darkreading.com":     "#article-main:not(header)",
	"developpez.com":      "div[itemprop=articleBody]",
	"dilbert.com":         "span.comic-title-name, img.img-comic",
	"financialsamurai.com": "article",
	"francetvinfo.fr":     ".text",
	"github.com":          "article.entry-content",
	"heise.de":            "header .article-content__lead, header .article-image, div.article-layout__content.article-content",
	"igen.fr":             "section.corps",
	"ing.dk":              "section.body",
	"lapresse.ca":         ".amorce, .entry",
	"lemonde.fr":          "article",
	"lepoint.fr":          ".art-text",
	"lesjoiesducode.fr":   ".blog-post-content img",
	"lesnumeriques.com":   ".text",
	"linux.com":           "div.content, div[property]",
	"medium.com":          ".section-content",
	"mac4ever.com":        "div[itemprop=articleBody]",
	"monwindows.com":      ".blog-post-body",
	"npr.org":             "#storytext",
	"oneindia.com":        ".io-article-body",
	"opensource.com":      "div[property]",
	"osnews.com":          "div.newscontent1",
	"phoronix.com":        "div.content",
	"pseudo-sciences.org": "#art_main",
	"raywenderlich.com":   "article",
	"slate.fr":            ".field-items",
	"techcrunch.com":      "div.article-entry",
	"theoatmeal.com":      "div#comic",
	"theregister.co.uk":   "#body",
	"turnoff.us":          "article.post-content",
	"universfreebox.com":  "#corps_corps",
	"version2.dk":         "section.body",
	"wdwnt.com":           "div.entry-content",
	"wired.com":           "main figure, article",
	"zeit.de":             ".summary, .article-body",
	"zdnet.com":           "div.storyBody",
	"openingsource.org":   "article.suxing-popup-gallery",
}
<|endoftext|>


<commit_before>// Package tdigest provides a highly accurate mergeable data-structure
// for quantile estimation.
package tdigest

import (
	"fmt"
	"math"
)

// TDigest is a quantile approximation data structure.
// Typical T-Digest use cases involve accumulating metrics on several
// distinct nodes of a cluster and then merging them together to get
// a system-wide quantile overview. Things such as: sensory data from
// IoT devices, quantiles over enormous document datasets (think
// ElasticSearch), performance metrics for distributed systems, etc.
type TDigest struct {
	summary     *summary
	compression float64
	count       uint32
	rng         TDigestRNG
}

// New creates a new digest.
//
// By default the digest is constructed with a configuration that
// should be useful for most use-cases.
func New(options ...tdigestOption) (*TDigest, error) {
	tdigest := &TDigest{
		compression: 100,
		count:       0,
		rng:         &globalRNG{},
	}

	for _, option := range options {
		err := option(tdigest)
		if err != nil {
			return nil, err
		}
	}

	tdigest.summary = newSummary(estimateCapacity(tdigest.compression))
	return tdigest, nil
}

func _quantile(index float64, previousIndex float64, nextIndex float64, previousMean float64, nextMean float64) float64 {
	delta := nextIndex - previousIndex
	previousWeight := (nextIndex - index) / delta
	nextWeight := (index - previousIndex) / delta
	return previousMean*previousWeight + nextMean*nextWeight
}

// Quantile returns the desired percentile estimation.
// Values of p must be between 0 and 1 (inclusive), will panic otherwise.
func (t *TDigest) Quantile(q float64) float64 {
	if q < 0 || q > 1 {
		panic("q must be between 0 and 1 (inclusive)")
	}

	if t.Len() == 0 {
		return math.NaN()
	} else if t.Len() == 1 {
		return t.summary.Mean(0)
	}

	index := q * float64(t.count-1)
	previousMean := math.NaN()
	previousIndex := float64(0)
	next, total := t.summary.FloorSum(index)

	if next > 0 {
		previousMean = t.summary.Mean(next - 1)
		previousIndex = total - float64(t.summary.Count(next-1)+1)/2
	}

	for {
		nextIndex := total + float64(t.summary.Count(next)-1)/2
		if nextIndex >= index {
			if math.IsNaN(previousMean) {
				// the index is before the 1st centroid
				if nextIndex == previousIndex {
					return t.summary.Mean(next)
				}
				// assume linear growth
				nextIndex2 := total + float64(t.summary.Count(next)) + float64(t.summary.Count(next+1)-1)/2
				previousMean = (nextIndex2*t.summary.Mean(next) - nextIndex*t.summary.Mean(next+1)) / (nextIndex2 - nextIndex)
			}
			// common case: two centroids found, the result in inbetween
			return _quantile(index, previousIndex, nextIndex, previousMean, t.summary.Mean(next))
		} else if next+1 == t.Len() {
			// the index is after the last centroid
			nextIndex2 := float64(t.count - 1)
			nextMean2 := (t.summary.Mean(next)*(nextIndex2-previousIndex) - previousMean*(nextIndex2-nextIndex)) / (nextIndex - previousIndex)
			return _quantile(index, nextIndex, nextIndex2, t.summary.Mean(next), nextMean2)
		}
		total += float64(t.summary.Count(next))
		previousMean = t.summary.Mean(next)
		previousIndex = nextIndex
		next++
	}
	// unreachable
}

func weightedAverage(x1 float64, w1 float64, x2 float64, w2 float64) float64 {
	if x1 > x2 {
		x1, x2, w1, w2 = x2, x1, w2, w1
	}
	return x1*w1/(w1+w2) + x2*w2/(w1+w2)
}

// AddWeighted registers a new sample in the digest.
//
// It's the main entry point for the digest and very likely the only
// method to be used for collecting samples. The count parameter is for
// when you are registering a sample that occurred multiple times - the
// most common value for this is 1.
//
// This will emit an error if `value` is NaN of if `count` is zero.
func (t *TDigest) AddWeighted(value float64, count uint32) (err error) {

	if count == 0 {
		return fmt.Errorf("Illegal datapoint <value: %.4f, count: %d>", value, count)
	}

	if t.Len() == 0 {
		err = t.summary.Add(value, count)
		t.count = count
		return err
	}

	start := t.summary.Floor(value)
	if start == -1 {
		start = 0
	}

	minDistance := math.MaxFloat64
	lastNeighbor := t.Len()
	for neighbor := start; neighbor < t.Len(); neighbor++ {
		z := math.Abs(t.summary.Mean(neighbor) - value)
		if z < minDistance {
			start = neighbor
			minDistance = z
		} else if z > minDistance {
			lastNeighbor = neighbor
			break
		}
	}

	closest := t.Len()
	sum := t.summary.HeadSum(start)
	var n float32

	for neighbor := start; neighbor != lastNeighbor; neighbor++ {
		c := float64(t.summary.Count(neighbor))
		var q float64
		if t.count == 1 {
			q = 0.5
		} else {
			q = (sum + (c-1)/2) / float64(t.count-1)
		}
		k := 4 * float64(t.count) * q * (1 - q) / t.compression

		if c+float64(count) <= k {
			n++
			if t.rng.Float32() < 1/n {
				closest = neighbor
			}
		}
		sum += c
	}

	if closest == t.Len() {
		t.summary.Add(value, count)
	} else {
		c := float64(t.summary.Count(closest))
		newMean := weightedAverage(t.summary.Mean(closest), c, value, float64(count))
		t.summary.setAt(closest, newMean, uint32(c)+count)
	}
	t.count += count

	if float64(t.Len()) > 20*t.compression {
		err = t.Compress()
	}

	return err
}

// Add(x) is an alias for AddWeighted(x,1)
// Read the documentation for AddWeighted for more details.
func (t *TDigest) Add(value float64) error {
	return t.AddWeighted(value, 1)
}

// Compress tries to reduce the number of individual centroids stored
// in the digest.
// Compression trades off accuracy for performance and happens
// automatically after a certain amount of distinct samples have been
// stored.
func (t *TDigest) Compress() (err error) {
	if t.Len() <= 1 {
		return nil
	}

	oldTree := t.summary
	t.summary = newSummary(uint(t.Len()))
	t.count = 0

	shuffle(oldTree.means, oldTree.counts, t.rng)
	oldTree.ForEach(func(mean float64, count uint32) bool {
		err = t.AddWeighted(mean, count)
		return err == nil
	})

	return err
}

// Merge joins a given digest into itself.
// Merging is useful when you have multiple TDigest instances running
// in separate threads and you want to compute quantiles over all the
// samples. This is particularly important on a scatter-gather/map-reduce
// scenario.
func (t *TDigest) Merge(other *TDigest) (err error) {
	if other.Len() == 0 {
		return nil
	}

	// We must keep the other digest intact
	data := other.summary.Clone()
	shuffle(data.means, data.counts, t.rng)

	data.ForEach(func(mean float64, count uint32) bool {
		err = t.AddWeighted(mean, count)
		return err == nil
	})
	return err
}

// Len returns the number of centroids in the TDigest.
func (t *TDigest) Len() int { return t.summary.Len() }

// ForEachCentroid calls the specified function for each centroid.
// Iteration stops when the supplied function returns false, or when all
// centroids have been iterated.
func (t *TDigest) ForEachCentroid(f func(mean float64, count uint32) bool) {
	t.summary.ForEach(f)
}

func shuffle(means []float64, counts []uint32, rng TDigestRNG) {
	for i := len(means) - 1; i > 1; i-- {
		j := rng.Intn(i + 1)
		means[i], means[j], counts[i], counts[j] = means[j], means[i], counts[j], counts[i]
	}
}

func estimateCapacity(compression float64) uint {
	return uint(compression) * 10
}
<commit_msg>Change TDigest.count to uint64 (was uint32)<commit_after>// Package tdigest provides a highly accurate mergeable data-structure
// for quantile estimation.
package tdigest

import (
	"fmt"
	"math"
)

// TDigest is a quantile approximation data structure.
// Typical T-Digest use cases involve accumulating metrics on several
// distinct nodes of a cluster and then merging them together to get
// a system-wide quantile overview. Things such as: sensory data from
// IoT devices, quantiles over enormous document datasets (think
// ElasticSearch), performance metrics for distributed systems, etc.
type TDigest struct {
	summary     *summary
	compression float64
	count       uint64
	rng         TDigestRNG
}

// New creates a new digest.
//
// By default the digest is constructed with a configuration that
// should be useful for most use-cases.
func New(options ...tdigestOption) (*TDigest, error) {
	tdigest := &TDigest{
		compression: 100,
		count:       0,
		rng:         &globalRNG{},
	}

	for _, option := range options {
		err := option(tdigest)
		if err != nil {
			return nil, err
		}
	}

	tdigest.summary = newSummary(estimateCapacity(tdigest.compression))
	return tdigest, nil
}

func _quantile(index float64, previousIndex float64, nextIndex float64, previousMean float64, nextMean float64) float64 {
	delta := nextIndex - previousIndex
	previousWeight := (nextIndex - index) / delta
	nextWeight := (index - previousIndex) / delta
	return previousMean*previousWeight + nextMean*nextWeight
}

// Quantile returns the desired percentile estimation.
// Values of p must be between 0 and 1 (inclusive), will panic otherwise.
func (t *TDigest) Quantile(q float64) float64 {
	if q < 0 || q > 1 {
		panic("q must be between 0 and 1 (inclusive)")
	}

	if t.Len() == 0 {
		return math.NaN()
	} else if t.Len() == 1 {
		return t.summary.Mean(0)
	}

	index := q * float64(t.count-1)
	previousMean := math.NaN()
	previousIndex := float64(0)
	next, total := t.summary.FloorSum(index)

	if next > 0 {
		previousMean = t.summary.Mean(next - 1)
		previousIndex = total - float64(t.summary.Count(next-1)+1)/2
	}

	for {
		nextIndex := total + float64(t.summary.Count(next)-1)/2
		if nextIndex >= index {
			if math.IsNaN(previousMean) {
				// the index is before the 1st centroid
				if nextIndex == previousIndex {
					return t.summary.Mean(next)
				}
				// assume linear growth
				nextIndex2 := total + float64(t.summary.Count(next)) + float64(t.summary.Count(next+1)-1)/2
				previousMean = (nextIndex2*t.summary.Mean(next) - nextIndex*t.summary.Mean(next+1)) / (nextIndex2 - nextIndex)
			}
			// common case: two centroids found, the result in inbetween
			return _quantile(index, previousIndex, nextIndex, previousMean, t.summary.Mean(next))
		} else if next+1 == t.Len() {
			// the index is after the last centroid
			nextIndex2 := float64(t.count - 1)
			nextMean2 := (t.summary.Mean(next)*(nextIndex2-previousIndex) - previousMean*(nextIndex2-nextIndex)) / (nextIndex - previousIndex)
			return _quantile(index, nextIndex, nextIndex2, t.summary.Mean(next), nextMean2)
		}
		total += float64(t.summary.Count(next))
		previousMean = t.summary.Mean(next)
		previousIndex = nextIndex
		next++
	}
	// unreachable
}

func weightedAverage(x1 float64, w1 float64, x2 float64, w2 float64) float64 {
	if x1 > x2 {
		x1, x2, w1, w2 = x2, x1, w2, w1
	}
	return x1*w1/(w1+w2) + x2*w2/(w1+w2)
}

// AddWeighted registers a new sample in the digest.
//
// It's the main entry point for the digest and very likely the only
// method to be used for collecting samples. The count parameter is for
// when you are registering a sample that occurred multiple times - the
// most common value for this is 1.
//
// This will emit an error if `value` is NaN of if `count` is zero.
func (t *TDigest) AddWeighted(value float64, count uint32) (err error) {

	if count == 0 {
		return fmt.Errorf("Illegal datapoint <value: %.4f, count: %d>", value, count)
	}

	if t.Len() == 0 {
		err = t.summary.Add(value, count)
		t.count = uint64(count)
		return err
	}

	start := t.summary.Floor(value)
	if start == -1 {
		start = 0
	}

	minDistance := math.MaxFloat64
	lastNeighbor := t.Len()
	for neighbor := start; neighbor < t.Len(); neighbor++ {
		z := math.Abs(t.summary.Mean(neighbor) - value)
		if z < minDistance {
			start = neighbor
			minDistance = z
		} else if z > minDistance {
			lastNeighbor = neighbor
			break
		}
	}

	closest := t.Len()
	sum := t.summary.HeadSum(start)
	var n float32

	for neighbor := start; neighbor != lastNeighbor; neighbor++ {
		c := float64(t.summary.Count(neighbor))
		var q float64
		if t.count == 1 {
			q = 0.5
		} else {
			q = (sum + (c-1)/2) / float64(t.count-1)
		}
		k := 4 * float64(t.count) * q * (1 - q) / t.compression

		if c+float64(count) <= k {
			n++
			if t.rng.Float32() < 1/n {
				closest = neighbor
			}
		}
		sum += c
	}

	if closest == t.Len() {
		t.summary.Add(value, count)
	} else {
		c := float64(t.summary.Count(closest))
		newMean := weightedAverage(t.summary.Mean(closest), c, value, float64(count))
		t.summary.setAt(closest, newMean, uint32(c)+count)
	}
	t.count += uint64(count)

	if float64(t.Len()) > 20*t.compression {
		err = t.Compress()
	}

	return err
}

// Add(x) is an alias for AddWeighted(x,1)
// Read the documentation for AddWeighted for more details.
func (t *TDigest) Add(value float64) error {
	return t.AddWeighted(value, 1)
}

// Compress tries to reduce the number of individual centroids stored
// in the digest.
// Compression trades off accuracy for performance and happens
// automatically after a certain amount of distinct samples have been
// stored.
func (t *TDigest) Compress() (err error) {
	if t.Len() <= 1 {
		return nil
	}

	oldTree := t.summary
	t.summary = newSummary(uint(t.Len()))
	t.count = 0

	shuffle(oldTree.means, oldTree.counts, t.rng)
	oldTree.ForEach(func(mean float64, count uint32) bool {
		err = t.AddWeighted(mean, count)
		return err == nil
	})

	return err
}

// Merge joins a given digest into itself.
// Merging is useful when you have multiple TDigest instances running
// in separate threads and you want to compute quantiles over all the
// samples. This is particularly important on a scatter-gather/map-reduce
// scenario.
func (t *TDigest) Merge(other *TDigest) (err error) {
	if other.Len() == 0 {
		return nil
	}

	// We must keep the other digest intact
	data := other.summary.Clone()
	shuffle(data.means, data.counts, t.rng)

	data.ForEach(func(mean float64, count uint32) bool {
		err = t.AddWeighted(mean, count)
		return err == nil
	})
	return err
}

// Len returns the number of centroids in the TDigest.
func (t *TDigest) Len() int { return t.summary.Len() }

// ForEachCentroid calls the specified function for each centroid.
// Iteration stops when the supplied function returns false, or when all
// centroids have been iterated.
func (t *TDigest) ForEachCentroid(f func(mean float64, count uint32) bool) {
	t.summary.ForEach(f)
}

func shuffle(means []float64, counts []uint32, rng TDigestRNG) {
	for i := len(means) - 1; i > 1; i-- {
		j := rng.Intn(i + 1)
		means[i], means[j], counts[i], counts[j] = means[j], means[i], counts[j], counts[i]
	}
}

func estimateCapacity(compression float64) uint {
	return uint(compression) * 10
}
<|endoftext|>


<commit_before>package authorization

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"

	"github.com/gitpods/gitpods/session"
	"github.com/google/jsonapi"
	"github.com/pressly/chi"
)

const megabyte = 1024 * 1024 * 1024

// NewHandler returns a RESTful http router interacting with the Service.
func NewHandler(s Service) *chi.Mux {
	r := chi.NewRouter()

	r.Post("/", authorize(s))

	return r
}

func authorize(s Service) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		var form struct {
			Email    string `json:"email"`
			Password string `json:"password"`
		}

		badCredentials := []*jsonapi.ErrorObject{{
			Title:  http.StatusText(http.StatusBadRequest),
			Detail: "Bad Credentials",
			Status: fmt.Sprintf("%d", http.StatusBadRequest),
		}}

		if err := json.NewDecoder(io.LimitReader(r.Body, megabyte)).Decode(&form); err != nil {
			w.WriteHeader(http.StatusBadRequest)
			jsonapi.MarshalErrors(w, badCredentials)
			return
		}

		user, err := s.AuthenticateUser(form.Email, form.Password)
		if err != nil {
			w.WriteHeader(http.StatusBadRequest)
			jsonapi.MarshalErrors(w, badCredentials)
			return
		}

		sess, err := s.CreateSession(user.ID, user.Username)
		if err != nil {
			w.WriteHeader(http.StatusBadRequest)
			jsonapi.MarshalErrors(w, badCredentials)
			return
		}

		cookie := &http.Cookie{
			Name:    session.CookieName,
			Value:   sess.ID,
			Path:    "/",
			Expires: sess.Expiry,
		}

		http.SetCookie(w, cookie)
	}
}
<commit_msg>Improve error detail message for bad credentials<commit_after>package authorization

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"

	"github.com/gitpods/gitpods/session"
	"github.com/google/jsonapi"
	"github.com/pressly/chi"
)

const megabyte = 1024 * 1024 * 1024

// NewHandler returns a RESTful http router interacting with the Service.
func NewHandler(s Service) *chi.Mux {
	r := chi.NewRouter()

	r.Post("/", authorize(s))

	return r
}

func authorize(s Service) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		var form struct {
			Email    string `json:"email"`
			Password string `json:"password"`
		}

		badCredentials := []*jsonapi.ErrorObject{{
			Title:  http.StatusText(http.StatusBadRequest),
			Detail: "Incorrect email or password",
			Status: fmt.Sprintf("%d", http.StatusBadRequest),
		}}

		if err := json.NewDecoder(io.LimitReader(r.Body, megabyte)).Decode(&form); err != nil {
			w.WriteHeader(http.StatusBadRequest)
			jsonapi.MarshalErrors(w, badCredentials)
			return
		}

		user, err := s.AuthenticateUser(form.Email, form.Password)
		if err != nil {
			w.WriteHeader(http.StatusBadRequest)
			jsonapi.MarshalErrors(w, badCredentials)
			return
		}

		sess, err := s.CreateSession(user.ID, user.Username)
		if err != nil {
			w.WriteHeader(http.StatusBadRequest)
			jsonapi.MarshalErrors(w, badCredentials)
			return
		}

		cookie := &http.Cookie{
			Name:    session.CookieName,
			Value:   sess.ID,
			Path:    "/",
			Expires: sess.Expiry,
		}

		http.SetCookie(w, cookie)
	}
}
<|endoftext|>


<commit_before>// +build linux darwin

/*
   Copyright The containerd Authors.

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

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

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

package sysx

import (
	"bytes"

	"golang.org/x/sys/unix"
)

// Listxattr calls syscall listxattr and reads all content
// and returns a string array
func Listxattr(path string) ([]string, error) {
	return listxattrAll(path, unix.Listxattr)
}

// Removexattr calls syscall removexattr
func Removexattr(path string, attr string) (err error) {
	return unix.Removexattr(path, attr)
}

// Setxattr calls syscall setxattr
func Setxattr(path string, attr string, data []byte, flags int) (err error) {
	return unix.Setxattr(path, attr, data, flags)
}

// Getxattr calls syscall getxattr
func Getxattr(path, attr string) ([]byte, error) {
	return getxattrAll(path, attr, unix.Getxattr)
}

// LListxattr lists xattrs, not following symlinks
func LListxattr(path string) ([]string, error) {
	return listxattrAll(path, unix.Llistxattr)
}

// LRemovexattr removes an xattr, not following symlinks
func LRemovexattr(path string, attr string) (err error) {
	return unix.Lremovexattr(path, attr)
}

// LSetxattr sets an xattr, not following symlinks
func LSetxattr(path string, attr string, data []byte, flags int) (err error) {
	return unix.Lsetxattr(path, attr, data, flags)
}

// LGetxattr gets an xattr, not following symlinks
func LGetxattr(path, attr string) ([]byte, error) {
	return getxattrAll(path, attr, unix.Lgetxattr)
}

const defaultXattrBufferSize = 128

type listxattrFunc func(path string, dest []byte) (int, error)

func listxattrAll(path string, listFunc listxattrFunc) ([]string, error) {
	var p []byte // nil on first execution

	for {
		n, err := listFunc(path, p) // first call gets buffer size.
		if err != nil {
			return nil, err
		}

		if n > len(p) {
			p = make([]byte, n)
			continue
		}

		p = p[:n]

		ps := bytes.Split(bytes.TrimSuffix(p, []byte{0}), []byte{0})
		var entries []string
		for _, p := range ps {
			s := string(p)
			if s != "" {
				entries = append(entries, s)
			}
		}

		return entries, nil
	}
}

type getxattrFunc func(string, string, []byte) (int, error)

func getxattrAll(path, attr string, getFunc getxattrFunc) ([]byte, error) {
	buf := make([]byte, defaultXattrBufferSize)
	n, err := getFunc(path, attr, buf)
	for err == unix.ERANGE {
		// Buffer too small, use zero-sized buffer to get the actual size
		n, err = getFunc(path, attr, []byte{})
		if err != nil {
			return nil, err
		}
		buf = make([]byte, n)
		n, err = getFunc(path, attr, buf)
	}
	if err != nil {
		return nil, err
	}
	return buf[:n], nil
}
<commit_msg>sysx/xattr: improve listxattrAll<commit_after>// +build linux darwin

/*
   Copyright The containerd Authors.

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

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

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

package sysx

import (
	"bytes"

	"golang.org/x/sys/unix"
)

// Listxattr calls syscall listxattr and reads all content
// and returns a string array
func Listxattr(path string) ([]string, error) {
	return listxattrAll(path, unix.Listxattr)
}

// Removexattr calls syscall removexattr
func Removexattr(path string, attr string) (err error) {
	return unix.Removexattr(path, attr)
}

// Setxattr calls syscall setxattr
func Setxattr(path string, attr string, data []byte, flags int) (err error) {
	return unix.Setxattr(path, attr, data, flags)
}

// Getxattr calls syscall getxattr
func Getxattr(path, attr string) ([]byte, error) {
	return getxattrAll(path, attr, unix.Getxattr)
}

// LListxattr lists xattrs, not following symlinks
func LListxattr(path string) ([]string, error) {
	return listxattrAll(path, unix.Llistxattr)
}

// LRemovexattr removes an xattr, not following symlinks
func LRemovexattr(path string, attr string) (err error) {
	return unix.Lremovexattr(path, attr)
}

// LSetxattr sets an xattr, not following symlinks
func LSetxattr(path string, attr string, data []byte, flags int) (err error) {
	return unix.Lsetxattr(path, attr, data, flags)
}

// LGetxattr gets an xattr, not following symlinks
func LGetxattr(path, attr string) ([]byte, error) {
	return getxattrAll(path, attr, unix.Lgetxattr)
}

const defaultXattrBufferSize = 128

type listxattrFunc func(path string, dest []byte) (int, error)

func listxattrAll(path string, listFunc listxattrFunc) ([]string, error) {
	buf := make([]byte, defaultXattrBufferSize)
	n, err := listFunc(path, buf)
	for err == unix.ERANGE {
		// Buffer too small, use zero-sized buffer to get the actual size
		n, err = listFunc(path, []byte{})
		if err != nil {
			return nil, err
		}
		buf = make([]byte, n)
		n, err = listFunc(path, buf)
	}
	if err != nil {
		return nil, err
	}

	ps := bytes.Split(bytes.TrimSuffix(buf[:n], []byte{0}), []byte{0})
	var entries []string
	for _, p := range ps {
		if len(p) > 0 {
			entries = append(entries, string(p))
		}
	}

	return entries, nil
}

type getxattrFunc func(string, string, []byte) (int, error)

func getxattrAll(path, attr string, getFunc getxattrFunc) ([]byte, error) {
	buf := make([]byte, defaultXattrBufferSize)
	n, err := getFunc(path, attr, buf)
	for err == unix.ERANGE {
		// Buffer too small, use zero-sized buffer to get the actual size
		n, err = getFunc(path, attr, []byte{})
		if err != nil {
			return nil, err
		}
		buf = make([]byte, n)
		n, err = getFunc(path, attr, buf)
	}
	if err != nil {
		return nil, err
	}
	return buf[:n], nil
}
<|endoftext|>


<commit_before>package pkg

import (
	"errors"
	"log"
	"net"
	"net/rpc"
	"os"
	"strconv"

	"github.com/avabot/ava/Godeps/_workspace/src/github.com/jmoiron/sqlx"
	_ "github.com/avabot/ava/Godeps/_workspace/src/github.com/lib/pq"
	"github.com/avabot/ava/shared/datatypes"
)

type PkgWrapper struct {
	P         *Pkg
	RPCClient *rpc.Client
}

// Pkg holds config options for any Ava package. Name must be globally unique
// Port takes the format of ":1234". Note that the colon is significant.
// ServerAddress will default to localhost if left blank.
type Pkg struct {
	Config  PkgConfig
	Trigger *datatypes.StructuredInput
}

type PkgConfig struct {
	Name          string
	ServerAddress string
	Port          int
}

type Ava int

var client *rpc.Client
var db *sqlx.DB
var (
	ErrMissingPackageName = errors.New("missing package name")
	ErrMissingPort        = errors.New("missing package port")
	ErrMissingTrigger     = errors.New("missing package trigger")
)

func NewPackage(name string, port int, trigger *datatypes.StructuredInput) (
	*Pkg, error) {
	return NewPackageWithServer(name, "", port, trigger)
}

func NewPackageWithServer(name, serverAddr string, port int,
	trigger *datatypes.StructuredInput) (*Pkg, error) {
	if len(name) == 0 {
		return &Pkg{}, ErrMissingPackageName
	}
	if trigger == nil {
		return &Pkg{}, ErrMissingTrigger
	}
	c := PkgConfig{
		Name:          name,
		Port:          port,
		ServerAddress: serverAddr,
	}
	return &Pkg{Config: c, Trigger: trigger}, nil
}

// Register with Ava to begin communicating over RPC.
func (p *Pkg) Register(pkgT interface{}) error {
	l, err := net.Listen("tcp", ":"+strconv.Itoa(p.Config.Port+1))
	if err != nil {
		log.Fatalln("rpc listen:", err, p.Config.Name)
	}
	if err := rpc.Register(pkgT); err != nil {
		log.Fatalln(err, p.Config.Name)
	}
	port, err := strconv.Atoi(os.Getenv("PORT"))
	if err != nil {
		return err
	}
	client, err = rpc.Dial("tcp", ":"+strconv.Itoa(port+1))
	if err != nil {
		return err
	}
	var notused error
	log.Println("calling register", p.Config.Name)
	err = client.Call("Ava.RegisterPackage", p, &notused)
	if err != nil {
		log.Println("err: registering package", p.Config.Name, err)
		return err
	}
	log.Println("connected with ava", p.Config.Name)
	if err = connectDB(); err != nil {
		return err
	}
	log.Println("connected with database", p.Config.Name)
	for {
		conn, err := l.Accept()
		if err != nil {
			log.Fatalln(err)
		}
		go rpc.ServeConn(conn)
	}
	return nil
}

func connectDB() error {
	var err error
	if os.Getenv("AVA_ENV") == "production" {
		db, err = sqlx.Connect("postgres", os.Getenv("DATABASE_URL"))
	} else {
		db, err = sqlx.Connect("postgres",
			"user=egtann dbname=ava sslmode=disable")
	}
	if err != nil {
		return err
	}
	return nil
}
<commit_msg>Add logs to port registration<commit_after>package pkg

import (
	"errors"
	"log"
	"net"
	"net/rpc"
	"os"
	"strconv"

	"github.com/avabot/ava/Godeps/_workspace/src/github.com/jmoiron/sqlx"
	_ "github.com/avabot/ava/Godeps/_workspace/src/github.com/lib/pq"
	"github.com/avabot/ava/shared/datatypes"
)

type PkgWrapper struct {
	P         *Pkg
	RPCClient *rpc.Client
}

// Pkg holds config options for any Ava package. Name must be globally unique
// Port takes the format of ":1234". Note that the colon is significant.
// ServerAddress will default to localhost if left blank.
type Pkg struct {
	Config  PkgConfig
	Trigger *datatypes.StructuredInput
}

type PkgConfig struct {
	Name          string
	ServerAddress string
	Port          int
}

type Ava int

var client *rpc.Client
var db *sqlx.DB
var (
	ErrMissingPackageName = errors.New("missing package name")
	ErrMissingPort        = errors.New("missing package port")
	ErrMissingTrigger     = errors.New("missing package trigger")
)

func NewPackage(name string, port int, trigger *datatypes.StructuredInput) (
	*Pkg, error) {
	return NewPackageWithServer(name, "", port, trigger)
}

func NewPackageWithServer(name, serverAddr string, port int,
	trigger *datatypes.StructuredInput) (*Pkg, error) {
	if len(name) == 0 {
		return &Pkg{}, ErrMissingPackageName
	}
	if trigger == nil {
		return &Pkg{}, ErrMissingTrigger
	}
	c := PkgConfig{
		Name:          name,
		Port:          port,
		ServerAddress: serverAddr,
	}
	return &Pkg{Config: c, Trigger: trigger}, nil
}

// Register with Ava to begin communicating over RPC.
func (p *Pkg) Register(pkgT interface{}) error {
	log.Println("connecting to port", p.Config.Port+1, "for", p.Config.Name)
	l, err := net.Listen("tcp", ":"+strconv.Itoa(p.Config.Port+1))
	if err != nil {
		log.Fatalln("rpc listen:", err, p.Config.Name)
	}
	if err := rpc.Register(pkgT); err != nil {
		log.Fatalln(err, p.Config.Name)
	}
	port, err := strconv.Atoi(os.Getenv("PORT"))
	if err != nil {
		return err
	}
	client, err = rpc.Dial("tcp", ":"+strconv.Itoa(port+1))
	if err != nil {
		return err
	}
	var notused error
	log.Println("calling register", p.Config.Name)
	err = client.Call("Ava.RegisterPackage", p, &notused)
	if err != nil {
		log.Println("err: registering package", p.Config.Name, err)
		return err
	}
	log.Println("connected with ava", p.Config.Name)
	if err = connectDB(); err != nil {
		return err
	}
	log.Println("connected with database", p.Config.Name)
	for {
		conn, err := l.Accept()
		if err != nil {
			log.Fatalln(err)
		}
		go rpc.ServeConn(conn)
	}
	return nil
}

func connectDB() error {
	var err error
	if os.Getenv("AVA_ENV") == "production" {
		db, err = sqlx.Connect("postgres", os.Getenv("DATABASE_URL"))
	} else {
		db, err = sqlx.Connect("postgres",
			"user=egtann dbname=ava sslmode=disable")
	}
	if err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package git

import (
	"fmt"
	"os"
	"os/exec"
	"strconv"
	"strings"

	"github.com/Masterminds/semver"
)

// Info contains the status of the working tree.
type Info struct {
	Branch     string
	SHA        string
	Tag        string
	Prerelease string
	IsTag      bool
	IsDirty    bool
}

// NewInfo instantiates and returns info.
func NewInfo() (info *Info, err error) {
	branch, err := Branch()
	if err != nil {
		return
	}

	sha, err := SHA()
	if err != nil {
		return
	}

	tag, isTag, err := Tag()
	if err != nil {
		return
	}

	_, isDirty, err := Status()
	if err != nil {
		return
	}

	info = &Info{
		Branch:  branch,
		SHA:     sha,
		Tag:     strings.TrimSuffix(tag, "\n"),
		IsTag:   isTag,
		IsDirty: isDirty,
	}

	return
}

// Branch returns the current git branch name.
func Branch() (branch string, err error) {
	branchBytes, err := exec.Command("git", "rev-parse", "--abbrev-ref", "HEAD").Output()
	if err != nil {
		return
	}
	branch = strings.TrimSuffix(string(branchBytes), "\n")
	err = ExportConformVar("branch", branch)
	fmt.Printf("Branch: %s\n", branch)

	return
}

// SHA returns the sha of the current commit.
func SHA() (sha string, err error) {
	shaBytes, err := exec.Command("git", "rev-parse", "--short", "HEAD").Output()
	if err != nil {
		return
	}
	sha = strings.TrimSuffix(string(shaBytes), "\n")
	err = ExportConformVar("sha", sha)
	fmt.Printf("SHA: %s\n", sha)

	return
}

// Tag returns the tag name if HEAD is a tag.
func Tag() (tag string, isTag bool, err error) {
	tagBytes, isTagErr := exec.Command("git", "describe", "--exact-match", "--tags", "HEAD").Output()
	if isTagErr == nil {
		isTag = true
	}
	tag = strings.TrimSuffix(string(tagBytes), "\n")
	if isTag {
		_, err = semver.NewVersion(tag[1:])
		if err != nil {
			return
		}
	}
	err = ExportConformVar("tag", tag)
	if err != nil {
		return
	}
	err = ExportConformVar("is_tag", strconv.FormatBool(isTag))
	if err != nil {
		return
	}
	fmt.Printf("IsTag: %v\n", isTag)
	fmt.Printf("Tag: %s\n", tag)

	return
}

// Status returns the status of the working tree.
func Status() (status string, isDirty bool, err error) {
	statusBytes, err := exec.Command("git", "status", "--porcelain").Output()
	if err != nil {
		return
	}
	status = strings.TrimSuffix(string(statusBytes), "\n")
	if status != "" {
		isDirty = true
	}
	err = ExportConformVar("is_dirty", strconv.FormatBool(isDirty))
	if err != nil {
		return
	}
	fmt.Printf("Status: %s\n", status)
	fmt.Printf("IsDirty: %v\n", isDirty)

	return
}

// ExportConformVar exports variable prefixed with CONFORM_
func ExportConformVar(name, value string) (err error) {
	variable := fmt.Sprintf("CONFORM_%s", strings.ToUpper(name))
	err = os.Setenv(variable, value)

	return
}
<commit_msg>Add pre-release to git info (#12)<commit_after>package git

import (
	"fmt"
	"os"
	"os/exec"
	"strconv"
	"strings"

	"github.com/Masterminds/semver"
)

// Info contains the status of the working tree.
type Info struct {
	Branch       string
	SHA          string
	Tag          string
	Prerelease   string
	IsTag        bool
	IsPrerelease bool
	IsDirty      bool
}

// NewInfo instantiates and returns info.
func NewInfo() (info *Info, err error) {
	branch, err := Branch()
	if err != nil {
		return
	}

	sha, err := SHA()
	if err != nil {
		return
	}

	tag, isTag, err := Tag()
	if err != nil {
		return
	}

	prerelease, isPrerelease, err := Prerelease(tag, isTag)
	if err != nil {
		return
	}

	_, isDirty, err := Status()
	if err != nil {
		return
	}

	info = &Info{
		Branch:       branch,
		SHA:          sha,
		Tag:          tag,
		Prerelease:   prerelease,
		IsTag:        isTag,
		IsPrerelease: isPrerelease,
		IsDirty:      isDirty,
	}

	return
}

// Branch returns the current git branch name.
func Branch() (branch string, err error) {
	branchBytes, err := exec.Command("git", "rev-parse", "--abbrev-ref", "HEAD").Output()
	if err != nil {
		return
	}
	branch = strings.TrimSuffix(string(branchBytes), "\n")
	err = ExportConformVar("branch", branch)
	fmt.Printf("Branch: %s\n", branch)

	return
}

// SHA returns the sha of the current commit.
func SHA() (sha string, err error) {
	shaBytes, err := exec.Command("git", "rev-parse", "--short", "HEAD").Output()
	if err != nil {
		return
	}
	sha = strings.TrimSuffix(string(shaBytes), "\n")
	err = ExportConformVar("sha", sha)
	fmt.Printf("SHA: %s\n", sha)

	return
}

// Tag returns the tag name if HEAD is a tag.
func Tag() (tag string, isTag bool, err error) {
	tagBytes, isTagErr := exec.Command("git", "describe", "--exact-match", "--tags", "HEAD").Output()
	if isTagErr == nil {
		isTag = true
	}
	tag = strings.TrimSuffix(string(tagBytes), "\n")
	if isTag {
		_, err = semver.NewVersion(tag[1:])
		if err != nil {
			return
		}
	}
	err = ExportConformVar("tag", tag)
	if err != nil {
		return
	}
	err = ExportConformVar("is_tag", strconv.FormatBool(isTag))
	if err != nil {
		return
	}
	fmt.Printf("Tag: %s\n", tag)
	fmt.Printf("IsTag: %v\n", isTag)

	return
}

// Prerelease returns the prerelease name if the tag is a prerelease.
func Prerelease(tag string, isTag bool) (prerelease string, isPrerelease bool, err error) {
	if isTag {
		var ver *semver.Version
		ver, err = semver.NewVersion(tag[1:])
		if err != nil {
			return
		}
		if ver.Prerelease() != "" {
			prerelease = ver.Prerelease()
			isPrerelease = true
		}
	}
	err = ExportConformVar("prerelease", prerelease)
	if err != nil {
		return
	}
	err = ExportConformVar("is_prerelease", strconv.FormatBool(isPrerelease))
	if err != nil {
		return
	}
	fmt.Printf("Prerelease: %s\n", prerelease)
	fmt.Printf("IsPrerelease: %v\n", isPrerelease)

	return
}

// Status returns the status of the working tree.
func Status() (status string, isDirty bool, err error) {
	statusBytes, err := exec.Command("git", "status", "--porcelain").Output()
	if err != nil {
		return
	}
	status = strings.TrimSuffix(string(statusBytes), "\n")
	if status != "" {
		isDirty = true
	}
	err = ExportConformVar("is_dirty", strconv.FormatBool(isDirty))
	if err != nil {
		return
	}
	fmt.Printf("Status: %s\n", status)
	fmt.Printf("IsDirty: %v\n", isDirty)

	return
}

// ExportConformVar exports variable prefixed with CONFORM_
func ExportConformVar(name, value string) (err error) {
	variable := fmt.Sprintf("CONFORM_%s", strings.ToUpper(name))
	err = os.Setenv(variable, value)

	return
}
<|endoftext|>


<commit_before>package task

import "github.com/synapse-garden/sg-proto/store"

func (*Task) Resource() store.Resource { return "tasks" }

// Deleted is a Resourcer which can notify that the convo has been
// deleted.
type Deleted string

// Resource implements Resourcer.Resource on Deleted.
func (Deleted) Resource() store.Resource { return "task-deleted" }
<commit_msg>Add task notif Resourcer for removal<commit_after>package task

import "github.com/synapse-garden/sg-proto/store"

func (*Task) Resource() store.Resource { return "tasks" }

// Deleted is a Resourcer which can notify that the convo has been
// deleted.
type Deleted string

// Resource implements Resourcer.Resource on Deleted.
func (Deleted) Resource() store.Resource { return "task-deleted" }

// Removed is a Resourcer which can be used to notify that the user has
// been removed from the Task without showing them the Task itself.
type Removed ID

// Resource implements Resourcer on Removed.
func (Removed) Resource() store.Resource { return "task-removed" }
<|endoftext|>


<commit_before>package resty

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"reflect"
	"testing"
)

var (
	LogRequests = false
)

type TestRequest struct {
	Method string
	Path   string
	Data   interface{}

	ExpectedStatus int
	ExpectedData   interface{}
}

func (tr *TestRequest) String() string {
	return tr.Method + " " + tr.Path
}

func (tr *TestRequest) Run(t *testing.T, c *Client) {
	r := c.Do(tr.Method, tr.Path, tr.Data, nil)
	if LogRequests {
		t.Logf("%s: %s", tr.String(), r.Value)
	}

	switch {
	case r.Err != nil:
		t.Fatalf("%s: error: %v, status: %d, resp: %s", tr.String(), r.Err, r.Status, r.Value)
	case tr.ExpectedStatus == 0 && r.Status != 200, r.Status != tr.ExpectedStatus:
		t.Fatalf("%s: wanted %d, got %d: %s", tr.String(), tr.ExpectedStatus, r.Status, r.Value)
	case tr.ExpectedData != nil:
		if err := compareRes(r.Value, getVal(tr.ExpectedData)); err != nil {
			t.Fatalf("%s: %v", tr.String(), err)
		}
	}
}

// a == result, b == expected
func compareRes(a, b []byte) error {
	var am, bm map[string]interface{}
	if err := json.Unmarshal(a, &am); err != nil {
		return fmt.Errorf("%s: %v", a, err)
	}
	if err := json.Unmarshal(b, &bm); err != nil {
		return fmt.Errorf("%s: %v", b, err)
	}

	for k, v := range bm {
		if ov := am[k]; !reflect.DeepEqual(v, ov) {
			return fmt.Errorf("wanted %v, got %v", v, ov)
		}
	}
	return nil
}

func getVal(v interface{}) []byte {
	switch v := v.(type) {
	case []byte:
		return v
	case string:
		return []byte(v)
	case io.Reader:
		b, _ := ioutil.ReadAll(v)
		return b
	case nil:
		return nil
	}
	j, _ := json.Marshal(v)
	return j
}
<commit_msg>show the key on map mismatch<commit_after>package resty

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"reflect"
	"testing"
)

var (
	LogRequests = false
)

type TestRequest struct {
	Method string
	Path   string
	Data   interface{}

	ExpectedStatus int
	ExpectedData   interface{}
}

func (tr *TestRequest) String() string {
	return tr.Method + " " + tr.Path
}

func (tr *TestRequest) Run(t *testing.T, c *Client) {
	r := c.Do(tr.Method, tr.Path, tr.Data, nil)
	if LogRequests {
		t.Logf("%s: %s", tr.String(), r.Value)
	}

	switch {
	case r.Err != nil:
		t.Fatalf("%s: error: %v, status: %d, resp: %s", tr.String(), r.Err, r.Status, r.Value)
	case tr.ExpectedStatus == 0 && r.Status != 200, r.Status != tr.ExpectedStatus:
		t.Fatalf("%s: wanted %d, got %d: %s", tr.String(), tr.ExpectedStatus, r.Status, r.Value)
	case tr.ExpectedData != nil:
		if err := compareRes(r.Value, getVal(tr.ExpectedData)); err != nil {
			t.Fatalf("%s: %v", tr.String(), err)
		}
	}
}

// a == result, b == expected
func compareRes(a, b []byte) error {
	var am, bm map[string]interface{}
	if err := json.Unmarshal(a, &am); err != nil {
		return fmt.Errorf("%s: %v", a, err)
	}
	if err := json.Unmarshal(b, &bm); err != nil {
		return fmt.Errorf("%s: %v", b, err)
	}

	for k, v := range bm {
		if ov := am[k]; !reflect.DeepEqual(v, ov) {
			return fmt.Errorf("%s wanted %v, got %v", k, v, ov)
		}
	}
	return nil
}

func getVal(v interface{}) []byte {
	switch v := v.(type) {
	case []byte:
		return v
	case string:
		return []byte(v)
	case io.Reader:
		b, _ := ioutil.ReadAll(v)
		return b
	case nil:
		return nil
	}
	j, _ := json.Marshal(v)
	return j
}
<|endoftext|>


<commit_before>package tinycfg

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"os"
	"sort"
	"strings"
	"sync"
)

const (
	delim         = "="
	commentPrefix = "//"
)

// A Config stores key, value pairs.
type Config struct {
	mu   sync.RWMutex
	vals map[string]string
}

// Get returns the value for a specified key or an empty string if the key was not found.
func (c *Config) Get(key string) string {
	c.mu.RLock()
	defer c.mu.RUnlock()
	return c.vals[key]
}

// Set adds a key, value pair or modifies an existing one. The returned error can be safely
// ignored if you are certain that both the key and value are valid. Keys are invalid if
// they contain '=', newline characters or are empty. Values are invalid if they contain
// newline characters or are empty.
func (c *Config) Set(key, value string) error {
	if key == "" {
		return errors.New("key cannot be empty")
	}
	if value == "" {
		return errors.New("value cannot be empty")
	}
	if strings.Contains(key, delim) {
		return fmt.Errorf("key cannot contain '%s'", delim)
	}
	if strings.Contains(value, "\n") {
		return errors.New("value cannot contain newlines")
	}
	if strings.Contains(key, "\n") {
		return errors.New("key cannot contain newlines")
	}
	c.mu.Lock()
	c.vals[key] = value
	c.mu.Unlock()
	return nil
}

// Delete removes a key, value pair.
func (c *Config) Delete(key string) {
	c.mu.Lock()
	defer c.mu.Unlock()
	delete(c.vals, key)
}

// Encode writes out a Config instance in the correct format to a Writer. Key, value pairs
// are listed in alphabetical order.
func (c *Config) Encode(w io.Writer) error {
	var lines []string
	c.mu.RLock()
	defer c.mu.RUnlock()
	for k, v := range c.vals {
		lines = append(lines, fmt.Sprintf("%s=%s", k, v))
	}
	sort.Sort(sort.StringSlice(lines))
	for _, v := range lines {
		_, err := fmt.Fprintln(w, v)
		if err != nil {
			return fmt.Errorf("unable to encode line: %s\n%s", v, err)
		}
	}
	return nil
}

// New returns an empty Config instance ready for use.
func New() *Config {
	return &Config{vals: make(map[string]string)}
}

// Open is a convenience function that opens a file at a specified path, passes it to Decode
// then closes the file.
func Open(path string) (*Config, error) {
	file, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer file.Close()
	return Decode(file)
}

// Decode creates a new Config instance from a Reader.
func Decode(r io.Reader) (*Config, error) {
	cfg := &Config{vals: make(map[string]string)}
	scanner := bufio.NewScanner(r)
	for lineNum := 1; scanner.Scan(); lineNum++ {
		line := strings.TrimSpace(scanner.Text())
		if line == "" || strings.HasPrefix(line, commentPrefix) {
			continue
		}
		args := strings.SplitN(line, delim, 2)
		key, value := strings.TrimSpace(args[0]), strings.TrimSpace(args[1])
		if key == "" || value == "" {
			return cfg, fmt.Errorf("no key/value pair found at line %d", lineNum)
		}
		if _, ok := cfg.vals[key]; ok {
			return cfg, fmt.Errorf("duplicate entry for key %s at line %d", key, lineNum)
		}
		cfg.vals[key] = value
	}
	if scanner.Err() != nil {
		return cfg, scanner.Err()
	}
	return cfg, nil
}

// Defaults is a convenience function that will apply a map of default key/values to a *Config, provided the keys are not already present.
func Defaults(cfg *Config, defaults map[string]string) error {
	for k, v := range defaults {
		if cfg.Get(k) == "" {
			if err := cfg.Set(k, v); err != nil {
				return err
			}
		}
	}
	return nil
}

// Missing checks for the existence of a slice of keys in a Config instance and returns a slice
// which contains keys that are missing, or nil if there are no missing keys.
func Missing(cfg *Config, required []string) []string {
	var missing []string
	for _, k := range required {
		if v := cfg.Get(k); v == "" {
			missing = append(missing, k)
		}
	}
	if len(missing) > 0 {
		return missing
	}
	return nil
}

// NewFromEnv returns a new Config instance populated from environment variables.
func NewFromEnv(keys []string) (*Config, error) {
	var buf bytes.Buffer
	for _, k := range keys {
		fmt.Fprintln(&buf, k, "=", os.Getenv(k))
	}
	cfg, err := Decode(&buf)
	return cfg, err
}
<commit_msg>Use sort.Strings<commit_after>package tinycfg

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"os"
	"sort"
	"strings"
	"sync"
)

const (
	delim         = "="
	commentPrefix = "//"
)

// A Config stores key, value pairs.
type Config struct {
	mu   sync.RWMutex
	vals map[string]string
}

// Get returns the value for a specified key or an empty string if the key was not found.
func (c *Config) Get(key string) string {
	c.mu.RLock()
	defer c.mu.RUnlock()
	return c.vals[key]
}

// Set adds a key, value pair or modifies an existing one. The returned error can be safely
// ignored if you are certain that both the key and value are valid. Keys are invalid if
// they contain '=', newline characters or are empty. Values are invalid if they contain
// newline characters or are empty.
func (c *Config) Set(key, value string) error {
	if key == "" {
		return errors.New("key cannot be empty")
	}
	if value == "" {
		return errors.New("value cannot be empty")
	}
	if strings.Contains(key, delim) {
		return fmt.Errorf("key cannot contain '%s'", delim)
	}
	if strings.Contains(value, "\n") {
		return errors.New("value cannot contain newlines")
	}
	if strings.Contains(key, "\n") {
		return errors.New("key cannot contain newlines")
	}
	c.mu.Lock()
	c.vals[key] = value
	c.mu.Unlock()
	return nil
}

// Delete removes a key, value pair.
func (c *Config) Delete(key string) {
	c.mu.Lock()
	defer c.mu.Unlock()
	delete(c.vals, key)
}

// Encode writes out a Config instance in the correct format to a Writer. Key, value pairs
// are listed in alphabetical order.
func (c *Config) Encode(w io.Writer) error {
	var lines []string
	c.mu.RLock()
	defer c.mu.RUnlock()
	for k, v := range c.vals {
		lines = append(lines, fmt.Sprintf("%s=%s", k, v))
	}
	sort.Strings(lines)
	for _, v := range lines {
		_, err := fmt.Fprintln(w, v)
		if err != nil {
			return fmt.Errorf("unable to encode line: %s\n%s", v, err)
		}
	}
	return nil
}

// New returns an empty Config instance ready for use.
func New() *Config {
	return &Config{vals: make(map[string]string)}
}

// Open is a convenience function that opens a file at a specified path, passes it to Decode
// then closes the file.
func Open(path string) (*Config, error) {
	file, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer file.Close()
	return Decode(file)
}

// Decode creates a new Config instance from a Reader.
func Decode(r io.Reader) (*Config, error) {
	cfg := &Config{vals: make(map[string]string)}
	scanner := bufio.NewScanner(r)
	for lineNum := 1; scanner.Scan(); lineNum++ {
		line := strings.TrimSpace(scanner.Text())
		if line == "" || strings.HasPrefix(line, commentPrefix) {
			continue
		}
		args := strings.SplitN(line, delim, 2)
		key, value := strings.TrimSpace(args[0]), strings.TrimSpace(args[1])
		if key == "" || value == "" {
			return cfg, fmt.Errorf("no key/value pair found at line %d", lineNum)
		}
		if _, ok := cfg.vals[key]; ok {
			return cfg, fmt.Errorf("duplicate entry for key %s at line %d", key, lineNum)
		}
		cfg.vals[key] = value
	}
	if scanner.Err() != nil {
		return cfg, scanner.Err()
	}
	return cfg, nil
}

// Defaults is a convenience function that will apply a map of default key/values to a *Config, provided the keys are not already present.
func Defaults(cfg *Config, defaults map[string]string) error {
	for k, v := range defaults {
		if cfg.Get(k) == "" {
			if err := cfg.Set(k, v); err != nil {
				return err
			}
		}
	}
	return nil
}

// Missing checks for the existence of a slice of keys in a Config instance and returns a slice
// which contains keys that are missing, or nil if there are no missing keys.
func Missing(cfg *Config, required []string) []string {
	var missing []string
	for _, k := range required {
		if v := cfg.Get(k); v == "" {
			missing = append(missing, k)
		}
	}
	if len(missing) > 0 {
		return missing
	}
	return nil
}

// NewFromEnv returns a new Config instance populated from environment variables.
func NewFromEnv(keys []string) (*Config, error) {
	var buf bytes.Buffer
	for _, k := range keys {
		fmt.Fprintln(&buf, k, "=", os.Getenv(k))
	}
	cfg, err := Decode(&buf)
	return cfg, err
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/dghubble/go-twitter/twitter"
	"github.com/dghubble/oauth1"
	"strings"
	"time"
)

var (
	twitterBot *TwitterBot
)

// TwitterBot ...
type TwitterBot struct {
	ID      string
	ImgPath string
	Client  *twitter.Client
	Follows map[string]string
}

// NewTwitterBot ...
func NewTwitterBot(cfg *TwitterConfig) *TwitterBot {
	config := oauth1.NewConfig(cfg.ConsumerKey, cfg.ConsumerSecret)
	token := oauth1.NewToken(cfg.AccessToken, cfg.AccessSecret)
	httpClient := config.Client(oauth1.NoContext, token)
	client := twitter.NewClient(httpClient)
	bot := &TwitterBot{
		ID:      cfg.IDSelf,
		ImgPath: cfg.ImgPath,
		Client:  client,
		Follows: map[string]string{
			"KanColle_STAFF": "294025417",
			"komatan":        "96604067",
			"maesanpicture":  "2381595966",
			"Strangestone":   "93332575",
			"kazuharukina":   "28787294",
		},
	}
	return bot
}

func hasHashTags(s string, tags []twitter.HashtagEntity) bool {
	for _, tag := range tags {
		if s == tag.Text {
			return true
		}
	}
	return false
}

func getMedias(tweet *twitter.Tweet) []twitter.MediaEntity {
	ee := tweet.ExtendedEntities
	if ee != nil {
		return ee.Media
	}
	return tweet.Entities.Media
}

func sendPics(medias []twitter.MediaEntity) {
	for _, media := range medias {
		switch media.Type {
		case "photo":
			go qqBot.SendPics(qqBot.SendGroupMsg, media.MediaURLHttps)
		}
	}
}

func logAllTrack(msg interface{}) {
	logger.Debug(msg)
}

func (t *TwitterBot) trackTweet(tweet *twitter.Tweet) {
	if tweet.RetweetedStatus != nil {
		// logger.Debugf("ignore retweet (%s):{%s}", tweet.User.Name, tweet.Text)
		return
	}
	flattenedText := strings.Replace(tweet.Text, "\n", `\n`, -1)
	medias := getMedias(tweet)
	switch tweet.User.IDStr {
	case t.Follows["KanColle_STAFF"]:
		msg := tweet.Text
		if tweet.Truncated {
			msg = tweet.FullText
		}
		logger.Infof("(%s):{%s}", tweet.User.Name, flattenedText)
		qqBot.SendGroupMsg(tweet.User.Name + "\n" + tweet.CreatedAt + "\n" + msg)
		sendPics(medias)

	case t.Follows["komatan"]:
		sendPics(medias)

	case t.Follows["maesanpicture"]:
		if !hasHashTags("毎日五月雨", tweet.Entities.Hashtags) || (len(medias) == 0) {
			logger.Debugf("(%s):{%s}", tweet.User.Name, flattenedText)
			return
		}
		logger.Infof("(%s):{%s}", tweet.User.Name, flattenedText)
		qqBot.SendGroupMsg(tweet.Text)
		sendPics(medias)

	case t.Follows["Strangestone"]:
		if !strings.HasPrefix(tweet.Text, "月曜日のたわわ") || (len(medias) == 0) {
			logger.Debugf("(%s):{%s}", tweet.User.Name, flattenedText)
			return
		}
		logger.Infof("(%s):{%s}", tweet.User.Name, flattenedText)
		qqBot.SendGroupMsg(tweet.Text)
		sendPics(medias)

	case t.Follows["kazuharukina"]:
		if !hasHashTags("和遥キナ毎日JK企画", tweet.Entities.Hashtags) || (len(medias) == 0) {
			logger.Debugf("(%s):{%s}", tweet.User.Name, flattenedText)
			return
		}
		logger.Infof("(%s):{%s}", tweet.User.Name, flattenedText)
		sendPics(medias)

	default:
		// logger.Debugf("(%s):{%s}", tweet.User.Name, flattenedText)
	}
}

func (t *TwitterBot) selfProceedPics(medias []twitter.MediaEntity, action int) {
	for _, media := range medias {
		switch media.Type {
		case "photo":
			switch action {
			case 1:
				downloadFile(media.MediaURLHttps, t.ImgPath)
				go qqBot.SendPics(qqBot.SendSelfMsg, media.MediaURLHttps)
			case -1:
				removeFile(media.MediaURLHttps, t.ImgPath)
			}
		}
	}
}

func (t *TwitterBot) selfEvent(event *twitter.Event) {
	flattenedText := strings.Replace(event.TargetObject.Text, "\n", `\n`, -1)
	if event.Source.IDStr != t.ID {
		logger.Debugf("%s: (%s):{%s}", event.Event, event.Source.Name, flattenedText)
		return
	}
	switch event.Event {
	case "favorite":
		medias := getMedias(event.TargetObject)
		logger.Infof("favorite: (%s):{%s} %d medias", event.TargetObject.User.Name, flattenedText, len(medias))
		go t.selfProceedPics(medias, 1)
	case "unfavorite":
		medias := getMedias(event.TargetObject)
		logger.Debugf("unfavorite: (%s):{%s} %d medias", event.TargetObject.User.Name, flattenedText, len(medias))
		go t.selfProceedPics(medias, -1)
	default:
		logger.Debug(event.Event)
	}
}

func (t *TwitterBot) selfTweet(tweet *twitter.Tweet) {
	if qqBot.Config.NameGroup != "" {
		if hasHashTags(qqBot.Config.NameGroup, tweet.Entities.Hashtags) {
			if tweet.QuotedStatus != nil {
				logger.Infof("(%s):{%s}", qqBot.Config.NameGroup, strings.Replace(tweet.QuotedStatus.Text, "\n", `\n`, -1))
				sendPics(getMedias(tweet.QuotedStatus))
			} else {
				logger.Infof("(%s):{%s}", qqBot.Config.NameGroup, strings.Replace(tweet.Text, "\n", `\n`, -1))
				sendPics(getMedias(tweet))
			}
		}
	}
}

// Track ...
func (t *TwitterBot) Track() {
	follows := []string{}
	for _, value := range t.Follows {
		follows = append(follows, value)
	}
	for i := 1; ; i++ {
		demux := twitter.NewSwitchDemux()
		demux.Tweet = t.trackTweet
		filterParams := &twitter.StreamFilterParams{
			Follow: follows,
		}
		stream, err := t.Client.Streams.Filter(filterParams)
		if err != nil {
			logger.Error(err)
			time.Sleep(time.Duration(i) * time.Second)
		}
		demux.HandleChan(stream.Messages)
	}
}

// Self ...
func (t *TwitterBot) Self() {
	for i := 1; ; i++ {
		demux := twitter.NewSwitchDemux()
		demux.Event = t.selfEvent
		demux.Tweet = t.selfTweet
		userParams := &twitter.StreamUserParams{
			With: t.ID,
		}
		stream, err := t.Client.Streams.User(userParams)
		if err != nil {
			logger.Error(err)
			time.Sleep(time.Duration(i) * time.Second)
		}
		demux.HandleChan(stream.Messages)
	}
}
<commit_msg>fix tz<commit_after>package main

import (
	"github.com/dghubble/go-twitter/twitter"
	"github.com/dghubble/oauth1"
	"strings"
	"time"
)

var (
	twitterBot *TwitterBot
)

// TwitterBot ...
type TwitterBot struct {
	ID      string
	ImgPath string
	Client  *twitter.Client
	Follows map[string]string
}

// NewTwitterBot ...
func NewTwitterBot(cfg *TwitterConfig) *TwitterBot {
	config := oauth1.NewConfig(cfg.ConsumerKey, cfg.ConsumerSecret)
	token := oauth1.NewToken(cfg.AccessToken, cfg.AccessSecret)
	httpClient := config.Client(oauth1.NoContext, token)
	client := twitter.NewClient(httpClient)
	bot := &TwitterBot{
		ID:      cfg.IDSelf,
		ImgPath: cfg.ImgPath,
		Client:  client,
		Follows: map[string]string{
			"KanColle_STAFF": "294025417",
			"komatan":        "96604067",
			"maesanpicture":  "2381595966",
			"Strangestone":   "93332575",
			"kazuharukina":   "28787294",
		},
	}
	return bot
}

func hasHashTags(s string, tags []twitter.HashtagEntity) bool {
	for _, tag := range tags {
		if s == tag.Text {
			return true
		}
	}
	return false
}

func getMedias(tweet *twitter.Tweet) []twitter.MediaEntity {
	ee := tweet.ExtendedEntities
	if ee != nil {
		return ee.Media
	}
	return tweet.Entities.Media
}

func sendPics(medias []twitter.MediaEntity) {
	for _, media := range medias {
		switch media.Type {
		case "photo":
			go qqBot.SendPics(qqBot.SendGroupMsg, media.MediaURLHttps)
		}
	}
}

func logAllTrack(msg interface{}) {
	logger.Debug(msg)
}

func (t *TwitterBot) trackTweet(tweet *twitter.Tweet) {
	if tweet.RetweetedStatus != nil {
		// logger.Debugf("ignore retweet (%s):{%s}", tweet.User.Name, tweet.Text)
		return
	}
	flattenedText := strings.Replace(tweet.Text, "\n", `\n`, -1)
	medias := getMedias(tweet)
	switch tweet.User.IDStr {
	case t.Follows["KanColle_STAFF"]:
		msg := tweet.Text
		if tweet.Truncated {
			msg = tweet.FullText
		}
		logger.Infof("(%s):{%s}", tweet.User.Name, flattenedText)
		t := tweet.CreatedAt
		ct, err := tweet.CreatedAtTime()
		if err == nil {
			tz, err := time.LoadLocation("Asia/Tokyo")
			if err == nil {
				t = ct.In(tz).String()
			}
		}
		qqBot.SendGroupMsg(tweet.User.Name + "\n" + t + "\n\n" + msg)
		sendPics(medias)

	case t.Follows["komatan"]:
		sendPics(medias)

	case t.Follows["maesanpicture"]:
		if !hasHashTags("毎日五月雨", tweet.Entities.Hashtags) || (len(medias) == 0) {
			logger.Debugf("(%s):{%s}", tweet.User.Name, flattenedText)
			return
		}
		logger.Infof("(%s):{%s}", tweet.User.Name, flattenedText)
		qqBot.SendGroupMsg(tweet.Text)
		sendPics(medias)

	case t.Follows["Strangestone"]:
		if !strings.HasPrefix(tweet.Text, "月曜日のたわわ") || (len(medias) == 0) {
			logger.Debugf("(%s):{%s}", tweet.User.Name, flattenedText)
			return
		}
		logger.Infof("(%s):{%s}", tweet.User.Name, flattenedText)
		qqBot.SendGroupMsg(tweet.Text)
		sendPics(medias)

	case t.Follows["kazuharukina"]:
		if !hasHashTags("和遥キナ毎日JK企画", tweet.Entities.Hashtags) || (len(medias) == 0) {
			logger.Debugf("(%s):{%s}", tweet.User.Name, flattenedText)
			return
		}
		logger.Infof("(%s):{%s}", tweet.User.Name, flattenedText)
		sendPics(medias)

	default:
		// logger.Debugf("(%s):{%s}", tweet.User.Name, flattenedText)
	}
}

func (t *TwitterBot) selfProceedPics(medias []twitter.MediaEntity, action int) {
	for _, media := range medias {
		switch media.Type {
		case "photo":
			switch action {
			case 1:
				downloadFile(media.MediaURLHttps, t.ImgPath)
				go qqBot.SendPics(qqBot.SendSelfMsg, media.MediaURLHttps)
			case -1:
				removeFile(media.MediaURLHttps, t.ImgPath)
			}
		}
	}
}

func (t *TwitterBot) selfEvent(event *twitter.Event) {
	flattenedText := strings.Replace(event.TargetObject.Text, "\n", `\n`, -1)
	if event.Source.IDStr != t.ID {
		logger.Debugf("%s: (%s):{%s}", event.Event, event.Source.Name, flattenedText)
		return
	}
	switch event.Event {
	case "favorite":
		medias := getMedias(event.TargetObject)
		logger.Infof("favorite: (%s):{%s} %d medias", event.TargetObject.User.Name, flattenedText, len(medias))
		go t.selfProceedPics(medias, 1)
	case "unfavorite":
		medias := getMedias(event.TargetObject)
		logger.Debugf("unfavorite: (%s):{%s} %d medias", event.TargetObject.User.Name, flattenedText, len(medias))
		go t.selfProceedPics(medias, -1)
	default:
		logger.Debug(event.Event)
	}
}

func (t *TwitterBot) selfTweet(tweet *twitter.Tweet) {
	if qqBot.Config.NameGroup != "" {
		if hasHashTags(qqBot.Config.NameGroup, tweet.Entities.Hashtags) {
			if tweet.QuotedStatus != nil {
				logger.Infof("(%s):{%s}", qqBot.Config.NameGroup, strings.Replace(tweet.QuotedStatus.Text, "\n", `\n`, -1))
				sendPics(getMedias(tweet.QuotedStatus))
			} else {
				logger.Infof("(%s):{%s}", qqBot.Config.NameGroup, strings.Replace(tweet.Text, "\n", `\n`, -1))
				sendPics(getMedias(tweet))
			}
		}
	}
}

// Track ...
func (t *TwitterBot) Track() {
	follows := []string{}
	for _, value := range t.Follows {
		follows = append(follows, value)
	}
	for i := 1; ; i++ {
		demux := twitter.NewSwitchDemux()
		demux.Tweet = t.trackTweet
		filterParams := &twitter.StreamFilterParams{
			Follow: follows,
		}
		stream, err := t.Client.Streams.Filter(filterParams)
		if err != nil {
			logger.Error(err)
			time.Sleep(time.Duration(i) * time.Second)
		}
		demux.HandleChan(stream.Messages)
	}
}

// Self ...
func (t *TwitterBot) Self() {
	for i := 1; ; i++ {
		demux := twitter.NewSwitchDemux()
		demux.Event = t.selfEvent
		demux.Tweet = t.selfTweet
		userParams := &twitter.StreamUserParams{
			With: t.ID,
		}
		stream, err := t.Client.Streams.User(userParams)
		if err != nil {
			logger.Error(err)
			time.Sleep(time.Duration(i) * time.Second)
		}
		demux.HandleChan(stream.Messages)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"sms"
)

func main() {
	sms := sms.NewBulkSMSSMSSender("username", "password")
	sms.Testmode = 0
	sms.RoutingGroup = 1

	msg := "lol, hi. das ist ein test!"
	receivers := []string{"49xxxxxx"}

	err, quote := sms.GetQuote(receivers, msg)
	if err != nil {
		fmt.Println(err.String())
		return
	}
	price := quote * 3.75 * 0.01   //mad math skills calculate price in MONEYS

	//we're cheap!
	if quote > 2.0 {
	    fmt.Printf("sorry, but %.2f credits (%.2f EUR) is too much for a sms!\n", quote, price)
	    return
	}

	fmt.Printf("this sms will cost %.4f eur\n", price)

    if err := sms.Send(receivers, msg); err != nil {
        fmt.Println(err.String())
        return
    }

    fmt.Println("sms sent")

}
<commit_msg>go 1 port, added sender id support<commit_after>package main

import (
	"fmt"
	"./sms"
)

func main() {
	sms := sms.NewBulkSMSSMSSender("USERNAME", "PASSWORD")
	sms.Testmode = 0
	sms.RoutingGroup = 2
	sms.SenderId = "Tabletten"

	msg := "https://github.com/jsz/gosms is awesome! -- sent from my Go"

	receivers := []string{"49178xxxxxx", "49172xxxxxxxx", }

	//quote gives you the cost of the sms in credits
	err, quote := sms.GetQuote(receivers, msg)
	if err != nil {
		fmt.Println(err)
		return
	}
	price := quote * 3.75 * 0.01   //mad math skills calculate price in MONEYS

	//we're cheap!
	if quote > 2.0 {
	    fmt.Printf("sorry, but %.2f credits (%.2f EUR) is too much for a sms!\n", quote, price)
	    return
	}

	fmt.Printf("this sms will cost %.4f eur\n", price)

    if err := sms.Send(receivers, msg); err != nil {
        fmt.Println(err)
        return
    }

    fmt.Println("sms sent")

}
<|endoftext|>


<commit_before>package thunder

import (
	"fmt"

	"github.com/kylelemons/gousb/usb"
)

const (
	DOWN = 1 << iota
	UP
	LEFT
	RIGHT
	FIRE
	STOP
)

const (
	LED_OFF = iota
	LED_ON
)

type ThunderLauncher struct {
	device *usb.Device
	ledOn  bool
}

func GetConnectedThunderLaunchers() ([]*ThunderLauncher, error) {
	ctx := usb.NewContext()
	//defer ctx.Close()

	devices, err := ctx.ListDevices(func(d *usb.Descriptor) bool {
		return d.Vendor == 0x2123 && d.Product == 0x1010
	})
	if err != nil {
		return nil, err
	}
	if len(devices) == 0 {
		return nil, fmt.Errorf("No connected Thunder Launcher devices found")
	}

	tl := make([]*ThunderLauncher, 0)
	for _, d := range devices {
		tl = append(tl, newThunderLauncher(d))
	}

	return tl, nil
}

func newThunderLauncher(device *usb.Device) *ThunderLauncher {
	tl := &ThunderLauncher{device: device}

	tl.LedOff()

	return tl
}

func (tl *ThunderLauncher) Close() error {
	return tl.device.Close()
}

func (tl *ThunderLauncher) control(msg []byte) error {
	_, err := tl.device.Control(0x21, 0x09, 0, 0, msg)
	return err
}

func (tl *ThunderLauncher) setLed(state byte) error {
	return tl.control([]byte{3, state})
}

func (tl *ThunderLauncher) LedOff() error {
	err := tl.setLed(LED_OFF)

	if err != nil {
		tl.ledOn = false
	}

	return err
}

func (tl *ThunderLauncher) LedOn() error {
	err := tl.setLed(LED_ON)

	if err != nil {
		tl.ledOn = true
	}

	return err
}

func (tl *ThunderLauncher) do(action byte) error {
	return tl.control([]byte{2, action})
}

func (tl *ThunderLauncher) Down() error {
	return tl.do(DOWN)
}

func (tl *ThunderLauncher) Up() error {
	return tl.do(UP)
}

func (tl *ThunderLauncher) Left() error {
	return tl.do(LEFT)
}

func (tl *ThunderLauncher) Right() error {
	return tl.do(RIGHT)
}

func (tl *ThunderLauncher) Fire() error {
	return tl.do(FIRE)
}

func (tl *ThunderLauncher) Stop() error {
	return tl.do(STOP)
}
<commit_msg>Add basic documentation for the thunder package<commit_after>// Package thunder provides a means to control USB connected Dream Cheeky
// Thunder Launchers (http://dreamcheeky.com/thunder-missile-launcher).
package thunder

import (
	"fmt"

	"github.com/kylelemons/gousb/usb"
)

const (
	DOWN = 1 << iota
	UP
	LEFT
	RIGHT
	FIRE
	STOP
)

const (
	LED_OFF = iota
	LED_ON
)

// ThunderLauncher provides funcs to control a USB connected Thunder Launcher.
type ThunderLauncher struct {
	device *usb.Device
	ledOn  bool
}

// GetConnectedThunderLaunchers returns a slice of *ThunderLaunchers, each
// member of the slice corresponding to a connected Thunder Launcher.
func GetConnectedThunderLaunchers() ([]*ThunderLauncher, error) {
	ctx := usb.NewContext()
	//defer ctx.Close()

	devices, err := ctx.ListDevices(func(d *usb.Descriptor) bool {
		return d.Vendor == 0x2123 && d.Product == 0x1010
	})
	if err != nil {
		return nil, err
	}
	if len(devices) == 0 {
		return nil, fmt.Errorf("No connected Thunder Launcher devices found")
	}

	tl := make([]*ThunderLauncher, 0)
	for _, d := range devices {
		tl = append(tl, newThunderLauncher(d))
	}

	return tl, nil
}

func newThunderLauncher(device *usb.Device) *ThunderLauncher {
	tl := &ThunderLauncher{device: device}

	tl.LedOff()

	return tl
}

// Close the USB connection to the Thunder Launcher.
func (tl *ThunderLauncher) Close() error {
	return tl.device.Close()
}

func (tl *ThunderLauncher) control(msg []byte) error {
	_, err := tl.device.Control(0x21, 0x09, 0, 0, msg)
	return err
}

func (tl *ThunderLauncher) setLed(state byte) error {
	return tl.control([]byte{3, state})
}

// Turn off the Thunder Launcher's LED.
func (tl *ThunderLauncher) LedOff() error {
	err := tl.setLed(LED_OFF)

	if err != nil {
		tl.ledOn = false
	}

	return err
}

// Turn on the Thunder Launcher's LED.
func (tl *ThunderLauncher) LedOn() error {
	err := tl.setLed(LED_ON)

	if err != nil {
		tl.ledOn = true
	}

	return err
}

func (tl *ThunderLauncher) do(action byte) error {
	return tl.control([]byte{2, action})
}

// Down starts moving the Thunder Launcher down.
func (tl *ThunderLauncher) Down() error {
	return tl.do(DOWN)
}

// Up starts moving the Thunder Launcher up.
func (tl *ThunderLauncher) Up() error {
	return tl.do(UP)
}

// Left starts moving the Thunder Launcher left.
func (tl *ThunderLauncher) Left() error {
	return tl.do(LEFT)
}

// Right starts moving the Thunder Launcher right.
func (tl *ThunderLauncher) Right() error {
	return tl.do(RIGHT)
}

// Fire starts the process of firing the Thunder Launcher.
func (tl *ThunderLauncher) Fire() error {
	return tl.do(FIRE)
}

// Stop ceases the last command sent to the Thunder Launcher. Only LedOff and
// LedOn don't require Stop to be called after their invocation.
func (tl *ThunderLauncher) Stop() error {
	return tl.do(STOP)
}
<|endoftext|>