Improve submission command with slash command, embeds, crossposting

late-submissions
Elnu 2 years ago
parent ebcb6e2ff7
commit fc9a9a9d49

@ -29,34 +29,42 @@ pub async fn challenge(ctx: Context<'_>) -> Result<(), Error> {
#[command( #[command(
prefix_command, prefix_command,
slash_command,
ephemeral,
broadcast_typing, broadcast_typing,
description_localized("en-US", "Submit to the latest handwriting challenge.") description_localized("en-US", "Submit to the latest handwriting challenge.")
)] )]
pub async fn submit(ctx: PrefixContext<'_>) -> Result<(), Error> { pub async fn submit(
ctx: Context<'_>,
submission: serenity::Attachment
) -> Result<(), Error> {
// TODO: The code for this command needs to be refactored, // TODO: The code for this command needs to be refactored,
// there are large duplicated sections that need to be merged somehow. // there are large duplicated sections that need to be merged somehow.
match ctx {
Context::Application(_) => { ctx.defer_ephemeral().await? },
Context::Prefix(ctx) => if ctx.msg.attachments.len() == 0 {
ctx.msg
.reply(&ctx.discord.http, "Please attach at least one image.")
.await?;
return Ok(());
},
}
let guild_data = get_guild_data(); let guild_data = get_guild_data();
let guild = ctx.msg.guild_id.unwrap().as_u64().to_string(); let guild = ctx.guild_id().unwrap().as_u64().to_string();
if !guild_data.contains_key(&guild) if !guild_data.contains_key(&guild)
|| !&guild_data[&guild] || !&guild_data[&guild]
.as_object() .as_object()
.unwrap() .unwrap()
.contains_key("submissionChannel") .contains_key("submissionChannel")
{ {
ctx.msg ctx.say("Submissions aren't enabled for this server yet.").await?;
.reply(
&ctx.discord.http,
"Submissions aren't enabled for this server yet.",
)
.await?;
return Ok(()); return Ok(());
} }
let current_guild_data = &guild_data[&guild].as_object().unwrap(); let current_guild_data = &guild_data[&guild].as_object().unwrap();
let submission_channel = current_guild_data["submissionChannel"].as_str().unwrap(); let submission_channel = current_guild_data["submissionChannel"].as_str().unwrap();
if submission_channel != ctx.msg.channel_id.as_u64().to_string() { if submission_channel != ctx.channel_id().as_u64().to_string() {
ctx.msg ctx.defer_ephemeral().await?;
.reply( ctx.say(
&ctx.discord.http,
format!( format!(
"Sorry, submissions aren't permitted here. Please go to <#{}>. Thanks!", "Sorry, submissions aren't permitted here. Please go to <#{}>. Thanks!",
guild guild
@ -65,12 +73,7 @@ pub async fn submit(ctx: PrefixContext<'_>) -> Result<(), Error> {
.await?; .await?;
return Ok(()); return Ok(());
} }
if ctx.msg.attachments.len() == 0 {
ctx.msg
.reply(&ctx.discord.http, "Please attach at least one image.")
.await?;
return Ok(());
}
let challenge_number = get_challenge_number(); let challenge_number = get_challenge_number();
let submission_images_dir = get_submission_images_dir(); let submission_images_dir = get_submission_images_dir();
@ -82,11 +85,17 @@ pub async fn submit(ctx: PrefixContext<'_>) -> Result<(), Error> {
let mut existing_submitter = false; let mut existing_submitter = false;
let mut invalid_types = false; let mut invalid_types = false;
let mut requires_rebuild = false; let mut requires_rebuild = false;
let attachments = vec![submission];
let attachments = match ctx {
Context::Application(_) => &attachments,
Context::Prefix(ctx) => &ctx.msg.attachments,
};
let author = ctx.author();
for (i, submission) in submission_data.iter_mut().enumerate() { for (i, submission) in submission_data.iter_mut().enumerate() {
if is_matching_submission(&submission, &ctx.msg.author) { if is_matching_submission(&submission, author) {
existing_submitter = true; existing_submitter = true;
let mut images = submission["images"].as_array_mut().unwrap().clone(); let mut images = submission["images"].as_array_mut().unwrap().clone();
for attachment in ctx.msg.attachments.iter() { for attachment in attachments.iter() {
let extension; let extension;
if let Some(content_type) = &attachment.content_type { if let Some(content_type) = &attachment.content_type {
if content_type == "image/png" { if content_type == "image/png" {
@ -105,8 +114,8 @@ pub async fn submit(ctx: PrefixContext<'_>) -> Result<(), Error> {
let file_name = format!( let file_name = format!(
"{}-{}-{}-{}.{}", "{}-{}-{}-{}.{}",
i + 1, i + 1,
slugify(&ctx.msg.author.name), slugify(&author.name),
ctx.msg.author.discriminator, author.discriminator,
images.len() + 1, images.len() + 1,
extension extension
); );
@ -125,10 +134,10 @@ pub async fn submit(ctx: PrefixContext<'_>) -> Result<(), Error> {
let mut submitter_data = Map::new(); let mut submitter_data = Map::new();
submitter_data.insert( submitter_data.insert(
String::from("username"), String::from("username"),
format!("{}#{}", ctx.msg.author.name, ctx.msg.author.discriminator).into(), format!("{}#{}", author.name, author.discriminator).into(),
); );
let mut images: Vec<String> = Vec::new(); let mut images: Vec<String> = Vec::new();
for attachment in ctx.msg.attachments.iter() { for attachment in attachments.iter() {
let extension; let extension;
if let Some(content_type) = &attachment.content_type { if let Some(content_type) = &attachment.content_type {
if content_type == "image/png" { if content_type == "image/png" {
@ -147,8 +156,8 @@ pub async fn submit(ctx: PrefixContext<'_>) -> Result<(), Error> {
let file_name = format!( let file_name = format!(
"{}-{}-{}{}.{}", "{}-{}-{}{}.{}",
submission_data.len() + 1, submission_data.len() + 1,
slugify(&ctx.msg.author.name), slugify(&author.name),
ctx.msg.author.discriminator, author.discriminator,
if images.len() == 0 { if images.len() == 0 {
String::from("") String::from("")
} else { } else {
@ -165,23 +174,95 @@ pub async fn submit(ctx: PrefixContext<'_>) -> Result<(), Error> {
submitter_data.insert(String::from("images"), images.into()); submitter_data.insert(String::from("images"), images.into());
submitter_data.insert( submitter_data.insert(
String::from("id"), String::from("id"),
ctx.msg.author.id.as_u64().to_string().into(), author.id.as_u64().to_string().into(),
); );
submission_data.push(submitter_data.into()); submission_data.push(submitter_data.into());
} }
set_submission_data(submission_data); set_submission_data(submission_data);
let mut message = String::new(); let mut message = String::new();
if requires_rebuild { if requires_rebuild {
message.push_str(&format!("Thank you for submitting! You can view your submission at <https://tegakituesday.com/{}>", challenge_number)); let thank_you = &format!("Thank you for submitting! You can view your submission at <https://tegakituesday.com/{}>", challenge_number);
let mut repost_here = true;
match ctx {
Context::Application(_) => message.push_str(thank_you),
Context::Prefix(ctx) => match ctx.msg.delete(&ctx.discord.http).await {
Ok(_) => {},
// don't repost image on this server if no manage messages perm
// (if we can't delete user's image it'll be displayed twice)
Err(_) => {
repost_here = false;
message.push_str(thank_you);
}
},
};
if invalid_types { if invalid_types {
message.push_str("\nSome of your attachments could not be uploaded; only **.png**, **.jpg**, and **.jpeg** files are permitted."); message.push_str("\nSome of your attachments could not be uploaded; only **.png**, **.jpg**, and **.jpeg** files are permitted.");
} }
leaderboard(&ctx).await?; leaderboard(&ctx).await?;
rebuild_site(); rebuild_site();
for attachment in attachments.iter() {
for (guild, data) in guild_data.iter() {
let here = guild.eq(&ctx.guild_id().unwrap().as_u64().to_string());
if !repost_here && here {
continue;
}
let data = data.as_object().unwrap();
if !data.contains_key("submissionChannel") {
continue;
}
let channel = serenity::ChannelId(
data["submissionChannel"]
.as_str()
.unwrap()
.parse::<u64>()
.unwrap(),
);
let invite = if data.contains_key("invite") {
Some(data["invite"].as_str().unwrap())
} else { None };
let accent_color = ctx
.discord()
.http
.get_user(*author.id.as_u64())
.await
.unwrap()
.accent_colour;
channel.send_message(&ctx.discord().http, |m| {
m.embed(|e| {
let username = format!("{}#{}", author.name, author.discriminator);
let n = get_challenge_number();
let mut description = format!("New submission to [Tegaki Tuesday #{n}](https://tegakituesday.com/{n})!");
if !here {
let guild = ctx.guild().unwrap();
description.push_str(&if let Some(invite) = invite {
format!("\nCrossposted from [{}](https://discord.gg/{invite})", guild.name)
} else {
format!("\nCrossposted from {}", guild.name)
});
};
e.description(description);
let mut embed_author = serenity::builder::CreateEmbedAuthor::default();
embed_author
.icon_url(get_avatar(&author))
.name(username)
.url(format!("https://discord.com/users/{}", author.id));
e.set_author(embed_author);
e.image(&attachment.url);
if let Some(accent_color) = accent_color {
e.color(accent_color);
}
e
});
m
}).await?;
};
};
} else if invalid_types { } else if invalid_types {
message.push_str("Sorry, your submission could not be uploaded; only **.png**, **.jpg**, and **.jpeg** files are permitted."); message.push_str("Sorry, your submission could not be uploaded; only **.png**, **.jpg**, and **.jpeg** files are permitted.");
} }
ctx.msg.reply(&ctx.discord.http, message).await?; if !message.is_empty() {
ctx.say(message).await?;
}
Ok(()) Ok(())
} }

@ -1,6 +1,6 @@
use crate::serenity; use crate::serenity;
use crate::Error; use crate::Error;
use crate::{Context, PrefixContext}; use crate::Context;
use rand::seq::IteratorRandom; use rand::seq::IteratorRandom;
use serde_json::Map; use serde_json::Map;
use serde_json::Value; use serde_json::Value;
@ -154,7 +154,7 @@ pub fn set_guild_data(guild_data: Map<String, Value>) {
pub async fn send(ctx: Context<'_>, message: &str, ping: bool, pin: bool) -> Result<(), Error> { pub async fn send(ctx: Context<'_>, message: &str, ping: bool, pin: bool) -> Result<(), Error> {
let guild_data = get_guild_data(); let guild_data = get_guild_data();
let mut announcements_count = 0; // let mut announcements_count = 0;
for (_guild, data) in guild_data.iter() { for (_guild, data) in guild_data.iter() {
let data = data.as_object().unwrap(); let data = data.as_object().unwrap();
if !data.contains_key("submissionChannel") { if !data.contains_key("submissionChannel") {
@ -191,14 +191,16 @@ pub async fn send(ctx: Context<'_>, message: &str, ping: bool, pin: bool) -> Res
Err(_) => (), Err(_) => (),
}; };
} }
announcements_count += 1; // announcements_count += 1;
} }
/*
ctx.say(format!( ctx.say(format!(
"Announced to {} server{}!", "Announced to {} server{}!",
announcements_count, announcements_count,
if announcements_count == 1 { "" } else { "s" } if announcements_count == 1 { "" } else { "s" }
)) ))
.await?; .await?;
*/
Ok(()) Ok(())
} }
@ -351,7 +353,7 @@ pub fn get_avatar(user: &serenity::User) -> String {
} }
} }
pub async fn leaderboard(ctx: &PrefixContext<'_>) -> Result<(), Error> { pub async fn leaderboard(ctx: &Context<'_>) -> Result<(), Error> {
const LENGTH: usize = 10; const LENGTH: usize = 10;
let mut submission_counts: HashMap<String, u32> = HashMap::new(); let mut submission_counts: HashMap<String, u32> = HashMap::new();
for challenge in 1..get_challenge_number() + 1 { for challenge in 1..get_challenge_number() + 1 {
@ -371,7 +373,7 @@ pub async fn leaderboard(ctx: &PrefixContext<'_>) -> Result<(), Error> {
for (i, (id, count)) in top_submitters[0..LENGTH].iter().enumerate() { for (i, (id, count)) in top_submitters[0..LENGTH].iter().enumerate() {
let place = i + 1; let place = i + 1;
let user = serenity::UserId(id.parse::<u64>().unwrap()) let user = serenity::UserId(id.parse::<u64>().unwrap())
.to_user(&ctx.discord.http) .to_user(&ctx.discord().http)
.await?; .await?;
let avatar = get_avatar(&user); let avatar = get_avatar(&user);
let profile = format!("https://discord.com/users/{id}"); let profile = format!("https://discord.com/users/{id}");

Loading…
Cancel
Save