Add permission checks to p.prefix command
This commit is contained in:
@@ -1,432 +0,0 @@
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
use clap::Parser;
|
||||
use payphone::{
|
||||
database::{
|
||||
self,
|
||||
models::{
|
||||
MessageData, Report as PayphoneReport, ReportResolution, Server as PayphoneServer,
|
||||
UserIncident, UserIncidentType,
|
||||
},
|
||||
},
|
||||
utils::{Bye, get_random_u64, u64_to_i64},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use time::OffsetDateTime;
|
||||
use tokio::{sync::Semaphore, time::Instant};
|
||||
|
||||
#[derive(Parser)]
|
||||
struct Args {
|
||||
dir: String,
|
||||
|
||||
#[arg(long, default_value_t = String::from("127.0.0.1"))]
|
||||
database_addr: String,
|
||||
|
||||
#[arg(long)]
|
||||
database_username: Option<String>,
|
||||
|
||||
#[arg(long)]
|
||||
database_password: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
struct Report {
|
||||
id: u64,
|
||||
message_id: u64,
|
||||
message_content: String,
|
||||
channel_id: u64,
|
||||
user_id: u64,
|
||||
created_at: String,
|
||||
resolved: String,
|
||||
resolved_at: Option<String>,
|
||||
resolved_by_id: Option<u64>,
|
||||
user_avatar_url: String,
|
||||
user_name: String,
|
||||
reply_user_name: Option<String>,
|
||||
reply_message_content: Option<String>,
|
||||
reply_user_avatar_url: Option<String>,
|
||||
reply_user_id: Option<u64>,
|
||||
reported_by_id: Option<u64>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
struct User {
|
||||
id: u64,
|
||||
is_masked: String,
|
||||
is_moderator: String,
|
||||
is_supporter_until: String,
|
||||
warns_count: u64,
|
||||
bans_count: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
struct Ban {
|
||||
id: u64,
|
||||
user_id: u64,
|
||||
created_at: String,
|
||||
expires_at: Option<String>,
|
||||
created_by_id: u64,
|
||||
expired_by_id: Option<u64>,
|
||||
report_id: Option<u64>,
|
||||
reason: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
struct Server {
|
||||
id: u64,
|
||||
prefix: String,
|
||||
language: String,
|
||||
name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
struct Warn {
|
||||
id: u64,
|
||||
user_id: u64,
|
||||
reason: String,
|
||||
created_at: String,
|
||||
created_by_id: u64,
|
||||
report_id: u64,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let args = Args::parse();
|
||||
|
||||
println!("Testing DB connection...");
|
||||
|
||||
let db = Arc::new(
|
||||
database::init(
|
||||
args.database_addr,
|
||||
args.database_username,
|
||||
args.database_password,
|
||||
)
|
||||
.await
|
||||
.expect("Failed to initialize database"),
|
||||
);
|
||||
|
||||
println!("Reading migration file {}/reports.csv", args.dir);
|
||||
|
||||
let mut rdr = csv::Reader::from_path(format!("{}/reports.csv", args.dir))
|
||||
.expect("Failed to open reports file");
|
||||
|
||||
let mut reports = Vec::new();
|
||||
|
||||
let format = time::format_description::parse(
|
||||
"[year]-[month]-[day] [hour]:[minute]:[second].[subsecond][offset_hour]",
|
||||
)
|
||||
.expect("Failed to parse format description");
|
||||
|
||||
for result in rdr.deserialize() {
|
||||
let report: Report = result.expect("Failed to deserialize report");
|
||||
reports.push(report);
|
||||
}
|
||||
|
||||
println!("Finished reading reports, found {} reports", reports.len());
|
||||
println!("Reading migration file {}/users.csv", args.dir);
|
||||
|
||||
let mut rdr = csv::Reader::from_path(format!("{}/users.csv", args.dir))
|
||||
.expect("Failed to open users file");
|
||||
|
||||
let mut users = HashMap::new();
|
||||
|
||||
for result in rdr.deserialize() {
|
||||
let user: User = result.expect("Failed to deserialize user");
|
||||
users.insert(user.id, user).bye();
|
||||
}
|
||||
|
||||
println!("Finished reading users, found {} users", users.len());
|
||||
println!("Reading migration file {}/bans.csv", args.dir);
|
||||
|
||||
let mut rdr =
|
||||
csv::Reader::from_path(format!("{}/bans.csv", args.dir)).expect("Failed to open bans file");
|
||||
|
||||
let mut bans = HashMap::new();
|
||||
|
||||
for result in rdr.deserialize() {
|
||||
let ban: Ban = result.expect("Failed to deserialize ban");
|
||||
bans.insert(ban.id, ban).bye();
|
||||
}
|
||||
|
||||
println!("Finished reading bans, found {} bans", bans.len());
|
||||
println!("Reading migration file {}/servers.csv", args.dir);
|
||||
|
||||
let mut rdr = csv::Reader::from_path(format!("{}/servers.csv", args.dir))
|
||||
.expect("Failed to open servers file");
|
||||
|
||||
let mut servers = HashMap::new();
|
||||
|
||||
for result in rdr.deserialize() {
|
||||
let server: Server = result.expect("Failed to deserialize server");
|
||||
servers.insert(server.id, server).bye();
|
||||
}
|
||||
|
||||
println!("Finished reading servers, found {} servers", servers.len());
|
||||
println!("Reading migration file {}/warns.csv", args.dir);
|
||||
|
||||
let mut rdr = csv::Reader::from_path(format!("{}/warns.csv", args.dir))
|
||||
.expect("Failed to open warns file");
|
||||
|
||||
let mut warns = HashMap::new();
|
||||
|
||||
for result in rdr.deserialize() {
|
||||
let warn: Warn = result.expect("Failed to deserialize warn");
|
||||
warns.insert(warn.id, warn).bye();
|
||||
}
|
||||
|
||||
println!("Finished reading warns, found {} warns", warns.len());
|
||||
println!("Starting migration...");
|
||||
println!("Inserting server configurations...");
|
||||
|
||||
let semaphore = Arc::new(Semaphore::new(50));
|
||||
let mut tasks = Vec::new();
|
||||
|
||||
for (i, server) in servers.values().enumerate() {
|
||||
let payphone_server = PayphoneServer {
|
||||
id: server.id,
|
||||
prefix: server.prefix.clone(),
|
||||
language: server.language.clone(),
|
||||
name: server.name.clone(),
|
||||
};
|
||||
|
||||
let task_db = db.clone();
|
||||
let task_semaphore = semaphore.clone();
|
||||
|
||||
tasks.push(tokio::spawn(async move {
|
||||
let _permit = task_semaphore.acquire().await;
|
||||
|
||||
let start = Instant::now();
|
||||
payphone_server
|
||||
.insert(&task_db)
|
||||
.await
|
||||
.expect("Failed to insert server");
|
||||
|
||||
let duration = start.elapsed();
|
||||
println!(
|
||||
"({i}) Inserted server {} in {:?}",
|
||||
payphone_server.id, duration
|
||||
);
|
||||
}));
|
||||
}
|
||||
|
||||
let bans = Arc::new(bans);
|
||||
let warns = Arc::new(warns);
|
||||
let reports = Arc::new(reports);
|
||||
|
||||
for (i, report) in reports.iter().enumerate() {
|
||||
let task_db = db.clone();
|
||||
let task_semaphore = semaphore.clone();
|
||||
let task_report = report.clone();
|
||||
let task_bans = bans.clone();
|
||||
let task_warns = warns.clone();
|
||||
let task_format = format.clone();
|
||||
|
||||
tasks.push(tokio::spawn(async move {
|
||||
let _permit = task_semaphore.acquire().await;
|
||||
|
||||
let start = Instant::now();
|
||||
|
||||
let resolution = if task_report.resolved == "t" {
|
||||
if task_bans.iter().any(|(_id, ban)| {
|
||||
ban.report_id
|
||||
.map(|report_id| task_report.id == report_id)
|
||||
.unwrap_or(false)
|
||||
}) {
|
||||
Some(ReportResolution::Ban)
|
||||
} else if task_warns
|
||||
.iter()
|
||||
.any(|(_id, warn)| warn.report_id == task_report.id)
|
||||
{
|
||||
Some(ReportResolution::Warn)
|
||||
} else {
|
||||
Some(ReportResolution::Skip)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let payphone_report = PayphoneReport {
|
||||
id: task_report.id,
|
||||
user_id: task_report.user_id,
|
||||
message: MessageData {
|
||||
id: u64_to_i64(task_report.message_id),
|
||||
call_id: 0,
|
||||
author_id: u64_to_i64(task_report.user_id),
|
||||
author_name: task_report.user_name,
|
||||
author_avatar_url: task_report.user_avatar_url,
|
||||
author_mask: None,
|
||||
content: task_report.message_content,
|
||||
attachments: vec![],
|
||||
sticker: None,
|
||||
propagation_message_id: None,
|
||||
propagation_channel_id: None,
|
||||
replying_to_message_id: None,
|
||||
is_deleted: false,
|
||||
is_edited: false,
|
||||
is_censored: false,
|
||||
},
|
||||
created_at: OffsetDateTime::parse(&task_report.created_at, &task_format)
|
||||
.expect("Failed to parse created_at")
|
||||
.to_utc(),
|
||||
created_by_ids: task_report
|
||||
.reported_by_id
|
||||
.map(|id| vec![id])
|
||||
.unwrap_or_default(),
|
||||
replying_to: None,
|
||||
reviewed_at: task_report.resolved_at.as_ref().map(|s| {
|
||||
OffsetDateTime::parse(s, &task_format)
|
||||
.expect("Failed to parse resolved_at")
|
||||
.to_utc()
|
||||
}),
|
||||
resolution,
|
||||
reviewed_by_id: task_report.resolved_by_id,
|
||||
};
|
||||
|
||||
payphone_report
|
||||
.insert(&task_db)
|
||||
.await
|
||||
.expect("Failed to insert report");
|
||||
|
||||
let duration = start.elapsed();
|
||||
println!(
|
||||
"({i}) Inserted report {} in {:?}",
|
||||
payphone_report.id, duration
|
||||
);
|
||||
}));
|
||||
}
|
||||
|
||||
for (i, ban) in bans.values().enumerate() {
|
||||
let task_semaphore = semaphore.clone();
|
||||
let task_format = format.clone();
|
||||
let task_db = db.clone();
|
||||
let task_reports = reports.clone();
|
||||
let task_ban = Arc::new(ban.clone());
|
||||
|
||||
tasks.push(tokio::spawn(async move {
|
||||
let _permit = task_semaphore.acquire().await;
|
||||
|
||||
let start = Instant::now();
|
||||
|
||||
let message = task_reports
|
||||
.iter()
|
||||
.find(|report| {
|
||||
task_ban
|
||||
.report_id
|
||||
.map(|id| id == report.id)
|
||||
.unwrap_or(false)
|
||||
})
|
||||
.map(|report| MessageData {
|
||||
id: u64_to_i64(report.message_id),
|
||||
call_id: 0,
|
||||
author_id: u64_to_i64(report.user_id),
|
||||
author_name: report.user_name.clone(),
|
||||
author_avatar_url: report.user_avatar_url.clone(),
|
||||
author_mask: None,
|
||||
content: report.message_content.clone(),
|
||||
attachments: vec![],
|
||||
sticker: None,
|
||||
propagation_message_id: None,
|
||||
propagation_channel_id: None,
|
||||
replying_to_message_id: None,
|
||||
is_deleted: false,
|
||||
is_edited: false,
|
||||
is_censored: false,
|
||||
});
|
||||
|
||||
let payphone_incident = UserIncident {
|
||||
incident_id: get_random_u64(),
|
||||
user_id: task_ban.user_id,
|
||||
report_id: task_ban.report_id,
|
||||
r#type: UserIncidentType::Ban,
|
||||
message,
|
||||
reason: task_ban.reason.clone(),
|
||||
created_at: OffsetDateTime::parse(&task_ban.created_at, &task_format)
|
||||
.expect("Failed to parse created_at")
|
||||
.to_utc(),
|
||||
created_by_id: Some(task_ban.created_by_id),
|
||||
duration: None,
|
||||
expires_at: task_ban.expires_at.as_ref().map(|expires_at| {
|
||||
OffsetDateTime::parse(expires_at, &task_format)
|
||||
.expect("Failed to parse expires_at")
|
||||
.to_utc()
|
||||
}),
|
||||
};
|
||||
|
||||
payphone_incident
|
||||
.insert(&task_db)
|
||||
.await
|
||||
.expect("Failed to insert ban");
|
||||
|
||||
let duration = start.elapsed();
|
||||
println!(
|
||||
"({i}) Inserted incident (ban) {} in {:?}",
|
||||
payphone_incident.incident_id, duration
|
||||
);
|
||||
}));
|
||||
}
|
||||
|
||||
for (i, warn) in warns.values().enumerate() {
|
||||
let task_semaphore = semaphore.clone();
|
||||
let task_format = format.clone();
|
||||
let task_db = db.clone();
|
||||
let task_reports = reports.clone();
|
||||
let task_warn = Arc::new(warn.clone());
|
||||
|
||||
tasks.push(tokio::spawn(async move {
|
||||
let _permit = task_semaphore.acquire().await;
|
||||
|
||||
let start = Instant::now();
|
||||
|
||||
let message = task_reports
|
||||
.iter()
|
||||
.find(|report| report.id == task_warn.report_id)
|
||||
.map(|report| MessageData {
|
||||
id: u64_to_i64(report.message_id),
|
||||
call_id: 0,
|
||||
author_id: u64_to_i64(report.user_id),
|
||||
author_name: report.user_name.clone(),
|
||||
author_avatar_url: report.user_avatar_url.clone(),
|
||||
author_mask: None,
|
||||
content: report.message_content.clone(),
|
||||
attachments: vec![],
|
||||
sticker: None,
|
||||
propagation_message_id: None,
|
||||
propagation_channel_id: None,
|
||||
replying_to_message_id: None,
|
||||
is_deleted: false,
|
||||
is_edited: false,
|
||||
is_censored: false,
|
||||
});
|
||||
|
||||
let payphone_incident = UserIncident {
|
||||
incident_id: get_random_u64(),
|
||||
user_id: task_warn.user_id,
|
||||
report_id: Some(task_warn.report_id),
|
||||
r#type: UserIncidentType::Warning,
|
||||
message,
|
||||
reason: task_warn.reason.clone(),
|
||||
created_at: OffsetDateTime::parse(&task_warn.created_at, &task_format)
|
||||
.expect("Failed to parse created_at")
|
||||
.to_utc(),
|
||||
created_by_id: Some(task_warn.created_by_id),
|
||||
duration: None,
|
||||
expires_at: None,
|
||||
};
|
||||
|
||||
payphone_incident
|
||||
.insert(&task_db)
|
||||
.await
|
||||
.expect("Failed to insert warn");
|
||||
|
||||
let duration = start.elapsed();
|
||||
println!(
|
||||
"({i}) Inserted incident (warn) {} in {:?}",
|
||||
payphone_incident.incident_id, duration
|
||||
);
|
||||
}));
|
||||
}
|
||||
|
||||
for task in tasks {
|
||||
task.await.expect("Failed to join task");
|
||||
}
|
||||
}
|
||||
+3
-10
@@ -196,21 +196,14 @@ pub enum Commands {
|
||||
database_addr: String,
|
||||
|
||||
/// The username to use when connecting to the database.
|
||||
#[arg(
|
||||
long,
|
||||
env = "PAYPHONE_DATABASE_USERNAME",
|
||||
value_name = "USERNAME"
|
||||
)]
|
||||
#[arg(long, env = "PAYPHONE_DATABASE_USERNAME", value_name = "USERNAME")]
|
||||
database_username: Option<String>,
|
||||
|
||||
/// The password to use when connecting to the database.
|
||||
#[arg(
|
||||
long,
|
||||
env = "PAYPHONE_DATABASE_PASSWORD",
|
||||
value_name = "PASSWORD"
|
||||
)]
|
||||
#[arg(long, env = "PAYPHONE_DATABASE_PASSWORD", value_name = "PASSWORD")]
|
||||
database_password: Option<String>,
|
||||
|
||||
/// The current shard being run.
|
||||
#[arg(
|
||||
long,
|
||||
short,
|
||||
|
||||
@@ -144,7 +144,7 @@ CREATE TABLE IF NOT EXISTS payphone.user_incidents (
|
||||
expires_at TIMESTAMP,
|
||||
duration ASCII,
|
||||
|
||||
PRIMARY KEY (user_id)
|
||||
PRIMARY KEY (user_id, incident_id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS payphone.server_incidents (
|
||||
|
||||
+25
-1
@@ -2,6 +2,7 @@ use std::{fmt::Debug, sync::Arc};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use thiserror::Error;
|
||||
use twilight_cache_inmemory::{DefaultInMemoryCache, ResourceType};
|
||||
use twilight_gateway::{Event, EventTypeFlags, Intents, Shard, ShardId, StreamExt};
|
||||
use twilight_http::Client;
|
||||
|
||||
@@ -88,11 +89,12 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Bot<T: Send + Sync = ()> {
|
||||
pub struct Bot<T: Send + Sync> {
|
||||
shard: ShardId,
|
||||
state: Arc<T>,
|
||||
prefixes: Arc<dyn PrefixGetter<T> + Send + Sync>,
|
||||
intents: Intents,
|
||||
cached_resource_types: Vec<ResourceType>,
|
||||
commands: Vec<Arc<dyn Command<T>>>,
|
||||
components: Vec<Arc<dyn Component<T>>>,
|
||||
on_ready_handlers: Vec<Arc<dyn OnReadyHandler<T>>>,
|
||||
@@ -113,6 +115,7 @@ impl<T: 'static + Send + Sync> Bot<T> {
|
||||
state: Arc::new(state),
|
||||
prefixes: Arc::new(prefixes),
|
||||
intents: Intents::empty(),
|
||||
cached_resource_types: Vec::new(),
|
||||
commands: Vec::new(),
|
||||
components: Vec::new(),
|
||||
on_ready_handlers: Vec::new(),
|
||||
@@ -147,6 +150,14 @@ impl<T: 'static + Send + Sync> Bot<T> {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn cache_resource_type(mut self, resource_type: ResourceType) -> Self {
|
||||
if !self.cached_resource_types.contains(&resource_type) {
|
||||
self.cached_resource_types.push(resource_type);
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn on_ready(mut self, handler: impl OnReadyHandler<T> + 'static) -> Self {
|
||||
self.on_ready_handlers.push(Arc::new(handler));
|
||||
self
|
||||
@@ -219,6 +230,16 @@ impl<T: 'static + Send + Sync> Bot<T> {
|
||||
|
||||
let bot = Arc::new(self);
|
||||
|
||||
let mut resource_types = ResourceType::empty();
|
||||
|
||||
for resource_type in &bot.cached_resource_types {
|
||||
resource_types.insert(*resource_type);
|
||||
}
|
||||
|
||||
let cache = DefaultInMemoryCache::builder()
|
||||
.resource_types(resource_types)
|
||||
.build();
|
||||
|
||||
let runtime = Arc::new(Runtime {
|
||||
http,
|
||||
app,
|
||||
@@ -226,6 +247,7 @@ impl<T: 'static + Send + Sync> Bot<T> {
|
||||
state: bot.state.clone(),
|
||||
prefixes: bot.prefixes.clone(),
|
||||
commands: bot.commands.clone(),
|
||||
cache,
|
||||
});
|
||||
|
||||
let mut gateway = Shard::new(bot.shard, token.to_string(), bot.intents);
|
||||
@@ -236,6 +258,8 @@ impl<T: 'static + Send + Sync> Bot<T> {
|
||||
let task_bot = bot.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
task_rt.cache.update(&event);
|
||||
|
||||
match event {
|
||||
Event::Ready(event) => {
|
||||
for handler in &task_bot.on_ready_handlers {
|
||||
|
||||
Vendored
-3
@@ -1,3 +0,0 @@
|
||||
pub struct RedisCache {
|
||||
|
||||
}
|
||||
@@ -33,6 +33,9 @@ pub enum CommandError {
|
||||
|
||||
#[error(transparent)]
|
||||
DiscordDeserializationFailed(#[from] DeserializeBodyError),
|
||||
|
||||
#[error("The cache's state was inconsistent.")]
|
||||
CacheStateInconsistent,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
||||
@@ -158,7 +158,6 @@
|
||||
//! ```
|
||||
|
||||
pub mod bot;
|
||||
pub mod cache;
|
||||
pub mod commands;
|
||||
pub mod events;
|
||||
pub mod interactions;
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use twilight_cache_inmemory::InMemoryCache;
|
||||
use twilight_http::Client;
|
||||
use twilight_model::oauth::Application;
|
||||
|
||||
@@ -45,6 +46,8 @@ pub struct Runtime<T> {
|
||||
|
||||
/// The application state.
|
||||
pub state: Arc<T>,
|
||||
|
||||
pub cache: InMemoryCache,
|
||||
}
|
||||
|
||||
impl<T> Runtime<T> {
|
||||
|
||||
@@ -3,4 +3,8 @@ idiot
|
||||
stupid
|
||||
dumb
|
||||
moron
|
||||
fool
|
||||
fool
|
||||
stfu
|
||||
syfm
|
||||
sybau
|
||||
gtfo
|
||||
@@ -75,6 +75,8 @@ goonbeam
|
||||
goonbeaming
|
||||
gooncream
|
||||
gooncreaming
|
||||
goonbud
|
||||
goonbuds
|
||||
boty hole
|
||||
suck my pp
|
||||
ass cracker
|
||||
@@ -97,4 +99,23 @@ whre
|
||||
tickle your pickle
|
||||
tickle yr pickle
|
||||
tickle his pickle
|
||||
tickle my pickle
|
||||
tickle my pickle
|
||||
peg
|
||||
peged
|
||||
peging
|
||||
butbang
|
||||
butbanged
|
||||
eat my ass
|
||||
eat my ahh
|
||||
eat mi ass
|
||||
eat my ahh
|
||||
eat my bum
|
||||
eat mi bum
|
||||
ima touch you
|
||||
ima touch u
|
||||
il touch you
|
||||
il touch u
|
||||
im going to touch you
|
||||
im going to touch u
|
||||
im gonna touch you
|
||||
im gonna touch u
|
||||
@@ -32,15 +32,17 @@ dumb fck
|
||||
dumb fuk
|
||||
dumb fuh
|
||||
dumb fuhk
|
||||
stfu
|
||||
shut the fvck up
|
||||
shut the fck up
|
||||
shut the fuck up
|
||||
syfm
|
||||
shut your fucking mouth
|
||||
sybau
|
||||
shut your fcking mouth
|
||||
shut your fvcking mouth
|
||||
shut your bitch as up
|
||||
shut your bih as up
|
||||
gtfo
|
||||
get the fuck out
|
||||
get the fvck out
|
||||
get the fck out
|
||||
get tfo
|
||||
blackie
|
||||
motherfucker
|
||||
mf
|
||||
motherfucker
|
||||
@@ -8,7 +8,9 @@ use crate::{
|
||||
Command, CommandError,
|
||||
arguments::{Argument, GreedyString, ParseArguments},
|
||||
context::CommandContext,
|
||||
permissions::Permission,
|
||||
},
|
||||
groups::servers::permissions::{GuildOnlyPermission, ServerModeratorPermission},
|
||||
state::State,
|
||||
};
|
||||
|
||||
@@ -37,6 +39,13 @@ impl Command<State> for PrefixCommand {
|
||||
}]
|
||||
}
|
||||
|
||||
fn permissions(&self) -> Vec<Box<dyn Permission<State> + Send + Sync + 'static>> {
|
||||
vec![
|
||||
Box::new(GuildOnlyPermission),
|
||||
Box::new(ServerModeratorPermission),
|
||||
]
|
||||
}
|
||||
|
||||
async fn execute(
|
||||
&self,
|
||||
ctx: CommandContext<'_, State>,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use async_trait::async_trait;
|
||||
use twilight_model::guild::Permissions;
|
||||
|
||||
use crate::{
|
||||
framework::commands::{CommandError, context::CommandContext, permissions::Permission},
|
||||
@@ -11,7 +12,15 @@ pub struct GuildOnlyPermission;
|
||||
#[async_trait]
|
||||
impl Permission<State> for GuildOnlyPermission {
|
||||
async fn check(&self, ctx: &CommandContext<State>) -> Result<bool, CommandError> {
|
||||
Ok(ctx.event.guild_id.is_none())
|
||||
Ok(ctx.event.guild_id.is_some())
|
||||
}
|
||||
|
||||
async fn on_handle_denied(&self, ctx: &CommandContext<State>) -> Result<(), CommandError> {
|
||||
ctx.send()
|
||||
.content("❌ This command can only be used in a server.")
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +29,37 @@ pub struct ServerModeratorPermission;
|
||||
|
||||
#[async_trait]
|
||||
impl Permission<State> for ServerModeratorPermission {
|
||||
async fn check(&self, _ctx: &CommandContext<State>) -> Result<bool, CommandError> {
|
||||
Ok(true)
|
||||
async fn check(&self, ctx: &CommandContext<State>) -> Result<bool, CommandError> {
|
||||
if let Some(guild_id) = ctx.event.guild_id {
|
||||
let permissions = ctx
|
||||
.rt
|
||||
.cache
|
||||
.permissions()
|
||||
.root(ctx.event.author.id, guild_id);
|
||||
|
||||
if let Ok(permissions) = permissions {
|
||||
let guild = ctx
|
||||
.rt
|
||||
.cache
|
||||
.guild(guild_id)
|
||||
.ok_or(CommandError::CacheStateInconsistent)?;
|
||||
|
||||
Ok(permissions
|
||||
.contains(Permissions::MANAGE_CHANNELS | Permissions::MANAGE_MESSAGES)
|
||||
|| guild.owner_id() == ctx.event.author.id)
|
||||
} else {
|
||||
Err(CommandError::CacheStateInconsistent)
|
||||
}
|
||||
} else {
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
||||
async fn on_handle_denied(&self, ctx: &CommandContext<State>) -> Result<(), CommandError> {
|
||||
ctx.send()
|
||||
.content("❌ This command can only be run by a server moderator.\n-# 🛈 Moderators must have permission to manage messages and to manage channels.")
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
pub mod cli;
|
||||
pub mod constants;
|
||||
pub mod database;
|
||||
pub mod discord;
|
||||
pub mod framework;
|
||||
pub mod groups;
|
||||
pub mod logging;
|
||||
pub mod state;
|
||||
pub mod utils;
|
||||
+7
-1
@@ -113,6 +113,7 @@ use clap::Parser;
|
||||
use notify::{Event, EventKind, Watcher};
|
||||
use tokio::net::TcpListener;
|
||||
use tower_http::services::ServeDir;
|
||||
use twilight_cache_inmemory::ResourceType;
|
||||
use twilight_gateway::{Intents, ShardId};
|
||||
|
||||
use crate::{
|
||||
@@ -227,7 +228,7 @@ async fn start(
|
||||
.await
|
||||
.expect("Could not connect to the database!");
|
||||
|
||||
log::info!("Database connection established successfully!");
|
||||
log::info!("Database connection established successfully.");
|
||||
|
||||
let state = State { db };
|
||||
|
||||
@@ -235,6 +236,7 @@ async fn start(
|
||||
.shard(shard)
|
||||
.intent(Intents::GUILDS)
|
||||
.intent(Intents::GUILD_MESSAGES)
|
||||
.intent(Intents::GUILD_MEMBERS)
|
||||
.intent(Intents::MESSAGE_CONTENT)
|
||||
.command(HelpCommand)
|
||||
.command(CallCommand::new())
|
||||
@@ -243,6 +245,10 @@ async fn start(
|
||||
.command(FriendRequestCommand)
|
||||
.command(PrefixCommand)
|
||||
.component(HelpGoBackButton)
|
||||
.cache_resource_type(ResourceType::GUILD)
|
||||
.cache_resource_type(ResourceType::CHANNEL)
|
||||
.cache_resource_type(ResourceType::MEMBER)
|
||||
.cache_resource_type(ResourceType::ROLE)
|
||||
.on_ready(ReadyHandler)
|
||||
.on_reconnect(ReconnectHandler)
|
||||
.on_message(CallMessageHandler)
|
||||
|
||||
Reference in New Issue
Block a user