dgrs

package module
v0.5.7 Latest Latest
Warning

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

Go to latest
Published: Feb 22, 2023 License: MIT Imports: 10 Imported by: 5

README

dgrs Go Report Card CI Coverage Status

DiscordGo Redis State - or for short: dgrs - is a custom state manager for DiscordGo which uses a Redis Instance to store and sync state.

This implementation has four core advantages:

  1. The default state management of DiscordGo uses multi-layer maps where all cached objects are stored in the application heap. If you are dealing with a lot of data, this can really increase the load on the applications garbage collector and can eventually reduce the performance of your bot. By storing all of those objects in Redis (which is also way more optimized for storing large amounts of data and making them quickly accessible), your applications GC is not responsible for keeping track of all of these objects.

  2. By connecting to the same Redis instance, you can share state across multiple sharded replicas of your bot fairly easily. You can even use the auto shard-ID system to automatically distribute shard IDs across your network. There is also an event bus which is using the Redis PubSub system to sharde events across instances like received direct messages.

  3. As long as your Redis instance is up, the state is persistently cached and you don't need to build up your cache state from scratch at every restart of your bot, which can save a lot of time and unnessecary API calls.

  4. You can set cache expirations for each type of state object after which the cached value is invalidated. This is not possible with the default state implementation of DiscordGo.

Usage

// Create a new DiscordGo session.
session, _ := discordgo.New("Bot " + token)

// Create the State instance passing the
// DiscordGo session and Redis client
// configuration.
state, err := dgrs.New(dgrs.Options{
	DiscordSession: session,
	RedisOptions: redis.Options{
		Addr: "localhost:6379",
	},
	FetchAndStore:  true,
})

guilds, err := state.Guilds()
if err != nil {
    log.Fatal(err)
}

for _, g := range guilds {
    fmt.Println(g.Name)
}

ToDo

  • Optimize state updating
  • Optimize code documentation
  • Add more unit tests (> 80% coverage)
  • Bind events to state updates
  • Add custom marshal/unmarshal function option

© 2021 Ringo Hoffmann (zekro Development).
Covered by the MIT License.

Documentation

Index

Constants

View Source
const (
	KeyGuild      = "guild"
	KeyMember     = "member"
	KeyUser       = "user"
	KeyRole       = "role"
	KeyChannel    = "chan"
	KeyEmoji      = "emoji"
	KeyMessage    = "message"
	KeyVoiceState = "vs"
	KeyPresence   = "presence"
)

Variables

View Source
var (
	ErrNilObject              = errors.New("object is nil")
	ErrUserNil                = errors.New("user object is nil")
	ErrInvalidType            = errors.New("invalid object type")
	ErrSessionNotProvided     = errors.New("when FetchAndStore is enabled, a discordgo session instance must be provided")
	ErrShardIDAlreadyReserved = errors.New("a shard with this ID is already reserved")
	ErrShardAlreadyAllocated  = errors.New("this instance already has a shard reserved")
)

Functions

This section is empty.

Types

type DirectMessageEvent added in v0.5.2

type DirectMessageEvent struct {
	Message  *discordgo.Message `json:"message"`
	Channel  *discordgo.Channel `json:"channel"`
	IsUpdate bool               `json:"is_update"`
}

type DiscordSession

type DiscordSession interface {
	AddHandler(interface{}) func()
	Channel(channelID string, options ...discordgo.RequestOption) (*discordgo.Channel, error)
	GuildChannels(guildID string, options ...discordgo.RequestOption) ([]*discordgo.Channel, error)
	GuildEmojis(guildID string, options ...discordgo.RequestOption) ([]*discordgo.Emoji, error)
	Guild(guildID string, options ...discordgo.RequestOption) (*discordgo.Guild, error)
	GuildMember(guildID, memberID string, options ...discordgo.RequestOption) (*discordgo.Member, error)
	GuildMembers(guildID string, after string, limit int, options ...discordgo.RequestOption) ([]*discordgo.Member, error)
	ChannelMessage(channelID, messageID string, options ...discordgo.RequestOption) (*discordgo.Message, error)
	ChannelMessages(channelID string, limit int, beforeID, afterID, aroundID string, options ...discordgo.RequestOption) ([]*discordgo.Message, error)
	GuildRoles(guildID string, options ...discordgo.RequestOption) ([]*discordgo.Role, error)
	User(userID string, options ...discordgo.RequestOption) (*discordgo.User, error)
}

DiscordSession describes required API functionalities of a discordgo.Session instance.

type IState added in v0.5.4

type IState interface {
	SetChannel(channel *discordgo.Channel) (err error)
	Channel(id string) (v *discordgo.Channel, err error)
	Channels(guildID string, forceFetch ...bool) (v []*discordgo.Channel, err error)
	RemoveChannel(id string, dehydrate ...bool) (err error)
	SetEmoji(guildID string, emoji *discordgo.Emoji) (err error)
	Emoji(guildID, emojiID string) (v *discordgo.Emoji, err error)
	Emojis(guildID string, forceFetch ...bool) (v []*discordgo.Emoji, err error)
	RemoveEmoji(guildID, emojiID string) (err error)
	Publish(channel string, payload interface{}) (err error)
	Subscribe(channel string, handler func(scan func(v interface{}) error)) (close func() error)
	SubscribeDMs(handler func(e *DirectMessageEvent)) (close func() error)
	SetGuild(guild *discordgo.Guild) (err error)
	Guild(id string, hydrate ...bool) (v *discordgo.Guild, err error)
	Guilds() (v []*discordgo.Guild, err error)
	RemoveGuild(id string, dehydrate ...bool) (err error)
	SetMember(guildID string, member *discordgo.Member) (err error)
	Member(guildID, memberID string, forceNoFetch ...bool) (v *discordgo.Member, err error)
	Members(guildID string, forceFetch ...bool) (v []*discordgo.Member, err error)
	MembersLimit(guildID, afterID string, limit int, forceFetch ...bool) (v []*discordgo.Member, err error)
	RemoveMember(guildID, memberID string) (err error)
	SetMessage(message *discordgo.Message) (err error)
	Message(channelID, messageID string) (v *discordgo.Message, err error)
	Messages(channelID string, forceFetch ...bool) (v []*discordgo.Message, err error)
	RemoveMessage(channelID, messageID string) (err error)
	SetPresence(guildID string, presence *discordgo.Presence) (err error)
	Presence(guildID, userID string) (v *discordgo.Presence, err error)
	Presences(guildID string) (v []*discordgo.Presence, err error)
	RemovePresence(guildID, userID string) (err error)
	SetRole(guildID string, role *discordgo.Role) (err error)
	Role(guildID, roleID string) (v *discordgo.Role, err error)
	Roles(guildID string, forceFetch ...bool) (v []*discordgo.Role, err error)
	RemoveRole(guildID, roleID string) (err error)
	Shards(pool int) (shards []*Shard, err error)
	ReserveShard(pool int, cid ...int) (id int, err error)
	ReleaseShard(pool, id int) (err error)
	Flush(subKeys ...string) (err error)
	SetUser(user *discordgo.User) (err error)
	User(id string) (v *discordgo.User, err error)
	Users() (v []*discordgo.User, err error)
	RemoveUser(id string) (err error)
	SelfUser() (v *discordgo.User, err error)
	SetSelfUser(user *discordgo.User) (err error)
	UserGuilds(id string) (res []string, err error)
	SetVoiceState(guildID string, vs *discordgo.VoiceState) (err error)
	VoiceState(guildID, userID string) (v *discordgo.VoiceState, err error)
	VoiceStates(guildID string) (v []*discordgo.VoiceState, err error)
	RemoveVoiceState(guildID, userID string) (err error)
}

IState defines the endpoints of the State instance.

type Lifetimes

type Lifetimes struct {
	OverrrideZero bool

	General,
	Guild,
	Member,
	User,
	Role,
	Channel,
	Emoji,
	Message,
	VoiceState,
	Presence time.Duration
}

Lifetimes wrap a grid of lifetime specifications for each cache object.

type MarshalFunc

type MarshalFunc func(interface{}) ([]byte, error)

type Options

type Options struct {
	// You can pass a pre-initialized redis instance
	// if you already have one.
	RedisClient *redis.Client

	// Discord session used to fetch unpresent data
	// and to hook event handlers into.
	DiscordSession DiscordSession

	// Redis client options to connect to a redis
	// instance.
	RedisOptions redis.Options

	// Fetch requested values directly from the Discord API
	// and store them in the cache.
	FetchAndStore bool

	// If set, all cache entries created by dgrs will be
	// flushed on initialization.
	FlushOnStartup bool

	// You can specify a timeout period for redis commands.
	// If not set, no timeout will be used.
	CommandTimeout time.Duration

	// You can specify either a general lifetime for
	// values stored in the cache or a per-type
	// lifetime which will override the default
	// lifetime for that specific object type.
	//
	// If no lifetime is set at all, a default value
	// of DefaultGeneralLifetime is used.
	Lifetimes Lifetimes

	// The prefix used for the redis storage keys.
	//
	// Defaults to 'gdrs'.
	KeyPrefix string

	// The function used to encode objects into the byte
	// data stored in the cache.
	//
	// Defaults to json.Marshal.
	MarshalFunc MarshalFunc

	// The function used to decode byte data from the
	// cache into objects.
	//
	// Defaults to json.Unmarshal.
	UnmarshalFunc UnmarshalFunc

	// The heartbeat duration for shards.
	ShardTimeout time.Duration

	// Set to true to broadcast DM events via
	// the eventbus.
	BroadcastDMs bool
}

Options defines State preferences.

type Shard added in v0.4.0

type Shard struct {
	ID            int       `json:"id"`
	Pool          int       `json:"pool"`
	LastHeartbeat time.Time `json:"lastheartbeat"`
}

type State

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

State utilizes a redis connection to be able to store and retrieve discordgo state objects.

Also, because state hooks event handlers into the passed discord session, it is also possible to maintain the current state automatically.

func New

func New(opts Options) (s *State, err error)

New returns a new State instance with the passed options.

func (*State) Channel

func (s *State) Channel(id string) (v *discordgo.Channel, err error)

Channel tries to retrieve a channel by the given channel ID.

If no channel was found and FetchAndStore is enabled, the object will be tried to be retrieved from the API. When this was successful, it is stored in the cache and the object is returned.

Otherwise, if the object was not found in the cache and FetchAndStore is disabled, nil is returned.

func (*State) Channels

func (s *State) Channels(guildID string, forceFetch ...bool) (v []*discordgo.Channel, err error)

Channels returns a list of channels of the given guild ID which are stored in the cache at the given moment.

func (*State) Emoji

func (s *State) Emoji(guildID, emojiID string) (v *discordgo.Emoji, err error)

Emoji tries to retrieve a channel by the given guild and emoji ID.

If no emoji was found and FetchAndStore is enabled, the object will be tried to be retrieved from the API. When this was successful, it is stored in the cache and the object is returned.

Otherwise, if the object was not found in the cache and FetchAndStore is disabled, nil is returned.

func (*State) Emojis

func (s *State) Emojis(guildID string, forceFetch ...bool) (v []*discordgo.Emoji, err error)

Emojis returns a list of emojis of the given guild ID which are stored in the cache at the given moment.

func (*State) Flush

func (s *State) Flush(subKeys ...string) (err error)

Flush deletes all keys in the cache stored by dgrs.

You can also specify sub keys like KeyGuild to only remove all guild entries, for example.

func (*State) Guild

func (s *State) Guild(id string, hydrate ...bool) (v *discordgo.Guild, err error)

Guild tries to retrieve a guild by the given guild ID.

If no guild was found and FetchAndStore is enabled, the object will be tried to be retrieved from the API. When this was successful, it is stored in the cache and the object is returned.

Otherwise, if the object was not found in the cache and FetchAndStore is disabled, nil is returned.

Optionally, when hydrate is set to true, members, roles, channels and emojis will also be obtained from cache and added to the guild object.

func (*State) Guilds

func (s *State) Guilds() (v []*discordgo.Guild, err error)

Guilds returns a list of guilds which are stored in the cache at the given moment.

func (*State) Member

func (s *State) Member(guildID, memberID string, forceNoFetch ...bool) (v *discordgo.Member, err error)

Member tries to retrieve a member by the given guild and member ID.

If no member was found and FetchAndStore is enabled, the object will be tried to be retrieved from the API. When this was successful, it is stored in the cache and the object is returned.

Otherwise, if the object was not found in the cache and FetchAndStore is disabled, nil is returned.

func (*State) Members

func (s *State) Members(guildID string, forceFetch ...bool) (v []*discordgo.Member, err error)

Members returns a list of members of the given guild ID which are stored in the cache at the given moment.

func (*State) MembersLimit added in v0.4.0

func (s *State) MembersLimit(guildID, afterID string, limit int, forceFetch ...bool) (v []*discordgo.Member, err error)

MembersLimit returns a list of members of the given guild ID which are stored in the cache at the given moment beginning from the given user ID (or first if empty) and limited by the given limit amount (or all if <= 0).

func (*State) Message

func (s *State) Message(channelID, messageID string) (v *discordgo.Message, err error)

Message tries to retrieve a message by the given channel and message ID.

If no message was found and FetchAndStore is enabled, the object will be tried to be retrieved from the API. When this was successful, it is stored in the cache and the object is returned.

Otherwise, if the object was not found in the cache and FetchAndStore is disabled, nil is returned.

func (*State) Messages

func (s *State) Messages(channelID string, forceFetch ...bool) (v []*discordgo.Message, err error)

Messages returns a list of messages of the given channel which are stored in the cache at the given moment.

func (*State) Presence

func (s *State) Presence(guildID, userID string) (v *discordgo.Presence, err error)

Presence tries to retrieve a presence by the given guild and user ID.

If the object was not found in the cache and FetchAndStore is disabled, nil is returned.

func (*State) Presences

func (s *State) Presences(guildID string) (v []*discordgo.Presence, err error)

Presences returns a list of presences of the given guild ID which are stored in the cache at the given moment.

func (*State) Publish added in v0.5.0

func (s *State) Publish(channel string, payload interface{}) (err error)

Publish pushes an event payload to the given channel to all subscribers connected to the same Redis instance.

func (*State) ReleaseShard added in v0.4.3

func (s *State) ReleaseShard(pool, id int) (err error)

func (*State) RemoveChannel

func (s *State) RemoveChannel(id string, dehydrate ...bool) (err error)

RemoveChannel removes a channel object from the cache by the given ID.

When dehydrate is passed as true, messages in this channel which are cached are purged from cache as well.

func (*State) RemoveEmoji

func (s *State) RemoveEmoji(guildID, emojiID string) (err error)

RemoveEmoji removes an emoji object from the cache by the given ID.

func (*State) RemoveGuild

func (s *State) RemoveGuild(id string, dehydrate ...bool) (err error)

RemoveGuild removes a guild object from the cache by the given ID.

When dehydrate is passed as true, objects linked to this guild (members, roles, voice states, emojis, channels and messages) are purged from cache as well.

func (*State) RemoveMember

func (s *State) RemoveMember(guildID, memberID string) (err error)

RemoveMember removes a member object from the cache by the given guild and member ID.

func (*State) RemoveMessage

func (s *State) RemoveMessage(channelID, messageID string) (err error)

RemoveMessage removes a message object from the cache by the given channel and message ID.

func (*State) RemovePresence

func (s *State) RemovePresence(guildID, userID string) (err error)

RemovePresence removes a presence object from the cache by the given guild and user ID.

func (*State) RemoveRole

func (s *State) RemoveRole(guildID, roleID string) (err error)

RemoveRole removes a role object from the cache by the given ID.

func (*State) RemoveUser

func (s *State) RemoveUser(id string) (err error)

RemoveUser removes a user object from the cache by the given ID.

func (*State) RemoveVoiceState

func (s *State) RemoveVoiceState(guildID, userID string) (err error)

RemoveVoiceState removes a voice state object from the cache by the given guild and user ID.

func (*State) ReserveShard added in v0.4.1

func (s *State) ReserveShard(pool int, cid ...int) (id int, err error)

func (*State) Role

func (s *State) Role(guildID, roleID string) (v *discordgo.Role, err error)

Role tries to retrieve a role by the given guild and role ID.

If no role was found and FetchAndStore is enabled, the object will be tried to be retrieved from the API. When this was successful, it is stored in the cache and the object is returned.

Otherwise, if the object was not found in the cache and FetchAndStore is disabled, nil is returned.

func (*State) Roles

func (s *State) Roles(guildID string, forceFetch ...bool) (v []*discordgo.Role, err error)

Roles returns a list of roles which are stored in the cache at the given moment on the given guild.

func (*State) SelfUser

func (s *State) SelfUser() (v *discordgo.User, err error)

SelfUser returns the current user object of the authenticated account.

This object is retrieved on receiving the 'Ready' event.

func (*State) SetChannel

func (s *State) SetChannel(channel *discordgo.Channel) (err error)

SetChannel sets the given channel object to the cache.

func (*State) SetEmoji

func (s *State) SetEmoji(guildID string, emoji *discordgo.Emoji) (err error)

SetEmoji sets the given emoji object to the cache.

func (*State) SetGuild

func (s *State) SetGuild(guild *discordgo.Guild) (err error)

SetGuild sets the given guild object to the cache.

If the guild object contains any members, roles, channels or emojis, these objects are also stroed subsequently in the cache.

func (*State) SetMember

func (s *State) SetMember(guildID string, member *discordgo.Member) (err error)

SetMember sets the given member object to the cache.

func (*State) SetMessage

func (s *State) SetMessage(message *discordgo.Message) (err error)

SetMessage sets the given message object to the cache.

func (*State) SetPresence

func (s *State) SetPresence(guildID string, presence *discordgo.Presence) (err error)

SetPresence sets the given presence object to the cache.

func (*State) SetRole

func (s *State) SetRole(guildID string, role *discordgo.Role) (err error)

SetRole sets the given role object to the cache.

func (*State) SetSelfUser

func (s *State) SetSelfUser(user *discordgo.User) (err error)

SetSelfUser allows to set a custom user object as self user to the cache.

func (*State) SetUser

func (s *State) SetUser(user *discordgo.User) (err error)

SetUser sets the given user object to the cache.

func (*State) SetVoiceState

func (s *State) SetVoiceState(guildID string, vs *discordgo.VoiceState) (err error)

SetVoiceState sets the given voice state object to the cache.

func (*State) Shards added in v0.4.0

func (s *State) Shards(pool int) (shards []*Shard, err error)

func (*State) Subscribe added in v0.5.0

func (s *State) Subscribe(
	channel string,
	handler func(scan func(v interface{}) error),
) (close func() error)

Subscribe starts an event listener on the given channel and passes all received events to the passed handler function. The handler is getting passed a scan function which takes an object instance to scan the payload into.

Subscribe returns a close function to close the listener on the channel.

func (*State) SubscribeDMs added in v0.5.2

func (s *State) SubscribeDMs(handler func(e *DirectMessageEvent)) (close func() error)

SubscribeDMs subscribes to the states DM event bus and executes handler on each received DM event with the event details passed as payload.

func (*State) User

func (s *State) User(id string) (v *discordgo.User, err error)

User tries to retrieve a user by the given user ID.

If no user was found and FetchAndStore is enabled, the object will be tried to be retrieved from the API. When this was successful, it is stored in the cache and the object is returned.

Otherwise, if the object was not found in the cache and FetchAndStore is disabled, nil is returned.

func (*State) UserGuilds added in v0.3.0

func (s *State) UserGuilds(id string) (res []string, err error)

UserGuilds returns a slice of Guild IDs the user is member of.

func (*State) Users

func (s *State) Users() (v []*discordgo.User, err error)

Users returns a list of users which are stored in the cache at the given moment.

func (*State) VoiceState

func (s *State) VoiceState(guildID, userID string) (v *discordgo.VoiceState, err error)

VoiceState tries to retrieve a voice state by the given guild and user ID.

Because voice states are tracked by the 'VoiceStateUpdate' event handler, an uncached voice state object will not be retrieved from the API on get.

func (*State) VoiceStates

func (s *State) VoiceStates(guildID string) (v []*discordgo.VoiceState, err error)

VoiceStates returns a list of voice states which are stored in the cache at the given moment on the given guild.

type UnmarshalFunc

type UnmarshalFunc func([]byte, interface{}) error

Directories

Path Synopsis
examples

Jump to

Keyboard shortcuts

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