diff --git a/go.mod b/go.mod index bc87594..795de84 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/gorilla/websocket v1.4.2 github.com/pkg/errors v0.9.1 github.com/sasha-s/go-csync v0.0.0-20160729053059-3bc6c8bdb3fa + golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 golang.org/x/net v0.0.0-20200202094626-16171245cfb2 // indirect golang.org/x/time v0.0.0-20191024005414-555d28b269f0 ) diff --git a/voice/integration_test.go b/voice/integration_test.go new file mode 100644 index 0000000..dd2c2e5 --- /dev/null +++ b/voice/integration_test.go @@ -0,0 +1,144 @@ +// +build integration + +package voice + +import ( + "encoding/binary" + "io" + "log" + "os" + "testing" + + "github.com/diamondburned/arikawa/discord" + "github.com/diamondburned/arikawa/state" +) + +type testConfig struct { + BotToken string + VoiceChID discord.Snowflake +} + +func mustConfig(t *testing.T) testConfig { + var token = os.Getenv("BOT_TOKEN") + if token == "" { + t.Fatal("Missing $BOT_TOKEN") + } + + var sid = os.Getenv("VOICE_ID") + if sid == "" { + t.Fatal("Missing $VOICE_ID") + } + + id, err := discord.ParseSnowflake(sid) + if err != nil { + t.Fatal("Invalid $VOICE_ID:", err) + } + + return testConfig{ + BotToken: token, + VoiceChID: id, + } +} + +// file is only a few bytes lolmao +func nicoReader(t *testing.T) (read func() []byte) { + f, err := os.Open("testdata/nico.dca") + if err != nil { + t.Fatal("Failed to open nico.dca:", err) + } + + t.Cleanup(func() { + f.Close() + }) + + var lenbuf [4]byte + + return func() []byte { + if _, err := io.ReadFull(f, lenbuf[:]); !catchRead(t, err) { + return nil + } + + // Read the integer + framelen := int(binary.LittleEndian.Uint32(lenbuf[:])) + + // Read exactly frame + frame := make([]byte, framelen) + + if _, err := io.ReadFull(f, frame); !catchRead(t, err) { + return nil + } + + return frame + } +} + +func catchRead(t *testing.T, err error) bool { + t.Helper() + + if err == io.EOF { + return false + } + if err != nil { + t.Fatal("Failed to read:", err) + } + return true +} + +func TestIntegration(t *testing.T) { + config := mustConfig(t) + + WSDebug = func(v ...interface{}) { + log.Println(v...) + } + // heart.Debug = func(v ...interface{}) { + // log.Println(append([]interface{}{"Pacemaker:"}, v...)...) + // } + + s, err := state.New("Bot " + config.BotToken) + if err != nil { + t.Fatal("Failed to create a new session:", err) + } + + v := NewVoice(s) + + if err := s.Open(); err != nil { + t.Fatal("Failed to connect:", err) + } + + // Validate the given voice channel. + c, err := s.Channel(config.VoiceChID) + if err != nil { + t.Fatal("Failed to get channel:", err) + } + if c.Type != discord.GuildVoice { + t.Fatal("Channel isn't a guild voice channel.") + } + + conn, err := v.JoinChannel(c.GuildID, c.ID, false, false) + if err != nil { + t.Fatal("Failed to join channel:", err) + } + + // Grab the file in the local test data. + read := nicoReader(t) + + // Trigger speaking. + if err := conn.Speaking(Microphone); err != nil { + t.Fatal("Failed to start speaking:", err) + } + + // Copy the audio? + for bytes := read(); bytes != nil; bytes = read() { + conn.OpusSend <- bytes + // conn.Write(bytes) + } + + // Finish speaking. + if err := conn.StopSpeaking(); err != nil { + t.Fatal("Failed to stop speaking:", err) + } + + if err := conn.Disconnect(s.Gateway); err != nil { + t.Fatal("Failed to disconnect:", err) + } +} diff --git a/voice/testdata/nico.dca b/voice/testdata/nico.dca new file mode 100644 index 0000000..e745c2c Binary files /dev/null and b/voice/testdata/nico.dca differ