forked from pothtonswer/discordmuffin
Add barebones voice receive example
This commit is contained in:
parent
84edff87fe
commit
38819ecd8e
4 changed files with 218 additions and 0 deletions
35
examples/voice_receive/README.md
Normal file
35
examples/voice_receive/README.md
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
<img align="right" src="http://bwmarrin.github.io/discordgo/img/discordgo.png">
|
||||||
|
|
||||||
|
## DiscordGo Voice Receive Example
|
||||||
|
|
||||||
|
This example experiments with receiving voice data from Discord. It joins
|
||||||
|
a specified voice channel, listens for 10 seconds and saves .ogg files for each
|
||||||
|
SSRC that it finds in the channel. An exercise left to the reader is to translate
|
||||||
|
these SSRCs to user IDs; see speaking update events for this information. :)
|
||||||
|
|
||||||
|
This example makes heavy use of the [Pion](https://github.com/pion) family of libraries.
|
||||||
|
Go check them out for anything to do with voice, video or WebRTC; it's a great
|
||||||
|
group of people maintaining the project!
|
||||||
|
|
||||||
|
Please note that voice receive is **not** officially supported, any may break
|
||||||
|
at essentially any time (and has in the past). This code works at the time of
|
||||||
|
its writing, but YMMV in the future.
|
||||||
|
|
||||||
|
**Join [Discord Gophers](https://discord.gg/0f1SbxBZjYoCtNPP)
|
||||||
|
Discord chat channel for support.**
|
||||||
|
|
||||||
|
### Build
|
||||||
|
|
||||||
|
To build, make sure that modules are enabled, and run:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
go build
|
||||||
|
```
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
|
||||||
|
Three flags are required: the bot's token, the guild ID containing the voice channel to join, and the ID of the voice channel to join.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./voice_receive -t MY_TOKEN -g 1234123412341234 -c 5678567856785678
|
||||||
|
```
|
7
examples/voice_receive/go.mod
Normal file
7
examples/voice_receive/go.mod
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
module github.com/bwmarrin/discordgo/examples/voice_receive
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/bwmarrin/discordgo v0.21.1
|
||||||
|
github.com/pion/rtp v1.6.0
|
||||||
|
github.com/pion/webrtc/v3 v3.0.0-20200721060053-ca3cc9d940bc
|
||||||
|
)
|
77
examples/voice_receive/go.sum
Normal file
77
examples/voice_receive/go.sum
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
github.com/bwmarrin/discordgo v0.21.1 h1:UI2PWwzvn5IFuscYcDc6QB/duhs9MUIjQ4HclcIZisc=
|
||||||
|
github.com/bwmarrin/discordgo v0.21.1/go.mod h1:c1WtWUGN6nREDmzIpyTp/iD3VYt4Fpx+bVyfBG7JE+M=
|
||||||
|
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
|
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
|
||||||
|
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||||
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/lucas-clemente/quic-go v0.7.1-0.20190401152353-907071221cf9/go.mod h1:PpMmPfPKO9nKJ/psF49ESTAGQSdfXxlg1otPbEB2nOw=
|
||||||
|
github.com/marten-seemann/qtls v0.2.3/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk=
|
||||||
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
|
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
|
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||||
|
github.com/pion/datachannel v1.4.19/go.mod h1:JzKF/zzeWgkOYwQ+KFb8JzbrUt8s63um+Qunu8VqTyw=
|
||||||
|
github.com/pion/dtls/v2 v2.0.2/go.mod h1:27PEO3MDdaCfo21heT59/vsdmZc0zMt9wQPcSlLu/1I=
|
||||||
|
github.com/pion/ice/v2 v2.0.0-rc.8/go.mod h1:BH71LWfapOO69LRT1b+Rg9/be1qQPjSZT9Pm3qQRBdY=
|
||||||
|
github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
|
||||||
|
github.com/pion/mdns v0.0.4/go.mod h1:R1sL0p50l42S5lJs91oNdUL58nm0QHrhxnSegr++qC0=
|
||||||
|
github.com/pion/quic v0.1.1/go.mod h1:zEU51v7ru8Mp4AUBJvj6psrSth5eEFNnVQK5K48oV3k=
|
||||||
|
github.com/pion/randutil v0.0.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
|
||||||
|
github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
|
||||||
|
github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
|
||||||
|
github.com/pion/rtcp v1.2.3/go.mod h1:zGhIv0RPRF0Z1Wiij22pUt5W/c9fevqSzT4jje/oK7I=
|
||||||
|
github.com/pion/rtp v1.6.0 h1:4Ssnl/T5W2LzxHj9ssYpGVEQh3YYhQFNVmSWO88MMwk=
|
||||||
|
github.com/pion/rtp v1.6.0/go.mod h1:QgfogHsMBVE/RFNno467U/KBqfUywEH+HK+0rtnwsdI=
|
||||||
|
github.com/pion/sctp v1.7.8/go.mod h1:EhpTUQu1/lcK3xI+eriS6/96fWetHGCvBi9MSsnaBN0=
|
||||||
|
github.com/pion/sdp/v2 v2.4.0/go.mod h1:L2LxrOpSTJbAns244vfPChbciR/ReU1KWfG04OpkR7E=
|
||||||
|
github.com/pion/srtp v1.5.0/go.mod h1:B+QgX5xPeQTNc1CJStJPHzOlHK66ViMDWTT0HZTCkcA=
|
||||||
|
github.com/pion/stun v0.3.5/go.mod h1:gDMim+47EeEtfWogA37n6qXZS88L5V6LqFcf+DZA2UA=
|
||||||
|
github.com/pion/transport v0.6.0/go.mod h1:iWZ07doqOosSLMhZ+FXUTq+TamDoXSllxpbGcfkCmbE=
|
||||||
|
github.com/pion/transport v0.8.10/go.mod h1:tBmha/UCjpum5hqTWhfAEs3CO4/tHSg0MYRhSzR+CZ8=
|
||||||
|
github.com/pion/transport v0.10.0/go.mod h1:BnHnUipd0rZQyTVB2SBGojFHT9CBt5C5TcsJSQGkvSE=
|
||||||
|
github.com/pion/transport v0.10.1/go.mod h1:PBis1stIILMiis0PewDw91WJeLJkyIMcEk+DwKOzf4A=
|
||||||
|
github.com/pion/turn/v2 v2.0.4/go.mod h1:1812p4DcGVbYVBTiraUmP50XoKye++AMkbfp+N27mog=
|
||||||
|
github.com/pion/udp v0.1.0/go.mod h1:BPELIjbwE9PRbd/zxI/KYBnbo7B6+oA6YuEaNE8lths=
|
||||||
|
github.com/pion/webrtc/v3 v3.0.0-20200721060053-ca3cc9d940bc h1:MEXLzhmwidVyYVKJ+Z+b+eumAc3Pz8yrms26RPHF4J4=
|
||||||
|
github.com/pion/webrtc/v3 v3.0.0-20200721060053-ca3cc9d940bc/go.mod h1:g1cyQ0CajooTxiCtJOcH+P5S2uC5cVwUhV3Rj4v0uUg=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
|
golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
|
||||||
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
99
examples/voice_receive/main.go
Normal file
99
examples/voice_receive/main.go
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/bwmarrin/discordgo"
|
||||||
|
"github.com/pion/rtp"
|
||||||
|
"github.com/pion/webrtc/v3/pkg/media"
|
||||||
|
"github.com/pion/webrtc/v3/pkg/media/oggwriter"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Variables used for command line parameters
|
||||||
|
var (
|
||||||
|
Token string
|
||||||
|
ChannelID string
|
||||||
|
GuildID string
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
flag.StringVar(&Token, "t", "", "Bot Token")
|
||||||
|
flag.StringVar(&GuildID, "g", "", "Guild in which voice channel exists")
|
||||||
|
flag.StringVar(&ChannelID, "c", "", "Voice channel to connect to")
|
||||||
|
flag.Parse()
|
||||||
|
}
|
||||||
|
|
||||||
|
func createPionRTPPacket(p *discordgo.Packet) *rtp.Packet {
|
||||||
|
return &rtp.Packet{
|
||||||
|
Header: rtp.Header{
|
||||||
|
Version: 2,
|
||||||
|
// Taken from Discord voice docs
|
||||||
|
PayloadType: 0x78,
|
||||||
|
SequenceNumber: p.Sequence,
|
||||||
|
Timestamp: p.Timestamp,
|
||||||
|
SSRC: p.SSRC,
|
||||||
|
},
|
||||||
|
Payload: p.Opus,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleVoice(c chan *discordgo.Packet) {
|
||||||
|
files := make(map[uint32]media.Writer)
|
||||||
|
for p := range c {
|
||||||
|
file, ok := files[p.SSRC]
|
||||||
|
if !ok {
|
||||||
|
var err error
|
||||||
|
file, err = oggwriter.New(fmt.Sprintf("%d.ogg", p.SSRC), 48000, 2)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("failed to create file %d.ogg, giving up on recording: %v\n", p.SSRC, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
files[p.SSRC] = file
|
||||||
|
}
|
||||||
|
// Construct pion RTP packet from DiscordGo's type.
|
||||||
|
rtp := createPionRTPPacket(p)
|
||||||
|
err := file.WriteRTP(rtp)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("failed to write to file %d.ogg, giving up on recording: %v\n", p.SSRC, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Once we made it here, we're done listening for packets. Close all files
|
||||||
|
for _, f := range files {
|
||||||
|
f.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
s, err := discordgo.New("Bot " + Token)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("error creating Discord session:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer s.Close()
|
||||||
|
|
||||||
|
// We only really care about receiving voice state updates.
|
||||||
|
s.Identify.Intents = discordgo.MakeIntent(discordgo.IntentsGuildVoiceStates)
|
||||||
|
|
||||||
|
err = s.Open()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("error opening connection:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
v, err := s.ChannelVoiceJoin(GuildID, ChannelID, true, false)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("failed to join voice channel:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
time.Sleep(10 * time.Second)
|
||||||
|
close(v.OpusRecv)
|
||||||
|
v.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
handleVoice(v.OpusRecv)
|
||||||
|
}
|
Loading…
Reference in a new issue