Skip to content

Commit 5f39134

Browse files
committed
Evaluate different error cases and actually retry
1 parent b56f585 commit 5f39134

File tree

6 files changed

+155
-74
lines changed

6 files changed

+155
-74
lines changed

src/database/guild_schedule.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,15 +178,15 @@ impl Entry for GuildSchedule {
178178

179179
match db.client.hgetall(Self::key(db, id)).await {
180180
Ok(schedule) => Ok(Some(schedule)),
181-
Err(err) if *err.kind() == ErrorKind::NotFound => return Ok(None),
181+
Err(err) if *err.kind() == ErrorKind::NotFound => Ok(None),
182182
Err(err) => Err(err),
183183
}
184184
}
185185

186186
async fn delete(db: &Database, id: impl Into<Key> + Send + Sync) -> Result<Self, Error> {
187187
let id: Key = id.into();
188188

189-
let schedule = match Self::get(&db, id.clone()).await {
189+
let schedule = match Self::get(db, id.clone()).await {
190190
Ok(Some(schedule)) => schedule,
191191
Ok(None) => {
192192
return Err(Error::new(

src/error.rs

Lines changed: 17 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ use std::{
44
num::NonZeroU16,
55
};
66

7-
use async_repeater::RepeaterHandle;
87
use chrono::{DateTime, Utc};
98
use poise::serenity_prelude::{
109
Context, Error as SerenityError, HttpError as SerenityHttpError, JsonErrorCode, MessageBuilder, User,
@@ -16,8 +15,6 @@ use tracing::{info, instrument, warn};
1615

1716
use crate::{
1817
Settings,
19-
database::{Database, guild_schedule::GuildSchedule},
20-
schedule::Schedule,
2118
schedule_runner::{RunnerError, ScheduleAction},
2219
setting_banner::SetBannerError,
2320
settings::SettingsError,
@@ -165,11 +162,9 @@ where
165162
/// This is a needed as well as the normal error handling in [crate::error::on_error] because
166163
/// the scheduler is running in its own task
167164
#[instrument(skip_all)]
168-
pub async fn handle_schedule_error(
165+
pub async fn evaluate_schedule_error(
169166
error: &RunnerError,
170167
ctx: Context,
171-
repeater_handle: RepeaterHandle<Schedule>,
172-
db: Database,
173168
owners: HashSet<UserId>,
174169
) -> Result<ScheduleAction, Error> {
175170
let guild_id = error.schedule().guild_id();
@@ -180,7 +175,7 @@ pub async fn handle_schedule_error(
180175
let message = MessageBuilder::new()
181176
.push_bold("Error in guild: ")
182177
.push_mono_line_safe(&*guild_name)
183-
.push_codeblock(&*error.to_string(), Some("rust"))
178+
.push(&*error.to_string())
184179
.build();
185180

186181
dm_users(&ctx, owners.clone(), &message).await?;
@@ -192,18 +187,10 @@ pub async fn handle_schedule_error(
192187
match error_response.status_code {
193188
StatusCode::FORBIDDEN => {
194189
// the bot does not have permissions to change the banner.
195-
// remove guild from queue
196-
let _ = repeater_handle.remove(guild_id).await;
197-
db.delete::<GuildSchedule>(error.schedule().guild_id().get())
198-
.await?;
199190
warn!("Missing permissions to change banner for {guild_id}. Unscheduling.");
200191
return Ok(ScheduleAction::Abort);
201192
}
202193
StatusCode::NOT_FOUND => {
203-
let _ = repeater_handle.remove(guild_id).await;
204-
db.delete::<GuildSchedule>(error.schedule().guild_id().get())
205-
.await?;
206-
207194
if error_response.error.code == JsonErrorCode::UnknownChannel {
208195
warn!(
209196
"Channel {channel_id} does not exist in guild: {guild_id}. Unscheduling."
@@ -239,24 +226,17 @@ pub async fn handle_schedule_error(
239226
match error_response.status_code {
240227
StatusCode::FORBIDDEN => {
241228
// the bot does not have permissions to change the banner.
242-
// remove guild from queue
243-
let _ = repeater_handle.remove(guild_id).await;
244-
db.delete::<GuildSchedule>(error.schedule().guild_id().get())
245-
.await?;
246229
warn!(
247230
"Missing permissions to change banner for {guild_id}. Unscheduling."
248231
);
249232
return Ok(ScheduleAction::Abort);
250233
}
251234
StatusCode::NOT_FOUND => {
252-
let _ = repeater_handle.remove(guild_id).await;
253-
db.delete::<GuildSchedule>(error.schedule().guild_id().get())
254-
.await?;
255235
warn!("Guild does not exist: {guild_id}. Unscheduling.");
256236
return Ok(ScheduleAction::Abort);
257237
}
258238
StatusCode::GATEWAY_TIMEOUT => {
259-
warn!("Gateway timed out. Retrying once.");
239+
warn!("Gateway timed out. Retrying");
260240
return Ok(ScheduleAction::RetrySameImage);
261241
}
262242
_ => tracing::error!("unsuccessful http request: {error_response:?}"),
@@ -269,39 +249,41 @@ pub async fn handle_schedule_error(
269249
}
270250
}
271251
}
272-
SetBannerError::CouldNotPickAUrl => warn!("guild_id={guild_id}: 'Could not pick a url'"),
273-
SetBannerError::CouldNotDeterminFileExtension => {
274-
warn!("guild_id={guild_id}: 'Could not determine file extenstion'");
252+
SetBannerError::CouldNotPickAUrl => {
253+
warn!("guild_id={guild_id}: 'Could not pick a url'. RNG failed")
254+
}
255+
SetBannerError::CouldNotDeterminFileExtension(url) => {
256+
warn!("guild_id={guild_id}: 'Could not determine file extenstion. url={url}'");
257+
return Ok(ScheduleAction::RetryNewImage);
275258
}
276259
SetBannerError::MissingBannerFeature => {
277-
let _ = repeater_handle.remove(guild_id).await;
278-
db.delete::<GuildSchedule>(error.schedule().guild_id().get())
279-
.await?;
280-
281260
let partial_guild = guild_id.to_partial_guild(&ctx.http).await?;
282261
let guild_owner = partial_guild.owner_id;
283-
info!(
262+
warn!(
284263
"Letting owner={guild_owner} of guild={guild_id} know about the missing banner feature"
285264
);
286265

287266
dm_user(&ctx, guild_owner, "Server has lost the required boost level. Stopping schedule. You can restart the bot after gaining the required boost level.").await?;
267+
return Ok(ScheduleAction::Abort);
288268
}
289269
SetBannerError::MissingAnimatedBannerFeature(url, ..) => {
290270
warn!(
291271
"guild_id={guild_id} with channel={channel_id} was trying to set an animated banner but does not have the feature. url={url}"
292272
);
293273
let partial_guild = guild_id.to_partial_guild(&ctx.http).await?;
294274
let guild_owner = partial_guild.owner_id;
295-
info!(
275+
warn!(
296276
"Letting owner={guild_owner} of guild={guild_id} know about the missing animated banner feature"
297277
);
298278

299279
dm_user(&ctx, guild_owner, &format!("Tried to set an animated banner but the server '{}' does not have the required boost level for animated banners", partial_guild.name)).await?;
280+
return Ok(ScheduleAction::RetryNewImage);
300281
}
301282
SetBannerError::ImageIsEmpty(url, ..) => {
302283
warn!(
303284
"guild_id={guild_id} with channel={channel_id} has selected an image with 0 bytes. url={url}"
304285
);
286+
return Ok(ScheduleAction::RetryNewImage);
305287
}
306288
SetBannerError::ImageIsTooBig(url, ..) => {
307289
warn!(
@@ -315,16 +297,19 @@ pub async fn handle_schedule_error(
315297
);
316298

317299
dm_user(&ctx, guild_owner, &format!("The channel you've set contains an image that is too big for discord. Maximum size is 10mb. The image is: {url}")).await?;
300+
return Ok(ScheduleAction::RetryNewImage);
318301
}
319302
SetBannerError::ImageUnkownSize(url, ..) => {
320303
warn!(
321304
"guild_id={guild_id} with channel={channel_id} has selected an image with unknown size. url={url}"
322305
);
306+
return Ok(ScheduleAction::RetryNewImage);
323307
}
324308
SetBannerError::Base64Encoding(url, ..) => {
325309
warn!(
326310
"guild_id={guild_id} with channel={channel_id} has selected an image wich could be encoded into base 64. url={url}"
327311
);
312+
return Ok(ScheduleAction::RetryNewImage);
328313
}
329314
}
330315
}

src/schedule_runner.rs

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::num::NonZeroUsize;
22

3-
use poise::serenity_prelude::GuildId;
3+
use poise::serenity_prelude::{GuildId, Message};
44
use tokio::pin;
55
use tokio_stream::StreamExt;
66
use tracing::{debug, error, instrument};
@@ -11,7 +11,7 @@ use crate::{
1111
database::{Database, guild_schedule::GuildSchedule},
1212
finding_media::find_media_in_channel,
1313
schedule::Schedule,
14-
setting_banner::RandomBanner,
14+
setting_banner::{BannerFromUrl, RandomBanner, SetBannerError},
1515
};
1616

1717
pub struct ScheduleRunner {
@@ -36,12 +36,34 @@ impl ScheduleRunner {
3636
}
3737
}
3838

39+
/// This is run every time a schedule is due
3940
#[instrument(skip_all)]
40-
pub async fn run(self) -> Result<Url, RunnerError> {
41+
pub async fn run(
42+
&self,
43+
avoid_list: &[Url],
44+
pick_this: Option<(Url, Message)>,
45+
) -> Result<Url, RunnerError> {
4146
let schedule = self.schedule.clone();
4247
let mut guild_id = schedule.guild_id();
43-
let channel = schedule.channel_id();
4448

49+
// if we have an override image given, just use it and skip the rest of the function
50+
if let Some((url, message)) = pick_this {
51+
debug!("Using override image: {url}");
52+
guild_id
53+
.set_banner_from_url_and_message(self.ctx.http.clone(), &self.http_client, &url, &message)
54+
.await
55+
.map_err(|err| RunnerError::new(err.into(), guild_id, self.schedule.clone()))?;
56+
debug!("Inserting schedule into database");
57+
let schedule = GuildSchedule::from(schedule.clone());
58+
59+
self.database
60+
.insert(&schedule, schedule.guild_id())
61+
.await
62+
.map_err(|err| RunnerError::new(err.into(), guild_id, self.schedule.clone()))?;
63+
return Ok(url);
64+
};
65+
66+
let channel = schedule.channel_id();
4567
let limit = schedule.message_limit().map_or(usize::MAX, NonZeroUsize::get);
4668

4769
debug!("Fetching images, limited to {} messages", limit);
@@ -50,7 +72,7 @@ impl ScheduleRunner {
5072
pin!(stream_of_media);
5173

5274
let mut images = Vec::new();
53-
while let Some(url) = stream_of_media
75+
while let Some(url_message_pair) = stream_of_media
5476
.try_next()
5577
.await
5678
.map_err(|err| RunnerError::new(err.into(), guild_id, self.schedule.clone()))?
@@ -61,7 +83,11 @@ impl ScheduleRunner {
6183
)
6284
})
6385
{
64-
images.push(url);
86+
if avoid_list.contains(&url_message_pair.0) {
87+
continue;
88+
}
89+
90+
images.push(url_message_pair);
6591
}
6692

6793
let img_count = images.len();
@@ -85,9 +111,13 @@ impl ScheduleRunner {
85111

86112
#[derive(Debug)]
87113
pub enum ScheduleAction {
114+
/// Everything's fine
88115
Continue,
116+
/// Use this url next run
89117
RetrySameImage,
118+
/// Avoid this image
90119
RetryNewImage,
120+
/// OH GOD WHY
91121
Abort,
92122
}
93123

@@ -117,6 +147,26 @@ impl RunnerError {
117147
&self.schedule
118148
}
119149

150+
pub fn attempted_url_and_message(&self) -> (Option<Url>, Option<Box<Message>>) {
151+
match &self.source {
152+
Error::SetBanner(set_banner_error) => match set_banner_error {
153+
SetBannerError::Transport(_) => (None, None),
154+
SetBannerError::DiscordApi(_) => (None, None),
155+
SetBannerError::CouldNotPickAUrl => (None, None),
156+
SetBannerError::CouldNotDeterminFileExtension(url) => (Some(url.clone()), None),
157+
SetBannerError::MissingBannerFeature => (None, None),
158+
SetBannerError::MissingAnimatedBannerFeature(url, message) => {
159+
(Some(url.clone()), Some(message.clone()))
160+
}
161+
SetBannerError::ImageIsEmpty(url, message) => (Some(url.clone()), Some(message.clone())),
162+
SetBannerError::ImageIsTooBig(url, message) => (Some(url.clone()), Some(message.clone())),
163+
SetBannerError::ImageUnkownSize(url, message) => (Some(url.clone()), Some(message.clone())),
164+
SetBannerError::Base64Encoding(url, message) => (Some(url.clone()), Some(message.clone())),
165+
},
166+
_ => (None, None),
167+
}
168+
}
169+
120170
pub fn source(&self) -> &crate::Error {
121171
&self.source
122172
}

src/setting_banner.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ pub enum SetBannerError {
2626
CouldNotPickAUrl,
2727

2828
#[error("Could not determin file extenstion on image")]
29-
CouldNotDeterminFileExtension,
29+
CouldNotDeterminFileExtension(Url),
3030

3131
#[error("Missing 'banner' feature")]
3232
MissingBannerFeature,
@@ -74,7 +74,7 @@ impl BannerFromUrl for GuildId {
7474
.path()
7575
.split('.')
7676
.next_back()
77-
.ok_or_else(|| SetBannerError::CouldNotDeterminFileExtension)?;
77+
.ok_or_else(|| SetBannerError::CouldNotDeterminFileExtension(url.clone()))?;
7878

7979
debug!("Found extension: {extension}");
8080
// Disable banner feature check when in dev environment

0 commit comments

Comments
 (0)