aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorCara Salter <cara@devcara.com>2022-01-07 13:33:31 -0500
committerCara Salter <cara@devcara.com>2022-01-07 13:33:31 -0500
commit3f9376a46efeb53b494a8b5272fc15be6ca5869a (patch)
treec3dc6e9a1b8362479718e8c8ea7d90ea6c3b6a75 /src
downloadglitch-ng-3f9376a46efeb53b494a8b5272fc15be6ca5869a.tar.gz
glitch-ng-3f9376a46efeb53b494a8b5272fc15be6ca5869a.zip
Initial Commit
Diffstat (limited to 'src')
-rw-r--r--src/commands/actions.rs109
-rw-r--r--src/commands/meta.rs38
-rw-r--r--src/commands/mod.rs4
-rw-r--r--src/commands/osu.rs166
-rw-r--r--src/commands/pony.rs137
-rw-r--r--src/main.rs103
6 files changed, 557 insertions, 0 deletions
diff --git a/src/commands/actions.rs b/src/commands/actions.rs
new file mode 100644
index 0000000..96862bb
--- /dev/null
+++ b/src/commands/actions.rs
@@ -0,0 +1,109 @@
+use crate::{Context, Error};
+use poise::serenity_prelude as serenity;
+
+use rand::Rng;
+
+static HUG_VEC: [&'static str; 48] = [
+ "https://media1.tenor.com/images/2d9902a4de4ad000be9ca64c5abac9a1/tenor.gif?itemid=8766309",
+ "https://media1.tenor.com/images/dd056f2e708792ce8085649eaf0c3307/tenor.gif?itemid=13964315",
+ "https://media1.tenor.com/images/4081db5de72d7b225be12e764715cb99/tenor.gif?itemid=13964310",
+ "https://i.kym-cdn.com/photos/images/original/000/360/344/26e.gif",
+ "https://media.giphy.com/media/t0K54lpOGUmlO/giphy.gif",
+ "https://i.kym-cdn.com/photos/images/newsfeed/000/464/096/ccb.gif",
+ "https://i.kym-cdn.com/photos/images/newsfeed/000/988/544/548.gif",
+ "https://i.gifer.com/IPJ1.gif",
+ "https://i.kym-cdn.com/photos/images/original/000/503/185/f38.gif",
+ "https://thumbs.gfycat.com/TepidPrestigiousAmazontreeboa-max-1mb.gif",
+ "https://thumbs.gfycat.com/SparseContentHypacrosaurus-small.gif",
+ "https://i.pinimg.com/originals/6d/7d/dc/6d7ddcb24307c502da6fcc17334251db.gif",
+ "https://i.pinimg.com/originals/16/b3/49/16b349b54d19818a895a2bd8ecebae3e.gif",
+ "https://66.media.tumblr.com/8e38820abca85c70a645079929471657/tumblr_oopymyHyz71vtnwhpo1_400.gif",
+ "https://media1.tenor.com/images/0ea597aa8a27770ff072b004a3dfbcdd/tenor.gif?itemid=14521586",
+ "https://media1.giphy.com/media/tKJqxkFT1y6qY/giphy.gif&key=909e231cc9819f389143bd5645661eac236a26cffc5bf69578000f0d2f8a6403",
+ "https://buffy.mlpforums.com/monthly_2019_05/2513213.gif.2bd1eb00f85e8c4ec5e212bac52a2815.gif",
+ "https://i.kym-cdn.com/photos/images/original/000/546/558/e57.gif",
+ "https://data.whicdn.com/images/333305271/original.gif",
+ "https://derpicdn.net/img/2014/4/10/597321/full.gif",
+ "https://images-wixmp-ed30a86b8c4ca887773594c2.wixmp.com/f/2b92ff77-0420-4b4a-bc2e-6a81f19bb447/d5o7mtq-61d57c24-4e23-4b3b-a67b-91b68ec155ff.gif?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1cm46YXBwOiIsImlzcyI6InVybjphcHA6Iiwib2JqIjpbW3sicGF0aCI6IlwvZlwvMmI5MmZmNzctMDQyMC00YjRhLWJjMmUtNmE4MWYxOWJiNDQ3XC9kNW83bXRxLTYxZDU3YzI0LTRlMjMtNGIzYi1hNjdiLTkxYjY4ZWMxNTVmZi5naWYifV1dLCJhdWQiOlsidXJuOnNlcnZpY2U6ZmlsZS5kb3dubG9hZCJdfQ._cX9QVNRzOu0YXp89DlFun4gwd-lBx3wRBBQhWz0Uog",
+ "https://mrwgifs.com/wp-content/uploads/2013/05/Fluttershy-Rainbow-Dash-Excited-Hug-On-My-Little-Pony.gif",
+ "https://i.kym-cdn.com/photos/images/original/001/284/428/e2c.gif",
+ "https://i.kym-cdn.com/photos/images/original/001/284/454/d15.gif",
+ "https://i.kym-cdn.com/photos/images/original/001/508/796/105.gif",
+ "https://i.kym-cdn.com/photos/images/original/000/453/448/604.gif",
+ "https://pa1.narvii.com/6513/153ac19fe9f83512ee28e93cec3955eb75391e3f_00.gif",
+ "https://img.cartoongoodies.com/wp-content/uploads/2019/11/07164015/My-Little-Pony-Bear-Hug.gif",
+ "https://i.imgur.com/9ko6n.gif",
+ "https://ii.yuki.la/7/7c/8c06bd8120840fcbd02ec9c6babccb2113bba37844ac605c483987259484a7c7.gif",
+ "https://derpicdn.net/img/view/2017/5/20/1441283.gif",
+ "https://i.kym-cdn.com/photos/images/original/001/286/795/785.gif",
+ "https://i1.wp.com/derpicdn.net/img/2014/2/2/541180/full.gif",
+ "https://thumbs.gfycat.com/GlossyCloseAfricancivet-size_restricted.gif",
+ "https://i.pinimg.com/originals/d5/bf/21/d5bf21c9e018d40a6e9a3a0273e52ff1.gif",
+ "https://i.pinimg.com/originals/14/4f/17/144f17e18f9bb5fbd033bf467affd1b4.gif",
+ "https://i.kym-cdn.com/photos/images/original/000/334/693/430.gif",
+ "https://i.kym-cdn.com/photos/images/original/000/968/146/1c5.gif",
+ "https://i.gifer.com/X3Cw.gif",
+ "https://www.tamatalk.com/IB/uploads/gallery/album_14/gallery_193175_14_257519.gif",
+ "https://ii.yuki.la/c/92/dd67cf2750331016171f62e86dbd8fb209700e4ddbfe21a3ac668a006dd0892c.gif",
+ "https://i0.kym-cdn.com/photos/images/original/000/456/998/a7a.gif",
+ "https://derpicdn.net/img/view/2016/9/11/1247199__safe_screencap_animated_scootaloo_eyes+closed_hug_griffon_the+fault+in+our+cutie+marks_spoiler-colon-s06e19_hovering.gif",
+ "https://derpicdn.net/img/2012/7/5/32216/full.gif",
+ "https://i.kym-cdn.com/photos/images/original/001/166/921/31e.gif",
+ "https://i.kym-cdn.com/photos/images/original/000/513/996/2b1.gif",
+ "https://i.kym-cdn.com/photos/images/original/001/284/346/fbe.gif",
+ "https://derpicdn.net/img/2019/7/6/2084796/medium.gif",
+];
+
+static BOOP_VEC: [&'static str; 15] = [
+ "https://i.pinimg.com/originals/8f/67/20/8f6720fb8b277f120658fbceef9303b0.gif",
+ "https://66.media.tumblr.com/b916203f91fdc2d3a85aef6e1454d785/tumblr_oxljyr11c01w0by9bo1_400.gif",
+ "https://i.pinimg.com/originals/a6/39/fe/a639fe75e4e15ae4705fe1cff55aa0fe.gif",
+ "https://i.kym-cdn.com/photos/images/original/001/406/562/2d4.gif",
+ "https://66.media.tumblr.com/8ce960ad7d7a100ce87027809c41a728/tumblr_opwdkf56Mc1w0by9bo1_400.gif",
+ "https://i.pinimg.com/originals/92/8b/47/928b477daf42151c0db18edb9221172f.gif",
+ "https://thumbs.gfycat.com/AgileRectangularArizonaalligatorlizard-small.gif",
+ "https://buffy.mlpforums.com/monthly_2017_11/img-1331978-1-mlfw2090-87516_-_animated_boop_fluttershy_rainbow_dash.gif.444b4e7258a9e3e82606a312e87a1cec.gif",
+ "https://i.imgur.com/GInYrc7.gif",
+ "https://i.pinimg.com/originals/86/1d/49/861d494ec0115bb96892aad8e937a4a3.gif",
+ "https://i.kym-cdn.com/photos/images/original/001/243/977/85a.gif",
+ "https://i.pinimg.com/originals/f7/5b/d4/f75bd4ab2ba0a94a742c67a6df10486b.gif",
+ "https://i.pinimg.com/originals/e7/59/29/e7592972772130c45aaaea1001edb6dc.gif",
+ "https://i.pinimg.com/originals/aa/60/27/aa60277149600ea29500aa0318d01289.gif",
+ "https://derpicdn.net/img/view/2018/3/16/1682500__safe_artist-colon-grypher_derpibooru+exclusive_tempest+shadow_my+little+pony-colon-+the+movie_spoiler-colon-my+little+pony+movie_animated_boop.gif",
+
+];
+
+/// Boops the user! D'awwww
+///
+/// Usage:
+/// ~boop <@User>
+#[poise::command(context_menu_command = "Boop!", slash_command, prefix_command)]
+pub async fn boop(ctx: Context<'_>,
+ #[description = "The user to be booped"] user: serenity::User,
+ ) -> Result<(), Error> {
+ let url = get_random_url_from_vec(BOOP_VEC.to_vec());
+ ctx.say(format!("<@{}> boops <@{}>! Awwwww!\n{}", ctx.author().id.0, user.id.0, url)).await?;
+
+ Ok(())
+}
+
+/// Hugs the user with a random gif
+///
+/// Usage:
+/// ~hug <@User>
+#[poise::command(context_menu_command = "Hug!", slash_command, prefix_command)]
+pub async fn hug(ctx: Context<'_>,
+ #[description = "The user to be hugged"] user: serenity::User,
+ ) -> Result<(), Error> {
+ let url = get_random_url_from_vec(HUG_VEC.to_vec());
+ ctx.say(format!("<@{}> hugs <@{}>! So kind of them.\n{}", ctx.author().id.0, user.id.0, url)).await?;
+
+ Ok(())
+}
+fn get_random_url_from_vec(vec: Vec<&str>) -> &str {
+ let mut url = "";
+ let rand = rand::thread_rng().gen_range(0..vec.len());
+ url = &vec[rand];
+
+ url.clone()
+}
diff --git a/src/commands/meta.rs b/src/commands/meta.rs
new file mode 100644
index 0000000..d2c5564
--- /dev/null
+++ b/src/commands/meta.rs
@@ -0,0 +1,38 @@
+use crate::{Context, Error};
+use poise::serenity_prelude as serenity;
+
+/// Pings the bot
+#[poise::command(prefix_command, slash_command)]
+pub async fn ping(ctx: Context<'_>) -> Result<(), Error> {
+ ctx.say("Pong!").await?;
+
+ Ok(())
+}
+
+/// Shows information about the bot
+#[poise::command(prefix_command, slash_command)]
+pub async fn about(ctx: Context<'_>) -> Result<(), Error> {
+ let current_version = env!("CARGO_PKG_VERSION");
+
+ if let Err(e) = ctx.send(|m| {
+ m.embed(|e| {
+ e.title("Glitch");
+
+ e
+ });
+ m
+ }).await {
+ return Err(e.into());
+ }
+
+ Ok(())
+}
+
+#[poise::command(prefix_command, slash_command, context_menu_command = "User Info")]
+pub async fn userinfo(ctx: Context<'_>,
+ #[description = "The user to get info on"]
+ user: serenity::User,
+ ) -> Result<(), Error> {
+
+ Ok(())
+}
diff --git a/src/commands/mod.rs b/src/commands/mod.rs
new file mode 100644
index 0000000..50636a3
--- /dev/null
+++ b/src/commands/mod.rs
@@ -0,0 +1,4 @@
+pub mod meta;
+pub mod pony;
+pub mod actions;
+pub mod osu;
diff --git a/src/commands/osu.rs b/src/commands/osu.rs
new file mode 100644
index 0000000..2b3961f
--- /dev/null
+++ b/src/commands/osu.rs
@@ -0,0 +1,166 @@
+use std::time::Duration;
+
+use crate::{Context, Error};
+use poise::serenity_prelude as serenity;
+use reqwest::{header, ClientBuilder};
+use serde::{Deserialize, Serialize};
+
+#[derive(Deserialize)]
+struct OsuTokenResponse {
+ pub access_token: String,
+}
+
+#[derive(Serialize)]
+struct OsuTokenRequest {
+ pub client_id: u32,
+ pub client_secret: String,
+ pub grant_type: String,
+ pub scope: String,
+}
+
+async fn setup_reqwest() -> Result<reqwest::Client, Error> {
+ let client_id = std::env::var("OSU_CLIENT_ID").unwrap();
+ let client_secret = std::env::var("OSU_CLIENT_SECRET").unwrap();
+
+ let token_req = OsuTokenRequest {
+ client_id: client_id.parse::<u32>().unwrap(),
+ client_secret,
+ grant_type: "client_credentials".into(),
+ scope: "public".into(),
+ };
+
+ let req = reqwest::Client::new().post("https://osu.ppy.sh/oauth/token")
+ .json(&token_req).send().await?
+ .json::<OsuTokenResponse>().await?;
+
+
+ let mut headers = header::HeaderMap::new();
+ headers.insert("Authorization", header::HeaderValue::from_str(format!("Bearer {}", req.access_token).as_str()).unwrap());
+
+ Ok(ClientBuilder::new()
+ .default_headers(headers)
+ .build().unwrap())
+}
+
+#[derive(Deserialize, Serialize, Clone, Debug)]
+struct OsuUser {
+ pub username: String,
+ pub avatar_url: String,
+ pub country_code: String,
+ pub is_supporter: bool,
+ pub join_date: chrono::DateTime<chrono::Utc>,
+ pub statistics: OsuUserStats,
+}
+
+#[derive(Deserialize, Serialize, Clone, Debug)]
+struct OsuUserStats {
+ pub global_rank: Option<u32>,
+ pub pp: f32,
+ pub hit_accuracy: Option<f32>,
+ pub grade_counts: OsuUserStatsGrades,
+ pub country_rank: Option<u32>,
+}
+
+#[derive(Deserialize, Serialize, Clone, Debug)]
+struct OsuUserStatsGrades {
+ pub ss: u32,
+ pub s: u32,
+ pub a: u32,
+}
+
+/// Gets an osu profile by username
+///
+/// Usage:
+/// ~osup <username>
+/// Examples:
+/// ~osup muirrum
+#[poise::command(slash_command, prefix_command)]
+pub async fn osup(ctx: Context<'_>,
+ #[description = "The osu! username or ID to look up"]
+ lookup: String,
+ ) -> Result<(), Error> {
+ let client = setup_reqwest().await?;
+
+ let mut res = client.get(format!("https://osu.ppy.sh/api/v2/users/{}?key=username", lookup))
+ .send().await?.json::<OsuUser>().await?;
+
+ res.country_code = res.country_code.to_lowercase();
+
+ ctx.send(|m| {
+ m.embed(|e| {
+ e.title(format!("osu! Profile: {}", res.clone().username));
+ e.thumbnail(res.clone().avatar_url);
+ e.field("Ranks", format!(":map: #{}\n:flag_{}: #{}", res.clone().statistics.global_rank.unwrap_or(0), res.clone().country_code, res.clone().statistics.country_rank.unwrap_or(0u32)), true);
+
+ e.field("Stats", format!("**PP:** {}\n**Acc:** {}%", res.clone().statistics.pp, res.clone().statistics.hit_accuracy.unwrap_or(0.0)), false);
+ e
+ });
+ m
+ }).await?;
+
+ Ok(())
+}
+
+#[derive(Deserialize, Debug, Clone)]
+struct OsuBeatMap {
+ pub id: u32,
+ pub mode: String,
+ pub status: String,
+ pub version: String,
+ pub total_length: u32,
+ pub difficulty_rating: f32,
+ pub bpm: u32,
+ pub last_updated: chrono::DateTime<chrono::Utc>,
+ pub passcount: u32,
+ pub playcount: u32,
+ pub beatmapset: OsuBeatMapSet,
+}
+
+#[derive(Deserialize, Debug, Clone)]
+struct OsuBeatMapSet {
+ pub id: u32,
+ pub nsfw: bool,
+ pub title: String,
+ pub artist: String,
+ pub covers: OsuBeatMapSetCovers,
+ pub creator: String,
+ pub tags: String,
+ pub submitted_date: chrono::DateTime<chrono::Utc>,
+}
+
+#[derive(Deserialize, Debug, Clone)]
+struct OsuBeatMapSetCovers {
+ #[serde(rename = "list@2x")]
+ pub list2: String,
+}
+
+/// Looks up an osu! beatmap by its ID
+///
+/// Usage:
+/// ~osubm <id>
+#[poise::command(slash_command, prefix_command)]
+pub async fn osubm(ctx: Context<'_>,
+ #[description = "The beatmap ID"]
+ bm_id: u32,
+ ) -> Result<(), Error> {
+ let client = setup_reqwest().await?;
+
+ let mut res = client.get(format!("https://osu.ppy.sh/api/v2/beatmaps/{}", bm_id))
+ .send().await?.json::<OsuBeatMap>().await?;
+
+ ctx.send(|m| {
+ m.embed(|e| {
+ e.title(format!("osu! Beatmap: {} by {}", res.beatmapset.title, res.beatmapset.creator));
+ e.image(res.beatmapset.covers.list2);
+ e.description(format!("**Length:** {} **BPM:** {}\n**Difficulty:** {}:star:", res.total_length, res.bpm, res.difficulty_rating));
+ e.footer(|f| {
+ f.text(format!("BM ID {} | BM Set ID {}\nCreated {}", res.id, res.beatmapset.id, res.beatmapset.submitted_date));
+ f
+ });
+ e
+ });
+ m
+ }).await?;
+
+ Ok(())
+}
diff --git a/src/commands/pony.rs b/src/commands/pony.rs
new file mode 100644
index 0000000..75b4238
--- /dev/null
+++ b/src/commands/pony.rs
@@ -0,0 +1,137 @@
+use crate::{Context, Error};
+use poise::serenity_prelude as serenity;
+
+use serde::Deserialize;
+use std::collections::HashMap;
+
+#[derive(Deserialize)]
+pub struct PonyResponse {
+ pub pony: Pony,
+}
+
+#[derive(Deserialize)]
+pub struct Pony {
+ pub id: u64,
+ pub derpiId: Option<u64>,
+ pub tags: Vec<String>,
+ pub sourceURL: Option<String>,
+ pub height: u64,
+ pub width: u64,
+ pub aspectRatio: f64,
+ pub mimeType: String,
+ pub originalFormat: String,
+
+ pub representations: PonyRepresentation,
+}
+
+#[derive(Deserialize)]
+pub struct PonyRepresentation {
+ pub full: String,
+ pub tall: String,
+ pub large: String,
+ pub medium: String,
+ pub small: String,
+ pub thumb: String,
+ pub thumbSmall: String,
+ pub thumbTiny: String,
+}
+
+/// Retrieves a random SFW pony image
+#[poise::command(slash_command, prefix_command)]
+pub async fn randpony(ctx: Context<'_>) -> Result<(), Error> {
+ let mut response_msg = ctx.say("Fetching a random pony image, please wait!").await?;
+
+ let client = reqwest::Client::new();
+ let res = reqwest::get("https://theponyapi.com/api/v1/pony/random")
+ .await?
+ .json::<PonyResponse>()
+ .await?;
+
+ match response_msg.unwrap().message().await {
+ Ok(mut msg) => {
+ msg.edit(&ctx.discord(), |m| {
+ m.content("");
+ m.embed(|e| {
+ e.title("Pony!");
+ e.image(res.pony.representations.full.clone());
+ let res_tags = get_tags_as_string(res.pony.tags);
+ e.field("Tags", format!("{:?}", res_tags), true);
+ e.field("Image URL", res.pony.representations.full.clone(), true);
+ if let Some(url) = res.pony.sourceURL {
+ e.field("Source", url, true);
+ } else {
+ e.field("Source", "None found", true);
+ }
+ e
+ });
+ m
+ }).await?;
+ },
+ Err(e) => {
+ ctx.say("Error editing message").await?;
+ },
+ };
+
+ Ok(())
+}
+
+/// Get a random pony image based on a set of tags
+///
+/// Usage:
+/// ~tpony <tags>
+/// Example:
+/// ~tpony twilight sparkle,fluttershy
+#[poise::command(slash_command, prefix_command)]
+pub async fn tpony(ctx: Context<'_>,
+ #[description = "List of tags"]
+ #[rest]
+ tags: String,
+ ) -> Result<(), Error> {
+ let mut response_msg = ctx.say(format!("Fetching pony image based on tags: {:?}", tags.clone())).await?;
+
+ let res = reqwest::get(format!("https://theponyapi.com/api/v1/pony/random?q={}", tags).as_str())
+ .await?
+ .json::<PonyResponse>()
+ .await?;
+
+ match response_msg.unwrap().message().await {
+ Ok(mut msg) => {
+ msg.edit(&ctx.discord(), |m| {
+ m.content("");
+ m.embed(|e| {
+ e.title("Pony!");
+ e.image(res.pony.representations.full.clone());
+ let res_tags = get_tags_as_string(res.pony.tags);
+ e.field("Tags", format!("{:?}", res_tags), true);
+ e.field("Image URL", res.pony.representations.full.clone(), true);
+ if let Some(url) = res.pony.sourceURL {
+ e.field("Source", url, true);
+ } else {
+ e.field("Source", "None found", true);
+ }
+ e
+ });
+ m
+ }).await?;
+ },
+ Err(e) => {
+ ctx.say("Error editing message").await?;
+ },
+ };
+
+
+ Ok(())
+}
+
+fn get_tags_as_string(tags: Vec<String>) -> String {
+ let mut response = String::from(tags.get(0).unwrap());
+ for (i, s) in tags.iter().enumerate() {
+ if i > 0 {
+ response.push_str(format!(", {}", s).as_str());
+ } else {
+ continue;
+ }
+ }
+
+ response
+}
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..bb686d2
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,103 @@
+use std::time::Duration;
+
+use dotenv::dotenv;
+
+type Error = Box<dyn std::error::Error + Send + Sync>;
+type Context<'a> = poise::Context<'a, Data, Error>;
+
+mod commands;
+
+pub struct Data {
+
+}
+
+/// Show help menu
+#[poise::command(prefix_command, slash_command)]
+async fn help(ctx: Context<'_>,
+ #[description = "Command to get help for"] command: Option<String>,
+ ) -> Result<(), Error> {
+ poise::builtins::help(ctx, command.as_deref(), poise::builtins::HelpConfiguration::default()).await?;
+ Ok(())
+}
+
+async fn on_error(error: poise::FrameworkError<'_, Data, Error>) {
+ match error {
+ poise::FrameworkError::Setup { error } => panic!("Failed to start bot: {:?}", error),
+ poise::FrameworkError::Command {error, ctx} => {
+ println!("Error in command {}: {:?}", ctx.command().name, error);
+ },
+ error => {
+ if let Err(e) = poise::builtins::on_error(error).await {
+ println!("Error handling error: {}", e);
+ }
+ }
+ }
+}
+
+/// Register application commands in this guild or globally
+///
+/// Run with no arguments to register in guild, run with argument "global" to register globally.
+#[poise::command(prefix_command, hide_in_help)]
+async fn register(ctx: Context<'_>, #[flag] global: bool) -> Result<(), Error> {
+ poise::builtins::register_application_commands(ctx, global).await?;
+
+ Ok(())
+}
+
+#[tokio::main]
+async fn main() {
+ dotenv().unwrap();
+ let options = poise::FrameworkOptions {
+ commands: vec![
+ help(),
+ register(),
+ commands::meta::ping(),
+ commands::meta::about(),
+ commands::meta::userinfo(),
+
+ commands::actions::boop(),
+ commands::actions::hug(),
+
+ commands::pony::randpony(),
+ commands::pony::tpony(),
+
+ commands::osu::osup(),
+ commands::osu::osubm(),
+ ],
+ on_error: |error| Box::pin(on_error(error)),
+ pre_command: |ctx| {
+ Box::pin(async move {
+ println!("Executing command {}...", ctx.command().name);
+ })
+ },
+ post_command: |ctx| {
+ Box::pin(async move {
+ println!("Done executing command {}!", ctx.command().name);
+ })
+ },
+
+ prefix_options: poise::PrefixFrameworkOptions {
+ prefix: Some("~".into()),
+ edit_tracker: Some(poise::EditTracker::for_timespan(Duration::from_secs(3600))),
+ additional_prefixes: vec![
+ poise::Prefix::Literal("hey glitch"),
+ poise::Prefix::Literal("hey glitch,"),
+ ],
+ ..Default::default()
+ },
+ ..Default::default()
+ };
+
+ poise::Framework::build()
+ .token(std::env::var("DISCORD_TOKEN").unwrap_or("BAD-TOKEN".into()))
+ .user_data_setup(move |_ctx, _ready, _framework| {
+ Box::pin(async move {
+ Ok(Data {})
+ })
+ })
+ .options(options)
+ .run()
+ .await
+ .unwrap();
+}
+