Compare commits

..

No commits in common. "751bc8aca549edbf58fde239c752d1bf33651b16" and "ea81778099ea24389369c1f6a318af8dcbb3f9e8" have entirely different histories.

14 changed files with 256 additions and 383 deletions

370
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1 +0,0 @@
DROP TABLE IF EXISTS "quarantine_channels";

View File

@ -1,5 +0,0 @@
CREATE TABLE IF NOT EXISTS "quarantine_channels" (
id SERIAL PRIMARY KEY,
qc_role_id BIGINT NOT NULL,
qc_channel_id BIGINT NOT NULL
);

View File

@ -4,6 +4,5 @@ pkgs.mkShell {
openssl
postgresql
pkg-config
diesel-cli
];
}
}

View File

@ -1,3 +1,4 @@
use built::chrono;
use manifold::error::{ManifoldError, ManifoldResult};
use manifold::{ManifoldContext, ManifoldData};
use poise::serenity_prelude as serenity;
@ -34,25 +35,6 @@ async fn security_record(ctx: ManifoldContext<'_>) -> ManifoldResult<()> {
Ok(())
}
#[poise::command(slash_command, prefix_command, required_permissions = "MANAGE_GUILD")]
async fn add_quarantine_channel(ctx: ManifoldContext<'_>, channel: serenity::ChannelId, role: serenity::RoleId) -> ManifoldResult<()> {
let qc = QuarantineChannel::new(role.as_u64().clone() as i64, channel.as_u64().clone() as i64);
qc.insert(&ctx.data().database)?;
ctx.reply(format!("Quarantine channel {channel} defined with quarantine role {role}", channel = channel, role = role)).await?;
Ok(())
}
#[poise::command(slash_command, prefix_command, required_permissions = "MANAGE_GUILD")]
async fn remove_quarantine_channel(ctx: ManifoldContext<'_>, channel: serenity::ChannelId) -> ManifoldResult<()> {
let qc = QuarantineChannel::get(&ctx.data().database, channel.as_u64().clone() as i64)?;
ctx.reply(format!("Quarantine channel {channel} is no longer defined.", channel = channel)).await?;
Ok(())
}
pub fn commands() -> [poise::Command<ManifoldData, ManifoldError>; 7] {
[void_user(), unvoid_user(), airlock_user(), record_chronicle(), security_record(), add_quarantine_channel(), remove_quarantine_channel()]
pub fn commands() -> [poise::Command<ManifoldData, ManifoldError>; 5] {
[void_user(), unvoid_user(), airlock_user(), record_chronicle(), security_record()]
}

View File

@ -3,8 +3,9 @@ use manifold::error::{ManifoldError, ManifoldResult};
use manifold::events::EventHandler;
use manifold::ManifoldData;
use poise::{async_trait, FrameworkContext, Event};
use poise::serenity_prelude::{Context, Member, Message};
use crate::badgey::handlers;
use poise::serenity_prelude::{Context, Mentionable, Message, RoleId};
use rand::rngs::SmallRng;
use rand::{Rng, SeedableRng};
use crate::badgey::models::custom_response::CustomResponse;
use crate::badgey::models::xp::{Rank, Xp};
@ -18,7 +19,6 @@ pub struct BadgeyHandler {
impl EventHandler for BadgeyHandler {
async fn listen(ctx: &Context, framework_ctx: FrameworkContext<'_, ManifoldData, ManifoldError>, event: &Event<'_>) -> ManifoldResult<()> {
match event {
Event::GuildMemberUpdate { old_if_available, new } => BadgeyHandler::guild_member_update(&ctx, &framework_ctx, old_if_available, new).await,
Event::Message { new_message } => BadgeyHandler::message(&ctx, &framework_ctx, new_message).await,
_ => Ok(())
}
@ -26,27 +26,73 @@ impl EventHandler for BadgeyHandler {
}
impl BadgeyHandler {
pub async fn guild_member_update (ctx: &Context, fctx: &FrameworkContext<'_, ManifoldData, ManifoldError>, old_if_available: &Option<Member>, new: &Member) -> ManifoldResult<()> {
handlers::airlock::airlock_handler(ctx, fctx, old_if_available, new).await?;
Ok(())
}
pub async fn message(ctx: &Context, fctx: &FrameworkContext<'_, ManifoldData, ManifoldError>, msg: &Message) -> ManifoldResult<()> {
// Ignore our own messages
if msg.author.id == ctx.http.get_current_user().await?.id || msg.content.starts_with(&fctx.user_data().await.bot_config.prefix) {
return Ok(())
}
let db = &fctx.user_data().await.database;
if let Ok(r) = CustomResponse::test_content(db, &msg.content_safe(&ctx.cache), &"!".to_string()) {
msg.channel_id.say(ctx, r.response).await?;
}
Xp::award(ctx, fctx, msg, &db).await?;
if fctx.user_data().await.user_info.lock().await.get_mut(&msg.author.id.as_u64()).is_none() {
debug!("Tried to add XP to a user we don't know about, aborting.");
return Ok(())
}
let mut rng = SmallRng::from_entropy();
let xp_reward = rng.gen_range(1..30).clone();
drop(rng);
let user_id_i64 = msg.author.id.as_u64().clone() as i64;
let mut xp = match Xp::get(db, &user_id_i64) {
Ok(x) => x,
Err(_) => Xp::new(&user_id_i64)
};
let valid = match xp.last_given_xp {
Some(t) => {
if (chrono::Utc::now().timestamp() - 60) > t {
true
} else {
false
}
},
None => true
};
if valid {
xp.last_given_xp = Some(chrono::Utc::now().timestamp());
xp.xp_value += &xp_reward;
let calculated_level = xp.get_level_from_xp(&db, None);
if xp.freeze_rank.is_some() {
xp.user_current_level = calculated_level.clone();
xp.insert(db)?;
return Ok(())
}
if (xp.user_current_level != calculated_level) || xp.user_current_level == 1 {
if let Some(guild) = msg.guild(&ctx.cache) {
let mut member = guild.member(&ctx, msg.author.id).await?;
let old_role_id = RoleId::from(Rank::get_rank_for_level(db, &xp.user_current_level, &xp.rank_track).unwrap_or(Rank::new()).role_id as u64);
xp.user_current_level = calculated_level;
let new_role = RoleId::from(Rank::get_rank_for_level(db, &calculated_level, &xp.rank_track)?.role_id as u64);
if (new_role != old_role_id || xp.user_current_level == 1) && msg.author.has_role(ctx, guild.id, new_role).await? == false {
member.remove_role(ctx, old_role_id).await?;
member.add_role(ctx, new_role).await?;
msg.channel_id.say(ctx, format!("{} is now a {}", msg.author.mention(), new_role.mention())).await?;
}
} else {
error!("Needed to add level to user, but couldn't get the member from the guild!");
}
}
xp.insert(db)?;
}
Ok(())
}

View File

@ -1,37 +0,0 @@
use std::collections::HashMap;
use manifold::error::{ManifoldError, ManifoldResult};
use manifold::ManifoldData;
use poise::FrameworkContext;
use poise::serenity_prelude::{ChannelId, Context, Member, Mentionable, RoleId};
use crate::badgey::models::quarantine_channel::QuarantineChannel;
pub async fn airlock_handler(ctx: &Context, fctx: &FrameworkContext<'_, ManifoldData, ManifoldError>, old: &Option<Member>, new: &Member) -> ManifoldResult<()> {
let db = &fctx.user_data.database;
// Is this user quarantined?
for role in new.roles.iter() {
if let Ok(channel) = QuarantineChannel::get(db, role.as_u64().clone() as i64) {
// Was this user JUST added to a quarantine channel?
if let Some(m) = old {
for role in m.roles.iter() {
if let Ok(_) = QuarantineChannel::get(db, role.as_u64().clone() as i64) {
// User already had quarantine role, return early
return Ok(())
}
}
let target_channel: ChannelId = ChannelId::from(channel.qc_channel_id as u64);
// User was just added to a quarantine channel. Post the initial ping message.
target_channel.say(ctx, format!("{mention}, please respond to this message", mention = new.mention())).await?;
target_channel.await_reply(ctx).author_id(new.user.id).await;
target_channel.say(ctx, format!("{mention}, you have been brought to a private area by a server staff member. This does not necessarily mean you have broken the server rules; just that a staff member wishes to discuss something with you in a private and recorded environemnt.\n\nPlease note that the message history of this channel is not visible to you, and if your discord client loses focus on this channel it may then appear empty to you when you return. All message sent will remain visible to staff.\n\nPlease wait - a staff member should arrive shortly to explain why you are here.", mention = new.mention())).await?;
return Ok(())
}
}
}
Ok(())
}

View File

@ -1 +0,0 @@
pub mod airlock;

View File

@ -9,7 +9,6 @@ mod commands;
mod events;
mod models;
mod schema;
mod handlers;
pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations");

View File

@ -1,3 +1,2 @@
pub mod custom_response;
pub mod xp;
pub mod quarantine_channel;

View File

@ -1,48 +0,0 @@
use diesel::{insert_into, Identifiable, Insertable, QueryDsl, Queryable, RunQueryDsl, Selectable};
use diesel::associations::HasTable;
use diesel::dsl::delete;
use diesel::prelude::*;
use manifold::Db;
use manifold::error::{ManifoldError, ManifoldResult};
use crate::badgey::schema::quarantine_channels as quarantine_table;
use crate::badgey::schema::*;
#[derive(Queryable, Selectable, Identifiable, Insertable, Debug, Clone)]
#[diesel(table_name = quarantine_channels)]
pub struct QuarantineChannel {
#[diesel(deserialize_as = i32)]
pub id: Option<i32>,
pub qc_role_id: i64,
pub qc_channel_id: i64,
}
impl QuarantineChannel {
pub fn new(role_id: i64, channel_id: i64) -> Self {
QuarantineChannel {
id: None,
qc_role_id: role_id,
qc_channel_id: channel_id
}
}
pub fn insert(&self, conn: &Db) -> ManifoldResult<usize> {
insert_into(quarantine_table::dsl::quarantine_channels)
.values(self)
.execute(&mut conn.get()?)
.map_err(|e| ManifoldError::from(e))
}
pub fn remove(&self, conn: &Db, needle: i64) -> ManifoldResult<usize> {
Ok(delete(quarantine_table::dsl::quarantine_channels)
.filter(quarantine_table::qc_role_id.eq(needle))
.execute(&mut conn.get()?)?)
}
pub fn get(conn: &Db, needle: i64) -> ManifoldResult<Self> {
Ok(quarantine_table::dsl::quarantine_channels
.filter(quarantine_table::qc_role_id.eq(needle))
.limit(1)
.select(QuarantineChannel::as_select())
.get_result(&mut conn.get()?)?)
}
}

View File

@ -1,15 +1,11 @@
use built::chrono;
use diesel::prelude::*;
use diesel::insert_into;
use rand::rngs::SmallRng;
use rand::{Rng, SeedableRng};
use manifold::{Db, ManifoldData};
use manifold::Db;
use manifold::error::{ManifoldError, ManifoldResult};
use manifold::models::user::UserInfo;
use manifold::schema::userinfo;
use poise::FrameworkContext;
use poise::serenity_prelude::{Context, Mentionable, Message, RoleId};
use crate::badgey::schema::xp as xp_table;
use crate::badgey::schema::*;
@ -117,63 +113,6 @@ impl Xp {
Ok(())
}
pub async fn award(ctx: &Context, fctx: &FrameworkContext<'_, ManifoldData, ManifoldError>, msg: &Message, db: &Db) -> ManifoldResult<()>{
if fctx.user_data().await.user_info.lock().await.get_mut(&msg.author.id.as_u64()).is_none() {
debug!("Tried to add XP to a user we don't know about, aborting.");
return Ok(())
}
let mut rng = SmallRng::from_entropy();
let xp_reward = rng.gen_range(1..30).clone();
drop(rng);
let user_id_i64 = msg.author.id.as_u64().clone() as i64;
let mut xp = match Xp::get(db, &user_id_i64) {
Ok(x) => x,
Err(_) => Xp::new(&user_id_i64)
};
let valid = match xp.last_given_xp {
Some(t) => (chrono::Utc::now().timestamp() - 60) > t,
None => true
};
if valid {
xp.last_given_xp = Some(chrono::Utc::now().timestamp());
xp.xp_value += &xp_reward;
let calculated_level = xp.get_level_from_xp(&db, None);
if xp.freeze_rank.is_some() {
xp.user_current_level = calculated_level.clone();
xp.insert(db)?;
return Ok(())
}
if (xp.user_current_level != calculated_level) || xp.user_current_level == 1 {
if let Some(guild) = msg.guild(&ctx.cache) {
let mut member = guild.member(&ctx, msg.author.id).await?;
let old_role_id = RoleId::from(Rank::get_rank_for_level(db, &xp.user_current_level, &xp.rank_track).unwrap_or(Rank::new()).role_id as u64);
xp.user_current_level = calculated_level;
let new_role = RoleId::from(Rank::get_rank_for_level(db, &calculated_level, &xp.rank_track)?.role_id as u64);
if (new_role != old_role_id || xp.user_current_level == 1) && msg.author.has_role(ctx, guild.id, new_role).await? == false {
member.remove_role(ctx, old_role_id).await?;
member.add_role(ctx, new_role).await?;
msg.channel_id.say(ctx, format!("{} is now a {}", msg.author.mention(), new_role.mention())).await?;
}
} else {
error!("Needed to add level to user, but couldn't get the member from the guild!");
}
}
xp.insert(db)?;
}
Ok(())
}
}
impl Rank {

View File

@ -12,14 +12,6 @@ diesel::table! {
}
}
diesel::table! {
quarantine_channels (id) {
id -> Int4,
qc_role_id -> Int8,
qc_channel_id -> Int8,
}
}
diesel::table! {
ranks (role_id) {
role_id -> Int8,
@ -73,7 +65,6 @@ diesel::joinable!(xp -> userinfo (user_id));
diesel::allow_tables_to_appear_in_same_query!(
custom_responses,
quarantine_channels,
ranks,
tracks,
userinfo,