voice

package
v0.0.0-...-a878fe9 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 5, 2024 License: ISC Imports: 15 Imported by: 0

README

Voice

Terminology

  • Discord Gateway - The standard Discord Gateway users connect to and receive update events from
  • Discord Voice Gateway - The Discord Voice gateway that allows voice connections to be configured
  • Voice Server - What the Discord Voice Gateway allows connection to for sending of Opus voice packets over UDP
  • Voice Packet - Opus encoded UDP packet that contains audio
  • Application - Could be a custom Discord Client or Bot (nothing that is within this package)
  • Library - Code within this package

Connection Flow

  • The application would get a new *Voice instance by calling NewVoice()
  • When the application wants to connect to a voice channel they would call JoinChannel() on the stored *Voice instance

  • The library sends a Voice State Update to the Discord Gateway.
  • The library waits until it receives a Voice Server Update from the Discord Gateway.
  • Once a Voice Server Update event is received, a new connection is opened to the Discord Voice Gateway.

  • The Discord Voice Gateway will first send a Hello Event which will be used to create a new *heart.PacemakerLoop and start sending heartbeats to the Discord Voice Gateway.
  • Afterwards, an Identify Command or Resume Command is sent to the Discord Voice Gateway depending on whether the library is reconnecting.

  • The Discord Voice Gateway should then respond with a Ready Event once the connection is opened, providing the required information to connect to a Voice Server.
  • Using the information provided in the Ready Event, a new UDP connection is opened to the Voice Server and IP Discovery occurs.
  • After IP Discovery returns the Application's external ip and port it connected to the Voice Server with, the library sends a Select Protocol Event to the Discord Voice Gateway.
  • The library waits until it receives a Session Description Event from the Discord Voice Gateway.
  • Once the Session Description Event is received, Speaking Events and Voice Packets can begin to be sent to the Discord Voice Gateway and Voice Server respectively.

Usage

  • The application would get a new *Voice instance by calling NewVoice() and keep it stored for when it needs to open voice connections.
  • When the application wants to connect to a voice channel they would call JoinChannel() on the stored *Voice instance.
  • JoinChannel() will block as it follows the Connection Flow, returning an error if one occurs and a *voice.Session if it was successful.
  • The application should now call (*voice.Session).Speaking() with the wanted voice flag (voicegateway.Microphone, voicegateway.Soundshare, or voicegateway.Priority).
  • The application can now send Voice Packets using the (*voice.Session).Write() method which will be sent to the Voice Server. (*voice.Session) also implements io.Writer.
  • When the application wants to stop sending Voice Packets they should call (*voice.Session).StopSpeaking(), then any required voice cleanup (closing streams, etc.), then (*voice.Session).Disconnect()

Examples

Check the integration tests at voice/integration_test.go.

Documentation

Overview

Package voice handles the Discord voice gateway and UDP connections. It does not handle book-keeping of those sessions.

This package abstracts the subpackage voice/voicesession and voice/udp.

Index

Examples

Constants

Intents are the intents needed for voice to work properly.

View Source
const Protocol = "xsalsa20_poly1305"

Protocol is the encryption protocol that this library uses.

View Source
const WSTimeout = 25 * time.Second

WSTimeout is the duration to wait for a gateway operation including Session to complete before erroring out. This only applies to functions that don't take in a context already.

Variables

View Source
var ErrAlreadyConnecting = errors.New("already connecting")

ErrAlreadyConnecting is returned when the session is already connecting.

View Source
var ErrCannotSend = errors.New("cannot send audio to closed channel")

ErrCannotSend is an error when audio is sent to a closed channel.

Functions

func AddIntents

func AddIntents(gw interface{ AddIntents(gateway.Intents) })

AddIntents adds the needed voice intents into gw. Bots should always call this before Open if voice is required.

Types

type MainSession

type MainSession interface {
	// AddHandler describes the method in handler.Handler.
	AddHandler(handler interface{}) (rm func())
	// Me returns the current user.
	Me() (*discord.User, error)
	// Channel queries for the channel with the given ID.
	Channel(discord.ChannelID) (*discord.Channel, error)
	// SendGateway is a helper to send messages over the gateway.
	SendGateway(ctx context.Context, m ws.Event) error
}

MainSession abstracts both session.Session and state.State.

type ReconnectError

type ReconnectError struct {
	Err error
}

ReconnectError is emitted into Session.Handler everytime the voice gateway fails to be reconnected. It implements the error interface.

func (ReconnectError) Error

func (e ReconnectError) Error() string

Error implements error.

func (ReconnectError) Unwrap

func (e ReconnectError) Unwrap() error

Unwrap returns e.Err.

type Session

type Session struct {
	*handler.Handler

	WSTimeout      time.Duration // global WSTimeout
	WSMaxRetry     int           // 2
	WSRetryDelay   time.Duration // 2s
	WSWaitDuration time.Duration // 5s
	// contains filtered or unexported fields
}

Session is a single voice session that wraps around the voice gateway and UDP connection.

Example
package main

import (
	"context"
	"log"
	"os"
	"os/signal"

	"github.com/powermaker450/arikawa/discord"
	"github.com/powermaker450/arikawa/state"
	"github.com/powermaker450/arikawa/voice"
	"github.com/powermaker450/arikawa/voice/testdata"
)

var (
	token     string
	channelID discord.ChannelID
)

func main() {
	ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
	defer cancel()

	s := state.New("Bot " + token)

	// This is required for bots.
	voice.AddIntents(s)

	if err := s.Open(ctx); err != nil {
		log.Fatalln("failed to open gateway:", err)
	}
	defer s.Close()

	v, err := voice.NewSession(s)
	if err != nil {
		log.Fatalln("failed to create voice session:", err)
	}

	if err := v.JoinChannelAndSpeak(ctx, channelID, false, false); err != nil {
		log.Fatalln("failed to join voice channel:", err)
	}
	defer v.Leave(ctx)

	if err := testdata.WriteOpus(v, "testdata/nico.dca"); err != nil {
		log.Fatalln("failed to write opus:", err)
	}
}
Output:

func NewSession

func NewSession(state MainSession) (*Session, error)

NewSession creates a new voice session for the current user.

func NewSessionCustom

func NewSessionCustom(ses MainSession, userID discord.UserID) *Session

NewSessionCustom creates a new voice session from the given session and user ID.

func (*Session) JoinChannel

func (s *Session) JoinChannel(ctx context.Context, chID discord.ChannelID, mute, deaf bool) error

JoinChannel joins the given voice channel with the default timeout.

func (*Session) JoinChannelAndSpeak

func (s *Session) JoinChannelAndSpeak(ctx context.Context, chID discord.ChannelID, mute, deaf bool) error

JoinChannelAndSpeak is a convenient function that calls JoinChannel then Speaking.

func (*Session) Leave

func (s *Session) Leave(ctx context.Context) error

Leave disconnects the current voice session from the currently connected channel.

func (*Session) ReadPacket

func (s *Session) ReadPacket() (*udp.Packet, error)

ReadPacket reads a single packet from the UDP connection. This is NOT at all thread safe, and must be used very carefully. The backing buffer is always reused.

func (*Session) SetUDPDialer

func (s *Session) SetUDPDialer(d udp.DialFunc)

SetUDPDialer sets the given dialer to be used for dialing UDP voice connections.

func (*Session) Speaking

func (s *Session) Speaking(ctx context.Context, flag voicegateway.SpeakingFlag) error

Speaking tells Discord we're speaking. This method should not be called concurrently.

If only NotSpeaking (0) is given, then even if the gateway cannot be reached, a nil error will be returned. This is because sending Discord a not-speaking event is a destruction command that doesn't affect the outcome of anything done after whatsoever.

func (*Session) Write

func (s *Session) Write(b []byte) (int, error)

Write writes into the UDP voice connection. This method is thread safe as far as calling other methods of Session goes; HOWEVER it is not thread safe to call Write itself concurrently.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL