[−][src]Crate automate
A low level and asynchronous rust library made for interacting with Discord's API.
This library provides all the tools that will handle setting up and maintaining a connection to Discord's API in order to make a bot. Before messing with the code of this library, you first need to get a bot token on Discord's developers portal. Create a new application and add a bot to the newly created application. You can then copy the bot's token by clicking the copy button.
In order to build your bot, you must first provide it with the settings you'd like to use which is done using the Configuration struct:
- Configuration::new: Takes the bot token as parameter. You can provide a hardcoded string, take it from the environment or retrieve it from a configuration file.
- Configuration::from_env: Does the same as
new
except it takes the bot token from the given environment variable. - Configuration::register: Registers stateful and stateless listeners.
- Configuration::enable_logging and Configuration::disable_logging: Enable or disable Automate's built in logger. You can disable it and use your own logger if necessary.
- Configuration::level_for: Sets the minimum log level for a line to be printed in the console output for the given module.
- Configuration::intents: Sets the events which will be sent to the bot using intents. Defaults to all events.
- Configuration::presence: Sets the presence of the bot.
The resulting configuration object can then be sent to Automate::launch which will start the bot.
Listeners
Discord sends various events through their API about messages, guild and user updates, etc. Automate will then relay these events to your bot through the listeners you will define. There are two ways to create listeners:
Stateless listeners
The easiest way to create a listener is to use a stateless listener. A stateless listener is
a simple asynchronous function with the #[listener]
attribute. As its name says, it doesn't
have a state and thus can't save data across calls.
#[listener] async fn print_hello(ctx: &Context, data: &MessageCreateDispatch) -> Result<(), Error> { println!("Hello!"); Ok(()) }
The function you declare must take two arguments as in the example above. The first argument is the session, it provides information about the bot and all the methods allowing you to send instructions to Discord through their HTTP API. The second argument is the dispatch struct which contains all the data about the event you received. Events and thus allowed types for the second argument are:
- ReadyDispatch: called right after the connection with discord is established. Provides data about current guilds, DMs and the bot user account.
- ChannelCreateDispatch: a channel (guild channel or DM) was created.
- ChannelUpdateDispatch: a channel (guild channel or DM) was updated.
- ChannelDeleteDispatch: a channel (guild channel or DM) was deleted.
- ChannelPinsUpdateDispatch: a message was pinned or unpinned.
- GuildCreateDispatch: a guild was created, became available or the bot was added to a guild.
- GuildUpdateDispatch: a guild was updated.
- GuildDeleteDispatch: a guild was deleted, became unavailable or the bot was removed from the guild.
- GuildBanAddDispatch: a user was banned from a guild.
- GuildBanRemoveDispatch: a user was unbanned from a guild.
- GuildEmojisUpdateDispatch: the emojis of a guild were updated.
- GuildIntegrationsUpdateDispatch: the integration of a guild was updated.
- GuildMemberAddDispatch: a user joined a guild.
- GuildMemberUpdateDispatch: a guild member was updated.
- GuildMemberRemoveDispatch: a user was removed from a guild.
- GuildMembersChunkDispatch: response to a request guild members (not yet implemented).
- GuildRoleCreateDispatch: a role was created.
- GuildRoleUpdateDispatch: a role was updated.
- GuildRoleDeleteDispatch: a role was deleted.
- InviteCreateDispatch: an invite to a channel was created.
- InviteDeleteDispatch: an invited to a channel was deleted.
- MessageCreateDispatch: a message was created
- MessageUpdateDispatch: a message updated.
- MessageDeleteDispatch: a message was deleted.
- MessageDeleteBulkDispatch: multiple messages were deleted at once.
- MessageReactionAddDispatch: a user reacted to a message.
- MessageReactionRemoveDispatch: a user's reaction was removed from a message.
- MessageReactionRemoveAllDispatch: all reactions were explicitly removed from a message.
- MessageReactionRemoveEmojiDispatch: all reactions for a given emoji were explicitly removed from a message.
- PresenceUpdateDispatch: user was updated.
- TypingStartDispatch: user started typing in a channel.
- UserUpdateDispatch: properties about the user changed.
- VoiceStateUpdateDispatch: a user joined, left, or moved a voice channel.
- VoiceServerUpdateDispatch: guild's voice server was updated.
- WebhooksUpdateDispatch: guild channel webhook was created, update, or deleted.
A listener function can be registered in the library by sending the name of the function to the
Configuration::register method using the stateless!
macro:
Configuration::from_env("DISCORD_API_TOKEN") .register(stateless!(print_hello));
More advanced examples can be found in the examples folder. If you want to keep data between calls, you probably want to use a stateful listener.
It is possible to use ̀lazy_static!
to store data across calls but this is probably not what
you want since data in the lazy_static
will be shared between shards and kept across
sessions.
Stateful listeners
Stateless listeners provide a clean and quick way to setup a listener, but as stated earlier, they do not allow keeping variables between two events which is necessary for a more advanced bot.
States will not be the same across shards and they will be destroyed and recreated in the case of a disconnection that could not resume and results in the creation of a new Discord session.
Stateful listeners work in the exact same way as stateless listeners except they're declared in an impl block of a struct that derives the State trait. Structs containing stateful listeners must do 3 things:
- Derive the State trait which can be done automatically using
the
#[derive(State)]
derive macro. - Implement Clone since they need to be cloned to be used between different shards and sessions.
- Implement the Initializable trait which defines a
single function that should return all the listeners of the struct. This can be done using
the
methods!
macro which takes the name of the struct followed by a colon and a comma-separated list of the listener methods.
#[macro_use] extern crate automate; use automate::{Context, Error, Snowflake}; use automate::events::{Initializable, StatefulListener}; use automate::gateway::MessageCreateDispatch; use std::collections::HashMap; #[derive(State, Default, Clone)] struct MessageCounter { messages: i32, } impl Initializable for MessageCounter { fn initialize() -> Vec<StatefulListener<Self>> { methods!(MessageCounter: count) } } impl MessageCounter { #[listener] async fn count(&mut self, _: &Context, data: &MessageCreateDispatch) -> Result<(), Error> { self.messages += 1; println!("A total of {} messages have been sent!", self.messages); Ok(()) } }
A state struct can be registered in the library by sending an instance of the struct to the
Configuration::register method using the stateful!
macro.
Configuration::from_env("DISCORD_API_TOKEN") .register(stateful!(MessageCounter::default()));
More advanced examples can be found in the ̀examples/counter.rs` example file.
Storage API
When receiving events, you will usually need more data than the event sends you. For example, you may need to know what the role of the user who sent a message is. This data can be fetched through the storages using the Context::storage and Context::storage_mut to fetch mutable data.
That can be achieved by fetching the data from Discord API each time you need it, but you will quickly get rate limited. That is why the storage API caches some of the data discord sends.
Caching storages
Automate creates 3 storages which you can not mutate, they only get mutated through gateway events:
use automate::{Context, Error}; use automate::gateway::{MessageCreateDispatch, User, Guild}; #[listener] async fn greet_multiple_roles(ctx: &Context, data: &MessageCreateDispatch) -> Result<(), Error> { if let Some(guild) = data.0.guild_id { let storage = ctx.storage::<Guild>().await; let guild = storage.get(guild); let member = guild.members.get(&data.0.author.id).unwrap(); //print hello if the user has at least 2 roles if member.roles.len() >= 2 { println!("Hello!"); } } Ok(()) }
Custom storages
You can also create your own storages. Having your own custom storages will usually allow you to store data without using stateful listeners and in a simpler way.
In order to do that, you will need to create two structs one being the stored struct which should implement Stored and a storage struct which should keep the stored values in memory and provide ways to retrieve and inserts objects. The storage struct should implement Storage.
See examples/levels.rs for a detailed example.
Deactivating storages
The storages API is by default enabled but you might not want it because you simply
do not need to cache the data sent by discord or because you do not have a lot of RAM
available to run the bot. In that case, you can disable the feature by setting the
default-features
key to false
for automate
in your Cargo.toml
file.
Sharding
Automate implements support for sharding through the ShardManager struct. However, you will not need to use the ShardManager directly in most cases since Automate::launch will automatically create as many shards as Discord recommends.
The reasons you would need to use the ShardManager are if you want to spread your bot across multiple servers or if you want to launch more or less shards than what Discord recommends.
Models
All the data sent by discord is deserialized into model structs and enums.
The data returned by discord can be of 4 kinds:
- Always present
- Nullable: Field will be included but the data can either be null or present.
- Optional: Field will either not be included at all or present
- Optional and nullable: The field can be present, null or not included.
Both nullable and optional are handled with Option enum, but optional nullable are wrapped in a double Options because in some cases you may need to know whether the data was not present, null or both.
For example, when editing a guild member, if you need to modify some fields but NOT the
nickname (which is optional and nullable), you will set the nick
field to None
.
But if you want to remove the nick, it needs to be set to null and you can achieve that
by sending Some(None)
.
Examples
use automate::{listener, stateless, Error, Context, Configuration, Automate}; use automate::gateway::MessageCreateDispatch; use automate::http::CreateMessage; #[listener] async fn say_hello(ctx: &Context, data: &MessageCreateDispatch) -> Result<(), Error> { let message = &data.0; if message.author.id != ctx.bot.id { let content = Some(format!("Hello {}!", message.author.username)); ctx.create_message(message.channel_id, CreateMessage { content, ..Default::default() }).await?; } Ok(()) } let config = Configuration::from_env("DISCORD_API_TOKEN") .register(stateless!(say_hello)); Automate::launch(config);
Re-exports
pub extern crate log; |
pub use async_trait::async_trait; |
pub use chrono; |
pub use lazy_static; |
pub use tokio; |
pub use sharding::ShardManager; |
Modules
encode | Types used by the library to transform objects into data that can be sent to and understood by Discord's API. |
events | Defines all the types and macros required to make and register listeners. |
gateway | Tools to interact with Discord's gateway API |
http | Tools to interact with Discord's HTTP API |
sharding | |
storage |
Macros
json | Creates a JSON string associating the given keys with the given values. |
map | Creates a hashmap associating the given keys to the given values. |
methods | |
stateful | Parses a list of state structs before sending them to the Configuration::register method. |
stateless |
Structs
Automate | Defines utility functions. |
Configuration | Allows specifying API token, registering stateful and stateless listeners, stating the shard id, intents and configuring logger. |
Context | Context about the current gateway session. Provides a way to interact with Discord HTTP API by dereferencing to HttpAPI. |
HttpAPI | Struct used to interact with the discord HTTP API. |
Snowflake | Twitter's snowflake
used by discord API to provide unique IDs for guilds, channels, users, etc.
Since a snowflake is 64bits in size, it is stored as a u64 in the
|
Enums
Error | Represents an error that occurred while using the library. |
Intent | System to help define which events discord will send to your bot. An intent is a single or a group of event types. |
Traits
Identifiable | Any object that has an id |
Attribute Macros
listener |
Derive Macros
State | |
Storage | |
Stored |