Skip to content

Commit

Permalink
Create "View on Godbolt" button (#200)
Browse files Browse the repository at this point in the history
* Implement View on Godbolt feature

* Bump version
  • Loading branch information
Headline authored Mar 6, 2023
1 parent 2c877e6 commit d2d4809
Show file tree
Hide file tree
Showing 9 changed files with 163 additions and 70 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "discord-compiler-bot"
description = "Discord bot to compile your spaghetti code."
version = "3.5.3"
version = "3.5.4"
authors = ["Michael Flaherty (Headline#9999)"]
edition = "2021"
build = "src/build.rs"
Expand Down
32 changes: 28 additions & 4 deletions src/commands/asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ use crate::utls::constants::*;
use crate::utls::discordhelpers;
use crate::utls::discordhelpers::embeds;

use crate::managers::compilation::CompilationDetails;
use serenity::builder::CreateEmbed;
use serenity::model::application::component::ButtonStyle;
use serenity::model::channel::{Message, ReactionType};
use serenity::model::user::User;

Expand All @@ -19,8 +21,30 @@ use crate::utls::parser;
#[command]
#[bucket = "nospam"]
pub async fn asm(ctx: &Context, msg: &Message, _args: Args) -> CommandResult {
let emb = handle_request(ctx.clone(), msg.content.clone(), msg.author.clone(), msg).await?;
let asm_embed = embeds::dispatch_embed(&ctx.http, msg.channel_id, emb).await?;
let (embed, compilation_details) =
handle_request(ctx.clone(), msg.content.clone(), msg.author.clone(), msg).await?;

// Send our final embed
let mut new_msg = embeds::embed_message(embed);
if let Some(b64) = compilation_details.base64 {
new_msg.components(|cmp| {
cmp.create_action_row(|row| {
row.create_button(|btn| {
btn.style(ButtonStyle::Link)
.url(format!("https://godbolt.org/clientstate/{}", b64))
.label("View on godbolt.org")
})
})
});
}

let asm_embed = msg
.channel_id
.send_message(&ctx.http, |e| {
*e = new_msg.clone();
e
})
.await?;

// Success/fail react
let compilation_successful = asm_embed.embeds[0].colour.unwrap().0 == COLOR_OKAY;
Expand All @@ -41,7 +65,7 @@ pub async fn handle_request(
mut content: String,
author: User,
msg: &Message,
) -> Result<CreateEmbed, CommandError> {
) -> Result<(CreateEmbed, CompilationDetails), CommandError> {
let data_read = ctx.data.read().await;
let loading_reaction = {
let botinfo_lock = data_read.get::<ConfigCache>().unwrap();
Expand Down Expand Up @@ -102,5 +126,5 @@ pub async fn handle_request(
// remove our loading emote
discordhelpers::delete_bot_reacts(&ctx, msg, loading_reaction).await?;

Ok(response.1)
Ok((response.1, response.0))
}
45 changes: 31 additions & 14 deletions src/commands/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,32 +12,50 @@ use tokio::sync::RwLockReadGuard;
use serenity::builder::CreateEmbed;
use serenity::client::Context;
use serenity::framework::standard::CommandError;
use serenity::model::application::component::ButtonStyle;
use serenity::model::channel::{Message, ReactionType};
use serenity::model::user::User;

use crate::cache::{CompilerCache, ConfigCache, StatsManagerCache};
use crate::managers::compilation::CompilationManager;
use crate::managers::compilation::{CompilationDetails, CompilationManager};

#[command]
#[bucket = "nospam"]
pub async fn compile(ctx: &Context, msg: &Message, _args: Args) -> CommandResult {
let data_read = ctx.data.read().await;

// Handle wandbox request logic
let embed = handle_request(ctx.clone(), msg.content.clone(), msg.author.clone(), msg).await?;
let (embed, compilation_details) =
handle_request(ctx.clone(), msg.content.clone(), msg.author.clone(), msg).await?;

// Send our final embed
let compilation_embed = embeds::dispatch_embed(&ctx.http, msg.channel_id, embed).await?;
let mut new_msg = embeds::embed_message(embed);
if let Some(b64) = compilation_details.base64 {
new_msg.components(|cmp| {
cmp.create_action_row(|row| {
row.create_button(|btn| {
btn.style(ButtonStyle::Link)
.url(format!("https://godbolt.org/clientstate/{}", b64))
.label("View on godbolt.org")
})
})
});
}

let sent = msg
.channel_id
.send_message(&ctx.http, |e| {
*e = new_msg.clone();
e
})
.await?;

// Success/fail react
let compilation_successful = compilation_embed.embeds[0].colour.unwrap().0 == COLOR_OKAY;
discordhelpers::send_completion_react(ctx, &compilation_embed, compilation_successful).await?;
let compilation_successful = sent.embeds[0].colour.unwrap().0 == COLOR_OKAY;
discordhelpers::send_completion_react(ctx, &sent, compilation_successful).await?;

let mut delete_cache = data_read.get::<MessageCache>().unwrap().lock().await;
delete_cache.insert(
msg.id.0,
MessageCacheEntry::new(compilation_embed, msg.clone()),
);
delete_cache.insert(msg.id.0, MessageCacheEntry::new(sent, msg.clone()));
debug!("Command executed");
Ok(())
}
Expand All @@ -47,7 +65,7 @@ pub async fn handle_request(
mut content: String,
author: User,
msg: &Message,
) -> Result<CreateEmbed, CommandError> {
) -> Result<(CreateEmbed, CompilationDetails), CommandError> {
let data_read = ctx.data.read().await;
let loading_reaction = {
let botinfo_lock = data_read.get::<ConfigCache>().unwrap();
Expand All @@ -66,7 +84,6 @@ pub async fn handle_request(
// Try to load in an attachment
let (code, ext) = parser::get_message_attachment(&msg.attachments).await?;
if !code.is_empty() {
// content.push_str(&format!("\n```{}\n{}\n```\n", ext, code));
writeln!(&mut content, "\n```{}\n{}\n```\n", ext, code).unwrap();
}

Expand Down Expand Up @@ -94,10 +111,10 @@ pub async fn handle_request(
// dispatch our req
let compilation_manager_lock: RwLockReadGuard<CompilationManager> =
compilation_manager.read().await;
let awd = compilation_manager_lock
let compilation_result = compilation_manager_lock
.compile(&parse_result, &author)
.await;
let result = match awd {
let result = match compilation_result {
Ok(r) => r,
Err(e) => {
// we failed, lets remove the loading react so it doesn't seem like we're still processing
Expand Down Expand Up @@ -139,5 +156,5 @@ pub async fn handle_request(
}
}

Ok(result.1)
Ok((result.1, result.0))
}
11 changes: 6 additions & 5 deletions src/commands/cpp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use serenity::{
prelude::*,
};

use crate::managers::compilation::CompilationDetails;
use crate::utls::discordhelpers::embeds::EmbedOptions;
use crate::{
cache::{CompilerCache, ConfigCache, MessageCache, MessageCacheEntry},
Expand All @@ -19,7 +20,8 @@ use crate::{
#[aliases("c++")]
#[bucket = "nospam"]
pub async fn cpp(ctx: &Context, msg: &Message, _args: Args) -> CommandResult {
let emb = handle_request(ctx.clone(), msg.content.clone(), msg.author.clone(), msg).await?;
let (emb, _) =
handle_request(ctx.clone(), msg.content.clone(), msg.author.clone(), msg).await?;

// Dispatch our request
let compilation_embed = embeds::dispatch_embed(&ctx.http, msg.channel_id, emb).await?;
Expand All @@ -40,7 +42,7 @@ pub async fn handle_request(
content: String,
author: User,
msg: &Message,
) -> std::result::Result<CreateEmbed, CommandError> {
) -> std::result::Result<(CreateEmbed, CompilationDetails), CommandError> {
let loading_reaction = {
let data_read = ctx.data.read().await;
let botinfo_lock = data_read.get::<ConfigCache>().unwrap();
Expand Down Expand Up @@ -98,7 +100,6 @@ pub async fn handle_request(

// remove our loading emote
discordhelpers::delete_bot_reacts(&ctx, msg, loading_reaction).await?;
let options = EmbedOptions::new(false, fake_parse.target.clone(), String::default());

Ok(result.1.to_embed(&author, &options))
let options = EmbedOptions::new(false, result.0.clone());
Ok((result.1.to_embed(&author, &options), result.0))
}
29 changes: 25 additions & 4 deletions src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use std::env;
use tokio::sync::MutexGuard;

use chrono::{DateTime, Utc};
use serenity::model::application::component::ButtonStyle;

use crate::{
cache::*,
Expand Down Expand Up @@ -198,15 +199,15 @@ impl EventHandler for Handler {
let _ = new_message.delete_reactions(&ctx.http).await;
if collector.is_some() {
let prefix = env::var("BOT_PREFIX").expect("Bot prefix is not set!");
let emb = match handle_request(
let (emb, details) = match handle_request(
ctx.clone(),
format!("{}compile\n```{}\n{}\n```", prefix, language, code),
new_message.author.clone(),
&new_message,
)
.await
{
Ok(emb) => emb,
Ok((emb, details)) => (emb, details),
Err(e) => {
let emb = embeds::build_fail_embed(
&new_message.author,
Expand All @@ -227,8 +228,28 @@ impl EventHandler for Handler {
return;
}
};
let _ =
embeds::dispatch_embed(&ctx.http, new_message.channel_id, emb).await;

// Send our final embed
let mut new_msg = embeds::embed_message(emb);
if let Some(b64) = details.base64 {
new_msg.components(|cmp| {
cmp.create_action_row(|row| {
row.create_button(|btn| {
btn.style(ButtonStyle::Link)
.url(format!("https://godbolt.org/clientstate/{}", b64))
.label("View on godbolt.org")
})
})
});
}

let _ = new_message
.channel_id
.send_message(&ctx.http, |e| {
*e = new_msg.clone();
e
})
.await;
}
}
}
Expand Down
40 changes: 25 additions & 15 deletions src/managers/compilation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ use crate::utls::discordhelpers::embeds::{EmbedOptions, ToEmbed};
use crate::utls::parser::ParserResult;

// struct containing any information resolved during the compilation step
#[derive(Default)]
#[derive(Default, Clone)]
pub struct CompilationDetails {
pub language: String,
pub compiler: String,
pub base64: Option<String>,
}

//Traits for compiler lookup
Expand Down Expand Up @@ -82,15 +83,13 @@ impl CompilationManager {
RequestHandler::CompilerExplorer => {
let result = self.compiler_explorer(parser_result).await?;

let options =
EmbedOptions::new(false, result.0.language.clone(), result.0.compiler.clone());
let options = EmbedOptions::new(false, result.0.clone());
Ok((result.0, result.1.to_embed(author, &options)))
}
RequestHandler::WandBox => {
let result = self.wandbox(parser_result).await?;

let options =
EmbedOptions::new(false, result.0.language.clone(), result.0.compiler.clone());
let options = EmbedOptions::new(false, result.0.clone());
Ok((result.0, result.1.to_embed(author, &options)))
}
RequestHandler::None => {
Expand All @@ -110,7 +109,7 @@ impl CompilationManager {
&self,
parse_result: &ParserResult,
author: &User,
) -> Result<(String, CreateEmbed), CommandError> {
) -> Result<(CompilationDetails, CreateEmbed), CommandError> {
let gbolt = match &self.gbolt {
Some(gbolt) => gbolt,
None => {
Expand Down Expand Up @@ -151,9 +150,17 @@ impl CompilationManager {
Err(CommandError::from(format!("Target '{}' either does not produce assembly or is not currently supported on godbolt.org", target)))
}
Some(compiler) => {
let response = Godbolt::send_request(&compiler, &parse_result.code, options, USER_AGENT).await?;
let options = EmbedOptions::new(true, target.to_string(), compiler.name);
Ok((compiler.lang, response.to_embed(author, &options)))
let response = Godbolt::send_request(&compiler, &parse_result.code, options.clone(), USER_AGENT).await?;
let base64 = Godbolt::get_base64(&compiler, &parse_result.code, options)?;

let details = CompilationDetails {
language: target.to_string(),
compiler: compiler.name,
base64: Some(base64)
};

let options = EmbedOptions::new(true, details.clone());
Ok((details, response.to_embed(author, &options)))
}
}
}
Expand Down Expand Up @@ -201,12 +208,6 @@ impl CompilationManager {
};
let compiler = gbolt.resolve(target).unwrap();

// report discovered information
let details = CompilationDetails {
compiler: compiler.name.clone(),
language: compiler.lang.clone(),
};

// add boilerplate code if needed & fix common mistakes
let mut code = parse_result.code.clone();
{
Expand All @@ -217,7 +218,16 @@ impl CompilationManager {

code = fix_common_problems(&compiler.lang, code);
}
let base64 = Godbolt::get_base64(&compiler, &code, options.clone())?;
let response = Godbolt::send_request(&compiler, &code, options, USER_AGENT).await?;

// report discovered information
let details = CompilationDetails {
compiler: compiler.name.clone(),
language: compiler.lang.clone(),
base64: Some(base64),
};

Ok((details, response))
}

Expand Down
2 changes: 1 addition & 1 deletion src/slashcmds/cpp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ pub async fn cpp(ctx: &Context, msg: &ApplicationCommandInteraction) -> CommandR
let data_read = ctx.data.read().await;
let compiler_lock = data_read.get::<CompilerCache>().unwrap().read().await;
let result = compiler_lock.compiler_explorer(&fake_parse).await?;
let options = EmbedOptions::new(false, fake_parse.target.clone(), String::default());
let options = EmbedOptions::new(false, result.0);

msg.edit_original_interaction_response(&ctx.http, |resp| {
resp.add_embed(result.1.to_embed(&msg.user, &options))
Expand Down
Loading

0 comments on commit d2d4809

Please sign in to comment.