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

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 (
"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
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"
"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