Skip to content

some more minor fixes #76

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 20 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ lazy_static = "1.4.0"
log = "0.4.0"
env_logger = "0.7.1"
envy = "0.4"
indexmap = "1.3"
indexmap = "1.6"
45 changes: 30 additions & 15 deletions src/api.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,34 @@
use crate::{
commands::{Args, Result},
db::DB,
schema::roles,
};
use crate::{command_history::CommandHistory, commands::Args, db::DB, schema::roles, Error};
use diesel::prelude::*;
use serenity::{model::prelude::*, utils::parse_username};

/// Send a reply to the channel the message was received on.
pub(crate) fn send_reply(args: &Args, message: &str) -> Result<()> {
args.msg.channel_id.say(&args.cx, message)?;
pub(crate) fn send_reply(args: &Args, message: &str) -> Result<(), Error> {
if let Some(response_id) = response_exists(args) {
info!("editing message: {:?}", response_id);
args.msg
.channel_id
.edit_message(&args.cx, response_id, |msg| msg.content(message))?;
} else {
let command_id = args.msg.id;
let response = args.msg.channel_id.say(&args.cx, message)?;

let mut data = args.cx.data.write();
let history = data.get_mut::<CommandHistory>().unwrap();
history.insert(command_id, response.id);
}

Ok(())
}

fn response_exists(args: &Args) -> Option<MessageId> {
let data = args.cx.data.read();
let history = data.get::<CommandHistory>().unwrap();
history.get(&args.msg.id).cloned()
}

/// Determine if a member sending a message has the `Role`.
pub(crate) fn has_role(args: &Args, role: &RoleId) -> Result<bool> {
pub(crate) fn has_role(args: &Args, role: &RoleId) -> Result<bool, Error> {
Ok(args
.msg
.member
Expand All @@ -23,7 +38,7 @@ pub(crate) fn has_role(args: &Args, role: &RoleId) -> Result<bool> {
.contains(role))
}

fn check_permission(args: &Args, role: Option<String>) -> Result<bool> {
fn check_permission(args: &Args, role: Option<String>) -> Result<bool, Error> {
use std::str::FromStr;
if let Some(role_id) = role {
Ok(has_role(args, &RoleId::from(u64::from_str(&role_id)?))?)
Expand All @@ -33,7 +48,7 @@ fn check_permission(args: &Args, role: Option<String>) -> Result<bool> {
}

/// Return whether or not the user is a mod.
pub(crate) fn is_mod(args: &Args) -> Result<bool> {
pub(crate) fn is_mod(args: &Args) -> Result<bool, Error> {
let role = roles::table
.filter(roles::name.eq("mod"))
.first::<(i32, String, String)>(&DB.get()?)
Expand All @@ -42,7 +57,7 @@ pub(crate) fn is_mod(args: &Args) -> Result<bool> {
check_permission(args, role.map(|(_, role_id, _)| role_id))
}

pub(crate) fn is_wg_and_teams(args: &Args) -> Result<bool> {
pub(crate) fn is_wg_and_teams(args: &Args) -> Result<bool, Error> {
let role = roles::table
.filter(roles::name.eq("wg_and_teams"))
.first::<(i32, String, String)>(&DB.get()?)
Expand All @@ -54,7 +69,7 @@ pub(crate) fn is_wg_and_teams(args: &Args) -> Result<bool> {
/// Set slow mode for a channel.
///
/// A `seconds` value of 0 will disable slowmode
pub(crate) fn slow_mode(args: Args) -> Result<()> {
pub(crate) fn slow_mode(args: Args) -> Result<(), Error> {
use std::str::FromStr;

if is_mod(&args)? {
Expand All @@ -75,7 +90,7 @@ pub(crate) fn slow_mode(args: Args) -> Result<()> {
Ok(())
}

pub(crate) fn slow_mode_help(args: Args) -> Result<()> {
pub(crate) fn slow_mode_help(args: Args) -> Result<(), Error> {
let help_string = "
Set slowmode on a channel
```
Expand All @@ -99,7 +114,7 @@ will disable slowmode on the `#bot-usage` channel.";
/// Kick a user from the guild.
///
/// Requires the kick members permission
pub(crate) fn kick(args: Args) -> Result<()> {
pub(crate) fn kick(args: Args) -> Result<(), Error> {
if is_mod(&args)? {
let user_id = parse_username(
&args
Expand All @@ -117,7 +132,7 @@ pub(crate) fn kick(args: Args) -> Result<()> {
Ok(())
}

pub(crate) fn kick_help(args: Args) -> Result<()> {
pub(crate) fn kick_help(args: Args) -> Result<(), Error> {
let help_string = "
Kick a user from the guild
```
Expand Down
62 changes: 22 additions & 40 deletions src/ban.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,11 @@
use crate::{
api,
commands::{Args, Result},
db::DB,
schema::bans,
text::ban_message,
api, commands::Args, db::DB, schema::bans, text::ban_message, Error, SendSyncError, HOUR,
};
use diesel::prelude::*;
use serenity::{model::prelude::*, prelude::*, utils::parse_username};
use std::{
sync::atomic::{AtomicBool, Ordering},
thread::sleep,
time::{Duration, SystemTime},
};

const HOUR: u64 = 3600;
static UNBAN_THREAD_INITIALIZED: AtomicBool = AtomicBool::new(false);
use std::time::{Duration, SystemTime};

pub(crate) fn save_ban(user_id: String, guild_id: String, hours: u64) -> Result<()> {
pub(crate) fn save_ban(user_id: String, guild_id: String, hours: u64) -> Result<(), Error> {
info!("Recording ban for user {}", &user_id);
let conn = DB.get()?;
diesel::insert_into(bans::table)
Expand All @@ -33,7 +22,7 @@ pub(crate) fn save_ban(user_id: String, guild_id: String, hours: u64) -> Result<
Ok(())
}

pub(crate) fn save_unban(user_id: String, guild_id: String) -> Result<()> {
pub(crate) fn save_unban(user_id: String, guild_id: String) -> Result<(), Error> {
info!("Recording unban for user {}", &user_id);
let conn = DB.get()?;
diesel::update(bans::table)
Expand All @@ -48,37 +37,30 @@ pub(crate) fn save_unban(user_id: String, guild_id: String) -> Result<()> {
Ok(())
}

pub(crate) fn start_unban_thread(cx: Context) {
pub(crate) fn unban_users(cx: &Context) -> Result<(), SendSyncError> {
use std::str::FromStr;
if !UNBAN_THREAD_INITIALIZED.load(Ordering::SeqCst) {
UNBAN_THREAD_INITIALIZED.store(true, Ordering::SeqCst);
type SendSyncError = Box<dyn std::error::Error + Send + Sync>;
std::thread::spawn(move || -> std::result::Result<(), SendSyncError> {
loop {
let conn = DB.get()?;
let to_unban = bans::table
.filter(
bans::unbanned
.eq(false)
.and(bans::end_time.le(SystemTime::now())),
)
.load::<(i32, String, String, bool, SystemTime, SystemTime)>(&conn)?;

for row in &to_unban {
let guild_id = GuildId::from(u64::from_str(&row.2)?);
info!("Unbanning user {}", &row.1);
guild_id.unban(&cx, u64::from_str(&row.1)?)?;
}
sleep(Duration::new(HOUR, 0));
}
});

let conn = DB.get()?;
let to_unban = bans::table
.filter(
bans::unbanned
.eq(false)
.and(bans::end_time.le(SystemTime::now())),
)
.load::<(i32, String, String, bool, SystemTime, SystemTime)>(&conn)?;

for row in &to_unban {
let guild_id = GuildId::from(u64::from_str(&row.2)?);
info!("Unbanning user {}", &row.1);
guild_id.unban(&cx, u64::from_str(&row.1)?)?;
}
Ok(())
}

/// Temporarily ban an user from the guild.
///
/// Requires the ban members permission
pub(crate) fn temp_ban(args: Args) -> Result<()> {
pub(crate) fn temp_ban(args: Args) -> Result<(), Error> {
let user_id = parse_username(
&args
.params
Expand Down Expand Up @@ -118,7 +100,7 @@ pub(crate) fn temp_ban(args: Args) -> Result<()> {
Ok(())
}

pub(crate) fn help(args: Args) -> Result<()> {
pub(crate) fn help(args: Args) -> Result<(), Error> {
let hours = 24;
let reason = "violating the code of conduct";

Expand Down
57 changes: 57 additions & 0 deletions src/command_history.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
use crate::{
commands::{Commands, PREFIX},
Error, SendSyncError, HOUR,
};
use indexmap::IndexMap;
use serenity::{model::prelude::*, prelude::*, utils::CustomMessage};
use std::time::Duration;

const MESSAGE_AGE_MAX: Duration = Duration::from_secs(HOUR);

pub(crate) struct CommandHistory;

impl TypeMapKey for CommandHistory {
type Value = IndexMap<MessageId, MessageId>;
}

pub(crate) fn replay_message(
cx: Context,
ev: MessageUpdateEvent,
cmds: &Commands,
) -> Result<(), Error> {
let age = ev.timestamp.and_then(|create| {
ev.edited_timestamp
.and_then(|edit| edit.signed_duration_since(create).to_std().ok())
});

if age.is_some() && age.unwrap() < MESSAGE_AGE_MAX {
let mut msg = CustomMessage::new();
msg.id(ev.id)
.channel_id(ev.channel_id)
.content(ev.content.unwrap_or_default());

let msg = msg.build();

if msg.content.starts_with(PREFIX) {
info!(
"sending edited message - {:?} {:?}",
msg.content, msg.author
);
cmds.execute(cx, &msg);
}
}

Ok(())
}

pub(crate) fn clear_command_history(cx: &Context) -> Result<(), SendSyncError> {
let mut data = cx.data.write();
let history = data.get_mut::<CommandHistory>().unwrap();

// always keep the last command in history
if history.len() > 0 {
info!("Clearing command history");
history.drain(..history.len() - 1);
}
Ok(())
}
Loading