Skip to content

Commit

Permalink
Merge branch 'develop' and bump Version to v0.18.0
Browse files Browse the repository at this point in the history
  • Loading branch information
bwmarrin committed Dec 27, 2017
2 parents 9ce7456 + b58212a commit 4a33b9b
Show file tree
Hide file tree
Showing 16 changed files with 330 additions and 275 deletions.
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
language: go
go:
- 1.6
- 1.7
- 1.8
- 1.9
install:
- go get github.com/bwmarrin/discordgo
- go get -v .
- go get -v github.com/golang/lint/golint
script:
- diff <(gofmt -d .) <(echo -n)
- go vet -x ./...
- golint ./...
- golint -set_exit_status ./...
- go test -v -race ./...
4 changes: 2 additions & 2 deletions discord.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (
)

// VERSION of DiscordGo, follows Semantic Versioning. (http://semver.org/)
const VERSION = "0.17.0"
const VERSION = "0.18.0"

// ErrMFA will be risen by New when the user has 2FA.
var ErrMFA = errors.New("account has 2FA enabled")
Expand Down Expand Up @@ -50,7 +50,7 @@ func New(args ...interface{}) (s *Session, err error) {
// Create an empty Session interface.
s = &Session{
State: NewState(),
ratelimiter: NewRatelimiter(),
Ratelimiter: NewRatelimiter(),
StateEnabled: true,
Compress: true,
ShouldReconnectOnError: true,
Expand Down
99 changes: 13 additions & 86 deletions discord_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package discordgo

import (
"fmt"
"os"
"runtime"
"sync/atomic"
Expand All @@ -14,29 +15,25 @@ var (
dg *Session // Stores a global discordgo user session
dgBot *Session // Stores a global discordgo bot session

envToken = os.Getenv("DG_TOKEN") // Token to use when authenticating the user account
envBotToken = os.Getenv("DGB_TOKEN") // Token to use when authenticating the bot account
envEmail = os.Getenv("DG_EMAIL") // Email to use when authenticating
envPassword = os.Getenv("DG_PASSWORD") // Password to use when authenticating
envGuild = os.Getenv("DG_GUILD") // Guild ID to use for tests
envChannel = os.Getenv("DG_CHANNEL") // Channel ID to use for tests
// envUser = os.Getenv("DG_USER") // User ID to use for tests
envAdmin = os.Getenv("DG_ADMIN") // User ID of admin user to use for tests
envToken = os.Getenv("DGU_TOKEN") // Token to use when authenticating the user account
envBotToken = os.Getenv("DGB_TOKEN") // Token to use when authenticating the bot account
envGuild = os.Getenv("DG_GUILD") // Guild ID to use for tests
envChannel = os.Getenv("DG_CHANNEL") // Channel ID to use for tests
envAdmin = os.Getenv("DG_ADMIN") // User ID of admin user to use for tests
)

func init() {
fmt.Println("Init is being called.")
if envBotToken != "" {
if d, err := New(envBotToken); err == nil {
dgBot = d
}
}

if envEmail == "" || envPassword == "" || envToken == "" {
return
}

if d, err := New(envEmail, envPassword, envToken); err == nil {
if d, err := New(envToken); err == nil {
dg = d
} else {
fmt.Println("dg is nil, error", err)
}
}

Expand Down Expand Up @@ -67,58 +64,11 @@ func TestInvalidToken(t *testing.T) {
}
}

// TestInvalidUserPass tests the New() function with an invalid Email and Pass
func TestInvalidEmailPass(t *testing.T) {

_, err := New("invalidemail", "invalidpassword")
if err == nil {
t.Errorf("New(InvalidEmail, InvalidPass) returned nil error.")
}

}

// TestInvalidPass tests the New() function with an invalid Password
func TestInvalidPass(t *testing.T) {

if envEmail == "" {
t.Skip("Skipping New(username,InvalidPass), DG_EMAIL not set")
return
}
_, err := New(envEmail, "invalidpassword")
if err == nil {
t.Errorf("New(Email, InvalidPass) returned nil error.")
}
}

// TestNewUserPass tests the New() function with a username and password.
// This should return a valid Session{}, a valid Session.Token.
func TestNewUserPass(t *testing.T) {

if envEmail == "" || envPassword == "" {
t.Skip("Skipping New(username,password), DG_EMAIL or DG_PASSWORD not set")
return
}

d, err := New(envEmail, envPassword)
if err != nil {
t.Fatalf("New(user,pass) returned error: %+v", err)
}

if d == nil {
t.Fatal("New(user,pass), d is nil, should be Session{}")
}

if d.Token == "" {
t.Fatal("New(user,pass), d.Token is empty, should be a valid Token.")
}
}

// TestNewToken tests the New() function with a Token. This should return
// the same as the TestNewUserPass function.
// TestNewToken tests the New() function with a Token.
func TestNewToken(t *testing.T) {

if envToken == "" {
t.Skip("Skipping New(token), DG_TOKEN not set")
t.Skip("Skipping New(token), DGU_TOKEN not set")
}

d, err := New(envToken)
Expand All @@ -135,32 +85,9 @@ func TestNewToken(t *testing.T) {
}
}

// TestNewUserPassToken tests the New() function with a username, password and token.
// This should return the same as the TestNewUserPass function.
func TestNewUserPassToken(t *testing.T) {

if envEmail == "" || envPassword == "" || envToken == "" {
t.Skip("Skipping New(username,password,token), DG_EMAIL, DG_PASSWORD or DG_TOKEN not set")
return
}

d, err := New(envEmail, envPassword, envToken)
if err != nil {
t.Fatalf("New(user,pass,token) returned error: %+v", err)
}

if d == nil {
t.Fatal("New(user,pass,token), d is nil, should be Session{}")
}

if d.Token == "" {
t.Fatal("New(user,pass,token), d.Token is empty, should be a valid Token.")
}
}

func TestOpenClose(t *testing.T) {
if envToken == "" {
t.Skip("Skipping TestClose, DG_TOKEN not set")
t.Skip("Skipping TestClose, DGU_TOKEN not set")
}

d, err := New(envToken)
Expand Down
5 changes: 3 additions & 2 deletions endpoints.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ var (
EndpointUserNotes = func(uID string) string { return EndpointUsers + "@me/notes/" + uID }

EndpointGuild = func(gID string) string { return EndpointGuilds + gID }
EndpointGuildInivtes = func(gID string) string { return EndpointGuilds + gID + "/invites" }
EndpointGuildChannels = func(gID string) string { return EndpointGuilds + gID + "/channels" }
EndpointGuildMembers = func(gID string) string { return EndpointGuilds + gID + "/members" }
EndpointGuildMember = func(gID, uID string) string { return EndpointGuilds + gID + "/members/" + uID }
Expand All @@ -98,7 +97,7 @@ var (
EndpointChannelMessages = func(cID string) string { return EndpointChannels + cID + "/messages" }
EndpointChannelMessage = func(cID, mID string) string { return EndpointChannels + cID + "/messages/" + mID }
EndpointChannelMessageAck = func(cID, mID string) string { return EndpointChannels + cID + "/messages/" + mID + "/ack" }
EndpointChannelMessagesBulkDelete = func(cID string) string { return EndpointChannel(cID) + "/messages/bulk_delete" }
EndpointChannelMessagesBulkDelete = func(cID string) string { return EndpointChannel(cID) + "/messages/bulk-delete" }
EndpointChannelMessagesPins = func(cID string) string { return EndpointChannel(cID) + "/pins" }
EndpointChannelMessagePin = func(cID, mID string) string { return EndpointChannel(cID) + "/pins/" + mID }

Expand All @@ -122,6 +121,8 @@ var (
EndpointRelationship = func(uID string) string { return EndpointRelationships() + "/" + uID }
EndpointRelationshipsMutual = func(uID string) string { return EndpointUsers + uID + "/relationships" }

EndpointGuildCreate = EndpointAPI + "guilds"

EndpointInvite = func(iID string) string { return EndpointAPI + "invite/" + iID }

EndpointIntegrationsJoin = func(iID string) string { return EndpointAPI + "integrations/" + iID + "/join" }
Expand Down
2 changes: 1 addition & 1 deletion event.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ type EventHandler interface {
Type() string

// Handle is called whenever an event of Type() happens.
// It is the recievers responsibility to type assert that the interface
// It is the receivers responsibility to type assert that the interface
// is the expected struct.
Handle(*Session, interface{})
}
Expand Down
2 changes: 1 addition & 1 deletion examples/appmaker/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ func main() {
ap.Name = Name
ap, err = dg.ApplicationCreate(ap)
if err != nil {
fmt.Println("error creating new applicaiton,", err)
fmt.Println("error creating new application,", err)
return
}

Expand Down
28 changes: 18 additions & 10 deletions logging.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const (
LogError int = iota

// LogWarning level is used for very abnormal events and errors that are
// also returend to a calling function.
// also returned to a calling function.
LogWarning

// LogInformational level is used for normal non-error activity
Expand All @@ -34,26 +34,34 @@ const (
LogDebug
)

// Logger can be used to replace the standard logging for discordgo
var Logger func(msgL, caller int, format string, a ...interface{})

// msglog provides package wide logging consistancy for discordgo
// the format, a... portion this command follows that of fmt.Printf
// msgL : LogLevel of the message
// caller : 1 + the number of callers away from the message source
// format : Printf style message format
// a ... : comma seperated list of values to pass
// a ... : comma separated list of values to pass
func msglog(msgL, caller int, format string, a ...interface{}) {

pc, file, line, _ := runtime.Caller(caller)
if Logger != nil {
Logger(msgL, caller, format, a...)
} else {

pc, file, line, _ := runtime.Caller(caller)

files := strings.Split(file, "/")
file = files[len(files)-1]
files := strings.Split(file, "/")
file = files[len(files)-1]

name := runtime.FuncForPC(pc).Name()
fns := strings.Split(name, ".")
name = fns[len(fns)-1]
name := runtime.FuncForPC(pc).Name()
fns := strings.Split(name, ".")
name = fns[len(fns)-1]

msg := fmt.Sprintf(format, a...)
msg := fmt.Sprintf(format, a...)

log.Printf("[DG%d] %s:%d:%s() %s\n", msgL, file, line, name, msg)
log.Printf("[DG%d] %s:%d:%s() %s\n", msgL, file, line, name, msg)
}
}

// helper function that wraps msglog for the Session struct
Expand Down
2 changes: 1 addition & 1 deletion oauth2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
func ExampleApplication() {

// Authentication Token pulled from environment variable DG_TOKEN
Token := os.Getenv("DG_TOKEN")
Token := os.Getenv("DGU_TOKEN")
if Token == "" {
return
}
Expand Down
49 changes: 30 additions & 19 deletions ratelimit.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ func NewRatelimiter() *RateLimiter {
}
}

// getBucket retrieves or creates a bucket
func (r *RateLimiter) getBucket(key string) *Bucket {
// GetBucket retrieves or creates a bucket
func (r *RateLimiter) GetBucket(key string) *Bucket {
r.Lock()
defer r.Unlock()

Expand All @@ -51,7 +51,7 @@ func (r *RateLimiter) getBucket(key string) *Bucket {
}

b := &Bucket{
remaining: 1,
Remaining: 1,
Key: key,
global: r.global,
}
Expand All @@ -68,41 +68,52 @@ func (r *RateLimiter) getBucket(key string) *Bucket {
return b
}

// LockBucket Locks until a request can be made
func (r *RateLimiter) LockBucket(bucketID string) *Bucket {

b := r.getBucket(bucketID)

b.Lock()

// GetWaitTime returns the duration you should wait for a Bucket
func (r *RateLimiter) GetWaitTime(b *Bucket, minRemaining int) time.Duration {
// If we ran out of calls and the reset time is still ahead of us
// then we need to take it easy and relax a little
if b.remaining < 1 && b.reset.After(time.Now()) {
time.Sleep(b.reset.Sub(time.Now()))

if b.Remaining < minRemaining && b.reset.After(time.Now()) {
return b.reset.Sub(time.Now())
}

// Check for global ratelimits
sleepTo := time.Unix(0, atomic.LoadInt64(r.global))
if now := time.Now(); now.Before(sleepTo) {
time.Sleep(sleepTo.Sub(now))
return sleepTo.Sub(now)
}

return 0
}

// LockBucket Locks until a request can be made
func (r *RateLimiter) LockBucket(bucketID string) *Bucket {
return r.LockBucketObject(r.GetBucket(bucketID))
}

// LockBucketObject Locks an already resolved bucket until a request can be made
func (r *RateLimiter) LockBucketObject(b *Bucket) *Bucket {
b.Lock()

if wait := r.GetWaitTime(b, 1); wait > 0 {
time.Sleep(wait)
}

b.remaining--
b.Remaining--
return b
}

// Bucket represents a ratelimit bucket, each bucket gets ratelimited individually (-global ratelimits)
type Bucket struct {
sync.Mutex
Key string
remaining int
Remaining int
limit int
reset time.Time
global *int64

lastReset time.Time
customRateLimit *customRateLimit
Userdata interface{}
}

// Release unlocks the bucket and reads the headers to update the buckets ratelimit info
Expand All @@ -113,10 +124,10 @@ func (b *Bucket) Release(headers http.Header) error {
// Check if the bucket uses a custom ratelimiter
if rl := b.customRateLimit; rl != nil {
if time.Now().Sub(b.lastReset) >= rl.reset {
b.remaining = rl.requests - 1
b.Remaining = rl.requests - 1
b.lastReset = time.Now()
}
if b.remaining < 1 {
if b.Remaining < 1 {
b.reset = time.Now().Add(rl.reset)
}
return nil
Expand Down Expand Up @@ -176,7 +187,7 @@ func (b *Bucket) Release(headers http.Header) error {
if err != nil {
return err
}
b.remaining = int(parsedRemaining)
b.Remaining = int(parsedRemaining)
}

return nil
Expand Down
Loading

0 comments on commit 4a33b9b

Please sign in to comment.