Explorar o código

Merge remote-tracking branch 'bwmarrin/develop' into pointers

Chris Rhodes %!s(int64=9) %!d(string=hai) anos
pai
achega
ea33680562
Modificáronse 7 ficheiros con 215 adicións e 53 borrados
  1. 2 2
      README.md
  2. 0 0
      examples/api_basic/api_basic.go
  3. 0 0
      examples/new_basic/new_basic.go
  4. 49 0
      message.go
  5. 5 40
      structs.go
  6. 127 0
      tests/discordgo_test.go
  7. 32 11
      voice.go

+ 2 - 2
README.md

@@ -63,8 +63,8 @@ that information in a nice format.
 Below is a list of examples and other projects using Discordgo.  Please submit 
 an issue if you would like your project added or removed from this list 
 
-- [Basic - New](https://github.com/bwmarrin/discordgo/tree/develop/example/new_basic) A basic example using the easy New() helper function
-- [Basic - API](https://github.com/bwmarrin/discordgo/tree/develop/example/api_basic) A basic example using the low level API functions.
+- [Basic - New](https://github.com/bwmarrin/discordgo/tree/develop/examples/new_basic) A basic example using the easy New() helper function
+- [Basic - API](https://github.com/bwmarrin/discordgo/tree/develop/examples/api_basic) A basic example using the low level API functions.
 - [Bruxism](https://github.com/iopred/bruxism) A chat bot for YouTube and Discord
 - [GoGerard](https://github.com/GoGerard/GoGerard) A modern bot for Discord
 - [Digo](https://github.com/sethdmoore/digo) A pluggable bot for your Discord server

example/api_basic/api_basic.go → examples/api_basic/api_basic.go


example/new_basic/new_basic.go → examples/new_basic/new_basic.go


+ 49 - 0
message.go

@@ -0,0 +1,49 @@
+// Discordgo - Discord bindings for Go
+// Available at https://github.com/bwmarrin/discordgo
+
+// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file contains code related to the Message struct
+
+package discordgo
+
+import (
+	"fmt"
+	"strings"
+)
+
+// A Message stores all data related to a specific Discord message.
+type Message struct {
+	ID              string        `json:"id"`
+	Author          User          `json:"author"`
+	Content         string        `json:"content"`
+	Attachments     []*Attachment `json:"attachments"`
+	Tts             bool          `json:"tts"`
+	Embeds          []*Embed      `json:"embeds"`
+	Timestamp       string        `json:"timestamp"`
+	MentionEveryone bool          `json:"mention_everyone"`
+	EditedTimestamp string        `json:"edited_timestamp"`
+	Mentions        []*User       `json:"mentions"`
+	ChannelID       string        `json:"channel_id"`
+}
+
+// An Attachment stores data for message attachments.
+type Attachment struct { //TODO figure this out
+}
+
+// An Embed stores data for message embeds.
+type Embed struct { // TODO figure this out
+}
+
+// ContentWithMentionsReplaced will replace all @<id> mentions with the
+// username of the mention.
+func (m *Message) ContentWithMentionsReplaced() string {
+	content := m.Content
+	for _, user := range m.Mentions {
+		content = strings.Replace(content, fmt.Sprintf("<@%s>", user.ID),
+			fmt.Sprintf("@%s", user.Username), -1)
+	}
+	return content
+}

+ 5 - 40
structs.go

@@ -13,9 +13,7 @@ package discordgo
 
 import (
 	"encoding/json"
-	"fmt"
 	"net"
-	"strings"
 	"sync"
 	"time"
 
@@ -27,9 +25,8 @@ import (
 // Debug : If set to ture debug logging will be displayed.
 type Session struct {
 	// General configurable settings.
-	Token       string // Authentication token for this session
-	Debug       bool   // Debug for printing JSON request/responses
-	AutoMention bool   // if set to True, ChannelSendMessage will auto mention <@ID>
+	Token string // Authentication token for this session
+	Debug bool   // Debug for printing JSON request/responses
 
 	// Settable Callback functions for Websocket Events
 	OnEvent                   func(*Session, *Event)
@@ -78,13 +75,14 @@ type Session struct {
 	// Everything below here is used for Voice testing.
 	// This stuff is almost guarenteed to change a lot
 	// and is even a bit hackish right now.
+	Voice      *voice
 	VwsConn    *websocket.Conn // new for voice
 	VSessionID string
 	VToken     string
 	VEndpoint  string
 	VGuildID   string
 	VChannelID string
-	Vop2       VoiceOP2
+	Vop2       voiceOP2
 	UDPConn    *net.UDPConn
 
 	// Managed state object, updated with events.
@@ -101,39 +99,6 @@ type Session struct {
 	listenChan chan struct{}
 }
 
-// A Message stores all data related to a specific Discord message.
-type Message struct {
-	ID              string        `json:"id"`
-	Author          User          `json:"author"`
-	Content         string        `json:"content"`
-	Attachments     []*Attachment `json:"attachments"`
-	Tts             bool          `json:"tts"`
-	Embeds          []*Embed      `json:"embeds"`
-	Timestamp       string        `json:"timestamp"`
-	MentionEveryone bool          `json:"mention_everyone"`
-	EditedTimestamp string        `json:"edited_timestamp"`
-	Mentions        []*User       `json:"mentions"`
-	ChannelID       string        `json:"channel_id"`
-}
-
-// ContentWithMentionsReplaced will replace all @<id> mentions with the
-// username of the mention.
-func (m *Message) ContentWithMentionsReplaced() string {
-	content := m.Content
-	for _, user := range m.Mentions {
-		content = strings.Replace(content, fmt.Sprintf("<@%s>", user.ID), fmt.Sprintf("@%s", user.Username), -1)
-	}
-	return content
-}
-
-// An Attachment stores data for message attachments.
-type Attachment struct { //TODO figure this out
-}
-
-// An Embed stores data for message embeds.
-type Embed struct { // TODO figure this out
-}
-
 // A VoiceRegion stores data for a specific voice region server.
 type VoiceRegion struct {
 	ID       string `json:"id"`
@@ -302,7 +267,7 @@ type Settings struct {
 type Event struct {
 	Type      string          `json:"t"`
 	State     int             `json:"s"`
-	Operation int             `json:"o"`
+	Operation int             `json:"op"`
 	Direction int             `json:"dir"`
 	RawData   json.RawMessage `json:"d"`
 }

+ 127 - 0
tests/discordgo_test.go

@@ -0,0 +1,127 @@
+package discordgo_test
+
+import (
+	"os"
+	"runtime"
+	"testing"
+	"time"
+
+	. "github.com/bwmarrin/discordgo"
+)
+
+//////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////// VARS NEEDED FOR TESTING
+var (
+	dg *Session // Stores global discordgo session
+
+	envToken    string = os.Getenv("DG_TOKEN")    // Token to use when authenticating
+	envUsername string = os.Getenv("DG_USERNAME") // Username to use when authenticating
+	envPassword string = os.Getenv("DG_PASSWORD") // Password to use when authenticating
+	envGuild    string = os.Getenv("DG_GUILD")    // Guild ID to use for tests
+	envChannel  string = os.Getenv("DG_CHANNEL")  // Channel ID to use for tests
+	envUser     string = os.Getenv("DG_USER")     // User ID to use for tests
+	envAdmin    string = os.Getenv("DG_ADMIN")    // User ID of admin user to use for tests
+)
+
+//////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////// HELPER FUNCTIONS USED FOR TESTING
+
+// This waits x time for the check bool to be the want bool
+func waitBoolEqual(timeout time.Duration, check *bool, want bool) bool {
+
+	start := time.Now()
+	for {
+		if *check == want {
+			return true
+		}
+
+		if time.Since(start) > timeout {
+			return false
+		}
+
+		runtime.Gosched()
+	}
+}
+
+// Checks if we're connected to Discord
+func isConnected() bool {
+
+	if dg == nil {
+		return false
+	}
+
+	if dg.Token == "" {
+		return false
+	}
+
+	// Need a way to see if the ws connection is nil
+
+	if !waitBoolEqual(10*time.Second, &dg.DataReady, true) {
+		return false
+	}
+
+	return true
+}
+
+//////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////// START OF TESTS
+
+// TestNew tests the New() function without any arguments.  This should return
+// a valid Session{} struct and no errors.
+func TestNew(t *testing.T) {
+
+	_, err := New()
+	if err != nil {
+		t.Errorf("New() returned error: %+v", err)
+	}
+}
+
+// TestNewUserPass tests the New() function with a username and password.
+// This should return a valid Session{}, a valid Session.Token, and open
+// a websocket connection to Discord.
+func TestNewUserPass(t *testing.T) {
+
+	if isConnected() {
+		t.Skip("Skipping New(username,password), already connected.")
+	}
+
+	if envUsername == "" || envPassword == "" {
+		t.Skip("Skipping New(username,password), DG_USERNAME or DG_PASSWORD not set")
+		return
+	}
+	// Not testing yet.
+}
+
+// TestNewToken tests the New() function with a Token.  This should return
+// the same as the TestNewUserPass function.
+func TestNewToken(t *testing.T) {
+
+	if isConnected() {
+		t.Skip("Skipping New(token), already connected.")
+	}
+
+	if envToken == "" {
+		t.Skip("Skipping New(token), DG_TOKEN not set")
+	}
+
+	d, err := New(envToken)
+	if err != nil {
+		t.Fatalf("New(envToken) returned error: %+v", err)
+	}
+
+	if d == nil {
+		t.Fatal("New(envToken), d is nil, should be Session{}")
+	}
+
+	if d.Token == "" {
+		t.Fatal("New(envToken), d.Token is empty, should be a valid Token.")
+	}
+
+	if !waitBoolEqual(10*time.Second, &d.DataReady, true) {
+		t.Fatal("New(envToken), d.DataReady is false after 10 seconds.  Should be true.")
+	}
+
+	t.Log("Successfully connected to Discord.")
+	dg = d
+
+}

+ 32 - 11
voice.go

@@ -18,23 +18,44 @@ import (
 	"fmt"
 	"net"
 	"strings"
+	"sync"
 	"time"
 
 	"github.com/gorilla/websocket"
 )
 
-// A VEvent is the initial structure for voice websocket events.  I think
-// I can reuse the data websocket structure here.
-type VEvent struct {
-	Type      string          `json:"t"`
-	State     int             `json:"s"`
-	Operation int             `json:"op"`
-	RawData   json.RawMessage `json:"d"`
+// A Voice struct holds all data and functions related to Discord Voice support.
+// NOTE: This is not used right at this moment, but it will be used soon.
+type voice struct {
+	Ready bool
+	WS    *voiceWS
+	UDP   *voiceUDP
+
+	SessionID string
+	Token     string
+	Endpoint  string
+	GuildID   string
+	ChannelID string
+	OP2       *voiceOP2
 }
 
-// A VoiceOP2 stores the data for voice operation 2 websocket events
+type voiceWS struct {
+	Ready bool
+	Chan  chan struct{}
+	Lock  sync.Mutex
+	Conn  *websocket.Conn
+}
+
+type voiceUDP struct {
+	Ready bool
+	Chan  chan struct{}
+	Lock  sync.Mutex
+	Conn  *net.UDPConn
+}
+
+// A voiceOP2 stores the data for voice operation 2 websocket events
 // which is sort of like the voice READY packet
-type VoiceOP2 struct {
+type voiceOP2 struct {
 	SSRC              uint32        `json:"ssrc"`
 	Port              int           `json:"port"`
 	Modes             []string      `json:"modes"`
@@ -117,7 +138,7 @@ func (s *Session) VoiceEvent(messageType int, message []byte) (err error) {
 		printJSON(message)
 	}
 
-	var e VEvent
+	var e Event
 	if err := json.Unmarshal(message, &e); err != nil {
 		return err
 	}
@@ -125,7 +146,7 @@ func (s *Session) VoiceEvent(messageType int, message []byte) (err error) {
 	switch e.Operation {
 
 	case 2: // READY packet
-		var st VoiceOP2
+		var st voiceOP2
 		if err := json.Unmarshal(e.RawData, &st); err != nil {
 			fmt.Println(e.Type, err)
 			printJSON(e.RawData) // TODO: Better error logginEventg