mirror of
https://github.com/stoatchat/stoatchat.git
synced 2026-07-14 21:47:02 +00:00
feat(core/database, bindings/node): suspend user
This commit is contained in:
@@ -2,12 +2,15 @@ use std::{collections::HashSet, str::FromStr, time::Duration};
|
||||
|
||||
use crate::{events::client::EventV1, Database, File, RatelimitEvent};
|
||||
|
||||
use authifier::config::{EmailVerificationConfig, Template};
|
||||
use iso8601_timestamp::Timestamp;
|
||||
use once_cell::sync::Lazy;
|
||||
use rand::seq::SliceRandom;
|
||||
use revolt_config::{config, FeaturesLimits};
|
||||
use revolt_models::v0;
|
||||
use revolt_models::v0::{self, UserFlags};
|
||||
use revolt_presence::filter_online;
|
||||
use revolt_result::{create_error, Result};
|
||||
use serde_json::json;
|
||||
use ulid::Ulid;
|
||||
|
||||
auto_derived_partial!(
|
||||
@@ -49,6 +52,10 @@ auto_derived_partial!(
|
||||
/// Bot information
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub bot: Option<BotInformation>,
|
||||
|
||||
/// Time until user is unsuspended
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub suspended_until: Option<Timestamp>,
|
||||
},
|
||||
"PartialUser"
|
||||
);
|
||||
@@ -62,6 +69,10 @@ auto_derived!(
|
||||
ProfileContent,
|
||||
ProfileBackground,
|
||||
DisplayName,
|
||||
|
||||
// internal fields
|
||||
Suspension,
|
||||
None,
|
||||
}
|
||||
|
||||
/// User's relationship with another user (or themselves)
|
||||
@@ -165,6 +176,7 @@ impl Default for User {
|
||||
flags: Default::default(),
|
||||
privileged: Default::default(),
|
||||
bot: Default::default(),
|
||||
suspended_until: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -659,17 +671,106 @@ impl User {
|
||||
}
|
||||
}
|
||||
FieldsUser::DisplayName => self.display_name = None,
|
||||
FieldsUser::Suspension => self.suspended_until = None,
|
||||
FieldsUser::None => {}
|
||||
}
|
||||
}
|
||||
|
||||
/// Suspend the user
|
||||
pub async fn suspend(&mut self, db: &Database) -> Result<()> {
|
||||
// Remove sessions (logout all)
|
||||
// Mark user as suspended
|
||||
// Disable account
|
||||
///
|
||||
/// - If a duration is specified, the user will be automatically unsuspended after the given time.
|
||||
/// - If a reason is specified, an email will be sent.
|
||||
pub async fn suspend(
|
||||
&mut self,
|
||||
db: &Database,
|
||||
duration_days: Option<usize>,
|
||||
reason: Option<Vec<String>>,
|
||||
) -> Result<()> {
|
||||
let authifier = db.clone().to_authifier().await;
|
||||
let mut account = authifier
|
||||
.database
|
||||
.find_account(&self.id)
|
||||
.await
|
||||
.map_err(|_| create_error!(InternalError))?;
|
||||
|
||||
account
|
||||
.disable(&authifier)
|
||||
.await
|
||||
.map_err(|_| create_error!(InternalError))?;
|
||||
|
||||
account
|
||||
.delete_all_sessions(&authifier, None)
|
||||
.await
|
||||
.map_err(|_| create_error!(InternalError))?;
|
||||
|
||||
self.update(
|
||||
db,
|
||||
PartialUser {
|
||||
flags: Some(UserFlags::SuspendedUntil as i32),
|
||||
suspended_until: duration_days.and_then(|dur| {
|
||||
Timestamp::now_utc().checked_add(iso8601_timestamp::Duration::days(dur as i64))
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
vec![],
|
||||
)
|
||||
.await?;
|
||||
|
||||
if let Some(reason) = reason {
|
||||
if let EmailVerificationConfig::Enabled { smtp, .. } =
|
||||
authifier.config.email_verification
|
||||
{
|
||||
smtp.send_email(
|
||||
account.email.clone(),
|
||||
// maybe move this to common area?
|
||||
&Template {
|
||||
title: "Account Suspension".to_string(),
|
||||
html: Some(include_str!("../../../templates/suspension.html").to_owned()),
|
||||
text: include_str!("../../../templates/suspension.txt").to_owned(),
|
||||
url: Default::default(),
|
||||
},
|
||||
json!({
|
||||
"email": account.email,
|
||||
"list": reason.join(", "),
|
||||
"duration": duration_days,
|
||||
"duration_diplay": if duration_days.is_some() {
|
||||
"block"
|
||||
} else {
|
||||
"none"
|
||||
}
|
||||
}),
|
||||
)
|
||||
.map_err(|_| create_error!(InternalError))?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Unsuspend the user
|
||||
pub async fn unsuspend(&mut self, db: &Database) -> Result<()> {
|
||||
self.update(
|
||||
db,
|
||||
PartialUser {
|
||||
flags: Some(0),
|
||||
suspended_until: None,
|
||||
..Default::default()
|
||||
},
|
||||
vec![],
|
||||
)
|
||||
.await?;
|
||||
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
/// Permanently ban the user
|
||||
///
|
||||
/// - If a reason is specified, an email will be sent.
|
||||
pub async fn ban(&mut self, _db: &Database, _reason: Option<String>) -> Result<()> {
|
||||
// Send ban email (if reason provided)
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
/// Mark as deleted
|
||||
pub async fn mark_deleted(&mut self, db: &Database) -> Result<()> {
|
||||
self.update(
|
||||
@@ -685,6 +786,7 @@ impl User {
|
||||
FieldsUser::StatusPresence,
|
||||
FieldsUser::ProfileContent,
|
||||
FieldsUser::ProfileBackground,
|
||||
FieldsUser::Suspension,
|
||||
],
|
||||
)
|
||||
.await
|
||||
|
||||
@@ -346,6 +346,8 @@ impl IntoDocumentPath for FieldsUser {
|
||||
FieldsUser::StatusPresence => "status.presence",
|
||||
FieldsUser::StatusText => "status.text",
|
||||
FieldsUser::DisplayName => "display_name",
|
||||
FieldsUser::Suspension => "suspended_until",
|
||||
FieldsUser::None => "none",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ static Q: Lazy<(Sender<AuthifierEvent>, Receiver<AuthifierEvent>)> = Lazy::new(|
|
||||
|
||||
/// Get sender
|
||||
pub fn sender() -> Sender<AuthifierEvent> {
|
||||
Q.0
|
||||
Q.0.clone()
|
||||
}
|
||||
|
||||
/// Start a new worker
|
||||
|
||||
@@ -15,7 +15,7 @@ pub mod process_embeds;
|
||||
pub mod web_push;
|
||||
|
||||
/// Spawn background workers
|
||||
pub async fn start_workers(db: Database, authifier_db: authifier::Database) {
|
||||
pub fn start_workers(db: Database, authifier_db: authifier::Database) {
|
||||
task::spawn(authifier_relay::worker());
|
||||
task::spawn(apple_notifications::worker(db.clone()));
|
||||
|
||||
|
||||
@@ -1171,6 +1171,7 @@ impl From<User> for crate::User {
|
||||
flags: Some(value.flags as i32),
|
||||
privileged: value.privileged,
|
||||
bot: value.bot.map(Into::into),
|
||||
suspended_until: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1209,6 +1210,8 @@ impl From<FieldsUser> for crate::FieldsUser {
|
||||
FieldsUser::StatusPresence => crate::FieldsUser::StatusPresence,
|
||||
FieldsUser::StatusText => crate::FieldsUser::StatusText,
|
||||
FieldsUser::DisplayName => crate::FieldsUser::DisplayName,
|
||||
|
||||
FieldsUser::Internal => crate::FieldsUser::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1222,6 +1225,9 @@ impl From<crate::FieldsUser> for FieldsUser {
|
||||
crate::FieldsUser::StatusPresence => FieldsUser::StatusPresence,
|
||||
crate::FieldsUser::StatusText => FieldsUser::StatusText,
|
||||
crate::FieldsUser::DisplayName => FieldsUser::DisplayName,
|
||||
|
||||
crate::FieldsUser::Suspension => FieldsUser::Internal,
|
||||
crate::FieldsUser::None => FieldsUser::Internal,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1338,4 +1344,4 @@ impl From<FieldsMessage> for crate::FieldsMessage {
|
||||
FieldsMessage::Pinned => crate::FieldsMessage::Pinned,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
625
crates/core/database/templates/suspension.html
Normal file
625
crates/core/database/templates/suspension.html
Normal file
@@ -0,0 +1,625 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<!-- Compiled with Bootstrap Email version: 1.5.1 -->
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge" />
|
||||
<meta name="x-apple-disable-message-reformatting" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta
|
||||
name="format-detection"
|
||||
content="telephone=no, date=no, address=no, email=no"
|
||||
/>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<style type="text/css">
|
||||
body,
|
||||
table,
|
||||
td {
|
||||
font-family: Helvetica, Arial, sans-serif !important;
|
||||
}
|
||||
.ExternalClass {
|
||||
width: 100%;
|
||||
}
|
||||
.ExternalClass,
|
||||
.ExternalClass p,
|
||||
.ExternalClass span,
|
||||
.ExternalClass font,
|
||||
.ExternalClass td,
|
||||
.ExternalClass div {
|
||||
line-height: 150%;
|
||||
}
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
* {
|
||||
color: inherit;
|
||||
}
|
||||
a[x-apple-data-detectors],
|
||||
u + #body a,
|
||||
#MessageViewBody a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
font-size: inherit;
|
||||
font-family: inherit;
|
||||
font-weight: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
img {
|
||||
-ms-interpolation-mode: bicubic;
|
||||
}
|
||||
table:not([class^="s-"]) {
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
mso-table-lspace: 0pt;
|
||||
mso-table-rspace: 0pt;
|
||||
border-spacing: 0px;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
table:not([class^="s-"]) td {
|
||||
border-spacing: 0px;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
@media screen and (max-width: 600px) {
|
||||
.w-full,
|
||||
.w-full > tbody > tr > td {
|
||||
width: 100% !important;
|
||||
}
|
||||
.w-24,
|
||||
.w-24 > tbody > tr > td {
|
||||
width: 96px !important;
|
||||
}
|
||||
.p-lg-10:not(table),
|
||||
.p-lg-10:not(.btn) > tbody > tr > td,
|
||||
.p-lg-10.btn td a {
|
||||
padding: 0 !important;
|
||||
}
|
||||
.p-6:not(table),
|
||||
.p-6:not(.btn) > tbody > tr > td,
|
||||
.p-6.btn td a {
|
||||
padding: 24px !important;
|
||||
}
|
||||
*[class*="s-lg-"] > tbody > tr > td {
|
||||
font-size: 0 !important;
|
||||
line-height: 0 !important;
|
||||
height: 0 !important;
|
||||
}
|
||||
.s-4 > tbody > tr > td {
|
||||
font-size: 16px !important;
|
||||
line-height: 16px !important;
|
||||
height: 16px !important;
|
||||
}
|
||||
.s-6 > tbody > tr > td {
|
||||
font-size: 24px !important;
|
||||
line-height: 24px !important;
|
||||
height: 24px !important;
|
||||
}
|
||||
.s-10 > tbody > tr > td {
|
||||
font-size: 40px !important;
|
||||
line-height: 40px !important;
|
||||
height: 40px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body
|
||||
class="bg-light"
|
||||
style="
|
||||
outline: 0;
|
||||
width: 100%;
|
||||
min-width: 100%;
|
||||
height: 100%;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-ms-text-size-adjust: 100%;
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
line-height: 24px;
|
||||
font-weight: normal;
|
||||
font-size: 16px;
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
color: #000000;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border-width: 0;
|
||||
"
|
||||
bgcolor="#f7fafc"
|
||||
>
|
||||
<table
|
||||
class="bg-light body"
|
||||
valign="top"
|
||||
role="presentation"
|
||||
border="0"
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
style="
|
||||
outline: 0;
|
||||
width: 100%;
|
||||
min-width: 100%;
|
||||
height: 100%;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-ms-text-size-adjust: 100%;
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
line-height: 24px;
|
||||
font-weight: normal;
|
||||
font-size: 16px;
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
color: #000000;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border-width: 0;
|
||||
"
|
||||
bgcolor="#f7fafc"
|
||||
>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td
|
||||
valign="top"
|
||||
style="line-height: 24px; font-size: 16px; margin: 0"
|
||||
align="left"
|
||||
bgcolor="#f7fafc"
|
||||
>
|
||||
<table
|
||||
class="container"
|
||||
role="presentation"
|
||||
border="0"
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
style="width: 100%"
|
||||
>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td
|
||||
align="center"
|
||||
style="
|
||||
line-height: 24px;
|
||||
font-size: 16px;
|
||||
margin: 0;
|
||||
padding: 0 16px;
|
||||
"
|
||||
>
|
||||
<!--[if (gte mso 9)|(IE)]>
|
||||
<table align="center" role="presentation">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td width="600">
|
||||
<![endif]-->
|
||||
<table
|
||||
align="center"
|
||||
role="presentation"
|
||||
border="0"
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
style="width: 100%; max-width: 600px; margin: 0 auto"
|
||||
>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td
|
||||
style="
|
||||
line-height: 24px;
|
||||
font-size: 16px;
|
||||
margin: 0;
|
||||
"
|
||||
align="left"
|
||||
>
|
||||
<table
|
||||
class="s-10 w-full"
|
||||
role="presentation"
|
||||
border="0"
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
style="width: 100%"
|
||||
width="100%"
|
||||
>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td
|
||||
style="
|
||||
line-height: 40px;
|
||||
font-size: 40px;
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
margin: 0;
|
||||
"
|
||||
align="left"
|
||||
width="100%"
|
||||
height="40"
|
||||
>
|
||||
 
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table
|
||||
class="ax-center"
|
||||
role="presentation"
|
||||
align="center"
|
||||
border="0"
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
style="margin: 0 auto"
|
||||
>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td
|
||||
style="
|
||||
line-height: 24px;
|
||||
font-size: 16px;
|
||||
margin: 0;
|
||||
"
|
||||
align="left"
|
||||
>
|
||||
<img
|
||||
alt="Revolt Logo"
|
||||
class="w-24"
|
||||
src="https://app.revolt.chat/assets/logo_round.png"
|
||||
style="
|
||||
height: auto;
|
||||
line-height: 100%;
|
||||
outline: none;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
width: 96px;
|
||||
border-style: none;
|
||||
border-width: 0;
|
||||
"
|
||||
width="96"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table
|
||||
class="s-10 w-full"
|
||||
role="presentation"
|
||||
border="0"
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
style="width: 100%"
|
||||
width="100%"
|
||||
>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td
|
||||
style="
|
||||
line-height: 40px;
|
||||
font-size: 40px;
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
margin: 0;
|
||||
"
|
||||
align="left"
|
||||
width="100%"
|
||||
height="40"
|
||||
>
|
||||
 
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table
|
||||
class="card p-6 p-lg-10 space-y-4"
|
||||
role="presentation"
|
||||
border="0"
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
style="
|
||||
border-radius: 6px;
|
||||
border-collapse: separate !important;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
border: 1px solid #e2e8f0;
|
||||
"
|
||||
bgcolor="#ffffff"
|
||||
>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td
|
||||
style="
|
||||
line-height: 24px;
|
||||
font-size: 16px;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 40px;
|
||||
"
|
||||
align="left"
|
||||
bgcolor="#ffffff"
|
||||
>
|
||||
<h1
|
||||
class="h3 fw-700"
|
||||
style="
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
font-weight: 700 !important;
|
||||
vertical-align: baseline;
|
||||
font-size: 28px;
|
||||
line-height: 33.6px;
|
||||
margin: 0;
|
||||
"
|
||||
align="left"
|
||||
>
|
||||
Account Suspended
|
||||
</h1>
|
||||
<table
|
||||
class="s-4 w-full"
|
||||
role="presentation"
|
||||
border="0"
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
style="width: 100%"
|
||||
width="100%"
|
||||
>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td
|
||||
style="
|
||||
line-height: 16px;
|
||||
font-size: 16px;
|
||||
width: 100%;
|
||||
height: 16px;
|
||||
margin: 0;
|
||||
"
|
||||
align="left"
|
||||
width="100%"
|
||||
height="16"
|
||||
>
|
||||
 
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p
|
||||
class=""
|
||||
style="
|
||||
line-height: 24px;
|
||||
font-size: 16px;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
"
|
||||
align="left"
|
||||
>
|
||||
Your account has been suspended, for one
|
||||
or more reasons:
|
||||
</p>
|
||||
<table
|
||||
class="s-4 w-full"
|
||||
role="presentation"
|
||||
border="0"
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
style="width: 100%"
|
||||
width="100%"
|
||||
>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td
|
||||
style="
|
||||
line-height: 16px;
|
||||
font-size: 16px;
|
||||
width: 100%;
|
||||
height: 16px;
|
||||
margin: 0;
|
||||
"
|
||||
align="left"
|
||||
width="100%"
|
||||
height="16"
|
||||
>
|
||||
 
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<ul class="">
|
||||
{{list}}
|
||||
</ul>
|
||||
<table
|
||||
class="s-4 w-full"
|
||||
role="presentation"
|
||||
border="0"
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
style="width: 100%"
|
||||
width="100%"
|
||||
>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td
|
||||
style="
|
||||
line-height: 16px;
|
||||
font-size: 16px;
|
||||
width: 100%;
|
||||
height: 16px;
|
||||
margin: 0;
|
||||
"
|
||||
align="left"
|
||||
width="100%"
|
||||
height="16"
|
||||
>
|
||||
 
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p
|
||||
style="display: {{duration_display}}; line-height: 24px; font-size: 16px; width: 100%; margin: 0;"
|
||||
class=""
|
||||
align="left"
|
||||
>
|
||||
You will be able to use your account again
|
||||
in {{duration}} days.
|
||||
</p>
|
||||
<table
|
||||
class="s-4 w-full"
|
||||
role="presentation"
|
||||
border="0"
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
style="width: 100%"
|
||||
width="100%"
|
||||
>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td
|
||||
style="
|
||||
line-height: 16px;
|
||||
font-size: 16px;
|
||||
width: 100%;
|
||||
height: 16px;
|
||||
margin: 0;
|
||||
"
|
||||
align="left"
|
||||
width="100%"
|
||||
height="16"
|
||||
>
|
||||
 
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p
|
||||
class=""
|
||||
style="
|
||||
line-height: 24px;
|
||||
font-size: 16px;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
"
|
||||
align="left"
|
||||
>
|
||||
Further violations may result in a
|
||||
permanent ban depending on severity,
|
||||
please abide by the
|
||||
<a
|
||||
href="https://revolt.chat/aup"
|
||||
style="color: #0d6efd"
|
||||
>Acceptable Usage Policy</a
|
||||
>.
|
||||
</p>
|
||||
<table
|
||||
class="s-4 w-full"
|
||||
role="presentation"
|
||||
border="0"
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
style="width: 100%"
|
||||
width="100%"
|
||||
>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td
|
||||
style="
|
||||
line-height: 16px;
|
||||
font-size: 16px;
|
||||
width: 100%;
|
||||
height: 16px;
|
||||
margin: 0;
|
||||
"
|
||||
align="left"
|
||||
width="100%"
|
||||
height="16"
|
||||
>
|
||||
 
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p
|
||||
style="
|
||||
line-height: 24px;
|
||||
font-size: 16px;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
"
|
||||
align="left"
|
||||
>
|
||||
Ban evasion is prohibited and will be
|
||||
dealt with accordingly.
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table
|
||||
class="s-6 w-full"
|
||||
role="presentation"
|
||||
border="0"
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
style="width: 100%"
|
||||
width="100%"
|
||||
>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td
|
||||
style="
|
||||
line-height: 24px;
|
||||
font-size: 24px;
|
||||
width: 100%;
|
||||
height: 24px;
|
||||
margin: 0;
|
||||
"
|
||||
align="left"
|
||||
width="100%"
|
||||
height="24"
|
||||
>
|
||||
 
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div
|
||||
class="text-muted text-center"
|
||||
style="color: #718096"
|
||||
align="center"
|
||||
>
|
||||
This email is intended for {{email}}<br />
|
||||
Sent from Revolt<br />
|
||||
Made in Europe
|
||||
</div>
|
||||
<table
|
||||
class="s-6 w-full"
|
||||
role="presentation"
|
||||
border="0"
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
style="width: 100%"
|
||||
width="100%"
|
||||
>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td
|
||||
style="
|
||||
line-height: 24px;
|
||||
font-size: 24px;
|
||||
width: 100%;
|
||||
height: 24px;
|
||||
margin: 0;
|
||||
"
|
||||
align="left"
|
||||
width="100%"
|
||||
height="24"
|
||||
>
|
||||
 
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<!--[if (gte mso 9)|(IE)]>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
38
crates/core/database/templates/suspension.original.html
Normal file
38
crates/core/database/templates/suspension.original.html
Normal file
@@ -0,0 +1,38 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<style>
|
||||
/* Add custom classes and styles that you want inlined here */
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-light">
|
||||
<div class="container">
|
||||
<img
|
||||
alt="Revolt Logo"
|
||||
class="ax-center my-10 w-24"
|
||||
src="https://app.revolt.chat/assets/logo_round.png"
|
||||
/>
|
||||
<div class="card p-6 p-lg-10 space-y-4">
|
||||
<h1 class="h3 fw-700">Account Suspended</h1>
|
||||
<p>Your account has been suspended, for one or more reasons:</p>
|
||||
<ul>
|
||||
{{list}}
|
||||
</ul>
|
||||
<p style="display:{{duration_display}}">
|
||||
You will be able to use your account again in {{duration}} days.
|
||||
</p>
|
||||
<p>
|
||||
Further violations may result in a permanent ban depending on
|
||||
severity, please abide by the
|
||||
<a href="https://revolt.chat/aup">Acceptable Usage Policy</a>.
|
||||
</p>
|
||||
<p>Ban evasion is prohibited and will be dealt with accordingly.</p>
|
||||
</div>
|
||||
<div class="text-muted text-center my-6">
|
||||
This email is intended for {{email}}<br />
|
||||
Sent from Revolt<br />
|
||||
Made in Europe
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
12
crates/core/database/templates/suspension.txt
Normal file
12
crates/core/database/templates/suspension.txt
Normal file
@@ -0,0 +1,12 @@
|
||||
Your account has been suspended, for one or more reasons:
|
||||
{{list}}
|
||||
|
||||
You will be able to use your account again in {{duration}} days.
|
||||
|
||||
Further violations may result in a permanent ban depending on severity, please abide by the Acceptable Usage Policy (https://revolt.chat/aup).
|
||||
|
||||
Ban evasion is prohibited and will be dealt with accordingly.
|
||||
|
||||
This email is intended for {{email}}
|
||||
Sent by Revolt
|
||||
Made in Europe
|
||||
Reference in New Issue
Block a user