diff --git a/changelog.md b/changelog.md index 123998e..5c792f4 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,13 @@ # Changelog +## 2025-05-07 - 2.1.0 - feat(smartmail) +Add new email validation helper methods (getMxRecords, isDisposableEmail, isRoleAccount) and an applyVariables method to Smartmail for dynamic templating. + +- Introduced getMxRecords in EmailAddressValidator to extract MX records from DNS responses. +- Added isDisposableEmail to determine if an email is from a disposable domain. +- Added isRoleAccount to identify role-based email addresses. +- Implemented applyVariables in Smartmail to update subject, body, and htmlBody templates with provided data. + ## 2025-05-07 - 2.0.1 - fix(readme) Update documentation to include usage of creation object reference and update API details. diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index 1739321..4fad680 100644 --- a/ts/00_commitinfo_data.ts +++ b/ts/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@push.rocks/smartmail', - version: '2.0.1', + version: '2.1.0', description: 'A unified format for representing and dealing with emails, with support for attachments and email validation.' } diff --git a/ts/smartmail.classes.emailaddressvalidator.ts b/ts/smartmail.classes.emailaddressvalidator.ts index 5facab3..b53f2d6 100644 --- a/ts/smartmail.classes.emailaddressvalidator.ts +++ b/ts/smartmail.classes.emailaddressvalidator.ts @@ -109,6 +109,65 @@ export class EmailAddressValidator { return result; } + + /** + * Gets the MX records for a domain + * @param domain The domain to get MX records for + * @returns Array of MX records as strings + */ + public async getMxRecords(domain: string): Promise { + const mxRecords = await this.checkMxRecords(domain); + + if (!mxRecords || !Array.isArray(mxRecords)) { + return []; + } + + // Extract exchange values from MX records + return mxRecords.map((record: any) => { + if (record && record.exchange) { + return record.exchange; + } + return ''; + }).filter(Boolean); + } + + /** + * Checks if an email is from a disposable domain + * @param email The email address to check + * @returns True if the email is from a disposable domain + */ + public async isDisposableEmail(email: string): Promise { + await this.fetchDomains(); + + if (!this.isValidEmailFormat(email)) { + return false; + } + + const domainPart = email.split('@')[1]; + return this.domainMap[domainPart] === 'disposable'; + } + + /** + * Checks if an email is a role account (e.g. info@, support@, etc.) + * @param email The email address to check + * @returns True if the email is a role account + */ + public isRoleAccount(email: string): boolean { + if (!this.isValidEmailFormat(email)) { + return false; + } + + const localPart = email.split('@')[0].toLowerCase(); + const roleAccounts = [ + 'admin', 'administrator', 'webmaster', 'hostmaster', 'postmaster', + 'info', 'support', 'sales', 'marketing', 'contact', 'help', + 'abuse', 'noc', 'security', 'billing', 'donations', 'donate', + 'staff', 'office', 'hr', 'jobs', 'careers', 'team', + 'enquiry', 'enquiries', 'feedback', 'no-reply', 'noreply' + ]; + + return roleAccounts.includes(localPart); + } /** * Validates an email address diff --git a/ts/smartmail.classes.smartmail.ts b/ts/smartmail.classes.smartmail.ts index f5f8715..b2dd109 100644 --- a/ts/smartmail.classes.smartmail.ts +++ b/ts/smartmail.classes.smartmail.ts @@ -73,6 +73,32 @@ export class Smartmail { const smartmustache = new plugins.smartmustache.SmartMustache(this.options.subject); return smartmustache.applyData(dataArg); } + + /** + * Applies variables to all template strings in the email + * @param variables Variables to apply to templates + */ + public applyVariables(variables: Record): void { + if (!variables || typeof variables !== 'object') { + return; + } + + // Process the subject, body, and HTML body with the provided variables + if (this.options.subject) { + const subjectMustache = new plugins.smartmustache.SmartMustache(this.options.subject); + this.options.subject = subjectMustache.applyData(variables); + } + + if (this.options.body) { + const bodyMustache = new plugins.smartmustache.SmartMustache(this.options.body); + this.options.body = bodyMustache.applyData(variables); + } + + if (this.options.htmlBody) { + const htmlBodyMustache = new plugins.smartmustache.SmartMustache(this.options.htmlBody); + this.options.htmlBody = htmlBodyMustache.applyData(variables); + } + } /** * Gets the processed plain text body with template variables applied