Get unread gmail mails with imap - javascript

Hello I'm trying to iplement some basics Google Api features and I get some issues when I try to get the unread messages in the gmail mailbox. Here is my code:
require('dotenv').config();
const nodemailer = require('nodemailer')
const { google } = require('googleapis')
const Imap = require('imap');
const CLIENT_ID = process.env.GOOGLE_CLIENT_ID
const CLIENT_SECRET = process.env.GOOGLE_CLIENT_SECRET
const REDIRECT_URI = process.env.GOOGLE_REDIRECT_URL
const REFRESH_TOKEN = process.env.GOOGLE_REFRESH_TOKEN
const oauht2Client = new google.auth.OAuth2(CLIENT_ID, CLIENT_SECRET, REDIRECT_URI)
oauht2Client.setCredentials({refresh_token : REFRESH_TOKEN})
function newMessages(/*onNewEmailCallback*/) {
const imap = new Imap({
user: "mymail#gmail.com",
xoauth2: oauht2Client.getAccessToken(),
host: 'imap.gmail.com',
port: 993,
tls: true,
tlsOptions: {
rejectUnauthorized: false
}
});
function showError(error) {
console.error('Error:', error);
}
function showMessage(msg) {
onNewEmailCallback(msg.subject);
console.log(msg.subject);
}
imap.connect();
imap.once('ready', () => {
imap.openBox('INBOX', true, (err, box) => {
if (err) showError(err);
imap.on('mail', (numNewMsgs) => {
imap.search(['UNSEEN'], (err, results) => {
if (err) showError(err);
results.forEach((result) => {
const f = imap.fetch(result, { bodies: '' });
f.on('message', (msg) => {
msg.on('body', (stream, info) => {
let buffer = '';
stream.on('data', (chunk) => {
buffer += chunk.toString('utf8');
});
stream.on('end', () => {
showMessage(Imap.parseHeader(buffer));
});
});
});
});
});
});
});
});
imap.once('error', showError);
imap.once('end', () => {
console.log('Connection terminée');
});
return imap;
}
When I call it in the index.js I get the error
/home/.../node_modules/imap/lib/Connection.js:1804
return str.replace(RE_BACKSLASH, '\\\\').replace(RE_DBLQUOTE, '\\"');
^
TypeError: str.replace is not a function
at escape
I'm probably passing a bad argument during the imap connection but I don't really know which one and I don't find anything online. Help PLEASE
By the way I had a certificate issue that's why I add the tlsOptions property.
Here the full traceback of the error:
/home/lerou/B-DEV-500-COT-5-2-area-segnon.gnonlonfoun/Back-End/node_modules/imap/lib/Connection.js:1804
return str.replace(RE_BACKSLASH, '\\\\').replace(RE_DBLQUOTE, '\\"');
^
TypeError: str.replace is not a function
at escape (/home/lerou/B-DEV-500-COT-5-2-area-/Back-End/node_modules/imap/lib/Connection.js:1804:14)
at Connection.<anonymous> (/home/lerou/B-DEV-500-COT-5-2-area-/Back-End/node_modules/imap/lib/Connection.js:1672:24)
at Connection._resTagged (/home/lerou/B-DEV-500-COT-5-2-area-/Back-End/node_modules/imap/lib/Connection.js:1535:22)
at Parser.<anonymous> (/home/lerou/B-DEV-500-COT-5-2-area-/Back-End/node_modules/imap/lib/Connection.js:194:10)
at Parser.emit (node:events:512:28)
at Parser._resTagged (/home/lerou/B-DEV-500-COT-5-2-area-/Back-End/node_modules/imap/lib/Parser.js:175:10)
at Parser._parse (/home/lerou/B-DEV-500-COT-5-2-area-/Back-End/node_modules/imap/lib/Parser.js:139:16)
at Parser._tryread (/home/lerou/B-DEV-500-COT-5-2-area-/Back-End/node_modules/imap/lib/Parser.js:82:15)
at Parser._cbReadable (/home/lerou/B-DEV-500-COT-5-2-area-/Back-End/node_modules/imap/lib/Parser.js:53:12)
at TLSSocket.emit (node:events:512:28)

Related

How to pass data from net.Socket.on to an outside variable or return it

I'm working with net in node.js and I'm sending packet to a server and listening to the response, but I can't manage to return it. Here's my code:
function packetsend (sockeT, packeT) {
var resp = null;
if(sockeT) {
sockeT.write(packeT);
sockeT.on('data', (data) => {
resp = data.toString()
})
}
return resp;
}
const socket = new net.Socket();
socket.connect({host: server, port: port}, function() {
console.log('Connected');
var packetRecv = packetsend(socket, 'some packet');
if (packetRecv === 'some') {
console.log("ok");
}
})
I don't understand why packetsend() function is not returning the updated resp variable, and sends undefined object instead. When I do console.log(data) inside the sockeT.on() I see that it receives data.
Try transforming your packetsend() function in an async function. Maybe it's not returning the resp because you return it before the event 'data' is invoked.
This is just an example of a possible implementation:
function packetsend (sockeT, packeT) {
return new Promise((resolve, reject) => {
if (sockeT) {
sockeT.write(packeT);
sockeT.on('data', (data) => {
resolve(data.toString());
});
//WARNING: I don't know this 'socketT' object, don't know if there is an 'error' event.
//but it's recommended to handle errors. This is just an example.
sockeT.on('error', (error) => {
reject(error);
});
}
else{
reject('Missing sockeT');
}
});
}
const socket = new net.Socket();
socket.connect({ host: server, port: port }, function () {
console.log('Connected');
packetsend(socket, 'some packet').then(packetRecv => {
console.log('Received data => '+ packetRecv);
if (packetRecv === 'some') {
console.log("ok");
}
}).catch(error => console.log(error));
})
Update: you can also use the async/await
const socket = new net.Socket();
socket.connect({ host: server, port: port }, async function () {
try {
console.log('Connected');
let packetRecv = await packetsend(socket, 'some packet');
console.log('Received data => '+ packetRecv);
if (packetRecv === 'some') {
console.log("ok");
}
}
catch (error) {
console.log(error);
}
});
Tips:
"promise" documentation
"eventEmitter" documentation
"async/await" documentation

Is there a correct way to handle promisified stream error in Nodejs

I am trying to catch an error in the controller and send status(500) to the front-end to let the user know that there is a streaming error. But for some reason the error is not caught and I am sending status(200) to the user. Let me know if i am doing something wrong.
file - utils.js
import WebSocket from 'ws';
import Twitter from 'twitter-lite';
import ck from 'ckey';
export const stream = (term, clients, twitterStream) => {
try {
const twitter = new Twitter({
// subdomain: 'api', // "api" is the default (change for other subdomains)
// version: '1.1', // version "1.1" is the default (change for other subdomains)
version: '2', // version "1.1" is the default (change for v2)
extension: false, // true is the default (this must be set to false for v2 endpoints)
consumer_key: ck.TWITTER_CONSUMER_KEY,
consumer_secret: ck.TWITTER_CONSUMER_SECRET,
access_token_key: ck.TWITTER_ACCESS_TOKEN_KEY,
access_token_secret: ck.TWITTER_ACCESS_TOKEN_SECRET,
});
let stream = twitter.stream('statuses/filter', { track: term });
new Promise(function (resolve, reject) {
stream.on('data', function (tweet) {
console.log('tweet');
resolve(broadcast(clients, JSON.stringify(tweet)));
});
stream.on('error', function (error) {
reject(error);
});
}).catch(function (e) {
console.log('stream error catch: ', e);
// throw e;
});
twitterStream = stream;
return twitterStream;
} catch (error) {
console.log('error from util', error);
// throw error;
}
};
const broadcast = (clients, message) => {
clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(message);
}
});
};
controller
import { stream } from './utils.js';
let twitterStream;
// Sets search term for twitter stream.
export const setSearchTerm = (req, res) => {
try {
const { term } = req.params;
console.log('setSearchTerm');
console.log('term: ', term);
if (twitterStream) {
console.log('getTweetPause');
twitterStream.destroy();
}
twitterStream = stream(term, req.app.locals.clients, twitterStream);
res.status(200).json({ message: 'Successful search request' });
} catch (error) {
res.status(500).json({ message: error });
}
};
file - utils.js
import WebSocket from 'ws';
import Twitter from 'twitter-lite';
import ck from 'ckey';
export const stream = (term) => {
const twitter = new Twitter({
// subdomain: 'api', // "api" is the default (change for other subdomains)
// version: '1.1', // version "1.1" is the default (change for other subdomains)
version: '2', // version "1.1" is the default (change for v2)
extension: false, // true is the default (this must be set to false for v2 endpoints)
consumer_key: ck.TWITTER_CONSUMER_KEY,
consumer_secret: ck.TWITTER_CONSUMER_SECRET,
access_token_key: ck.TWITTER_ACCESS_TOKEN_KEY,
access_token_secret: ck.TWITTER_ACCESS_TOKEN_SECRET,
});
let stream = twitter.stream('statuses/filter', { track: term });
return stream;
};
export const broadcast = (clients, message) => {
clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(message);
}
});
};
controller
import { stream, broadcast } from './utils.js';
let twitterStream;
// Sets search term for twitter stream.
export const setSearchTerm = async (req, res) => {
try {
const { term } = req.params;
console.log('setSearchTerm');
console.log('term: ', term);
if (twitterStream) {
console.log('getTweetPause');
twitterStream.destroy();
}
const currentStream = stream(term);
twitterStream = currentStream;
await new Promise((resolve, reject) => {
currentStream.on('data', function (tweet) {
console.log('tweets: ');
broadcast(req.app.locals.clients, JSON.stringify(tweet));
resolve(tweet);
});
currentStream.on('error', function (error) {
reject(error);
});
});
res.status(200).json({ message: 'Successful HTTP request' });
} catch (error) {
console.log('error catch: ');
res.status(500).json({ message: error });
}
};

Async Function working in Express but not NestJs

I initially created a little express server to run a report and file write function.
var ssrs = require('mssql-ssrs');
var fs = require('fs');
const express = require('express')
const app = express()
const port = 3001
app.get('/', (req, res) => {
reportCreation();
res.send('File Created');
})
app.get('/api', (req, res) => {
reportCreation();
res.json({'File Created': true});
})
app.listen(port, () => {
console.log(`Report Api listening at http://localhost:${port}`)
})
The function reportCreation() is an async function which gets a report from a SSRS. This works fine
async function reportCreation() {
var serverUrl = 'http://reportServerName/ReportServer/ReportExecution2005.asmx';
ssrs.setServerUrl(serverUrl);
var reportPath = '/ApplicationPortalReports/TestReportNew';
var fileType = 'word';
var parameters = { ApplicationId: 3, TrainingCardId: 267, PortalPersonId: 52 }
var auth = {
username: 'USERNAME',
password: 'PASSWORD',
domain: 'dmz'
};
try {
var report = await ssrs.reportExecution.getReportByUrl(reportPath, fileType, parameters, auth)
} catch (error) {
console.log(error);
}
console.log(report);
try {
fs.writeFile('ReportApiTest.doc', report, (err) => {
if (!err) console.log('Data written');
});
} catch (error) {
console.log(error);
}
I have been working a lot with NestJs recently and wanted to use the same function but within a NestJs service.
#Injectable()
export class AppService {
async getReport(): Promise<string> {
const serverUrl = 'http://reportServerName/ReportServer/ReportExecution2005.asmx';
ssrs.setServerUrl(serverUrl);
const reportPath = '/ApplicationPortalReports/TestReportNew';
const fileType = 'word';
// var parameters = {appId: 3, ReportInstanceId: 1 }
const parameters = {ApplicationId: 3, TrainingCardId: 267, PortalPersonId: 52 };
const auth = {
username: 'USERNAME',
password: 'PASSWORD',
domain: 'dmz'
};
try {
var report = await ssrs.reportExecution.getReportByUrl(reportPath, fileType, parameters, auth)
} catch (error) {
console.log(error);
}
console.log(report);
// excel = xlsx
// word = doc
// pdf = pdf
try {
fs.writeFile('ReportApiTest.doc', report, (err) => {
if (!err) { console.log('Data written');
return 'File Written Succesfully'}
});
} catch (error) {
console.log(error);
return 'File Write Error'
}
}
}
As you can see the files are almost identical, but when I run it through NestJs I get an error which looks like a problem with the line
var report = await ssrs.reportExecution.getReportByUrl(reportPath, fileType, parameters, auth)
not awaiting. Why does this work with Express and not NestJS? Below is the error from NestJs
buffer.js:219
throw new ERR_INVALID_ARG_TYPE(
^
TypeError [ERR_INVALID_ARG_TYPE]: The first argument must be one of type string, Buffer, ArrayBuffer,
Array, or Array-like Object. Received type undefined
at Function.from (buffer.js:219:9)
at new Buffer (buffer.js:179:17)
at Object.createType3Message (C:\Projects\SSRS-report-api\ssrs-report-api\node_modules\httpntlm\ntlm.js:172:19)
at sendType3Message (C:\Projects\SSRS-report-api\ssrs-report-api\node_modules\httpntlm\httpntlm.js:77:23)
at Immediate._onImmediate (C:\Projects\SSRS-report-api\ssrs-report-api\node_modules\httpntlm\httpntlm.js:101:4)
within the mssql-ssrs node package the getReportByURL looks like this
async function getReportByUrl(reportPath, fileType, params, auth) {
try {
var config = {
binary: true, // very important
username: auth.userName,
password: auth.password,
workstation: auth.workstation,
domain: auth.domain,
url: soap.getServerUrl()
+ "?" + (testReportPath(reportPath).replace(/\s/g, '+'))
+ "&rs:Command=Render&rs:Format=" + reportFormat(fileType)
+ formatParamsToUrl(params)
};
} catch (err) { report.errorHandler(err) }
return new Promise((resolve, reject) => {
config.url = encodeURI(config.url);
httpntlm.post(config, function (err, res) {
if (res.statusCode === 500) { reject(res) }
if (err || res.statusCode !== 200) { reject(err) }
else { resolve(res.body) }
})
})
}
Here is the app.controller.ts
#Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
#Get()
getHello(): Promise<string> {
return this.appService.getReport();
}
}
This is not an answer for the question. But after I see your code, I can see an error you will face in future if await ssrs.reportExecution.getReportByUrl(reportPath, fileType, parameters, auth) failed. Actually you see above error because of this.
The way you used the try catch is really bad.
Here's the way I code it.
#Injectable()
export class AppService {
async getReport(): Promise<string> {
const serverUrl = 'http://reportServerName/ReportServer/ReportExecution2005.asmx';
ssrs.setServerUrl(serverUrl);
const reportPath = '/ApplicationPortalReports/TestReportNew';
const fileType = 'word';
// var parameters = {appId: 3, ReportInstanceId: 1 }
const parameters = {ApplicationId: 3, TrainingCardId: 267, PortalPersonId: 52 };
const auth = {
username: 'USERNAME',
password: 'PASSWORD',
domain: 'dmz'
};
const report = await ssrs.reportExecution.getReportByUrl(reportPath, fileType, parameters, auth)
return new Promise(function(resolve, reject) {
fs.writeFile('ReportApiTest.doc', report, , function(err) {
if (err) reject(err);
resolve("File Created");
});
});
}
And in my controller
#POST
async writeFile() {
try {
const res = await this.appService.getReport();
return res;
} catch(err) {
// handle your error
}
}
I had fudged the code in the node_module changing the userName variable to username and had not done the same in the NestJS version. I forgot I had done that so now it is working.

How to connect Dialogflow Fulfillment with MySQL DB?

I tried to connect my Dialogflow agent with MySQL DB at my GCP, but it didn't work well.
I just wanna see the results in console, but there's no results about that.
I can only see the result "Undefined", but since I am not familiar with Node.js, I am not sure what this means.
Since there is no content in the result, I cannot proceed to the next step.
Here is my index.js script :
// See https://github.com/dialogflow/dialogflow-fulfillment-nodejs
// for Dialogflow fulfillment library docs, samples, and to report issues
'use strict';
const functions = require('firebase-functions');
const {WebhookClient} = require('dialogflow-fulfillment');
const {Card, Suggestion} = require('dialogflow-fulfillment');
const mysql = require('mysql');
process.env.DEBUG = 'dialogflow:debug'; // enables lib debugging statements
exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
const agent = new WebhookClient({ request, response });
console.log('Dialogflow Request headers: ' + JSON.stringify(request.headers));
console.log('Dialogflow Request body: ' + JSON.stringify(request.body));
function welcome(agent) {
agent.add(`Welcome to my agent!`);
}
function fallback(agent) {
agent.add(`I didn't understand`);
agent.add(`I'm sorry, can you try again?`);
}
function connectToDatabase() {
const connection = mysql.createConnection({
host: '34.64.***.***',
user: 'username',
password: 'password',
database: 'dbname'
});
return new Promise((resolve,reject) => {
connection.connect();
console.log('connection successed.');
resolve(connection);
});
}
function queryDatabase(connection, name){
return new Promise((resolve, reject) => {
connection.query('SELECT * FROM tb_user WHERE name = ?', name, (error, results, fields) => {
console.log('query successed.');
resolve(results);
});
});
}
function queryDatabase2(connection){
return new Promise((resolve, reject) => {
connection.query('SELECT * FROM tb_user', (error, results, fields) => {
if (error) console.log(error);
console.log('results', results);
});
});
}
function handleReadFromMysql(agent) {
const user_name = agent.parameters.name;
console.log(user_name.name);
return connectToDatabase()
.then(connection => {
console.log('connectToDatabase passed');
return queryDatabase(connection, user_name.name)
//return queryDatabase2(connection)
.then(result => {
console.log('queryDatabase passed');
console.log(result);
agent.add(`User name is ${user_name} .`);
//result.map(user => {
//if(user_name === user.name) {
//agent.add(`UserID is ${user.id}`);
//}
//});
connection.end();
});
});
}
let intentMap = new Map();
intentMap.set('Default Welcome Intent', welcome);
intentMap.set('Default Fallback Intent', fallback);
intentMap.set('findName', handleReadFromMysql);
agent.handleRequest(intentMap);
});
and the logs are :
enter image description here
Please somebody help me...
I solved this problem 4 months ago, so I share my code to help another like me.
I use socketPath of DB info. I forgot the reason.
function connectToDatabase() {
const connection = mysql.createConnection({
socketPath: '/cloudsql/*************:asia-northeast3:****',
user: '****',
password: '****',
database: '****'
});
return new Promise((resolve, reject) => {
connection.connect();
console.log('connection successed.');
resolve(connection);
});
}
And make query function like :
function findNameCountQuery(connection, name) {
return new Promise((resolve, reject) => {
var sql = `SELECT * FROM tb_user WHERE name = ?`;
var execSql = connection.query(sql, [name], (error, results) => {
if (error) throw error;
resolve(results);
});
});
Then I use agent function like :
async function findName(agent) {
const user_name = agent.parameters.person.name;
const connection = await connectToDatabase();
const result = await findNameQuery(connection, user_name);
result.map(user => {
message += `사용자 ID seq number : ${user.seq}\n`;
});
agent.add(message);
connection.end();
}
Hope to be helpful.

Better error handling with Promises?

I am currently experimenting Google Firebase functions to access Google APIs. It's running fine, but I am a little bit lost in trying to manage the errors that could be detected ...
In the .HTTPS getGoogleUsers functions , I would like to return an HTTP status code ( 200 or error code ) , and the data ( or error message )
As far as I can see , I can get errors:
from the connect() function ( 500: Internal server error or 401 Unauthorized )
from the listUsers() function ( 500: Internal server error or 400 Bad Request )
Am I totally or partially wrong ? what should be my strategy in this case ?
thanks for feedback ..
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const {google} = require('googleapis');
const KEY = require('./service-key.json');
// Create JSON Web Token Client
function connect () {
return new Promise((resolve, reject) => {
const jwtClient = new google.auth.JWT(
KEY.client_email,
null,
KEY.private_key,
['https://www.googleapis.com/auth/admin.directory.user'],
'adminuser#mydomain.com'
);
jwtClient.authorize((err) => {
if(err) {
reject(err);
} else {
resolve(jwtClient);
}
});
});
}
function listUsers (client) {
return new Promise((resolve, reject) => {
google.admin('directory_v1').users.list({
auth: client,
domain: 'mydomain.com',
}, (err, response) => {
if (err) {
reject(err);
}
resolve(response.data.users);
});
});
}
function getAllUsers () {
connect()
.then(client => {
return listUsers(client);
})
.catch(error => {
return error;
})
}
exports.getGoogleUsers = functions.https.onRequest((req, res) => {
const users = getAllUsers();
if (error) {
status = error.status;
data = error.message;
} else {
status = 200;
data = users;
}
res.send({ status: status, datas: data })
});
I think you are looking for
function getAllUsers () {
return connect().then(listUsers);
//^^^^^^
}
exports.getGoogleUsers = functions.https.onRequest((req, res) => {
getAllUsers().then(users => {
return {status: 200, datas: users};
}, error => {
return {status: error.status, datas: error.message};
}).then(response => {
res.send(response);
});
});
This uses the .then(…, …) method with two callbacks to distinguish between success and error case, and to wait for the result of the promise.

Categories

Resources