#discord-bot #discord #bots #serenity #api-bindings

yanked discord-base

The crate I use to build my bots on top of, made using Serenity in Rust!

0.1.0 Feb 9, 2021

#25 in #serenity

MIT/Apache

32KB
663 lines

discord-base

The repo for the crate I use to build my bots on top of, made using Serenity in Rust!

How to set it up

  1. IDK myself yet..
  2. Go to the application page, select your bot and set the description. The info command will use that and the account that has that application
  3. Then check usage right under here

How to use it

start(config_path: &str)

  • Does everything to start up the bot
  • Or creates the default config file if it's not there, then you should edit that file
  • Use it as the first thing in your #[tokio::main] function
use discord_base::start;
start("config.toml").await;

log(ctx: &Context, msg: impl Display + AsRef<[u8]>)

  • DMs the owner the message
  • Reverts to print_and_write if it failed, also telling why it failed
use discord_base::log;
log(&ctx, "Heya owner! How are you?").await;

print_and_write(msg: impl Display)

  • Prints and writes the message and the current time in UTC to the log_file set in the config
/*Assume the config file has this in it:
log_file = "logs.txt"
*/
use discord_base::print_and_write;
print_and_write("Magic text appearing in the text file");
/* The file logs.txt is now created and has these in it:
5 February Friday 10:38:20: Magic text appearing in the text file

*/
// Pretend five seconds passed
print_and_write("And even more text here");
/* The file now has these in it:
5 February Friday 10:38:20: Magic text appearing in the text file

5 February Friday 10:38:25: And even more text here

*/

send_embed(ctx: &Context, reply: &Message, is_error: bool, mut embed: CreateEmbed)

  • Sends the embed to the channel reply is in, colours it with the colour you gave in your config file
  • Unless is_error is true then it's the error colour (That's all that parameter does)
  • If it's failed to send though, tries to tell why in the server in plain text (without embeds)
  • Or DMs the reply's sender if that also fails
use discord_base::send_embed;
let mut embed = CreateEmbed::default();
embed.title("This is my title");
send_embed(ctx, msg, true, embed).await;

What else it does

Error handling

Everything that's done in this crate follows these principals:

  • If the action isn't expected by the user, don't inform them even if it fails
  • If anything else, do inform them. Fall back to DMing the user if informing the user failed
  • If it's a user error, tell them the error and how to fix it if it's not obvious
  • If it's a bot error, tell them how to report it and what they can do and inform the owner of the bot. Fall back to printing and logging in a file
  • If we can't be sure, tell them the error and how to report it if it looks like a bot error

These, combined with Rust's safety, ensure the best user experience. Most of these errors could be handled by falling back (Not using embeds, DMing the user etc.) but this overcomplicates the bot and makes it inconsistent, without forcing the user to fix it, so it's not a good practice

Optimisation

  • I've tried my best to use statics and avoid awaits
  • Also adding buckets and rate limit handling to ensure it isn't abused
  • Combined with Rust's and SQLite's performance, the bot should be really lightweight

I can't say fast because we'll be bottlenecked by Discord anyway. It's still definitely fast enough that the user won't notice at all

Presence

  • Sets the presence to Playing a game: @[bot's username] help (This looks much better than other presences Discord allows)

Commands

All these don't have a prefix so they're run with @bot [command]. You set your own prefix for the groups you create
(I made it this way because usually only these commands collide with other bots so you can use convenient prefixes for your own commands)

Info command

  • An info command that gets the desciption and owner from the application page and the GitHub page and invite link from the config file

Prefix command

  • A prefix command that sets the prefix for the guild, which works for every command in addition to @bot and the prefixes you set for your groups
  • This isn't as simple as it seems. It means the bot has to check if the message starts with its prefix in that server for every message that's sent
  • To further optimise this, the bot first checks the message's first max prefix length (10) + longest command's length characters if it includes any of the commands, if not it doesn't unnecessarily check since there's no way the message includes a command

Help command

(Provided by Serenity's standard framework)

  • A nice help command, listing all the other commands and their groups
  • Gives more information about a command with help [command]
  • Suggests similar commands if help [command] is.. similar to another command

Who I am

Just some (currently) 17 years old girl from Turkey coding
Started with Python, gave a shot to JS but now that I know Rust exists never going back
Basically all I did was Discord bots (at least at the time of writing)
License and stuff I don't care, neither should you but contact me if you want to ask anything

How you can contact me

  • Very Fast: Discord: aria#7553
  • Slow: GitHub issues or something
  • Way slower (or never, if you're unlucky): wentaas@gmail.com

Ideas I had but decided not to implement

Handling permissions

Too expensive, limited, bad for UX, unnecessary and inconsistent. Doing proper error checking and informing the user on an error is just a better option

Localisation

Makes everything more expensive, you now have to check the language for every message you're sending, which means you can't use any static string. Community translation will always be inconsistent, slow and incomplete. Most users wouldn't expect or use it. Having separate bots for each language is just a better option

Customisation

Again makes everything more expensive, since it means you can't use any static string. If someone is hosting the bot, they most likely have enough knowledge to search for a string in the source and replace it then build. It isn't necessary at all and I still tried to include customisation when it didn't mean a performance loss

Dependencies

~58MB
~1M SLoC