-
Notifications
You must be signed in to change notification settings - Fork 461
Add email tracking helper API (Fixes #646) #821
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,150 @@ | ||
| # Email Tracking with Gmail.js | ||
|
|
||
| This guide shows you how to track emails that have been sent using Gmail.js. | ||
|
|
||
| ## Overview | ||
|
|
||
| Gmail.js provides a simple API for tracking sent emails through the `gmail.track` namespace. This is perfect for: | ||
| - Analytics and monitoring | ||
| - Email delivery confirmation | ||
| - Logging sent emails | ||
| - Building email tracking features | ||
|
|
||
| ## Basic Usage | ||
|
|
||
| ### Track Sent Emails | ||
|
|
||
| ```javascript | ||
| const gmail = new Gmail(); | ||
|
|
||
| // Track all sent emails | ||
| gmail.track.sent_emails(function(emailData, url, body, xhr) { | ||
| console.log('Email sent!'); | ||
| console.log('To:', emailData.to); | ||
| console.log('Subject:', emailData.subject); | ||
| console.log('Body:', emailData.body); | ||
| }); | ||
| ``` | ||
|
|
||
| ### Track Scheduled Emails | ||
|
|
||
| ```javascript | ||
| // Track emails scheduled for later | ||
| gmail.track.scheduled_emails(function(emailData, url, body, xhr) { | ||
| console.log('Email scheduled!'); | ||
| console.log('To:', emailData.to); | ||
| console.log('Scheduled time:', emailData.scheduled_time); | ||
| }); | ||
| ``` | ||
|
|
||
| ### Track All Emails (Sent + Scheduled) | ||
|
|
||
| ```javascript | ||
| // Track both sent and scheduled emails | ||
| gmail.track.all_emails(function(emailData, type, url, body, xhr) { | ||
| if (type === 'sent') { | ||
| console.log('Email sent immediately'); | ||
| } else if (type === 'scheduled') { | ||
| console.log('Email scheduled for later'); | ||
| } | ||
|
|
||
| console.log('Email data:', emailData); | ||
| }); | ||
| ``` | ||
|
|
||
| ## Email Data Structure | ||
|
|
||
| The `emailData` object contains information about the sent email: | ||
|
|
||
| ```javascript | ||
| { | ||
| to: ['[email protected]'], // Array of recipients | ||
| cc: ['[email protected]'], // CC recipients | ||
| bcc: ['[email protected]'], // BCC recipients | ||
| subject: 'Email Subject', // Email subject | ||
| body: 'Email body content...', // Email body (HTML or plain text) | ||
| from: '[email protected]', // Sender email | ||
| thread_id: '1234567890abcdef', // Thread ID | ||
| message_id: 'msg-a:r123', // Message ID | ||
| scheduled_time: 1234567890, // Unix timestamp (for scheduled emails) | ||
| // ... additional fields | ||
| } | ||
| ``` | ||
|
|
||
| ## Advanced Examples | ||
|
|
||
| ### Save Sent Emails to Database | ||
|
|
||
| ```javascript | ||
| gmail.track.sent_emails(function(emailData) { | ||
| // Send to your backend API | ||
| fetch('/api/emails/track', { | ||
| method: 'POST', | ||
| headers: { 'Content-Type': 'application/json' }, | ||
| body: JSON.stringify({ | ||
| to: emailData.to, | ||
| subject: emailData.subject, | ||
| sentAt: new Date().toISOString(), | ||
| threadId: emailData.thread_id | ||
| }) | ||
| }); | ||
| }); | ||
| ``` | ||
|
|
||
| ### Email Analytics | ||
|
|
||
| ```javascript | ||
| let emailsSentToday = 0; | ||
| let emailsByRecipient = {}; | ||
|
|
||
| gmail.track.sent_emails(function(emailData) { | ||
| emailsSentToday++; | ||
|
|
||
| // Track by recipient | ||
| emailData.to.forEach(function(recipient) { | ||
| emailsByRecipient[recipient] = (emailsByRecipient[recipient] || 0) + 1; | ||
| }); | ||
|
|
||
| console.log('Emails sent today:', emailsSentToday); | ||
| console.log('By recipient:', emailsByRecipient); | ||
| }); | ||
| ``` | ||
|
|
||
| ### Notification System | ||
|
|
||
| ```javascript | ||
| gmail.track.sent_emails(function(emailData) { | ||
| // Show browser notification | ||
| if (Notification.permission === 'granted') { | ||
| new Notification('Email Sent!', { | ||
| body: `To: ${emailData.to.join(', ')}\nSubject: ${emailData.subject}`, | ||
| icon: '/path/to/icon.png' | ||
| }); | ||
| } | ||
| }); | ||
| ``` | ||
|
|
||
| ### Debug Logger | ||
|
|
||
| ```javascript | ||
| // Use the built-in logger for debugging | ||
| gmail.track.sent_emails(gmail.track.logger('MY APP')); | ||
|
|
||
| // Or with custom prefix | ||
| gmail.track.sent_emails(gmail.track.logger('Email Tracker')); | ||
| ``` | ||
|
|
||
| ## Use Cases | ||
|
|
||
| 1. **Email Delivery Confirmation** - Track when emails are successfully sent | ||
| 2. **Email Analytics Dashboard** - Build analytics for sent emails | ||
| 3. **Email Backup System** - Automatically backup all sent emails | ||
| 4. **Compliance and Auditing** - Log all sent emails for compliance | ||
| 5. **Email Templates Tracking** - Track template usage | ||
| 6. **A/B Testing** - Track different email variations | ||
|
|
||
| ## Related | ||
|
|
||
| - [Gmail.js Documentation](../README.md) | ||
| - [Observer API](../README.md#observers) | ||
| - [Issue #646](https://github.com/KartikTalwar/gmail.js/issues/646) - Original feature request | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -749,7 +749,7 @@ | |
| "false": !1, | ||
| "null": null | ||
| }; | ||
| each(params.replace(/\+/g, " ").split("&"), function (v, j) { | ||
|
Check warning on line 752 in src/gmail.js
|
||
| var param = v.split("="), | ||
| key = decodeURIComponent(param[0]), | ||
| val, | ||
|
|
@@ -800,7 +800,7 @@ | |
| } | ||
| }; | ||
|
|
||
| api.tools.parse_actions = function(params, xhr) { | ||
|
Check warning on line 803 in src/gmail.js
|
||
|
|
||
| // upload_attachment event - if found, don"t check other observers. See issue #22 | ||
| if(params.url.act === "fup" || params.url.act === "fuv" || params.body_is_object) { | ||
|
|
@@ -1918,7 +1918,7 @@ | |
| } | ||
|
|
||
| let parsedResponse = []; | ||
| let originalResponse = response; | ||
|
Check warning on line 1921 in src/gmail.js
|
||
| try { | ||
| // gmail post response structure | ||
| // )}]"\n<datalength><rawData>\n<dataLength><rawData>... | ||
|
|
@@ -2097,7 +2097,7 @@ | |
| const win = api.helper.get_xhr_window(); | ||
|
|
||
| api.tools.patch(win.XMLHttpRequest.prototype.open, (orig) => { | ||
| win.XMLHttpRequest.prototype.open = function (method, url, async, user, password) { | ||
|
Check warning on line 2100 in src/gmail.js
|
||
| var out = orig.apply(this, arguments); | ||
| this.xhrParams = { | ||
| method: method.toString(), | ||
|
|
@@ -2756,7 +2756,7 @@ | |
|
|
||
| // observes every element inserted into the DOM by Gmail and looks at the classes on those elements, | ||
| // checking for any configured observers related to those classes | ||
| api.tools.insertion_observer = function(target, dom_observers, dom_observer_map, sub) { | ||
|
Check warning on line 2759 in src/gmail.js
|
||
| //console.log("insertion", target, target.className); | ||
| if(!dom_observer_map) return; | ||
|
|
||
|
|
@@ -4616,6 +4616,135 @@ | |
| api.tools.embedded_data_watcher(); | ||
| } | ||
|
|
||
| /** | ||
| * Email tracking helper namespace | ||
| * Provides easy-to-use functions for tracking sent emails | ||
| */ | ||
| api.track = {}; | ||
|
|
||
| /** | ||
| * Track sent emails with a callback function | ||
| * This is a convenience wrapper around api.observe.on('send_message') | ||
| * | ||
| * @param {Function} callback - Function to call when an email is sent | ||
| * Receives (emailData, url, body, xhr) as parameters | ||
| * @returns {Function} - Returns the callback for potential cleanup | ||
| * | ||
| * @example | ||
| * gmail.track.sent_emails(function(emailData, url, body, xhr) { | ||
| * console.log('Email sent!', emailData); | ||
| * console.log('To:', emailData.to); | ||
| * console.log('Subject:', emailData.subject); | ||
| * console.log('Body:', emailData.body); | ||
| * }); | ||
| */ | ||
| api.track.sent_emails = function(callback) { | ||
| if (typeof callback !== 'function') { | ||
| api.tools.error('track.sent_emails requires a callback function'); | ||
| return null; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If people are using our API in a way which can never succeed, we should throw an error, not return This is anyway a kind of API where people are almost guaranteed to not check the return-value (as you yourself are not doing in your examples). |
||
| } | ||
|
|
||
| // Wrap the callback to provide cleaner parameters | ||
| const wrappedCallback = function(url, body, emailData, xhr) { | ||
| callback(emailData, url, body, xhr); | ||
| }; | ||
|
|
||
| // Register the observer | ||
| api.observe.on('send_message', wrappedCallback); | ||
|
|
||
| return wrappedCallback; | ||
| }; | ||
|
|
||
| /** | ||
| * Track scheduled emails with a callback function | ||
| * This is a convenience wrapper around api.observe.on('send_scheduled_message') | ||
| * | ||
| * @param {Function} callback - Function to call when an email is scheduled | ||
| * Receives (emailData, url, body, xhr) as parameters | ||
| * @returns {Function} - Returns the callback for potential cleanup | ||
| * | ||
| * @example | ||
| * gmail.track.scheduled_emails(function(emailData, url, body, xhr) { | ||
| * console.log('Email scheduled!', emailData); | ||
| * console.log('Scheduled time:', emailData.scheduled_time); | ||
| * }); | ||
| */ | ||
| api.track.scheduled_emails = function(callback) { | ||
| if (typeof callback !== 'function') { | ||
| api.tools.error('track.scheduled_emails requires a callback function'); | ||
| return null; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If people are using our API in a way which can never succeed, we should through, not return This is anyway a kind of API where people are almost guaranteed to not check the return-value (as you yourself are not doing in your examples). |
||
| } | ||
|
|
||
| // Wrap the callback to provide cleaner parameters | ||
| const wrappedCallback = function(url, body, emailData, xhr) { | ||
| callback(emailData, url, body, xhr); | ||
| }; | ||
|
|
||
| // Register the observer | ||
| api.observe.on('send_scheduled_message', wrappedCallback); | ||
|
|
||
| return wrappedCallback; | ||
| }; | ||
|
|
||
| /** | ||
| * Track both sent and scheduled emails with a single callback | ||
| * | ||
| * @param {Function} callback - Function to call when any email is sent or scheduled | ||
| * Receives (emailData, type, url, body, xhr) as parameters | ||
| * where type is either 'sent' or 'scheduled' | ||
| * @returns {Object} - Returns object with both callbacks for potential cleanup | ||
| * | ||
| * @example | ||
| * gmail.track.all_emails(function(emailData, type, url, body, xhr) { | ||
| * if (type === 'sent') { | ||
| * console.log('Email sent immediately'); | ||
| * } else if (type === 'scheduled') { | ||
| * console.log('Email scheduled for later'); | ||
| * } | ||
| * console.log('Email data:', emailData); | ||
| * }); | ||
| */ | ||
| api.track.all_emails = function(callback) { | ||
| if (typeof callback !== 'function') { | ||
| api.tools.error('track.all_emails requires a callback function'); | ||
| return null; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If people are using our API in a way which can never succeed, we should through, not return This is anyway a kind of API where people are almost guaranteed to not check the return-value (as you yourself are not doing in your examples). |
||
| } | ||
|
|
||
| const sentCallback = api.track.sent_emails(function(emailData, url, body, xhr) { | ||
| callback(emailData, 'sent', url, body, xhr); | ||
| }); | ||
|
|
||
| const scheduledCallback = api.track.scheduled_emails(function(emailData, url, body, xhr) { | ||
| callback(emailData, 'scheduled', url, body, xhr); | ||
| }); | ||
|
|
||
| return { | ||
| sent: sentCallback, | ||
| scheduled: scheduledCallback | ||
| }; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What value does it provide to return these items? If the intent is to allow people to unregister, the API should provide symmetric means of doing so. |
||
| }; | ||
|
|
||
| /** | ||
| * Get a simple email tracking logger (for debugging/testing) | ||
| * Returns a function that logs email details to console | ||
| * | ||
| * @param {String} prefix - Optional prefix for console logs | ||
| * @returns {Function} - Logger function | ||
| * | ||
| * @example | ||
| * gmail.track.sent_emails(gmail.track.logger('MY APP')); | ||
| */ | ||
| api.track.logger = function(prefix) { | ||
| prefix = prefix || 'Gmail.js'; | ||
| return function(emailData, url, body, xhr) { | ||
|
Check warning on line 4739 in src/gmail.js
|
||
| console.log(prefix + ' - Email sent!'); | ||
| console.log('To:', emailData.to); | ||
| console.log('Subject:', emailData.subject); | ||
| console.log('Body preview:', emailData.body ? emailData.body.substring(0, 100) : 'N/A'); | ||
| console.log('Full data:', emailData); | ||
| }; | ||
| }; | ||
|
|
||
| return api; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All in all I'm positive to these changes. It makes some functionality more up-front and accessible. That's good! One thing though, when adding new APIs, make sure to add the type-definitions for those new APIs to We have many typescript users these days 😄 |
||
| }; | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why add this documentation to a new markdown-file, rather than the existing README with all the other API documentation?