Use timezones wherever possible.
Replace uses of NaiveDateTime with DateTime<Utc>. Use timezones in postman to update days correctly. Use chrono::Months to update months rather than using MySQL query.
This commit is contained in:
		
							
								
								
									
										34
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										34
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							@@ -56,9 +56,9 @@ dependencies = [
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "aho-corasick"
 | 
			
		||||
version = "0.7.19"
 | 
			
		||||
version = "0.7.20"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e"
 | 
			
		||||
checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "memchr",
 | 
			
		||||
]
 | 
			
		||||
@@ -225,15 +225,15 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "bytes"
 | 
			
		||||
version = "1.2.1"
 | 
			
		||||
version = "1.3.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db"
 | 
			
		||||
checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "cc"
 | 
			
		||||
version = "1.0.76"
 | 
			
		||||
version = "1.0.77"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "76a284da2e6fe2092f2353e51713435363112dfd60030e22add80be333fb928f"
 | 
			
		||||
checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "cfg-if"
 | 
			
		||||
@@ -417,9 +417,9 @@ dependencies = [
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "crossbeam-queue"
 | 
			
		||||
version = "0.3.7"
 | 
			
		||||
version = "0.3.8"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "ebb3d1683412e9be6a15533314f00ec223c0762c522a3f77f048b265aab4470c"
 | 
			
		||||
checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "cfg-if",
 | 
			
		||||
 "crossbeam-utils",
 | 
			
		||||
@@ -427,9 +427,9 @@ dependencies = [
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "crossbeam-utils"
 | 
			
		||||
version = "0.8.13"
 | 
			
		||||
version = "0.8.14"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "422f23e724af1240ec469ea1e834d87a4b59ce2efe2c6a96256b0c47e2fd86aa"
 | 
			
		||||
checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "cfg-if",
 | 
			
		||||
]
 | 
			
		||||
@@ -1911,9 +1911,9 @@ checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "poise"
 | 
			
		||||
version = "0.3.0"
 | 
			
		||||
version = "0.4.1"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "f6c01d22dcda434b0dfe956c60f6ac9b0352c4c2f4af852afb3155a971cd306d"
 | 
			
		||||
checksum = "aee439543df35482730552e7c9ed0c45a5f1d521548e6c0249967c4ba8828f60"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "async-trait",
 | 
			
		||||
 "derivative",
 | 
			
		||||
@@ -1930,9 +1930,9 @@ dependencies = [
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "poise_macros"
 | 
			
		||||
version = "0.3.0"
 | 
			
		||||
version = "0.4.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "52ff861b6a52ec47bc54eb17424c025feeb040e82836036276c25dda045a8a0c"
 | 
			
		||||
checksum = "53d21213ff7aeef5ab69729a5cddfb351a84a9bf3dadf9f470032440d43746c2"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "darling",
 | 
			
		||||
 "proc-macro2",
 | 
			
		||||
@@ -2133,7 +2133,7 @@ checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "reminder_rs"
 | 
			
		||||
version = "1.6.7"
 | 
			
		||||
version = "1.6.8"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "base64",
 | 
			
		||||
 "chrono",
 | 
			
		||||
@@ -2518,9 +2518,9 @@ dependencies = [
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "serde_json"
 | 
			
		||||
version = "1.0.88"
 | 
			
		||||
version = "1.0.89"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "8e8b3801309262e8184d9687fb697586833e939767aea0dda89f5a8e650e8bd7"
 | 
			
		||||
checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "itoa 1.0.4",
 | 
			
		||||
 "ryu",
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,11 @@
 | 
			
		||||
[package]
 | 
			
		||||
name = "reminder_rs"
 | 
			
		||||
version = "1.6.8"
 | 
			
		||||
version = "1.6.9"
 | 
			
		||||
authors = ["jellywx <judesouthworth@pm.me>"]
 | 
			
		||||
edition = "2018"
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
poise = "0.3"
 | 
			
		||||
poise = "0.4"
 | 
			
		||||
dotenv = "0.15"
 | 
			
		||||
tokio = { version = "1", features = ["process", "full"] }
 | 
			
		||||
reqwest = "0.11"
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
use chrono::{DateTime, Days, Duration};
 | 
			
		||||
use chrono::{DateTime, Days, Duration, Months, TimeZone};
 | 
			
		||||
use chrono_tz::Tz;
 | 
			
		||||
use lazy_static::lazy_static;
 | 
			
		||||
use log::{error, info, warn};
 | 
			
		||||
@@ -62,7 +62,8 @@ pub fn substitute(string: &str) -> String {
 | 
			
		||||
        let format = caps.name("format").map(|m| m.as_str());
 | 
			
		||||
 | 
			
		||||
        if let (Some(final_time), Some(format)) = (final_time, format) {
 | 
			
		||||
            let dt = NaiveDateTime::from_timestamp(final_time, 0);
 | 
			
		||||
            match NaiveDateTime::from_timestamp_opt(final_time, 0) {
 | 
			
		||||
                Some(dt) => {
 | 
			
		||||
                    let now = Utc::now().naive_utc();
 | 
			
		||||
 | 
			
		||||
                    let difference = {
 | 
			
		||||
@@ -74,6 +75,10 @@ pub fn substitute(string: &str) -> String {
 | 
			
		||||
                    };
 | 
			
		||||
 | 
			
		||||
                    fmt_displacement(format, difference.num_seconds() as u64)
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                None => String::new(),
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            String::new()
 | 
			
		||||
        }
 | 
			
		||||
@@ -243,10 +248,10 @@ pub struct Reminder {
 | 
			
		||||
    attachment: Option<Vec<u8>>,
 | 
			
		||||
    attachment_name: Option<String>,
 | 
			
		||||
 | 
			
		||||
    utc_time: NaiveDateTime,
 | 
			
		||||
    utc_time: DateTime<Utc>,
 | 
			
		||||
    timezone: String,
 | 
			
		||||
    restartable: bool,
 | 
			
		||||
    expires: Option<NaiveDateTime>,
 | 
			
		||||
    expires: Option<DateTime<Utc>>,
 | 
			
		||||
    interval_seconds: Option<u32>,
 | 
			
		||||
    interval_months: Option<u32>,
 | 
			
		||||
 | 
			
		||||
@@ -330,9 +335,7 @@ WHERE
 | 
			
		||||
 | 
			
		||||
    async fn reset_webhook(&self, pool: impl Executor<'_, Database = Database> + Copy) {
 | 
			
		||||
        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
 | 
			
		||||
        )
 | 
			
		||||
        .execute(pool)
 | 
			
		||||
@@ -341,44 +344,25 @@ UPDATE channels SET webhook_id = NULL, webhook_token = NULL WHERE channel = ?
 | 
			
		||||
 | 
			
		||||
    async fn refresh(&self, pool: impl Executor<'_, Database = Database> + Copy) {
 | 
			
		||||
        if self.interval_seconds.is_some() || self.interval_months.is_some() {
 | 
			
		||||
            let now = Utc::now().naive_local();
 | 
			
		||||
            let mut updated_reminder_time = self.utc_time;
 | 
			
		||||
            let now = Utc::now();
 | 
			
		||||
            let mut updated_reminder_time =
 | 
			
		||||
                self.utc_time.with_timezone(&self.timezone.parse().unwrap_or(Tz::UTC));
 | 
			
		||||
 | 
			
		||||
            if let Some(interval) = self.interval_months {
 | 
			
		||||
                match sqlx::query!(
 | 
			
		||||
                    // use the second date_add to force return value to datetime
 | 
			
		||||
                    "SELECT DATE_ADD(DATE_ADD(?, INTERVAL ? MONTH), INTERVAL 0 SECOND) AS new_time",
 | 
			
		||||
                    updated_reminder_time,
 | 
			
		||||
                    interval
 | 
			
		||||
                )
 | 
			
		||||
                .fetch_one(pool)
 | 
			
		||||
                .await
 | 
			
		||||
                {
 | 
			
		||||
                    Ok(row) => match row.new_time {
 | 
			
		||||
                        Some(datetime) => {
 | 
			
		||||
                            updated_reminder_time = datetime;
 | 
			
		||||
                        }
 | 
			
		||||
                        None => {
 | 
			
		||||
                            warn!("Could not update interval by months: got NULL");
 | 
			
		||||
                updated_reminder_time = updated_reminder_time
 | 
			
		||||
                    .checked_add_months(Months::new(interval))
 | 
			
		||||
                    .unwrap_or_else(|| {
 | 
			
		||||
                        warn!("Could not add months to a reminder");
 | 
			
		||||
 | 
			
		||||
                            updated_reminder_time += Duration::days(30);
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    Err(e) => {
 | 
			
		||||
                        warn!("Could not update interval by months: {:?}", e);
 | 
			
		||||
 | 
			
		||||
                        // naively fallback to adding 30 days
 | 
			
		||||
                        updated_reminder_time += Duration::days(30);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                        updated_reminder_time
 | 
			
		||||
                    });
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            fn increment_days(
 | 
			
		||||
                now: NaiveDateTime,
 | 
			
		||||
                mut new_time: NaiveDateTime,
 | 
			
		||||
            fn increment_days<T: TimeZone>(
 | 
			
		||||
                now: DateTime<Utc>,
 | 
			
		||||
                mut new_time: DateTime<T>,
 | 
			
		||||
                interval: u32,
 | 
			
		||||
            ) -> Option<NaiveDateTime> {
 | 
			
		||||
            ) -> Option<DateTime<T>> {
 | 
			
		||||
                while new_time < now {
 | 
			
		||||
                    new_time = new_time.checked_add_days(Days::new((interval / 86400).into()))?;
 | 
			
		||||
                }
 | 
			
		||||
@@ -403,16 +387,12 @@ UPDATE channels SET webhook_id = NULL, webhook_token = NULL WHERE channel = ?
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if self.expires.map_or(false, |expires| {
 | 
			
		||||
                NaiveDateTime::from_timestamp(updated_reminder_time.timestamp(), 0) > expires
 | 
			
		||||
            }) {
 | 
			
		||||
            if self.expires.map_or(false, |expires| updated_reminder_time > expires) {
 | 
			
		||||
                self.force_delete(pool).await;
 | 
			
		||||
            } else {
 | 
			
		||||
                sqlx::query!(
 | 
			
		||||
                    "
 | 
			
		||||
UPDATE reminders SET `utc_time` = ? WHERE `id` = ?
 | 
			
		||||
                    ",
 | 
			
		||||
                    updated_reminder_time,
 | 
			
		||||
                    "UPDATE reminders SET `utc_time` = ? WHERE `id` = ?",
 | 
			
		||||
                    updated_reminder_time.with_timezone(&Utc),
 | 
			
		||||
                    self.id
 | 
			
		||||
                )
 | 
			
		||||
                .execute(pool)
 | 
			
		||||
@@ -425,12 +405,7 @@ UPDATE reminders SET `utc_time` = ? WHERE `id` = ?
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn force_delete(&self, pool: impl Executor<'_, Database = Database> + Copy) {
 | 
			
		||||
        sqlx::query!(
 | 
			
		||||
            "
 | 
			
		||||
DELETE FROM reminders WHERE `id` = ?
 | 
			
		||||
            ",
 | 
			
		||||
            self.id
 | 
			
		||||
        )
 | 
			
		||||
        sqlx::query!("DELETE FROM reminders WHERE `id` = ?", self.id)
 | 
			
		||||
            .execute(pool)
 | 
			
		||||
            .await
 | 
			
		||||
            .expect(&format!("Could not delete Reminder {}", self.id));
 | 
			
		||||
@@ -571,9 +546,7 @@ DELETE FROM reminders WHERE `id` = ?
 | 
			
		||||
                    .map_or(true, |inner| inner >= Utc::now().naive_local()))
 | 
			
		||||
        {
 | 
			
		||||
            let _ = sqlx::query!(
 | 
			
		||||
                "
 | 
			
		||||
UPDATE `channels` SET paused = 0, paused_until = NULL WHERE `channel` = ?
 | 
			
		||||
                ",
 | 
			
		||||
                "UPDATE `channels` SET paused = 0, paused_until = NULL WHERE `channel` = ?",
 | 
			
		||||
                self.channel_id
 | 
			
		||||
            )
 | 
			
		||||
            .execute(pool)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,6 @@
 | 
			
		||||
use std::{
 | 
			
		||||
    collections::HashSet,
 | 
			
		||||
    string::ToString,
 | 
			
		||||
    time::{SystemTime, UNIX_EPOCH},
 | 
			
		||||
};
 | 
			
		||||
use std::{collections::HashSet, string::ToString};
 | 
			
		||||
 | 
			
		||||
use chrono::NaiveDateTime;
 | 
			
		||||
use chrono::{DateTime, NaiveDateTime, Utc};
 | 
			
		||||
use chrono_tz::Tz;
 | 
			
		||||
use num_integer::Integer;
 | 
			
		||||
use poise::{
 | 
			
		||||
@@ -60,8 +56,8 @@ pub async fn pause(
 | 
			
		||||
            let parsed = natural_parser(&until, &timezone.to_string()).await;
 | 
			
		||||
 | 
			
		||||
            if let Some(timestamp) = parsed {
 | 
			
		||||
                let dt = NaiveDateTime::from_timestamp(timestamp, 0);
 | 
			
		||||
 | 
			
		||||
                match NaiveDateTime::from_timestamp_opt(timestamp, 0) {
 | 
			
		||||
                    Some(dt) => {
 | 
			
		||||
                        channel.paused = true;
 | 
			
		||||
                        channel.paused_until = Some(dt);
 | 
			
		||||
 | 
			
		||||
@@ -72,6 +68,15 @@ pub async fn pause(
 | 
			
		||||
                            timestamp
 | 
			
		||||
                        ))
 | 
			
		||||
                        .await?;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    None => {
 | 
			
		||||
                        ctx.say(format!(
 | 
			
		||||
                            "Time processed could not be interpreted as `DateTime`. Please write the time as clearly as possible",
 | 
			
		||||
                        ))
 | 
			
		||||
                        .await?;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                ctx.say(
 | 
			
		||||
                    "Time could not be processed. Please write the time as clearly as possible",
 | 
			
		||||
@@ -432,11 +437,8 @@ pub fn show_delete_page(reminders: &[Reminder], page: usize, timezone: Tz) -> Cr
 | 
			
		||||
    reply
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn time_difference(start_time: NaiveDateTime) -> String {
 | 
			
		||||
    let unix_time = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() as i64;
 | 
			
		||||
    let now = NaiveDateTime::from_timestamp(unix_time, 0);
 | 
			
		||||
 | 
			
		||||
    let delta = (now - start_time).num_seconds();
 | 
			
		||||
fn time_difference(start_time: DateTime<Utc>) -> String {
 | 
			
		||||
    let delta = (Utc::now() - start_time).num_seconds();
 | 
			
		||||
 | 
			
		||||
    let (minutes, seconds) = delta.div_rem(&60);
 | 
			
		||||
    let (hours, minutes) = minutes.div_rem(&60);
 | 
			
		||||
 
 | 
			
		||||
@@ -175,17 +175,15 @@ impl<'a> MultiReminderBuilder<'a> {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn time<T: Into<i64>>(mut self, time: T) -> Self {
 | 
			
		||||
        self.utc_time = NaiveDateTime::from_timestamp(time.into(), 0);
 | 
			
		||||
        if let Some(utc_time) = NaiveDateTime::from_timestamp_opt(time.into(), 0) {
 | 
			
		||||
            self.utc_time = utc_time;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn expires<T: Into<i64>>(mut self, time: Option<T>) -> Self {
 | 
			
		||||
        if let Some(t) = time {
 | 
			
		||||
            self.expires = Some(NaiveDateTime::from_timestamp(t.into(), 0));
 | 
			
		||||
        } else {
 | 
			
		||||
            self.expires = None;
 | 
			
		||||
        }
 | 
			
		||||
        self.expires = time.map(|t| NaiveDateTime::from_timestamp_opt(t.into(), 0)).flatten();
 | 
			
		||||
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@ pub mod look_flags;
 | 
			
		||||
 | 
			
		||||
use std::hash::{Hash, Hasher};
 | 
			
		||||
 | 
			
		||||
use chrono::{NaiveDateTime, TimeZone};
 | 
			
		||||
use chrono::{DateTime, NaiveDateTime, Utc};
 | 
			
		||||
use chrono_tz::Tz;
 | 
			
		||||
use poise::serenity_prelude::{
 | 
			
		||||
    model::id::{ChannelId, GuildId, UserId},
 | 
			
		||||
@@ -24,7 +24,7 @@ pub struct Reminder {
 | 
			
		||||
    pub id: u32,
 | 
			
		||||
    pub uid: String,
 | 
			
		||||
    pub channel: u64,
 | 
			
		||||
    pub utc_time: NaiveDateTime,
 | 
			
		||||
    pub utc_time: DateTime<Utc>,
 | 
			
		||||
    pub interval_seconds: Option<u32>,
 | 
			
		||||
    pub interval_months: Option<u32>,
 | 
			
		||||
    pub expires: Option<NaiveDateTime>,
 | 
			
		||||
@@ -310,16 +310,15 @@ WHERE
 | 
			
		||||
            count + 1,
 | 
			
		||||
            self.display_content(),
 | 
			
		||||
            self.channel,
 | 
			
		||||
            timezone.timestamp(self.utc_time.timestamp(), 0).format("%Y-%m-%d %H:%M:%S")
 | 
			
		||||
            self.utc_time.with_timezone(timezone).format("%Y-%m-%d %H:%M:%S")
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn display(&self, flags: &LookFlags, timezone: &Tz) -> String {
 | 
			
		||||
        let time_display = match flags.time_display {
 | 
			
		||||
            TimeDisplayType::Absolute => timezone
 | 
			
		||||
                .timestamp(self.utc_time.timestamp(), 0)
 | 
			
		||||
                .format("%Y-%m-%d %H:%M:%S")
 | 
			
		||||
                .to_string(),
 | 
			
		||||
            TimeDisplayType::Absolute => {
 | 
			
		||||
                self.utc_time.with_timezone(timezone).format("%Y-%m-%d %H:%M:%S").to_string()
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            TimeDisplayType::Relative => format!("<t:{}:R>", self.utc_time.timestamp()),
 | 
			
		||||
        };
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,9 @@
 | 
			
		||||
use chrono::NaiveDateTime;
 | 
			
		||||
use chrono::{DateTime, Utc};
 | 
			
		||||
use sqlx::MySqlPool;
 | 
			
		||||
 | 
			
		||||
pub struct Timer {
 | 
			
		||||
    pub name: String,
 | 
			
		||||
    pub start_time: NaiveDateTime,
 | 
			
		||||
    pub start_time: DateTime<Utc>,
 | 
			
		||||
    pub owner: u64,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user