Skip to content

Instantly share code, notes, and snippets.

@dperussina
Created June 3, 2024 18:30
Show Gist options
  • Save dperussina/6e2f9d541e92f7335b0ad867ce41f146 to your computer and use it in GitHub Desktop.
Save dperussina/6e2f9d541e92f7335b0ad867ce41f146 to your computer and use it in GitHub Desktop.
// Importing required modules
const fs = require('fs');
const readline = require('readline');
const { google } = require('googleapis');
var tools = require('tagnet-tools');
// const { send } = require('process');
const fetch = require('node-fetch');
const path = require('path');
const { JSDOM } = require("jsdom");
// Define the scopes and token path
const SCOPES = ['https://www.googleapis.com/auth/gmail.modify'];
const TOKEN_PATH = '/DATA/apps/Catamaran_EmailAgent/src/token.json';
// const TOKEN_PATH = 'token.json';
const BASE_URL = 'https://services.agentgrid.net/Catamaran';
// Function to preprocess data
async function preprocess() {
console.log('preprocess() Start ');
// Return a promise to handle asynchronous operations
return new Promise((resolve, reject) => {
var _sproc = 'EXEC SP_FindMissingProNumbers';
// Execute SQL query using tagnet_queue function
tools.sql.tagnet_queue(_sproc, function (err, result) {
console.log('preprocess() End ');
// Handle errors
if (err) {
console.log('preprocess ERROR: ', _sproc, ' ', err, ' ', result);
resolve(err);
} else {
resolve(result);
}
});
});
}
async function main() {
// Load client secrets from a local file.
const credentialsPath = '/DATA/apps/Catamaran_EmailAgent/src/credentials.json';
// const credentialsPath = 'credentials.json';
fs.readFile(credentialsPath, (err, content) => {
if (err) return console.log('Error loading client secret file:', err);
// Authorize and then call the Gmail API.
authorize(JSON.parse(content), checkInbox);
});
}
// Authorize with OAuth2 client
function authorize(credentials, callback) {
const { client_secret, client_id, redirect_uris } = credentials.web;
const oAuth2Client = new google.auth.OAuth2(
client_id, client_secret, redirect_uris[0]);
// Check if we have previously stored a token.
fs.readFile(TOKEN_PATH, (err, token) => {
if (err) return getNewToken(oAuth2Client, callback);
oAuth2Client.setCredentials(JSON.parse(token));
callback(oAuth2Client);
});
}
// Get and store new token after prompting for user authorization
function getNewToken(oAuth2Client, callback) {
const authUrl = oAuth2Client.generateAuthUrl({
access_type: 'offline',
scope: SCOPES,
});
console.log('Authorize this app by visiting this url:', authUrl);
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
rl.question('Enter the code from that page here: ', (code) => {
rl.close();
oAuth2Client.getToken(code, (err, token) => {
if (err) return console.error('Error retrieving access token', err);
oAuth2Client.setCredentials(token);
// Store the token to disk for later program executions
fs.writeFile(TOKEN_PATH, JSON.stringify(token), (err) => {
if (err) return console.error(err);
console.log('Token stored to', TOKEN_PATH);
});
callback(oAuth2Client);
});
});
}
// Download and convert attachment to Base64
async function downloadAndConvertToBase64(auth, messageId, part) {
const gmail = google.gmail({ version: 'v1', auth });
try {
const response = await gmail.users.messages.attachments.get({
userId: 'me',
messageId: messageId,
id: part.body.attachmentId
});
// The data from the Gmail API is already in base64 but uses URL-safe characters.
// Convert it to standard base64 by replacing URL-safe characters.
const base64Data = response.data.data.replace(/-/g, '+').replace(/_/g, '/');
return base64Data;
} catch (error) {
console.error('Error downloading or converting attachment:', error);
}
}
// Check Inbox for unread messages
function checkInbox(auth) {
const gmail = google.gmail({ version: 'v1', auth });
gmail.users.messages.list({
userId: 'me',
q: 'is:unread',
}, (err, res) => {
if (err) return console.log('The API returned an error: ' + err);
const messages = res.data.messages;
if (messages && messages.length) {
console.log('Messages: ', messages.length);
messages.forEach((message) => {
gmail.users.messages.get({
userId: 'me',
id: message.id,
}, (err, res) => {
if (err) return console.log('The API returned an error: ' + err);
var _messageid = res.data.id;
// Extracting email headers
const getHeaderValue = (headerName) => {
const header = res.data.payload.headers.find(h => h.name === headerName);
return header ? header.value : null;
};
var _existingConversation = {};
// Extracting email details
var _to = getHeaderValue('To') || getHeaderValue('to') || getHeaderValue('To');
var _cc = getHeaderValue('Cc') || getHeaderValue('cc') || getHeaderValue('CC'); // Extracting CC recipients
var _sender = getHeaderValue('Sender');
var _for = getHeaderValue('for');
if (_cc == null) {
_cc = _to;
} else {
_cc = _cc + ', ' + _to;
}
if (_sender != null) {
_to = _sender;
}
var _deliveredTo = getHeaderValue('Delivered-To');
var _from = getHeaderValue('From');
var _replyTo = getHeaderValue('Reply-To');
console.log('To: ', _to, ' Sender:', _sender, '_deliveredTo : ', _deliveredTo, ' _replyTo: ', _replyTo, ' _for: ', _for, ' _from: ', _from, ' _cc: ', _cc, ' MessageID: ', _messageid);
// console.log(_from, ' ', _replyTo);
if (_to == null && _deliveredTo != null) {
_to = _deliveredTo;
}
if (_replyTo != null) {
if ((_from && !_from.includes('fragilepak.com') && !_from.includes('agentgrid.net')) || (_replyTo.includes('fragilepak.com') || _replyTo.includes('agentgrid.net'))) {
_from = _replyTo;
}
_cc = _cc + ', ' + _replyTo;
}
// console.log('CC: ', _cc);
var _subject = getHeaderValue('Subject');
var _threadid = res.data.threadId;
var _body = '';
var _direction = 'Inbound';
var _numattachements = 0;
var isHtml = false;
// console.log('$$$ Before Body Search ')
if (res.data.payload.parts && res.data.payload.parts.length > 0) {
// console.log('$$ Starting Body Search')
const htmlPart = res.data.payload.parts.find(part => part.mimeType === 'text/html');
if (htmlPart && htmlPart.body.data) {
_body = Buffer.from(htmlPart.body.data, 'base64').toString('utf-8');
isHtml = true;
console.log('****: FOUND HTML');
} else {
console.log('### Trying Alt Method ', res.data.payload.parts)
const textPart = res.data.payload.parts.find(part => part.mimeType === 'text/plain');
if (textPart && textPart.body.data) {
_body = Buffer.from(textPart.body.data, 'base64').toString('utf-8');
// isHtml remains false
console.log('****: FOUND TEXT');
} else {
// _body = Buffer.from(res.data.payload.parts[0].body.data, 'base64').toString('utf-8');
// _body = Buffer.from(res.data.payload.parts[0].body.data, 'base64').toString('utf-8');
try {
_body = Buffer.from(res.data.payload.parts[0].body.data, 'base64').toString('utf-8');
console.log('Trying Old Method ', _subject)
} catch (err) {
console.log('1 ****: Error Old Method: ', err, ' ');
}
if (_body == '') {
console.log('Trying Nested Method ', _subject)
try {
_body = Buffer.from(res.data.payload.parts[0].parts[0].body.data, 'base64').toString('utf-8');
} catch (err) {
console.log('2 ****: Error: ', err, ' ');
}
}
function findBodyData(parts) {
for (const part of parts) {
if (part.body && part.body.data) {
return part.body.data;
}
if (part.parts) {
const foundData = findBodyData(part.parts);
if (foundData) {
return foundData;
}
}
}
return null;
}
if (_body == '') {
console.log('Trying THIS IS MY LAST RESORT Method ', _subject, ' MessageID: ', _messageid);
const bodyData = findBodyData(res.data.payload.parts);
if (bodyData) {
_body = Buffer.from(bodyData, 'base64').toString('utf-8');
} else {
console.log('Could not find body data');
}
}
}
}
// Once you have _body and isHtml, call formatEmailBody to format the content.
if (_to == 'help@agentgrid.net') {
_body = extractInformation(_body)
} else {
_body = formatEmailBody(_body, isHtml);
}
// _body = Buffer.from(res.data.payload.parts[0].body.data, 'base64').toString('utf-8');
// }
} else {
console.log('$$$ Alt Before Body Search ')
try {
_body = Buffer.from(res.data.payload.body.data, 'base64').toString('utf-8');
} catch (err) {
console.log('****: Error: ', err, ' ');
}
if (_to == 'help@agentgrid.net') {
_body = extractInformation(_body)
} else {
_body = formatEmailBody(_body, isHtml);
}
}
// Parsing _to variable to find catamaran.messages with a + sign
const regex = /catamaran\.messages\+(.*?)@/;
// console.log('^^^ _to: ', _to)
_to = extractEmails(_to);
if (_to) {
try {
const match = _to.match(regex);
if (match) {
if (_existingConversation === null) {
// Handle the case where _existingConversation is null
// For example, initializing it or logging an error
_existingConversation = { conversation: null }; // or any appropriate default value
}
console.log('^^^ match: ', match)
_existingConversation.conversation = match[1];
}
} catch (err) {
console.log('****: Error: ', err, ' ');
}
}
try {
if (_replyTo) {
_replyTo = extractEmails(_replyTo);
console.log('About to test _replyTo ', _replyTo)
if (_existingConversation === null) {
// Handle the case where _existingConversation is null
// For example, initializing it or logging an error
_existingConversation = { conversation: null }; // or any appropriate default value
}
const match1 = _replyTo.match(regex);
if (match1) {
console.log('^^^ match: ', match1)
_existingConversation.conversation = match1[1];
}
}
} catch (err) {
console.error('****: _replyTo Error: ', err, ' ');
}
try {
if (_cc && _cc != null) {
_cc = extractEmails(_cc);
console.log('About to test _cc ', _cc)
if (_existingConversation === null) {
// Handle the case where _existingConversation is null
// For example, initializing it or logging an error
_existingConversation = { conversation: null }; // or any appropriate default value
}
const match2 = _cc.match(regex);
if (match2) {
console.log('^^^ match: ', match2)
_existingConversation.conversation = match2[1];
}
}
} catch (err) {
console.log('****: _cc Error: ', err, ' ');
}
// return;
// Checking for attachments
if (res.data.payload.parts) {
const attachments = res.data.payload.parts.filter(part => part.filename && part.filename.length > 0);
_numattachements = attachments.length;
// console.log(`Number of attachments: ${_numattachements}`);
attachments.forEach(async (attachmentPart) => {
const base64Data = await downloadAndConvertToBase64(auth, res.data.id, attachmentPart);
// console.log(`Attachment (filename: ${attachmentPart.filename}) in base64 length:`, base64Data.length);
const fileExtension = path.extname(attachmentPart.filename);
var _sproc = "EXEC catamaran.SP_InsertEmailAttachmentQueue @Base64 ='" + base64Data + "', "
_sproc += "@MessageID ='" + _messageid + "', "
if (_existingConversation && _existingConversation.conversation != null && _existingConversation.conversation.length > 0) {
_sproc += "@CatamaranConversationID ='" + _existingConversation.conversation + "', "
} else {
_sproc += "@CatamaranConversationID ='', "
}
_sproc += "@FileName ='" + attachmentPart.filename + "', "
_sproc += "@FileType ='" + fileExtension + "' "
tools.sql.tagnet_queue(_sproc, function (err, result) {
if (err) {
console.log('catamaran.SP_InsertEmailAttachmentQueue ERROR: ', _sproc, ' ', err, ' ', result);
}
const filePath = path.join(__dirname, 'queue', attachmentPart.filename);
// console.log(`Attachment (filename: ${attachmentPart.filename}) in base64 length:`, base64Data.length);
try {
fs.writeFileSync(filePath, base64Data, 'base64');
} catch (err) {
console.error('Error writing file:', err);
}
});
});
}
// extractGroups(_messageid, extractEmails(_to), extractEmails(_cc));
if (_body == '') {
console.log('!!! Missing body: , ', _subject)
} else {
if (_existingConversation.conversation && _existingConversation.conversation != null && _existingConversation.conversation.length > 0) {
} else {
_existingConversation = extractURLParams(_body);
}
}
// Preparing SQL procedure call
var _sproc = "EXEC catamaran.InsertIntoEmailQueue @To ='" + extractEmails(_to) + "', "
_sproc += "@From ='" + extractEmail(_from || "") + "', "
_sproc += "@CC ='" + (extractEmails(_cc) == null ? '' : extractEmails(_cc)) + "', "
_sproc += "@Subject ='" + (_subject || "").replace(/'/g, '"') + "', "
_sproc += "@Body ='" + (_body || "").replace(/'/g, '"') + "', "
_sproc += "@MessageID ='" + (_messageid || "") + "', "
_sproc += "@ThreadID ='" + (_threadid || "") + "', "
_sproc += "@ProNumber ='', "
_sproc += "@Direction ='" + (_direction || "") + "', "
_sproc += "@NumAttachements ='" + (_numattachements || "") + "'";
if (_existingConversation && _existingConversation.conversation != null && _existingConversation.conversation.length > 0) {
_sproc += ", @ExistingCatarmaranConversationID ='" + _existingConversation.conversation + "' "
}
// console.log(_sproc);
// Calling SQL procedure
if (_body == '') {
// console.log('!!! Missing body: , ', _subject, ' ', _body)
// markMessageUnread(_messageid, auth);
// return;
_body = 'No Message Body';
}
tools.sql.tagnet_queue(_sproc, function (err, result) {
if (err) {
console.log('catamaran.InsertIntoEmailQueue ERROR: ', _sproc, ' ', err, ' ', result);
} else {
markAndArchiveGmailMessage(_messageid, auth);
}
});
});
});
} else {
console.log('No new messages.');
// resolve();
}
});
// postPendingMessages();
}
// Function to post pending messages
async function postPendingMessages() {
console.log('postPendingMessages() Start ');
var _records = [];
const _sproc = "EXEC [catamaran].[SP_GetEmailsPendingTransfer]";
// Retrieve pending emails from the database
tools.sql.tagnet_queue(_sproc, function (err, result) {
// console.log('postPendingMessages() SQL: ', _sproc, ' ', result, err);
if (err) {
console.log('catamaran.SP_GetEmailsPendingTransfer ERROR: ', _sproc, ' ', err, ' ', result);
}
// console.log('postPendingMessages() result: ', result, ' Length: ', result.length);
_records = result;
// Process each pending email
tools.flow.queue_task(_records, function (data) {
return function (next) {
// console.log('postPendingMessages() Working: ', _records);
var _conversationID = "";
// Check if the email has an existing conversation ID
if (data.CatarmaranConversationID == null) {
// New Conversation
if (data.CC != '') {
data.To = data.To + ', ' + data.CC;
}
createConversation({
conversationType: 1, // Type of conversation (1 for new conversation)
// 1 = Normal (single pro), 2 = linked (more than one pro)
subject: data.Subject,
proNumbers: data.ProNumber,
emails: data.To,
message: data.Body,
isUrgent: false,
status: data.ProNumber.includes(',') ? 2 : 1, // Set status based on presence of commas // 0 = N/A, 1 = New, 2 = Open, 3 = Pending, 4 = Closed
userEmail: data.From,
isMaster: false,
masterPro: null,
id: data.id,
source: 'emailBot'
}, function (err, result) {
if (err) {
console.log('createConversation ERROR: ', err, ' ', result);
} else {
console.log('createConversation success, marking sent: ', result, ' ', data.id);
// [catamaran].[SP_SetEmailQueueSent]
tools.sql.tagnet_queue("EXEC [catamaran].[SP_SetEmailQueueSent] @id ='" + data.id + "'", function (err, result) {
if (err) {
console.log('catamaran.SP_SetEmailQueueSent ERROR: ', err, ' ', result, ' ', data.id);
}
});
}
_conversationID = result;
// Check if the email has attachments
if (data.NumAttachements > 0) {
// Retrieve attachments for the email from the database
tools.sql.tagnet_queue("EXEC [catamaran].[SP_GetEmailAttachmentsByMessageIDPendingTransfer] @MessageID ='" + data.MessageID + "'", function (err, result) {
if (err) {
console.log('catamaran.SP_GetEmailAttachmentsByMessageIDPendingTransfer ERROR: ', err, ' ', result);
}
tools.flow.queue_task(result, function (attachment) {
return function (nextAttachment) {
// Insert each attachment into the conversation
// Read the file from the file system
fs.readFile('/DATA/apps/Catamaran_EmailAgent/src/queue/' + attachment.FileName, 'base64', function (err, data) {
// fs.readFile('./queue/' + attachment.FileName, 'base64', function (err, data) {
if (err) {
console.error('Error reading file:', err);
nextAttachment();
return;
}
// Store the base64 data in the attachment.Base64FileData variable
attachment.Base64FileData = data;
// Insert the attachment into the conversation
insertAttachmentByConversationId({
conversationId: parseInt(_conversationID),
userEmail: data.From,
// documentId: attachment.FileName, //TODO: Needs to be GUID
documentName: attachment.FileName,
// documentType: attachment.DocumentType,
text: 'Email Attachement',
fileType: attachment.FileType,
fileBase64String: attachment.Base64FileData,
partnerPrimaryOrderID: data.ProNumber,
source: 'emailBot'
}, function (err, result) {
if (err) {
console.error('insertAttachmentByConversationId ERROR: ', err, ' ', result);
nextAttachment();
return;
}
tools.sql.tagnet_queue("EXEC [catamaran].[SP_SetEmailAttachmentQueueSent] @id ='" + attachment.id + "'", function (err, result) {
if (err) {
console.log('catamaran.SP_SetEmailAttachmentQueueSent ERROR: ', err, ' ', result, ' ', data.id);
nextAttachment();
return;
}
nextAttachment();
// Delete the file from the file system
fs.unlink('/DATA/apps/Catamaran_EmailAgent/src/queue/' + attachment.FileName, function (err) {
// fs.unlink('./queue/' + attachment.FileName, function (err) {
if (err) {
console.error('Error deleting file:', err);
} else {
console.log('File deleted successfully', attachment.FileName);
}
});
})
});
});
}
}, function (err, result) {
console.log('postPendingMessages() result: ', result);
});
});
}
next();
});
} else {
// Existing Conversation
_conversationID = data.CatarmaranConversationID;
// Insert the email message into the existing conversation
insertConversationMessage({ conversationId: data.CatarmaranConversationID, message: data.Body, messageBy: data.From, source: 'emailBot' }, function (err, result) {
if (err) {
console.log('createConversation ERROR: ', err, ' ', result);
} else {
console.log('createConversation success, marking sent: ', result, ' ', data.id);
// [catamaran].[SP_SetEmailQueueSent]
tools.sql.tagnet_queue("EXEC [catamaran].[SP_SetEmailQueueSent] @id ='" + data.id + "'", function (err, result) {
if (err) {
console.log('catamaran.SP_SetEmailQueueSent ERROR: ', err, ' ', result, ' ', data.id);
}
});
if (data.CC != '') {
data.To = data.To + ', ' + data.CC;
}
var _payload = {
conversationId: data.CatarmaranConversationID // conversation id
, email: data.To //to and cc
, userEmail: data.From // from
, source: 'emailBot' // source
};
InsertEmailByConversationId(_payload, function (err, reply) {
if (err) {
console.log('InsertEmailByConversationId ERROR: ', err, ' ', reply, ' ', _payload);
} else {
console.log('InsertEmailByConversationId success: ', _payload.conversationId)
}
});
}
});
// Check if the email has attachments
if (data.NumAttachements > 0) {
// Retrieve attachments for the email from the database
tools.sql.tagnet_queue("EXEC [catamaran].[SP_GetEmailAttachmentsByMessageIDPendingTransfer] @MessageID ='" + data.MessageID + "'", function (err, result) {
if (err) {
console.log('catamaran.SP_GetEmailAttachmentsByMessageIDPendingTransfer ERROR: ', err, ' ', result);
}
tools.flow.queue_task(result, function (attachment) {
return function (nextAttachment) {
// Insert each attachment into the conversation
// Read the file from the file system
fs.readFile('/DATA/apps/Catamaran_EmailAgent/src/queue/' + attachment.FileName, 'base64', function (err, data) {
// fs.readFile('./queue/' + attachment.FileName, 'base64', function (err, data) {
if (err) {
console.error('Error reading file:', err);
nextAttachment();
return;
}
// Store the base64 data in the attachment.Base64FileData variable
attachment.Base64FileData = data;
// console.log('insertAttachmentByConversationId: file: ', attachment.FileName, ' ', attachment.Base64FileData.length, ' data: ', attachment.Base64FileData);
// Insert the attachment into the conversation
insertAttachmentByConversationId({
conversationId: parseInt(_conversationID),
userEmail: data.From,
// documentId: attachment.FileName, //TODO: Needs to be GUID
documentName: attachment.FileName,
// documentType: attachment.DocumentType,
text: 'Email Attachement',
fileType: attachment.FileType,
fileBase64String: attachment.Base64FileData,
partnerPrimaryOrderID: data.ProNumber,
source: 'emailBot'
}, function (err, result) {
console.log('insertAttachmentByConversationId: ', _conversationID, ' Done. Error: ', err);
if (err) {
console.error('insertAttachmentByConversationId ERROR: ', err, ' ', result);
nextAttachment();
return;
}
tools.sql.tagnet_queue("EXEC [catamaran].[SP_SetEmailAttachmentQueueSent] @id ='" + attachment.id + "'", function (err, result) {
if (err) {
console.log('catamaran.SP_SetEmailAttachmentQueueSent ERROR: ', err, ' ', result, ' ', data.id);
nextAttachment();
return;
}
nextAttachment();
// Delete the file from the file system
fs.unlink('/DATA/apps/Catamaran_EmailAgent/src/queue/' + attachment.FileName, function (err) {
// fs.unlink('./queue/' + attachment.FileName, function (err) {
if (err) {
console.error('Error deleting file:', err);
} else {
console.log('File deleted successfully', attachment.FileName);
}
});
})
});
});
}
}, function (err, result) {
console.log('postPendingMessages() result: ', result);
});
});
}
next();
}
};
}, function (err, result) {
console.log('postPendingMessages(): ', result);
});
});
}
// postPendingMessages();
// Extract email from input string
function extractEmail(input) {
// This regular expression matches a pattern enclosed in angle brackets (< >).
const match = input.match(/<([^>]+)>/);
return match ? match[1] : input;
}
// Extract multiple emails from input string
function extractEmails(input) {
// Regular expression to match email addresses in the format of "user@example.com" or "<user@example.com>"
const regex = /([a-zA-Z0-9._+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6})|<([a-zA-Z0-9._+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6})>/g;
let match;
const emails = [];
while ((match = regex.exec(input)) !== null) {
// The email can be in either the first or second capturing group, depending on whether it was enclosed in angle brackets
const email = match[1] || match[2];
if (!emails.includes(email)) {
emails.push(email);
}
}
return emails.length ? emails.join(', ') : null;
}
// Create a conversation
function createConversation(conversationData, callback) {
// Make a POST request to the API endpoint
console.log('createConversation: ', conversationData);
fetch(BASE_URL + '/api/TicketConversation/CreateConversation', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(conversationData),
})
.then((response) => response.text()) // Get the raw body of the response
.then((data) => {
// Handle the response data
console.log('createConversation: ', data);
tools.sql.tagnet_queue("EXEC catamaran.SP_SetConverationID_ByID @id ='" + conversationData.id + "', @ConversationID = '" + data + "';", function (err, result) {
console.log('writing conversation id to email queue: ', err, ' ', result);
});
callback(false, data);
})
.catch((error) => {
// Handle any errors
console.error('createConversation ERROR: ', error);
callback(true, error);
});
}
// Insert a conversation message
function insertConversationMessage(conversationMessage, callback) {
console.log('insertConversationMessage: ', conversationMessage);
// Make a POST request to the API endpoint
fetch(BASE_URL + '/api/TicketConversation/InsertConversationMessage', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(conversationMessage),
})
.then((response) => response)
.then((data) => {
// Handle the response data
console.log('insertConversationMessage: ', data);
callback(false, data);
})
.catch((error) => {
// Handle any errors
console.error('insertConversationMessage ERROR: ', error);
callback(true, error);
});
}
// Insert emails to a conversation
function InsertEmailByConversationId(conversationMessage, callback) {
console.log('InsertEmailByConversationId: ', conversationMessage);
// Make a POST request to the API endpoint
fetch(BASE_URL + '/api/TicketConversation/InsertEmailByConversationId', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(conversationMessage),
})
.then((response) => response)
.then((data) => {
// Handle the response data
console.log('InsertEmailByConversationId: ', data);
callback(false, data);
})
.catch((error) => {
// Handle any errors
console.error('InsertEmailByConversationId ERROR: ', error);
callback(true, error);
});
}
// Insert an attachment by conversation ID
function insertAttachmentByConversationId(attachmentData, callback) {
// Make a POST request to the API endpoint
// console.log('insertAttachmentByConversationId: ', attachmentData.fileBase64String, ' ', attachmentData.fileBase64String.length);
attachmentData.fileBase64String.length;
fetch(BASE_URL + '/api/TicketConversation/UploadFileAttachment', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(attachmentData),
})
.then((response) => response.json())
.then((data) => {
// Handle the response data
console.log('insertAttachmentByConversationId: ', data);
callback(false, data);
})
.catch((error) => {
// Handle any errors
console.error('insertAttachmentByConversationId ERROR: ', error);
callback(true, error);
});
}
// Function to extract URL parameters from a base64 encoded URL
function extractURLParams(url) {
try {
// Decode the URL
const decodedUrl = Buffer.from(url, 'base64').toString('utf-8');
// Regular expression to extract the parameters
const regex = /pro=(\w+)&conversation=(\d+)/;
// Match the regex against the decoded URL
const match = decodedUrl.match(regex);
console.log('### extractURLParams(url)');
// If there is a match, extract the parameters and return them
if (match) {
const pro = match[1];
const conversation = match[2];
console.log('### pro, conversation: ', pro, conversation);
return { pro, conversation };
}
// If there is no match, return null
return null;
} catch (err) {
console.error('extractURLParams Avoided error: ', err);
return null;
}
}
// Function to execute the script
async function executeScript() {
console.log('##### executeScript() Start #####');
try {
await preprocess(); // Preprocess the data
await main(); // Load client secrets and authorize
await preprocess(); // Preprocess the data again
await postPendingMessages(); // Post pending messages
} catch (error) {
console.error('Error executing script:', error);
}
setTimeout(executeScript, 30000); // Schedule the next execution after 30 seconds
}
// Create a queue to govern the speed of API calls
const apiQueue = [];
let isProcessingQueue = false;
// Function to enqueue a task in the API queue
function enqueueTask(task) {
apiQueue.push(task);
if (!isProcessingQueue) {
processQueue();
}
}
// Function to process the API queue
async function processQueue() {
isProcessingQueue = true;
while (apiQueue.length > 0) {
const task = apiQueue.shift();
try {
await task();
} catch (error) {
console.error('Error processing task:', error);
}
}
isProcessingQueue = false;
}
// Function to mark and archive a Gmail message
function markAndArchiveGmailMessage(messageId, auth) {
// Add the task to the queue
console.log('markAndArchiveGmailMessage(messageId): ', messageId);
enqueueTask(async () => {
console.log('in apiQueue markAndArchiveGmailMessage(messageId): ', messageId);
try {
// await markGmailMessageAsRead(messageId, auth); // Mark the message as read
// await archiveGmailMessage(messageId, auth); // Archive the message
await deleteGmailMessageById(messageId, auth); // Delete the message
} catch (error) {
console.error('Error marking and archiving Gmail message:', error);
}
});
}
// Function to mark and archive a Gmail message
function markMessageUnread(messageId, auth) {
// Add the task to the queue
console.log('markMessageUnread(messageId): ', messageId);
enqueueTask(async () => {
console.log('in apiQueue markMessageUnread(messageId): ', messageId);
try {
await markGmailMessageAsUnRead(messageId, auth); // Mark the message as read
} catch (error) {
console.error('Error marking unread Gmail message:', error);
}
});
}
// Function to mark a Gmail message as unread
function markGmailMessageAsUnRead(messageId, auth) {
console.log('markGmailMessageAsUnRead(messageId): ', messageId);
const gmail = google.gmail({ version: 'v1', auth });
return new Promise((resolve, reject) => {
gmail.users.messages.modify(
{
userId: 'me',
id: messageId,
resource: {
addLabelIds: ['UNREAD'], // add the 'UNREAD' label to mark the message as read
},
},
(err, res) => {
if (err) {
console.error('Error marking Gmail message as unread:', err);
reject(err);
} else {
console.log('Gmail message marked as unread successfully');
resolve();
}
}
);
});
}
// Function to mark a Gmail message as Trash
function deleteGmailMessageById(messageId, auth) {
// Your code to trash the Gmail message goes here
// You can use the Gmail API to modify the message's label
// Here's an example using the googleapis library:
console.log('archiveGmailMessage(messageId): ', messageId);
const gmail = google.gmail({ version: 'v1', auth });
return new Promise((resolve, reject) => {
gmail.users.messages.trash({
userId: 'me',
id: messageId,
}, (err, res) => {
if (err) {
console.error('Error deleting Gmail message:', err);
// Handle the error
reject(err);
} else {
console.log('Gmail message deleted successfully');
// Handle the success
resolve();
}
});
});
}
function formatEmailBody(body, isHtml = false) {
// Define the labels of the information to be extracted
// const labels = [
// "Type of Inquiry",
// "Name",
// "Email",
// "Phone Number",
// "PRO Number",
// "Message"
// ];
// const containsText = body.includes('Adjusts the display of header images based on the user"s preference for dark color schemes.');
// Object to store the extracted information
let extractedInfo = {};
if (isHtml) {
// Create a DOM from the HTML body
const dom = new JSDOM(body);
const document = dom.window.document;
// Replace all occurrences of two spaces with a single space
const htmlString = document.documentElement.outerHTML.replace(/style="[^"]*"/g, '').replace(/ {2}/g, ' ').replace(/<style\b[^<]*(?:(?!<\/style>)<[^<]*)*<\/style>/gi, '');
// Return the transformed HTML as a string
return htmlString;
} else {
// For plain text, perform simple transformations for readability
// Example: Convert email quotes to a more readable format
let formattedText = body.replace(/^>+/gm, " ");
// Replace multiple line breaks with a single one to clean up spacing
formattedText = formattedText.replace(/\n\s*\n/g, "<br>");
formattedText = formattedText.replace(/\r\n/g, "<br>");
// Additional formatting rules can be applied here
// Remove excess <br> tags in a row
formattedText = formattedText.replace(/<br\s*\/?>\s*(<br\s*\/?>\s*){2,}/g, "<br>");
// return formattedText;
// Additional formatting rules can be applied here
return formattedText;
}
}
function convertExtractedInfoToString(info) {
// Convert each key-value pair into a string in the format "Key: Value"
const readableString = Object.entries(info)
.map(([key, value]) => `${key}: ${value}`)
.join('<br>'); // Join all strings with a newline character
return readableString;
}
function extractInformation(body) {
console.log(' %%%%%%%%%%%%%%% extractInformation(body): ', body);
const dom = new JSDOM(body);
const document = dom.window.document;
const labels = [
"Type of Inquiry",
"Name",
"Email",
"Phone Number",
"PRO Number",
"Message"
];
let extractedInfo = {};
labels.forEach(label => {
// Locate the <strong> element that contains the label
const strongTag = Array.from(document.querySelectorAll('strong')).find(el => el.textContent.includes(label));
// Assuming the corresponding value is in the next <td class="field-value">
// Note: Depending on the actual HTML structure, you might need a different method to correctly locate the value.
let value = '';
if (strongTag) {
// Directly navigating to the .field-value that is supposed to follow
const fieldValueElement = strongTag.closest('tr').nextElementSibling.querySelector('.field-value');
if (fieldValueElement) {
value = fieldValueElement.textContent.trim();
} else {
// If no direct .field-value found, fall back to the next element's text
const nextElement = strongTag.parentElement.nextElementSibling;
if (nextElement) {
value = nextElement.textContent.trim();
}
}
}
extractedInfo[label] = value;
console.log(' %%%%%%%%%%%%%%% extractInformation(extractedInfo): ', extractedInfo);
});
return convertExtractedInfoToString(extractedInfo);
}
executeScript();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment