Handle deleted channels in sender
This commit is contained in:
parent
82dab53744
commit
5ee9094bac
@ -237,11 +237,11 @@ impl Into<CreateEmbed> for Embed {
|
|||||||
pub struct Reminder {
|
pub struct Reminder {
|
||||||
id: u32,
|
id: u32,
|
||||||
|
|
||||||
channel_id: u64,
|
channel_id: Option<u64>,
|
||||||
webhook_id: Option<u64>,
|
webhook_id: Option<u64>,
|
||||||
webhook_token: Option<String>,
|
webhook_token: Option<String>,
|
||||||
|
|
||||||
channel_paused: bool,
|
channel_paused: Option<bool>,
|
||||||
channel_paused_until: Option<NaiveDateTime>,
|
channel_paused_until: Option<NaiveDateTime>,
|
||||||
enabled: bool,
|
enabled: bool,
|
||||||
|
|
||||||
@ -297,7 +297,7 @@ SELECT
|
|||||||
reminders.`username` AS username
|
reminders.`username` AS username
|
||||||
FROM
|
FROM
|
||||||
reminders
|
reminders
|
||||||
INNER JOIN
|
LEFT JOIN
|
||||||
channels
|
channels
|
||||||
ON
|
ON
|
||||||
reminders.channel_id = channels.id
|
reminders.channel_id = channels.id
|
||||||
@ -310,7 +310,6 @@ WHERE
|
|||||||
reminders
|
reminders
|
||||||
WHERE
|
WHERE
|
||||||
reminders.`utc_time` <= NOW() AND
|
reminders.`utc_time` <= NOW() AND
|
||||||
reminders.`channel_id` IS NOT NULL AND
|
|
||||||
`status` = 'pending' AND
|
`status` = 'pending' AND
|
||||||
(
|
(
|
||||||
reminders.`interval_seconds` IS NOT NULL
|
reminders.`interval_seconds` IS NOT NULL
|
||||||
@ -344,7 +343,10 @@ WHERE
|
|||||||
|
|
||||||
async fn reset_webhook(&self, pool: impl Executor<'_, Database = Database> + Copy) {
|
async fn reset_webhook(&self, pool: impl Executor<'_, Database = Database> + Copy) {
|
||||||
let _ = sqlx::query!(
|
let _ = sqlx::query!(
|
||||||
"UPDATE channels SET webhook_id = NULL, webhook_token = NULL WHERE channel = ?",
|
"
|
||||||
|
UPDATE channels SET webhook_id = NULL, webhook_token = NULL
|
||||||
|
WHERE channel = ?
|
||||||
|
",
|
||||||
self.channel_id
|
self.channel_id
|
||||||
)
|
)
|
||||||
.execute(pool)
|
.execute(pool)
|
||||||
@ -416,7 +418,9 @@ WHERE
|
|||||||
self.set_sent(pool).await;
|
self.set_sent(pool).await;
|
||||||
} else {
|
} else {
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"UPDATE reminders SET `utc_time` = ? WHERE `id` = ?",
|
"
|
||||||
|
UPDATE reminders SET `utc_time` = ? WHERE `id` = ?
|
||||||
|
",
|
||||||
updated_reminder_time.with_timezone(&Utc),
|
updated_reminder_time.with_timezone(&Utc),
|
||||||
self.id
|
self.id
|
||||||
)
|
)
|
||||||
@ -449,7 +453,10 @@ WHERE
|
|||||||
|
|
||||||
if *LOG_TO_DATABASE {
|
if *LOG_TO_DATABASE {
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"INSERT INTO stat (type, reminder_id, message) VALUES ('reminder_failed', ?, ?)",
|
"
|
||||||
|
INSERT INTO stat (type, reminder_id, message)
|
||||||
|
VALUES ('reminder_failed', ?, ?)
|
||||||
|
",
|
||||||
self.id,
|
self.id,
|
||||||
message,
|
message,
|
||||||
)
|
)
|
||||||
@ -462,7 +469,10 @@ WHERE
|
|||||||
async fn log_success(&self, pool: impl Executor<'_, Database = Database> + Copy) {
|
async fn log_success(&self, pool: impl Executor<'_, Database = Database> + Copy) {
|
||||||
if *LOG_TO_DATABASE {
|
if *LOG_TO_DATABASE {
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"INSERT INTO stat (type, reminder_id) VALUES ('reminder_sent', ?)",
|
"
|
||||||
|
INSERT INTO stat (type, reminder_id)
|
||||||
|
VALUES ('reminder_sent', ?)
|
||||||
|
",
|
||||||
self.id,
|
self.id,
|
||||||
)
|
)
|
||||||
.execute(pool)
|
.execute(pool)
|
||||||
@ -491,7 +501,11 @@ WHERE
|
|||||||
message: &'static str,
|
message: &'static str,
|
||||||
) {
|
) {
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"UPDATE reminders SET `status` = 'failed', `status_message` = ? WHERE `id` = ?",
|
"
|
||||||
|
UPDATE reminders
|
||||||
|
SET `status` = 'failed', `status_message` = ?, `status_change_time` = NOW()
|
||||||
|
WHERE `id` = ?
|
||||||
|
",
|
||||||
message,
|
message,
|
||||||
self.id
|
self.id
|
||||||
)
|
)
|
||||||
@ -501,7 +515,9 @@ WHERE
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn pin_message<M: Into<u64>>(&self, message_id: M, http: impl AsRef<Http>) {
|
async fn pin_message<M: Into<u64>>(&self, message_id: M, http: impl AsRef<Http>) {
|
||||||
let _ = http.as_ref().pin_message(self.channel_id, message_id.into(), None).await;
|
if let Some(channel_id) = self.channel_id {
|
||||||
|
let _ = http.as_ref().pin_message(channel_id, message_id.into(), None).await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send(
|
pub async fn send(
|
||||||
@ -511,10 +527,11 @@ WHERE
|
|||||||
) {
|
) {
|
||||||
async fn send_to_channel(
|
async fn send_to_channel(
|
||||||
cache_http: impl CacheHttp,
|
cache_http: impl CacheHttp,
|
||||||
|
channel_id: u64,
|
||||||
reminder: &Reminder,
|
reminder: &Reminder,
|
||||||
embed: Option<CreateEmbed>,
|
embed: Option<CreateEmbed>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let channel = ChannelId(reminder.channel_id).to_channel(&cache_http).await;
|
let channel = ChannelId(channel_id).to_channel(&cache_http).await;
|
||||||
|
|
||||||
match channel {
|
match channel {
|
||||||
Ok(Channel::Guild(channel)) => {
|
Ok(Channel::Guild(channel)) => {
|
||||||
@ -546,6 +563,7 @@ WHERE
|
|||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Channel::Private(channel)) => {
|
Ok(Channel::Private(channel)) => {
|
||||||
match channel
|
match channel
|
||||||
.send_message(&cache_http.http(), |m| {
|
.send_message(&cache_http.http(), |m| {
|
||||||
@ -575,7 +593,9 @@ WHERE
|
|||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
|
|
||||||
_ => Err(Error::Other("Channel not of valid type")),
|
_ => Err(Error::Other("Channel not of valid type")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -630,124 +650,151 @@ WHERE
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.enabled
|
match self.channel_id {
|
||||||
&& !(self.channel_paused
|
Some(channel_id) => {
|
||||||
&& self
|
if self.enabled
|
||||||
.channel_paused_until
|
&& !(self.channel_paused.unwrap_or(false)
|
||||||
.map_or(true, |inner| inner >= Utc::now().naive_local()))
|
&& self
|
||||||
{
|
.channel_paused_until
|
||||||
let _ = sqlx::query!(
|
.map_or(true, |inner| inner >= Utc::now().naive_local()))
|
||||||
"UPDATE `channels` SET paused = 0, paused_until = NULL WHERE `channel` = ?",
|
{
|
||||||
self.channel_id
|
let _ = sqlx::query!(
|
||||||
)
|
"
|
||||||
.execute(pool)
|
UPDATE `channels`
|
||||||
.await;
|
SET paused = 0, paused_until = NULL
|
||||||
|
WHERE `channel` = ?
|
||||||
|
",
|
||||||
|
self.channel_id
|
||||||
|
)
|
||||||
|
.execute(pool)
|
||||||
|
.await;
|
||||||
|
|
||||||
let embed = Embed::from_id(pool, self.id).await.map(|e| e.into());
|
let embed = Embed::from_id(pool, self.id).await.map(|e| e.into());
|
||||||
|
|
||||||
let result = if let (Some(webhook_id), Some(webhook_token)) =
|
let result = if let (Some(webhook_id), Some(webhook_token)) =
|
||||||
(self.webhook_id, &self.webhook_token)
|
(self.webhook_id, &self.webhook_token)
|
||||||
{
|
{
|
||||||
let webhook_res =
|
let webhook_res = cache_http
|
||||||
cache_http.http().get_webhook_with_token(webhook_id, webhook_token).await;
|
.http()
|
||||||
|
.get_webhook_with_token(webhook_id, webhook_token)
|
||||||
|
.await;
|
||||||
|
|
||||||
if let Ok(webhook) = webhook_res {
|
if let Ok(webhook) = webhook_res {
|
||||||
send_to_webhook(cache_http, &self, webhook, embed).await
|
send_to_webhook(cache_http, &self, webhook, embed).await
|
||||||
} else {
|
} else {
|
||||||
warn!("Webhook vanished for reminder {}: {:?}", self.id, webhook_res);
|
warn!("Webhook vanished for reminder {}: {:?}", self.id, webhook_res);
|
||||||
|
|
||||||
self.reset_webhook(pool).await;
|
self.reset_webhook(pool).await;
|
||||||
send_to_channel(cache_http, &self, embed).await
|
send_to_channel(cache_http, channel_id, &self, embed).await
|
||||||
}
|
|
||||||
} else {
|
|
||||||
send_to_channel(cache_http, &self, embed).await
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Err(e) = result {
|
|
||||||
if let Error::Http(error) = e {
|
|
||||||
if let HttpError::UnsuccessfulRequest(http_error) = *error {
|
|
||||||
match http_error.error.code {
|
|
||||||
10003 => {
|
|
||||||
self.log_error(
|
|
||||||
pool,
|
|
||||||
"Could not be sent as channel does not exist",
|
|
||||||
None::<&'static str>,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
self.set_failed(
|
|
||||||
pool,
|
|
||||||
"Could not be sent as channel does not exist",
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
10004 => {
|
|
||||||
self.log_error(
|
|
||||||
pool,
|
|
||||||
"Could not be sent as guild does not exist",
|
|
||||||
None::<&'static str>,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
self.set_failed(pool, "Could not be sent as guild does not exist")
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
50001 => {
|
|
||||||
self.log_error(
|
|
||||||
pool,
|
|
||||||
"Could not be sent as missing access",
|
|
||||||
None::<&'static str>,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
self.set_failed(pool, "Could not be sent as missing access").await;
|
|
||||||
}
|
|
||||||
50007 => {
|
|
||||||
self.log_error(
|
|
||||||
pool,
|
|
||||||
"Could not be sent as user has DMs disabled",
|
|
||||||
None::<&'static str>,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
self.set_failed(pool, "Could not be sent as user has DMs disabled")
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
50013 => {
|
|
||||||
self.log_error(
|
|
||||||
pool,
|
|
||||||
"Could not be sent as permissions are invalid",
|
|
||||||
None::<&'static str>,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
self.set_failed(
|
|
||||||
pool,
|
|
||||||
"Could not be sent as permissions are invalid",
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
self.log_error(
|
|
||||||
pool,
|
|
||||||
"HTTP error sending reminder",
|
|
||||||
Some(http_error),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
self.refresh(pool).await;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.log_error(pool, "(Likely) a parsing error", Some(error)).await;
|
send_to_channel(cache_http, channel_id, &self, embed).await
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(e) = result {
|
||||||
|
if let Error::Http(error) = e {
|
||||||
|
if let HttpError::UnsuccessfulRequest(http_error) = *error {
|
||||||
|
match http_error.error.code {
|
||||||
|
10003 => {
|
||||||
|
self.log_error(
|
||||||
|
pool,
|
||||||
|
"Could not be sent as channel does not exist",
|
||||||
|
None::<&'static str>,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
self.set_failed(
|
||||||
|
pool,
|
||||||
|
"Could not be sent as channel does not exist",
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
10004 => {
|
||||||
|
self.log_error(
|
||||||
|
pool,
|
||||||
|
"Could not be sent as guild does not exist",
|
||||||
|
None::<&'static str>,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
self.set_failed(
|
||||||
|
pool,
|
||||||
|
"Could not be sent as guild does not exist",
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
50001 => {
|
||||||
|
self.log_error(
|
||||||
|
pool,
|
||||||
|
"Could not be sent as missing access",
|
||||||
|
None::<&'static str>,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
self.set_failed(
|
||||||
|
pool,
|
||||||
|
"Could not be sent as missing access",
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
50007 => {
|
||||||
|
self.log_error(
|
||||||
|
pool,
|
||||||
|
"Could not be sent as user has DMs disabled",
|
||||||
|
None::<&'static str>,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
self.set_failed(
|
||||||
|
pool,
|
||||||
|
"Could not be sent as user has DMs disabled",
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
50013 => {
|
||||||
|
self.log_error(
|
||||||
|
pool,
|
||||||
|
"Could not be sent as permissions are invalid",
|
||||||
|
None::<&'static str>,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
self.set_failed(
|
||||||
|
pool,
|
||||||
|
"Could not be sent as permissions are invalid",
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
self.log_error(
|
||||||
|
pool,
|
||||||
|
"HTTP error sending reminder",
|
||||||
|
Some(http_error),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
self.refresh(pool).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.log_error(pool, "(Likely) a parsing error", Some(error)).await;
|
||||||
|
self.refresh(pool).await;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.log_error(pool, "Non-HTTP error", Some(e)).await;
|
||||||
|
self.refresh(pool).await;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.log_success(pool).await;
|
||||||
self.refresh(pool).await;
|
self.refresh(pool).await;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.log_error(pool, "Non-HTTP error", Some(e)).await;
|
info!("Reminder {} is paused", self.id);
|
||||||
|
|
||||||
self.refresh(pool).await;
|
self.refresh(pool).await;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
self.log_success(pool).await;
|
|
||||||
self.refresh(pool).await;
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
info!("Reminder {} is paused", self.id);
|
|
||||||
|
|
||||||
self.refresh(pool).await;
|
None => {
|
||||||
|
info!("Reminder {} is orphaned", self.id);
|
||||||
|
|
||||||
|
self.log_error(pool, "Orphaned", Option::<u8>::None).await;
|
||||||
|
self.set_failed(pool, "Could not be sent as channel was deleted").await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,8 @@ impl ChannelData {
|
|||||||
UPDATE channels
|
UPDATE channels
|
||||||
SET name = ?, nudge = ?, blacklisted = ?, webhook_id = ?, webhook_token = ?,
|
SET name = ?, nudge = ?, blacklisted = ?, webhook_id = ?, webhook_token = ?,
|
||||||
paused = ?, paused_until = ?
|
paused = ?, paused_until = ?
|
||||||
WHERE id = ?",
|
WHERE id = ?
|
||||||
|
",
|
||||||
self.name,
|
self.name,
|
||||||
self.nudge,
|
self.nudge,
|
||||||
self.blacklisted,
|
self.blacklisted,
|
||||||
|
@ -173,6 +173,7 @@ pub async fn import_reminders(
|
|||||||
utc_time: record.utc_time,
|
utc_time: record.utc_time,
|
||||||
status: "pending".to_string(),
|
status: "pending".to_string(),
|
||||||
status_change_time: None,
|
status_change_time: None,
|
||||||
|
status_message: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
create_reminder(
|
create_reminder(
|
||||||
|
@ -361,7 +361,8 @@ pub async fn get_reminders(
|
|||||||
reminders.username,
|
reminders.username,
|
||||||
reminders.utc_time,
|
reminders.utc_time,
|
||||||
reminders.status,
|
reminders.status,
|
||||||
reminders.status_change_time
|
reminders.status_change_time,
|
||||||
|
reminders.status_message
|
||||||
FROM reminders
|
FROM reminders
|
||||||
LEFT JOIN channels ON channels.id = reminders.channel_id
|
LEFT JOIN channels ON channels.id = reminders.channel_id
|
||||||
WHERE FIND_IN_SET(`status`, ?) AND reminders.guild_id = (SELECT id FROM guilds WHERE guild = ?)",
|
WHERE FIND_IN_SET(`status`, ?) AND reminders.guild_id = (SELECT id FROM guilds WHERE guild = ?)",
|
||||||
@ -549,7 +550,8 @@ pub async fn edit_reminder(
|
|||||||
|
|
||||||
match sqlx::query_as_unchecked!(
|
match sqlx::query_as_unchecked!(
|
||||||
Reminder,
|
Reminder,
|
||||||
"SELECT reminders.attachment,
|
"
|
||||||
|
SELECT reminders.attachment,
|
||||||
reminders.attachment_name,
|
reminders.attachment_name,
|
||||||
reminders.avatar,
|
reminders.avatar,
|
||||||
channels.channel,
|
channels.channel,
|
||||||
@ -576,7 +578,8 @@ pub async fn edit_reminder(
|
|||||||
reminders.username,
|
reminders.username,
|
||||||
reminders.utc_time,
|
reminders.utc_time,
|
||||||
reminders.status,
|
reminders.status,
|
||||||
reminders.status_change_time
|
reminders.status_change_time,
|
||||||
|
reminders.status_message
|
||||||
FROM reminders
|
FROM reminders
|
||||||
LEFT JOIN channels ON channels.id = reminders.channel_id
|
LEFT JOIN channels ON channels.id = reminders.channel_id
|
||||||
WHERE uid = ?",
|
WHERE uid = ?",
|
||||||
|
@ -151,6 +151,7 @@ pub struct Reminder {
|
|||||||
username: Option<String>,
|
username: Option<String>,
|
||||||
utc_time: NaiveDateTime,
|
utc_time: NaiveDateTime,
|
||||||
status: String,
|
status: String,
|
||||||
|
status_message: Option<String>,
|
||||||
status_change_time: Option<NaiveDateTime>,
|
status_change_time: Option<NaiveDateTime>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -504,7 +505,8 @@ pub async fn create_reminder(
|
|||||||
|
|
||||||
// write to db
|
// write to db
|
||||||
match sqlx::query!(
|
match sqlx::query!(
|
||||||
"INSERT INTO reminders (
|
"
|
||||||
|
INSERT INTO reminders (
|
||||||
uid,
|
uid,
|
||||||
attachment,
|
attachment,
|
||||||
attachment_name,
|
attachment_name,
|
||||||
@ -594,7 +596,8 @@ pub async fn create_reminder(
|
|||||||
reminders.username,
|
reminders.username,
|
||||||
reminders.utc_time,
|
reminders.utc_time,
|
||||||
reminders.status,
|
reminders.status,
|
||||||
reminders.status_change_time
|
reminders.status_change_time,
|
||||||
|
reminders.status_message
|
||||||
FROM reminders
|
FROM reminders
|
||||||
LEFT JOIN channels ON channels.id = reminders.channel_id
|
LEFT JOIN channels ON channels.id = reminders.channel_id
|
||||||
WHERE uid = ?",
|
WHERE uid = ?",
|
||||||
|
@ -758,6 +758,16 @@ div.reminderError .errorHead .reminderTime {
|
|||||||
border-style: solid;
|
border-style: solid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.reminderError .reminderMessage {
|
||||||
|
font-size: 1rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
color: rgb(54, 54, 54);
|
||||||
|
flex-grow: 1;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
/* other stuff */
|
/* other stuff */
|
||||||
|
|
||||||
.half-rem {
|
.half-rem {
|
||||||
|
@ -30,6 +30,8 @@ document.addEventListener("paneLoad", (ev) => {
|
|||||||
{ zone: "UTC" }
|
{ zone: "UTC" }
|
||||||
);
|
);
|
||||||
newRow.querySelector(".reminderName").textContent = reminder.name;
|
newRow.querySelector(".reminderName").textContent = reminder.name;
|
||||||
|
newRow.querySelector(".reminderMessage").textContent =
|
||||||
|
reminder.status_message;
|
||||||
newRow.querySelector(".reminderTime").textContent = statusTime
|
newRow.querySelector(".reminderTime").textContent = statusTime
|
||||||
.toLocal()
|
.toLocal()
|
||||||
.toLocaleString(luxon.DateTime.DATETIME_MED);
|
.toLocaleString(luxon.DateTime.DATETIME_MED);
|
||||||
|
@ -9,6 +9,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="reminderName">
|
<div class="reminderName">
|
||||||
Reminder
|
Reminder
|
||||||
|
</div>
|
||||||
|
<div class="reminderMessage">
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="reminderTime">
|
<div class="reminderTime">
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user