//! 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 = 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, //! server_id: Option, //! user_id: Option //! ) -> Vec { //! // The runtime contains your state at [`Runtime::state`]. //! //! vec!["p.".to_string()] //! } //! //! let bot: Bot = 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 = Bot::new(state, ["p."]); //! let bot: Bot = Bot::new(state, "p."); //! let bot: Bot = Bot::new(state, "p.".to_string()); //! let bot: Bot = 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 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 //! .map_err(|_| CommandError::MessageSendingFailed)?; //! //! 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 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 //! .map_err(|_| CommandError::MessageSendingFailed)?; //! //! Ok(()) //! } //! } //! ``` pub mod bot; pub mod commands; pub mod events; pub mod interactions; pub mod runtime;