Allow channels to be ignored for XP gain #20

Merged
xyon merged 2 commits from feature/ignore-for-xp into main 2025-02-17 21:55:10 +00:00
7 changed files with 119 additions and 42 deletions
Showing only changes of commit d90eeef19d - Show all commits

View File

@ -0,0 +1,18 @@
ALTER TABLE IF EXISTS "channels"
RENAME TO "quarantine_channels";
-- Make the quarantine channels table hold more generic channel info
ALTER TABLE IF EXISTS "quarantine_channels"
RENAME COLUMN "channel_id" TO "qc_channel_id";
-- Channels with this flag should retain old qc behaviour
ALTER TABLE IF EXISTS "quarantine_channels"
DROP COLUMN "is_quarantine_channel";
-- Setting flag to false allows channels to be excluded from XP even if not QC (though QC channels via the above flag imply this flag too)
ALTER TABLE IF EXISTS "quarantine_channels"
DROP COLUMN "is_valid_for_xp";
-- Ensure that the role ID can be null for non-qc channels
ALTER TABLE IF EXISTS "quarantine_channels"
ALTER COLUMN "qc_role_id" SET NOT NULL;

View File

@ -0,0 +1,19 @@
-- Make the quarantine channels table hold more generic channel info
ALTER TABLE IF EXISTS "quarantine_channels"
RENAME COLUMN "qc_channel_id" TO "channel_id";
-- Channels with this flag should retain old qc behaviour
ALTER TABLE IF EXISTS "quarantine_channels"
ADD COLUMN "is_quarantine_channel" BOOL NOT NULL DEFAULT FALSE;
-- Setting flag to false allows channels to be excluded from XP even if not QC (though QC channels via the above flag imply this flag too)
ALTER TABLE IF EXISTS "quarantine_channels"
ADD COLUMN "is_valid_for_xp" BOOL NOT NULL DEFAULT TRUE;
-- Ensure that the role ID can be null for non-qc channels
ALTER TABLE IF EXISTS "quarantine_channels"
ALTER COLUMN "qc_role_id" DROP NOT NULL;
-- Rename table to something more suitable
ALTER TABLE IF EXISTS "quarantine_channels"
RENAME TO "channels";

View File

@ -2,7 +2,7 @@ use built::chrono;
use manifold::error::{ManifoldError, ManifoldResult};
use manifold::{ManifoldContext, ManifoldData};
use poise::serenity_prelude as serenity;
use crate::badgey::models::quarantine_channel::QuarantineChannel;
use crate::badgey::models::quarantine_channel::Channel;
#[poise::command(slash_command, prefix_command, required_permissions = "MODERATE_MEMBERS")]
async fn void_user(_ctx: ManifoldContext<'_>, _target: serenity::User) -> ManifoldResult<()> {
@ -38,25 +38,44 @@ async fn security_record(ctx: ManifoldContext<'_>) -> ManifoldResult<()> {
#[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);
let qc = Channel::new(Some(role.as_u64().clone() as i64), channel.as_u64().clone() as i64, true, false);
qc.insert(&ctx.data().database)?;
ctx.reply(format!("OK: Quarantine channel {channel} defined with quarantine role {role}", channel = channel, role = role)).await?;
ctx.reply(format!("OK: 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 db = &ctx.data().database;
QuarantineChannel::get(db, channel.as_u64().clone() as i64)?.remove(db)?;
Channel::get_by_channel_id(db, channel.as_u64().clone() as i64)?.remove(db)?;
ctx.reply(format!("OK: Quarantine channel {channel} is no longer defined.", channel = channel)).await?;
ctx.reply(format!("OK: 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()]
#[poise::command(slash_command, prefix_command, required_permissions = "MANAGE_GUILD")]
async fn ignore_channel_for_xp(ctx: ManifoldContext<'_>, channel: serenity::ChannelId) -> ManifoldResult<()> {
let qc = Channel::new(None, channel.as_u64().clone() as i64, false, false);
qc.insert(&ctx.data().database)?;
ctx.reply(format!("OK: Channel <#{channel}> will no longer allow users to accrue XP.", channel = channel)).await?;
Ok(())
}
#[poise::command(slash_command, prefix_command, required_permissions = "MANAGE_GUILD")]
async fn unignore_channel_for_xp(ctx: ManifoldContext<'_>, channel: serenity::ChannelId) -> ManifoldResult<()> {
let db = &ctx.data().database;
Channel::get_by_channel_id(db, channel.as_u64().clone() as i64)?.remove(db)?;
ctx.reply(format!("OK: Channel <#{channel}> will now permit users to accrue XP.", channel = channel)).await?;
Ok(())
}
pub fn commands() -> [poise::Command<ManifoldData, ManifoldError>; 9] {
[void_user(), unvoid_user(), airlock_user(), record_chronicle(), security_record(), add_quarantine_channel(), remove_quarantine_channel(), ignore_channel_for_xp(), unignore_channel_for_xp()]
}

View File

@ -3,7 +3,7 @@ use manifold::ManifoldData;
use poise::FrameworkContext;
use poise::serenity_prelude::{ChannelId, Context, Member, Mentionable};
use crate::badgey::models::quarantine_channel::QuarantineChannel;
use crate::badgey::models::quarantine_channel::Channel;
pub async fn airlock_handler(ctx: &Context, fctx: &FrameworkContext<'_, ManifoldData, ManifoldError>, old: &Option<Member>, new: &Member) -> ManifoldResult<()> {
@ -11,17 +11,17 @@ pub async fn airlock_handler(ctx: &Context, fctx: &FrameworkContext<'_, Manifold
// Is this user quarantined?
for role in new.roles.iter() {
if let Ok(channel) = QuarantineChannel::get(db, role.as_u64().clone() as i64) {
if let Ok(channel) = Channel::get_by_qc_role_id(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) {
if let Ok(_) = Channel::get_by_qc_role_id(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);
let target_channel: ChannelId = ChannelId::from(channel.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?;

View File

@ -3,45 +3,57 @@ 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::channels as channel_table;
use crate::badgey::schema::*;
#[derive(Queryable, Selectable, Identifiable, Insertable, Debug, Clone)]
#[diesel(table_name = quarantine_channels)]
pub struct QuarantineChannel {
#[diesel(table_name = channels)]
pub struct Channel {
#[diesel(deserialize_as = i32)]
pub id: Option<i32>,
pub qc_role_id: i64,
pub qc_channel_id: i64,
pub qc_role_id: Option<i64>,
pub channel_id: i64,
pub is_quarantine_channel: bool,
pub is_valid_for_xp: bool,
}
impl QuarantineChannel {
pub fn new(role_id: i64, channel_id: i64) -> Self {
QuarantineChannel {
impl Channel {
pub fn new(qc_role_id: Option<i64>, channel_id: i64, is_quarantine_channel: bool, is_valid_for_xp: bool) -> Self {
Channel {
id: None,
qc_role_id: role_id,
qc_channel_id: channel_id
qc_role_id,
channel_id,
is_quarantine_channel,
is_valid_for_xp,
}
}
pub fn insert(&self, conn: &Db) -> ManifoldResult<usize> {
insert_into(quarantine_table::dsl::quarantine_channels)
insert_into(channel_table::dsl::channels)
.values(self)
.execute(&mut conn.get()?)
.map_err(|e| ManifoldError::from(e))
}
pub fn remove(&self, conn: &Db) -> ManifoldResult<usize> {
Ok(delete(quarantine_table::dsl::quarantine_channels)
.filter(quarantine_table::qc_role_id.eq(self.qc_role_id))
Ok(delete(channel_table::dsl::channels)
.filter(channel_table::qc_role_id.eq(self.qc_role_id))
.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))
pub fn get_by_channel_id(conn: &Db, needle: i64) -> ManifoldResult<Self> {
Ok(channel_table::dsl::channels
.filter(channel_table::channel_id.eq(needle))
.limit(1)
.select(QuarantineChannel::as_select())
.select(Channel::as_select())
.get_result(&mut conn.get()?)?)
}
pub fn get_by_qc_role_id(conn: &Db, needle: i64) -> ManifoldResult<Self> {
Ok(channel_table::dsl::channels
.filter(channel_table::qc_role_id.eq(needle))
.limit(1)
.select(Channel::as_select())
.get_result(&mut conn.get()?)?)
}
}

View File

@ -11,7 +11,7 @@ use manifold::models::user::UserInfo;
use manifold::schema::userinfo;
use poise::FrameworkContext;
use poise::serenity_prelude::{Context, Mentionable, Message, RoleId};
use crate::badgey::models::quarantine_channel::QuarantineChannel;
use crate::badgey::models::quarantine_channel::Channel;
use crate::badgey::schema::xp as xp_table;
use crate::badgey::schema::*;
@ -171,18 +171,25 @@ impl Xp {
let xp_reward = rng.gen_range(1..30).clone();
drop(rng);
let user_id_i64 = msg.author.id.as_u64().clone() as i64;
let channel_id_i64 = msg.channel_id.as_u64().clone() as i64;
let mut valid_channel = true;
if let Ok(c) = Channel::get_by_channel_id(&fctx.user_data.database, channel_id_i64) {
valid_channel = c.is_valid_for_xp && !c.is_quarantine_channel;
}
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 && QuarantineChannel::get(&fctx.user_data.database, msg.channel_id.as_u64().clone() as i64).is_err(),
let valid_user = match xp.last_given_xp {
Some(t) => (chrono::Utc::now().timestamp() - 60) > t,
None => true
};
if valid {
if valid_user && valid_channel {
xp.last_given_xp = Some(chrono::Utc::now().timestamp());
xp.xp_value += &xp_reward;
let calculated_level = xp.get_level_from_xp(&db, None);

View File

@ -1,5 +1,15 @@
// @generated automatically by Diesel CLI.
diesel::table! {
channels (id) {
id -> Int4,
qc_role_id -> Nullable<Int8>,
channel_id -> Int8,
is_quarantine_channel -> Bool,
is_valid_for_xp -> Bool,
}
}
diesel::table! {
custom_responses (id) {
id -> Int4,
@ -12,14 +22,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,
@ -72,8 +74,8 @@ diesel::joinable!(custom_responses -> userinfo (added_for));
diesel::joinable!(xp -> userinfo (user_id));
diesel::allow_tables_to_appear_in_same_query!(
channels,
custom_responses,
quarantine_channels,
ranks,
tracks,
userinfo,