Solving Invalid Code Error in ZohoCRM-SDK - crm

I'm using Zoho CRM Nodejs SDK zcrmsdk to get all users.
const {
OAuthToken,
TokenType
} = require('zcrmsdk/models/authenticator/oauth_token');
let token = new OAuthToken(
'1000.XXXX',
'4eXXXX',
'GRANT',
TokenType.GRANT,
'https://www.google.com/'
);
// User's TOKEN Store
const FileStore = require('zcrmsdk/models/authenticator/store/file_store')
.FileStore;
let tokenstore = new FileStore(
'/mnt/c/Users/Shadab/Repositories/ZOHO_CRM/nodejs_sdk_tokens.txt'
);
const SDKConfigBuilder = require('zcrmsdk/routes/sdk_config_builder')
.MasterModel;
let sdkConfig = new SDKConfigBuilder()
.setPickListValidation(false)
.setAutoRefreshFields(true)
.build();
let resourcePath = '/mnt/c/Users/Shadab/Repositories/ZOHO_CRM/';
// PROXY
const RequestProxy = require('zcrmsdk/routes/request_proxy').RequestProxy;
let requestProxy = new RequestProxy(
'proxyHost',
80,
'proxyUser',
'password'
);
console.log('user', crmclient.API);
let zoho = await crmclient.Initializer.initialize(
user,
environment,
token,
tokenstore,
sdkConfig,
resourcePath,
logger
);
console.log(await new UsersOperations().getUsers());
but i'm getting invalid_code errror. It is happening when sdk internally calls generateAccessToken function.
url: https://accounts.zoho.com/oauth/v2/token,
formdata: {
method: 'POST',
headers: {},
body: FormData {
_overheadLength: 543,
_valueLength: 188,
_valuesToMeasure: [],
writable: false,
readable: true,
dataSize: 0,
maxDataSize: 2097152,
pauseStreams: true,
_released: false,
_streams: [
'----------------------------520556686822378093129396\r\n' +
'Content-Disposition: form-data; name="grant_type"\r\n' +
'\r\n',
'authorization_code',
[Function: bound ],
'----------------------------520556686822378093129396\r\n' +
'Content-Disposition: form-data; name="client_id"\r\n' +
'\r\n',
'1000XXX',
[Function: bound ],
'----------------------------520556686822378093129396\r\n' +
'Content-Disposition: form-data; name="client_secret"\r\n' +
'\r\n',
'4e1XXX',
[Function: bound ],
'----------------------------520556686822378093129396\r\n' +
'Content-Disposition: form-data; name="redirect_uri"\r\n' +
'\r\n',
'https://www.google.com/',
[Function: bound ],
'----------------------------520556686822378093129396\r\n' +
'Content-Disposition: form-data; name="code"\r\n' +
'\r\n',
'GRANT',
[Function: bound ]
],
_currentStream: null,
_insideLoop: false,
_pendingNext: false,
_boundary: '--------------------------520556686822378093129396'
},
encoding: 'utf8',
allowGetBody: true,
throwHttpErrors: false
}
The Client id and secret key is from the Server Based Application. I'm running this from my local machine.

Related

Why am I getting a different response from programmatically querying this endpoint?

I am trying to use the createChatInviteLink API method to create a chat invite.
This is the response I get when I try to access it through my browser:
{"ok":true,
"result":{"invite_link":"https://t.me/+4MooeJCENSORED",
"creator":{"id":CENSORED,"is_bot":true,"first_name":"CENSORED","username":"CENSORED"},
"expire_date":1674321925,
"creates_join_request":true,
"is_primary":false,
"is_revoked":false}}
However, none of that useful response shows up when I try the same exact query through my web app. Instead, I get this:
RESPONSE Response {
size: 0,
timeout: 0,
[Symbol(Body internals)]: {
body: PassThrough {
_readableState: [ReadableState],
_events: [Object: null prototype],
_eventsCount: 2,
_maxListeners: undefined,
_writableState: [WritableState],
allowHalfOpen: true,
[Symbol(kCapture)]: false,
[Symbol(kCallback)]: null
},
disturbed: false,
error: null
},
[Symbol(Response internals)]: {
url: 'https://api.telegram.org/botCENSORED/createChatInviteLink?chat_id=CENSORED&expire_date=1674422960&creates_join_request=true',
status: 200,
statusText: 'OK',
headers: Headers { [Symbol(map)]: [Object: null prototype] },
counter: 0
}
}
Is there a specific part of the response I need to access? Even response.body isn't returning anything useful.
This is the code that tries to fetch the response:
export async function generateGroupInviteURL(
botToken: string,
groupId: string
) {
const fiveMinutesLaterTimestamp = Math.round(new Date().getTime() / 1000) + 300;
fetch(
`https://api.telegram.org/bot${botToken}/createChatInviteLink?chat_id=${groupId}&expire_date=${fiveMinutesLaterTimestamp}&creates_join_request=true`
).then((res) => {
console.log("RESPONSE", res);
});
}
Any ideas on what I'm doing wrong or need to be doing to access the useful part?

Dart how to add complex parameter

I need to get url with query parameters, but I dont know how.
I need this -> "entityTypeId=172&filter[id]=1&filter[id]=3&filter[id]=5".
In JS i can do like that
var httpBuildQuery = require('http-build-query');
var params = {
entityTypeId: 172,
filter: {
id: [1, 3, 5]
}};
const url = url + "?" + httpBuildQuery(params);
console.log(httpBuildQuery(params));
In PHP
$params = array(
'filter' => array ('ID' => array('1', '3', '5'),),
'entityTypeId' => 172,
);
http_build_query($params);
In dart I tried this
var uri = Uri(
scheme: 'http',
host: 'b24-ybr1v4.bitrix24.ru',
path: '/rest/1/token/crm.item.list.json',
queryParameters: {
'entityTypeId': '172',
'filter': [
{'id': '1'}
],
},
);
But in this case I get error:
The following TypeErrorImpl was thrown while handling a gesture:
Expected a value of type 'String', but got one of type 'IdentityMap<String, String>'
How to get parameter like "filter[id]"?
You could try moving the [id] part into the query parameter key.
var uri = Uri(
scheme: 'http',
host: 'b24-ybr1v4.bitrix24.ru',
path: '/rest/1/token/crm.item.list.json',
queryParameters: {
'entityTypeId': '172',
'filter[id]': ['1', '3', '5'],
},
);

GS Firebase function can't set offset?

I am using firebase functions to crop certain area of pdf and convert them to image using ghostscript
[The wrapper https://www.npmjs.com/package/node-gs and compiled version of gs v9.2 "https://github.com/sina-masnadi/node-gs/tarball/master" ]
and this is the code i am using :
const functions = require('firebase-functions');
const { Storage } = require('#google-cloud/storage');
const gcs = new Storage();
const spawn = require('child-process-promise').spawn;
const path = require('path');
const os = require('os');
const fs = require('fs');
var gs = require('gs');
const THUMB_MAX_HEIGHT = 200;
const THUMB_MAX_WIDTH = 200;
const THUMB_SUFFIX = '-thumb';
//This function triggers whenever any pdf is uploaded to the firebase storage
//and attempts to generate
exports.makePreviews = functions.storage.object().onFinalize(async (object, event) => {
//Checking for pdf files
if (!object.name.endsWith('.pdf')) return false;
const filePath = object.name;
//slicing name and path
const splitFileName = object.name.split(".");
console.log(splitFileName);
const fileID = splitFileName;
//creating temporary path strings for gcp file system
const fileName = path.basename(filePath);
const tempFilePath = path.join(os.tmpdir(), fileName);
const newName1 = path.basename(filePath, '.pdf') + '01.jpeg';
const tempNewPath1 = path.join(os.tmpdir(), newName1);
const newName2 = path.basename(filePath, '.pdf') + '02.jpeg';
const tempNewPath2 = path.join(os.tmpdir(), newName2);
const thumbName = path.basename(filePath, '.pdf') + THUMB_SUFFIX + '.jpeg';
const tempThumbPath = path.join(os.tmpdir(), thumbName);
//downloading file from firebase storage
const bucket = gcs.bucket(object.bucket);
return bucket.file(filePath).download({
destination: tempFilePath
}).then(async () => {
console.log('PDF downloaded locally to', tempFilePath);
//generating two preview JPEGS
await new Promise((resolve, reject) => {
gs()
.safer()
.batch()
.nopause()
.option('-dTextAlphaBits=4')
.option('-dGraphicsAlphaBits=4')
.option('-dDEVICEWIDTHPOINTS=238')
.option('-dDEVICEHEIGHTPOINTS=149.5')
.option('-dFIXEDMEDIA')
.res(600)
.option('-dDownScaleFactor=2')
.executablePath('gs')
.device('jpeg')
.output(tempNewPath2)
.option('-c "<</PageOffset[-308.5 40]>> setpagedevice"')
.option('-sPDFPassword=01011977')
.input(tempFilePath)
.exec((err, stdout, stderr) => {
if (!err) {
console.log('Part One Exceuted');
bucket.upload(tempNewPath1, {
destination: 'files/' + fileID + '.jpeg'
}).then(() => {
console.log('stdout', stdout);
console.log('stderr', stderr);
}).catch(err => {
console.log(err);
});
resolve();
} else {
console.log('gs error:', err);
reject(err);
}
});
});
await new Promise((resolve, reject) => {
gs()
.safer()
.batch()
.nopause()
.option('-dTextAlphaBits=4')
.option('-dGraphicsAlphaBits=4')
.option('-dDEVICEWIDTHPOINTS=238')
.option('-dDEVICEHEIGHTPOINTS=149.5')
.option('-dFIXEDMEDIA')
.res(600)
.option('-dDownScaleFactor=2')
.executablePath('gs')
.device('jpeg')
.output(tempNewPath2)
.option('-c "<</PageOffset[-308.5 40]>> setpagedevice"')
.option('-sPDFPassword=01011977')
.input(tempFilePath)
.exec((err, stdout, stderr) => {
if (!err) {
console.log('gs Part two excuted');
bucket.upload(tempNewPath1, {
destination: 'files/' + fileID + '-2.jpeg'
}).then(() => {
console.log('stdout', stdout);
console.log('stderr', stderr);
})
.catch(err => {
console.log(err);
});
resolve();
} else {
console.log('gs error:', err);
reject(err);
}
});
});
//generating thumbnail from the first JPEG
return spawn('convert', [tempNewPath1, '-thumbnail', `${THUMB_MAX_WIDTH}x${THUMB_MAX_HEIGHT}>`, tempThumbPath], {
capture: ['stdout', 'stderr']
});
}).then(async () => {
console.log('PNG created at', tempNewPath1 + 'and' + tempNewPath2);
console.log('Thumbnail created at', tempThumbPath);
//uploading the files back to firebase storage
return bucket.upload(tempThumbPath, {
destination: 'files/' + fileID + 'thumb.jpeg'
});
}).then(() => {
//once the files have been uploaded delete the local temporary
//files to free up disk space.
fs.unlinkSync(tempNewPath1);
fs.unlinkSync(tempNewPath2);
fs.unlinkSync(tempThumbPath);
return fs.unlinkSync(tempFilePath);
}).catch((err) => {
console.log('exception:', err);
return err;
});
});
deploying the above code, The Log:
[ 'PAN_01011977', 'pdf' ]
PDF downloaded locally to /tmp/PAN_01011977.pdf
gs command: -dSAFER,-dBATCH,-dNOPAUSE,-dTextAlphaBits=4,-dGraphicsAlphaBits=4,-dDEVICEWIDTHPOINTS=238,-dDEVICEHEIGHTPOINTS=149.5,-dFIXEDMEDIA,-r600,-dDownScaleFactor=2,-sDEVICE=jpeg,-sOutputFile=/tmp/PAN_0101197702.jpeg,-c "<</PageOffset[-308.5 40]>> setpagedevice",-sPDFPassword=01011977,/tmp/PAN_01011977.pdf
Part One Exceuted
gs command: -dSAFER,-dBATCH,-dNOPAUSE,-dTextAlphaBits=4,-dGraphicsAlphaBits=4,-dDEVICEWIDTHPOINTS=238,-dDEVICEHEIGHTPOINTS=149.5,-dFIXEDMEDIA,-r600,-dDownScaleFactor=2,-sDEVICE=jpeg,-sOutputFile=/tmp/PAN_0101197702.jpeg,-c "<</PageOffset[-308.5 40]>> setpagedevice",-sPDFPassword=01011977,/tmp/PAN_01011977.pdf
{ Error: ENOENT: no such file or directory, stat '/tmp/PAN_0101197701.jpeg'
errno: -2,
code: 'ENOENT',
syscall: 'stat',
path: '/tmp/PAN_0101197701.jpeg' }
gs Part two excuted
{ Error: ENOENT: no such file or directory, stat '/tmp/PAN_0101197701.jpeg'
errno: -2,
code: 'ENOENT',
syscall: 'stat',
path: '/tmp/PAN_0101197701.jpeg' }
and the error :
and the error log
exception: { ChildProcessError: `convert /tmp/PAN_0101197701.jpeg -thumbnail 200x200> /tmp/PAN_01011977-thumb.jpeg` failed with code 1
at ChildProcess.<anonymous> (/srv/node_modules/child-process-promise/lib/index.js:132:23)
at emitTwo (events.js:126:13)
at ChildProcess.emit (events.js:214:7)
at maybeClose (internal/child_process.js:915:16)
at Process.ChildProcess._handle.onexit (internal/child_process.js:209:5)
name: 'ChildProcessError',
code: 1,
childProcess:
ChildProcess {
domain:
Domain {
domain: null,
_events: [Object],
_eventsCount: 1,
_maxListeners: undefined,
members: [Array] },
_events: { error: [Function], close: [Function] },
_eventsCount: 2,
_maxListeners: undefined,
_closesNeeded: 3,
_closesGot: 3,
connected: false,
signalCode: null,
exitCode: 1,
killed: false,
spawnfile: 'convert',
_handle: null,
spawnargs:
[ 'convert',
'/tmp/PAN_0101197701.jpeg',
'-thumbnail',
'200x200>',
'/tmp/PAN_01011977-thumb.jpeg' ],
pid: 14,
stdin:
Socket {
connecting: false,
_hadError: false,
_handle: null,
_parent: null,
_host: null,
_readableState: [Object],
readable: false,
domain: [Object],
_events: [Object],
_eventsCount: 2,
_maxListeners: undefined,
_writableState: [Object],
writable: false,
allowHalfOpen: false,
_bytesDispatched: 0,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: null,
_server: null,
_idleNext: null,
_idlePrev: null,
_idleTimeout: -1,
[Symbol(asyncId)]: 4540,
[Symbol(bytesRead)]: 0 },
stdout:
Socket {
connecting: false,
_hadError: false,
_handle: null,
_parent: null,
_host: null,
_readableState: [Object],
readable: false,
domain: [Object],
_events: [Object],
_eventsCount: 3,
_maxListeners: undefined,
_writableState: [Object],
writable: false,
allowHalfOpen: false,
_bytesDispatched: 0,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: null,
_server: null,
_idleNext: null,
_idlePrev: null,
_idleTimeout: -1,
write: [Function: writeAfterFIN],
[Symbol(asyncId)]: 4541,
[Symbol(bytesRead)]: 0 },
stderr:
Socket {
connecting: false,
_hadError: false,
_handle: null,
_parent: null,
_host: null,
_readableState: [Object],
readable: false,
domain: [Object],
_events: [Object],
_eventsCount: 3,
_maxListeners: undefined,
_writableState: [Object],
writable: false,
allowHalfOpen: false,
_bytesDispatched: 0,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: null,
_server: null,
_idleNext: null,
_idlePrev: null,
_idleTimeout: -1,
write: [Function: writeAfterFIN],
[Symbol(asyncId)]: 4542,
[Symbol(bytesRead)]: 232 },
stdio: [ [Object], [Object], [Object] ] },
stdout: '',
stderr: 'convert-im6.q16: unable to open image `/tmp/PAN_0101197701.jpeg\': No such file or directory # error/blob.c/OpenBlob/2701.\nconvert-im6.q16: no images defined `/tmp/PAN_01011977-thumb.jpeg\' # error/convert.c/ConvertImageCommand/3258.\n' }
Error serializing return value: TypeError: Converting circular structure to JSON
Function execution took 9561 ms, finished with status: 'ok'
The problem is in using the below option in gs without this the function works but it didn't crops the pdf just converts to full page image.
//.option('-c "<</PageOffset [ -64.2 40 ]>> setpagedevice"')
//.option('-c "<</PageOffset [ -308.5 40 ]>> setpagedevice"')
How can i use the above option ?
Edit
Tried to terminate -c with -f but no luck
$ node index.js
gs command: -dSAFER,-dBATCH,-dNOPAUSE,-dTextAlphaBits=4,-dGraphicsAlphaBits=4,-dDEVICEWIDTHPOINTS=238,-dDEVICEHEIGHTPOINTS=149.5,-dFIXEDMEDIA,-r150,-dDownScaleFactor=2,-sPDFPassword=01011977,-sDEVICE=jpeg,-sOutputFile=/home/jcol/Desktop/gs_offline/functions/output.jpeg,-c <</PageOffset[-64.2 40]>>setpagedevice,-f,/home/jcol/Desktop/gs_offline/functions/pan.pdf
Suceess
GPL Ghostscript 9.20 (2016-09-26)
Copyright (C) 2016 Artifex Software, Inc. All rights reserved.
This software comes with NO WARRANTY: see the file PUBLIC for details.
Processing pages 1 through 1.
Page 1
Loading NimbusSans-Regular font from %rom%Resource/Font/NimbusSans-Regular... 4244908 2808684 2600016 1250276 3 done.
In the absence of an example file (and ideally the actual command line being sent to Ghostscript) as well as the lack of the back channel output (stout and stderr) the only observation I can make is that the 'option' you refer to (actually a piece of PostScript programming) introduces PostScript input with the -c switch but does not terminate it with -f. That means anything which folows this on the comand line will be treated as more PostScript, which is likely to either lead to an error or a 'hang', awaiting more input.
For Future Readers
And For Newbies like ME
There was some confusion in using libraries in NODEJS as i was new to it.
Firstly i was using some gs-wrapper from some tutorials which was not on NPM and that was the cause of the error as that didn't for some reason supported commands.
After some Research i came to Node-gs from npm and i checked Node-GS API at their page in NPM and it had dedicated option for command option.
Now why i posted this Answer :
The NODE-GS library is the only to support serverless architecture like Firebsae Functions. But error handling in this is worst as this only spawns Ghostscirpt directly from the Executable.
For Example if you provide wrong password for PDF then the library just going to push Ghostscript Unrecoverable Error in error Message.
(I know if you are using Ghostscirpt in serverless functions you're smart enough to check if password is correct or not on client side but just for example)
At the time of writing this i found Ghostscript4JS for NODE which uses C++ API of Ghostscript but unfortunately this library doesn't supports serverless architecture as the library depends on system installed Ghostscirpt but the developer said it has some some plans on it.
Check if the library now supported serverless Architecture.
And in the end the struggle as a newbie i had to go through was find portable version of Ghostscirpt to use with NODE-GS and you can find it here Ghostscript Releases
For Firebase Function Users
Firebase functions is built upon Ubuntu Bionic 18.04 arch x64 so you have to use x86_64 version of Ghostscript and The latest at the time of writing is Ghostscript 9.52
B-Bye :)

Unable to add attendees to google calendar using dialogflow fulfilment using node.js in the Firebase Online Editor

First post from a non-development expert. I have been scouring the libraries and online resources on how to use Dialogflow Fulfilment to integrate with the Google Calendar API. I am able to set the start and end times, the summary, description, location successfully in the Google Calendar, but I am unable to add attendees successfully. I have tried many variations on the attendees format. Here is the code I am using I removed the private key for security reasons. One note when I used the square brackets as some sites suggested I always received a response that the time was already booked.
'use strict';
const functions = require('firebase-functions');
const {google} = require('googleapis');
const {WebhookClient} = require('dialogflow-fulfillment');
// Enter your calendar ID below and service account JSON below
const calendarId = 'piec3rnlo2v2p2cemgjdjfctmg#group.calendar.google.com';
const serviceAccount = {
"type": "service_account",
"project_id": "whatduewhen2020v1-kgwjyd",
"private_key_id": "2a2dead3e050ef295cfef9c2c27bd2ac7d2b7471",
"private_key": "-----BEGIN PRIVATE KEY-----,
"client_email": "google-calendar#whatduewhen2020v1-kgwjyd.iam.gserviceaccount.com",
"client_id": "114290887729963225819",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/google-calendar%40whatduewhen2020v1-kgwjyd.iam.gserviceaccount.com"
}; // Starts with {"type": "service_account",...
// Set up Google Calendar Service account credentials
const serviceAccountAuth = new google.auth.JWT({
email: serviceAccount.client_email,
key: serviceAccount.private_key,
scopes: 'https://www.googleapis.com/auth/calendar'
});
const calendar = google.calendar('v3');
process.env.DEBUG = 'dialogflow:*'; // enables lib debugging statements
const timeZone = 'America/Toronto';
const timeZoneOffset = '-05:00';
exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
const agent = new WebhookClient({ request, response });
console.log("Parameters", agent.parameters);
const appointment_type = agent.parameters.AppointmentType;
const attendee_email = agent.parameters.email;
const attendee_phone = agent.parameters.phone_number;
const attendee_firstname = agent.parameters.given_name;
const attendee_lastname = agent.parameters.last_name;
function makeAppointment (agent) {
// Calculate appointment start and end datetimes (end = +1hr from start)
//console.log("Parameters", agent.parameters.date);
const dateTimeStart = new Date(Date.parse(agent.parameters.date.split('T')[0] + 'T' + agent.parameters.time.split('T')[1].split('-')[0] + timeZoneOffset));
const dateTimeEnd = new Date(new Date(dateTimeStart).setHours(dateTimeStart.getHours() + 1));
const appointmentTimeString = dateTimeStart.toLocaleString(
'en-US',
{ month: 'long', day: 'numeric', hour: 'numeric', timeZone: timeZone }
);
// Check the availibility of the time, and make an appointment if there is time on the calendar
return createCalendarEvent(dateTimeStart, dateTimeEnd, appointment_type, attendee_email, attendee_phone, attendee_firstname, attendee_lastname).then(() => {
agent.add(`Excellent, it looks like ${appointmentTimeString} is available and we have reserved the time for you!.`);
}).catch(() => {
agent.add(`I'm so sorry, it looks like we're already booked on ${appointmentTimeString} is there an alternate day or time you are available?`);
});
}
let intentMap = new Map();
intentMap.set('Schedule Appointment', makeAppointment);
agent.handleRequest(intentMap);
});
function createCalendarEvent (dateTimeStart, dateTimeEnd, appointment_type, attendee_email, attendee_phone, attendee_firstname, attendee_lastname) {
return new Promise((resolve, reject) => {
calendar.events.list({
auth: serviceAccountAuth, // List events for time period
calendarId: calendarId,
timeMin: dateTimeStart.toISOString(),
timeMax: dateTimeEnd.toISOString()
}, (err, calendarResponse) => {
// Check if there is a event already on the Calendar
if (err || calendarResponse.data.items.length > 0) {
reject(err || new Error('Requested time conflicts with another appointment'));
} else {
// Create event for the requested time period
calendar.events.insert({ auth: serviceAccountAuth,
calendarId: calendarId,
resource: {summary: ' Membership Discussion' + ' ' + attendee_email + ' ' + attendee_phone ,
description: ' Membership Discussion' + ' ' + attendee_email + ' ' + attendee_phone,
location: 'Call ' + attendee_firstname + ' ' + attendee_lastname + ' at ' + attendee_phone,
start: {dateTime: dateTimeStart},
end: {dateTime: dateTimeEnd},
attendees: { email: 'new#example.com'} },
}, (err, event) => {
err ? reject(err) : resolve(event);
}
);
}
});
});
}

Grunt jshint reporter to send out emails

I have added grunt jshint task to my grunt. I created custom reporter to send out jsHint output as email. My custom reporter function is invoked. But no emails are coming through. There are no errors in the code.
Grunt version: "grunt": "^0.4.5",
"nodemailer": "^1.11.0",
"nodemailer-sendmail-transport": "^1.0.0"
Here is the sample code:
var nodemailer = require('nodemailer');
var smtpTransport = require('nodemailer-smtp-transport');
var async = require('async');
module.exports = {
reporter: function (errors) {
var len = errors.length,
str = '';
var items = [1];
errors.forEach(function (r) {
var file = r.file,
err = r.error;
str += file + ": line " + err.line + ", col " +
err.character + ", " + err.reason + "\n";
});
if (str) {
str += "\n" + len + " error" + ((len === 1) ? "" : "s") + "\n";
}
var transporter = nodemailer.createTransport( smtpTransport( {
service: "gmail",
secureConnection: false, // use SSL
port: 587, // port for secure SMTP
auth: {
user: "<my gmail username>",
pass: "<gmail account password>"
},
tls:{
ciphers:'SSLv3'
},
logger: true, // log to console
debug: true // include SMTP traffic in the logs
}));
// setup e-mail data with unicode symbols
var mailOptions = {
from: '<sender address>',
to: '<recipient address>',
subject: 'Hello', // Subject line
text: "why are you not working"
/* text: str */// plaintext body
/*html: '<b>Hello world</b>' // html body*/
};
async.eachSeries(items, function (item, next) {
transporter.sendMail(mailOptions, function(error, response){
// THIS CALLBACK IS NOT CALLED AT ALL
if(error){
console.log(error);
}else{
console.log("Message sent");
}
next(null);
});
}, function(err){
// All tasks are done now
console.log('All tasks are done now');
});
}
};
with async or without async doesn't matter. No emails are coming. I tried bye turning on the "Receive emails from unsecured apps" by following another stackoverflow post. That also did not help.
I would like to know is this correct approach or not? Any help/input is appreicated.

Resources