<commit_before>package baa

import (
	"errors"
	"log"
	"net/http"
	"os"
	"strings"
	"sync"
)

const (
	// DEV mode
	DEV = "development"
	// PROD mode
	PROD = "production"
	// TEST mode
	TEST = "test"
)

// Env default application runtime environment
var Env string

// Baa provlider an application
type Baa struct {
	debug           bool
	name            string
	di              DIer
	router          Router
	pool            sync.Pool
	errorHandler    ErrorHandleFunc
	notFoundHandler HandlerFunc
	middleware      []HandlerFunc
}

// Middleware middleware handler
type Middleware interface{}

// HandlerFunc context handler func
type HandlerFunc func(*Context)

// ErrorHandleFunc HTTP error handleFunc
type ErrorHandleFunc func(error, *Context)

// appInstances storage application instances
var appInstances map[string]*Baa

// defaultAppName default application name
const defaultAppName = "_default_"

// New create a baa application without any config.
func New() *Baa {
	b := new(Baa)
	b.middleware = make([]HandlerFunc, 0)
	b.pool = sync.Pool{
		New: func() interface{} {
			return NewContext(nil, nil, b)
		},
	}
	if Env != PROD {
		b.debug = true
	}
	b.SetDIer(NewDI())
	b.SetDI("router", NewTree(b))
	b.SetDI("logger", log.New(os.Stderr, "[Baa] ", log.LstdFlags))
	b.SetDI("render", newRender())
	b.SetNotFound(b.DefaultNotFoundHandler)
	return b
}

// Instance register or returns named application
func Instance(name string) *Baa {
	if name == "" {
		name = defaultAppName
	}
	if appInstances[name] == nil {
		appInstances[name] = New()
		appInstances[name].name = name
	}
	return appInstances[name]
}

// Default initial a default app then returns
func Default() *Baa {
	return Instance(defaultAppName)
}

// Server returns the internal *http.Server.
func (b *Baa) Server(addr string) *http.Server {
	s := &http.Server{Addr: addr}
	return s
}

// Run runs a server.
func (b *Baa) Run(addr string) {
	b.run(b.Server(addr))
}

// RunTLS runs a server with TLS configuration.
func (b *Baa) RunTLS(addr, certfile, keyfile string) {
	b.run(b.Server(addr), certfile, keyfile)
}

// RunServer runs a custom server.
func (b *Baa) RunServer(s *http.Server) {
	b.run(s)
}

// RunTLSServer runs a custom server with TLS configuration.
func (b *Baa) RunTLSServer(s *http.Server, crtFile, keyFile string) {
	b.run(s, crtFile, keyFile)
}

func (b *Baa) run(s *http.Server, files ...string) {
	s.Handler = b
	b.Logger().Printf("Run mode: %s", Env)
	if len(files) == 0 {
		b.Logger().Printf("Listen %s", s.Addr)
		b.Logger().Fatal(s.ListenAndServe())
	} else if len(files) == 2 {
		b.Logger().Printf("Listen %s with TLS", s.Addr)
		b.Logger().Fatal(s.ListenAndServeTLS(files[0], files[1]))
	} else {
		panic("invalid TLS configuration")
	}
}

func (b *Baa) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	c := b.pool.Get().(*Context)
	c.Reset(w, r)

	// build handler chain
	h := b.Router().Match(r.Method, r.URL.Path, c)
	// notFound
	if h == nil {
		c.handlers = append(c.handlers, b.notFoundHandler)
	} else {
		c.handlers = append(c.handlers, h...)
	}

	c.Next()

	b.pool.Put(c)
}

// SetDIer set baa di
func (b *Baa) SetDIer(v DIer) {
	b.di = v
}

// SetDebug set baa debug
func (b *Baa) SetDebug(v bool) {
	b.debug = v
}

// Debug returns baa debug state
func (b *Baa) Debug() bool {
	return b.debug
}

// Logger return baa logger
func (b *Baa) Logger() Logger {
	return b.GetDI("logger").(Logger)
}

// Render return baa render
func (b *Baa) Render() Renderer {
	return b.GetDI("render").(Renderer)
}

// Router return baa router
func (b *Baa) Router() Router {
	if b.router == nil {
		b.router = b.GetDI("router").(Router)
	}
	return b.router
}

// Use registers a middleware
func (b *Baa) Use(m ...Middleware) {
	for i := range m {
		if m[i] != nil {
			b.middleware = append(b.middleware, wrapMiddleware(m[i]))
		}
	}
}

// SetDI registers a dependency injection
func (b *Baa) SetDI(name string, h interface{}) {
	switch name {
	case "logger":
		if _, ok := h.(Logger); !ok {
			panic("DI logger must be implement interface baa.Logger")
		}
	case "render":
		if _, ok := h.(Renderer); !ok {
			panic("DI render must be implement interface baa.Renderer")
		}
	case "router":
		if _, ok := h.(Router); !ok {
			panic("DI router must be implement interface baa.Router")
		}
	}
	b.di.Set(name, h)
}

// GetDI fetch a registered dependency injection
func (b *Baa) GetDI(name string) interface{} {
	return b.di.Get(name)
}

// Static set static file route
// h used for set Expries ...
func (b *Baa) Static(prefix string, dir string, index bool, h HandlerFunc) {
	if prefix == "" {
		panic("baa.Static prefix can not be empty")
	}
	if dir == "" {
		panic("baa.Static dir can not be empty")
	}
	b.Get(prefix+"*", newStatic(prefix, dir, index, h))
}

// StaticFile shortcut for serve file
func (b *Baa) StaticFile(pattern string, path string) RouteNode {
	return b.Get(pattern, func(c *Context) {
		if err := serveFile(path, c); err != nil {
			c.Error(err)
		}
	})
}

// SetAutoHead sets the value who determines whether add HEAD method automatically
// when GET method is added. Combo router will not be affected by this value.
func (b *Baa) SetAutoHead(v bool) {
	b.Router().SetAutoHead(v)
}

// SetAutoTrailingSlash optional trailing slash.
func (b *Baa) SetAutoTrailingSlash(v bool) {
	b.Router().SetAutoTrailingSlash(v)
}

// Route is a shortcut for same handlers but different HTTP methods.
//
// Example:
// 		baa.Route("/", "GET,POST", h)
func (b *Baa) Route(pattern, methods string, h ...HandlerFunc) RouteNode {
	var ru RouteNode
	var ms []string
	if methods == "*" {
		for m := range RouterMethods {
			ms = append(ms, m)
		}
	} else {
		ms = strings.Split(methods, ",")
	}
	for _, m := range ms {
		ru = b.Router().Add(strings.TrimSpace(m), pattern, h)
	}
	return ru
}

// Group registers a list of same prefix route
func (b *Baa) Group(pattern string, f func(), h ...HandlerFunc) {
	b.Router().GroupAdd(pattern, f, h)
}

// Any is a shortcut for b.Router().handle("*", pattern, handlers)
func (b *Baa) Any(pattern string, h ...HandlerFunc) RouteNode {
	var ru RouteNode
	for m := range RouterMethods {
		ru = b.Router().Add(m, pattern, h)
	}
	return ru
}

// Delete is a shortcut for b.Route(pattern, "DELETE", handlers)
func (b *Baa) Delete(pattern string, h ...HandlerFunc) RouteNode {
	return b.Router().Add("DELETE", pattern, h)
}

// Get is a shortcut for b.Route(pattern, "GET", handlers)
func (b *Baa) Get(pattern string, h ...HandlerFunc) RouteNode {
	return b.Router().Add("GET", pattern, h)
}

// Head is a shortcut forb.Route(pattern, "Head", handlers)
func (b *Baa) Head(pattern string, h ...HandlerFunc) RouteNode {
	return b.Router().Add("HEAD", pattern, h)
}

// Options is a shortcut for b.Route(pattern, "Options", handlers)
func (b *Baa) Options(pattern string, h ...HandlerFunc) RouteNode {
	return b.Router().Add("OPTIONS", pattern, h)
}

// Patch is a shortcut for b.Route(pattern, "PATCH", handlers)
func (b *Baa) Patch(pattern string, h ...HandlerFunc) RouteNode {
	return b.Router().Add("PATCH", pattern, h)
}

// Post is a shortcut for b.Route(pattern, "POST", handlers)
func (b *Baa) Post(pattern string, h ...HandlerFunc) RouteNode {
	return b.Router().Add("POST", pattern, h)
}

// Put is a shortcut for b.Route(pattern, "Put", handlers)
func (b *Baa) Put(pattern string, h ...HandlerFunc) RouteNode {
	return b.Router().Add("PUT", pattern, h)
}

// SetNotFound set not found route handler
func (b *Baa) SetNotFound(h HandlerFunc) {
	b.notFoundHandler = h
}

// NotFound execute not found handler
func (b *Baa) NotFound(c *Context) {
	if b.notFoundHandler != nil {
		b.notFoundHandler(c)
		return
	}
	http.NotFound(c.Resp, c.Req)
}

// SetError set error handler
func (b *Baa) SetError(h ErrorHandleFunc) {
	b.errorHandler = h
}

// Error execute internal error handler
func (b *Baa) Error(err error, c *Context) {
	if err == nil {
		err = errors.New("Internal Server Error")
	}
	if b.errorHandler != nil {
		b.errorHandler(err, c)
		return
	}
	code := http.StatusInternalServerError
	msg := http.StatusText(code)
	if b.debug {
		msg = err.Error()
	}
	b.Logger().Println(err)
	http.Error(c.Resp, msg, code)
}

// DefaultNotFoundHandler invokes the default HTTP error handler.
func (b *Baa) DefaultNotFoundHandler(c *Context) {
	code := http.StatusNotFound
	msg := http.StatusText(code)
	http.Error(c.Resp, msg, code)
}

// URLFor use named route return format url
func (b *Baa) URLFor(name string, args ...interface{}) string {
	return b.Router().URLFor(name, args...)
}

// wrapMiddleware wraps middleware.
func wrapMiddleware(m Middleware) HandlerFunc {
	switch m := m.(type) {
	case HandlerFunc:
		return m
	case func(*Context):
		return m
	case http.Handler, http.HandlerFunc:
		return WrapHandlerFunc(func(c *Context) {
			m.(http.Handler).ServeHTTP(c.Resp, c.Req)
		})
	case func(http.ResponseWriter, *http.Request):
		return WrapHandlerFunc(func(c *Context) {
			m(c.Resp, c.Req)
		})
	default:
		panic("unknown middleware")
	}
}

// WrapHandlerFunc wrap for context handler chain
func WrapHandlerFunc(h HandlerFunc) HandlerFunc {
	return func(c *Context) {
		h(c)
		c.Next()
	}
}

func init() {
	appInstances = make(map[string]*Baa)
	Env = os.Getenv("BAA_ENV")
	if Env == "" {
		Env = DEV
	}
}
<commit_msg>add websocket for router<commit_after>package baa

import (
	"errors"
	"log"
	"net/http"
	"os"
	"strings"
	"sync"

	"github.com/gorilla/websocket"
)

const (
	// DEV mode
	DEV = "development"
	// PROD mode
	PROD = "production"
	// TEST mode
	TEST = "test"
)

// Env default application runtime environment
var Env string

// Baa provlider an application
type Baa struct {
	debug           bool
	name            string
	di              DIer
	router          Router
	pool            sync.Pool
	errorHandler    ErrorHandleFunc
	notFoundHandler HandlerFunc
	middleware      []HandlerFunc
}

// Middleware middleware handler
type Middleware interface{}

// HandlerFunc context handler func
type HandlerFunc func(*Context)

// ErrorHandleFunc HTTP error handleFunc
type ErrorHandleFunc func(error, *Context)

// appInstances storage application instances
var appInstances map[string]*Baa

// defaultAppName default application name
const defaultAppName = "_default_"

// New create a baa application without any config.
func New() *Baa {
	b := new(Baa)
	b.middleware = make([]HandlerFunc, 0)
	b.pool = sync.Pool{
		New: func() interface{} {
			return NewContext(nil, nil, b)
		},
	}
	if Env != PROD {
		b.debug = true
	}
	b.SetDIer(NewDI())
	b.SetDI("router", NewTree(b))
	b.SetDI("logger", log.New(os.Stderr, "[Baa] ", log.LstdFlags))
	b.SetDI("render", newRender())
	b.SetNotFound(b.DefaultNotFoundHandler)
	return b
}

// Instance register or returns named application
func Instance(name string) *Baa {
	if name == "" {
		name = defaultAppName
	}
	if appInstances[name] == nil {
		appInstances[name] = New()
		appInstances[name].name = name
	}
	return appInstances[name]
}

// Default initial a default app then returns
func Default() *Baa {
	return Instance(defaultAppName)
}

// Server returns the internal *http.Server.
func (b *Baa) Server(addr string) *http.Server {
	s := &http.Server{Addr: addr}
	return s
}

// Run runs a server.
func (b *Baa) Run(addr string) {
	b.run(b.Server(addr))
}

// RunTLS runs a server with TLS configuration.
func (b *Baa) RunTLS(addr, certfile, keyfile string) {
	b.run(b.Server(addr), certfile, keyfile)
}

// RunServer runs a custom server.
func (b *Baa) RunServer(s *http.Server) {
	b.run(s)
}

// RunTLSServer runs a custom server with TLS configuration.
func (b *Baa) RunTLSServer(s *http.Server, crtFile, keyFile string) {
	b.run(s, crtFile, keyFile)
}

func (b *Baa) run(s *http.Server, files ...string) {
	s.Handler = b
	b.Logger().Printf("Run mode: %s", Env)
	if len(files) == 0 {
		b.Logger().Printf("Listen %s", s.Addr)
		b.Logger().Fatal(s.ListenAndServe())
	} else if len(files) == 2 {
		b.Logger().Printf("Listen %s with TLS", s.Addr)
		b.Logger().Fatal(s.ListenAndServeTLS(files[0], files[1]))
	} else {
		panic("invalid TLS configuration")
	}
}

func (b *Baa) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	c := b.pool.Get().(*Context)
	c.Reset(w, r)

	// build handler chain
	h := b.Router().Match(r.Method, r.URL.Path, c)
	// notFound
	if h == nil {
		c.handlers = append(c.handlers, b.notFoundHandler)
	} else {
		c.handlers = append(c.handlers, h...)
	}

	c.Next()

	b.pool.Put(c)
}

// SetDIer set baa di
func (b *Baa) SetDIer(v DIer) {
	b.di = v
}

// SetDebug set baa debug
func (b *Baa) SetDebug(v bool) {
	b.debug = v
}

// Debug returns baa debug state
func (b *Baa) Debug() bool {
	return b.debug
}

// Logger return baa logger
func (b *Baa) Logger() Logger {
	return b.GetDI("logger").(Logger)
}

// Render return baa render
func (b *Baa) Render() Renderer {
	return b.GetDI("render").(Renderer)
}

// Router return baa router
func (b *Baa) Router() Router {
	if b.router == nil {
		b.router = b.GetDI("router").(Router)
	}
	return b.router
}

// Use registers a middleware
func (b *Baa) Use(m ...Middleware) {
	for i := range m {
		if m[i] != nil {
			b.middleware = append(b.middleware, wrapMiddleware(m[i]))
		}
	}
}

// SetDI registers a dependency injection
func (b *Baa) SetDI(name string, h interface{}) {
	switch name {
	case "logger":
		if _, ok := h.(Logger); !ok {
			panic("DI logger must be implement interface baa.Logger")
		}
	case "render":
		if _, ok := h.(Renderer); !ok {
			panic("DI render must be implement interface baa.Renderer")
		}
	case "router":
		if _, ok := h.(Router); !ok {
			panic("DI router must be implement interface baa.Router")
		}
	}
	b.di.Set(name, h)
}

// GetDI fetch a registered dependency injection
func (b *Baa) GetDI(name string) interface{} {
	return b.di.Get(name)
}

// Static set static file route
// h used for set Expries ...
func (b *Baa) Static(prefix string, dir string, index bool, h HandlerFunc) {
	if prefix == "" {
		panic("baa.Static prefix can not be empty")
	}
	if dir == "" {
		panic("baa.Static dir can not be empty")
	}
	b.Get(prefix+"*", newStatic(prefix, dir, index, h))
}

// StaticFile shortcut for serve file
func (b *Baa) StaticFile(pattern string, path string) RouteNode {
	return b.Get(pattern, func(c *Context) {
		if err := serveFile(path, c); err != nil {
			c.Error(err)
		}
	})
}

// SetAutoHead sets the value who determines whether add HEAD method automatically
// when GET method is added. Combo router will not be affected by this value.
func (b *Baa) SetAutoHead(v bool) {
	b.Router().SetAutoHead(v)
}

// SetAutoTrailingSlash optional trailing slash.
func (b *Baa) SetAutoTrailingSlash(v bool) {
	b.Router().SetAutoTrailingSlash(v)
}

// Route is a shortcut for same handlers but different HTTP methods.
//
// Example:
// 		baa.Route("/", "GET,POST", h)
func (b *Baa) Route(pattern, methods string, h ...HandlerFunc) RouteNode {
	var ru RouteNode
	var ms []string
	if methods == "*" {
		for m := range RouterMethods {
			ms = append(ms, m)
		}
	} else {
		ms = strings.Split(methods, ",")
	}
	for _, m := range ms {
		ru = b.Router().Add(strings.TrimSpace(m), pattern, h)
	}
	return ru
}

// Group registers a list of same prefix route
func (b *Baa) Group(pattern string, f func(), h ...HandlerFunc) {
	b.Router().GroupAdd(pattern, f, h)
}

// Any is a shortcut for b.Router().handle("*", pattern, handlers)
func (b *Baa) Any(pattern string, h ...HandlerFunc) RouteNode {
	var ru RouteNode
	for m := range RouterMethods {
		ru = b.Router().Add(m, pattern, h)
	}
	return ru
}

// Delete is a shortcut for b.Route(pattern, "DELETE", handlers)
func (b *Baa) Delete(pattern string, h ...HandlerFunc) RouteNode {
	return b.Router().Add("DELETE", pattern, h)
}

// Get is a shortcut for b.Route(pattern, "GET", handlers)
func (b *Baa) Get(pattern string, h ...HandlerFunc) RouteNode {
	return b.Router().Add("GET", pattern, h)
}

// Head is a shortcut forb.Route(pattern, "Head", handlers)
func (b *Baa) Head(pattern string, h ...HandlerFunc) RouteNode {
	return b.Router().Add("HEAD", pattern, h)
}

// Options is a shortcut for b.Route(pattern, "Options", handlers)
func (b *Baa) Options(pattern string, h ...HandlerFunc) RouteNode {
	return b.Router().Add("OPTIONS", pattern, h)
}

// Patch is a shortcut for b.Route(pattern, "PATCH", handlers)
func (b *Baa) Patch(pattern string, h ...HandlerFunc) RouteNode {
	return b.Router().Add("PATCH", pattern, h)
}

// Post is a shortcut for b.Route(pattern, "POST", handlers)
func (b *Baa) Post(pattern string, h ...HandlerFunc) RouteNode {
	return b.Router().Add("POST", pattern, h)
}

// Put is a shortcut for b.Route(pattern, "Put", handlers)
func (b *Baa) Put(pattern string, h ...HandlerFunc) RouteNode {
	return b.Router().Add("PUT", pattern, h)
}

// Websocket register a websocket router handler
func (b *Baa) Websocket(pattern string, h func(*websocket.Conn)) RouteNode {
	var upgrader = websocket.Upgrader{
		ReadBufferSize:    4096,
		WriteBufferSize:   4096,
		EnableCompression: true,
		CheckOrigin: func(r *http.Request) bool {
			return true
		},
	}

	return b.Route(pattern, "GET,POST", func(c *Context) {
		conn, err := upgrader.Upgrade(c.Resp, c.Req, nil)
		if err != nil {
			b.Logger().Panicf("websocket upgrade connection error: %v", err)
			return
		}
		h(conn)
	})
}

// SetNotFound set not found route handler
func (b *Baa) SetNotFound(h HandlerFunc) {
	b.notFoundHandler = h
}

// NotFound execute not found handler
func (b *Baa) NotFound(c *Context) {
	if b.notFoundHandler != nil {
		b.notFoundHandler(c)
		return
	}
	http.NotFound(c.Resp, c.Req)
}

// SetError set error handler
func (b *Baa) SetError(h ErrorHandleFunc) {
	b.errorHandler = h
}

// Error execute internal error handler
func (b *Baa) Error(err error, c *Context) {
	if err == nil {
		err = errors.New("Internal Server Error")
	}
	if b.errorHandler != nil {
		b.errorHandler(err, c)
		return
	}
	code := http.StatusInternalServerError
	msg := http.StatusText(code)
	if b.debug {
		msg = err.Error()
	}
	b.Logger().Println(err)
	http.Error(c.Resp, msg, code)
}

// DefaultNotFoundHandler invokes the default HTTP error handler.
func (b *Baa) DefaultNotFoundHandler(c *Context) {
	code := http.StatusNotFound
	msg := http.StatusText(code)
	http.Error(c.Resp, msg, code)
}

// URLFor use named route return format url
func (b *Baa) URLFor(name string, args ...interface{}) string {
	return b.Router().URLFor(name, args...)
}

// wrapMiddleware wraps middleware.
func wrapMiddleware(m Middleware) HandlerFunc {
	switch m := m.(type) {
	case HandlerFunc:
		return m
	case func(*Context):
		return m
	case http.Handler, http.HandlerFunc:
		return WrapHandlerFunc(func(c *Context) {
			m.(http.Handler).ServeHTTP(c.Resp, c.Req)
		})
	case func(http.ResponseWriter, *http.Request):
		return WrapHandlerFunc(func(c *Context) {
			m(c.Resp, c.Req)
		})
	default:
		panic("unknown middleware")
	}
}

// WrapHandlerFunc wrap for context handler chain
func WrapHandlerFunc(h HandlerFunc) HandlerFunc {
	return func(c *Context) {
		h(c)
		c.Next()
	}
}

func init() {
	appInstances = make(map[string]*Baa)
	Env = os.Getenv("BAA_ENV")
	if Env == "" {
		Env = DEV
	}
}
<|endoftext|>


<commit_before>package syndicate

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path"
	"sync"
	"time"

	"golang.org/x/net/context"

	"github.com/gholt/ring"
	pb "github.com/pandemicsyn/syndicate/api/proto"
)

type ringslave struct {
	sync.RWMutex
	version int64
	last    time.Time
	r       ring.Ring
	b       *ring.Builder
	spath   string
}

func (s *ringslave) Store(c context.Context, r *pb.RingMsg) (*pb.StoreResult, error) {
	log.Println("Got store request:", r.Version, r.Deadline, r.Rollback)
	s.Lock()
	defer s.Unlock()

	s.last = time.Now()
	bs, rs, err := s.saveRingAndBuilderBytes(&r.Ring, &r.Builder, r.Version)
	if err != nil {
		return &pb.StoreResult{
			Version: r.Version,
			Ring:    rs,
			Builder: bs,
			ErrMsg:  fmt.Sprintf("Encountered error during save: %s", err),
		}, nil
	}
	s.version = r.Version

	_, builder, err := ring.RingOrBuilder(path.Join(s.spath, fmt.Sprintf("%d.oort.builder", r.Version)))
	if err != nil || builder == nil {
		return &pb.StoreResult{
			Version: r.Version,
			Ring:    false,
			Builder: false,
			ErrMsg:  fmt.Sprintf("Encountered error during builder load: %s", err),
		}, nil

	}
	oldbuilder := s.b
	s.b = builder

	ring, _, err := ring.RingOrBuilder(path.Join(s.spath, fmt.Sprintf("%d.oort.ring.gz", r.Version)))
	if err != nil || ring == nil || ring.Version() != r.Version {
		//restore builder
		s.b = oldbuilder
		return &pb.StoreResult{
			Version: r.Version,
			Ring:    false,
			Builder: false,
			ErrMsg:  fmt.Sprintf("Encountered error during ring load: %s", err),
		}, nil

	}
	s.r = ring
	s.version = r.Version
	return &pb.StoreResult{Version: r.Version, Ring: true, Builder: true, ErrMsg: ""}, nil
}

func (s *ringslave) Revert(c context.Context, r *pb.RingMsg) (*pb.StoreResult, error) {
	log.Println("Got revert request to revert to version:", r.Version)
	return &pb.StoreResult{}, nil
}

func (s *ringslave) Status(c context.Context, r *pb.StatusRequest) (*pb.StatusMsg, error) {
	log.Printf("Current status: %+v\n", s)
	return &pb.StatusMsg{}, nil
}

func writeBytes(filename string, b *[]byte) error {
	dir, name := path.Split(filename)
	if dir == "" {
		dir = "."
	}
	f, err := ioutil.TempFile(dir, name+".")
	if err != nil {
		return err
	}
	tmp := f.Name()
	s, err := f.Write(*b)
	if err != nil {
		f.Close()
		return err
	}
	if s != len(*b) {
		f.Close()
		return err
	}
	if err = f.Close(); err != nil {
		return err
	}
	return os.Rename(tmp, filename)
}

func (s *ringslave) saveRingAndBuilderBytes(ring, builder *[]byte, version int64) (builderstatus, ringstatus bool, err error) {
	err = writeBytes(path.Join(s.spath, fmt.Sprintf("%d.oort.builder.gz", version)), builder)
	if err != nil {
		return false, false, err
	}
	err = writeBytes(path.Join(s.spath, fmt.Sprintf("%d.oort.ring.gz", version)), ring)
	if err != nil {
		return true, false, err
	}
	return true, true, err
}

func (s *ringslave) Setup(c context.Context, r *pb.RingMsg) (*pb.StoreResult, error) {
	log.Println("Got setup request:", r.Version, r.Deadline, r.Rollback)
	s.Lock()
	defer s.Unlock()

	s.last = time.Now()
	bs, rs, err := s.saveRingAndBuilderBytes(&r.Ring, &r.Builder, r.Version)
	if err != nil {
		return &pb.StoreResult{
			Version: r.Version,
			Ring:    rs,
			Builder: bs,
			ErrMsg:  fmt.Sprintf("Encountered error during save: %s", err),
		}, nil
	}
	s.version = r.Version

	_, builder, err := ring.RingOrBuilder(path.Join(s.spath, fmt.Sprintf("%d.oort.builder.gz", r.Version)))
	if err != nil || builder == nil {
		return &pb.StoreResult{
			Version: r.Version,
			Ring:    false,
			Builder: false,
			ErrMsg:  fmt.Sprintf("Encountered error during builder load: %s", err),
		}, nil

	}
	oldbuilder := s.b
	s.b = builder

	ring, _, err := ring.RingOrBuilder(path.Join(s.spath, fmt.Sprintf("%d.oort.ring.gz", r.Version)))
	if err != nil || ring == nil || ring.Version() != r.Version {
		//restore builder
		s.b = oldbuilder
		return &pb.StoreResult{
			Version: r.Version,
			Ring:    false,
			Builder: false,
			ErrMsg:  fmt.Sprintf("Encountered error during ring load: %s", err),
		}, nil

	}
	s.r = ring
	s.version = r.Version
	return &pb.StoreResult{Version: r.Version, Ring: true, Builder: true, ErrMsg: ""}, nil
}
<commit_msg>Attempt to create parent dirs if missing<commit_after>package syndicate

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path"
	"sync"
	"time"

	"golang.org/x/net/context"

	"github.com/gholt/ring"
	pb "github.com/pandemicsyn/syndicate/api/proto"
)

type ringslave struct {
	sync.RWMutex
	version int64
	last    time.Time
	r       ring.Ring
	b       *ring.Builder
	spath   string
}

func (s *ringslave) Store(c context.Context, r *pb.RingMsg) (*pb.StoreResult, error) {
	log.Println("Got store request:", r.Version, r.Deadline, r.Rollback)
	s.Lock()
	defer s.Unlock()

	s.last = time.Now()
	bs, rs, err := s.saveRingAndBuilderBytes(&r.Ring, &r.Builder, r.Version)
	if err != nil {
		return &pb.StoreResult{
			Version: r.Version,
			Ring:    rs,
			Builder: bs,
			ErrMsg:  fmt.Sprintf("Encountered error during save: %s", err),
		}, nil
	}
	s.version = r.Version

	_, builder, err := ring.RingOrBuilder(path.Join(s.spath, fmt.Sprintf("%d.oort.builder", r.Version)))
	if err != nil || builder == nil {
		return &pb.StoreResult{
			Version: r.Version,
			Ring:    false,
			Builder: false,
			ErrMsg:  fmt.Sprintf("Encountered error during builder load: %s", err),
		}, nil

	}
	oldbuilder := s.b
	s.b = builder

	ring, _, err := ring.RingOrBuilder(path.Join(s.spath, fmt.Sprintf("%d.oort.ring.gz", r.Version)))
	if err != nil || ring == nil || ring.Version() != r.Version {
		//restore builder
		s.b = oldbuilder
		return &pb.StoreResult{
			Version: r.Version,
			Ring:    false,
			Builder: false,
			ErrMsg:  fmt.Sprintf("Encountered error during ring load: %s", err),
		}, nil

	}
	s.r = ring
	s.version = r.Version
	return &pb.StoreResult{Version: r.Version, Ring: true, Builder: true, ErrMsg: ""}, nil
}

func (s *ringslave) Revert(c context.Context, r *pb.RingMsg) (*pb.StoreResult, error) {
	log.Println("Got revert request to revert to version:", r.Version)
	return &pb.StoreResult{}, nil
}

func (s *ringslave) Status(c context.Context, r *pb.StatusRequest) (*pb.StatusMsg, error) {
	log.Printf("Current status: %+v\n", s)
	return &pb.StatusMsg{}, nil
}

func writeBytes(filename string, b *[]byte) error {
	dir, name := path.Split(filename)
	if dir == "" {
		dir = "."
	}
	_ = os.MkdirAll(dir, 0755)
	f, err := ioutil.TempFile(dir, name+".")
	if err != nil {
		return err
	}
	tmp := f.Name()
	s, err := f.Write(*b)
	if err != nil {
		f.Close()
		return err
	}
	if s != len(*b) {
		f.Close()
		return err
	}
	if err = f.Close(); err != nil {
		return err
	}
	return os.Rename(tmp, filename)
}

func (s *ringslave) saveRingAndBuilderBytes(ring, builder *[]byte, version int64) (builderstatus, ringstatus bool, err error) {
	err = writeBytes(path.Join(s.spath, fmt.Sprintf("%d.oort.builder.gz", version)), builder)
	if err != nil {
		return false, false, err
	}
	err = writeBytes(path.Join(s.spath, fmt.Sprintf("%d.oort.ring.gz", version)), ring)
	if err != nil {
		return true, false, err
	}
	return true, true, err
}

func (s *ringslave) Setup(c context.Context, r *pb.RingMsg) (*pb.StoreResult, error) {
	log.Println("Got setup request:", r.Version, r.Deadline, r.Rollback)
	s.Lock()
	defer s.Unlock()

	s.last = time.Now()
	bs, rs, err := s.saveRingAndBuilderBytes(&r.Ring, &r.Builder, r.Version)
	if err != nil {
		return &pb.StoreResult{
			Version: r.Version,
			Ring:    rs,
			Builder: bs,
			ErrMsg:  fmt.Sprintf("Encountered error during save: %s", err),
		}, nil
	}
	s.version = r.Version

	_, builder, err := ring.RingOrBuilder(path.Join(s.spath, fmt.Sprintf("%d.oort.builder.gz", r.Version)))
	if err != nil || builder == nil {
		return &pb.StoreResult{
			Version: r.Version,
			Ring:    false,
			Builder: false,
			ErrMsg:  fmt.Sprintf("Encountered error during builder load: %s", err),
		}, nil

	}
	oldbuilder := s.b
	s.b = builder

	ring, _, err := ring.RingOrBuilder(path.Join(s.spath, fmt.Sprintf("%d.oort.ring.gz", r.Version)))
	if err != nil || ring == nil || ring.Version() != r.Version {
		//restore builder
		s.b = oldbuilder
		return &pb.StoreResult{
			Version: r.Version,
			Ring:    false,
			Builder: false,
			ErrMsg:  fmt.Sprintf("Encountered error during ring load: %s", err),
		}, nil

	}
	s.r = ring
	s.version = r.Version
	return &pb.StoreResult{Version: r.Version, Ring: true, Builder: true, ErrMsg: ""}, nil
}
<|endoftext|>


<commit_before>// Copyright ©2015 The bíogo Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package index provides common code for CSI and tabix BGZF indexing.
package index

import (
	"io"

	"github.com/biogo/hts/bgzf"
)

// ReferenceStats holds mapping statistics for a genomic reference.
type ReferenceStats struct {
	// Chunk is the span of the indexed BGZF
	// holding alignments to the reference.
	Chunk bgzf.Chunk

	// Mapped is the count of mapped reads.
	Mapped uint64

	// Unmapped is the count of unmapped reads.
	Unmapped uint64
}

// Reader wraps a bgzf.Reader to provide a mechanism to read a selection of
// BGZF chunks.
type ChunkReader struct {
	r *bgzf.Reader

	wasBlocked bool

	chunks []bgzf.Chunk
}

// NewChunkReader returns a ChunkReader to read from r, limiting the reads to
// the provided chunks. The provided bgzf.Reader will be put into Blocked mode.
func NewChunkReader(r *bgzf.Reader, chunks []bgzf.Chunk) (*ChunkReader, error) {
	b := r.Blocked
	r.Blocked = true
	if len(chunks) != 0 {
		err := r.Seek(chunks[0].Begin)
		if err != nil {
			return nil, err
		}
	}
	return &ChunkReader{r: r, wasBlocked: b, chunks: chunks}, nil
}

// Read satisfies the io.Reader interface.
func (r *ChunkReader) Read(p []byte) (int, error) {
	if len(r.chunks) == 0 && vOffset(r.r.LastChunk().End) >= vOffset(r.chunks[0].End) {
		return 0, io.EOF
	}

	// Ensure the byte slice does not extend beyond the end of
	// the current chunk. We do not need to consider reading
	// beyond the end of the block because the bgzf.Reader is in
	// blocked mode and so will stop there anyway.
	if r.r.LastChunk().End.File == r.chunks[0].End.File {
		p = p[:r.chunks[0].End.Block-r.r.LastChunk().End.Block]
	}

	n, err := r.r.Read(p)
	if err != nil {
		if n != 0 && err == io.EOF {
			err = nil
		}
		return n, err
	}
	if len(r.chunks) != 0 && vOffset(r.r.LastChunk().End) >= vOffset(r.chunks[0].End) {
		err = r.r.Seek(r.chunks[0].Begin)
		r.chunks = r.chunks[1:]
	}
	return n, err
}

func vOffset(o bgzf.Offset) int64 {
	return o.File<<16 | int64(o.Block)
}

// Close returns the bgzf.Reader to its original blocking mode and releases it.
// The bgzf.Reader is not closed.
func (r *ChunkReader) Close() error {
	r.r.Blocked = r.wasBlocked
	r.r = nil
	return nil
}
<commit_msg>bgzf/index: fix end of chunks logic<commit_after>// Copyright ©2015 The bíogo Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package index provides common code for CSI and tabix BGZF indexing.
package index

import (
	"io"

	"github.com/biogo/hts/bgzf"
)

// ReferenceStats holds mapping statistics for a genomic reference.
type ReferenceStats struct {
	// Chunk is the span of the indexed BGZF
	// holding alignments to the reference.
	Chunk bgzf.Chunk

	// Mapped is the count of mapped reads.
	Mapped uint64

	// Unmapped is the count of unmapped reads.
	Unmapped uint64
}

// Reader wraps a bgzf.Reader to provide a mechanism to read a selection of
// BGZF chunks.
type ChunkReader struct {
	r *bgzf.Reader

	wasBlocked bool

	chunks []bgzf.Chunk
}

// NewChunkReader returns a ChunkReader to read from r, limiting the reads to
// the provided chunks. The provided bgzf.Reader will be put into Blocked mode.
func NewChunkReader(r *bgzf.Reader, chunks []bgzf.Chunk) (*ChunkReader, error) {
	b := r.Blocked
	r.Blocked = true
	if len(chunks) != 0 {
		err := r.Seek(chunks[0].Begin)
		if err != nil {
			return nil, err
		}
	}
	return &ChunkReader{r: r, wasBlocked: b, chunks: chunks}, nil
}

// Read satisfies the io.Reader interface.
func (r *ChunkReader) Read(p []byte) (int, error) {
	if len(r.chunks) == 0 || vOffset(r.r.LastChunk().End) >= vOffset(r.chunks[0].End) {
		return 0, io.EOF
	}

	// Ensure the byte slice does not extend beyond the end of
	// the current chunk. We do not need to consider reading
	// beyond the end of the block because the bgzf.Reader is in
	// blocked mode and so will stop there anyway.
	if r.r.LastChunk().End.File == r.chunks[0].End.File {
		p = p[:r.chunks[0].End.Block-r.r.LastChunk().End.Block]
	}

	n, err := r.r.Read(p)
	if err != nil {
		if n != 0 && err == io.EOF {
			err = nil
		}
		return n, err
	}
	if len(r.chunks) != 0 && vOffset(r.r.LastChunk().End) >= vOffset(r.chunks[0].End) {
		err = r.r.Seek(r.chunks[0].Begin)
		r.chunks = r.chunks[1:]
	}
	return n, err
}

func vOffset(o bgzf.Offset) int64 {
	return o.File<<16 | int64(o.Block)
}

// Close returns the bgzf.Reader to its original blocking mode and releases it.
// The bgzf.Reader is not closed.
func (r *ChunkReader) Close() error {
	r.r.Blocked = r.wasBlocked
	r.r = nil
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"math/rand"
	"net/http"
	"net/url"
	"sort"
	"strconv"
	"time"

	"encoding/json"

	"strings"

	csay "github.com/dhruvbird/go-cowsay"
	"github.com/dpatrie/urbandictionary"
	"github.com/go-xorm/xorm"
	"github.com/gorilla/mux"
	"github.com/ogier/pflag"
	"github.com/olekukonko/tablewriter"
	"github.com/superp00t/godog/phoxy"

	_ "github.com/go-sql-driver/mysql"
)

var (
	DB *xorm.Engine
)

func main() {
	ap := pflag.StringP("api_key", "a", "admin", "the API key for use with the Phoxy administration API")

	name := pflag.StringP("name", "n", "harambe", "username")
	driver := pflag.StringP("driver", "r", "mysql", "SQL driver")
	db := pflag.StringP("db", "d", "", "SQL database")

	endpoint := pflag.StringP("endpoint", "e", "https://ikrypto.club/phoxy/", "connect to the standard XMPP BOSH server")
	logEvents := pflag.BoolP("log_events", "l", false, "log Phoxy events")
	bf := pflag.BoolP("be_filter", "b", false, "filter spam")
	api := pflag.StringP("http_listen", "h", "", "spin up HTTP management server")
	msg := pflag.StringP("msg", "s", "", "greeting message")

	pflag.Parse()
	protocol := phoxy.PHOXY
	if strings.HasSuffix(*endpoint, "http-bind/") {
		protocol = phoxy.BOSH
	}

	if strings.HasPrefix(*endpoint, "wss://") {
		protocol = phoxy.WS
	}
	opts := phoxy.Opts{
		Type:     protocol,
		Username: *name,
		Chatroom: "lobby",
		Endpoint: *endpoint,
		APIKey:   *ap,
		BeFilter: *bf,
	}

	if *db != "" {
		var err error
		DB, err = xorm.NewEngine(*driver, *db)
		if err != nil {
			log.Fatal(err)
		}

		err = DB.Sync2(new(phoxy.Event))
		if err != nil {
			log.Fatal(err)
		}
	}

	if *api != "" {
		go func() {
			r := mux.NewRouter()

			r.HandleFunc("/messages", func(rw http.ResponseWriter, r *http.Request) {
				enc := json.NewEncoder(rw)
				p := r.URL.Query()
				if p.Get("fmt") == "yes" {
					enc.SetIndent("", "    ")
				}
				var ev []phoxy.Event

				// q := DB.Where("body REGEXP ?", p.Get("r"))

				err := DB.Find(&ev)
				if err != nil {
					log.Println(err)
				}
				enc.Encode(ev)
			})

			log.Fatal(http.ListenAndServe(*api, r))
		}()
	}

	b, err := phoxy.New(&opts)
	if err != nil {
		log.Fatal(err)
	}

	if *logEvents {
		if DB == nil {
			log.Fatal("You need to supply a database to log events.")
		}
		b.Intercept(traceEvent)
	}

	start := time.Now()
	cmd := NewCmdParser()
	topic := "No topic yet"
	unAuthMsg := "You are not authorized to perform this action."
	color := "#000000"
	cmd.Add("ban", "bans a user", func(c *CmdCall) string {
		if len(c.Args) < 1 {
			return "usage: .ban <username>"
		}

		targ := c.Args[0]

		if b.IsAuthorized(b.Opts.Chatroom, c.From) {
			go func() {
				time.Sleep(1000 * time.Millisecond)
				b.Ban(b.Opts.Chatroom, targ)
			}()
			return fmt.Sprintf("%s has been banned.", targ)
		}

		return unAuthMsg
	})

	cmd.Add("interrupt", "interrupt all connections from a user's IP", func(c *CmdCall) string {
		if !b.IsAuthorized(b.Opts.Chatroom, c.From) {
			return unAuthMsg
		}

		if len(c.Args) < 1 {
			return "usage: ipban <username>"
		}

		b.Interrupt(b.Opts.Chatroom, c.Args[0])
		return ""
	})

	cmd.Add("clearjail", "clear server IP jail", func(c *CmdCall) string {
		if !b.IsAuthorized(b.Opts.Chatroom, c.From) {
			return unAuthMsg
		}

		b.ClearJail()
		return "jail cleared."
	})

	cmd.Add("lockdown", "sets the lockdown level (1 to require captcha, 2 for registered users only)", func(c *CmdCall) string {
		if len(c.Args) == 0 {
			return "usage: lockdown <level>"
		}

		if b.AccessLevel(b.Opts.Chatroom, c.From) > 2 {
			b.Lockdown(b.Opts.Chatroom, c.Args[0])
			return ""
		}

		return unAuthMsg
	})

	cmd.Add("cleanup", "destroys recently connected users", func(c *CmdCall) string {
		if len(c.Args) < 1 {
			return "usage: .cleanup <seconds>"
		}

		if b.IsAuthorized(b.Opts.Chatroom, c.From) {
			ti, err := strconv.ParseInt(c.Args[0], 10, 64)
			if err != nil {
				return err.Error()
			}

			dn, err := b.Cleanup(b.Opts.Chatroom, ti)
			return fmt.Sprintf("Cleaned up %d names.", dn)
		}

		return unAuthMsg
	})

	cmd.Add("set_topic", "sets the topic", func(c *CmdCall) string {
		if b.Opts.Type == phoxy.PHOXY && b.AccessLevel(b.Opts.Chatroom, c.From) < 2 {
			return unAuthMsg
		}

		top := strings.Join(c.Args, " ")
		topic = top
		return fmt.Sprintf("topic set to \"%s\"", topic)
	})

	cmd.Add("uptime", "shows how much uptime this bot has", func(c *CmdCall) string {
		return fmt.Sprintf("uptime: %v", time.Since(start))
	})

	// Fun functions
	cmd.Add("ratpost", "Queries a random rat from Reddit", func(c *CmdCall) string {
		if b.AccessLevel(b.Opts.Chatroom, c.From) < 3 {
			return unAuthMsg
		}

		if len(c.Args) != 0 {
			return getRat(b, c.Args[0])
		}
		return getRat(b, "")
	})

	cmd.Add("urbandict", "Searches a term on the Urban Dictionary", func(c *CmdCall) string {
		if len(c.Args) == 0 {
			return "usage: urbandict <searchterm>"
		}

		searchterm := strings.Join(c.Args, " ")

		UDresponse, err := urbandictionary.Query(searchterm)
		if err != nil {
			return err.Error()
		}

		if len(UDresponse.Results) == 0 {
			return "No definition found"
		}

		return UDresponse.Results[0].Definition
	})

	cmd.Add("quote", "Selects a random quote from https://ikrypto.club/quotes/", func(c *CmdCall) string {
		u, err := url.Parse("https://ikrypto.club/quotes/api/quotes")
		if err != nil {
			panic(err)
		}

		if len(c.Args) > 0 {
			q := u.Query()
			q.Set("q", c.Args[0])
			u.RawQuery = q.Encode()
		}

		r, err := http.Get(u.String())
		if err != nil {
			return ""
		}
		var q []Quote
		json.NewDecoder(r.Body).Decode(&q)
		rand.Seed(time.Now().Unix())
		if len(q) == 0 {
			return "no quote found"
		}
		qe := q[rand.Intn(len(q))]
		return qe.Body
	})

	cmd.Add("cowsay", "the cow says stuff", func(c *CmdCall) string {
		if len(c.Args) == 0 {
			return "usage: .cowsay <text>"
		}

		txt := strings.Join(c.Args, " ")
		return "```" + csay.Format(txt)
	})

	cmd.Add("chmod", "chmod <user> <level>", func(c *CmdCall) string {
		acs := b.AccessLevel(b.Opts.Chatroom, c.From)
		if len(c.Args) == 1 {
			acs2 := b.AccessLevel(b.Opts.Chatroom, c.Args[0])
			return fmt.Sprintf("%s's access level is currently %d", c.Args[0], acs2)
		}

		if len(c.Args) == 2 {
			if acs < 6 {
				return unAuthMsg
			}

			if err := b.Chmod(b.Opts.Chatroom, c.Args[0], c.Args[1]); err != nil {
				return "chmod unsuccessful."
			}
		} else {
			return "usage: chmod <user> <level>"
		}
		return ""
	})

	cmd.Add("set_color", "sets the bot's color", func(c *CmdCall) string {
		if len(c.Args) < 1 {
			return "usage: set_color <hex color>"
		}

		color = c.Args[0]
		b.SendColor(color)
		return ""
	})

	cmd.Add("help", "shows this message", func(c *CmdCall) string {
		buf := new(bytes.Buffer)
		table := tablewriter.NewWriter(buf)
		table.SetHeader([]string{"Command", "Description"})
		table.SetBorder(false)

		var help [][]string
		var ids []string
		for cn, _ := range cmd.Cmds {
			ids = append(ids, cn)
		}

		sort.Strings(ids)

		for _, id := range ids {
			help = append(help, []string{id, cmd.Cmds[id].Description})
		}

		table.AppendBulk(help)
		table.Render()
		return "```" + buf.String()
	})

	b.HandleFunc(phoxy.GROUPMESSAGE, func(ev *phoxy.Event) {
		t := time.Now()
		fmt.Printf("[%d:%d:%d]\t<%s>\t%s\n", t.Hour(), t.Minute(), t.Second(), ev.Username, ev.Body)

		// if IsSpam(ev.Body) {
		// 	b.Interrupt(b.Opts.Chatroom, ev.Username)
		// 	log.Println("Removing spam")
		// 	return
		// }

		if ev.Body == "." {
			b.GroupMessage(".")
			return
		}

		u, err := url.Parse(ev.Body)
		if err == nil {
			if u.Host == "www.youtube.com" {
				var resp noEmbedResp
				err := getJSON("https://noembed.com/embed", map[string]string{"url": u.String()}, &resp)
				if err == nil {
					b.Groupf("\"%s\" by %s", resp.Title, resp.Author)
				}
			}
		}

		msg := cmd.Parse(false, ev.Username, ev.Body)
		if msg != "" {
			b.GroupMessage(msg)
		}
	})

	b.HandleFunc(phoxy.VERIFY, func(ev *phoxy.Event) {
		isp := IsSpam(ev.Username, ev.Body)
		ev.Callback <- !isp
		log.Println(ev.Body)
		log.Println("Is spam", isp)
		if isp {
			log.Println("Banning", ev.Username)
			b.Interrupt(b.Opts.Chatroom, ev.Username)
		}
	})

	b.HandleFunc(phoxy.USERJOIN, func(ev *phoxy.Event) {
		if ev.Username == b.Opts.Username {
			return
		}

		b.SendColor(color)
		// Don't bother currently logged in users
		if start.UnixNano() > (time.Now().UnixNano() - (10 * time.Second).Nanoseconds()) {
			return
		}

		dur := 4000 * time.Millisecond
		time.Sleep(dur)
		if *msg != "" {
			b.GroupMessage(*msg)
			return
		}
		b.Groupf("Hey, %s! The topic of this conversation is %s", ev.Username, topic)
	})

	err = b.Connect()
	log.Fatal("Error connecting,", err)
}

type Quote struct {
	Id         int64  `json:"id" xorm:"'id'"`
	Body       string `json:"body" xorm:"longtext 'body'"`
	Time       int64  `json:"time" xorm:"'time'"`
	TimeFormat string `json:"-" xorm:"-"`
}

type noEmbedResp struct {
	Title  string `json:"title"`
	Author string `json:"author_name"`
}

func traceEvent(ev *phoxy.Event) {
	if DB != nil {
		DB.Insert(ev)
	}
}

func getJSON(uri string, queryP map[string]string, v interface{}) error {
	if len(queryP) != 0 {
		p, err := url.Parse(uri)
		if err != nil {
			return err
		}

		q := p.Query()
		for k, v := range queryP {
			q.Set(k, v)
		}
		p.RawQuery = q.Encode()
		uri = p.String()
	}

	req, err := http.NewRequest("GET", uri, nil)
	if err != nil {
		log.Fatal(err)
	}
	log.Println(uri)

	req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 6.1; rv:31.0) Gecko/20100101 Firefox/31.0")
	client := &http.Client{}
	r, err := client.Do(req)
	if err != nil {
		return err
	}

	if r.StatusCode != 200 {
		return fmt.Errorf("Server returned %d", r.StatusCode)
	}

	var buf bytes.Buffer
	io.Copy(&buf, r.Body)
	// log.Println(buf.String())
	return json.Unmarshal(buf.Bytes(), &v)
}
<commit_msg>rate limiting; bot flagging<commit_after>package main

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"math/rand"
	"net/http"
	"net/url"
	"sort"
	"strconv"
	"sync"
	"time"

	"encoding/json"

	"strings"

	csay "github.com/dhruvbird/go-cowsay"
	"github.com/dpatrie/urbandictionary"
	"github.com/go-xorm/xorm"
	"github.com/gorilla/mux"
	"github.com/ogier/pflag"
	"github.com/olekukonko/tablewriter"
	"github.com/superp00t/godog/phoxy"

	_ "github.com/go-sql-driver/mysql"
)

var (
	DB *xorm.Engine
)

func main() {
	ap := pflag.StringP("api_key", "a", "admin", "the API key for use with the Phoxy administration API")

	name := pflag.StringP("name", "n", "harambe", "username")
	driver := pflag.StringP("driver", "r", "mysql", "SQL driver")
	db := pflag.StringP("db", "d", "", "SQL database")

	endpoint := pflag.StringP("endpoint", "e", "https://ikrypto.club/phoxy/", "connect to the standard XMPP BOSH server")
	logEvents := pflag.BoolP("log_events", "l", false, "log Phoxy events")
	bf := pflag.BoolP("be_filter", "b", false, "filter spam")
	api := pflag.StringP("http_listen", "h", "", "spin up HTTP management server")
	msg := pflag.StringP("msg", "s", "", "greeting message")

	pflag.Parse()
	protocol := phoxy.PHOXY
	if strings.HasSuffix(*endpoint, "http-bind/") {
		protocol = phoxy.BOSH
	}

	if strings.HasPrefix(*endpoint, "wss://") {
		protocol = phoxy.WS
	}
	opts := phoxy.Opts{
		Type:     protocol,
		Username: *name,
		Chatroom: "lobby",
		Endpoint: *endpoint,
		APIKey:   *ap,
		BeFilter: *bf,
	}

	if *db != "" {
		var err error
		DB, err = xorm.NewEngine(*driver, *db)
		if err != nil {
			log.Fatal(err)
		}

		err = DB.Sync2(new(phoxy.Event))
		if err != nil {
			log.Fatal(err)
		}
	}

	if *api != "" {
		go func() {
			r := mux.NewRouter()

			r.HandleFunc("/messages", func(rw http.ResponseWriter, r *http.Request) {
				enc := json.NewEncoder(rw)
				p := r.URL.Query()
				if p.Get("fmt") == "yes" {
					enc.SetIndent("", "    ")
				}
				var ev []phoxy.Event

				// q := DB.Where("body REGEXP ?", p.Get("r"))

				err := DB.Find(&ev)
				if err != nil {
					log.Println(err)
				}
				enc.Encode(ev)
			})

			log.Fatal(http.ListenAndServe(*api, r))
		}()
	}

	b, err := phoxy.New(&opts)
	if err != nil {
		log.Fatal(err)
	}

	if *logEvents {
		if DB == nil {
			log.Fatal("You need to supply a database to log events.")
		}
		b.Intercept(traceEvent)
	}

	start := time.Now()
	cmd := NewCmdParser()
	topic := "No topic yet"
	unAuthMsg := "You are not authorized to perform this action."
	color := "#000000"
	cmd.Add("ban", "bans a user", func(c *CmdCall) string {
		if len(c.Args) < 1 {
			return "usage: .ban <username>"
		}

		targ := c.Args[0]

		if b.IsAuthorized(b.Opts.Chatroom, c.From) {
			go func() {
				time.Sleep(1000 * time.Millisecond)
				b.Ban(b.Opts.Chatroom, targ)
			}()
			return fmt.Sprintf("%s has been banned.", targ)
		}

		return unAuthMsg
	})

	cmd.Add("interrupt", "interrupt all connections from a user's IP", func(c *CmdCall) string {
		if !b.IsAuthorized(b.Opts.Chatroom, c.From) {
			return unAuthMsg
		}

		if len(c.Args) < 1 {
			return "usage: ipban <username>"
		}

		b.Interrupt(b.Opts.Chatroom, c.Args[0])
		return ""
	})

	cmd.Add("clearjail", "clear server IP jail", func(c *CmdCall) string {
		if !b.IsAuthorized(b.Opts.Chatroom, c.From) {
			return unAuthMsg
		}

		b.ClearJail()
		return "jail cleared."
	})

	cmd.Add("lockdown", "sets the lockdown level (1 to require captcha, 2 for registered users only)", func(c *CmdCall) string {
		if len(c.Args) == 0 {
			return "usage: lockdown <level>"
		}

		if b.AccessLevel(b.Opts.Chatroom, c.From) > 2 {
			b.Lockdown(b.Opts.Chatroom, c.Args[0])
			return ""
		}

		return unAuthMsg
	})

	cmd.Add("cleanup", "destroys recently connected users", func(c *CmdCall) string {
		if len(c.Args) < 1 {
			return "usage: .cleanup <seconds>"
		}

		if b.IsAuthorized(b.Opts.Chatroom, c.From) {
			ti, err := strconv.ParseInt(c.Args[0], 10, 64)
			if err != nil {
				return err.Error()
			}

			dn, err := b.Cleanup(b.Opts.Chatroom, ti)
			return fmt.Sprintf("Cleaned up %d names.", dn)
		}

		return unAuthMsg
	})

	cmd.Add("set_topic", "sets the topic", func(c *CmdCall) string {
		if b.Opts.Type == phoxy.PHOXY && b.AccessLevel(b.Opts.Chatroom, c.From) < 2 {
			return unAuthMsg
		}

		top := strings.Join(c.Args, " ")
		topic = top
		return fmt.Sprintf("topic set to \"%s\"", topic)
	})

	cmd.Add("uptime", "shows how much uptime this bot has", func(c *CmdCall) string {
		return fmt.Sprintf("uptime: %v", time.Since(start))
	})

	// Fun functions
	cmd.Add("ratpost", "Queries a random rat from Reddit", func(c *CmdCall) string {
		if b.AccessLevel(b.Opts.Chatroom, c.From) < 3 {
			return unAuthMsg
		}

		if len(c.Args) != 0 {
			return getRat(b, c.Args[0])
		}
		return getRat(b, "")
	})

	cmd.Add("urbandict", "Searches a term on the Urban Dictionary", func(c *CmdCall) string {
		if len(c.Args) == 0 {
			return "usage: urbandict <searchterm>"
		}

		searchterm := strings.Join(c.Args, " ")

		UDresponse, err := urbandictionary.Query(searchterm)
		if err != nil {
			return err.Error()
		}

		if len(UDresponse.Results) == 0 {
			return "No definition found"
		}

		return UDresponse.Results[0].Definition
	})

	cmd.Add("quote", "Selects a random quote from https://ikrypto.club/quotes/", func(c *CmdCall) string {
		u, err := url.Parse("https://ikrypto.club/quotes/api/quotes")
		if err != nil {
			panic(err)
		}

		if len(c.Args) > 0 {
			q := u.Query()
			q.Set("q", c.Args[0])
			u.RawQuery = q.Encode()
		}

		r, err := http.Get(u.String())
		if err != nil {
			return ""
		}
		var q []Quote
		json.NewDecoder(r.Body).Decode(&q)
		rand.Seed(time.Now().Unix())
		if len(q) == 0 {
			return "no quote found"
		}
		qe := q[rand.Intn(len(q))]
		return qe.Body
	})

	cmd.Add("cowsay", "the cow says stuff", func(c *CmdCall) string {
		if len(c.Args) == 0 {
			return "usage: .cowsay <text>"
		}

		txt := strings.Join(c.Args, " ")
		return "```" + csay.Format(txt)
	})

	cmd.Add("chmod", "chmod <user> <level>", func(c *CmdCall) string {
		acs := b.AccessLevel(b.Opts.Chatroom, c.From)
		if len(c.Args) == 1 {
			acs2 := b.AccessLevel(b.Opts.Chatroom, c.Args[0])
			return fmt.Sprintf("%s's access level is currently %d", c.Args[0], acs2)
		}

		if len(c.Args) == 2 {
			if acs < 6 {
				return unAuthMsg
			}

			if err := b.Chmod(b.Opts.Chatroom, c.Args[0], c.Args[1]); err != nil {
				return "chmod unsuccessful."
			}
		} else {
			return "usage: chmod <user> <level>"
		}
		return ""
	})

	cmd.Add("set_color", "sets the bot's color", func(c *CmdCall) string {
		if len(c.Args) < 1 {
			return "usage: set_color <hex color>"
		}

		color = c.Args[0]
		b.SendColor(color)
		return ""
	})

	cmd.Add("help", "shows this message", func(c *CmdCall) string {
		buf := new(bytes.Buffer)
		table := tablewriter.NewWriter(buf)
		table.SetHeader([]string{"Command", "Description"})
		table.SetBorder(false)

		var help [][]string
		var ids []string
		for cn, _ := range cmd.Cmds {
			ids = append(ids, cn)
		}

		sort.Strings(ids)

		for _, id := range ids {
			help = append(help, []string{id, cmd.Cmds[id].Description})
		}

		table.AppendBulk(help)
		table.Render()
		return "```" + buf.String()
	})

	IsABot := make(map[string]bool)
	Rates := make(map[string]int64)
	BotL := new(sync.Mutex)
	go func() {
		for {
			time.Sleep(60 * time.Second)
			BotL.Lock()
			Rates = make(map[string]int64)
		}
	}()

	b.HandleFunc(phoxy.GROUPMESSAGE, func(ev *phoxy.Event) {
		t := time.Now()
		fmt.Printf("[%d:%d:%d]\t<%s>\t%s\n", t.Hour(), t.Minute(), t.Second(), ev.Username, ev.Body)

		// if IsSpam(ev.Body) {
		// 	b.Interrupt(b.Opts.Chatroom, ev.Username)
		// 	log.Println("Removing spam")
		// 	return
		// }
		BotL.Lock()
		Rates[ev.Username] += int64(len(ev.Body))
		if Rates[ev.Username] > 1000 {
			IsABot[ev.Username] = true
		}

		if strings.HasPrefix(ev.Body, "+bot") {
			IsABot[ev.Username] = true
		}

		if IsABot[ev.Username] {
			BotL.Unlock()
			return
		}

		BotL.Unlock()

		if ev.Body == "." {
			b.GroupMessage(".")
			return
		}

		u, err := url.Parse(ev.Body)
		if err == nil {
			if u.Host == "www.youtube.com" {
				var resp noEmbedResp
				err := getJSON("https://noembed.com/embed", map[string]string{"url": u.String()}, &resp)
				if err == nil {
					b.Groupf("\"%s\" by %s", resp.Title, resp.Author)
				}
			}
		}

		msg := cmd.Parse(false, ev.Username, ev.Body)
		if msg != "" {
			b.GroupMessage(msg)
		}
	})

	b.HandleFunc(phoxy.VERIFY, func(ev *phoxy.Event) {
		isp := IsSpam(ev.Username, ev.Body)
		ev.Callback <- !isp
		log.Println(ev.Body)
		log.Println("Is spam", isp)
		if isp {
			log.Println("Banning", ev.Username)
			b.Interrupt(b.Opts.Chatroom, ev.Username)
		}
	})

	b.HandleFunc(phoxy.USERJOIN, func(ev *phoxy.Event) {
		if ev.Username == b.Opts.Username {
			return
		}

		b.SendColor(color)
		// Don't bother currently logged in users
		if start.UnixNano() > (time.Now().UnixNano() - (10 * time.Second).Nanoseconds()) {
			return
		}

		dur := 4000 * time.Millisecond
		time.Sleep(dur)
		if *msg != "" {
			b.GroupMessage(*msg)
			return
		}
		b.Groupf("+bot Hey, %s! The topic of this conversation is %s", ev.Username, topic)
	})

	go func() {
		time.Sleep(5 * time.Second)
		b.GroupMessage("+bot")
	}()

	err = b.Connect()
	log.Fatal("Error connecting,", err)
}

type Quote struct {
	Id         int64  `json:"id" xorm:"'id'"`
	Body       string `json:"body" xorm:"longtext 'body'"`
	Time       int64  `json:"time" xorm:"'time'"`
	TimeFormat string `json:"-" xorm:"-"`
}

type noEmbedResp struct {
	Title  string `json:"title"`
	Author string `json:"author_name"`
}

func traceEvent(ev *phoxy.Event) {
	if DB != nil {
		DB.Insert(ev)
	}
}

func getJSON(uri string, queryP map[string]string, v interface{}) error {
	if len(queryP) != 0 {
		p, err := url.Parse(uri)
		if err != nil {
			return err
		}

		q := p.Query()
		for k, v := range queryP {
			q.Set(k, v)
		}
		p.RawQuery = q.Encode()
		uri = p.String()
	}

	req, err := http.NewRequest("GET", uri, nil)
	if err != nil {
		log.Fatal(err)
	}
	log.Println(uri)

	req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 6.1; rv:31.0) Gecko/20100101 Firefox/31.0")
	client := &http.Client{}
	r, err := client.Do(req)
	if err != nil {
		return err
	}

	if r.StatusCode != 200 {
		return fmt.Errorf("Server returned %d", r.StatusCode)
	}

	var buf bytes.Buffer
	io.Copy(&buf, r.Body)
	// log.Println(buf.String())
	return json.Unmarshal(buf.Bytes(), &v)
}
<|endoftext|>


<commit_before>package unison

import (
	"errors"
	"fmt"
	"os"
	"os/signal"
	"strconv"
	"strings"
	"syscall"

	"github.com/Sirupsen/logrus"
	"github.com/s1kx/unison/state"
	"github.com/bwmarrin/discordgo"

	"github.com/s1kx/unison/events"
)

// EnvUnisonDiscordToken environment string to collect discord bot token.
const EnvUnisonDiscordToken = "UNISON_DISCORD_TOKEN"

// EnvUnisonCommandPrefix The command prefix to trigger commands. Defaults to mention. @botname
const EnvUnisonCommandPrefix = "UNISON_COMMAND_PREFIX"

// EnvUnisonState the default bot state. Defaults to 0. "normal"
const EnvUnisonState = "UNISON_STATE"

// DiscordGoBotTokenPrefix discordgo requires this token prefix
const DiscordGoBotTokenPrefix = "Bot "

// used to detect interupt signals and handle graceful shut down
var termSignal chan os.Signal

// BotSettings contains the definition of bot behavior.
// It is used for creating the actual bot.
type BotSettings struct {
	Token         string
	CommandPrefix string
	BotState      state.Type

	Commands   []*Command
	EventHooks []*EventHook
	Services   []*Service
}

// Run start the bot. Connect to discord, setup commands, hooks and services.
func Run(settings *BotSettings) error {
	// TODO: Validate commands

	// three steps are done before setting up a connection.
	// 1. Make sure the discord token exists.
	// 2. Set a prefered way of triggering commands.
	//		This must be done after establishin a discord socket. See bot.onReady()
	// 3. Decide the bot state.

	// 1. Make sure the discord token exists.
	//
	token := settings.Token
	// if it was not specified in the Settings struct, check the environment variable
	if token == "" {
		token = os.Getenv(EnvUnisonDiscordToken)

		// if the env var was empty as well, crash the bot as this is required.
		if token == "" {
			return errors.New("Missing env var " + EnvUnisonDiscordToken + ". This is required. Specify in either Settings struct or env var.")
		}

		logrus.Info("Using bot token from environment variable.")
	}
	// discordgo requires "Bot " prefix for Bot applications
	if !strings.HasPrefix(token, DiscordGoBotTokenPrefix) {
		token = DiscordGoBotTokenPrefix + token
	}

	// Initialize discord client
	ds, err := discordgo.New(token)
	if err != nil {
		return err
	}

	// 2. Set a prefered way of triggering commands
	//
	cprefix := settings.CommandPrefix
	// if not given, check the environment variable
	if cprefix == "" {
		cprefix = os.Getenv(EnvUnisonCommandPrefix)

		// in case this was not set, we trigger by mention
		if cprefix == "" {
			cprefix = ds.State.User.Mention()
		}

		// update Settings
		settings.CommandPrefix = cprefix
	}
	logrus.Info("Commands are triggered by `" + cprefix + "`")

	// 3. Decide the bot state.
	//
	uState := settings.BotState
	// check if valid state
	if uState == state.MissingState {
		// chjeck environment variable
		uStateStr := os.Getenv(EnvUnisonState)

		if uStateStr == "" {
			uState = state.Normal // uint8(1)
		} else {
			i, e := strconv.ParseInt(uStateStr, 10, 16)
			if e != nil {
				return e
			}

			uState = state.Type(i)
		}
	}
	state.DefaultState = uState

	// Initialize and start bot
	bot, err := newBot(settings, ds)
	if err != nil {
		return err
	}
	bot.Run()

	return nil
}

// Bot is an active bot session.
type Bot struct {
	*BotSettings
	Discord *discordgo.Session

	// Lookup map for name/alias => command
	commandMap map[string]*Command
	// Lookup map for name => hook
	eventHookMap map[string]*EventHook
	// Lookup map for name => service
	serviceMap map[string]*Service

	eventDispatcher *eventDispatcher

	// Command prefix
	commandPrefix string

	readyState *discordgo.Ready
	User       *discordgo.User

	state state.Type // default bot state
}

func newBot(settings *BotSettings, ds *discordgo.Session) (*Bot, error) {
	// Initialize bot
	bot := &Bot{
		BotSettings: settings,
		Discord:     ds,

		commandMap:      make(map[string]*Command),
		eventHookMap:    make(map[string]*EventHook),
		serviceMap:      make(map[string]*Service),
		eventDispatcher: newEventDispatcher(),

		commandPrefix: settings.CommandPrefix,
	}

	// Register commands
	for _, cmd := range bot.Commands {
		err := bot.RegisterCommand(cmd)
		if err != nil {
			return nil, err
		}
	}

	// Register event hooks
	for _, hook := range bot.EventHooks {
		err := bot.RegisterEventHook(hook)
		if err != nil {
			return nil, err
		}
	}

	// Register services
	for _, srv := range bot.Services {
		err := bot.RegisterService(srv)
		if err != nil {
			return nil, err
		}
	}

	return bot, nil
}

// GetServiceData a data value from existing services
func (bot *Bot) GetServiceData(srvName string, key string) string {
	if val, ok := bot.serviceMap[srvName]; ok {
		if d, s := val.Data[key]; s {
			// key exist
			return d
		}
	}

	return ""
}

// SetServiceData update or set a new value for a given service key
func (bot *Bot) SetServiceData(srvName string, key string, val string) string {
	if v, ok := bot.serviceMap[srvName]; ok {
		if _, s := v.Data[key]; s {
			bot.serviceMap[srvName].Data[key] = val

			return val
		}
	}

	return ""
}

// Run Start the bot instance
func (bot *Bot) Run() error {
	// Add handler to wait for ready state in order to initialize the bot fully.
	bot.Discord.AddHandler(bot.onReady)

	// Add generic handler for event hooks
	// Add command handler
	bot.Discord.AddHandler(func(ds *discordgo.Session, event interface{}) {
		bot.onEvent(ds, event)
	})

	// Handle joining new guilds
	bot.Discord.AddHandler(onGuildJoin)

	// Open the websocket and begin listening.
	logrus.Info("Opening WS connection to Discord .. ")
	err := bot.Discord.Open()
	if err != nil {
		return fmt.Errorf("error: %s", err)
	}
	logrus.Info("OK")

	// Create context for services
	ctx := NewContext(bot, bot.Discord, termSignal)

	// Run services
	for _, srv := range bot.serviceMap {
		if srv.Deactivated {
			continue
		}

		// run service
		go srv.Action(ctx)
	}

	// create a add bot url
	logrus.Infof("Add bot using: https://discordapp.com/oauth2/authorize?client_id=%s&scope=bot", bot.Discord.State.User.ID)

	logrus.Info("Bot is now running.  Press CTRL-C to exit.")
	termSignal = make(chan os.Signal, 1)
	signal.Notify(termSignal, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill)
	<-termSignal
	fmt.Println("") // keep the `^C` on it's own line for prettiness
	logrus.Info("Shutting down bot..")

	// Cleanly close down the Discord session.
	logrus.Info("\tClosing WS discord connection .. ")
	err = bot.Discord.Close()
	if err != nil {
		return err
	}
	logrus.Info("\tClosed WS discord connection.")

	logrus.Info("Shutdown successfully")

	return nil
}

func (bot *Bot) RegisterCommand(cmd *Command) error {
	name := cmd.Name
	if ex, exists := bot.commandMap[name]; exists {
		return &DuplicateCommandError{Existing: ex, New: cmd, Name: name}
	}
	bot.commandMap[name] = cmd

	// TODO: Register aliases

	return nil
}

func (bot *Bot) RegisterEventHook(hook *EventHook) error {
	name := hook.Name
	if ex, exists := bot.eventHookMap[name]; exists {
		return &DuplicateEventHookError{Existing: ex, New: hook}
	}
	bot.eventHookMap[name] = hook

	if len(hook.Events) == 0 {
		logrus.Warnf("Hook '%s' is not subscribed to any events", name)
	}

	bot.eventDispatcher.AddHook(hook)

	return nil
}

func (bot *Bot) RegisterService(srv *Service) error {
	name := srv.Name
	if ex, exists := bot.serviceMap[name]; exists {
		return &DuplicateServiceError{Existing: ex, New: srv, Name: name}
	}
	bot.serviceMap[name] = srv

	return nil
}

func (bot *Bot) onEvent(ds *discordgo.Session, dv interface{}) {
	// Inspect and wrap event
	ev, err := events.NewDiscordEvent(dv)
	if err != nil {
		logrus.Errorf("event handler: %s", err)
	}

	// Create context for handlers
	ctx := NewContext(bot, ds, termSignal)

	// check if event was triggered by bot itself
	self := false
	// TODO: check

	// Invoke event hooks for the hooks that are subscribed to the event type
	bot.eventDispatcher.Dispatch(ctx, ev, self)

	// Invoke command handler on new messages
	if ev.Type == events.MessageCreateEvent {
		handleMessageCreate(ctx, ev.Event.(*discordgo.MessageCreate))
	}
}

// Bot state
//

func (bot *Bot) GetState(guildID string) (state.Type, error) {
	return state.GetGuildState(guildID)
}
func (bot *Bot) SetState(guildID string, st state.Type) error {
	return state.SetGuildState(guildID, st)
}

// Event listeners
//

func (bot *Bot) onReady(ds *discordgo.Session, r *discordgo.Ready) {
	// Set bot state
	bot.readyState = r
	bot.User = r.User

	logrus.WithFields(logrus.Fields{
		"ID":       r.User.ID,
		"Username": r.User.Username,
	}).Infof("Websocket connected.")
}

func onGuildJoin(s *discordgo.Session, event *discordgo.GuildCreate) {

	if event.Guild.Unavailable {
		return
	}

	// Add this guild to the database
	guildID := event.Guild.ID
	st, err := state.GetGuildState(guildID)
	if err != nil {
		// should this be handled? 0.o
	}
	if st == state.MissingState {

		err := state.SetGuildState(guildID, state.DefaultState)
		if err != nil {
			logrus.Error("Unable to set default state for guild " + event.Guild.Name)
		} else {
			logrus.Info("Joined Guild `" + event.Guild.Name + "`, and set state to `" + state.ToStr(state.DefaultState) + "`")
		}
	} else {
		logrus.Info("Checked Guild `" + event.Guild.Name + "`, with state `" + state.ToStr(st) + "`")
	}
}
<commit_msg>fixed null reference on missing command prefix<commit_after>package unison

import (
	"errors"
	"fmt"
	"os"
	"os/signal"
	"strconv"
	"strings"
	"syscall"

	"github.com/Sirupsen/logrus"
	"github.com/s1kx/unison/state"
	"github.com/bwmarrin/discordgo"

	"github.com/s1kx/unison/events"
)

// EnvUnisonDiscordToken environment string to collect discord bot token.
const EnvUnisonDiscordToken = "UNISON_DISCORD_TOKEN"

// EnvUnisonCommandPrefix The command prefix to trigger commands. Defaults to mention. @botname
const EnvUnisonCommandPrefix = "UNISON_COMMAND_PREFIX"

// EnvUnisonState the default bot state. Defaults to 0. "normal"
const EnvUnisonState = "UNISON_STATE"

// DiscordGoBotTokenPrefix discordgo requires this token prefix
const DiscordGoBotTokenPrefix = "Bot "

// used to detect interupt signals and handle graceful shut down
var termSignal chan os.Signal

// BotSettings contains the definition of bot behavior.
// It is used for creating the actual bot.
type BotSettings struct {
	Token         string
	CommandPrefix string
	BotState      state.Type

	Commands   []*Command
	EventHooks []*EventHook
	Services   []*Service
}

// Run start the bot. Connect to discord, setup commands, hooks and services.
func Run(settings *BotSettings) error {
	// TODO: Validate commands

	// three steps are done before setting up a connection.
	// 1. Make sure the discord token exists.
	// 2. Set a prefered way of triggering commands.
	//		This must be done after establishin a discord socket. See bot.onReady()
	// 3. Decide the bot state.

	// 1. Make sure the discord token exists.
	//
	token := settings.Token
	// if it was not specified in the Settings struct, check the environment variable
	if token == "" {
		token = os.Getenv(EnvUnisonDiscordToken)

		// if the env var was empty as well, crash the bot as this is required.
		if token == "" {
			return errors.New("Missing env var " + EnvUnisonDiscordToken + ". This is required. Specify in either Settings struct or env var.")
		}

		logrus.Info("Using bot token from environment variable.")
	}
	// discordgo requires "Bot " prefix for Bot applications
	if !strings.HasPrefix(token, DiscordGoBotTokenPrefix) {
		token = DiscordGoBotTokenPrefix + token
	}

	// Initialize discord client
	ds, err := discordgo.New(token)
	if err != nil {
		return err
	}

	// 2. Set a prefered way of triggering commands
	//
	cprefix := settings.CommandPrefix
	// if not given, check the environment variable
	if cprefix == "" {
		cprefix = os.Getenv(EnvUnisonCommandPrefix)

		// in case this was not set, we trigger by mention
		if cprefix == "" {
			// This must be set after web socket connection has been opened
			// as the username of the bot is still unknown at this stage.
			// cprefix = ds.State.User.Mention()
		}

		// update Settings
		settings.CommandPrefix = cprefix
	}
	logrus.Info("Commands are triggered by `" + cprefix + "`")

	// 3. Decide the bot state.
	//
	uState := settings.BotState
	// check if valid state
	if uState == state.MissingState {
		// chjeck environment variable
		uStateStr := os.Getenv(EnvUnisonState)

		if uStateStr == "" {
			uState = state.Normal // uint8(1)
		} else {
			i, e := strconv.ParseInt(uStateStr, 10, 16)
			if e != nil {
				return e
			}

			uState = state.Type(i)
		}
	}
	state.DefaultState = uState

	// Initialize and start bot
	bot, err := newBot(settings, ds)
	if err != nil {
		return err
	}
	bot.Run()

	return nil
}

// Bot is an active bot session.
type Bot struct {
	*BotSettings
	Discord *discordgo.Session

	// Lookup map for name/alias => command
	commandMap map[string]*Command
	// Lookup map for name => hook
	eventHookMap map[string]*EventHook
	// Lookup map for name => service
	serviceMap map[string]*Service

	eventDispatcher *eventDispatcher

	// Command prefix
	commandPrefix string

	readyState *discordgo.Ready
	User       *discordgo.User

	state state.Type // default bot state
}

func newBot(settings *BotSettings, ds *discordgo.Session) (*Bot, error) {
	// Initialize bot
	bot := &Bot{
		BotSettings: settings,
		Discord:     ds,

		commandMap:      make(map[string]*Command),
		eventHookMap:    make(map[string]*EventHook),
		serviceMap:      make(map[string]*Service),
		eventDispatcher: newEventDispatcher(),

		commandPrefix: settings.CommandPrefix,
	}

	// Register commands
	for _, cmd := range bot.Commands {
		err := bot.RegisterCommand(cmd)
		if err != nil {
			return nil, err
		}
	}

	// Register event hooks
	for _, hook := range bot.EventHooks {
		err := bot.RegisterEventHook(hook)
		if err != nil {
			return nil, err
		}
	}

	// Register services
	for _, srv := range bot.Services {
		err := bot.RegisterService(srv)
		if err != nil {
			return nil, err
		}
	}

	return bot, nil
}

// GetServiceData a data value from existing services
func (bot *Bot) GetServiceData(srvName string, key string) string {
	if val, ok := bot.serviceMap[srvName]; ok {
		if d, s := val.Data[key]; s {
			// key exist
			return d
		}
	}

	return ""
}

// SetServiceData update or set a new value for a given service key
func (bot *Bot) SetServiceData(srvName string, key string, val string) string {
	if v, ok := bot.serviceMap[srvName]; ok {
		if _, s := v.Data[key]; s {
			bot.serviceMap[srvName].Data[key] = val

			return val
		}
	}

	return ""
}

// Run Start the bot instance
func (bot *Bot) Run() error {
	// Add handler to wait for ready state in order to initialize the bot fully.
	bot.Discord.AddHandler(bot.onReady)

	// Add generic handler for event hooks
	// Add command handler
	bot.Discord.AddHandler(func(ds *discordgo.Session, event interface{}) {
		bot.onEvent(ds, event)
	})

	// Handle joining new guilds
	bot.Discord.AddHandler(onGuildJoin)

	// Open the websocket and begin listening.
	logrus.Info("Opening WS connection to Discord .. ")
	err := bot.Discord.Open()
	if err != nil {
		return fmt.Errorf("error: %s", err)
	}
	logrus.Info("OK")

	// check how the bot is triggered. if it's "", we set it by mention
	if bot.commandPrefix == "" {
		bot.commandPrefix = ds.State.User.Mention()
		bot.BotSettings.CommandPrefix = bot.commandPrefix
	}

	// Create context for services
	ctx := NewContext(bot, bot.Discord, termSignal)

	// Run services
	for _, srv := range bot.serviceMap {
		if srv.Deactivated {
			continue
		}

		// run service
		go srv.Action(ctx)
	}

	// create a add bot url
	logrus.Infof("Add bot using: https://discordapp.com/oauth2/authorize?client_id=%s&scope=bot", bot.Discord.State.User.ID)

	logrus.Info("Bot is now running.  Press CTRL-C to exit.")
	termSignal = make(chan os.Signal, 1)
	signal.Notify(termSignal, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill)
	<-termSignal
	fmt.Println("") // keep the `^C` on it's own line for prettiness
	logrus.Info("Shutting down bot..")

	// Cleanly close down the Discord session.
	logrus.Info("\tClosing WS discord connection .. ")
	err = bot.Discord.Close()
	if err != nil {
		return err
	}
	logrus.Info("\tClosed WS discord connection.")

	logrus.Info("Shutdown successfully")

	return nil
}

func (bot *Bot) RegisterCommand(cmd *Command) error {
	name := cmd.Name
	if ex, exists := bot.commandMap[name]; exists {
		return &DuplicateCommandError{Existing: ex, New: cmd, Name: name}
	}
	bot.commandMap[name] = cmd

	// TODO: Register aliases

	return nil
}

func (bot *Bot) RegisterEventHook(hook *EventHook) error {
	name := hook.Name
	if ex, exists := bot.eventHookMap[name]; exists {
		return &DuplicateEventHookError{Existing: ex, New: hook}
	}
	bot.eventHookMap[name] = hook

	if len(hook.Events) == 0 {
		logrus.Warnf("Hook '%s' is not subscribed to any events", name)
	}

	bot.eventDispatcher.AddHook(hook)

	return nil
}

func (bot *Bot) RegisterService(srv *Service) error {
	name := srv.Name
	if ex, exists := bot.serviceMap[name]; exists {
		return &DuplicateServiceError{Existing: ex, New: srv, Name: name}
	}
	bot.serviceMap[name] = srv

	return nil
}

func (bot *Bot) onEvent(ds *discordgo.Session, dv interface{}) {
	// Inspect and wrap event
	ev, err := events.NewDiscordEvent(dv)
	if err != nil {
		logrus.Errorf("event handler: %s", err)
	}

	// Create context for handlers
	ctx := NewContext(bot, ds, termSignal)

	// check if event was triggered by bot itself
	self := false
	// TODO: check

	// Invoke event hooks for the hooks that are subscribed to the event type
	bot.eventDispatcher.Dispatch(ctx, ev, self)

	// Invoke command handler on new messages
	if ev.Type == events.MessageCreateEvent {
		handleMessageCreate(ctx, ev.Event.(*discordgo.MessageCreate))
	}
}

// Bot state
//

func (bot *Bot) GetState(guildID string) (state.Type, error) {
	return state.GetGuildState(guildID)
}
func (bot *Bot) SetState(guildID string, st state.Type) error {
	return state.SetGuildState(guildID, st)
}

// Event listeners
//

func (bot *Bot) onReady(ds *discordgo.Session, r *discordgo.Ready) {
	// Set bot state
	bot.readyState = r
	bot.User = r.User

	logrus.WithFields(logrus.Fields{
		"ID":       r.User.ID,
		"Username": r.User.Username,
	}).Infof("Websocket connected.")
}

func onGuildJoin(s *discordgo.Session, event *discordgo.GuildCreate) {

	if event.Guild.Unavailable {
		return
	}

	// Add this guild to the database
	guildID := event.Guild.ID
	st, err := state.GetGuildState(guildID)
	if err != nil {
		// should this be handled? 0.o
	}
	if st == state.MissingState {

		err := state.SetGuildState(guildID, state.DefaultState)
		if err != nil {
			logrus.Error("Unable to set default state for guild " + event.Guild.Name)
		} else {
			logrus.Info("Joined Guild `" + event.Guild.Name + "`, and set state to `" + state.ToStr(state.DefaultState) + "`")
		}
	} else {
		logrus.Info("Checked Guild `" + event.Guild.Name + "`, with state `" + state.ToStr(st) + "`")
	}
}
<|endoftext|>


<commit_before>package bruxism

import (
	"bytes"
	"encoding/json"
	"errors"
	"image"
	"image/png"
	"io/ioutil"
	"log"
	"mime/multipart"
	"net/http"
	"os"
	"runtime/debug"
)

// VersionString is the current version of the bot
const VersionString string = "0.5"

type serviceEntry struct {
	Service
	Plugins         map[string]Plugin
	messageChannels []chan Message
}

// Bot enables registering of Services and Plugins.
type Bot struct {
	Services    map[string]*serviceEntry
	ImgurID     string
	ImgurAlbum  string
	MashableKey string
}

// MessageRecover is the default panic handler for bruxism.
func MessageRecover() {
	if r := recover(); r != nil {
		log.Println("Recovered:", string(debug.Stack()))
	}
}

// NewBot will create a new bot.
func NewBot() *Bot {
	return &Bot{
		Services: make(map[string]*serviceEntry, 0),
	}
}

func (b *Bot) getData(service Service, plugin Plugin) []byte {
	if b, err := ioutil.ReadFile(service.Name() + "/" + plugin.Name()); err == nil {
		return b
	}
	return nil
}

// RegisterService registers a service with the bot.
func (b *Bot) RegisterService(service Service) {
	if b.Services[service.Name()] != nil {
		log.Println("Service with that name already registered", service.Name())
	}
	serviceName := service.Name()
	b.Services[serviceName] = &serviceEntry{
		Service: service,
		Plugins: make(map[string]Plugin, 0),
	}
	b.RegisterPlugin(service, NewHelpPlugin())
}

// RegisterPlugin registers a plugin on a service.
func (b *Bot) RegisterPlugin(service Service, plugin Plugin) {
	s := b.Services[service.Name()]
	if s.Plugins[plugin.Name()] != nil {
		log.Println("Plugin with that name already registered", plugin.Name())
	}
	s.Plugins[plugin.Name()] = plugin
}

func (b *Bot) listen(service Service, messageChan <-chan Message) {
	serviceName := service.Name()
	for {
		message := <-messageChan
		log.Printf("<%s> %s: %s\n", message.Channel(), message.UserName(), message.Message())
		plugins := b.Services[serviceName].Plugins
		for _, plugin := range plugins {
			go plugin.Message(b, service, message)
		}
	}
}

// Open will open all the current services and begins listening.
func (b *Bot) Open() {
	for _, service := range b.Services {
		if messageChan, err := service.Open(); err == nil {
			for _, plugin := range service.Plugins {
				plugin.Load(b, service, b.getData(service, plugin))
			}
			go b.listen(service.Service, messageChan)
		} else {
			log.Printf("Error creating service %s: %v\n", service.Name(), err)
		}
	}
}

// Save will save the current plugin state for all plugins on all services.
func (b *Bot) Save() {
	for _, service := range b.Services {
		serviceName := service.Name()
		if err := os.Mkdir(serviceName, os.ModePerm); err != nil {
			if !os.IsExist(err) {
				log.Println("Error creating service directory.")
			}
		}
		for _, plugin := range service.Plugins {
			if data, err := plugin.Save(); err != nil {
				log.Printf("Error saving plugin %s %s. %v", serviceName, plugin.Name(), err)
			} else if data != nil {
				if err := ioutil.WriteFile(serviceName+"/"+plugin.Name(), data, os.ModePerm); err != nil {
					log.Printf("Error saving plugin %s %s. %v", serviceName, plugin.Name(), err)
				}
			}
		}
	}
}

// UploadToImgur uploads image data to Imgur and returns the url to it.
func (b *Bot) UploadToImgur(image image.Image, filename string) (string, error) {
	if b.ImgurID == "" {
		return "", errors.New("No Imgur client ID provided.")
	}

	bodyBuf := &bytes.Buffer{}
	bodywriter := multipart.NewWriter(bodyBuf)

	writer, err := bodywriter.CreateFormFile("image", filename)
	if err != nil {
		return "", err
	}

	err = png.Encode(writer, image)
	if err != nil {
		return "", err
	}

	contentType := bodywriter.FormDataContentType()
	if b.ImgurAlbum != "" {
		bodywriter.WriteField("album", b.ImgurAlbum)
	}
	bodywriter.Close()

	r, err := http.NewRequest("POST", "https://api.imgur.com/3/image", bodyBuf)
	if err != nil {
		return "", err
	}

	r.Header.Set("Content-Type", contentType)
	r.Header.Set("Authorization", "Client-ID "+b.ImgurID)

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

	if resp.StatusCode != 200 {
		return "", errors.New(string(body))
	}

	j := make(map[string]interface{})

	err = json.Unmarshal(body, &j)
	if err != nil {
		return "", err
	}

	return j["data"].(map[string]interface{})["link"].(string), nil
}
<commit_msg>Bump the version.<commit_after>package bruxism

import (
	"bytes"
	"encoding/json"
	"errors"
	"image"
	"image/png"
	"io/ioutil"
	"log"
	"mime/multipart"
	"net/http"
	"os"
	"runtime/debug"
)

// VersionString is the current version of the bot
const VersionString string = "0.6"

type serviceEntry struct {
	Service
	Plugins         map[string]Plugin
	messageChannels []chan Message
}

// Bot enables registering of Services and Plugins.
type Bot struct {
	Services    map[string]*serviceEntry
	ImgurID     string
	ImgurAlbum  string
	MashableKey string
}

// MessageRecover is the default panic handler for bruxism.
func MessageRecover() {
	if r := recover(); r != nil {
		log.Println("Recovered:", string(debug.Stack()))
	}
}

// NewBot will create a new bot.
func NewBot() *Bot {
	return &Bot{
		Services: make(map[string]*serviceEntry, 0),
	}
}

func (b *Bot) getData(service Service, plugin Plugin) []byte {
	if b, err := ioutil.ReadFile(service.Name() + "/" + plugin.Name()); err == nil {
		return b
	}
	return nil
}

// RegisterService registers a service with the bot.
func (b *Bot) RegisterService(service Service) {
	if b.Services[service.Name()] != nil {
		log.Println("Service with that name already registered", service.Name())
	}
	serviceName := service.Name()
	b.Services[serviceName] = &serviceEntry{
		Service: service,
		Plugins: make(map[string]Plugin, 0),
	}
	b.RegisterPlugin(service, NewHelpPlugin())
}

// RegisterPlugin registers a plugin on a service.
func (b *Bot) RegisterPlugin(service Service, plugin Plugin) {
	s := b.Services[service.Name()]
	if s.Plugins[plugin.Name()] != nil {
		log.Println("Plugin with that name already registered", plugin.Name())
	}
	s.Plugins[plugin.Name()] = plugin
}

func (b *Bot) listen(service Service, messageChan <-chan Message) {
	serviceName := service.Name()
	for {
		message := <-messageChan
		log.Printf("<%s> %s: %s\n", message.Channel(), message.UserName(), message.Message())
		plugins := b.Services[serviceName].Plugins
		for _, plugin := range plugins {
			go plugin.Message(b, service, message)
		}
	}
}

// Open will open all the current services and begins listening.
func (b *Bot) Open() {
	for _, service := range b.Services {
		if messageChan, err := service.Open(); err == nil {
			for _, plugin := range service.Plugins {
				plugin.Load(b, service, b.getData(service, plugin))
			}
			go b.listen(service.Service, messageChan)
		} else {
			log.Printf("Error creating service %s: %v\n", service.Name(), err)
		}
	}
}

// Save will save the current plugin state for all plugins on all services.
func (b *Bot) Save() {
	for _, service := range b.Services {
		serviceName := service.Name()
		if err := os.Mkdir(serviceName, os.ModePerm); err != nil {
			if !os.IsExist(err) {
				log.Println("Error creating service directory.")
			}
		}
		for _, plugin := range service.Plugins {
			if data, err := plugin.Save(); err != nil {
				log.Printf("Error saving plugin %s %s. %v", serviceName, plugin.Name(), err)
			} else if data != nil {
				if err := ioutil.WriteFile(serviceName+"/"+plugin.Name(), data, os.ModePerm); err != nil {
					log.Printf("Error saving plugin %s %s. %v", serviceName, plugin.Name(), err)
				}
			}
		}
	}
}

// UploadToImgur uploads image data to Imgur and returns the url to it.
func (b *Bot) UploadToImgur(image image.Image, filename string) (string, error) {
	if b.ImgurID == "" {
		return "", errors.New("No Imgur client ID provided.")
	}

	bodyBuf := &bytes.Buffer{}
	bodywriter := multipart.NewWriter(bodyBuf)

	writer, err := bodywriter.CreateFormFile("image", filename)
	if err != nil {
		return "", err
	}

	err = png.Encode(writer, image)
	if err != nil {
		return "", err
	}

	contentType := bodywriter.FormDataContentType()
	if b.ImgurAlbum != "" {
		bodywriter.WriteField("album", b.ImgurAlbum)
	}
	bodywriter.Close()

	r, err := http.NewRequest("POST", "https://api.imgur.com/3/image", bodyBuf)
	if err != nil {
		return "", err
	}

	r.Header.Set("Content-Type", contentType)
	r.Header.Set("Authorization", "Client-ID "+b.ImgurID)

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

	if resp.StatusCode != 200 {
		return "", errors.New(string(body))
	}

	j := make(map[string]interface{})

	err = json.Unmarshal(body, &j)
	if err != nil {
		return "", err
	}

	return j["data"].(map[string]interface{})["link"].(string), nil
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/go-telegram-bot-api/telegram-bot-api"
	"log"
	"net/http"
	"net/url"
	"strings"
)

func InitBot(token string) (*tgbotapi.BotAPI, error) {
	bot, err := tgbotapi.NewBotAPI(token)
	if err != nil {
		return nil, err
	}

	bot.Debug = true
	log.Println("Account: ", bot.Self.UserName)

	return bot, nil
}

func InitWebHook(bot *tgbotapi.BotAPI, config Config) (tgbotapi.UpdatesChannel, error) {
	URL, err := url.Parse(config.Url)
	if err != nil {
		return nil, err
	}

	bot.SetWebhook(tgbotapi.WebhookConfig{URL: URL})

	updates := bot.ListenForWebhook(URL.Path)

	go http.ListenAndServe("localhost:"+config.Port, nil)
	if err != nil {
		return nil, err
	}

	return updates, nil
}

func ProcessUpdates(updates tgbotapi.UpdatesChannel, bot *tgbotapi.BotAPI) {

	for update := range updates {
		if update.Message == nil {
			continue
		}

		log.Println(update.Message.Chat.ID, update.Message.From.ID ,update.Message.Text)
		text := update.Message.Text
		var response string

		switch true {
		case strings.Contains(text, "/add"):
			response = "adding money"
		case strings.Contains(text, "/rem"):
			response = "removing money"
		case strings.Contains(text, "/status"):
			response = "getting status"
		default:
			response = "i don't get what you want from me."
		}
		msg := tgbotapi.NewMessage(update.Message.Chat.ID, response)
		msg.ReplyToMessageID = update.Message.MessageID

		bot.Send(msg)
	}
}
<commit_msg>add basic operation<commit_after>package main

import (
	"github.com/go-telegram-bot-api/telegram-bot-api"
	"log"
	"net/http"
	"net/url"
	"strconv"
	"strings"
)

var store = make(map[string]map[string]float64)

func InitBot(token string) (*tgbotapi.BotAPI, error) {
	bot, err := tgbotapi.NewBotAPI(token)
	if err != nil {
		return nil, err
	}

	//bot.Debug = true
	log.Println("Account: ", bot.Self.UserName)

	return bot, nil
}

func InitWebHook(bot *tgbotapi.BotAPI, config Config) (tgbotapi.UpdatesChannel, error) {
	URL, err := url.Parse(config.Url)
	if err != nil {
		return nil, err
	}

	bot.SetWebhook(tgbotapi.WebhookConfig{URL: URL})

	updates := bot.ListenForWebhook(URL.Path)

	go http.ListenAndServe("localhost:"+config.Port, nil)
	if err != nil {
		return nil, err
	}

	return updates, nil
}

func ProcessUpdates(updates tgbotapi.UpdatesChannel, bot *tgbotapi.BotAPI) {

	for update := range updates {
		if update.Message == nil {
			continue
		}

		log.Println(update.Message.Chat.ID, update.Message.From.ID, update.Message.Text)
		text := update.Message.Text
		var response string

		account := strconv.FormatInt(int64(update.Message.From.ID), 10)
		money, err := strconv.ParseFloat(strings.Split(text, " ")[0], 64)
		if err != nil {
			log.Println("[ERROR]: ", err)
		}

		switch true {
		case strings.Contains(text, "/add"):
			response = addMoney(money, account, "default")
		case strings.Contains(text, "/rem"):
			response = "removing money"
		case strings.Contains(text, "/status"):
			response = "getting status"
		default:
			response = "i don't get what you want from me."
		}
		msg := tgbotapi.NewMessage(update.Message.Chat.ID, response)
		msg.ReplyToMessageID = update.Message.MessageID

		bot.Send(msg)
	}
}

func addMoney(money float64, account, category string) string {
	val, ok := store[account]
	if !ok {
		log.Println("store value: ", val)
		store[account] = make(map[string]float64)
		log.Println("store value: ", val)
	}
	log.Println("category", category)
	return "add: " + strconv.FormatFloat(money, 'f', -1, 64)
}
<|endoftext|>


<commit_before>package log

import (
	"encoding/json"
	"fmt"
	"log"
	"log/syslog"
)

var slog *syslog.Writer

func init() {
	var err error
	slog, err = syslog.New(syslog.LOG_INFO, "[herd]")
	if err != nil {
		log.Printf("syslog.New err: %s", err)
	}
}

func Info(f string, v ...interface{}) string {
	s := fmt.Sprintf(f, v...)
	log.Printf(s)
	return s
}

func Err(f string, v ...interface{}) string {
	s := fmt.Sprintf(f, v...)
	slog.Err(s)
	log.Printf(s)
	return s
}

func Marshal(v interface{}) string {

	j, err := json.MarshalIndent(v, "", "    ")
	if err != nil {
		e := "Err: json.MarshalIndent()"
		slog.Err(e)
		log.Printf(e)
		return ""
	}

	s := string(j)

	log.Printf(s)

	return s
}
<commit_msg>Remove slog.<commit_after>package log

import (
	"encoding/json"
	"fmt"
	"log"
)

func Info(f string, v ...interface{}) string {
	s := fmt.Sprintf(f, v...)
	log.Printf(s)
	return s
}

func Err(f string, v ...interface{}) string {
	s := fmt.Sprintf(f, v...)
	log.Printf(s)
	return s
}

func Marshal(v interface{}) string {

	j, err := json.MarshalIndent(v, "", "    ")
	if err != nil {
		e := "Err: json.MarshalIndent()"
		log.Printf(e)
		return ""
	}

	s := string(j)

	log.Printf(s)

	return s
}
<|endoftext|>