forked from pothtonswer/discordmuffin
Merge remote-tracking branch 'bwmarrin/develop' into pointers
This commit is contained in:
commit
ea33680562
7 changed files with 215 additions and 53 deletions
|
@ -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
49
message.go
Normal 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
|
||||||
|
}
|
45
structs.go
45
structs.go
|
@ -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
127
tests/discordgo_test.go
Normal 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
|
||||||
|
|
||||||
|
}
|
43
voice.go
43
voice.go
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue