@wopr-network/platform-core 1.54.0 → 1.56.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth/better-auth.d.ts +2 -0
- package/dist/auth/better-auth.js +2 -2
- package/dist/email/default-templates.js +49 -49
- package/dist/email/drizzle-notification-template-repository.d.ts +11 -0
- package/dist/email/drizzle-notification-template-repository.js +18 -0
- package/dist/email/notification-service.d.ts +4 -1
- package/dist/email/notification-service.js +37 -31
- package/dist/email/notification-service.test.js +14 -0
- package/dist/email/notification-templates.js +154 -111
- package/dist/email/templates.d.ts +9 -9
- package/dist/email/templates.js +61 -55
- package/dist/email/templates.test.js +1 -1
- package/package.json +1 -1
- package/src/auth/better-auth.ts +6 -2
- package/src/email/default-templates.ts +56 -56
- package/src/email/drizzle-notification-template-repository.ts +27 -0
- package/src/email/notification-service.test.ts +14 -0
- package/src/email/notification-service.ts +36 -30
- package/src/email/notification-templates.ts +155 -107
- package/src/email/templates.test.ts +1 -1
- package/src/email/templates.ts +67 -47
|
@@ -10,7 +10,7 @@ import { escapeHtml } from "./resend-adapter.js";
|
|
|
10
10
|
// ---------------------------------------------------------------------------
|
|
11
11
|
// Shared layout helpers (duplicated locally so this file is self-contained)
|
|
12
12
|
// ---------------------------------------------------------------------------
|
|
13
|
-
function wrapHtml(title, bodyContent) {
|
|
13
|
+
function wrapHtml(title, bodyContent, brandName = "WOPR") {
|
|
14
14
|
return `<!DOCTYPE html>
|
|
15
15
|
<html>
|
|
16
16
|
<head>
|
|
@@ -25,7 +25,7 @@ function wrapHtml(title, bodyContent) {
|
|
|
25
25
|
<table role="presentation" style="width: 600px; margin: 0 auto; background-color: #ffffff; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
|
|
26
26
|
${bodyContent}
|
|
27
27
|
</table>
|
|
28
|
-
<p style="margin-top: 20px; color: #a0aec0; font-size: 12px;">© ${new Date().getFullYear()}
|
|
28
|
+
<p style="margin-top: 20px; color: #a0aec0; font-size: 12px;">© ${new Date().getFullYear()} ${escapeHtml(brandName)}. All rights reserved.</p>
|
|
29
29
|
</td>
|
|
30
30
|
</tr>
|
|
31
31
|
</table>
|
|
@@ -60,34 +60,40 @@ function footer(text) {
|
|
|
60
60
|
</td>
|
|
61
61
|
</tr>`;
|
|
62
62
|
}
|
|
63
|
-
function copyright() {
|
|
64
|
-
return `\n\n(c) ${new Date().getFullYear()}
|
|
63
|
+
function copyright(brandName = "WOPR") {
|
|
64
|
+
return `\n\n(c) ${new Date().getFullYear()} ${brandName}. All rights reserved.`;
|
|
65
|
+
}
|
|
66
|
+
/** Extract the brand name from template data, defaulting to "WOPR". */
|
|
67
|
+
function brand(data) {
|
|
68
|
+
return data.brandName || "WOPR";
|
|
65
69
|
}
|
|
66
70
|
// ---------------------------------------------------------------------------
|
|
67
71
|
// Individual template renderers
|
|
68
72
|
// ---------------------------------------------------------------------------
|
|
69
73
|
function creditsDepletedTemplate(data) {
|
|
74
|
+
const b = brand(data);
|
|
70
75
|
const creditsUrl = data.creditsUrl || "";
|
|
71
76
|
const parts = [
|
|
72
|
-
heading(
|
|
73
|
-
paragraph(
|
|
77
|
+
heading(`Your ${escapeHtml(b)} Credits Are Depleted`),
|
|
78
|
+
paragraph(`<p>Your ${escapeHtml(b)} credit balance has reached $0. All agent capabilities have been paused.</p>` +
|
|
74
79
|
"<p>Add credits now to resume service immediately.</p>"),
|
|
75
80
|
];
|
|
76
81
|
if (creditsUrl)
|
|
77
82
|
parts.push(button(creditsUrl, "Add Credits"));
|
|
78
83
|
parts.push(footer("Your data is preserved. Add credits to reactivate."));
|
|
79
84
|
return {
|
|
80
|
-
subject:
|
|
81
|
-
html: wrapHtml("Credits Depleted", parts.join("\n")),
|
|
82
|
-
text: `Your
|
|
85
|
+
subject: `Your ${b} credits are depleted — capabilities paused`,
|
|
86
|
+
html: wrapHtml("Credits Depleted", parts.join("\n"), b),
|
|
87
|
+
text: `Your ${b} Credits Are Depleted\n\nYour ${b} credit balance has reached $0. All agent capabilities have been paused.\n\nAdd credits now to resume service immediately.\n${creditsUrl ? `\nAdd credits: ${creditsUrl}\n` : ""}${copyright(b)}`,
|
|
83
88
|
};
|
|
84
89
|
}
|
|
85
90
|
function gracePeriodStartTemplate(data) {
|
|
91
|
+
const b = brand(data);
|
|
86
92
|
const balanceDollars = escapeHtml(data.balanceDollars || "$0.00");
|
|
87
93
|
const graceDays = Number(data.graceDays) || 7;
|
|
88
94
|
const creditsUrl = data.creditsUrl || "";
|
|
89
95
|
const parts = [
|
|
90
|
-
heading(
|
|
96
|
+
heading(`Action Needed: Top Up to Keep Your ${escapeHtml(b)} Agents Running`),
|
|
91
97
|
paragraph(`<p>Your current balance is <strong>${balanceDollars}</strong> and the monthly deduction could not be processed.</p>` +
|
|
92
98
|
`<p>You have a <strong>${graceDays}-day grace period</strong> to add credits before your account is suspended.</p>`),
|
|
93
99
|
];
|
|
@@ -95,15 +101,16 @@ function gracePeriodStartTemplate(data) {
|
|
|
95
101
|
parts.push(button(creditsUrl, "Add Credits Now"));
|
|
96
102
|
parts.push(footer("This is a critical notification about your account status."));
|
|
97
103
|
return {
|
|
98
|
-
subject:
|
|
99
|
-
html: wrapHtml("Grace Period Started", parts.join("\n")),
|
|
100
|
-
text: `Action Needed: Top Up to Keep Your
|
|
104
|
+
subject: `Action needed: top up to keep your ${b} agents running`,
|
|
105
|
+
html: wrapHtml("Grace Period Started", parts.join("\n"), b),
|
|
106
|
+
text: `Action Needed: Top Up to Keep Your ${b} Agents Running\n\nYour current balance is ${data.balanceDollars} and the monthly deduction could not be processed.\n\nYou have a ${graceDays}-day grace period to add credits before your account is suspended.\n${creditsUrl ? `\nAdd credits: ${creditsUrl}\n` : ""}${copyright(b)}`,
|
|
101
107
|
};
|
|
102
108
|
}
|
|
103
109
|
function gracePeriodWarningTemplate(data) {
|
|
110
|
+
const b = brand(data);
|
|
104
111
|
const creditsUrl = data.creditsUrl || "";
|
|
105
112
|
const parts = [
|
|
106
|
-
heading("Last Chance: Your
|
|
113
|
+
heading("Last Chance: Your Agents Will Be Suspended Tomorrow"),
|
|
107
114
|
paragraph("<p>Your grace period expires tomorrow. If you do not add credits, your account will be suspended.</p>" +
|
|
108
115
|
"<p>Add credits now to keep your agents running.</p>"),
|
|
109
116
|
];
|
|
@@ -111,17 +118,18 @@ function gracePeriodWarningTemplate(data) {
|
|
|
111
118
|
parts.push(button(creditsUrl, "Add Credits Now", "#dc2626"));
|
|
112
119
|
parts.push(footer("This is a critical notification about your account status."));
|
|
113
120
|
return {
|
|
114
|
-
subject: "Last chance: your
|
|
115
|
-
html: wrapHtml("Grace Period Warning", parts.join("\n")),
|
|
116
|
-
text: `Last Chance: Your
|
|
121
|
+
subject: "Last chance: your agents will be suspended tomorrow",
|
|
122
|
+
html: wrapHtml("Grace Period Warning", parts.join("\n"), b),
|
|
123
|
+
text: `Last Chance: Your Agents Will Be Suspended Tomorrow\n\nYour grace period expires tomorrow. If you do not add credits, your account will be suspended.\n${creditsUrl ? `\nAdd credits: ${creditsUrl}\n` : ""}${copyright(b)}`,
|
|
117
124
|
};
|
|
118
125
|
}
|
|
119
126
|
function autoSuspendedTemplate(data) {
|
|
127
|
+
const b = brand(data);
|
|
120
128
|
const reason = escapeHtml(data.reason || "Grace period expired");
|
|
121
129
|
const creditsUrl = data.creditsUrl || "";
|
|
122
130
|
const parts = [
|
|
123
131
|
heading("Your Account Has Been Suspended"),
|
|
124
|
-
paragraph(`<p>Your
|
|
132
|
+
paragraph(`<p>Your ${escapeHtml(b)} account has been automatically suspended.</p>` +
|
|
125
133
|
`<p><strong>Reason:</strong> ${reason}</p>` +
|
|
126
134
|
`<p>Add credits to reactivate your account immediately.</p>`),
|
|
127
135
|
];
|
|
@@ -130,11 +138,12 @@ function autoSuspendedTemplate(data) {
|
|
|
130
138
|
parts.push(footer("Your data is preserved for 30 days."));
|
|
131
139
|
return {
|
|
132
140
|
subject: "Your account has been suspended",
|
|
133
|
-
html: wrapHtml("Account Suspended", parts.join("\n")),
|
|
134
|
-
text: `Your Account Has Been Suspended\n\nReason: ${data.reason}\n\nAdd credits to reactivate your account immediately.\n${creditsUrl ? `\nAdd credits: ${creditsUrl}\n` : ""}${copyright()}`,
|
|
141
|
+
html: wrapHtml("Account Suspended", parts.join("\n"), b),
|
|
142
|
+
text: `Your Account Has Been Suspended\n\nReason: ${data.reason}\n\nAdd credits to reactivate your account immediately.\n${creditsUrl ? `\nAdd credits: ${creditsUrl}\n` : ""}${copyright(b)}`,
|
|
135
143
|
};
|
|
136
144
|
}
|
|
137
145
|
function autoTopupSuccessTemplate(data) {
|
|
146
|
+
const b = brand(data);
|
|
138
147
|
const amountDollars = escapeHtml(data.amountDollars || "$0.00");
|
|
139
148
|
const newBalanceDollars = escapeHtml(data.newBalanceDollars || "$0.00");
|
|
140
149
|
const creditsUrl = data.creditsUrl || "";
|
|
@@ -148,12 +157,14 @@ function autoTopupSuccessTemplate(data) {
|
|
|
148
157
|
parts.push(footer("Auto top-up keeps your agents running without interruption."));
|
|
149
158
|
return {
|
|
150
159
|
subject: `Auto top-up: ${data.amountDollars} credits added`,
|
|
151
|
-
html: wrapHtml("Auto Top-Up Successful", parts.join("\n")),
|
|
152
|
-
text: `Auto Top-Up: ${data.amountDollars} Credits Added\n\nYour auto top-up was successful. ${data.amountDollars} in credits has been added.\n\nYour new balance is ${data.newBalanceDollars}.\n${creditsUrl ? `\nView credits: ${creditsUrl}\n` : ""}${copyright()}`,
|
|
160
|
+
html: wrapHtml("Auto Top-Up Successful", parts.join("\n"), b),
|
|
161
|
+
text: `Auto Top-Up: ${data.amountDollars} Credits Added\n\nYour auto top-up was successful. ${data.amountDollars} in credits has been added.\n\nYour new balance is ${data.newBalanceDollars}.\n${creditsUrl ? `\nView credits: ${creditsUrl}\n` : ""}${copyright(b)}`,
|
|
153
162
|
};
|
|
154
163
|
}
|
|
155
164
|
function autoTopupFailedTemplate(data) {
|
|
165
|
+
const b = brand(data);
|
|
156
166
|
const creditsUrl = data.creditsUrl || "";
|
|
167
|
+
const supportEmail = data.supportEmail || "support@wopr.bot";
|
|
157
168
|
const parts = [
|
|
158
169
|
heading("Auto Top-Up Failed"),
|
|
159
170
|
paragraph("<p>Your auto top-up failed. We were unable to charge your payment method.</p>" +
|
|
@@ -161,11 +172,11 @@ function autoTopupFailedTemplate(data) {
|
|
|
161
172
|
];
|
|
162
173
|
if (creditsUrl)
|
|
163
174
|
parts.push(button(creditsUrl, "Add Credits"));
|
|
164
|
-
parts.push(footer(
|
|
175
|
+
parts.push(footer(`If you need help, contact ${escapeHtml(supportEmail)}.`));
|
|
165
176
|
return {
|
|
166
177
|
subject: "Auto top-up failed — update your payment method",
|
|
167
|
-
html: wrapHtml("Auto Top-Up Failed", parts.join("\n")),
|
|
168
|
-
text: `Auto Top-Up Failed\n\nYour auto top-up failed. We were unable to charge your payment method.\n\nPlease update your payment method or add credits manually to avoid service interruption.\n${creditsUrl ? `\nAdd credits: ${creditsUrl}\n` : ""}${copyright()}`,
|
|
178
|
+
html: wrapHtml("Auto Top-Up Failed", parts.join("\n"), b),
|
|
179
|
+
text: `Auto Top-Up Failed\n\nYour auto top-up failed. We were unable to charge your payment method.\n\nPlease update your payment method or add credits manually to avoid service interruption.\n${creditsUrl ? `\nAdd credits: ${creditsUrl}\n` : ""}${copyright(b)}`,
|
|
169
180
|
};
|
|
170
181
|
}
|
|
171
182
|
function cryptoPaymentConfirmedTemplate(data) {
|
|
@@ -176,76 +187,84 @@ function cryptoPaymentConfirmedTemplate(data) {
|
|
|
176
187
|
paragraph(`<p>Your crypto payment has been confirmed. <strong>${amountDollars}</strong> in credits has been added to your account.</p>` +
|
|
177
188
|
`<p>Your new balance is <strong>${newBalanceDollars}</strong>.</p>`),
|
|
178
189
|
];
|
|
179
|
-
|
|
190
|
+
const b = brand(data);
|
|
191
|
+
parts.push(footer(`Thank you for supporting ${escapeHtml(b)}!`));
|
|
180
192
|
return {
|
|
181
193
|
subject: `Crypto payment confirmed: ${data.amountDollars} credits added`,
|
|
182
|
-
html: wrapHtml("Crypto Payment Confirmed", parts.join("\n")),
|
|
183
|
-
text: `Crypto Payment Confirmed: ${data.amountDollars} Credits Added\n\nYour crypto payment has been confirmed. ${data.amountDollars} in credits has been added.\n\nYour new balance is ${data.newBalanceDollars}.\n${copyright()}`,
|
|
194
|
+
html: wrapHtml("Crypto Payment Confirmed", parts.join("\n"), b),
|
|
195
|
+
text: `Crypto Payment Confirmed: ${data.amountDollars} Credits Added\n\nYour crypto payment has been confirmed. ${data.amountDollars} in credits has been added.\n\nYour new balance is ${data.newBalanceDollars}.\n${copyright(b)}`,
|
|
184
196
|
};
|
|
185
197
|
}
|
|
186
198
|
function adminSuspendedTemplate(data) {
|
|
199
|
+
const b = brand(data);
|
|
187
200
|
const reason = escapeHtml(data.reason || "Policy violation");
|
|
201
|
+
const supportEmail = data.supportEmail || "support@wopr.bot";
|
|
188
202
|
const parts = [
|
|
189
203
|
heading("Your Account Has Been Suspended"),
|
|
190
|
-
paragraph(`<p>Your
|
|
204
|
+
paragraph(`<p>Your ${escapeHtml(b)} account has been suspended by an administrator.</p>` +
|
|
191
205
|
`<p><strong>Reason:</strong> ${reason}</p>` +
|
|
192
|
-
`<p>If you believe this is an error, please contact
|
|
206
|
+
`<p>If you believe this is an error, please contact ${escapeHtml(supportEmail)}.</p>`),
|
|
193
207
|
];
|
|
194
|
-
parts.push(footer(
|
|
208
|
+
parts.push(footer(`Contact ${escapeHtml(supportEmail)} if you have questions.`));
|
|
195
209
|
return {
|
|
196
210
|
subject: "Your account has been suspended",
|
|
197
|
-
html: wrapHtml("Account Suspended", parts.join("\n")),
|
|
198
|
-
text: `Your Account Has Been Suspended\n\nReason: ${data.reason}\n\nIf you believe this is an error, please contact
|
|
211
|
+
html: wrapHtml("Account Suspended", parts.join("\n"), b),
|
|
212
|
+
text: `Your Account Has Been Suspended\n\nReason: ${data.reason}\n\nIf you believe this is an error, please contact ${supportEmail}.\n${copyright(b)}`,
|
|
199
213
|
};
|
|
200
214
|
}
|
|
201
|
-
function adminReactivatedTemplate(
|
|
215
|
+
function adminReactivatedTemplate(data) {
|
|
216
|
+
const b = brand(data);
|
|
202
217
|
const parts = [
|
|
203
218
|
heading("Your Account Has Been Reactivated"),
|
|
204
|
-
paragraph(
|
|
219
|
+
paragraph(`<p>Your ${escapeHtml(b)} account has been reactivated. You now have full access to all services.</p>` +
|
|
205
220
|
"<p>Your agents and channels are ready to use.</p>"),
|
|
206
221
|
];
|
|
207
222
|
parts.push(footer("Welcome back!"));
|
|
208
223
|
return {
|
|
209
224
|
subject: "Your account has been reactivated",
|
|
210
|
-
html: wrapHtml("Account Reactivated", parts.join("\n")),
|
|
211
|
-
text: `Your Account Has Been Reactivated\n\nYour
|
|
225
|
+
html: wrapHtml("Account Reactivated", parts.join("\n"), b),
|
|
226
|
+
text: `Your Account Has Been Reactivated\n\nYour ${b} account has been reactivated. You now have full access to all services.\n${copyright(b)}`,
|
|
212
227
|
};
|
|
213
228
|
}
|
|
214
229
|
function creditsGrantedTemplate(data) {
|
|
230
|
+
const b = brand(data);
|
|
215
231
|
const amountDollars = escapeHtml(data.amountDollars || "$0.00");
|
|
216
232
|
const reason = escapeHtml(data.reason || "");
|
|
217
233
|
const parts = [
|
|
218
234
|
heading(`You Received ${amountDollars} in Credits`),
|
|
219
|
-
paragraph(`<p><strong>${amountDollars}</strong> in credits has been added to your
|
|
235
|
+
paragraph(`<p><strong>${amountDollars}</strong> in credits has been added to your ${escapeHtml(b)} account.</p>` +
|
|
220
236
|
(reason ? `<p><strong>Note:</strong> ${reason}</p>` : "")),
|
|
221
237
|
];
|
|
222
|
-
parts.push(footer(
|
|
238
|
+
parts.push(footer(`Thank you for using ${escapeHtml(b)}!`));
|
|
223
239
|
return {
|
|
224
240
|
subject: `You received ${data.amountDollars} in credits`,
|
|
225
|
-
html: wrapHtml("Credits Granted", parts.join("\n")),
|
|
226
|
-
text: `You Received ${data.amountDollars} in Credits\n\n${data.amountDollars} has been added to your account.${reason ? `\n\nNote: ${data.reason}` : ""}\n${copyright()}`,
|
|
241
|
+
html: wrapHtml("Credits Granted", parts.join("\n"), b),
|
|
242
|
+
text: `You Received ${data.amountDollars} in Credits\n\n${data.amountDollars} has been added to your account.${reason ? `\n\nNote: ${data.reason}` : ""}\n${copyright(b)}`,
|
|
227
243
|
};
|
|
228
244
|
}
|
|
229
245
|
function roleChangedTemplate(data) {
|
|
246
|
+
const b = brand(data);
|
|
230
247
|
const newRole = escapeHtml(data.newRole || "");
|
|
248
|
+
const supportEmail = data.supportEmail || "support@wopr.bot";
|
|
231
249
|
const parts = [
|
|
232
250
|
heading("Your Role Has Been Updated"),
|
|
233
|
-
paragraph(`<p>Your role on the
|
|
251
|
+
paragraph(`<p>Your role on the ${escapeHtml(b)} platform has been updated to <strong>${newRole}</strong>.</p>` +
|
|
234
252
|
"<p>Your new permissions are now active.</p>"),
|
|
235
253
|
];
|
|
236
|
-
parts.push(footer(
|
|
254
|
+
parts.push(footer(`If you did not expect this change, contact ${escapeHtml(supportEmail)}.`));
|
|
237
255
|
return {
|
|
238
256
|
subject: "Your role has been updated",
|
|
239
|
-
html: wrapHtml("Role Changed", parts.join("\n")),
|
|
240
|
-
text: `Your Role Has Been Updated\n\nYour role has been updated to ${data.newRole}.\n${copyright()}`,
|
|
257
|
+
html: wrapHtml("Role Changed", parts.join("\n"), b),
|
|
258
|
+
text: `Your Role Has Been Updated\n\nYour role has been updated to ${data.newRole}.\n${copyright(b)}`,
|
|
241
259
|
};
|
|
242
260
|
}
|
|
243
261
|
function teamInviteTemplate(data) {
|
|
262
|
+
const b = brand(data);
|
|
244
263
|
const tenantName = escapeHtml(data.tenantName || "a tenant");
|
|
245
264
|
const inviteUrl = data.inviteUrl || "";
|
|
246
265
|
const parts = [
|
|
247
266
|
heading(`You've Been Invited to Join ${tenantName}`),
|
|
248
|
-
paragraph(`<p>You've been invited to join <strong>${tenantName}</strong> on the
|
|
267
|
+
paragraph(`<p>You've been invited to join <strong>${tenantName}</strong> on the ${escapeHtml(b)} platform.</p>` +
|
|
249
268
|
"<p>Click below to accept the invitation.</p>"),
|
|
250
269
|
];
|
|
251
270
|
if (inviteUrl)
|
|
@@ -253,25 +272,27 @@ function teamInviteTemplate(data) {
|
|
|
253
272
|
parts.push(footer("If you did not expect this invitation, you can ignore this email."));
|
|
254
273
|
return {
|
|
255
274
|
subject: `You've been invited to join ${data.tenantName}`,
|
|
256
|
-
html: wrapHtml("Team Invite", parts.join("\n")),
|
|
257
|
-
text: `You've Been Invited to Join ${data.tenantName}\n\n${inviteUrl ? `Accept: ${inviteUrl}\n` : ""}${copyright()}`,
|
|
275
|
+
html: wrapHtml("Team Invite", parts.join("\n"), b),
|
|
276
|
+
text: `You've Been Invited to Join ${data.tenantName}\n\n${inviteUrl ? `Accept: ${inviteUrl}\n` : ""}${copyright(b)}`,
|
|
258
277
|
};
|
|
259
278
|
}
|
|
260
279
|
function agentCreatedTemplate(data) {
|
|
280
|
+
const b = brand(data);
|
|
261
281
|
const agentName = escapeHtml(data.agentName || "your agent");
|
|
262
282
|
const parts = [
|
|
263
|
-
heading(`Your
|
|
283
|
+
heading(`Your ${escapeHtml(b)} ${agentName} Is Ready`),
|
|
264
284
|
paragraph(`<p>Your new agent <strong>${agentName}</strong> has been created and is ready to use.</p>` +
|
|
265
285
|
"<p>Connect it to a channel to start receiving and sending messages.</p>"),
|
|
266
286
|
];
|
|
267
287
|
parts.push(footer("Happy building!"));
|
|
268
288
|
return {
|
|
269
|
-
subject: `Your
|
|
270
|
-
html: wrapHtml("Agent Created", parts.join("\n")),
|
|
271
|
-
text: `Your
|
|
289
|
+
subject: `Your ${b} ${data.agentName} is ready`,
|
|
290
|
+
html: wrapHtml("Agent Created", parts.join("\n"), b),
|
|
291
|
+
text: `Your ${b} ${data.agentName} Is Ready\n\nYour new agent has been created and is ready to use.\n${copyright(b)}`,
|
|
272
292
|
};
|
|
273
293
|
}
|
|
274
294
|
function channelConnectedTemplate(data) {
|
|
295
|
+
const b = brand(data);
|
|
275
296
|
const channelName = escapeHtml(data.channelName || "A channel");
|
|
276
297
|
const agentName = escapeHtml(data.agentName || "your agent");
|
|
277
298
|
const parts = [
|
|
@@ -282,11 +303,12 @@ function channelConnectedTemplate(data) {
|
|
|
282
303
|
parts.push(footer("Your agent is live!"));
|
|
283
304
|
return {
|
|
284
305
|
subject: `${data.channelName} connected to ${data.agentName}`,
|
|
285
|
-
html: wrapHtml("Channel Connected", parts.join("\n")),
|
|
286
|
-
text: `${data.channelName} Connected to ${data.agentName}\n\n${data.channelName} has been successfully connected to ${data.agentName}.\n${copyright()}`,
|
|
306
|
+
html: wrapHtml("Channel Connected", parts.join("\n"), b),
|
|
307
|
+
text: `${data.channelName} Connected to ${data.agentName}\n\n${data.channelName} has been successfully connected to ${data.agentName}.\n${copyright(b)}`,
|
|
287
308
|
};
|
|
288
309
|
}
|
|
289
310
|
function channelDisconnectedTemplate(data) {
|
|
311
|
+
const b = brand(data);
|
|
290
312
|
const channelName = escapeHtml(data.channelName || "A channel");
|
|
291
313
|
const agentName = escapeHtml(data.agentName || "your agent");
|
|
292
314
|
const reason = escapeHtml(data.reason || "");
|
|
@@ -299,77 +321,85 @@ function channelDisconnectedTemplate(data) {
|
|
|
299
321
|
parts.push(footer("Your agent data is preserved."));
|
|
300
322
|
return {
|
|
301
323
|
subject: `${data.channelName} disconnected from ${data.agentName}`,
|
|
302
|
-
html: wrapHtml("Channel Disconnected", parts.join("\n")),
|
|
303
|
-
text: `${data.channelName} Disconnected from ${data.agentName}\n\n${reason ? `Reason: ${data.reason}\n\n` : ""}Reconnect from your dashboard to restore service.\n${copyright()}`,
|
|
324
|
+
html: wrapHtml("Channel Disconnected", parts.join("\n"), b),
|
|
325
|
+
text: `${data.channelName} Disconnected from ${data.agentName}\n\n${reason ? `Reason: ${data.reason}\n\n` : ""}Reconnect from your dashboard to restore service.\n${copyright(b)}`,
|
|
304
326
|
};
|
|
305
327
|
}
|
|
306
328
|
function agentSuspendedTemplate(data) {
|
|
329
|
+
const b = brand(data);
|
|
307
330
|
const agentName = escapeHtml(data.agentName || "Your agent");
|
|
308
331
|
const reason = escapeHtml(data.reason || "");
|
|
332
|
+
const supportEmail = data.supportEmail || "support@wopr.bot";
|
|
309
333
|
const parts = [
|
|
310
334
|
heading(`${agentName} Has Been Paused`),
|
|
311
335
|
paragraph(`<p>Your agent <strong>${agentName}</strong> has been paused.</p>` +
|
|
312
336
|
(reason ? `<p><strong>Reason:</strong> ${reason}</p>` : "")),
|
|
313
337
|
];
|
|
314
|
-
parts.push(footer(
|
|
338
|
+
parts.push(footer(`Contact ${escapeHtml(supportEmail)} if you have questions.`));
|
|
315
339
|
return {
|
|
316
340
|
subject: `${data.agentName} has been paused`,
|
|
317
|
-
html: wrapHtml("Agent Paused", parts.join("\n")),
|
|
318
|
-
text: `${data.agentName} Has Been Paused\n\n${reason ? `Reason: ${data.reason}\n` : ""}${copyright()}`,
|
|
341
|
+
html: wrapHtml("Agent Paused", parts.join("\n"), b),
|
|
342
|
+
text: `${data.agentName} Has Been Paused\n\n${reason ? `Reason: ${data.reason}\n` : ""}${copyright(b)}`,
|
|
319
343
|
};
|
|
320
344
|
}
|
|
321
345
|
function accountDeletionRequestedTemplate(data) {
|
|
346
|
+
const b = brand(data);
|
|
322
347
|
const email = escapeHtml(data.email || "");
|
|
323
348
|
const deleteAfterDate = escapeHtml(data.deleteAfterDate || "");
|
|
324
349
|
const cancelUrl = data.cancelUrl || "";
|
|
350
|
+
const supportEmail = data.supportEmail || "support@wopr.bot";
|
|
325
351
|
const parts = [
|
|
326
352
|
heading("Account Deletion Requested"),
|
|
327
353
|
paragraph(`<p>Hi <strong>${email}</strong>,</p>` +
|
|
328
|
-
`<p>We've received your request to delete your
|
|
354
|
+
`<p>We've received your request to delete your ${escapeHtml(b)} account and all associated data.</p>` +
|
|
329
355
|
`<p>Your account will be permanently deleted on <strong>${deleteAfterDate}</strong>. Until then, you can cancel this request and keep your account.</p>` +
|
|
330
356
|
`<p>After that date, all your data will be permanently and irreversibly removed, including bots, conversation history, credit records, and plugin configurations.</p>`),
|
|
331
357
|
];
|
|
332
358
|
if (cancelUrl)
|
|
333
359
|
parts.push(button(cancelUrl, "Cancel Deletion", "#22c55e"));
|
|
334
|
-
parts.push(footer(
|
|
360
|
+
parts.push(footer(`If you did not request this, please contact ${escapeHtml(supportEmail)} immediately.`));
|
|
335
361
|
return {
|
|
336
|
-
subject:
|
|
337
|
-
html: wrapHtml("Account Deletion Requested", parts.join("\n")),
|
|
338
|
-
text: `Account Deletion Requested\n\nHi ${data.email},\n\nWe've received your request to delete your
|
|
362
|
+
subject: `Your ${b} account deletion request`,
|
|
363
|
+
html: wrapHtml("Account Deletion Requested", parts.join("\n"), b),
|
|
364
|
+
text: `Account Deletion Requested\n\nHi ${data.email},\n\nWe've received your request to delete your ${b} account and all associated data.\n\nYour account will be permanently deleted on ${data.deleteAfterDate}. Until then, you can cancel this request.\n\nAfter that date, all your data will be permanently and irreversibly removed.\n${cancelUrl ? `\nCancel deletion: ${cancelUrl}\n` : ""}If you did not request this, please contact ${supportEmail} immediately.${copyright(b)}`,
|
|
339
365
|
};
|
|
340
366
|
}
|
|
341
367
|
function accountDeletionCancelledTemplate(data) {
|
|
368
|
+
const b = brand(data);
|
|
342
369
|
const email = escapeHtml(data.email || "");
|
|
370
|
+
const supportEmail = data.supportEmail || "support@wopr.bot";
|
|
343
371
|
const parts = [
|
|
344
372
|
heading("Account Deletion Cancelled"),
|
|
345
373
|
paragraph(`<p>Hi <strong>${email}</strong>,</p>` +
|
|
346
374
|
`<p>Your account deletion request has been cancelled. Your account and all data remain intact.</p>` +
|
|
347
375
|
`<p>No further action is needed.</p>`),
|
|
348
|
-
footer(
|
|
376
|
+
footer(`If you didn't cancel this, please contact ${escapeHtml(supportEmail)}.`),
|
|
349
377
|
];
|
|
350
378
|
return {
|
|
351
|
-
subject:
|
|
352
|
-
html: wrapHtml("Account Deletion Cancelled", parts.join("\n")),
|
|
353
|
-
text: `Account Deletion Cancelled\n\nHi ${data.email},\n\nYour account deletion request has been cancelled. Your account and all data remain intact.\n\nNo further action is needed.${copyright()}`,
|
|
379
|
+
subject: `Your ${b} account deletion has been cancelled`,
|
|
380
|
+
html: wrapHtml("Account Deletion Cancelled", parts.join("\n"), b),
|
|
381
|
+
text: `Account Deletion Cancelled\n\nHi ${data.email},\n\nYour account deletion request has been cancelled. Your account and all data remain intact.\n\nNo further action is needed.${copyright(b)}`,
|
|
354
382
|
};
|
|
355
383
|
}
|
|
356
384
|
function accountDeletionCompletedTemplate(data) {
|
|
385
|
+
const b = brand(data);
|
|
357
386
|
const email = escapeHtml(data.email || "");
|
|
358
387
|
const parts = [
|
|
359
388
|
heading("Your Account Has Been Deleted"),
|
|
360
389
|
paragraph(`<p>Hi <strong>${email}</strong>,</p>` +
|
|
361
|
-
`<p>Your
|
|
390
|
+
`<p>Your ${escapeHtml(b)} account and all associated data have been permanently deleted as requested.</p>` +
|
|
362
391
|
`<p>This includes all bots, conversation history, credit records, billing data, and plugin configurations.</p>` +
|
|
363
|
-
`<p>If you'd like to use
|
|
364
|
-
footer(
|
|
392
|
+
`<p>If you'd like to use ${escapeHtml(b)} again in the future, you're welcome to create a new account.</p>`),
|
|
393
|
+
footer(`Thank you for using ${escapeHtml(b)}. We're sorry to see you go.`),
|
|
365
394
|
];
|
|
366
395
|
return {
|
|
367
|
-
subject:
|
|
368
|
-
html: wrapHtml("Account Deleted", parts.join("\n")),
|
|
369
|
-
text: `Your Account Has Been Deleted\n\nHi ${data.email},\n\nYour
|
|
396
|
+
subject: `Your ${b} account has been deleted`,
|
|
397
|
+
html: wrapHtml("Account Deleted", parts.join("\n"), b),
|
|
398
|
+
text: `Your Account Has Been Deleted\n\nHi ${data.email},\n\nYour ${b} account and all associated data have been permanently deleted as requested.\n\nThis includes all bots, conversation history, credit records, billing data, and plugin configurations.\n\nIf you'd like to use ${b} again in the future, you're welcome to create a new account.${copyright(b)}`,
|
|
370
399
|
};
|
|
371
400
|
}
|
|
372
401
|
function dividendWeeklyDigestTemplate(data) {
|
|
402
|
+
const b = brand(data);
|
|
373
403
|
const weeklyTotal = escapeHtml(data.weeklyTotalDollars || "$0.00");
|
|
374
404
|
const lifetimeTotal = escapeHtml(data.lifetimeTotalDollars || "$0.00");
|
|
375
405
|
const distributionCount = Number(data.distributionCount) || 0;
|
|
@@ -382,7 +412,7 @@ function dividendWeeklyDigestTemplate(data) {
|
|
|
382
412
|
const unsubscribeUrl = data.unsubscribeUrl || "";
|
|
383
413
|
const poolAvgDollars = `$${(poolAvgCents / 100).toFixed(2)}`;
|
|
384
414
|
const parts = [
|
|
385
|
-
heading(
|
|
415
|
+
heading(`${escapeHtml(b)} Paid You ${weeklyTotal} This Week`),
|
|
386
416
|
paragraph(`<p>Here's your weekly dividend summary for <strong>${weekStart} – ${weekEnd}</strong>.</p>` +
|
|
387
417
|
`<table style="width: 100%; border-collapse: collapse; margin: 16px 0;">` +
|
|
388
418
|
`<tr><td style="padding: 8px 0; color: #4a5568;">This week's dividends</td><td style="padding: 8px 0; text-align: right; font-weight: 600; color: #1a1a1a;">${weeklyTotal}</td></tr>` +
|
|
@@ -402,9 +432,9 @@ function dividendWeeklyDigestTemplate(data) {
|
|
|
402
432
|
parts.push(`<tr><td style="padding: 0 40px 20px 40px; text-align: center; color: #a0aec0; font-size: 12px;"><a href="${escapeHtml(unsubscribeUrl)}" style="color: #a0aec0; text-decoration: underline;">Unsubscribe from dividend digests</a></td></tr>`);
|
|
403
433
|
}
|
|
404
434
|
return {
|
|
405
|
-
subject:
|
|
406
|
-
html: wrapHtml("Weekly Dividend Digest", parts.join("\n")),
|
|
407
|
-
text:
|
|
435
|
+
subject: `${b} paid you ${data.weeklyTotalDollars} this week`,
|
|
436
|
+
html: wrapHtml("Weekly Dividend Digest", parts.join("\n"), b),
|
|
437
|
+
text: `${b} Paid You ${data.weeklyTotalDollars} This Week\n\n` +
|
|
408
438
|
`Weekly summary for ${data.weekStartDate} – ${data.weekEndDate}:\n\n` +
|
|
409
439
|
`This week's dividends: ${data.weeklyTotalDollars}\n` +
|
|
410
440
|
`Days with distributions: ${distributionCount} of 7\n` +
|
|
@@ -414,19 +444,20 @@ function dividendWeeklyDigestTemplate(data) {
|
|
|
414
444
|
`Next dividend: ${data.nextDividendDate}\n\n` +
|
|
415
445
|
`Community dividends are distributed daily from platform revenue.` +
|
|
416
446
|
(unsubscribeUrl ? `\n\nUnsubscribe: ${unsubscribeUrl}` : "") +
|
|
417
|
-
copyright(),
|
|
447
|
+
copyright(b),
|
|
418
448
|
};
|
|
419
449
|
}
|
|
420
450
|
function customTemplate(data) {
|
|
421
|
-
const
|
|
451
|
+
const b = brand(data);
|
|
452
|
+
const subject = data.subject || `Message from ${b}`;
|
|
422
453
|
const rawBody = data.bodyText || "";
|
|
423
454
|
const escapedBody = escapeHtml(rawBody).replace(/\n/g, "<br>\n");
|
|
424
|
-
const parts = [heading(
|
|
425
|
-
parts.push(footer(
|
|
455
|
+
const parts = [heading(`Message from ${escapeHtml(b)}`), paragraph(`<p>${escapedBody}</p>`)];
|
|
456
|
+
parts.push(footer(`This is an administrative message from ${escapeHtml(b)}.`));
|
|
426
457
|
return {
|
|
427
458
|
subject,
|
|
428
|
-
html: wrapHtml(escapeHtml(subject), parts.join("\n")),
|
|
429
|
-
text: `${rawBody}\n${copyright()}`,
|
|
459
|
+
html: wrapHtml(escapeHtml(subject), parts.join("\n"), b),
|
|
460
|
+
text: `${rawBody}\n${copyright(b)}`,
|
|
430
461
|
};
|
|
431
462
|
}
|
|
432
463
|
// ---------------------------------------------------------------------------
|
|
@@ -473,6 +504,7 @@ export function renderNotificationTemplate(template, data) {
|
|
|
473
504
|
case "agent-suspended":
|
|
474
505
|
return agentSuspendedTemplate(data);
|
|
475
506
|
case "affiliate-credit-match": {
|
|
507
|
+
const b = brand(data);
|
|
476
508
|
const amountDollars = escapeHtml(data.amountDollars || "$0.00");
|
|
477
509
|
const creditsUrl = data.creditsUrl || "";
|
|
478
510
|
const parts = [
|
|
@@ -481,14 +513,15 @@ export function renderNotificationTemplate(template, data) {
|
|
|
481
513
|
];
|
|
482
514
|
if (creditsUrl)
|
|
483
515
|
parts.push(button(creditsUrl, "View Credit Balance"));
|
|
484
|
-
parts.push(footer(
|
|
516
|
+
parts.push(footer(`Thank you for spreading the word about ${escapeHtml(b)}!`));
|
|
485
517
|
return {
|
|
486
518
|
subject: `You earned ${data.amountDollars} in affiliate credits!`,
|
|
487
|
-
html: wrapHtml("Affiliate Credits Earned", parts.join("\n")),
|
|
488
|
-
text: `You Earned Affiliate Credits!\n\nA user you referred just made their first purchase, and you've been credited ${data.amountDollars}.\n${creditsUrl ? `\nView your balance: ${creditsUrl}\n` : ""}${copyright()}`,
|
|
519
|
+
html: wrapHtml("Affiliate Credits Earned", parts.join("\n"), b),
|
|
520
|
+
text: `You Earned Affiliate Credits!\n\nA user you referred just made their first purchase, and you've been credited ${data.amountDollars}.\n${creditsUrl ? `\nView your balance: ${creditsUrl}\n` : ""}${copyright(b)}`,
|
|
489
521
|
};
|
|
490
522
|
}
|
|
491
523
|
case "spend-alert": {
|
|
524
|
+
const b = brand(data);
|
|
492
525
|
const currentSpend = escapeHtml(String(data.currentSpendDollars ?? "$0.00"));
|
|
493
526
|
const alertAt = escapeHtml(String(data.alertAtDollars ?? "$0.00"));
|
|
494
527
|
const creditsUrl = data.creditsUrl || "";
|
|
@@ -503,11 +536,11 @@ export function renderNotificationTemplate(template, data) {
|
|
|
503
536
|
parts.push(footer("This alert fires once per day when your spend exceeds your configured threshold."));
|
|
504
537
|
return {
|
|
505
538
|
subject: `Spending alert: you've reached your ${data.alertAtDollars} threshold`,
|
|
506
|
-
html: wrapHtml("Spending Alert", parts.join("\n")),
|
|
539
|
+
html: wrapHtml("Spending Alert", parts.join("\n"), b),
|
|
507
540
|
text: `Spending Alert: Threshold Reached\n\nYour monthly spend has reached ${data.currentSpendDollars}, ` +
|
|
508
541
|
`crossing your alert threshold of ${data.alertAtDollars}.\n` +
|
|
509
542
|
(creditsUrl ? `\nReview spending: ${creditsUrl}\n` : "") +
|
|
510
|
-
copyright(),
|
|
543
|
+
copyright(b),
|
|
511
544
|
};
|
|
512
545
|
}
|
|
513
546
|
case "custom":
|
|
@@ -521,6 +554,7 @@ export function renderNotificationTemplate(template, data) {
|
|
|
521
554
|
case "account-deletion-completed":
|
|
522
555
|
return accountDeletionCompletedTemplate(data);
|
|
523
556
|
case "fleet-update-available": {
|
|
557
|
+
const b = brand(data);
|
|
524
558
|
const version = escapeHtml(data.version || "");
|
|
525
559
|
const changelogDate = escapeHtml(data.changelogDate || "");
|
|
526
560
|
const changelogSummary = escapeHtml(data.changelogSummary || "");
|
|
@@ -538,11 +572,12 @@ export function renderNotificationTemplate(template, data) {
|
|
|
538
572
|
faParts.push(footer("Review the changelog and update when ready."));
|
|
539
573
|
return {
|
|
540
574
|
subject: `Fleet update available: ${data.version}`,
|
|
541
|
-
html: wrapHtml("Fleet Update Available", faParts.join("\n")),
|
|
542
|
-
text: `Fleet Update Available: ${data.version}\n\nA new version ${data.version} is available for your fleet.\n${changelogDate ? `Released: ${data.changelogDate}\n` : ""}${changelogSummary ? `Changelog: ${data.changelogSummary}\n` : ""}${fleetUrl ? `\nFleet dashboard: ${fleetUrl}\n` : ""}${copyright()}`,
|
|
575
|
+
html: wrapHtml("Fleet Update Available", faParts.join("\n"), b),
|
|
576
|
+
text: `Fleet Update Available: ${data.version}\n\nA new version ${data.version} is available for your fleet.\n${changelogDate ? `Released: ${data.changelogDate}\n` : ""}${changelogSummary ? `Changelog: ${data.changelogSummary}\n` : ""}${fleetUrl ? `\nFleet dashboard: ${fleetUrl}\n` : ""}${copyright(b)}`,
|
|
543
577
|
};
|
|
544
578
|
}
|
|
545
579
|
case "fleet-update-complete": {
|
|
580
|
+
const b = brand(data);
|
|
546
581
|
const fcVersion = escapeHtml(data.version || "");
|
|
547
582
|
const succeeded = Number(data.succeeded) || 0;
|
|
548
583
|
const failed = Number(data.failed) || 0;
|
|
@@ -568,22 +603,25 @@ export function renderNotificationTemplate(template, data) {
|
|
|
568
603
|
: "Review failed instances and retry if needed."));
|
|
569
604
|
return {
|
|
570
605
|
subject: `Fleet updated to ${data.version} \u2014 ${statusText}`,
|
|
571
|
-
html: wrapHtml("Fleet Update Complete", fcParts.join("\n")),
|
|
572
|
-
text: `Fleet Updated to ${data.version}\n\nSucceeded: ${succeeded}\nFailed: ${failed}\nTotal: ${total}\n${failed > 0 ? "\nSome instances failed to update.\n" : ""}${fleetUrl2 ? `\nFleet dashboard: ${fleetUrl2}\n` : ""}${copyright()}`,
|
|
606
|
+
html: wrapHtml("Fleet Update Complete", fcParts.join("\n"), b),
|
|
607
|
+
text: `Fleet Updated to ${data.version}\n\nSucceeded: ${succeeded}\nFailed: ${failed}\nTotal: ${total}\n${failed > 0 ? "\nSome instances failed to update.\n" : ""}${fleetUrl2 ? `\nFleet dashboard: ${fleetUrl2}\n` : ""}${copyright(b)}`,
|
|
573
608
|
};
|
|
574
609
|
}
|
|
575
|
-
case "low-balance":
|
|
610
|
+
case "low-balance": {
|
|
611
|
+
const b = brand(data);
|
|
576
612
|
return {
|
|
577
|
-
subject:
|
|
613
|
+
subject: `Your ${b} credits are running low`,
|
|
578
614
|
html: wrapHtml("Low Balance", [
|
|
579
|
-
heading(
|
|
615
|
+
heading(`Your ${escapeHtml(b)} Credits Are Running Low`),
|
|
580
616
|
paragraph(`<p>Your balance is <strong>${escapeHtml(data.balanceDollars || "$0.00")}</strong>. Top up to keep your agents running.</p>`),
|
|
581
617
|
...(data.creditsUrl ? [button(data.creditsUrl, "Buy Credits")] : []),
|
|
582
618
|
footer("This is an automated billing notification."),
|
|
583
|
-
].join("\n")),
|
|
584
|
-
text: `Your
|
|
619
|
+
].join("\n"), b),
|
|
620
|
+
text: `Your ${b} Credits Are Running Low\n\nBalance: ${data.balanceDollars}\n${data.creditsUrl ? `\nBuy credits: ${data.creditsUrl}\n` : ""}${copyright(b)}`,
|
|
585
621
|
};
|
|
586
|
-
|
|
622
|
+
}
|
|
623
|
+
case "credit-purchase-receipt": {
|
|
624
|
+
const b = brand(data);
|
|
587
625
|
return {
|
|
588
626
|
subject: "Credits added to your account",
|
|
589
627
|
html: wrapHtml("Credits Added", [
|
|
@@ -592,31 +630,36 @@ export function renderNotificationTemplate(template, data) {
|
|
|
592
630
|
(data.newBalanceDollars
|
|
593
631
|
? `<p>New balance: <strong>${escapeHtml(data.newBalanceDollars)}</strong></p>`
|
|
594
632
|
: "")),
|
|
595
|
-
footer(
|
|
596
|
-
].join("\n")),
|
|
597
|
-
text: `Credits Added\n\n${data.amountDollars} added.\n${copyright()}`,
|
|
633
|
+
footer(`Thank you for supporting ${escapeHtml(b)}!`),
|
|
634
|
+
].join("\n"), b),
|
|
635
|
+
text: `Credits Added\n\n${data.amountDollars} added.\n${copyright(b)}`,
|
|
598
636
|
};
|
|
599
|
-
|
|
637
|
+
}
|
|
638
|
+
case "welcome": {
|
|
639
|
+
const b = brand(data);
|
|
600
640
|
return {
|
|
601
|
-
subject:
|
|
641
|
+
subject: `Welcome to ${b}`,
|
|
602
642
|
html: wrapHtml("Welcome", [
|
|
603
|
-
heading(
|
|
643
|
+
heading(`Welcome to ${escapeHtml(b)}!`),
|
|
604
644
|
paragraph("<p>Your account is now active. Start building!</p>"),
|
|
605
645
|
footer("Happy building!"),
|
|
606
|
-
].join("\n")),
|
|
607
|
-
text: `Welcome to
|
|
646
|
+
].join("\n"), b),
|
|
647
|
+
text: `Welcome to ${b}!\n\nYour account is now active.\n${copyright(b)}`,
|
|
608
648
|
};
|
|
609
|
-
|
|
649
|
+
}
|
|
650
|
+
case "password-reset": {
|
|
651
|
+
const b = brand(data);
|
|
610
652
|
return {
|
|
611
|
-
subject:
|
|
653
|
+
subject: `Reset your ${b} password`,
|
|
612
654
|
html: wrapHtml("Reset Password", [
|
|
613
655
|
heading("Reset Your Password"),
|
|
614
656
|
paragraph("<p>Click below to reset your password.</p>"),
|
|
615
657
|
...(data.resetUrl ? [button(data.resetUrl, "Reset Password")] : []),
|
|
616
658
|
footer("If you did not request this, ignore this email."),
|
|
617
|
-
].join("\n")),
|
|
618
|
-
text: `Reset Your Password\n\n${data.resetUrl ? `${data.resetUrl}\n` : ""}${copyright()}`,
|
|
659
|
+
].join("\n"), b),
|
|
660
|
+
text: `Reset Your Password\n\n${data.resetUrl ? `${data.resetUrl}\n` : ""}${copyright(b)}`,
|
|
619
661
|
};
|
|
662
|
+
}
|
|
620
663
|
default: {
|
|
621
664
|
// Exhaustiveness check
|
|
622
665
|
const _exhaustive = template;
|