discordha

package module
v0.0.0-...-f81dd51 Latest Latest
Warning

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

Go to latest
Published: Jan 5, 2024 License: MIT Imports: 14 Imported by: 7

README

DiscordHA (Discord High Availability)

DiscordHA is library on to be used together with discordgo to deploy Discord bots in high availability. This will allow deploying Discord bots with zero-downtime and without the risk of handling events twice. DiscordHA is designed to run in a Kubernetes environment with multiple replica's available. It relies on Etcd as a locking system to prevent events of being received twice, this works in a first locked principle for timestamped events (e.g. messages), and a leader election system for non-timestamped events (e.g. reactions).

DiscordHA is not meant for sharding but enables Discord bots to have multiple replicas watching the same guilds (servers).

Example

func main(){
	dg, err := discordgo.New("Bot " + os.Getenv("BOT_TOKEN"))
	if err != nil {
		fmt.Prinf("error creating Discord session: %v\n", err)
        return
	}

	haLogger := log.New(os.Stderr, "discordha: ", log.Ldate|log.Ltime)
	ha, err := discordha.New(&discordha.Config{
		Session:       dg,
		HA:            true, // set to false to run 1 replica for debugging, this disables locking and caching
		EtcdEndpoints: []string{"etcd-bob.etcd.svc.cluster.local:2379"},
		Context:       context.TODO(),
		Log:           *haLogger,
	})
	if err != nil {
        fmt.Prinf("error creating Discord HA: %v\n", err)
		return
	}
    defer s.ha.Stop()
    
    // using AddHandler on discordha will handle it on one replica
	ha.AddHandler(func(sess *discordgo.Session, m *discordgo.MessageCreate) {
        fmt.Printf("Received Message: %q", m.Message.Content)
    })
   

	err = dg.Open()
	if err != nil {
        fmt.Prinf("error opening connectio: %v\n", err)
        return
	}
    defer dg.Close()

	log.Println("DiscordHA Example is now running.  Press CTRL-C to exit.")
	sc := make(chan os.Signal, 1)
	signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt)
	<-sc
}

Used by

Roadmap

  • Add support for sharded event listeners
  • Improve the voice system
  • Improve locking system for events
  • Facilitate Etcd over HTTPS
  • Add e2e testing

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrorCacheKeyNotExist = errors.New("Cache key does not exist")

ErrorCacheKeyNotExist is the error the cache returns if a key does not exist

Functions

This section is empty.

Types

type Config

type Config struct {
	Session                          *discordgo.Session
	HA                               bool
	LockUpdateInterval               time.Duration
	LockTTL                          time.Duration
	EtcdEndpoints                    []string
	Context                          context.Context
	Log                              log.Logger
	VerboseLevel                     int
	DoNotParticipateInLeaderElection bool
}

Config contains the configuration for HA

type HA

type HA interface {
	// AddHandler makes a discordgo handler HA, it will only be ran on one replica
	AddHandler(handler interface{}) func()
	// CacheRead allows to read a cached object from etcd shared across all replicas
	CacheRead(cache, key string, want interface{}) (interface{}, error)
	// CacheWrite allows to read a cached object from etcd shared across all replicas
	CacheWrite(cache, key string, data interface{}, ttl time.Duration) error
	// Stop should be run when the program terminates
	Stop()

	// LockVoice locks a voice channel for a specific module and Guild, returns true if successful, this function may change soon!
	LockVoice(moduleID, guildID string) (bool, error)
	// LockVoice locks a voice channel for a specific module and Guild, this function may change soon!
	UnlockVoice(moduleID, guildID string) error
	// SendVoiceCommand sends a string command to the instance handling the voice channel
	// These can be received using WatchVoiceCommands
	// this function may change soon!
	SendVoiceCommand(command VoiceCommand) error
	// WatchVoiceCommands gives a channel with commands transmitted by SendVoiceCommand
	// this function may change soon!
	WatchVoiceCommands(ctx context.Context, moduleID string) chan VoiceCommand
}

func New

func New(c *Config) (HA, error)

New gives a HA instance for a given configuration

type HAInstance

type HAInstance struct {
	// contains filtered or unexported fields
}

HA is a helper struct for high available discordgo using etcd

func (*HAInstance) AddHandler

func (h *HAInstance) AddHandler(handler interface{}) func()

func (*HAInstance) AmLeader

func (h *HAInstance) AmLeader(ctx context.Context) bool

func (*HAInstance) CacheRead

func (h *HAInstance) CacheRead(cache, key string, want interface{}) (interface{}, error)

CacheRead reads a key from a specific cache, returns ErrorCacheKeyNotExist if not found

func (*HAInstance) CacheWrite

func (h *HAInstance) CacheWrite(cache, key string, data interface{}, ttl time.Duration) error

CacheWrite writes an object to a specific cache with a specific key, will be purged after TTL expires

func (*HAInstance) ElectLeader

func (h *HAInstance) ElectLeader(ctx context.Context) error

func (*HAInstance) Lock

func (h *HAInstance) Lock(obj interface{}) (bool, string, error)

Lock tries to acquire a lock on an event, it will return true if the instance that requests it may process the request.

func (*HAInstance) LockVoice

func (h *HAInstance) LockVoice(moduleID, guildID string) (bool, error)

LockVoice locks a voice channel ID, returns true if successful

func (*HAInstance) ResignLeader

func (h *HAInstance) ResignLeader(ctx context.Context) error

func (*HAInstance) SendVoiceCommand

func (h *HAInstance) SendVoiceCommand(command VoiceCommand) error

SendVoiceCommand sends a string command to the instance handling the voice channel These can be received using WatchVoiceCommands

func (*HAInstance) Stop

func (h *HAInstance) Stop()

Stop should be run when the program terminates

func (*HAInstance) Unlock

func (h *HAInstance) Unlock(lockKey string) error

Unlock will release a lock on an event

func (*HAInstance) UnlockVoice

func (h *HAInstance) UnlockVoice(moduleID, guildID string) error

UnlockVoice unlocks a voice channel ID

func (*HAInstance) WatchVoiceCommands

func (h *HAInstance) WatchVoiceCommands(ctx context.Context, moduleID string) chan VoiceCommand

WatchVoiceCommands gives a channel with commands transmitted by SendVoiceCommand for a specifid ModuleID

type VoiceCommand

type VoiceCommand struct {
	ModuleID  string `json:"moduleID"`
	GuildID   string `json:"guildID"`
	ChannelID string `json:"channelID"`
	File      string `json:"file"`
	UserID    string `json:"userID"`
}

Jump to

Keyboard shortcuts

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