165 lines
5.1 KiB
Rust
165 lines
5.1 KiB
Rust
//! A framework for building Discord bots, built with Twilight.
|
|
//!
|
|
//! Since Twilight is a low-level library, it can be hard to build a bot with it. Payphone's
|
|
//! framework provides a high-level interface for building Discord bots, with a focus on modularity
|
|
//! and ease of use. The framework is designed to be extensible, allowing you to add new features
|
|
//! and commands without modifying the core codebase. It also provides a set of utilities for
|
|
//! common tasks, such as sending messages, handling events, and managing state.
|
|
//!
|
|
//! # Creating a Bot
|
|
//!
|
|
//! Creating a bot with Payphone is as simple as creating a new instance of the `Bot` struct and
|
|
//! running it. The `Bot` struct is the main entry point for the framework and provides methods for
|
|
//! running the bot, handling events, and managing state.
|
|
//!
|
|
//! ```
|
|
//! use crate::framework::bot::Bot;
|
|
//! use crate::state::State;
|
|
//!
|
|
//! // Your app's state.
|
|
//! let state = State::new();
|
|
//!
|
|
//! // Create a new bot instance with the state and a list of prefixes.
|
|
//! let bot: Bot<State> = Bot::new(state, ["p."]);
|
|
//!
|
|
//! bot.run(&token).await.unwrap();
|
|
//! ```
|
|
//!
|
|
//! ### Prefixes
|
|
//!
|
|
//! The framework supports multiple prefixes per command and allows you to set them dynamically
|
|
//! based on server and user IDs.
|
|
//!
|
|
//! ```
|
|
//! async fn get_prefixes(
|
|
//! rt: &Runtime<State>,
|
|
//! server_id: Option<u64>,
|
|
//! user_id: Option<u64>
|
|
//! ) -> Vec<String> {
|
|
//! // The runtime contains your state at [`Runtime::state`].
|
|
//!
|
|
//! vec!["p.".to_string()]
|
|
//! }
|
|
//!
|
|
//! let bot: Bot<State> = Bot::new(state, get_prefixes);
|
|
//! ```
|
|
//!
|
|
//! Otherwise, you can just pass a static list of prefixes as shown in the previous example.
|
|
//!
|
|
//! ```
|
|
//! // All of these are valid ways to create a bot with a prefix.
|
|
//! let bot: Bot<State> = Bot::new(state, ["p."]);
|
|
//! let bot: Bot<State> = Bot::new(state, "p.");
|
|
//! let bot: Bot<State> = Bot::new(state, "p.".to_string());
|
|
//! let bot: Bot<State> = Bot::new(state, ["p.".to_string()]);
|
|
//! ```
|
|
//!
|
|
//! Setting no prefix will make the bot raise errors when receiving messages.
|
|
//!
|
|
//! ## Adding Commands
|
|
//!
|
|
//! Creating a command is quite simple. You just need to implement the `Command` trait and register
|
|
//! it with the bot.
|
|
//!
|
|
//! ```
|
|
//! use crate::framework::commands::{Command, CommandError, context::CommandContext};
|
|
//!
|
|
//! pub struct MyCommand;
|
|
//!
|
|
//! #[async_trait::async_trait]
|
|
//! impl Command<State> for MyCommand {
|
|
//! fn name(&self) -> &'static str {
|
|
//! "my_command"
|
|
//! }
|
|
//!
|
|
//! async fn execute(&self, ctx: CommandContext<'_, T>, _args: &str) -> Result<(), CommandError> {
|
|
//! // Your command logic here.
|
|
//! ctx.send()
|
|
//! .content("Hello, world!")
|
|
//! .await?;
|
|
//!
|
|
//! Ok(())
|
|
//! }
|
|
//! }
|
|
//! ```
|
|
//!
|
|
//! Then you just need to register the command with the bot:
|
|
//!
|
|
//! ```
|
|
//! let bot = Bot::new(state, ["p."]).command(MyCommand);
|
|
//!
|
|
//! bot.run(&token).await.unwrap();
|
|
//! ```
|
|
//!
|
|
//! Log into your Discord account and invite the bot to your server. Then you can test the command
|
|
//! by sending `p.my_command` in a channel the bot has access to.
|
|
//!
|
|
//! ### Configuring Commands Further
|
|
//!
|
|
//! Commands can be configured with additional metadata, such as aliases, descriptions, and
|
|
//! arguments. The following is a full example of a command with all these features:
|
|
//!
|
|
//! ```
|
|
//! use crate::framework::commands::{
|
|
//! Command, CommandError, context::CommandContext, arguments::NeedyGreedyString
|
|
//! };
|
|
//! use crate::state::State;
|
|
//!
|
|
//! pub struct HelloCommand;
|
|
//!
|
|
//! #[async_trait::async_trait]
|
|
//! impl Command<State> for HelloCommand {
|
|
//! fn name(&self) -> &'static str {
|
|
//! "hello"
|
|
//! }
|
|
//!
|
|
//! /// A list of alternative names for the command.
|
|
//! fn aliases(&self) -> &'static [&'static str] {
|
|
//! &["hola", "hey", "hi", "konnichiwa"]
|
|
//! }
|
|
//!
|
|
//! /// A description of the command and its functionality.
|
|
//! fn description(&self) -> &'static str {
|
|
//! "This command responds with a greeting."
|
|
//! }
|
|
//!
|
|
//! /// A list of arguments the command accepts. These are solely for documentation purposes,
|
|
//! /// they won't affect the parsing you do in the command's `execute` method.
|
|
//! fn arguments(&self) -> &'static [Argument] {
|
|
//! &[
|
|
//! Argument {
|
|
//! name: "name",
|
|
//! is_optional: false,
|
|
//! is_greedy: true,
|
|
//! },
|
|
//! ]
|
|
//! }
|
|
//!
|
|
//! async fn execute(
|
|
//! &self,
|
|
//! ctx: CommandContext<'_, T>,
|
|
//! args: &str,
|
|
//! ) -> Result<(), CommandError> {
|
|
//! // Parse the arguments using the `ParseArguments` trait.
|
|
//! let name: NeedyGreedyString = ParseArguments::parse(
|
|
//! args
|
|
//! )
|
|
//! .unwrap_or(NeedyGreedyString("???".to_string()));
|
|
//!
|
|
//! ctx.send()
|
|
//! .content(format!("Hello, {}!", name.0))
|
|
//! .await?;
|
|
//!
|
|
//! Ok(())
|
|
//! }
|
|
//! }
|
|
//! ```
|
|
|
|
pub mod bot;
|
|
pub mod commands;
|
|
pub mod events;
|
|
pub mod interactions;
|
|
pub mod messages;
|
|
pub mod runtime;
|
|
pub mod tasks;
|