Skip to content

Commit

Permalink
Add shutdown function
Browse files Browse the repository at this point in the history
  • Loading branch information
tazz4843 committed Feb 3, 2024
1 parent e07c283 commit 65e9bc1
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 4 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions scripty_audio_handler/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ license = "EUPL-1.2"
ahash = "0.8"
dashmap = "5"
tracing = "0.1"
secrecy = "0.8"
backtrace = "0.3"
async-trait = "0.1"
parking_lot = "0.12"
Expand Down
21 changes: 21 additions & 0 deletions scripty_audio_handler/src/connect.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use ahash::RandomState;
use dashmap::DashMap;
use scripty_premium::PremiumTierList;
use secrecy::ExposeSecret;
use serenity::{
builder::{CreateWebhook, ExecuteWebhook},
model::id::{ChannelId, GuildId},
Expand Down Expand Up @@ -48,6 +49,10 @@ pub async fn connect_to_vc(
}
}
};
let Some(ref webhook_token) = webhook.token else {
return Err(Error::no_webhook_token());
};
let webhook_id = webhook.id;

// automatically leave after the specified time period
let premium_tier = scripty_premium::get_guild(guild_id.get()).await;
Expand Down Expand Up @@ -85,6 +90,22 @@ pub async fn connect_to_vc(
debug!(%guild_id, "muting call");
call.mute(true).await?;

debug!(%guild_id, "placing info into redis");
scripty_redis::run_transaction("SET", |f| {
f.arg("EX")
.arg(leave_delta + 5)
.arg(format!("voice:{{{}}}:webhook_token", guild_id))
.arg(webhook_token.expose_secret());
})
.await?;
scripty_redis::run_transaction("SET", |f| {
f.arg("EX")
.arg(leave_delta + 5)
.arg(format!("voice:{{{}}}:webhook_id", guild_id))
.arg(webhook_id.get());
})
.await?;

debug!(%guild_id, "initializing audio handler");
let handler = crate::AudioHandler::new(
guild_id,
Expand Down
21 changes: 21 additions & 0 deletions scripty_audio_handler/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,19 @@ pub struct Error {
pub enum ErrorKind {
Join(JoinError),
Database(sqlx::Error),
Redis(scripty_redis::TransactionError),
Serenity(serenity::Error),
NoWebhookToken,
}

impl Error {
pub fn no_webhook_token() -> Self {
Self {
kind: ErrorKind::NoWebhookToken,
backtrace: Backtrace::new_unresolved(),
}
}

pub fn is_timed_out(&self) -> bool {
matches!(self.kind, ErrorKind::Join(JoinError::TimedOut))
}
Expand Down Expand Up @@ -59,12 +68,24 @@ impl From<serenity::Error> for Error {
}
}

impl From<scripty_redis::TransactionError> for Error {
#[inline]
fn from(e: scripty_redis::TransactionError) -> Self {
Self {
kind: ErrorKind::Redis(e),
backtrace: Backtrace::new_unresolved(),
}
}
}

impl Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match &self.kind {
ErrorKind::Join(e) => write!(f, "JoinError: {}", e),
ErrorKind::Database(e) => write!(f, "DatabaseError: {}", e),
ErrorKind::Serenity(e) => write!(f, "SerenityError: {}", e),
ErrorKind::NoWebhookToken => write!(f, "No webhook token found"),
ErrorKind::Redis(e) => write!(f, "RedisError: {}", e),
}
}
}
Expand Down
19 changes: 19 additions & 0 deletions scripty_bot_utils/src/error/error_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,15 @@ impl From<scripty_audio_handler::Error> for Error {
scripty_audio_handler::ErrorKind::Join(e) => Self::join(e),
scripty_audio_handler::ErrorKind::Database(e) => Self::db(e),
scripty_audio_handler::ErrorKind::Serenity(e) => Self::serenity(e),
scripty_audio_handler::ErrorKind::Redis(scripty_redis::TransactionError::Redis(e)) => {
Self::redis(e)
}
scripty_audio_handler::ErrorKind::Redis(scripty_redis::TransactionError::Deadpool(
e,
)) => Self::redis_pool(e),
scripty_audio_handler::ErrorKind::NoWebhookToken => {
Self::custom("No webhook token found".to_string())
}
};
err.bt = e.backtrace;
err
Expand Down Expand Up @@ -344,3 +353,13 @@ impl From<GenericMessageError> for Error {
}
}
}

impl From<scripty_redis::TransactionError> for Error {
#[inline]
fn from(e: scripty_redis::TransactionError) -> Self {
match e {
scripty_redis::TransactionError::Deadpool(e) => Self::from(e),
scripty_redis::TransactionError::Redis(e) => Self::from(e),
}
}
}
1 change: 1 addition & 0 deletions scripty_commands/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ num-format = "0.4"
scripty_db = { path = "../scripty_db" }
scripty_i18n = { path = "../scripty_i18n" }
scripty_utils = { path = "../scripty_utils" }
scripty_redis = { path = "../scripty_redis" }
scripty_config = { path = "../scripty_config" }
scripty_automod = { path = "../scripty_automod" }
scripty_premium = { path = "../scripty_premium" }
Expand Down
87 changes: 83 additions & 4 deletions scripty_commands/src/cmds/admin/shutdown.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,96 @@
use poise::CreateReply;
use scripty_bot_utils::{Context, Error};
use serenity::{
all::{Webhook, WebhookId},
builder::ExecuteWebhook,
};

#[poise::command(prefix_command, hide_in_help, owners_only)]
pub async fn shutdown(ctx: Context<'_>) -> Result<(), Error> {
let msg = ctx.say("shutting down").await?;
let cfg = scripty_config::get_config();

let webhook_builder = ExecuteWebhook::new().content(format!(
"Scripty is shutting down for maintenance. If you have any questions, please feel free to \
join our support server at {}.",
cfg.support_invite
));

// iterate over all active voice connections and notify
let songbird = scripty_audio_handler::get_songbird();
for (guild_id, call) in songbird.iter() {
// leave the call
call.lock().await.leave().await?;
let calls = songbird.iter().map(|x| x.0).collect::<Vec<_>>();
msg.edit(
ctx,
CreateReply::new().content(format!(
"shutting down {} voice connections\ndone: 0 (0%), succeeded 0, failed 0",
calls.len()
)),
)
.await?;
let mut success = 0;
let mut error = 0;
let mut to_do = calls.len();
let total = calls.len();

for guild_id in calls {
let f = async {
// leave the call
songbird.remove(guild_id).await?;

// notify the guild

// need to fetch from redis
let webhook_id = scripty_redis::run_transaction::<u64>("GET", |f| {
f.arg(format!("voice:{{{}}}:webhook_id", guild_id));
})
.await
.map(WebhookId::new)?;
let webhook_token = scripty_redis::run_transaction::<String>("GET", |f| {
f.arg(format!("voice:{{{}}}:webhook_token", guild_id));
})
.await?;

// notify the guild
let hook = Webhook::from_id_with_token(ctx.http(), webhook_id, &webhook_token).await?;
hook.execute(&ctx.serenity_context().http, false, webhook_builder.clone())
.await?;

Result::<(), Error>::Ok(())
};

match f.await {
Ok(()) => success += 1,
Err(e) => {
error += 1;
error!("error while shutting down voice connection: {}", e);
}
}
to_do -= 1;

msg.edit(
ctx,
CreateReply::new().content(format!(
"shutting down {} voice connections\ndone: {} ({}%), succeeded {}, failed {}",
total,
total - to_do,
(total - to_do) * 100 / total,
success,
error
)),
)
.await?;
}

msg.edit(
ctx,
CreateReply::new().content("done shutting down voice connections, shutting down bot now"),
)
.await?;
ctx.data()
.shard_manager
.get()
.ok_or_else(|| Error::custom("shard manager not found".to_string()))?
.shutdown_all()
.await;

Ok(())
}

0 comments on commit 65e9bc1

Please sign in to comment.