mirror of
https://github.com/supleed2/nanobot.git
synced 2024-12-22 22:25:51 +00:00
Add verification module
This commit is contained in:
parent
57aea8793d
commit
092562e8c4
258
src/verify/login.rs
Normal file
258
src/verify/login.rs
Normal file
|
@ -0,0 +1,258 @@
|
||||||
|
use crate::{Data, Error};
|
||||||
|
use poise::serenity_prelude as serenity;
|
||||||
|
use poise::Modal;
|
||||||
|
|
||||||
|
const LOGIN_INTRO: &str = indoc::indoc! {"
|
||||||
|
To use automatic verification via Imperial Login:
|
||||||
|
- Open the link provided and login using your shortcode
|
||||||
|
- Your account will be checked and then the login details immediately discarded
|
||||||
|
- Your shortcode will then be connected to your Discord Account by Nano
|
||||||
|
|
||||||
|
You can then complete the remaining details in the next step!
|
||||||
|
"};
|
||||||
|
|
||||||
|
pub(crate) async fn login_1(
|
||||||
|
ctx: &serenity::Context,
|
||||||
|
m: &serenity::MessageComponentInteraction,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
m.create_interaction_response(&ctx.http, |i| {
|
||||||
|
i.kind(serenity::InteractionResponseType::UpdateMessage)
|
||||||
|
.interaction_response_data(|d| {
|
||||||
|
d.content(LOGIN_INTRO).components(|c| {
|
||||||
|
c.create_action_row(|a| {
|
||||||
|
a.create_button(|b| {
|
||||||
|
b.style(serenity::ButtonStyle::Danger)
|
||||||
|
.emoji('🔙')
|
||||||
|
.custom_id("restart")
|
||||||
|
})
|
||||||
|
.create_button(|b| {
|
||||||
|
b.style(serenity::ButtonStyle::Link)
|
||||||
|
.emoji('🚀')
|
||||||
|
.label("Login Here")
|
||||||
|
.url(format!("https://icas.8bitsqu.id/verify?id={}", m.user.id.0))
|
||||||
|
})
|
||||||
|
.create_button(|b| {
|
||||||
|
b.style(serenity::ButtonStyle::Secondary)
|
||||||
|
.emoji('👉')
|
||||||
|
.label("Then continue")
|
||||||
|
.custom_id("login_2")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
const LOGIN_FORM: &str = indoc::indoc! {"
|
||||||
|
Congratulations, your Imperial shortcode has been connected to your Discord Account by Nano!
|
||||||
|
|
||||||
|
The last step is a short form with some extra details
|
||||||
|
"};
|
||||||
|
|
||||||
|
pub(crate) async fn login_2(
|
||||||
|
ctx: &serenity::Context,
|
||||||
|
m: &serenity::MessageComponentInteraction,
|
||||||
|
data: &Data,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
match crate::db::get_pending_by_id(&data.db, m.user.id.0 as i64).await {
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Error in login_2: {e}");
|
||||||
|
m.create_interaction_response(&ctx.http, |i| {
|
||||||
|
i.kind(serenity::InteractionResponseType::ChannelMessageWithSource)
|
||||||
|
.interaction_response_data(|d| {
|
||||||
|
d.content("Sorry, something went wrong. Please try again")
|
||||||
|
.ephemeral(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.await?
|
||||||
|
}
|
||||||
|
Ok(None) => {
|
||||||
|
m.create_interaction_response(&ctx.http, |i| {
|
||||||
|
i.kind(serenity::InteractionResponseType::ChannelMessageWithSource)
|
||||||
|
.interaction_response_data(|d| {
|
||||||
|
d.content("Error, have you completed login verification via the link?")
|
||||||
|
.ephemeral(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.await?
|
||||||
|
}
|
||||||
|
Ok(Some(_)) => {
|
||||||
|
m.create_interaction_response(&ctx.http, |i| {
|
||||||
|
i.kind(serenity::InteractionResponseType::UpdateMessage)
|
||||||
|
.interaction_response_data(|d| {
|
||||||
|
d.content(LOGIN_FORM).components(|c| {
|
||||||
|
c.create_action_row(|a| {
|
||||||
|
a.create_button(|b| {
|
||||||
|
b.style(serenity::ButtonStyle::Danger)
|
||||||
|
.emoji('🔙')
|
||||||
|
.custom_id("login_1")
|
||||||
|
})
|
||||||
|
.create_button(|b| {
|
||||||
|
b.style(serenity::ButtonStyle::Primary)
|
||||||
|
.emoji('📑')
|
||||||
|
.label("Form")
|
||||||
|
.custom_id("login_3")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.await?
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn login_3(
|
||||||
|
ctx: &serenity::Context,
|
||||||
|
m: &serenity::MessageComponentInteraction,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
m.create_interaction_response(&ctx.http, |i| {
|
||||||
|
i.kind(serenity::InteractionResponseType::UpdateMessage)
|
||||||
|
.interaction_response_data(|d| {
|
||||||
|
d.content("Are you a fresher?").components(|c| {
|
||||||
|
c.create_action_row(|a| {
|
||||||
|
a.create_button(|b| {
|
||||||
|
b.style(serenity::ButtonStyle::Danger)
|
||||||
|
.emoji('🔙')
|
||||||
|
.custom_id("login_2")
|
||||||
|
})
|
||||||
|
.create_button(|b| {
|
||||||
|
b.style(serenity::ButtonStyle::Success)
|
||||||
|
.emoji('✅')
|
||||||
|
.label("Fresher")
|
||||||
|
.custom_id("login_4f")
|
||||||
|
})
|
||||||
|
.create_button(|b| {
|
||||||
|
b.style(serenity::ButtonStyle::Primary)
|
||||||
|
.emoji('❌')
|
||||||
|
.label("Non-fresher")
|
||||||
|
.custom_id("login_4n")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn login_4(
|
||||||
|
ctx: &serenity::Context,
|
||||||
|
m: &serenity::MessageComponentInteraction,
|
||||||
|
fresher: bool,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
m.create_interaction_response(&ctx.http, |i| {
|
||||||
|
i.kind(serenity::InteractionResponseType::UpdateMessage)
|
||||||
|
.interaction_response_data(|d| {
|
||||||
|
d.content("And a preferred name for Nano whois commands")
|
||||||
|
.components(|c| {
|
||||||
|
c.create_action_row(|a| {
|
||||||
|
a.create_button(|b| {
|
||||||
|
b.style(serenity::ButtonStyle::Danger)
|
||||||
|
.emoji('🔙')
|
||||||
|
.custom_id("login_3")
|
||||||
|
})
|
||||||
|
.create_button(|b| {
|
||||||
|
b.style(serenity::ButtonStyle::Primary)
|
||||||
|
.emoji('💬')
|
||||||
|
.label("Name")
|
||||||
|
.custom_id(if fresher { "login_5f" } else { "login_5n" })
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Modal)]
|
||||||
|
#[name = "Preferred Name"]
|
||||||
|
struct Nickname {
|
||||||
|
#[name = "Preferred name for Nano whois commands"]
|
||||||
|
#[placeholder = "Firstname Lastname"]
|
||||||
|
nickname: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn login_5(
|
||||||
|
ctx: &serenity::Context,
|
||||||
|
m: &serenity::MessageComponentInteraction,
|
||||||
|
fresher: bool,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
m.create_interaction_response(&ctx.http, |i| {
|
||||||
|
*i = Nickname::create(
|
||||||
|
None,
|
||||||
|
if fresher {
|
||||||
|
"login_6f".to_string()
|
||||||
|
} else {
|
||||||
|
"login_6n".to_string()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
i
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn login_6(
|
||||||
|
ctx: &serenity::Context,
|
||||||
|
m: &serenity::ModalSubmitInteraction,
|
||||||
|
data: &Data,
|
||||||
|
fresher: bool,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
match Nickname::parse(m.data.clone()) {
|
||||||
|
Ok(Nickname { nickname }) => {
|
||||||
|
// Delete from manual if exists
|
||||||
|
let _ = crate::db::delete_manual_by_id(&data.db, m.user.id.0 as i64).await;
|
||||||
|
|
||||||
|
match crate::db::insert_member_from_pending(
|
||||||
|
&data.db,
|
||||||
|
m.user.id.0 as i64,
|
||||||
|
&nickname,
|
||||||
|
fresher,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(()) => {
|
||||||
|
let mut mm = m.member.clone().unwrap();
|
||||||
|
crate::verify::apply_role(ctx, &mut mm, data.member).await?;
|
||||||
|
if fresher {
|
||||||
|
crate::verify::apply_role(ctx, &mut mm, data.fresher).await?;
|
||||||
|
}
|
||||||
|
m.create_interaction_response(&ctx.http, |i| {
|
||||||
|
i.kind(serenity::InteractionResponseType::UpdateMessage)
|
||||||
|
.interaction_response_data(|d| {
|
||||||
|
d.content(if fresher {
|
||||||
|
"Congratulations, you have completed verification and now \
|
||||||
|
have access to the ICAS Discord and freshers thread"
|
||||||
|
} else {
|
||||||
|
"Congratulations, you have completed verification and now \
|
||||||
|
have access to the ICAS Discord"
|
||||||
|
})
|
||||||
|
.components(|c| c)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.await?
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Error: {e}");
|
||||||
|
m.create_interaction_response(&ctx.http, |i| {
|
||||||
|
i.kind(serenity::InteractionResponseType::ChannelMessageWithSource)
|
||||||
|
.interaction_response_data(|d| {
|
||||||
|
d.content("Sorry, something went wrong. Please try again")
|
||||||
|
.ephemeral(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.await?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Error: {e}")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
|
}
|
266
src/verify/manual.rs
Normal file
266
src/verify/manual.rs
Normal file
|
@ -0,0 +1,266 @@
|
||||||
|
use crate::{Data, Error};
|
||||||
|
use poise::serenity_prelude as serenity;
|
||||||
|
use poise::Modal;
|
||||||
|
|
||||||
|
const MANUAL_INTRO: &str = indoc::indoc! {"
|
||||||
|
Submit details to be manually checked by a committee member:
|
||||||
|
- Your Imperial Shortcode
|
||||||
|
- Your First and Last Names as on your Imperial record
|
||||||
|
- Preferred First and Last Names for the Nano whois command
|
||||||
|
- URL to proof of being an Imperial student, e.g. photo of College ID Card \
|
||||||
|
or screenshot of College Acceptance Letter, if you need to upload this, \
|
||||||
|
you can send it in a DM and then copy the image URL
|
||||||
|
|
||||||
|
We try to respond quickly but this may take a day or two during busy term times :)
|
||||||
|
|
||||||
|
First, are you a fresher?
|
||||||
|
"};
|
||||||
|
|
||||||
|
pub(crate) async fn manual_1(
|
||||||
|
ctx: &serenity::Context,
|
||||||
|
m: &serenity::MessageComponentInteraction,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
m.create_interaction_response(&ctx.http, |i| {
|
||||||
|
i.kind(serenity::InteractionResponseType::UpdateMessage)
|
||||||
|
.interaction_response_data(|d| {
|
||||||
|
d.content(MANUAL_INTRO).components(|c| {
|
||||||
|
c.create_action_row(|a| {
|
||||||
|
a.create_button(|b| {
|
||||||
|
b.style(serenity::ButtonStyle::Danger)
|
||||||
|
.emoji('🔙')
|
||||||
|
.custom_id("restart")
|
||||||
|
})
|
||||||
|
.create_button(|b| {
|
||||||
|
b.style(serenity::ButtonStyle::Success)
|
||||||
|
.emoji('✅')
|
||||||
|
.label("Fresher")
|
||||||
|
.custom_id("manual_2f")
|
||||||
|
})
|
||||||
|
.create_button(|b| {
|
||||||
|
b.style(serenity::ButtonStyle::Primary)
|
||||||
|
.emoji('❌')
|
||||||
|
.label("Non-fresher")
|
||||||
|
.custom_id("manual_2n")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Modal)]
|
||||||
|
#[name = "Manual Verification"]
|
||||||
|
struct Manual {
|
||||||
|
#[name = "Imperial Shortcode"]
|
||||||
|
#[placeholder = "ab1234"]
|
||||||
|
shortcode: String,
|
||||||
|
#[name = "Name as on Imperial record"]
|
||||||
|
#[placeholder = "Firstname Lastname"]
|
||||||
|
realname: String,
|
||||||
|
#[name = "URL to proof image"]
|
||||||
|
#[placeholder = "E.g. photo of College ID Card \
|
||||||
|
or screenshot of College Acceptance Letter"]
|
||||||
|
url: String,
|
||||||
|
#[name = "Preferred name for Nano whois commands"]
|
||||||
|
#[placeholder = "Firstname Lastname"]
|
||||||
|
nickname: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn manual_2(
|
||||||
|
ctx: &serenity::Context,
|
||||||
|
m: &serenity::MessageComponentInteraction,
|
||||||
|
data: &Data,
|
||||||
|
fresher: bool,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
// Delete from manual if exists
|
||||||
|
let _ = crate::db::delete_manual_by_id(&data.db, m.user.id.0 as i64).await;
|
||||||
|
|
||||||
|
m.create_interaction_response(&ctx.http, |i| {
|
||||||
|
*i = Manual::create(
|
||||||
|
None,
|
||||||
|
if fresher {
|
||||||
|
"manual_3f".to_string()
|
||||||
|
} else {
|
||||||
|
"manual_3n".to_string()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
i
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn manual_3(
|
||||||
|
ctx: &serenity::Context,
|
||||||
|
m: &serenity::ModalSubmitInteraction,
|
||||||
|
data: &Data,
|
||||||
|
fresher: bool,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
match Manual::parse(m.data.clone()) {
|
||||||
|
Ok(Manual {
|
||||||
|
shortcode,
|
||||||
|
realname,
|
||||||
|
url,
|
||||||
|
nickname,
|
||||||
|
}) => {
|
||||||
|
if ::url::Url::parse(&url).is_err() {
|
||||||
|
m.create_interaction_response(&ctx.http, |i| {
|
||||||
|
i.kind(serenity::InteractionResponseType::ChannelMessageWithSource)
|
||||||
|
.interaction_response_data(|d| {
|
||||||
|
d.content("The url provided is invalid, please try again")
|
||||||
|
.ephemeral(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete from pending if exists
|
||||||
|
let _ = crate::db::delete_pending_by_id(&data.db, m.user.id.0 as i64).await?;
|
||||||
|
|
||||||
|
let prompt_sent = data
|
||||||
|
.au_ch_id
|
||||||
|
.send_message(&ctx.http, |cm| {
|
||||||
|
cm.add_embed(|e| {
|
||||||
|
e.title("New verification request from")
|
||||||
|
.thumbnail(m.user.avatar_url().unwrap_or(
|
||||||
|
"https://cdn.discordapp.com/embed/avatars/0.png".to_string(),
|
||||||
|
))
|
||||||
|
.description(&m.user)
|
||||||
|
.field("Real Name (To be checked)", &realname, true)
|
||||||
|
.field("Imperial Shortcode (To be checked", &shortcode, true)
|
||||||
|
.field("Fresher (To be checked)", fresher, true)
|
||||||
|
.field("Nickname (Nano whois commands)", &nickname, true)
|
||||||
|
.field("Verification URL (Also displayed below)", &url, true)
|
||||||
|
.image(&url)
|
||||||
|
.timestamp(serenity::Timestamp::now())
|
||||||
|
})
|
||||||
|
.components(|c| {
|
||||||
|
c.create_action_row(|a| {
|
||||||
|
a.create_button(|b| {
|
||||||
|
b.style(serenity::ButtonStyle::Success)
|
||||||
|
.emoji('✅')
|
||||||
|
.label("Accept")
|
||||||
|
.custom_id(format!("verify-y-{}", m.user.id))
|
||||||
|
})
|
||||||
|
.create_button(|b| {
|
||||||
|
b.style(serenity::ButtonStyle::Danger)
|
||||||
|
.emoji('❎')
|
||||||
|
.label("Deny")
|
||||||
|
.custom_id(format!("verify-n-{}", m.user.id))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.is_ok();
|
||||||
|
|
||||||
|
let inserted = crate::db::insert_manual(
|
||||||
|
&data.db,
|
||||||
|
crate::ManualMember {
|
||||||
|
discord_id: m.user.id.0 as i64,
|
||||||
|
shortcode,
|
||||||
|
nickname,
|
||||||
|
realname,
|
||||||
|
fresher,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.is_ok();
|
||||||
|
|
||||||
|
let msg = if prompt_sent {
|
||||||
|
if inserted {
|
||||||
|
"Thanks, your verification request has been sent, we'll try to get back to you quickly!"
|
||||||
|
} else {
|
||||||
|
"Thanks, your verification request has been sent, but there was an issue, please ask a Committee member to take a look!"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
"Sending your verification request failed, please try again."
|
||||||
|
};
|
||||||
|
|
||||||
|
m.create_interaction_response(&ctx.http, |i| {
|
||||||
|
i.kind(serenity::InteractionResponseType::UpdateMessage)
|
||||||
|
.interaction_response_data(|d| d.content(msg).components(|c| c))
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
Err(e) => eprintln!("Error: {e}"),
|
||||||
|
};
|
||||||
|
m.create_interaction_response(&ctx.http, |i| {
|
||||||
|
i.kind(serenity::InteractionResponseType::ChannelMessageWithSource)
|
||||||
|
.interaction_response_data(|d| {
|
||||||
|
d.content("Sorry, something went wrong. Please try again")
|
||||||
|
.ephemeral(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn manual_4(
|
||||||
|
ctx: &serenity::Context,
|
||||||
|
m: &serenity::MessageComponentInteraction,
|
||||||
|
data: &Data,
|
||||||
|
id: &str,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let verify = match id.chars().nth(7) {
|
||||||
|
Some('y') => true,
|
||||||
|
Some('n') => false,
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
let user = id
|
||||||
|
.chars()
|
||||||
|
.skip(9)
|
||||||
|
.collect::<String>()
|
||||||
|
.parse::<u64>()
|
||||||
|
.map(serenity::UserId)
|
||||||
|
.unwrap_or_default()
|
||||||
|
.to_user(ctx)
|
||||||
|
.await
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
match crate::db::insert_member_from_manual(&data.db, user.id.0 as i64).await {
|
||||||
|
Ok(()) => {
|
||||||
|
let fresher = crate::db::get_member_by_id(&data.db, user.id.0 as i64)
|
||||||
|
.await?
|
||||||
|
.unwrap()
|
||||||
|
.fresher;
|
||||||
|
let mut mm = m.member.clone().unwrap();
|
||||||
|
crate::verify::apply_role(ctx, &mut mm, data.member).await?;
|
||||||
|
if fresher {
|
||||||
|
crate::verify::apply_role(ctx, &mut mm, data.fresher).await?;
|
||||||
|
}
|
||||||
|
m.create_interaction_response(&ctx.http, |i| {
|
||||||
|
i.kind(serenity::InteractionResponseType::UpdateMessage)
|
||||||
|
.interaction_response_data(|d| {
|
||||||
|
d.components(|c| c).embed(|e| {
|
||||||
|
e.title(format!(
|
||||||
|
"Verification {} for",
|
||||||
|
if verify { "accepted" } else { "denied" },
|
||||||
|
))
|
||||||
|
.description(&user)
|
||||||
|
.thumbnail(user.avatar_url().unwrap_or_default())
|
||||||
|
.timestamp(serenity::Timestamp::now())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.await?
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Error: {e}");
|
||||||
|
m.create_interaction_response(&ctx.http, |i| {
|
||||||
|
i.kind(serenity::InteractionResponseType::ChannelMessageWithSource)
|
||||||
|
.interaction_response_data(|d| {
|
||||||
|
d.content(format!("Failed to add user {user} to member database"))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.await?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
179
src/verify/membership.rs
Normal file
179
src/verify/membership.rs
Normal file
|
@ -0,0 +1,179 @@
|
||||||
|
use crate::{Data, Error};
|
||||||
|
use poise::serenity_prelude as serenity;
|
||||||
|
use poise::Modal;
|
||||||
|
|
||||||
|
const MEMBERSHIP_INTRO: &str = indoc::indoc! {"
|
||||||
|
To use automatic verification via Membership:
|
||||||
|
- Enter your Union order number (from this academic year)
|
||||||
|
- Enter your Imperial shortcode
|
||||||
|
- Enter your preferred name for Nano whois commands
|
||||||
|
- Your shortcode will then be connected to your Discord Account by Nano
|
||||||
|
|
||||||
|
First, are you a fresher?
|
||||||
|
"};
|
||||||
|
|
||||||
|
pub(crate) async fn membership_1(
|
||||||
|
ctx: &serenity::Context,
|
||||||
|
m: &serenity::MessageComponentInteraction,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
m.create_interaction_response(&ctx.http, |i| {
|
||||||
|
i.kind(serenity::InteractionResponseType::UpdateMessage)
|
||||||
|
.interaction_response_data(|d| {
|
||||||
|
d.content(MEMBERSHIP_INTRO).components(|c| {
|
||||||
|
c.create_action_row(|a| {
|
||||||
|
a.create_button(|b| {
|
||||||
|
b.style(serenity::ButtonStyle::Danger)
|
||||||
|
.emoji('🔙')
|
||||||
|
.custom_id("restart")
|
||||||
|
})
|
||||||
|
.create_button(|b| {
|
||||||
|
b.style(serenity::ButtonStyle::Success)
|
||||||
|
.emoji('✅')
|
||||||
|
.label("Fresher")
|
||||||
|
.custom_id("membership_2f")
|
||||||
|
})
|
||||||
|
.create_button(|b| {
|
||||||
|
b.style(serenity::ButtonStyle::Primary)
|
||||||
|
.emoji('❌')
|
||||||
|
.label("Non-fresher")
|
||||||
|
.custom_id("membership_2n")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Modal)]
|
||||||
|
#[name = "ICAS Membership Verification"]
|
||||||
|
struct Membership {
|
||||||
|
#[name = "ICAS Membership Union Order Number"]
|
||||||
|
#[placeholder = "1234567"]
|
||||||
|
order: String,
|
||||||
|
#[name = "Imperial Shortcode"]
|
||||||
|
#[placeholder = "ab1234"]
|
||||||
|
shortcode: String,
|
||||||
|
#[name = "Preferred name for Nano whois commands"]
|
||||||
|
#[placeholder = "Firstname Lastname"]
|
||||||
|
nickname: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn membership_2(
|
||||||
|
ctx: &serenity::Context,
|
||||||
|
m: &serenity::MessageComponentInteraction,
|
||||||
|
data: &Data,
|
||||||
|
fresher: bool,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
// Delete from pending if exists
|
||||||
|
let _ = crate::db::delete_pending_by_id(&data.db, m.user.id.0 as i64).await;
|
||||||
|
|
||||||
|
// Delete from manual if exists
|
||||||
|
let _ = crate::db::delete_manual_by_id(&data.db, m.user.id.0 as i64).await;
|
||||||
|
|
||||||
|
m.create_interaction_response(&ctx.http, |i| {
|
||||||
|
*i = Membership::create(
|
||||||
|
None,
|
||||||
|
if fresher {
|
||||||
|
"membership_3f".to_string()
|
||||||
|
} else {
|
||||||
|
"membership_3n".to_string()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
i
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn membership_3(
|
||||||
|
ctx: &serenity::Context,
|
||||||
|
m: &serenity::ModalSubmitInteraction,
|
||||||
|
data: &Data,
|
||||||
|
fresher: bool,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
match Membership::parse(m.data.clone()) {
|
||||||
|
Ok(Membership {
|
||||||
|
order,
|
||||||
|
shortcode,
|
||||||
|
nickname,
|
||||||
|
}) => {
|
||||||
|
let members = match crate::ea::get_members_list(&data.ea_key, &data.ea_url).await {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Error: {e}");
|
||||||
|
m.create_interaction_response(&ctx.http, |i| {
|
||||||
|
i.kind(serenity::InteractionResponseType::ChannelMessageWithSource)
|
||||||
|
.interaction_response_data(|d| {
|
||||||
|
d.content("Sorry, getting membership data failed. Please try again")
|
||||||
|
.ephemeral(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let member = match members
|
||||||
|
.iter()
|
||||||
|
.find(|&member| member.order_no.to_string() == order && member.login == shortcode)
|
||||||
|
{
|
||||||
|
Some(m) => m,
|
||||||
|
None => {
|
||||||
|
m.create_interaction_response(&ctx.http, |i| {
|
||||||
|
let msg = "Sorry, your order was not found, please check the \
|
||||||
|
order number and that it is for your current year's membership";
|
||||||
|
i.kind(serenity::InteractionResponseType::ChannelMessageWithSource)
|
||||||
|
.interaction_response_data(|d| d.content(msg).ephemeral(true))
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if crate::db::insert_member(
|
||||||
|
&data.db,
|
||||||
|
crate::Member {
|
||||||
|
discord_id: m.user.id.0 as i64,
|
||||||
|
shortcode,
|
||||||
|
nickname,
|
||||||
|
realname: format!("{} {}", member.first_name, member.surname),
|
||||||
|
fresher,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.is_ok()
|
||||||
|
{
|
||||||
|
let mut mm = m.member.clone().unwrap();
|
||||||
|
crate::verify::apply_role(ctx, &mut mm, data.member).await?;
|
||||||
|
if fresher {
|
||||||
|
crate::verify::apply_role(ctx, &mut mm, data.fresher).await?;
|
||||||
|
}
|
||||||
|
m.create_interaction_response(&ctx.http, |i| {
|
||||||
|
i.kind(serenity::InteractionResponseType::UpdateMessage)
|
||||||
|
.interaction_response_data(|d| {
|
||||||
|
d.content(if fresher {
|
||||||
|
"Congratulations, you have completed verification and now \
|
||||||
|
have access to the ICAS Discord and freshers thread"
|
||||||
|
} else {
|
||||||
|
"Congratulations, you have completed verification and now \
|
||||||
|
have access to the ICAS Discord"
|
||||||
|
})
|
||||||
|
.components(|c| c)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => eprintln!("Error: {e}"),
|
||||||
|
};
|
||||||
|
m.create_interaction_response(&ctx.http, |i| {
|
||||||
|
i.kind(serenity::InteractionResponseType::ChannelMessageWithSource)
|
||||||
|
.interaction_response_data(|d| {
|
||||||
|
d.content("Sorry, something went wrong. Please try again")
|
||||||
|
.ephemeral(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
107
src/verify/mod.rs
Normal file
107
src/verify/mod.rs
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
use crate::{Data, Error};
|
||||||
|
use poise::serenity_prelude as serenity;
|
||||||
|
|
||||||
|
pub(crate) mod login;
|
||||||
|
pub(crate) use login::*;
|
||||||
|
|
||||||
|
pub(crate) mod membership;
|
||||||
|
pub(crate) use membership::*;
|
||||||
|
|
||||||
|
pub(crate) mod manual;
|
||||||
|
pub(crate) use manual::*;
|
||||||
|
|
||||||
|
pub(crate) async fn unknown(
|
||||||
|
ctx: &serenity::Context,
|
||||||
|
m: &serenity::MessageComponentInteraction,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
m.create_interaction_response(&ctx.http, |i| {
|
||||||
|
i.kind(serenity::InteractionResponseType::ChannelMessageWithSource)
|
||||||
|
.interaction_response_data(|d| {
|
||||||
|
d.content("Sorry, something went wrong. Please try again or message <@99217900254035968> for help")
|
||||||
|
.ephemeral(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
const START_MSG: &str = indoc::indoc! {"
|
||||||
|
There are 3 available methods for verification.
|
||||||
|
- 🚀 Automatic verification via Imperial Login (Quickest)
|
||||||
|
- ✈️ Automatic verification via ICAS Membership (Easiest)
|
||||||
|
- 🚗 Manual verification, eg. using College ID Card or Acceptance Letter
|
||||||
|
"};
|
||||||
|
|
||||||
|
pub(crate) async fn start(
|
||||||
|
ctx: &serenity::Context,
|
||||||
|
m: &serenity::MessageComponentInteraction,
|
||||||
|
data: &Data,
|
||||||
|
init: bool,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
// Check if user is already verified
|
||||||
|
if let Some(member) = crate::db::get_member_by_id(&data.db, m.user.id.0 as i64).await? {
|
||||||
|
let mut mm = m.member.clone().unwrap();
|
||||||
|
apply_role(ctx, &mut mm, data.member).await?;
|
||||||
|
if member.fresher {
|
||||||
|
apply_role(ctx, &mut mm, data.fresher).await?;
|
||||||
|
}
|
||||||
|
m.create_interaction_response(&ctx.http, |i| {
|
||||||
|
i.kind(serenity::InteractionResponseType::ChannelMessageWithSource)
|
||||||
|
.interaction_response_data(|d| {
|
||||||
|
d.content("Welcome, you're already verified, re-applied your roles!")
|
||||||
|
.ephemeral(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.await?
|
||||||
|
} else {
|
||||||
|
m.create_interaction_response(&ctx.http, |i| {
|
||||||
|
i.kind(if init {
|
||||||
|
serenity::InteractionResponseType::ChannelMessageWithSource
|
||||||
|
} else {
|
||||||
|
serenity::InteractionResponseType::UpdateMessage
|
||||||
|
})
|
||||||
|
.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?
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn apply_role(
|
||||||
|
ctx: &serenity::Context,
|
||||||
|
member: &mut serenity::Member,
|
||||||
|
role: serenity::RoleId,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
Ok(member.add_role(&ctx.http, role).await?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn remove_role(
|
||||||
|
ctx: &serenity::Context,
|
||||||
|
member: &mut serenity::Member,
|
||||||
|
role: serenity::RoleId,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
Ok(member.remove_role(&ctx.http, role).await?)
|
||||||
|
}
|
Loading…
Reference in a new issue