diff --git a/package-lock.json b/package-lock.json
index 3d57398a..3b598d73 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -27,6 +27,7 @@
"fs-extra": "^8.1.0",
"got": "^11.8.2",
"hcaptcha": "^0.1.0",
+ "he": "^1.2.0",
"image-pixels": "^1.1.1",
"ip2location-nodejs": "^9.6.3",
"is-valid-hostname": "^1.0.2",
@@ -58,6 +59,7 @@
"@types/dicer": "^0.2.2",
"@types/express": "^4.17.17",
"@types/fs-extra": "^11.0.1",
+ "@types/he": "^1.2.3",
"@types/morgan": "^1.9.4",
"@types/ndarray": "^1.0.11",
"@types/node": "^18.14.4",
@@ -2381,6 +2383,7 @@
"resolved": "https://registry.npmjs.org/@redis/client/-/client-1.6.1.tgz",
"integrity": "sha512-/KCsg3xSlR+nCK8/8ZYSknYxvXHwubJrU82F3Lm1Fp6789VQ0/3RJKfsmRXjqfaTA++23CvC3hqmqe/2GEt6Kw==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"cluster-key-slot": "1.1.2",
"generic-pool": "3.9.0",
@@ -3344,6 +3347,13 @@
"@types/node": "*"
}
},
+ "node_modules/@types/he": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@types/he/-/he-1.2.3.tgz",
+ "integrity": "sha512-q67/qwlxblDzEDvzHhVkwc1gzVWxaNxeyHUBF4xElrvjL11O+Ytze+1fGpBHlr/H9myiBUaUXNnNPmBHxxfAcA==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@types/http-cache-semantics": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz",
@@ -3419,6 +3429,7 @@
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.112.tgz",
"integrity": "sha512-i+Vukt9POdS/MBI7YrrkkI5fMfwFtOjphSmt4WXYLfwqsfr6z/HdCx7LqT9M7JktGob8WNgj8nFB4TbGNE4Cog==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"undici-types": "~5.26.4"
}
@@ -3564,6 +3575,7 @@
"integrity": "sha512-4O3idHxhyzjClSMJ0a29AcoK0+YwnEqzI6oz3vlRf3xw0zbzt15MzXwItOlnr5nIth6zlY2RENLsOPvhyrKAQA==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"@typescript-eslint/scope-manager": "8.34.1",
"@typescript-eslint/types": "8.34.1",
@@ -4049,6 +4061,7 @@
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"dev": true,
"license": "MIT",
+ "peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -5842,6 +5855,7 @@
"integrity": "sha512-GsGizj2Y1rCWDu6XoEekL3RLilp0voSePurjZIkxL3wlm5o5EC9VpgaP7lrCvjnkuLvzFBQWB3vWB3K5KQTveQ==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.12.1",
@@ -5988,6 +6002,7 @@
"integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"@rtsao/scc": "^1.1.0",
"array-includes": "^3.1.8",
@@ -7116,6 +7131,15 @@
"integrity": "sha512-iMrDmH2VpIEKOrcKWidVjI89FdDKTEdZ7PfPWkP27sTazIIkob8YfdY2ezaufAnWBiUUcvzsn0qF+dyXtBH2Vw==",
"license": "MIT"
},
+ "node_modules/he": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
+ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
+ "license": "MIT",
+ "bin": {
+ "he": "bin/he"
+ }
+ },
"node_modules/http-cache-semantics": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz",
@@ -8416,6 +8440,7 @@
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-7.8.7.tgz",
"integrity": "sha512-5Bo4CrUxrPITrhMKsqUTOkXXo2CoRC5tXxVQhnddCzqDMwRXfyStrxj1oY865g8gaekSBhxAeNkYyUSJvGm9Hw==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"bson": "^5.5.0",
"kareem": "2.5.1",
@@ -10654,6 +10679,7 @@
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
"license": "Apache-2.0",
+ "peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
diff --git a/package.json b/package.json
index 7d0c32bf..8e844b4d 100644
--- a/package.json
+++ b/package.json
@@ -43,6 +43,7 @@
"fs-extra": "^8.1.0",
"got": "^11.8.2",
"hcaptcha": "^0.1.0",
+ "he": "^1.2.0",
"image-pixels": "^1.1.1",
"ip2location-nodejs": "^9.6.3",
"is-valid-hostname": "^1.0.2",
@@ -74,6 +75,7 @@
"@types/dicer": "^0.2.2",
"@types/express": "^4.17.17",
"@types/fs-extra": "^11.0.1",
+ "@types/he": "^1.2.3",
"@types/morgan": "^1.9.4",
"@types/ndarray": "^1.0.11",
"@types/node": "^18.14.4",
@@ -88,4 +90,4 @@
"ndarray": "^1.0.19",
"typescript": "^4.9.5"
}
-}
\ No newline at end of file
+}
diff --git a/src/assets/emails/confirmationTemplate.html b/src/assets/emails/confirmationTemplate.html
deleted file mode 100644
index 0a946255..00000000
--- a/src/assets/emails/confirmationTemplate.html
+++ /dev/null
@@ -1,203 +0,0 @@
-
-
-
-
-
-
-
-
- Hello {{username}}! Your Pretendo Network ID activation is almost complete. Please click the link in this email to confirm your e-mail address and complete the activation process.
-
-
-
-
-
-
-
-
- | |
-
-
-
- | |
-
-
-
-
-
-
-
-
-
- |
-
-
- | |
-
-
-
-
-
- | |
-
-
-
- | |
-
-
- |
- Hello {{username}}!
- |
-
-
- | |
-
-
- |
- Your Pretendo Network ID activation is almost complete. Please click the link below to confirm your e-mail address and complete the activation process.
- |
-
-
- | |
-
-
- |
-
- Confirm email address
-
- |
-
-
- | |
-
-
- |
- You may also enter the following 6-digit code on your console:
- |
-
-
- | |
-
-
- |
- {{confirmation-code}}
- |
-
-
- | |
-
-
- |
- We hope you have fun using our services!
- |
-
-
- | |
-
-
- |
- The Pretendo Network team
- |
-
-
- | |
-
-
- |
- |
-
-
- |
-
-
- | |
-
-
- |
- Note: this email message was auto-generated, please do not respond. For further assistance, please join our Discord server or make a post on our Forum.
- |
-
-
- | |
-
-
- |
-
-
- |
- |
-
-
- |
-
-
- |
-
-
-
-
\ No newline at end of file
diff --git a/src/assets/emails/genericTemplate.html b/src/assets/emails/genericTemplate.html
index ce875fe9..0c5955fb 100644
--- a/src/assets/emails/genericTemplate.html
+++ b/src/assets/emails/genericTemplate.html
@@ -1,5 +1,7 @@
-
+
+
@@ -8,45 +10,65 @@
:root {
color-scheme: light dark;
- supported-color-schemes:light dark;
+ supported-color-schemes: light dark;
}
@media (prefers-color-scheme: light) {
body.email-body,
table.centerer,
table.wrapper {
- background-color: #FFFFFF !important;
- color: #FFFFFF !important;
+ background-color: #673DB6 !important;
+ color: #673DB6 !important;
}
table.card {
- background-color: #673DB6 !important;
+ background-color: #fff !important;
}
span.shoutout {
- color: #D9C6FA !important;
+ color: #9D6FF3 !important;
+ }
+ td {
+ color: #673DB6 !important;
}
- td.confirm-link {
+ td a {
+ color: #673DB6 !important;
+ font-weight: 700 !important;
+ text-decoration: underline !important;
+ }
+ td.primary,
+ td.primary a,
+ td.primary span {
background-color: #9D6FF3 !important;
+ color: #fff !important;
}
- td.confirm-code {
+ td.secondary,
+ td.secondary a,
+ td.secondary span {
background-color: #D9C6FA !important;
color: #45297A !important;
}
td.notice {
- color: #9D6FF3 !important;
+ color: #c5adf2 !important;
}
td.notice a {
- color: #673DB6 !important;
+ color: #fff !important;
+ font-weight: 700 !important;
+ }
+ td strong,
+ td b {
+ font-weight: 700 !important;
+ color: #9D6FF3 !important;
}
img.logo {
- content: url("https://assets.pretendo.cc/images/pretendo-wordmark-singlecolor-purple.png") !important;
+ content: url("https://assets.pretendo.cc/images/wordmark-white.png") !important;
}
}
+
@media (prefers-color-scheme: dark) {
body.email-body,
table.centerer,
table.wrapper {
background-color: #1B1F3B !important;
- color: #FFFFFF !important;
+ color: #A1A8D9 !important;
}
table.card {
background-color: #23274A !important;
@@ -54,37 +76,131 @@
span.shoutout {
color: #CAB1FB !important;
}
- td.confirm-link {
+ td {
+ color: #A1A8D9 !important;
+ }
+ td a {
+ color: #fff !important;
+ font-weight: 700 !important;
+ text-decoration: underline !important;
+ }
+ td.header {
+ color: #fff !important;
+ }
+ td.primary {
background-color: #673DB6 !important;
}
- td.confirm-code {
+ td.secondary {
background-color: #373C65 !important;
- color: #ffffff !important;
+ color: #fff !important;
+ }
+ td.signature {
+ color: #A1A8D9 !important;
}
td.notice {
color: #8990C1 !important;
}
td.notice a {
- color: #CAC1F5 !important;
+ color: #fff !important;
+ }
+ td strong,
+ td b {
+ font-weight: 700 !important;
+ color: #fff !important;
}
+ /* isn't this redundant? no. icloud web doesn't display the logo correctly without this. */
+ img.logo {
+ content: url("https://assets.pretendo.cc/images/wordmark-purple-white.png") !important;
+ }
+ }
+
+ td.button a,
+ td.button span,
+ u+.email-body td.button a,
+ u+.email-body td.button span {
+ text-decoration: none !important;
+ }
+
+ /* the following specifically targets gmail, because it doesn't support media queries and automatically inverts colors */
+ u+.email-body,
+ u+.email-body table.centerer,
+ u+.email-body table.wrapper {
+ background-color: #1B1F3B !important;
+ background-image: linear-gradient(#1B1F3B, #1B1F3B) !important;
+ color: #fff !important;
+ }
+ u+.email-body table.card {
+ background-color: #23274A !important;
+ background-image: linear-gradient(#23274A, #23274A) !important;
+ }
+ u+.email-body span.shoutout {
+ color: #fff !important;
+ }
+ u+.email-body td {
+ color: #fff !important;
+ }
+ u+.email-body td a {
+ color: #fff !important;
+ font-weight: 700 !important;
+ text-decoration: underline !important;
+ }
+ u+.email-body td.primary,
+ u+.email-body td.primary a,
+ u+.email-body td.primary span {
+ background-color: #9D6FF3 !important;
+ background-image: linear-gradient(#9D6FF3, #9D6FF3) !important;
+ color: #fff !important;
+ }
+ u+.email-body td.secondary,
+ u+.email-body td.secondary a,
+ u+.email-body td.secondary span {
+ background-color: #373C65 !important;
+ background-image: linear-gradient(#373C65, #373C65) !important;
+ color: #fff !important;
+ }
+ u+.email-body td.notice {
+ color: #fff !important;
+ }
+ u+.email-body td.notice a {
+ color: #fff !important;
+ font-weight: 700 !important;
+ }
+ u+.email-body strong,
+ u+.email-body b {
+ font-weight: 700 !important;
+ color: #fff !important;
+ }
+ u+.email-body .gmail-s {
+ background: #000 !important;
+ mix-blend-mode: screen !important;
+ }
+ u+.email-body .gmail-d {
+ background: #000 !important;
+ mix-blend-mode: difference !important;
}
-
- {{preview}}
-
+
+
+
+
+
+
-
+
- | |
+ |
- | |
+ |
|
@@ -92,72 +208,71 @@
|
-
-
+
+
|
- | |
+ |
-
+
- | |
+ |
-
- | |
-
-
- |
- Dear {{username}},
- |
-
-
- | |
-
-
- |
- {{paragraph}}
- |
-
-
+
+
+
- | |
+ |
+
- |
- The Pretendo Network team
+ |
+
+
+ The Pretendo Network team
+
+
|
- | |
+ |
|
- |
+ |
- | |
+ |
- |
- Note: this email message was auto-generated, please do not respond. For further assistance, please join our Discord server or make a post on our Forum.
+ |
+
+
+ Note: This is an automatic email; please do not respond. For assistance, please
+ visit forum.pretendo.network.
+
+
|
- | |
+ |
|
|
- |
+ |
|
@@ -166,5 +281,7 @@
+
+
\ No newline at end of file
diff --git a/src/assets/images/wordmark-purple-white.png b/src/assets/images/wordmark-purple-white.png
new file mode 100644
index 00000000..66c89d53
Binary files /dev/null and b/src/assets/images/wordmark-purple-white.png differ
diff --git a/src/assets/images/wordmark-white.png b/src/assets/images/wordmark-white.png
new file mode 100644
index 00000000..f2fe07fe
Binary files /dev/null and b/src/assets/images/wordmark-white.png differ
diff --git a/src/mailer.ts b/src/mailer.ts
index 0744e14b..eb7b7a0a 100644
--- a/src/mailer.ts
+++ b/src/mailer.ts
@@ -2,11 +2,11 @@ import path from 'node:path';
import fs from 'node:fs';
import nodemailer from 'nodemailer';
import * as aws from '@aws-sdk/client-ses';
+import { encode } from 'he';
import { config, disabledFeatures } from '@/config-manager';
import type { MailerOptions } from '@/types/common/mailer-options';
const genericEmailTemplate = fs.readFileSync(path.join(__dirname, './assets/emails/genericTemplate.html'), 'utf8');
-const confirmationEmailTemplate = fs.readFileSync(path.join(__dirname, './assets/emails/confirmationTemplate.html'), 'utf8');
let transporter: nodemailer.Transporter;
@@ -28,31 +28,208 @@ if (!disabledFeatures.email) {
});
}
-export async function sendMail(options: MailerOptions): Promise {
- if (!disabledFeatures.email) {
- const { to, subject, username, paragraph, preview, text, link, confirmation } = options;
+interface emailComponent {
+ type: 'header' | 'paragraph';
+ text: string;
+ replacements?: emailTextReplacements;
+}
+interface paddingComponent {
+ type: 'padding';
+ size: number;
+}
+interface buttonComponent {
+ type: 'button';
+ text: string;
+ link?: string;
+ primary?: boolean;
+}
+interface emailTextReplacements {
+ [key: string]: string;
+}
+
+export class CreateEmail {
+ // an array which stores all components of the email
+ private readonly componentArray: (emailComponent | paddingComponent | buttonComponent)[] = [];
+
+ /**
+ * adds padding of the specified height in em units
+ */
+ private addPadding(size: number): paddingComponent {
+ return {
+ type: 'padding',
+ size
+ };
+ }
+
+ /**
+ * adds a header. for greetings, do addHeader("Hi {{pnid}}!", { pnid: "theUsername" })
+ */
+ public addHeader(text: string, replacements?: emailTextReplacements): this {
+ const component: emailComponent = { type: 'header', text, replacements };
+ this.componentArray.push(this.addPadding(3), component, this.addPadding(2));
+
+ return this;
+ }
- let html = confirmation ? confirmationEmailTemplate : genericEmailTemplate;
+ /**
+ * adds a paragraph. for links, do addParagraph("this is a [named link](https://example.org)."). for greetings, do addParagraph("Hi {{pnid}}!", { pnid: "theUsername" })
+ */
+ public addParagraph(text: string, replacements?: emailTextReplacements): this {
+ const component: emailComponent = { type: 'paragraph', text, replacements };
+ this.componentArray.push(component, this.addPadding(1));
- html = html.replace(/{{username}}/g, username);
- html = html.replace(/{{paragraph}}/g, paragraph || '');
- html = html.replace(/{{preview}}/g, preview || '');
- html = html.replace(/{{confirmation-href}}/g, confirmation?.href || '');
- html = html.replace(/{{confirmation-code}}/g, confirmation?.code || '');
+ return this;
+ }
+
+ /**
+ * adds a button
+ *
+ * @param {String} text the button text
+ * @param {String} [link] the link
+ * @param {boolean} [primary] set to false to use the secondary button styles (true by default)
+ */
+ public addButton(text: string, link?: string, primary: boolean = true): this {
+ const component: buttonComponent = { type: 'button', text, link, primary };
+ this.componentArray.push(component, this.addPadding(2));
+
+ return this;
+ }
+
+ private addGmailDarkModeFix(el: string): string {
+ return ``;
+ }
- if (link) {
- const { href, text } = link;
+ // parses pnid name and links. set the plaintext bool (false by default) to use no html
+ private parseReplacements(c: emailComponent, plainText: boolean = false): string {
+ let tempText = c.text;
- const button = `| | | ${text} | `;
- html = html.replace(//g, button);
+ // for now only replaces the pnid for shoutouts. could easily be expanded to add more.
+ if (c?.replacements) {
+ Object.entries(c.replacements).forEach(([key, value]) => {
+ const safeValue = encode(value);
+
+ if (key === 'pnid') {
+ if (plainText) {
+ tempText = tempText.replace(/{{pnid}}/g, safeValue);
+ } else {
+ tempText = tempText.replace(/{{pnid}}/g, `${safeValue}`);
+ }
+ }
+ });
+ }
+
+ // wrap and in a element, to fix color on thunderbird and weight on icloud mail web
+ const bRegex = /.*?<\/b>|.*?<\/strong>/g;
+
+ if (!plainText) {
+ tempText = tempText.replace(bRegex, el => `${el}`);
}
+ // replace [links](https://example.com) with html anchor tags or a plaintext representation
+ const linkRegex = /\[(?.*?)\]\((?.*?)\)/g;
+
+ if (plainText) {
+ tempText = tempText.replace(linkRegex, '$ ($)');
+ } else {
+ tempText = tempText.replace(linkRegex, '$');
+ }
+
+ return tempText;
+ }
+
+ // generates the html version of the email
+ public toHTML(): string {
+ let innerHTML = '';
+
+ this.componentArray.map((c, i) => {
+ let el = '';
+
+ /* double padding causes issues, and the signature already has padding, so if the last element
+ * is padding we just yeet it
+ */
+ if (i === this.componentArray.length - 1 && c.type === 'padding') {
+ return;
+ }
+
+ switch (c.type) {
+ case 'padding':
+ innerHTML += `\n| | `;
+ break;
+ case 'header':
+ el = this.parseReplacements(c);
+ innerHTML += `\n `;
+ break;
+ case 'paragraph':
+ el = this.parseReplacements(c);
+ innerHTML += `\n| ${this.addGmailDarkModeFix(el)} | `;
+ break;
+ case 'button':
+ if (c.link) {
+ el = `${el}`;
+ } else {
+ el = `${el}`;
+ }
+ innerHTML += `\n| ${this.addGmailDarkModeFix(el)} | `;
+ break;
+ }
+ });
+
+ const generatedHTML = genericEmailTemplate
+ .replace('', innerHTML)
+ .replace('', this.toPlainText());
+
+ return generatedHTML;
+ }
+
+ // generates the plaintext version that shows up on the email preview (and is shown by plaintext clients)
+ public toPlainText(): string {
+ let plainText = '';
+
+ this.componentArray.forEach((c) => {
+ let el = '';
+ switch (c.type) {
+ case 'padding':
+ break;
+ case 'header':
+ el = this.parseReplacements(c, true);
+ plainText += `\n${el}`;
+ break;
+ case 'paragraph':
+ el = this.parseReplacements(c, true);
+ plainText += `\n${el}`;
+ break;
+ case 'button':
+ if (c.link) {
+ plainText += `\n\n${c.text}: ${c.link}\n`;
+ } else {
+ plainText += ` ${c.text}\n`;
+ }
+ break;
+ }
+ });
+
+ // the signature is baked into the template, so it needs to be added manually to the plaintext version
+ plainText += '\n\n- The Pretendo Network team';
+
+ // and so is the notice about the email being auto-generated
+ plainText += '\n\nNote: This is an automatic email; please do not respond. For assistance, please visit https://forum.pretendo.network.';
+
+ plainText = plainText.replace(/(<([^>]+)>)/gi, '');
+
+ return plainText;
+ }
+}
+
+export async function sendMail(options: MailerOptions): Promise {
+ if (!disabledFeatures.email) {
+ const { to, subject, email } = options;
+
await transporter.sendMail({
from: config.email.from,
to,
subject,
- text,
- html
+ text: email.toPlainText(),
+ html: email.toHTML()
});
}
}
diff --git a/src/types/common/mailer-options.ts b/src/types/common/mailer-options.ts
index d3999f71..f8a31efb 100644
--- a/src/types/common/mailer-options.ts
+++ b/src/types/common/mailer-options.ts
@@ -1,16 +1,7 @@
+import type { CreateEmail } from '@/mailer';
+
export interface MailerOptions {
to: string;
subject: string;
- username: string;
- paragraph?: string;
- preview?: string;
- text: string;
- link?: {
- href: string;
- text: string;
- };
- confirmation?: {
- href: string;
- code: string;
- };
+ email: CreateEmail;
}
diff --git a/src/util.ts b/src/util.ts
index f364d0a1..41d90ae4 100644
--- a/src/util.ts
+++ b/src/util.ts
@@ -4,7 +4,7 @@ import { S3 } from '@aws-sdk/client-s3';
import fs from 'fs-extra';
import bufferCrc32 from 'buffer-crc32';
import { crc32 } from 'crc';
-import { sendMail } from '@/mailer';
+import { sendMail, CreateEmail } from '@/mailer';
import { SystemType } from '@/types/common/system-types';
import { TokenType } from '@/types/common/token-types';
import { config, disabledFeatures } from '@/config-manager';
@@ -201,39 +201,47 @@ export function nascError(errorCode: string): URLSearchParams {
}
export async function sendConfirmationEmail(pnid: mongoose.HydratedDocument): Promise {
+ const email = new CreateEmail()
+ .addHeader('Hello {{pnid}}!', { pnid: pnid.username })
+ .addParagraph('Your Pretendo Network ID activation is almost complete. Please click the link below to confirm your e-mail address and complete the activation process.')
+ .addButton('Confirm email address', `https://api.pretendo.cc/v1/email/verify?token=${pnid.identification.email_token}`)
+ .addParagraph('You may also enter the following 6-digit code on your console:')
+ .addButton(pnid.identification.email_code, '', false)
+ .addParagraph('We hope you have fun using our services!');
+
const options = {
to: pnid.email.address,
subject: '[Pretendo Network] Please confirm your email address',
- username: pnid.username,
- confirmation: {
- href: `https://api.pretendo.cc/v1/email/verify?token=${pnid.identification.email_token}`,
- code: pnid.identification.email_code
- },
- text: `Hello ${pnid.username}! \r\n\r\nYour Pretendo Network ID activation is almost complete. Please click the link to confirm your e-mail address and complete the activation process: \r\nhttps://api.pretendo.cc/v1/email/verify?token=${pnid.identification.email_token} \r\n\r\nYou may also enter the following 6-digit code on your console: ${pnid.identification.email_code}`
+ email
};
await sendMail(options);
}
export async function sendEmailConfirmedEmail(pnid: mongoose.HydratedDocument): Promise {
+ const email = new CreateEmail()
+ .addHeader('Dear {{pnid}}!', { pnid: pnid.username })
+ .addParagraph('Your email address has been confirmed.')
+ .addParagraph('We hope you have fun on Pretendo Network!');
+
const options = {
to: pnid.email.address,
subject: '[Pretendo Network] Email address confirmed',
- username: pnid.username,
- paragraph: 'your email address has been confirmed. We hope you have fun on Pretendo Network!',
- text: `Dear ${pnid.username}, \r\n\r\nYour email address has been confirmed. We hope you have fun on Pretendo Network!`
+ email
};
await sendMail(options);
}
export async function sendEmailConfirmedParentalControlsEmail(pnid: mongoose.HydratedDocument): Promise {
+ const email = new CreateEmail()
+ .addHeader('Dear {{pnid}},', { pnid: pnid.username })
+ .addParagraph('your email address has been confirmed for use with Parental Controls.');
+
const options = {
to: pnid.email.address,
subject: '[Pretendo Network] Email address confirmed for Parental Controls',
- username: pnid.username,
- paragraph: 'your email address has been confirmed for use with Parental Controls.',
- text: `Dear ${pnid.username}, \r\n\r\nYour email address has been confirmed for use with Parental Controls.`
+ email
};
await sendMail(options);
@@ -254,31 +262,31 @@ export async function sendForgotPasswordEmail(pnid: mongoose.HydratedDocument {
+export async function sendPNIDDeletedEmail(emailAddress: string, username: string): Promise {
+ const email = new CreateEmail()
+ .addHeader('Dear {{pnid}},', { pnid: username })
+ .addParagraph('your PNID has successfully been deleted.')
+ .addParagraph('If you had a tier subscription, a separate cancellation email will be sent. If you do not receive this cancellation email, or you are still being charged for your subscription, please contact @jonbarrow on our [Discord server](https://discord.pretendo.network/).');
+
const options = {
- to: email,
+ to: emailAddress,
subject: '[Pretendo Network] PNID Deleted',
- username: username,
- link: {
- text: 'Discord Server',
- href: 'https://discord.com/invite/pretendo'
- },
- text: `Your PNID ${username} has successfully been deleted. If you had a tier subscription, a separate cancellation email will be sent. If you do not receive this cancellation email, or your subscription is still being charged, please contact @jon on our Discord server`
+ email
};
await sendMail(options);
|