Skip to content

Containers and iterables

Bilal Bassam edited this page Feb 4, 2022 · 4 revisions

Discordia relies on custom classes to store, retrieve, update, and delete Discord data in situations where simple Lua tables may not be sufficient.

When certain payloads are received from the Discord gateway via WebSocket, data is passed to an event handler where it is encapsulated in containers, stored in caches, and sometimes made accessible via other iterables.

Containers

Discordia uses special Container classes to store raw Discord data as objects with useful properties and methods.

Every container has a client and parent property. The client property is a handle for the main client instance that is manages the container while the parent property is a handle for the object where the container is directly cached. Sometimes, these are the same. For example, guild.parent and guild.client are equivalent. Otherwise, the parent will be defined differently. For example, role.parent and role.guild are equivalent. Additionally, role.guild.client and role.client are equivalent, since both the guild and its roles are necessarily managed by the same client.

Every container has a __hash method that returns a certain property that can be used to uniquely identify the container. For example, message:__hash() is equivalent to message.id and is guaranteed to be unique for all messages.

Every container has a __tostring method that returns a string of the container name and the container hash. This string has the general format Name: hash.

Every container has an __eq method that allowed containers to be compared with the == operator. For two containers to be equivalent, they must be of the same type and their hashes must be identical.

The following is a list of Discord containers, their parents, and their hash properties:

Container Parent Hash
Ban Guild user.id
Emoji Guild id
GroupChannel Client id
Guild Client id
GuildCategoryChannel Guild id
GuildTextChannel Guild id
GuildVoiceChannel Guild id
Invite Client code
Member Guild user.id
Message TextChannel id
PermissionOverwrite GuildChannel id
PrivateChannel Client id
Reaction Message emojiId or emojiName
Relationship Client user.id
Role Guild id
User Client id
Webhook Client id

Iterables

Discordia uses special Iterable classes to cache and access containers. They function similarly to tables, with __len and __pairs methods, plus a variety of methods that help to find the object that you want, such as get, find, and iter.

Primary Iterables - Caches

For a variety of containers and the main client instance, there are properties that are some sort of iterable class. Caches are a special type of iterable that are used as the main storage for Discordia containers. Cache properties and the objects that they contain are updated via various gateway events.

Parent Property Iterable Type Container Type
Client guilds Cache Guild
Client groupChannels Cache GroupChannel
Client privateChannels Cache PrivateChannel
Client users Cache User
Guild categories Cache GuildCategoryChannel
Guild emojis Cache Emoji
Guild members Cache Member
Guild roles Cache Role
Guild textChannels Cache GuildTextChannel
Guild voiceChannels Cache GuildVoiceChannel
Message reactions Cache Reaction
TextChannel permissionOverwrites Cache PermissionOverwrite
TextChannel messages WeakCache Message

Secondary Iterables

Some other containers have properties for secondary iterables. These iterables do not store original objects; they store references to cached objects listed above. Conveniently, all iterable classes, including caches, have the same methods (and no properties), so Discordia users do not necessarily have to remember which iterable is used; remembering one set of iterable methods is sufficient. Secondary iterable properties and the objects that they reference are updated via various gateway events.

Parent Property Iterable Type Container Type
GroupChannel recipients SecondaryCache User
GuildTextChannel members FilteredIterable Member
Message mentionedUsers ArrayIterable User
Message mentionedRoles ArrayIterable Role
Message mentionedChannels ArrayIterable Channel
Role members FilteredIterable Member
User mutualGuilds FilteredIterable Guild

Static Iterables

Some objects cannot be reliably updated by gateway events alone. These objects are instead accessed by HTTP requests via class methods. Every time the methods listed below are called, they will make an HTTP request and return a new copy of an iterable and containers (if successful). Containers stored in static caches (bans, invites, webhooks) are not updated via gateway events. Containers stored in static secondary caches (messages, users) are updated via gateway events, but objects are never added to or removed from the cache via gateway events.

Parent Method Iterable Type Container Type
Guild getBans Cache Ban
Guild getInvites Cache Invite
Guild getWebhooks Cache Webhook
GuildChannel getInvites Cache Invite
GuildTextChannel getWebhooks Cache Webhook
Reaction getUsers SecondaryCache User
TextChannel getMessages SecondaryCache Message
TextChannel getPinnedMessages SecondaryCache Message

Examples

Below are some examples of accessing role objects. In some cases, the following predicate is used to filter by color:

local function fn(r) return r.color > 0 end

Counting Objects

local n = #guild.roles -- count all roles
local n = guild.roles:count(fn) -- count some roles

Accessing One Object

local role = guild.roles:get('1234567890') -- get by Snowflake ID
local role = guild.roles:find(fn) -- get by predicate

Accessing Many Objects

for id, role in pairs(guild.roles) do
	print(id, role.name) -- iterate all roles
end

for role in guild.roles:iter() do
	print(role.name) -- iterate all roles
end

for role in guild.roles:findAll(fn) do
	print(role.name, role.color) -- iterate through some roles
end

local roles = roles:toArray() -- get an array of roles
local roles = roles:toArray('position') -- get a sorted array of roles
local roles = roles:toArray('position', fn) -- get a sorted, filtered array of roles

Accessing Properties in Bulk

local rows = guild.roles:select('position', 'id', 'name') -- get a sorted array of properties
local row = rows[1]
print(row[1]) -- position
print(row[2]) -- id
print(row[3]) -- name

Special Cases

There are some special cases or exceptions to the rules outlined above.

Guild Unavailability

When there is a Discord server outage, some guilds (and their child objects) may become unavailable. When this happens, Discordia guild objects may not be cached at all, may be mostly empty, or may be an old copy. In the latter two cases, guild.unavailible would be true.

Offline Members

By default, offline guild members may not be cached in Discordia. To compensate for missing members, Guild:getMember will make an HTTP request to fetch a member if it is not cached. If you do need to have all members cached, then the cacheAllMembers option must be enabled on client initialization. You can check the ratio of cached to total members by comparing the #guild.members and guild.totalMemberCount values.

Private Channels

Private channels may not be cached if they were previously closed. If you are unable to access the channel object via either Client:getChannel or other methods, use User:getPrivateMessage or the User:send shortcut.

Messages

Discordia has no cached messages on startup and will only weakly cache the most recent messages as they are received. If you want to access messages that are not cached, either use TextChannel:getMessages (and related methods) or TextChannel:getMessage, which will make an HTTP request to fetch the message if it is not cached.

Permission Overwrites

Permission overwrites are only cached if they were edited. If you want to get an overwrite for a specific role or member, use GuildChannel:getPermissionOverwriteFor. This will create a new object if one does not already exist.

Clone this wiki locally