From 732247456d966db16070a71e9936b97e405214e2 Mon Sep 17 00:00:00 2001 From: Bruce Marriner Date: Fri, 8 Jan 2016 09:11:33 -0600 Subject: [PATCH 1/9] Got to start somewhere. --- tests/discordgo_test.go | 98 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 tests/discordgo_test.go diff --git a/tests/discordgo_test.go b/tests/discordgo_test.go new file mode 100644 index 0000000..6ce1c48 --- /dev/null +++ b/tests/discordgo_test.go @@ -0,0 +1,98 @@ +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() + } +} + +////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////// 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 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 envToken == "" { + t.Skip("Skipping New(token), DG_TOKEN not set") + return + } + + 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 + +} From 29a6b3abefc210384531e54e04b7c939675b56b5 Mon Sep 17 00:00:00 2001 From: Bruce Marriner Date: Fri, 8 Jan 2016 09:17:59 -0600 Subject: [PATCH 2/9] Renamed example to examples --- {example => examples}/api_basic/api_basic.go | 0 {example => examples}/new_basic/new_basic.go | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename {example => examples}/api_basic/api_basic.go (100%) rename {example => examples}/new_basic/new_basic.go (100%) diff --git a/example/api_basic/api_basic.go b/examples/api_basic/api_basic.go similarity index 100% rename from example/api_basic/api_basic.go rename to examples/api_basic/api_basic.go diff --git a/example/new_basic/new_basic.go b/examples/new_basic/new_basic.go similarity index 100% rename from example/new_basic/new_basic.go rename to examples/new_basic/new_basic.go From aebe839e1324844a75e20ca9a24e342cd101bca8 Mon Sep 17 00:00:00 2001 From: Bruce Marriner Date: Fri, 8 Jan 2016 09:20:07 -0600 Subject: [PATCH 3/9] Updated readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9023210..44744c3 100644 --- a/README.md +++ b/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 From 4f3b4b7a4bff2bba0fd1076e61c08362d553030b Mon Sep 17 00:00:00 2001 From: Bruce Marriner Date: Fri, 8 Jan 2016 10:18:31 -0600 Subject: [PATCH 4/9] Added isConnected helper func --- tests/discordgo_test.go | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/tests/discordgo_test.go b/tests/discordgo_test.go index 6ce1c48..d736cd9 100644 --- a/tests/discordgo_test.go +++ b/tests/discordgo_test.go @@ -43,6 +43,26 @@ func waitBoolEqual(timeout time.Duration, check *bool, want bool) bool { } } +// 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 @@ -60,6 +80,11 @@ func TestNew(t *testing.T) { // 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 @@ -70,9 +95,13 @@ func TestNewUserPass(t *testing.T) { // 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") - return } d, err := New(envToken) From 7fc9331e04fcc317e1dbc4747bdd96bc50c8edb8 Mon Sep 17 00:00:00 2001 From: Bruce Marriner Date: Fri, 8 Jan 2016 10:29:01 -0600 Subject: [PATCH 5/9] Created message.go to store code related to the Message struct. --- message.go | 26 ++++++++++++++++++++++++++ structs.go | 10 ---------- 2 files changed, 26 insertions(+), 10 deletions(-) create mode 100644 message.go diff --git a/message.go b/message.go new file mode 100644 index 0000000..86e6701 --- /dev/null +++ b/message.go @@ -0,0 +1,26 @@ +// Discordgo - Discord bindings for Go +// Available at https://github.com/bwmarrin/discordgo + +// Copyright 2015-2016 Bruce Marriner . 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" +) + +// ContentWithMentionsReplaced will replace all @ 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 +} diff --git a/structs.go b/structs.go index ca6ed5b..7763c6a 100644 --- a/structs.go +++ b/structs.go @@ -116,16 +116,6 @@ type Message struct { ChannelID string `json:"channel_id"` } -// ContentWithMentionsReplaced will replace all @ 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 } From 831fb29915c1e47dda0f42a63ae576df661c3d3d Mon Sep 17 00:00:00 2001 From: Bruce Marriner Date: Fri, 8 Jan 2016 10:33:29 -0600 Subject: [PATCH 6/9] Moved Message struct and related structs into message.go --- message.go | 23 +++++++++++++++++++++++ structs.go | 23 ----------------------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/message.go b/message.go index 86e6701..a833863 100644 --- a/message.go +++ b/message.go @@ -14,6 +14,29 @@ import ( "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 @ mentions with the // username of the mention. func (m *Message) ContentWithMentionsReplaced() string { diff --git a/structs.go b/structs.go index 7763c6a..4ca28d9 100644 --- a/structs.go +++ b/structs.go @@ -101,29 +101,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"` -} - -// 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"` From e1e20260d3746dd53e4050c68e1deb4c4a576417 Mon Sep 17 00:00:00 2001 From: Bruce Marriner Date: Fri, 8 Jan 2016 10:46:27 -0600 Subject: [PATCH 7/9] Fixed operation in Event struct so it actually works. --- structs.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/structs.go b/structs.go index 4ca28d9..e89c27a 100644 --- a/structs.go +++ b/structs.go @@ -78,6 +78,7 @@ 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 @@ -269,7 +270,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"` } From 6e4f495f6a6f0a5fe5dec19a19834868478b19ba Mon Sep 17 00:00:00 2001 From: Bruce Marriner Date: Fri, 8 Jan 2016 10:52:27 -0600 Subject: [PATCH 8/9] Removed unused AutoMention, renamed VoiceOP2 --- structs.go | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/structs.go b/structs.go index e89c27a..5dddf65 100644 --- a/structs.go +++ b/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) // should Event be *Event? @@ -85,7 +82,7 @@ type Session struct { VEndpoint string VGuildID string VChannelID string - Vop2 VoiceOP2 + Vop2 voiceOP2 UDPConn *net.UDPConn // Managed state object, updated with events. From 59ed5b0b4045b1f7e931f820b5c37ac51763d73f Mon Sep 17 00:00:00 2001 From: Bruce Marriner Date: Fri, 8 Jan 2016 10:53:33 -0600 Subject: [PATCH 9/9] The start of the voice rework begins. --- voice.go | 43 ++++++++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/voice.go b/voice.go index 7345a91..ea6754d 100644 --- a/voice.go +++ b/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