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

This commit is contained in:
Chris Rhodes 2016-01-08 09:40:56 -08:00
commit ea33680562
7 changed files with 215 additions and 53 deletions

View file

@ -63,8 +63,8 @@ that information in a nice format.
Below is a list of examples and other projects using Discordgo. Please submit 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 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 - 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/example/api_basic) A basic example using the low level API functions. - [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 - [Bruxism](https://github.com/iopred/bruxism) A chat bot for YouTube and Discord
- [GoGerard](https://github.com/GoGerard/GoGerard) A modern bot for 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 - [Digo](https://github.com/sethdmoore/digo) A pluggable bot for your Discord server

49
message.go Normal file
View file

@ -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
}

View file

@ -13,9 +13,7 @@ package discordgo
import ( import (
"encoding/json" "encoding/json"
"fmt"
"net" "net"
"strings"
"sync" "sync"
"time" "time"
@ -27,9 +25,8 @@ import (
// Debug : If set to ture debug logging will be displayed. // Debug : If set to ture debug logging will be displayed.
type Session struct { type Session struct {
// General configurable settings. // General configurable settings.
Token string // Authentication token for this session Token string // Authentication token for this session
Debug bool // Debug for printing JSON request/responses Debug bool // Debug for printing JSON request/responses
AutoMention bool // if set to True, ChannelSendMessage will auto mention <@ID>
// Settable Callback functions for Websocket Events // Settable Callback functions for Websocket Events
OnEvent func(*Session, *Event) OnEvent func(*Session, *Event)
@ -78,13 +75,14 @@ type Session struct {
// Everything below here is used for Voice testing. // Everything below here is used for Voice testing.
// This stuff is almost guarenteed to change a lot // This stuff is almost guarenteed to change a lot
// and is even a bit hackish right now. // and is even a bit hackish right now.
Voice *voice
VwsConn *websocket.Conn // new for voice VwsConn *websocket.Conn // new for voice
VSessionID string VSessionID string
VToken string VToken string
VEndpoint string VEndpoint string
VGuildID string VGuildID string
VChannelID string VChannelID string
Vop2 VoiceOP2 Vop2 voiceOP2
UDPConn *net.UDPConn UDPConn *net.UDPConn
// Managed state object, updated with events. // Managed state object, updated with events.
@ -101,39 +99,6 @@ type Session struct {
listenChan chan 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. // A VoiceRegion stores data for a specific voice region server.
type VoiceRegion struct { type VoiceRegion struct {
ID string `json:"id"` ID string `json:"id"`
@ -302,7 +267,7 @@ type Settings struct {
type Event struct { type Event struct {
Type string `json:"t"` Type string `json:"t"`
State int `json:"s"` State int `json:"s"`
Operation int `json:"o"` Operation int `json:"op"`
Direction int `json:"dir"` Direction int `json:"dir"`
RawData json.RawMessage `json:"d"` RawData json.RawMessage `json:"d"`
} }

127
tests/discordgo_test.go Normal file
View file

@ -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
}

View file

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