Coder Social home page Coder Social logo

serenity's Introduction

ci-badge docs-badge guild-badge crates.io version rust-version-badge

serenity

serenity logo

Serenity is a Rust library for the Discord API.

View the examples on how to use serenity's API. To make a bot with slash commands or text commands, see the poise framework built on top of serenity. To send and receive data from voice channels, see the songbird library.

Serenity supports bot login via the use of Client::builder.

You may also check your tokens prior to login via the use of validate_token.

Once logged in, you may add handlers to your client to dispatch Events, by implementing the handlers in a trait, such as EventHandler::message. This will cause your handler to be called when a Event::MessageCreate is received. Each handler is given a Context, giving information about the event. See the client's module-level documentation.

The Shard is transparently handled by the library, removing unnecessary complexity. Sharded connections are automatically handled for you. See the gateway's documentation for more information.

A Cache is also provided for you. This will be updated automatically for you as data is received from the Discord API via events. When calling a method on a Context, the cache will first be searched for relevant data to avoid unnecessary HTTP requests to the Discord API. For more information, see the cache's module-level documentation.

Note that - although this documentation will try to be as up-to-date and accurate as possible - Discord hosts official documentation. If you need to be sure that some information piece is accurate, refer to their docs.

Example Bot

A basic ping-pong bot looks like:

use std::env;

use serenity::async_trait;
use serenity::model::channel::Message;
use serenity::prelude::*;

struct Handler;

#[async_trait]
impl EventHandler for Handler {
    async fn message(&self, ctx: Context, msg: Message) {
        if msg.content == "!ping" {
            if let Err(why) = msg.channel_id.say(&ctx.http, "Pong!").await {
                println!("Error sending message: {why:?}");
            }
        }
    }
}

#[tokio::main]
async fn main() {
    // Login with a bot token from the environment
    let token = env::var("DISCORD_TOKEN").expect("Expected a token in the environment");
    // Set gateway intents, which decides what events the bot will be notified about
    let intents = GatewayIntents::GUILD_MESSAGES
        | GatewayIntents::DIRECT_MESSAGES
        | GatewayIntents::MESSAGE_CONTENT;

    // Create a new instance of the Client, logging in as a bot.
    let mut client =
        Client::builder(&token, intents).event_handler(Handler).await.expect("Err creating client");

    // Start listening for events by starting a single shard
    if let Err(why) = client.start().await {
        println!("Client error: {why:?}");
    }
}

Full Examples

Full examples, detailing and explaining usage of the basic functionality of the library, can be found in the examples directory.

Installation

Add the following to your Cargo.toml file:

[dependencies]
serenity = "0.12"
tokio = { version = "1.21.2", features = ["macros", "rt-multi-thread"] }

MSRV Policy

Serenity's minimum supported Rust version (MSRV) is Rust 1.74.

We opt to keep MSRV stable on the current branch. This means it will remain unchanged between minor releases. Occasionally, dependencies may violate SemVer and update their own MSRV in a breaking way. As a result, pinning their versions will become necessary to successfully build Serenity using an older Rust release.

The next branch tracks the latest Rust release as its MSRV. This allows for swift development as new languages features are stabilized, and reduces technical debt in the long run. When a new major release is cut, the MSRV on current will be updated to that of next, and we will commit to supporting that MSRV until the following major release.

Features

Features can be enabled or disabled by configuring the library through Cargo.toml:

[dependencies.serenity]
default-features = false
features = ["pick", "your", "feature", "names", "here"]
version = "0.12"

The default features are: builder, cache, chrono, client, framework, gateway, http, model, standard_framework, utils, and rustls_backend.

There are these alternative default features, they require to set default-features = false:

  • default_native_tls: Uses native_tls_backend instead of the default rustls_backend.
  • default_no_backend: Excludes the default backend, pick your own backend instead.

If you are unsure which to pick, use the default features by not setting default-features = false.

The following is a full list of features:

  • builder: The builders used in conjunction with models' methods.
  • cache: The cache will store information about guilds, channels, users, and other data, to avoid performing REST requests. If you are low on RAM, do not enable this.
  • collector: A collector awaits events, such as receiving a message from a user or reactions on a message, and allows for responding to the events in a convenient fashion. Collectors can be configured to enforce certain criteria the events must meet.
  • client: A manager for shards and event handlers, abstracting away the work of handling shard events and updating the cache, if enabled.
  • framework: Enables the framework, which is a utility to allow simple command parsing, before/after command execution, prefix setting, and more.
  • gateway: A Shard, used as a higher-level interface for communicating with the Discord gateway over a WebSocket client.
  • http: Functions providing a wrapper over Discord's REST API at a low enough level that optional parameters can be provided at will via a JsonMap.
  • model: Method implementations for models, acting as helper methods over the HTTP functions.
  • standard_framework: A standard, default implementation of the Framework. NOTE: Deprecated as of v0.12.1. Using the poise framework is recommended instead.
  • utils: Utility functions for common use cases by users.
  • voice: Enables registering a voice plugin to the client, which will handle actual voice connections from Discord. lavalink-rs or Songbird are recommended voice plugins.
  • default_native_tls: Default features but using native_tls_backend instead of rustls_backend.
  • tokio_task_builder: Enables tokio's tracing feature and uses tokio::task::Builder to spawn tasks with names if RUSTFLAGS="--cfg tokio_unstable" is set.
  • unstable_discord_api: Enables features of the Discord API that do not have a stable interface. The features might not have official documentation or are subject to change.
  • simd_json: Enables SIMD accelerated JSON parsing and rendering for API calls, if supported on the target CPU architecture.
  • temp_cache: Enables temporary caching in functions that retrieve data via the HTTP API.
  • chrono: Uses the chrono crate to represent timestamps. If disabled, the time crate is used instead.
  • interactions_endpoint: Enables tools related to Discord's Interactions Endpoint URL feature

To enable all parts of the codebase, use the "full" feature.

For possibly more up-to-date information, check the Cargo.toml.

Serenity offers two TLS-backends, rustls_backend by default, you need to pick one if you do not use the default features:

  • rustls_backend: Uses Rustls for all platforms, a pure Rust TLS implementation.
  • native_tls_backend: Uses SChannel on Windows, Secure Transport on macOS, and OpenSSL on other platforms.

If you want all of the default features except for cache for example, you can list all but that:

[dependencies.serenity]
default-features = false
features = [
    "builder",
    "chrono",
    "client",
    "framework",
    "gateway",
    "http",
    "model",
    "standard_framework",
    "utils",
    "rustls_backend",
]
version = "0.12"

Dependencies

If you use the native_tls_backend and you are not developing on macOS or Windows, you will need:

  • openssl

Hosting

If you want a quick and easy way to host your bot, you can use shuttle, a Rust-native cloud development platform that allows deploying Serenity bots for free.

Projects extending Serenity

  • lavalink-rs: An interface to Lavalink and Andesite, an audio sending node based on Lavaplayer
  • Songbird: An async Rust library for the Discord voice API.
  • Poise: Experimental command framework, with advanced features like edit tracking, single function slash and prefix commands and flexible argument parsing.

serenity's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

serenity's Issues

Getting the permissions of a user

I'm creating a bot that needs to make sure that the calling user has specific Permissions. How would one do this in a command? I haven't found a related field on the User struct and can't find documentation on how to do so.

It seems like you could call something like .ban_members() with the Permissions struct after getting permissions, however the User does not seem to contain permission data.

Thanks.

Expose the ablity to create a voice connection

Exposing the ability to create a voice connection independent of a discord instance would allow users to connect to discord in another thread/instance/server/lib and spawn voice connections from a completely different program by simply transferring basic information back and forth.

fancy message reaction handling

  • message.on_reaction_remove(reaction, closure)
  • message.on_any_reaction_remove(closure)
  • message.on_reaction(reaction, closure)
  • message.on_any_reaction(closure)
  • message.on_reaction_remove_all(closure)

not sure if we really need latter 3.

Message reaction actions

Store functions to call when specific reactions are made (and removed?) on messages.

Design on a userland and internals level still mostly undeveloped.

Typo in example 06

There is a typo in the word "equivalent" in example 06 here. It might also occur in other locations.

Model Docs

These could definitely use some struct-level docs:

  • Embed
  • Emoji
  • Game
  • Guild
  • GuildChannel
  • Invite
  • Member
  • Message
  • Presence
  • PrivateChannel
  • Reaction
  • Relationship
  • Role
  • User
  • VoiceRegion
  • Webhook

The rest could use some work but aren't as important

Determine weak points of the lib

Determine what is more difficult to accomplish than other parts of the library, or what is needlessly complex.

Starters:

  • getting the guild id of a message in a guild

Optimizations

Mainly, users should be detached from the member, and should be stored separately in the state.

Rewrite the framework

It's gotten to be a mess in a lot of areas, especially Framework::dispatch. Over time checks and other stuff have been added but not in a clean way. There's generally a lot of duplicate code as well.

Allow direct playing of pre-encoded Opus audio

It would be nice if there were a built-in way to directly play correctly pre-encoded Opus, to avoid having to spawn and involve ffmpeg unnecessarily.

This was also requested of discord-rs and someone filed a PR but it was never merged, in case its code may be useful.

Fails to compile if voice feature not present

If the voice feature is not enabled, it fails to compile with the message:

error[E0463]: can't find crate for `byteorder`
   --> ~/.cargo/git/checkouts/serenity-3ee4e93465cd5f21/6a1c257/src/lib.rs:102:1
    |
102 | extern crate byteorder;
    | ^^^^^^^^^^^^^^^^^^^^^^^ can't find crate

Workaround for now: enable voice feature.

Remaining examples needing documentation

Examples are fundamental to good documentation, and so far most methods don't have proper examples.

Examples should at least compile and no_run where necessary (for example, where the example would normally hit the REST API or instantiate a client). Examples that assert should run as part of our doctests.

Here's an example of a simple doctest:

/// ```rust
/// use serenity::utils;
///
/// let url = "discord.gg/0cDvIgU2voY8RSYL";
///
/// assert_eq!(utils::parse_invite(url), "0cDvIgU2voY8RSYL");
/// ```

It's not good to unwrap options or results in examples, as users can copy the code directly and end up with problems. An example of a more complex doctest surrounded in a try_main to entice usage of the ? operator looks like:

/// ```rust,no_run
/// # use serenity::model::GuildId;
/// # use std::error::Error;
/// #
/// # fn try_main() -> Result<(), Box<Error>> {
/// #     let mut guild = GuildId(0).get()?;
/// use serenity::utils;
///
/// // assuming a `guild` has already been bound
///
/// let base64_icon = utils::read_image("./guild_icon.png")?;
///
/// guild.edit(|g| g.icon(Some(&base64_icon)))?;
/// #     Ok(())
/// # }
/// #
/// # fn main() {
/// #     try_main().unwrap();
/// # }
/// ```

In this example, we surround the code visible to the user in try_main, so that we can make use of ?. Documentation example lines starting with # hides that line from the generated documentation. See the Rust API Guidelines for more info.

If you'd like to claim one or more sections leave a comment with what ones or mention it in the serenity channel in Discord API.

The following is a list that still needs examples:

  • cache (the cache module docs need an example as well)
    • Cache (#103)
      • guild
      • group
      • private_channel
      • role
      • user
  • framework (#101)
    • help_commands::{plain, with_embeds}
    • Configuration (others optional)
      • blocked_guilds
      • blocked_users
      • disabled_commands
      • owners
      • prefix
      • prefixes
    • Framework
      • bucket
      • simple_bucket
      • on
      • group
      • on_dispatch_error
      • before
      • after
  • model
    • permissions
      • Permissions can use a struct-level example
      • PRESET_GENERAL
      • PRESET_TEXT
    • Attachment
      • dimensions
    • Ban can use a struct-level example of when you'd use it
    • ChannelId
      • broadcast_typing can use a more thorough example
      • create_permission
      • create_reaction
      • delete
      • delete_message
      • delete_messages
      • delete_permission
      • delete_reaction
      • edit can use a more thorough example
      • edit_message
      • message
      • messages
      • pin
      • reaction_users
      • say
      • send_message
      • unpin
    • CurrentUser (#104)
      • avatar_url
      • distinct
      • static_avatar_url
      • invite_url
    • Embed (#105)
      • fake
    • Emoji (#105)
      • delete
      • edit
      • find_guild_id
      • url can use a doctest with an assertion
    • Game (method examples preferably show interacting with Shard methods) (#105)
      • playing
      • streaming
    • Group (others optional)
      • add_recipient
      • broadcast_typing shows an example when say, sending a file, to indicate work is being done
      • create_reaction
      • delete_messages
      • delete_permission
      • delete_reaction
      • edit_message
      • icon_url
      • message
      • reaction_users
      • remove_recipient
      • say
      • send_files
      • send_message
      • unpin
    • Guild (others optional)
      • bans
      • channels
      • create_channel could use a better example in a framework command
      • create_emoji
      • create_integration
      • create_role could use a second, complex example
      • delete_emoji
      • delete_integration
      • delete_role
      • edit_emoji
      • edit_nickname
      • edit_emoji
      • is_large example on when to use it, such as requesting member chunks on guild create
      • kick
      • member
      • member_named
      • move_member
      • start_prune showing a framework command example
      • unban
    • i got tired of typing here
  • utils
    • builder (see CreateInvite or ExecuteWebhook for examples)
      • CreateEmbed
      • CreateEmbedAuthor
      • CreateEmbedField
      • CreateEmbedFooter
      • CreateMessage
      • EditChannel
      • EditGuild
      • EditMember
      • EditProfile
      • EditRole
      • GetMessages

Allow explicit disconnect

I've been prototyping a bot in first Javascript, then Go, then Rust with Discord.rs and now Serenity and all prior libraries I used allowed me to cleanly, explicitly shutdown/disconnect the connection(s), which I did for example when the application received a SIGINT or whenever else I felt it to be useful.

As far as I know this library doesn't currently have the ability to do this, which means that when I terminate the application I still see the bot in my server for a while until it times out.

Add Client::with_data

with_data will allow users to pass arbitrary data prior to starting the client, which should be able to be accessed across all contexts.

Allow conditional compilation of separate modules

Modules should be split into entirely separate conditionally compiled components, e.g.:

  • Core (current ::models and ::internal) (required)
  • WS (::client::gateway)
  • REST (::client::rest)
  • Client (the rest of ::client)
  • Utils (::utils)
    ext is already conditionally compiled

So, WS/REST should not be under Client's module.

Tests

Summary:

  • Add mock tests for calls to the REST API;
  • Integration tests for these as well, which will be ignored by Travis due to spurious failures, but should be tested locally;
  • Go back through commits and add tests for "fixes"; i.e. regression tests;
  • Model methods;
  • Utility-related (Colour/MessageBuilder);
  • Cache/Framework.

Specific items:

Mock: fake the data that we expect to be returned; tested by Travis
Integration: actual requests to the API, which have #[ignore] attributes so they are run locally only; not tested by Travis

  • Mock routes (refer to sidebar on Discord docs for which routes these are)
    • Channel
    • Guild
    • Invite
    • User
    • Webhooks
  • Integration routes (same)
    • Channel
    • Guild
    • Invite
    • User
    • Webhooks
  • Go through commits for fixes (such as embed decoding) and add regression tests (@zeyla)
  • Test model methods (refer to filenames in src/model/*.rs)
    • channel
    • gateway
    • guild
    • id
    • misc
    • permissions
    • user
    • webhook
  • ext
    • Cache (ensure updating with an event properly stores the data, removing removes it, etc.)
    • Framework
  • Display/mention formatting
  • Ratelimit test script
  • Restrictions on Context methods (message limit, only usable by bot user, etc)

Openssl 0.7.14 fails to build

Going here and compiling 0.9.5 succeeds, however this older version doesn't seem to work. Any way to update the dependencies so that it has this newer version?

Error:

Build failed, waiting for other jobs to finish...
error: failed to run custom build command for `openssl v0.7.14`
process didn't exit successfully: `C:\Users\ngupton\Desktop\serenity\target\debug\build\openssl-5464f8f6e728c35a\build-script-build` (exit code: 101)
--- stdout
TARGET = Some("x86_64-pc-windows-msvc")
OPT_LEVEL = Some("0")
PROFILE = Some("debug")
TARGET = Some("x86_64-pc-windows-msvc")
debug=true opt-level=0
HOST = Some("x86_64-pc-windows-msvc")
TARGET = Some("x86_64-pc-windows-msvc")
TARGET = Some("x86_64-pc-windows-msvc")
HOST = Some("x86_64-pc-windows-msvc")
CC_x86_64-pc-windows-msvc = None
CC_x86_64_pc_windows_msvc = None
HOST_CC = None
CC = None
TARGET = Some("x86_64-pc-windows-msvc")
HOST = Some("x86_64-pc-windows-msvc")
CFLAGS_x86_64-pc-windows-msvc = None
CFLAGS_x86_64_pc_windows_msvc = None
HOST_CFLAGS = None
CFLAGS = None
running: "C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\bin\\amd64\\cl.exe" "/nologo" "/MD" "/Z7" "/I" "C:\\OpenSSL-Win64\\include\\" "/FoC:\\Users\\ngupton\\Desktop\\serenity\\target\\debug\\build\\openssl-5464f8f6e728c35a\\out\\src\\c_helpers.o" "/c" "src/c_helpers.c"
c_helpers.c
C:\OpenSSL-Win64\include\openssl/lhash.h(198): warning C4090: 'function': different 'const' qualifiers
src/c_helpers.c(4): error C2037: left of 'references' specifies undefined struct/union 'ssl_st'
src/c_helpers.c(4): error C2065: 'CRYPTO_LOCK_SSL': undeclared identifier
src/c_helpers.c(8): error C2037: left of 'references' specifies undefined struct/union 'ssl_ctx_st'
src/c_helpers.c(8): error C2065: 'CRYPTO_LOCK_SSL_CTX': undeclared identifier
src/c_helpers.c(12): error C2037: left of 'references' specifies undefined struct/union 'x509_st'
src/c_helpers.c(12): error C2065: 'CRYPTO_LOCK_X509': undeclared identifier
ExitStatus(ExitStatus(2))


command did not execute successfully, got: exit code: 2



--- stderr
thread 'main' panicked at 'explicit panic', C:\Users\ngupton\.cargo\registry\src\github.com-1ecc6299db9ec823\gcc-0.3.41\src\lib.rs:1018
note: Run with `RUST_BACKTRACE=1` for a backtrace.

Add an `on_cached` Client method

The method will fire when all GuildCreates have been received.

Questions:

  • Should this fire when each shard is fully cached (providing the shard ID), or when all shards the Client is responsible for are fully cached?
  • What information should be provided in the event fire?

Spaces cause part of command to be sent as an argument.

After typing a prefix it seems that any amount of whitespace is cut out while checking for the command to invoke. However, when parsing arguments the whitespace still remains and the command is cut up.

For example, given the following command:

command!(cmd(_ctx, msg, args) {
    if args.len() == 1 {
        println!("Got argument '{}'", args[0]);
    } else {
        println!("No arguments.");
    }
});

I get the following behavior, assuming a prefix of + (With _ being spaces because I don't know how to make spaces not compact)

+cmd => "No arguments"
+_cmd => "No arguments"
+__cmd => "Got argument 'd'"
+___cmd => "Got argument 'md'"
etc.

Conditional compilation flags

Add conditional compilation for:

  • voice (disabled by default)
  • state (enabled by default)
  • framework (enabled by default)
  • extras (message builder, utils) (disabled by default)
  • struct utils (Message::edit, Channel::delete, etc.) (enabled by default)

Channel Type incorrectly reported

Channel types for GuildChannels, Groups, and PrivateChannels are incorrectly reported.

Guild Channels will always be Groups for example. This is because the build script is incorrectly making channel type numbers correspond to a different ChannelType.

Framework Ideas

Right now it's mostly a command parser (https://raw.githubusercontent.com/zeyla/serenity.rs/feb2ee1eddc92cf02fc9e64534030ae0d374418b/examples/06_command_framework.rs), but a full framework is the end goal.

Basically an issue for tracking ideas.

Current function signature: fn weather(context: Context, message: Message, arguments: Vec<String>)

Ideas

Named parameters

Status: Done โœ“

Rather than the current signature, allow fn weather(context: Context, message: Message, location: String, action: Option<String>)

Struct parameters

Status: Done in a good enough fashion โœ“ #36

Possibly something like

#[derive(Deserialize)]
struct WeatherArguments {
    pub action: Option<String>,
    pub location: String,
}

fn weather(context: Context, message: Message, arguments: WeatherArguments)

Pre-/Post-Command Closures

Status: Done โœ“ #13
Closures that can run before or after command execution (not specific to a command). Idea courtesy @fwrs

Help Command

Status: Done โœ“ #40

Multiple Prefixes

Status: Done โœ“ #38

MessageBuilder unsafe content

  • by default message builder should escape all mentions as there's mention function
  • add push_unsafe method that ensures that user input doesn't cause unwanted results such as formatting, mention spam, everyone/here, custom emojis etc
  • add push_italic, push_italic_unsafe, push_mono, push_mono_unsafe, push_bold, push_bold_unsafe, push_codeblock, push_codeblock_unsafe. because in different contexts normalizing is different (eg in inline monospace you shouldn't escape bold formatting)
  • also put normalize methods in context I guess so message builder isn't mandatory

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.