Update to Poise v0.6

This commit is contained in:
Aadi Desai 2024-01-24 04:05:34 +00:00
parent 54a93953bf
commit a44f0ede7e
Signed by: supleed2
SSH key fingerprint: SHA256:CkbNRs0yVzXEiUp2zd0PSxsfRUMFF9bLlKXtE1xEbKM
15 changed files with 862 additions and 789 deletions

533
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -11,21 +11,21 @@ publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
anyhow = "1.0.75" anyhow = "1.0.79"
axum = "0.7.2" axum = "0.7.4"
indoc = "2.0.4" indoc = "2.0.4"
poise = "0.5.7" poise = "0.6.1"
reqwest = { version = "0.11.22", features = ["json"] } reqwest = { version = "0.11.23", features = ["json"] }
serde = { version = "1.0.193", features = ["derive"] } serde = { version = "1.0.195", features = ["derive"] }
shuttle-runtime = { version = "0.35.0", default-features = false } shuttle-runtime = { version = "0.36.0", default-features = false }
shuttle-secrets = "0.35.0" shuttle-secrets = "0.36.0"
shuttle-shared-db = { version = "0.35.0", features = ["postgres"] } shuttle-shared-db = { version = "0.36.0", features = ["postgres"] }
sqlx = { version = "0.7.3", features = [ sqlx = { version = "0.7.3", features = [
"macros", "macros",
"postgres", "postgres",
"runtime-tokio-native-tls", "runtime-tokio-native-tls",
] } ] }
tokio = "1.34.0" tokio = "1.35.1"
tracing = "0.1.40" tracing = "0.1.40"
tracing-subscriber = { version = "0.3.18", features = ["time"] } tracing-subscriber = { version = "0.3.18", features = ["time"] }
url = "2.5.0" url = "2.5.0"

View file

@ -97,7 +97,7 @@ pub(crate) async fn refresh_non_members(ctx: ACtx<'_>) -> Result<(), Error> {
ctx.defer().await?; ctx.defer().await?;
let mut members = ctx.data().server.members_iter(ctx.http()).boxed(); let mut members = ctx.data().server.members_iter(ctx.http()).boxed();
let mut cnt = 0; let mut cnt = 0;
while let Some(Ok(mut m)) = members.next().await { while let Some(Ok(m)) = members.next().await {
if m.roles.is_empty() { if m.roles.is_empty() {
m.add_role(ctx.http(), ctx.data().non_member).await?; m.add_role(ctx.http(), ctx.data().non_member).await?;
cnt += 1; cnt += 1;
@ -122,7 +122,7 @@ pub(crate) async fn set_members_non_fresher(ctx: ACtx<'_>) -> Result<(), Error>
ctx.say(format!("{updated} updated to non-fresher, removing roles")) ctx.say(format!("{updated} updated to non-fresher, removing roles"))
.await?; .await?;
let mut members = ctx.data().server.members_iter(ctx.http()).boxed(); let mut members = ctx.data().server.members_iter(ctx.http()).boxed();
while let Some(Ok(mut m)) = members.next().await { while let Some(Ok(m)) = members.next().await {
let _ = m.remove_role(ctx.http(), ctx.data().fresher).await; let _ = m.remove_role(ctx.http(), ctx.data().fresher).await;
} }
ctx.say("Roles removed").await?; ctx.say("Roles removed").await?;

View file

@ -1,6 +1,8 @@
use crate::{db, ACtx, Error, Gaijin}; use crate::{db, ACtx, Error, Gaijin};
use poise::serenity_prelude as serenity; use poise::{
use poise::Modal; serenity_prelude::{self as serenity, CreateAttachment, CreateMessage},
Modal,
};
use std::fmt::Write; use std::fmt::Write;
/// Get the number of entries in the gaijin table /// Get the number of entries in the gaijin table
@ -57,9 +59,11 @@ pub(crate) async fn get_all_gaijin(ctx: ACtx<'_>) -> Result<(), Error> {
ctx.say("Sending gaijin db data in followup message") ctx.say("Sending gaijin db data in followup message")
.await?; .await?;
ctx.channel_id() ctx.channel_id()
.send_files(&ctx.http(), vec!["gaijin.rs"], |cm| { .send_files(
cm.content("File: gaijin db") &ctx.http(),
}) vec![CreateAttachment::path("gaijin.rs").await?],
CreateMessage::new().content("File: gaijin db"),
)
.await?; .await?;
} }
Err(e) => { Err(e) => {

View file

@ -1,5 +1,5 @@
use crate::{db, ACtx, Error, ManualMember}; use crate::{db, ACtx, Error, ManualMember};
use poise::serenity_prelude as serenity; use poise::serenity_prelude::{self as serenity, CreateAttachment, CreateMessage};
use poise::Modal; use poise::Modal;
/// Get the number of manual members in the manual table /// Get the number of manual members in the manual table
@ -49,9 +49,11 @@ pub(crate) async fn get_all_manual(ctx: ACtx<'_>) -> Result<(), Error> {
ctx.say("Sending manual db data in followup message") ctx.say("Sending manual db data in followup message")
.await?; .await?;
ctx.channel_id() ctx.channel_id()
.send_files(&ctx.http(), vec!["manual.rs"], |cm| { .send_files(
cm.content("File: manual db") &ctx.http(),
}) vec![CreateAttachment::path("manual.rs").await?],
CreateMessage::new().content("File: manual db"),
)
.await?; .await?;
} }
Err(e) => { Err(e) => {

View file

@ -1,5 +1,5 @@
use crate::{db, ACtx, Error, Member}; use crate::{db, ACtx, Error, Member};
use poise::serenity_prelude as serenity; use poise::serenity_prelude::{self as serenity, CreateAttachment, CreateMessage};
use poise::Modal; use poise::Modal;
/// Get the number of members in the members table /// Get the number of members in the members table
@ -57,9 +57,11 @@ pub(crate) async fn get_all_members(ctx: ACtx<'_>) -> Result<(), Error> {
ctx.say("Sending members db data in followup message") ctx.say("Sending members db data in followup message")
.await?; .await?;
ctx.channel_id() ctx.channel_id()
.send_files(&ctx.http(), vec!["members.rs"], |cm| { .send_files(
cm.content("File: members db") &ctx.http(),
}) vec![CreateAttachment::path("members.rs").await?],
CreateMessage::new().content("File: members db"),
)
.await?; .await?;
} }
Err(e) => { Err(e) => {

View file

@ -1,5 +1,5 @@
use crate::{ACtx, Data, Error}; use crate::{ACtx, Data, Error};
use poise::serenity_prelude as serenity; use poise::serenity_prelude::{self as serenity, CreateActionRow, CreateButton, CreateMessage};
use poise::Modal; use poise::Modal;
pub(crate) mod members; pub(crate) mod members;
@ -67,24 +67,21 @@ pub(crate) async fn setup(
.await?; .await?;
let emoji = emoji.unwrap_or_default().chars().next().unwrap_or('🚀'); let emoji = emoji.unwrap_or_default().chars().next().unwrap_or('🚀');
channel channel
.send_message(ctx.http(), |m| { .send_message(
m.content(message).components(|c| { ctx.http(),
c.create_action_row(|a| { CreateMessage::new()
a.create_button(|b| { .content(message)
b.style(serenity::ButtonStyle::Secondary) .components(vec![CreateActionRow::Buttons(vec![
.emoji('📖') CreateButton::new("info")
.label("More info") .style(serenity::ButtonStyle::Secondary)
.custom_id("info") .emoji('📖')
}) .label("More info"),
.create_button(|b| { CreateButton::new("start")
b.style(serenity::ButtonStyle::Primary) .style(serenity::ButtonStyle::Primary)
.emoji(emoji) .emoji(emoji)
.label(text.unwrap_or("Begin".to_string())) .label(text.unwrap_or("Begin".to_string())),
.custom_id("start") ])]),
}) )
})
})
})
.await?; .await?;
} else { } else {
ctx.say("Modal timed out, try again...").await?; ctx.say("Modal timed out, try again...").await?;

View file

@ -1,5 +1,5 @@
use crate::{db, ACtx, Error, PendingMember}; use crate::{db, ACtx, Error, PendingMember};
use poise::serenity_prelude as serenity; use poise::serenity_prelude::{self as serenity, CreateAttachment, CreateMessage};
use poise::Modal; use poise::Modal;
/// Get the number of pending members in the pending table /// Get the number of pending members in the pending table
@ -49,9 +49,11 @@ pub(crate) async fn get_all_pending(ctx: ACtx<'_>) -> Result<(), Error> {
ctx.say("Sending pending db data in followup message") ctx.say("Sending pending db data in followup message")
.await?; .await?;
ctx.channel_id() ctx.channel_id()
.send_files(&ctx.http(), vec!["pending.rs"], |cm| { .send_files(
cm.content("File: pending db") &ctx.http(),
}) vec![CreateAttachment::path("pending.rs").await?],
CreateMessage::new().content("File: pending db"),
)
.await?; .await?;
} }
Err(e) => { Err(e) => {

View file

@ -1,5 +1,5 @@
use crate::{db, ACtx, Error}; use crate::{db, ACtx, Error};
use poise::serenity_prelude as serenity; use poise::{serenity_prelude as serenity, CreateReply};
use std::fmt::Write; use std::fmt::Write;
/// Unreachable, used to create whois command folder /// Unreachable, used to create whois command folder
@ -19,14 +19,19 @@ pub(crate) async fn whois_by_id(ctx: ACtx<'_>, id: serenity::Member) -> Result<(
tracing::info!("{} {}", ctx.author().name, id.user.name); tracing::info!("{} {}", ctx.author().name, id.user.name);
match db::get_member_by_id(&ctx.data().db, id.user.id.into()).await? { match db::get_member_by_id(&ctx.data().db, id.user.id.into()).await? {
Some(m) => { Some(m) => {
ctx.send(|c| c.content(format!("{id}: {}", m.nickname)).ephemeral(true)) ctx.send(
.await? CreateReply::default()
.content(format!("{id}: {}", m.nickname))
.ephemeral(true),
)
.await?
} }
None => { None => {
ctx.send(|c| { ctx.send(
c.content(format!("No member entry found for {id}")) CreateReply::default()
.ephemeral(true) .content(format!("No member entry found for {id}"))
}) .ephemeral(true),
)
.await? .await?
} }
}; };
@ -39,29 +44,29 @@ pub(crate) async fn whois_by_id(ctx: ACtx<'_>, id: serenity::Member) -> Result<(
pub(crate) async fn whois_by_nickname(ctx: ACtx<'_>, nickname: String) -> Result<(), Error> { pub(crate) async fn whois_by_nickname(ctx: ACtx<'_>, nickname: String) -> Result<(), Error> {
tracing::info!("{} {nickname}", ctx.author().name); tracing::info!("{} {nickname}", ctx.author().name);
if let Some(m) = db::get_member_by_nickname(&ctx.data().db, &nickname).await? { if let Some(m) = db::get_member_by_nickname(&ctx.data().db, &nickname).await? {
ctx.send(|c| { ctx.send(
c.content(format!("{nickname}: <@{}>", m.discord_id)) CreateReply::default()
.ephemeral(true) .content(format!("{nickname}: <@{}>", m.discord_id))
}) .ephemeral(true),
)
.await?; .await?;
} else { } else {
let members = db::get_member_by_nickname_fuzzy(&ctx.data().db, &nickname, 3).await?; let members = db::get_member_by_nickname_fuzzy(&ctx.data().db, &nickname, 3).await?;
if members.is_empty() { if members.is_empty() {
ctx.send(|c| { ctx.send(
c.content(format!("No member entry found for nickname {nickname}")) CreateReply::default()
.ephemeral(true) .content(format!("No member entry found for nickname {nickname}"))
}) .ephemeral(true),
)
.await?; .await?;
} else { } else {
ctx.send(|c| { ctx.send(CreateReply::default().ephemeral(true).content(format!(
c.ephemeral(true).content(format!( "Possible matches for {nickname}: {}",
"Possible matches for {nickname}: {}", members.iter().fold(String::new(), |mut s, g| {
members.iter().fold(String::new(), |mut s, g| { write!(s, " <@{}>", g.discord_id).expect("String write! is infallible");
write!(s, " <@{}>", g.discord_id).expect("String write! is infallible"); s
s })
}) )))
))
})
.await?; .await?;
} }
} }
@ -74,29 +79,29 @@ pub(crate) async fn whois_by_nickname(ctx: ACtx<'_>, nickname: String) -> Result
pub(crate) async fn whois_by_realname(ctx: ACtx<'_>, realname: String) -> Result<(), Error> { pub(crate) async fn whois_by_realname(ctx: ACtx<'_>, realname: String) -> Result<(), Error> {
tracing::info!("{} {realname}", ctx.author().name); tracing::info!("{} {realname}", ctx.author().name);
if let Some(m) = db::get_member_by_realname(&ctx.data().db, &realname).await? { if let Some(m) = db::get_member_by_realname(&ctx.data().db, &realname).await? {
ctx.send(|c| { ctx.send(
c.content(format!("{realname}: <@{}>", m.discord_id)) CreateReply::default()
.ephemeral(true) .content(format!("{realname}: <@{}>", m.discord_id))
}) .ephemeral(true),
)
.await?; .await?;
} else { } else {
let members = db::get_member_by_realname_fuzzy(&ctx.data().db, &realname, 3).await?; let members = db::get_member_by_realname_fuzzy(&ctx.data().db, &realname, 3).await?;
if members.is_empty() { if members.is_empty() {
ctx.send(|c| { ctx.send(
c.content(format!("No member entry found for realname {realname}")) CreateReply::default()
.ephemeral(true) .content(format!("No member entry found for realname {realname}"))
}) .ephemeral(true),
)
.await?; .await?;
} else { } else {
ctx.send(|c| { ctx.send(CreateReply::default().ephemeral(true).content(format!(
c.ephemeral(true).content(format!( "Possible matches for {realname}: {}",
"Possible matches for {realname}: {}", members.iter().fold(String::new(), |mut s, g| {
members.iter().fold(String::new(), |mut s, g| { write!(s, " <@{}>", g.discord_id).expect("String write! is infallible");
write!(s, " <@{}>", g.discord_id).expect("String write! is infallible"); s
s })
}) )))
))
})
.await?; .await?;
} }
}; };

View file

@ -1,6 +1,6 @@
#![warn(clippy::pedantic)] #![warn(clippy::pedantic)]
use anyhow::Context as _; use anyhow::Context as _;
use poise::serenity_prelude as serenity; use poise::serenity_prelude::{self as serenity, FullEvent};
mod cmds; mod cmds;
mod db; mod db;
@ -59,14 +59,24 @@ struct Gaijin {
university: String, university: String,
} }
macro_rules! secret {
($s: literal, $ss: ident) => {
$ss.get($s).context(format!("{} not found", $s))?
};
($s: literal, $ss: ident, $t: ty) => {
secret!($s, $ss)
.parse::<$t>()
.context(format!("{} not valid {}", $s, stringify!($t)))?
};
}
#[shuttle_runtime::main] #[shuttle_runtime::main]
async fn poise( async fn nanobot(
#[shuttle_secrets::Secrets] secret_store: shuttle_secrets::SecretStore, #[shuttle_secrets::Secrets] secret_store: shuttle_secrets::SecretStore,
#[shuttle_shared_db::Postgres] pool: sqlx::PgPool, #[shuttle_shared_db::Postgres] pool: sqlx::PgPool,
) -> Result<service::NanoBot, shuttle_runtime::Error> { ) -> Result<service::NanoBot, shuttle_runtime::Error> {
// Set Up Tracing Subscriber // Set Up Tracing Subscriber
init_tracing_subscriber(); init_tracing_subscriber();
tracing::info!("Tracing Subscriber Set Up");
// Run SQLx Migrations // Run SQLx Migrations
sqlx::migrate!() sqlx::migrate!()
@ -74,58 +84,8 @@ async fn poise(
.await .await
.map_err(shuttle_runtime::CustomError::new)?; .map_err(shuttle_runtime::CustomError::new)?;
// Load secrets // Load token
let au_ch_id = secret_store let token = secret!("DISCORD_TOKEN", secret_store);
.get("AU_CHANNEL_ID")
.expect("AU_CHANNEL_ID not found")
.parse()
.expect("AU_CHANNEL_ID not valid u64");
let ea_key = secret_store
.get("EA_API_KEY")
.expect("EA_API_KEY not found");
let ea_url = secret_store
.get("EA_API_URL")
.expect("EA_API_URL not found");
let token = secret_store
.get("DISCORD_TOKEN")
.expect("DISCORD_TOKEN not found");
let fresher = secret_store
.get("FRESHER_ID")
.expect("FRESHER_ID not found")
.parse()
.expect("FRESHER_ID not valid u64");
let gaijin = secret_store
.get("GAIJIN_ID")
.expect("GAIJIN_ID not found")
.parse()
.expect("GAIJIN_ID not valid u64");
let gn_ch_id = secret_store
.get("GN_CHANNEL_ID")
.expect("GN_CHANNEL_ID not found")
.parse()
.expect("GN_CHANNEL_ID not valid u64");
let member = secret_store
.get("MEMBER_ID")
.expect("MEMBER_ID not found")
.parse()
.expect("MEMBER_ID not valid u64");
let non_member = secret_store
.get("NON_MEMBER_ID")
.expect("NON_MEMBER_ID not found")
.parse()
.expect("NON_MEMBER_ID not valid u64");
let old_member = secret_store
.get("OLD_MEMBER_ID")
.expect("OLD_MEMBER_ID not found")
.parse()
.expect("OLD_MEMBER_ID not valid u64");
let server = secret_store
.get("SERVER_ID")
.expect("SERVER_ID not found")
.parse::<u64>()
.expect("SERVER_ID not valid u64")
.into();
tracing::info!("Secrets loaded");
// Build Axum Router // Build Axum Router
let router = axum::Router::new() let router = axum::Router::new()
@ -142,33 +102,40 @@ async fn poise(
); );
// Build Poise Instance // Build Poise Instance
let discord = poise::Framework::builder() let framework = poise::Framework::builder()
.options(poise::FrameworkOptions { .options(poise::FrameworkOptions {
commands: all_commands(), commands: all_commands(),
event_handler: { |c, e, f, d| Box::pin(event_handler(c, e, f, d)) }, event_handler: { |c, e, f, d| Box::pin(event_handler(c, e, f, d)) },
..Default::default() ..Default::default()
}) })
.token(token)
.intents(serenity::GatewayIntents::non_privileged())
.setup(move |ctx, _, _| { .setup(move |ctx, _, _| {
Box::pin(async move { Box::pin(async move {
ctx.set_activity(serenity::Activity::competing("autoverification")) ctx.set_activity(Some(serenity::ActivityData::custom(
.await; "Verifying members since 2023",
)));
Ok(Data { Ok(Data {
au_ch_id, au_ch_id: secret!("AU_CHANNEL_ID", secret_store, _),
db: pool, db: pool,
ea_key, ea_key: secret!("EA_API_KEY", secret_store),
ea_url, ea_url: secret!("EA_API_URL", secret_store),
fresher, fresher: secret!("FRESHER_ID", secret_store, _),
gaijin, gaijin: secret!("GAIJIN_ID", secret_store, _),
gn_ch_id, gn_ch_id: secret!("GN_CHANNEL_ID", secret_store, _),
member, member: secret!("MEMBER_ID", secret_store, _),
non_member, non_member: secret!("NON_MEMBER_ID", secret_store, _),
old_member, old_member: secret!("OLD_MEMBER_ID", secret_store, _),
server, server: secret!("SERVER_ID", secret_store, _),
}) })
}) })
}); })
.build();
// Build Discord struct
let discord = service::Discord {
framework,
token,
intents: serenity::GatewayIntents::non_privileged(),
};
// Return NanoBot // Return NanoBot
Ok(service::NanoBot { discord, router }) Ok(service::NanoBot { discord, router })
@ -176,16 +143,16 @@ async fn poise(
async fn event_handler( async fn event_handler(
ctx: &serenity::Context, ctx: &serenity::Context,
event: &poise::Event<'_>, event: &FullEvent,
_framework: poise::FrameworkContext<'_, Data, Error>, _framework: poise::FrameworkContext<'_, Data, Error>,
data: &Data, data: &Data,
) -> Result<(), Error> { ) -> Result<(), Error> {
match event { match event {
poise::Event::GuildMemberAddition { new_member } => { FullEvent::GuildMemberAddition { new_member } => {
tracing::info!("Member joined: {}", new_member.user.name); tracing::info!("Member joined: {}", new_member.user.name);
} }
poise::Event::InteractionCreate { FullEvent::InteractionCreate {
interaction: serenity::Interaction::MessageComponent(m), interaction: serenity::Interaction::Component(m),
} => { } => {
tracing::info!("Interaction: {} by {}", m.data.custom_id, m.user.name); tracing::info!("Interaction: {} by {}", m.data.custom_id, m.user.name);
match m.data.custom_id.as_str() { match m.data.custom_id.as_str() {
@ -214,8 +181,8 @@ async fn event_handler(
} }
} }
} }
poise::Event::InteractionCreate { FullEvent::InteractionCreate {
interaction: serenity::Interaction::ModalSubmit(m), interaction: serenity::Interaction::Modal(m),
} => { } => {
tracing::info!("Modal submit: {} by {}", m.data.custom_id, m.user.name); tracing::info!("Modal submit: {} by {}", m.data.custom_id, m.user.name);
match m.data.custom_id.as_str() { match m.data.custom_id.as_str() {

View file

@ -1,10 +1,17 @@
use crate::{Data, Error}; use crate::{Data, Error};
use poise::serenity_prelude as serenity;
pub(crate) struct NanoBot { pub(crate) struct NanoBot {
pub discord: poise::FrameworkBuilder<Data, Error>, pub discord: Discord,
pub router: axum::Router, pub router: axum::Router,
} }
pub(crate) struct Discord {
pub framework: poise::Framework<Data, Error>,
pub token: String,
pub intents: serenity::GatewayIntents,
}
#[shuttle_runtime::async_trait] #[shuttle_runtime::async_trait]
impl shuttle_runtime::Service for NanoBot { impl shuttle_runtime::Service for NanoBot {
async fn bind(mut self, addr: std::net::SocketAddr) -> Result<(), shuttle_runtime::Error> { async fn bind(mut self, addr: std::net::SocketAddr) -> Result<(), shuttle_runtime::Error> {
@ -18,8 +25,13 @@ impl shuttle_runtime::Service for NanoBot {
) )
.into_future(); .into_future();
let mut client = serenity::ClientBuilder::new(self.discord.token, self.discord.intents)
.framework(self.discord.framework)
.await
.map_err(shuttle_runtime::CustomError::new)?;
tokio::select! { tokio::select! {
_ = self.discord.run_autosharded() => {}, _ = client.start_autosharded() => {},
_ = serve => {}, _ = serve => {},
}; };

View file

@ -1,5 +1,8 @@
use crate::{Data, Error}; use crate::{Data, Error};
use poise::serenity_prelude as serenity; use poise::serenity_prelude::{
self as serenity, CreateActionRow, CreateButton, CreateEmbed, CreateInteractionResponse,
CreateInteractionResponseMessage, CreateMessage,
};
use poise::Modal; use poise::Modal;
const LOGIN_INTRO: &str = indoc::indoc! {" const LOGIN_INTRO: &str = indoc::indoc! {"
@ -14,34 +17,28 @@ const LOGIN_INTRO: &str = indoc::indoc! {"
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
pub(crate) async fn login_1( pub(crate) async fn login_1(
ctx: &serenity::Context, ctx: &serenity::Context,
m: &serenity::MessageComponentInteraction, m: &serenity::ComponentInteraction,
) -> Result<(), Error> { ) -> Result<(), Error> {
m.create_interaction_response(&ctx.http, |i| { let verify_url = format!("https://icas.8bitsqu.id/verify?id={}", m.user.id.get());
i.kind(serenity::InteractionResponseType::UpdateMessage) m.create_response(
.interaction_response_data(|d| { &ctx.http,
d.content(LOGIN_INTRO).components(|c| { CreateInteractionResponse::UpdateMessage(
c.create_action_row(|a| { CreateInteractionResponseMessage::new()
a.create_button(|b| { .content(LOGIN_INTRO)
b.style(serenity::ButtonStyle::Danger) .components(vec![CreateActionRow::Buttons(vec![
.emoji('🔙') CreateButton::new("restart")
.custom_id("restart") .style(serenity::ButtonStyle::Danger)
}) .emoji('🔙'),
.create_button(|b| { CreateButton::new_link(verify_url)
b.style(serenity::ButtonStyle::Link) .emoji('🚀')
.emoji('🚀') .label("Login Here"),
.label("Login Here") CreateButton::new("login_2")
.url(format!("https://icas.8bitsqu.id/verify?id={}", m.user.id.0)) .style(serenity::ButtonStyle::Secondary)
}) .emoji('👉')
.create_button(|b| { .label("Then continue"),
b.style(serenity::ButtonStyle::Secondary) ])]),
.emoji('👉') ),
.label("Then continue") )
.custom_id("login_2")
})
})
})
})
})
.await?; .await?;
Ok(()) Ok(())
} }
@ -55,52 +52,50 @@ const LOGIN_FORM: &str = indoc::indoc! {"
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
pub(crate) async fn login_2( pub(crate) async fn login_2(
ctx: &serenity::Context, ctx: &serenity::Context,
m: &serenity::MessageComponentInteraction, m: &serenity::ComponentInteraction,
data: &Data, data: &Data,
) -> Result<(), Error> { ) -> Result<(), Error> {
match crate::db::get_pending_by_id(&data.db, m.user.id.into()).await { match crate::db::get_pending_by_id(&data.db, m.user.id.into()).await {
Err(e) => { Err(e) => {
tracing::error!("{e}"); tracing::error!("{e}");
m.create_interaction_response(&ctx.http, |i| { m.create_response(
i.kind(serenity::InteractionResponseType::ChannelMessageWithSource) &ctx.http,
.interaction_response_data(|d| { CreateInteractionResponse::Message(
d.content("Sorry, something went wrong. Please try again") CreateInteractionResponseMessage::new()
.ephemeral(true) .content("Sorry, something went wrong. Please try again")
}) .ephemeral(true),
}) ),
)
.await?; .await?;
} }
Ok(None) => { Ok(None) => {
m.create_interaction_response(&ctx.http, |i| { m.create_response(
i.kind(serenity::InteractionResponseType::ChannelMessageWithSource) &ctx.http,
.interaction_response_data(|d| { CreateInteractionResponse::Message(
d.content("Error, have you completed login verification via the link?") CreateInteractionResponseMessage::new()
.ephemeral(true) .content("Error, have you completed login verification via the link?")
}) .ephemeral(true),
}) ),
)
.await?; .await?;
} }
Ok(Some(_)) => { Ok(Some(_)) => {
m.create_interaction_response(&ctx.http, |i| { m.create_response(
i.kind(serenity::InteractionResponseType::UpdateMessage) &ctx.http,
.interaction_response_data(|d| { CreateInteractionResponse::UpdateMessage(
d.content(LOGIN_FORM).components(|c| { CreateInteractionResponseMessage::new()
c.create_action_row(|a| { .content(LOGIN_FORM)
a.create_button(|b| { .components(vec![CreateActionRow::Buttons(vec![
b.style(serenity::ButtonStyle::Danger) CreateButton::new("login_1")
.emoji('🔙') .style(serenity::ButtonStyle::Danger)
.custom_id("login_1") .emoji('🔙'),
}) CreateButton::new("login_3")
.create_button(|b| { .style(serenity::ButtonStyle::Primary)
b.style(serenity::ButtonStyle::Primary) .emoji('📑')
.emoji('📑') .label("Form"),
.label("Form") ])]),
.custom_id("login_3") ),
}) )
})
})
})
})
.await?; .await?;
} }
}; };
@ -110,34 +105,28 @@ pub(crate) async fn login_2(
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
pub(crate) async fn login_3( pub(crate) async fn login_3(
ctx: &serenity::Context, ctx: &serenity::Context,
m: &serenity::MessageComponentInteraction, m: &serenity::ComponentInteraction,
) -> Result<(), Error> { ) -> Result<(), Error> {
m.create_interaction_response(&ctx.http, |i| { m.create_response(
i.kind(serenity::InteractionResponseType::UpdateMessage) &ctx.http,
.interaction_response_data(|d| { CreateInteractionResponse::UpdateMessage(
d.content("Are you a fresher?").components(|c| { CreateInteractionResponseMessage::new()
c.create_action_row(|a| { .content("Are you a fresher?")
a.create_button(|b| { .components(vec![CreateActionRow::Buttons(vec![
b.style(serenity::ButtonStyle::Danger) CreateButton::new("login_2")
.emoji('🔙') .style(serenity::ButtonStyle::Danger)
.custom_id("login_2") .emoji('🔙'),
}) CreateButton::new("login_4f")
.create_button(|b| { .style(serenity::ButtonStyle::Success)
b.style(serenity::ButtonStyle::Success) .emoji('✅')
.emoji('✅') .label("Fresher"),
.label("Fresher") CreateButton::new("login_4n")
.custom_id("login_4f") .style(serenity::ButtonStyle::Primary)
}) .emoji('❌')
.create_button(|b| { .label("Non-fresher"),
b.style(serenity::ButtonStyle::Primary) ])]),
.emoji('❌') ),
.label("Non-fresher") )
.custom_id("login_4n")
})
})
})
})
})
.await?; .await?;
Ok(()) Ok(())
} }
@ -145,30 +134,25 @@ pub(crate) async fn login_3(
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
pub(crate) async fn login_4( pub(crate) async fn login_4(
ctx: &serenity::Context, ctx: &serenity::Context,
m: &serenity::MessageComponentInteraction, m: &serenity::ComponentInteraction,
fresher: bool, fresher: bool,
) -> Result<(), Error> { ) -> Result<(), Error> {
m.create_interaction_response(&ctx.http, |i| { m.create_response(
i.kind(serenity::InteractionResponseType::UpdateMessage) &ctx.http,
.interaction_response_data(|d| { CreateInteractionResponse::UpdateMessage(
d.content("And a preferred name for Nano whois commands") CreateInteractionResponseMessage::new()
.components(|c| { .content("And a preferred name for Nano whois commands")
c.create_action_row(|a| { .components(vec![CreateActionRow::Buttons(vec![
a.create_button(|b| { CreateButton::new("login_3")
b.style(serenity::ButtonStyle::Danger) .style(serenity::ButtonStyle::Danger)
.emoji('🔙') .emoji('🔙'),
.custom_id("login_3") CreateButton::new(if fresher { "login_5f" } else { "login_5n" })
}) .style(serenity::ButtonStyle::Primary)
.create_button(|b| { .emoji('💬')
b.style(serenity::ButtonStyle::Primary) .label("Name"),
.emoji('💬') ])]),
.label("Name") ),
.custom_id(if fresher { "login_5f" } else { "login_5n" }) )
})
})
})
})
})
.await?; .await?;
Ok(()) Ok(())
} }
@ -184,20 +168,20 @@ struct Nickname {
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
pub(crate) async fn login_5( pub(crate) async fn login_5(
ctx: &serenity::Context, ctx: &serenity::Context,
m: &serenity::MessageComponentInteraction, m: &serenity::ComponentInteraction,
fresher: bool, fresher: bool,
) -> Result<(), Error> { ) -> Result<(), Error> {
m.create_interaction_response(&ctx.http, |i| { m.create_response(
*i = Nickname::create( &ctx.http,
Nickname::create(
None, None,
if fresher { if fresher {
"login_6f".to_string() "login_6f".to_string()
} else { } else {
"login_6n".to_string() "login_6n".to_string()
}, },
); ),
i )
})
.await?; .await?;
Ok(()) Ok(())
} }
@ -205,7 +189,7 @@ pub(crate) async fn login_5(
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
pub(crate) async fn login_6( pub(crate) async fn login_6(
ctx: &serenity::Context, ctx: &serenity::Context,
m: &serenity::ModalSubmitInteraction, m: &serenity::ModalInteraction,
data: &Data, data: &Data,
fresher: bool, fresher: bool,
) -> Result<(), Error> { ) -> Result<(), Error> {
@ -234,34 +218,38 @@ pub(crate) async fn login_6(
if fresher { if fresher {
crate::verify::apply_role(ctx, &mut mm, data.fresher).await?; crate::verify::apply_role(ctx, &mut mm, data.fresher).await?;
} }
m.create_interaction_response(&ctx.http, |i| { let msg = if fresher {
i.kind(serenity::InteractionResponseType::UpdateMessage) "Congratulations, you have completed verification and now \
.interaction_response_data(|d| { have access to the ICAS Discord and freshers thread"
d.content(if fresher { } else {
"Congratulations, you have completed verification and now \ "Congratulations, you have completed verification and now \
have access to the ICAS Discord and freshers thread" have access to the ICAS Discord"
} else { };
"Congratulations, you have completed verification and now \ m.create_response(
have access to the ICAS Discord" &ctx.http,
}) CreateInteractionResponse::UpdateMessage(
.components(|c| c) CreateInteractionResponseMessage::new()
}) .content(msg)
}) .components(vec![]),
),
)
.await?; .await?;
data.au_ch_id data.au_ch_id
.send_message(&ctx.http, |cm| { .send_message(
cm.add_embed(|e| { &ctx.http,
e.thumbnail( CreateMessage::new().embed(
m.user.avatar_url().unwrap_or(super::AVATAR.to_string()), CreateEmbed::new()
) .thumbnail(
.title("Member verified via login") m.user.avatar_url().unwrap_or(super::AVATAR.to_string()),
.description(&m.user) )
.field("Fresher", fresher, true) .title("Member verified via login")
.field("Nickname", nickname, true) .description(m.user.to_string())
.field("Name", p.realname, true) .field("Fresher", fresher.to_string(), true)
.timestamp(serenity::Timestamp::now()) .field("Nickname", nickname, true)
}) .field("Name", p.realname, true)
}) .timestamp(serenity::Timestamp::now()),
),
)
.await?; .await?;
let _ = mm.remove_role(&ctx.http, data.non_member).await; let _ = mm.remove_role(&ctx.http, data.non_member).await;
if mm.roles.contains(&data.old_member) { if mm.roles.contains(&data.old_member) {
@ -273,13 +261,14 @@ pub(crate) async fn login_6(
} }
Err(e) => { Err(e) => {
tracing::error!("Error: {e}"); tracing::error!("Error: {e}");
m.create_interaction_response(&ctx.http, |i| { m.create_response(
i.kind(serenity::InteractionResponseType::ChannelMessageWithSource) &ctx.http,
.interaction_response_data(|d| { CreateInteractionResponse::Message(
d.content("Sorry, something went wrong. Please try again") CreateInteractionResponseMessage::new()
.ephemeral(true) .content("Sorry, something went wrong. Please try again")
}) .ephemeral(true),
}) ),
)
.await?; .await?;
} }
} }

View file

@ -1,5 +1,8 @@
use crate::{Data, Error}; use crate::{Data, Error};
use poise::serenity_prelude as serenity; use poise::serenity_prelude::{
self as serenity, CreateActionRow, CreateButton, CreateEmbed, CreateInteractionResponse,
CreateInteractionResponseMessage, CreateMessage,
};
use poise::Modal; use poise::Modal;
const MANUAL_INTRO: &str = indoc::indoc! {" const MANUAL_INTRO: &str = indoc::indoc! {"
@ -19,34 +22,28 @@ const MANUAL_INTRO: &str = indoc::indoc! {"
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
pub(crate) async fn manual_1( pub(crate) async fn manual_1(
ctx: &serenity::Context, ctx: &serenity::Context,
m: &serenity::MessageComponentInteraction, m: &serenity::ComponentInteraction,
) -> Result<(), Error> { ) -> Result<(), Error> {
m.create_interaction_response(&ctx.http, |i| { m.create_response(
i.kind(serenity::InteractionResponseType::UpdateMessage) &ctx.http,
.interaction_response_data(|d| { CreateInteractionResponse::UpdateMessage(
d.content(MANUAL_INTRO).components(|c| { CreateInteractionResponseMessage::new()
c.create_action_row(|a| { .content(MANUAL_INTRO)
a.create_button(|b| { .components(vec![CreateActionRow::Buttons(vec![
b.style(serenity::ButtonStyle::Danger) CreateButton::new("restart")
.emoji('🔙') .style(serenity::ButtonStyle::Danger)
.custom_id("restart") .emoji('🔙'),
}) CreateButton::new("manual_2f")
.create_button(|b| { .style(serenity::ButtonStyle::Success)
b.style(serenity::ButtonStyle::Success) .emoji('✅')
.emoji('✅') .label("Fresher"),
.label("Fresher") CreateButton::new("manual_2n")
.custom_id("manual_2f") .style(serenity::ButtonStyle::Primary)
}) .emoji('❌')
.create_button(|b| { .label("Non-fresher"),
b.style(serenity::ButtonStyle::Primary) ])]),
.emoji('❌') ),
.label("Non-fresher") )
.custom_id("manual_2n")
})
})
})
})
})
.await?; .await?;
Ok(()) Ok(())
} }
@ -72,24 +69,24 @@ struct Manual {
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
pub(crate) async fn manual_2( pub(crate) async fn manual_2(
ctx: &serenity::Context, ctx: &serenity::Context,
m: &serenity::MessageComponentInteraction, m: &serenity::ComponentInteraction,
data: &Data, data: &Data,
fresher: bool, fresher: bool,
) -> Result<(), Error> { ) -> Result<(), Error> {
// Delete from manual if exists // Delete from manual if exists
let _ = crate::db::delete_manual_by_id(&data.db, m.user.id.into()).await; let _ = crate::db::delete_manual_by_id(&data.db, m.user.id.into()).await;
m.create_interaction_response(&ctx.http, |i| { m.create_response(
*i = Manual::create( &ctx.http,
Manual::create(
None, None,
if fresher { if fresher {
"manual_3f".to_string() "manual_3f".to_string()
} else { } else {
"manual_3n".to_string() "manual_3n".to_string()
}, },
); ),
i )
})
.await?; .await?;
Ok(()) Ok(())
} }
@ -97,7 +94,7 @@ pub(crate) async fn manual_2(
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
pub(crate) async fn manual_3( pub(crate) async fn manual_3(
ctx: &serenity::Context, ctx: &serenity::Context,
m: &serenity::ModalSubmitInteraction, m: &serenity::ModalInteraction,
data: &Data, data: &Data,
fresher: bool, fresher: bool,
) -> Result<(), Error> { ) -> Result<(), Error> {
@ -109,13 +106,14 @@ pub(crate) async fn manual_3(
nickname, nickname,
}) => { }) => {
if ::url::Url::parse(&url).is_err() { if ::url::Url::parse(&url).is_err() {
m.create_interaction_response(&ctx.http, |i| { m.create_response(
i.kind(serenity::InteractionResponseType::ChannelMessageWithSource) &ctx.http,
.interaction_response_data(|d| { CreateInteractionResponse::Message(
d.content("The url provided is invalid, please try again") CreateInteractionResponseMessage::new()
.ephemeral(true) .content("The url provided is invalid, please try again")
}) .ephemeral(true),
}) ),
)
.await?; .await?;
return Ok(()); return Ok(());
} }
@ -125,36 +123,33 @@ pub(crate) async fn manual_3(
let prompt_sent = data let prompt_sent = data
.au_ch_id .au_ch_id
.send_message(&ctx.http, |cm| { .send_message(
cm.add_embed(|e| { &ctx.http,
e.title("New verification request from") CreateMessage::new()
.thumbnail(m.user.avatar_url().unwrap_or(super::AVATAR.to_string())) .embed(
.description(&m.user) CreateEmbed::new()
.field("Real Name (To be checked)", &realname, true) .title("New verification request from")
.field("Imperial Shortcode (To be checked", &shortcode, true) .thumbnail(m.user.avatar_url().unwrap_or(super::AVATAR.to_string()))
.field("Fresher (To be checked)", fresher, true) .description(m.user.to_string())
.field("Nickname (Nano whois commands)", &nickname, true) .field("Real Name (To be checked)", &realname, true)
.field("Verification URL (Also displayed below)", &url, true) .field("Imperial Shortcode (To be checked", &shortcode, true)
.image(&url) .field("Fresher (To be checked)", fresher.to_string(), true)
.timestamp(serenity::Timestamp::now()) .field("Nickname (Nano whois commands)", &nickname, true)
}) .field("Verification URL (Also displayed below)", &url, true)
.components(|c| { .image(&url)
c.create_action_row(|a| { .timestamp(serenity::Timestamp::now()),
a.create_button(|b| { )
b.style(serenity::ButtonStyle::Success) .components(vec![CreateActionRow::Buttons(vec![
.emoji('✅') CreateButton::new(format!("verify-y-{}", m.user.id))
.label("Accept") .style(serenity::ButtonStyle::Success)
.custom_id(format!("verify-y-{}", m.user.id)) .emoji('✅')
}) .label("Accept"),
.create_button(|b| { CreateButton::new(format!("verify-n-{}", m.user.id))
b.style(serenity::ButtonStyle::Danger) .style(serenity::ButtonStyle::Danger)
.emoji('❎') .emoji('❎')
.label("Deny") .label("Deny"),
.custom_id(format!("verify-n-{}", m.user.id)) ])]),
}) )
})
})
})
.await .await
.is_ok(); .is_ok();
@ -181,22 +176,27 @@ pub(crate) async fn manual_3(
"Sending your verification request failed, please try again." "Sending your verification request failed, please try again."
}; };
m.create_interaction_response(&ctx.http, |i| { m.create_response(
i.kind(serenity::InteractionResponseType::UpdateMessage) &ctx.http,
.interaction_response_data(|d| d.content(msg).components(|c| c)) CreateInteractionResponse::UpdateMessage(
}) CreateInteractionResponseMessage::new()
.content(msg)
.components(vec![]),
),
)
.await?; .await?;
return Ok(()); return Ok(());
} }
Err(e) => tracing::error!("{e}"), Err(e) => tracing::error!("{e}"),
}; };
m.create_interaction_response(&ctx.http, |i| { m.create_response(
i.kind(serenity::InteractionResponseType::ChannelMessageWithSource) &ctx.http,
.interaction_response_data(|d| { CreateInteractionResponse::Message(
d.content("Sorry, something went wrong. Please try again") CreateInteractionResponseMessage::new()
.ephemeral(true) .content("Sorry, something went wrong. Please try again")
}) .ephemeral(true),
}) ),
)
.await?; .await?;
Ok(()) Ok(())
} }
@ -204,7 +204,7 @@ pub(crate) async fn manual_3(
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
pub(crate) async fn manual_4( pub(crate) async fn manual_4(
ctx: &serenity::Context, ctx: &serenity::Context,
m: &serenity::MessageComponentInteraction, m: &serenity::ComponentInteraction,
data: &Data, data: &Data,
id: &str, id: &str,
) -> Result<(), Error> { ) -> Result<(), Error> {
@ -214,7 +214,7 @@ pub(crate) async fn manual_4(
.skip(9) .skip(9)
.collect::<String>() .collect::<String>()
.parse::<u64>() .parse::<u64>()
.map(serenity::UserId) .map(serenity::UserId::new)
.unwrap_or_default() .unwrap_or_default()
.to_user(ctx) .to_user(ctx)
.await .await
@ -239,20 +239,25 @@ pub(crate) async fn manual_4(
if fresher { if fresher {
crate::verify::apply_role(ctx, &mut member, data.fresher).await?; crate::verify::apply_role(ctx, &mut member, data.fresher).await?;
} }
m.create_interaction_response(&ctx.http, |i| { m.create_response(
i.kind(serenity::InteractionResponseType::UpdateMessage) &ctx.http,
.interaction_response_data(|d| { CreateInteractionResponse::UpdateMessage(
d.components(|c| c).embed(|e| { CreateInteractionResponseMessage::new()
e.thumbnail(user.avatar_url().unwrap_or(super::AVATAR.to_string())) .components(vec![])
.embed(
CreateEmbed::new()
.thumbnail(
user.avatar_url().unwrap_or(super::AVATAR.to_string()),
)
.title("Member verified via manual") .title("Member verified via manual")
.description(&user) .description(user.to_string())
.field("Fresher", fresher, true) .field("Fresher", fresher.to_string(), true)
.field("Nickname", mm.nickname, true) .field("Nickname", mm.nickname, true)
.field("Name", mm.realname, true) .field("Name", mm.realname, true)
.timestamp(serenity::Timestamp::now()) .timestamp(serenity::Timestamp::now()),
}) ),
}) ),
}) )
.await?; .await?;
let _ = member.remove_role(&ctx.http, data.non_member).await; let _ = member.remove_role(&ctx.http, data.non_member).await;
if member.roles.contains(&data.old_member) { if member.roles.contains(&data.old_member) {
@ -263,29 +268,33 @@ pub(crate) async fn manual_4(
} }
Err(e) => { Err(e) => {
tracing::error!("{e}"); tracing::error!("{e}");
m.create_interaction_response(&ctx.http, |i| { m.create_response(
i.kind(serenity::InteractionResponseType::ChannelMessageWithSource) &ctx.http,
.interaction_response_data(|d| { CreateInteractionResponse::Message(
d.content(format!("Failed to add user {user} to member database")) CreateInteractionResponseMessage::new()
}) .content(format!("Failed to add user {user} to member database")),
}) ),
)
.await?; .await?;
} }
} }
} else { } else {
crate::db::delete_manual_by_id(&data.db, user.id.into()).await?; crate::db::delete_manual_by_id(&data.db, user.id.into()).await?;
tracing::info!("{} ({}) denied via manual", user.name, user.id); tracing::info!("{} ({}) denied via manual", user.name, user.id);
m.create_interaction_response(&ctx.http, |i| { m.create_response(
i.kind(serenity::InteractionResponseType::UpdateMessage) &ctx.http,
.interaction_response_data(|d| { CreateInteractionResponse::UpdateMessage(
d.components(|c| c).embed(|e| { CreateInteractionResponseMessage::new()
e.title("Member denied via manual") .components(vec![])
.description(&user) .embed(
CreateEmbed::new()
.title("Member denied via manual")
.description(user.to_string())
.thumbnail(user.avatar_url().unwrap_or(super::AVATAR.to_string())) .thumbnail(user.avatar_url().unwrap_or(super::AVATAR.to_string()))
.timestamp(serenity::Timestamp::now()) .timestamp(serenity::Timestamp::now()),
}) ),
}) ),
}) )
.await?; .await?;
} }

View file

@ -1,5 +1,8 @@
use crate::{Data, Error}; use crate::{Data, Error};
use poise::serenity_prelude as serenity; use poise::serenity_prelude::{
self as serenity, CreateActionRow, CreateButton, CreateEmbed, CreateInteractionResponse,
CreateInteractionResponseMessage, CreateMessage,
};
use poise::Modal; use poise::Modal;
const MEMBERSHIP_INTRO: &str = indoc::indoc! {" const MEMBERSHIP_INTRO: &str = indoc::indoc! {"
@ -17,34 +20,28 @@ const MEMBERSHIP_INTRO: &str = indoc::indoc! {"
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
pub(crate) async fn membership_1( pub(crate) async fn membership_1(
ctx: &serenity::Context, ctx: &serenity::Context,
m: &serenity::MessageComponentInteraction, m: &serenity::ComponentInteraction,
) -> Result<(), Error> { ) -> Result<(), Error> {
m.create_interaction_response(&ctx.http, |i| { m.create_response(
i.kind(serenity::InteractionResponseType::UpdateMessage) &ctx.http,
.interaction_response_data(|d| { CreateInteractionResponse::UpdateMessage(
d.content(MEMBERSHIP_INTRO).components(|c| { CreateInteractionResponseMessage::new()
c.create_action_row(|a| { .content(MEMBERSHIP_INTRO)
a.create_button(|b| { .components(vec![CreateActionRow::Buttons(vec![
b.style(serenity::ButtonStyle::Danger) CreateButton::new("restart")
.emoji('🔙') .style(serenity::ButtonStyle::Danger)
.custom_id("restart") .emoji('🔙'),
}) CreateButton::new("membership_2f")
.create_button(|b| { .style(serenity::ButtonStyle::Success)
b.style(serenity::ButtonStyle::Success) .emoji('✅')
.emoji('✅') .label("Fresher"),
.label("Fresher") CreateButton::new("membership_2n")
.custom_id("membership_2f") .style(serenity::ButtonStyle::Primary)
}) .emoji('❌')
.create_button(|b| { .label("Non-fresher"),
b.style(serenity::ButtonStyle::Primary) ])]),
.emoji('❌') ),
.label("Non-fresher") )
.custom_id("membership_2n")
})
})
})
})
})
.await?; .await?;
Ok(()) Ok(())
} }
@ -66,7 +63,7 @@ struct Membership {
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
pub(crate) async fn membership_2( pub(crate) async fn membership_2(
ctx: &serenity::Context, ctx: &serenity::Context,
m: &serenity::MessageComponentInteraction, m: &serenity::ComponentInteraction,
data: &Data, data: &Data,
fresher: bool, fresher: bool,
) -> Result<(), Error> { ) -> Result<(), Error> {
@ -76,17 +73,17 @@ pub(crate) async fn membership_2(
// Delete from manual if exists // Delete from manual if exists
let _ = crate::db::delete_manual_by_id(&data.db, m.user.id.into()).await; let _ = crate::db::delete_manual_by_id(&data.db, m.user.id.into()).await;
m.create_interaction_response(&ctx.http, |i| { m.create_response(
*i = Membership::create( &ctx.http,
Membership::create(
None, None,
if fresher { if fresher {
"membership_3f".to_string() "membership_3f".to_string()
} else { } else {
"membership_3n".to_string() "membership_3n".to_string()
}, },
); ),
i )
})
.await?; .await?;
Ok(()) Ok(())
} }
@ -94,7 +91,7 @@ pub(crate) async fn membership_2(
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
pub(crate) async fn membership_3( pub(crate) async fn membership_3(
ctx: &serenity::Context, ctx: &serenity::Context,
m: &serenity::ModalSubmitInteraction, m: &serenity::ModalInteraction,
data: &Data, data: &Data,
fresher: bool, fresher: bool,
) -> Result<(), Error> { ) -> Result<(), Error> {
@ -108,13 +105,14 @@ pub(crate) async fn membership_3(
Ok(v) => v, Ok(v) => v,
Err(e) => { Err(e) => {
tracing::error!("{e}"); tracing::error!("{e}");
m.create_interaction_response(&ctx.http, |i| { m.create_response(
i.kind(serenity::InteractionResponseType::ChannelMessageWithSource) &ctx.http,
.interaction_response_data(|d| { CreateInteractionResponse::Message(
d.content("Sorry, getting membership data failed. Please try again") CreateInteractionResponseMessage::new()
.ephemeral(true) .content("Sorry, getting membership data failed. Please try again")
}) .ephemeral(true),
}) ),
)
.await?; .await?;
return Ok(()); return Ok(());
} }
@ -123,12 +121,16 @@ pub(crate) async fn membership_3(
((member.login.is_empty() && member.cid == shortcode) || member.login == shortcode) ((member.login.is_empty() && member.cid == shortcode) || member.login == shortcode)
&& member.order_no.to_string() == order && member.order_no.to_string() == order
}) else { }) else {
m.create_interaction_response(&ctx.http, |i| { let msg = "Sorry, your order was not found, please check the \
let msg = "Sorry, your order was not found, please check the \ order number and that it is for your current year's membership";
order number and that it is for your current year's membership"; m.create_response(
i.kind(serenity::InteractionResponseType::ChannelMessageWithSource) &ctx.http,
.interaction_response_data(|d| d.content(msg).ephemeral(true)) CreateInteractionResponse::Message(
}) CreateInteractionResponseMessage::new()
.content(msg)
.ephemeral(true),
),
)
.await?; .await?;
return Ok(()); return Ok(());
}; };
@ -157,32 +159,35 @@ pub(crate) async fn membership_3(
if fresher { if fresher {
crate::verify::apply_role(ctx, &mut mm, data.fresher).await?; crate::verify::apply_role(ctx, &mut mm, data.fresher).await?;
} }
m.create_interaction_response(&ctx.http, |i| { m.create_response(
i.kind(serenity::InteractionResponseType::UpdateMessage) &ctx.http,
.interaction_response_data(|d| { CreateInteractionResponse::UpdateMessage(
d.content(if fresher { CreateInteractionResponseMessage::new()
.content(if fresher {
"Congratulations, you have completed verification and now \ "Congratulations, you have completed verification and now \
have access to the ICAS Discord and freshers thread" have access to the ICAS Discord and freshers thread"
} else { } else {
"Congratulations, you have completed verification and now \ "Congratulations, you have completed verification and now \
have access to the ICAS Discord" have access to the ICAS Discord"
}) })
.components(|c| c) .components(vec![]),
}) ),
}) )
.await?; .await?;
data.au_ch_id data.au_ch_id
.send_message(&ctx.http, |cm| { .send_message(
cm.add_embed(|e| { &ctx.http,
e.thumbnail(m.user.avatar_url().unwrap_or(super::AVATAR.to_string())) CreateMessage::new().embed(
CreateEmbed::new()
.thumbnail(m.user.avatar_url().unwrap_or(super::AVATAR.to_string()))
.title("Member verified via membership") .title("Member verified via membership")
.description(&m.user) .description(m.user.to_string())
.field("Fresher", fresher, true) .field("Fresher", fresher.to_string(), true)
.field("Nickname", nickname, true) .field("Nickname", nickname, true)
.field("Name", realname, true) .field("Name", realname, true)
.timestamp(serenity::Timestamp::now()) .timestamp(serenity::Timestamp::now()),
}) ),
}) )
.await?; .await?;
let _ = mm.remove_role(&ctx.http, data.non_member).await; let _ = mm.remove_role(&ctx.http, data.non_member).await;
if mm.roles.contains(&data.old_member) { if mm.roles.contains(&data.old_member) {
@ -196,13 +201,14 @@ pub(crate) async fn membership_3(
} }
Err(e) => tracing::error!("{e}"), Err(e) => tracing::error!("{e}"),
}; };
m.create_interaction_response(&ctx.http, |i| { m.create_response(
i.kind(serenity::InteractionResponseType::ChannelMessageWithSource) &ctx.http,
.interaction_response_data(|d| { CreateInteractionResponse::Message(
d.content("Sorry, something went wrong. Please try again") CreateInteractionResponseMessage::new()
.ephemeral(true) .content("Sorry, something went wrong. Please try again")
}) .ephemeral(true),
}) ),
)
.await?; .await?;
Ok(()) Ok(())
} }

View file

@ -1,5 +1,8 @@
use crate::{Data, Error}; use crate::{Data, Error};
use poise::serenity_prelude as serenity; use poise::serenity_prelude::{
self as serenity, CacheHttp, CreateActionRow, CreateButton, CreateInteractionResponse,
CreateInteractionResponseMessage, CreateMessage,
};
pub(crate) mod login; pub(crate) mod login;
pub(crate) use login::*; pub(crate) use login::*;
@ -23,12 +26,16 @@ const INFO_MSG: &str = indoc::indoc! {"
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
pub(crate) async fn info( pub(crate) async fn info(
ctx: &serenity::Context, ctx: &serenity::Context,
m: &serenity::MessageComponentInteraction, m: &serenity::ComponentInteraction,
) -> Result<(), Error> { ) -> Result<(), Error> {
m.create_interaction_response(&ctx.http, |i| { m.create_response(
i.kind(serenity::InteractionResponseType::ChannelMessageWithSource) &ctx.http,
.interaction_response_data(|d| d.content(INFO_MSG).ephemeral(true)) CreateInteractionResponse::Message(
}) CreateInteractionResponseMessage::new()
.content(INFO_MSG)
.ephemeral(true),
),
)
.await?; .await?;
Ok(()) Ok(())
} }
@ -36,15 +43,19 @@ pub(crate) async fn info(
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
pub(crate) async fn unknown( pub(crate) async fn unknown(
ctx: &serenity::Context, ctx: &serenity::Context,
m: &serenity::MessageComponentInteraction, m: &serenity::ComponentInteraction,
) -> Result<(), Error> { ) -> Result<(), Error> {
m.create_interaction_response(&ctx.http, |i| { m.create_response(
i.kind(serenity::InteractionResponseType::ChannelMessageWithSource) &ctx.http,
.interaction_response_data(|d| { CreateInteractionResponse::Message(
d.content("Sorry, something went wrong. Please try again or message <@99217900254035968> for help") CreateInteractionResponseMessage::new()
.ephemeral(true) .content(
}) "Sorry, something went wrong. Please try again \
}) or message <@99217900254035968> for help",
)
.ephemeral(true),
),
)
.await?; .await?;
Ok(()) Ok(())
} }
@ -59,7 +70,7 @@ const START_MSG: &str = indoc::indoc! {"
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
pub(crate) async fn start( pub(crate) async fn start(
ctx: &serenity::Context, ctx: &serenity::Context,
m: &serenity::MessageComponentInteraction, m: &serenity::ComponentInteraction,
data: &Data, data: &Data,
init: bool, init: bool,
) -> Result<(), Error> { ) -> Result<(), Error> {
@ -70,46 +81,41 @@ pub(crate) async fn start(
if member.fresher { if member.fresher {
apply_role(ctx, &mut mm, data.fresher).await?; apply_role(ctx, &mut mm, data.fresher).await?;
} }
m.create_interaction_response(&ctx.http, |i| { m.create_response(
i.kind(serenity::InteractionResponseType::ChannelMessageWithSource) &ctx.http,
.interaction_response_data(|d| { CreateInteractionResponse::Message(
d.content("Welcome, you're already verified, re-applied your roles!") CreateInteractionResponseMessage::new()
.ephemeral(true) .content("Welcome, you're already verified, re-applied your roles!")
}) .ephemeral(true),
}) ),
)
.await?; .await?;
} else { } else {
m.create_interaction_response(&ctx.http, |i| { let irm = CreateInteractionResponseMessage::new()
i.kind(if init { .content(START_MSG)
serenity::InteractionResponseType::ChannelMessageWithSource .ephemeral(true)
.components(vec![CreateActionRow::Buttons(vec![
CreateButton::new("login_1")
.style(serenity::ButtonStyle::Primary)
.emoji('🚀')
.label("Login"),
CreateButton::new("membership_1")
.style(serenity::ButtonStyle::Secondary)
.emoji(serenity::ReactionType::Unicode("✈️".to_string()))
.label("Membership"),
CreateButton::new("manual_1")
.style(serenity::ButtonStyle::Secondary)
.emoji('🚗')
.label("Manual"),
])]);
m.create_response(
&ctx.http,
if init {
CreateInteractionResponse::Message(irm)
} else { } else {
serenity::InteractionResponseType::UpdateMessage CreateInteractionResponse::UpdateMessage(irm)
}) },
.interaction_response_data(|d| { )
d.content(START_MSG).ephemeral(true).components(|c| {
c.create_action_row(|a| {
a.create_button(|b| {
b.style(serenity::ButtonStyle::Primary)
.emoji('🚀')
.label("Login")
.custom_id("login_1")
})
.create_button(|b| {
b.style(serenity::ButtonStyle::Secondary)
.emoji(serenity::ReactionType::Unicode("✈️".to_string()))
.label("Membership")
.custom_id("membership_1")
})
.create_button(|b| {
b.style(serenity::ButtonStyle::Secondary)
.emoji('🚗')
.label("Manual")
.custom_id("manual_1")
})
})
})
})
})
.await?; .await?;
}; };
Ok(()) Ok(())
@ -135,14 +141,15 @@ pub(crate) async fn remove_role(
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
pub(crate) async fn welcome_user( pub(crate) async fn welcome_user(
http: impl AsRef<serenity::http::Http>, http: impl CacheHttp,
channel: &serenity::ChannelId, channel: &serenity::ChannelId,
user: &serenity::User, user: &serenity::User,
fresher: bool, fresher: bool,
) -> Result<(), Error> { ) -> Result<(), Error> {
channel channel
.send_message(http, |m| { .send_message(
m.content(format!( http,
CreateMessage::new().content(format!(
"Welcome to ICAS {user}, if you have any questions, \ "Welcome to ICAS {user}, if you have any questions, \
feel free to ping a committee member{}!", feel free to ping a committee member{}!",
if fresher { if fresher {
@ -150,8 +157,8 @@ pub(crate) async fn welcome_user(
} else { } else {
"" ""
} }
)) )),
}) )
.await?; .await?;
Ok(()) Ok(())
} }