1package main 2 3import ( 4 "flag" 5 "fmt" 6 "time" 7 8 "github.com/bwmarrin/discordgo" 9 "github.com/pion/rtp" 10 "github.com/pion/webrtc/v3/pkg/media" 11 "github.com/pion/webrtc/v3/pkg/media/oggwriter" 12) 13 14// Variables used for command line parameters 15var ( 16 Token string 17 ChannelID string 18 GuildID string 19) 20 21func init() { 22 flag.StringVar(&Token, "t", "", "Bot Token") 23 flag.StringVar(&GuildID, "g", "", "Guild in which voice channel exists") 24 flag.StringVar(&ChannelID, "c", "", "Voice channel to connect to") 25 flag.Parse() 26} 27 28func createPionRTPPacket(p *discordgo.Packet) *rtp.Packet { 29 return &rtp.Packet{ 30 Header: rtp.Header{ 31 Version: 2, 32 // Taken from Discord voice docs 33 PayloadType: 0x78, 34 SequenceNumber: p.Sequence, 35 Timestamp: p.Timestamp, 36 SSRC: p.SSRC, 37 }, 38 Payload: p.Opus, 39 } 40} 41 42func handleVoice(c chan *discordgo.Packet) { 43 files := make(map[uint32]media.Writer) 44 for p := range c { 45 file, ok := files[p.SSRC] 46 if !ok { 47 var err error 48 file, err = oggwriter.New(fmt.Sprintf("%d.ogg", p.SSRC), 48000, 2) 49 if err != nil { 50 fmt.Printf("failed to create file %d.ogg, giving up on recording: %v\n", p.SSRC, err) 51 return 52 } 53 files[p.SSRC] = file 54 } 55 // Construct pion RTP packet from DiscordGo's type. 56 rtp := createPionRTPPacket(p) 57 err := file.WriteRTP(rtp) 58 if err != nil { 59 fmt.Printf("failed to write to file %d.ogg, giving up on recording: %v\n", p.SSRC, err) 60 } 61 } 62 63 // Once we made it here, we're done listening for packets. Close all files 64 for _, f := range files { 65 f.Close() 66 } 67} 68 69func main() { 70 s, err := discordgo.New("Bot " + Token) 71 if err != nil { 72 fmt.Println("error creating Discord session:", err) 73 return 74 } 75 defer s.Close() 76 77 // We only really care about receiving voice state updates. 78 s.Identify.Intents = discordgo.IntentsGuildVoiceStates 79 80 err = s.Open() 81 if err != nil { 82 fmt.Println("error opening connection:", err) 83 return 84 } 85 86 v, err := s.ChannelVoiceJoin(GuildID, ChannelID, true, false) 87 if err != nil { 88 fmt.Println("failed to join voice channel:", err) 89 return 90 } 91 92 go func() { 93 time.Sleep(10 * time.Second) 94 close(v.OpusRecv) 95 v.Close() 96 }() 97 98 handleVoice(v.OpusRecv) 99} 100