I am trying to get mail id from twitter user through nodejs at the result I only got tweets and other details of users but I don't get mail id. I attach my code anyone tell me whether my code is write way to get mail id
// twitter api script
var config = require("./config");
// In config file I have my consumer_key, consumer_secret, access_token_key,access_token_secret
var fs = require('fs');
var Twitter = require('twitter');
var client = new Twitter(config.twitter);
var writer = fs.createWriteStream('tweets.json')
var start_time = 0;
var handles = "";
fs.readFile('./t_handle.csv', 'utf8', function(err, contents) {
var params = {
'include_email' : true
};
handles = contents.split("\n");
writer.write("[");
handles.forEach(function(handle){
handle = handle.split("/")[3];
if(typeof handle !== 'undefined' && handle){
handle = handle.split("?")[0];
get_handle_tweets(handle,params);
}
});
});
function get_handle_tweets(handle){
client.get('statuses/user_timeline', { screen_name: handle }, function(error, tweets,params,response) {
if(error){
console.log(error);
}else{
tweets.forEach(function(tweet){
writer.write(JSON.stringify(tweet)+",\n");
});
}
});
}
First check if when you Authorize for your app in the Will be able to see field, "see your email address" is there , if not check if you have valid privacy policy and TOS URL since white listing the app is no longer required, also in php I had to Change 'include_email' : true to 'include_email' : 'true' sometime ago when I tried this, so that could be the issue.
Related
I have a problem with my nodejs code and the connection to the official whatsapp business api.
The bot connects the webhook correctly, the messages arrive to the server correctly but the code I have implemented to make it respond is not being effective, I checked the code from top to bottom but I can't find the fault.
I leave you the codes so you have more context:
whatsappController.js:
const fs = require("fs");
const myConsole = new console.Console(fs.createWriteStream("./logs.txt"));
const whatsappService = require("../services/whatsappService")
const VerifyToken = (req, res) => {
try {
var accessToken = "456E7GR****************************";
var token = req.query["hub.verify_token"];
var challenge = req.query["hub.challenge"];
if(challenge != null && token != null && token == accessToken){
res.send(challenge);
}
else{
res.status(400).send();
}
} catch(e) {
res.status(400).send();
}
}
const ReceivedMessage = (req, res) => {
try {
var entry = (req.body["entry"])[0];
var changes = (entry["changes"])[0];
var value = changes["value"];
var messageObject = value["messages"];
if(typeof messageObject != "undefined"){
var messages = messageObject[0];
var text = GetTextUser(messages);
var number = messages["from"];
myConsole.log("Message: " + text + " from: " + number);
whatsappService.SendMessageWhatsApp("The user say: " + text, number);
myConsole.log(messages);
myConsole.log(messageObject);
}
res.send("EVENT_RECEIVED");
}catch(e) {
myConsole.log(e);
res.send("EVENT_RECEIVED");
}
}
function GetTextUser(messages){
var text = "";
var typeMessage = messages["type"];
if(typeMessage == "text"){
text = (messages["text"])["body"];
}
else if(typeMessage == "interactive"){
var interactiveObject = messages["interactive"];
var typeInteractive = interactiveObject["type"];
if(typeInteractive == "button_reply"){
text = (interactiveObject["button_reply"])["title"];
}
else if(typeInteractive == "list_reply"){
text = (interactiveObject["list_reply"])["title"];
}else{
myConsole.log("sin mensaje");
}
}else{
myConsole.log("sin mensaje");
}
return text;
}
module.exports = {
VerifyToken,
ReceivedMessage
}
The second file is whatsappService which I make the connection with the api using the token and I also send the format of the message I want to send when I receive a hello for example...
const https = require("https");
function SendMessageWhatsApp(textResponse, number){
const data = JSON.stringify({
"messaging_product": "whatsapp",
"recipient_type": "individual",
"to": number,
"type": "text",
"text": {
"preview_url": false,
"body": textResponse
}
});
const options = {
host:"graph.facebook.com",
path:"/v15.0/1119744*************/messages",
method:"POST",
body:data,
headers: {
"Content-Type":"application/json",
Authorization:"Bearer EAAWNbICfuWEBAK5ObPbD******************************************************"
}
};
const req = https.request(options, res => {
res.on("data", d=> {
process.stdout.write(d);
});
});
req.on("error", error => {
console.error(error);
});
req.write(data);
req.end();
}
module.exports = {
SendMessageWhatsApp
};
Then I declare the routes for the get (to check token) and post (to receive and reply to messages) methods:
const expres = require("express");
const router = expres.Router();
const whatsappController = require("../controllers/whatsappControllers");
router
.get("/", whatsappController.VerifyToken)
.post("/", whatsappController.ReceivedMessage)
module.exports = router;
Last but not least the index file for the code to run correctly:
const express = require("express");
const apiRoute = require("./routes/routes");
const app = express();
const PORT = process.env.PORT || 3000
app.use(express.json());
app.use("/whatsapp", apiRoute);
app.listen(PORT, () => (console.log("El puerto es: " + PORT)));
I should clarify that I did the tests with Postman and they were all successful, it responds and receives messages correctly, finally I did the tests by uploading the bot to the Azure service and it works without problem until it has to answer/replicate the user's message.
The bot is not responding to the user when he talks to it but everything arrives correctly to the server and it processes it with a 200 response. I attach the evidence that there is no problem in the reception.
Finally I must say that in the meta platform I have everything configured as specified by the same platform, I have already configured the api to answer the messages through the webhooks and everything is correct, I just can't get the bot to answer correctly.
The bot is hosted in the Azure service.
Solved: some numbers have a problema with the api of WAB in my country (Argentina) the phone numbers start in +54 9 11. The problem is the 9 in the phone number, and this have a conflict in the servers of meta, Solution quit number 9 to numbers of this country and the message will send to user.
I am making a chat bot using the QnA template (Microsoft Azure). Basically, a user asks a question and the bot will try and find the answer in an FAQ document. If it fails, I want it to run a Bing search with the user's query and replies with the most accurate answer. I found this example that uses Bing Web Search API: https://learn.microsoft.com/en-us/azure/cognitive-services/bing-web-search/quickstarts/nodejs. For now, I just want the bot to reply with the first link of the search for example.
However, I don't know how to merge the code in the link, with the generated code for the QnA Bot (in Node.js):
var restify = require('restify');
var builder = require('botbuilder');
var botbuilder_azure = require("botbuilder-azure");
var builder_cognitiveservices = require("botbuilder-cognitiveservices");
var request = require('request');
// Setup Restify Server
var server = restify.createServer();
server.listen(process.env.port || process.env.PORT || 3978, function () {
console.log('%s listening to %s', server.name, server.url);
});
// Create chat connector for communicating with the Bot Framework Service
var connector = new builder.ChatConnector({
appId: process.env.MicrosoftAppId,
appPassword: process.env.MicrosoftAppPassword,
openIdMetadata: process.env.BotOpenIdMetadata
});
// Listen for messages from users
server.post('/api/messages', connector.listen());
var tableName = 'botdata';
var azureTableClient = new botbuilder_azure.AzureTableClient(tableName, process.env['AzureWebJobsStorage']);
var tableStorage = new botbuilder_azure.AzureBotStorage({ gzipData: false }, azureTableClient);
// Create your bot with a function to receive messages from the user
var bot = new builder.UniversalBot(connector);
bot.set('storage', tableStorage);
// Recognizer and and Dialog for GA QnAMaker service
var recognizer = new builder_cognitiveservices.QnAMakerRecognizer({
knowledgeBaseId: process.env.QnAKnowledgebaseId,
authKey: process.env.QnAAuthKey || process.env.QnASubscriptionKey, // Backward compatibility with QnAMaker (Preview)
endpointHostName: process.env.QnAEndpointHostName
});
var basicQnAMakerDialog = new builder_cognitiveservices.QnAMakerDialog({
recognizers: [recognizer],
defaultMessage: 'Sorry, I cannot find anything on that topic',
qnaThreshold: 0.3
});
// Override the invokeAnswer function from QnAMakerDialog
builder_cognitiveservices.QnAMakerDialog.prototype.invokeAnswer = function (session, recognizeResult, threshold, noMatchMessage) {
var qnaMakerResult = recognizeResult;
session.privateConversationData.qnaFeedbackUserQuestion = session.message.text;
if (qnaMakerResult.score >= threshold && qnaMakerResult.answers.length > 0) {
if (this.isConfidentAnswer(qnaMakerResult) || this.qnaMakerTools == null) {
this.respondFromQnAMakerResult(session, qnaMakerResult);
this.defaultWaitNextMessage(session, qnaMakerResult);
}
else {
this.qnaFeedbackStep(session, qnaMakerResult);
}
}
else {
this.noMatch(session, noMatchMessage, qnaMakerResult);
}
};
// API call to Bing
basicQnAMakerDialog.noMatch = function (session, noMatchMessage, qnaMakerResult) {
var term = session.message.text;
var key = 'i hid it';
var options = {
url: "https://api.cognitive.microsoft.com/bing/v7.0/search?q=" + term,
method: 'GET',
headers : {
'Ocp-Apim-Subscription-Key' : key
}
};
request(options, function(err,res, body){
if(err){
console.error(err);
session.send(noMatchMessage);
}
body = JSON.parse(body);
session.send("I found a thing: " + body["webPages"]["value"][0]["name"]);
});
};
bot.dialog('basicQnAMakerDialog', basicQnAMakerDialog);
bot.dialog('/', //basicQnAMakerDialog);
[
function (session, results) {
session.replaceDialog('basicQnAMakerDialog');
},
]);
In the function inside the bot.dialog, I think I should add a condition such as: if the bot returns the default message, open a web page and let the "term" to search be the user's last message. However, I don't know how to code this exactly, and where to do so. Also, I don't know how to exit from the replaceDialog function in order to reply with something other than the default message or the answers in the FAQ.
PS: I don't have much experience with javascript or web development in general. Any help will be appreciated :)
What you are implementing involves two steps, extracting out the "noMatch" case to a method so you can make a full response message, and then the API call itself.
The "noMatch" method.
First, you will want to override the invokeAnswer function from QnAMakerDialog (This override changes nothing but adding a call to a separate method for the noMatch case.)
builder_cognitiveservices.QnAMakerDialog.prototype.invokeAnswer = function (session, recognizeResult, threshold, noMatchMessage) {
var qnaMakerResult = recognizeResult;
session.privateConversationData.qnaFeedbackUserQuestion = session.message.text;
if (qnaMakerResult.score >= threshold && qnaMakerResult.answers.length > 0) {
if (this.isConfidentAnswer(qnaMakerResult) || this.qnaMakerTools == null) {
this.respondFromQnAMakerResult(session, qnaMakerResult);
this.defaultWaitNextMessage(session, qnaMakerResult);
}
else {
this.qnaFeedbackStep(session, qnaMakerResult);
}
}
else {
this.noMatch(session, noMatchMessage, qnaMakerResult);
}
};
After this you will define your basicQnAMakerDialog as usual, but consider making the default message field something for an error case or "no results found". (This doesn't really matter, as you can arbitrarily decide what you send back within the noMatch method, but it's nice to have the default there)
var basicQnAMakerDialog = new builder_cognitiveservices.QnAMakerDialog({
recognizers: [recognizer],
defaultMessage: 'Sorry, I can't find anything on that topic'
qnaThreshold: 0.3
});
After this you can define the noMatch method seperately.
basicQnAMakerDialog.noMatch = function (session, noMatchMessage, qnaMakerResult) {
session.send(noMatchMessage); //Contains the default message
this.defaultWaitNextMessage(session, qnaMakerResult);
}
Within this method we can call the API or do basically whatever we want.
Credit to gliddell on this override
The API call.
Within the noMatch method, we will make an API call to Bing
basicQnAMakerDialog.noMatch = function (session, noMatchMessage, qnaMakerResult) {
var term = session.message.text;
var key = <Environment Var containing the Bing key>;
var options = {
url: "https://api.cognitive.microsoft.com/bing/v7.0/search?q=" + term,
method: 'GET',
headers : {
'Ocp-Apim-Subscription-Key' : key
}
};
request(options, function(err,res, body){
if(err){
console.error(err);
session.send(noMatchMessage);
}
body = JSON.parse(body);
session.send("I found a thing: " + body["webPages"]["value"][0]["name"]);
});
};
You can customize the message you are sending back as much as you like after you get the results from the search.
My NodeJS backend communicates with the Mailchimp API (v3) in order to register new subscribers. I'm unable to find the value that allows setting a simple "YES" or "NO" in this case.
From the list settings UI, here is my custom field:
The following code properly registers the user but the custom "WEB3JS" field isn't set (with npm install mailchimp-api-v3 installed).
What could I be doing wrong?
var Mailchimp = require('mailchimp-api-v3')
var mailchimp = new Mailchimp(/* API key */);
return function handler(req, res){
const email = req.body.email;
const name = req.body.name;
const web3 = req.body.web3;
const body = {
email_address : email,
status : 'subscribed'
};
body.WEB3JS = web3 ? 'YES' : 'NO';
body.WEB3JS = 'YES'; // Debug: force "yes"
mailchimp.post('/lists/**LIST_ID**/members', body)
.then(function(results) {
res.json({});
})
Recently I had to hide my IP address using CloudFlare, but with my MX record pointed to my own domain, I can't.
I know that Mailgun can forward all the emails to user#example.com to Gmail, but I don't want that. I hope the email can be forwarded example.com's own SMTP server.
I decided to use Mailgun catch_all() and forward("https://exmaple.com/incoming-email?key=SECRET_KEY"), and wrote some node.js code for that.
var nodemailer = require('nodemailer');
var express = require('express');
var multer = require('multer');
var router = express.Router();
var KEY = 'SECRET_KEY';
var ADDITIONAL_HEADERS = ['timestamp', 'token', 'signature', 'mime-version'];
var transporter = nodemailer.createTransport('smtp://127.0.0.1');
var storage = multer.memoryStorage();
var upload = multer({
storage: storage
});
router.post('/', upload.any(), function (req, res, next) {
if (!req.query.key || req.query.key !== KEY) {
res.sendStatus(403);
return;
}
console.log('Incoming mail: ', req.body.From);
req.body.to = req.body.To;
req.body.cc = req.body.Cc;
req.body.from = req.body.From;
req.body.date = req.body.Date;
req.body.references = req.body.References;
req.body.replyTo = req.body['Reply-To'];
req.body.inReplyTo = req.body['In-Reply-To'];
req.body.messageId = req.body['Message-Id'];
req.body.html = req.body['body-html'];
req.body.text = req.body['body-plain'];
req.body.headers = {};
for (var key in req.body) {
if (key.toLowerCase().indexOf('X-') !== -1 ||
ADDITIONAL_HEADERS.indexOf(key.toLowerCase()) !== -1) {
req.body.headers[key] = req.body[key];
}
}
if (req.files) {
req.body.attachments = req.files;
req.body.attachments.forEach(function (attachment) {
attachment.filename = attachment.originalname;
attachment.contentType = attachment.mimetype;
attachment.content = attachment.buffer;
});
}
transporter.sendMail(req.body, function (err, info) {
if (err) {
console.log(err);
res.status(400).send(err);
return;
}
console.log(info);
res.send(info);
});
});
module.exports = router;
As you can see, the code is able to forward any incoming emails to localhost SMTP server. Using the code about, I can receive emails using user#example.com. The email received will have correct from, to, cc, subject and body and even attachments.
However, a day later I am forced to be unsubscribed from Arch Linux mailing lists, and a person emailed to me that:
It appears that your mail server is misconfigured and sends at least
some mails it receives from our mailing lists back to the list. It also
removes some headers in the process which help mailman to detect such
behaviour. Since those headers are missing, mailman will send the mail
out once again to everyone, leading to a never ending loop.
After I called out for help, I have also received some emails indicate that I was unable to receive any emails from mailing lists in the period.
Why could it go wrong? What are the "some headers" in the email removed in the forwarding / wrapping process? Is it Mailgun's problem or my own problem? If it's my problem, what could be added to the code above? Thanks.
I found that when the email is Bcc to you, then the mail will be looked like:
To: announce#example-mailing-list.com
Bcc: you#example.com
So that when using the code in the question, the mail will be handle by the node.js code, then the mail will be sent back to announce#example-mailing-list.com using smtp://127.0.0.1, causing infinite loop.
I have updated my code to find all the email addresses of my domain from the recipient list and set them as To field, and the infinite loop no longer presents.
For example, the recipient list will be looked like: announce#example-mailing-list.com, you#example.com, so I am finding all email addresses ending with #example.com and make them To field.
Caveat: some information will be missing though, like I can no longer see the original To field of an email. However, you can append the original To field to any fields, like subject.
var nodemailer = require('nodemailer');
var express = require('express');
var multer = require('multer');
var router = express.Router();
var MY_DOMAIN = 'example.com';
var KEY = 'SECRET_KEY;
var ADDITIONAL_HEADERS = ['timestamp', 'token', 'signature', 'mime-version',
'return-path', 'dkim-signature'
];
var transporter = nodemailer.createTransport('smtp://127.0.0.1');
var storage = multer.memoryStorage();
var upload = multer({
storage: storage
});
router.post('/', upload.any(), function (req, res, next) {
if (!req.query.key || req.query.key !== KEY) {
res.sendStatus(403);
return;
}
console.log('Incoming mail: ', req.body.From);
req.body.to = req.body.recipient.split(', ').filter(email => email.indexOf('#' + MY_DOMAIN) !== -1);
req.body.from = req.body.From;
req.body.date = req.body.Date;
req.body.references = req.body.References;
req.body.replyTo = req.body['Reply-To'];
req.body.inReplyTo = req.body['In-Reply-To'];
req.body.messageId = req.body['Message-Id'];
req.body.html = req.body['body-html'];
req.body.text = req.body['body-plain'];
req.body.headers = {};
for (var key in req.body) {
if (key.toLowerCase().indexOf('x-') !== -1 ||
ADDITIONAL_HEADERS.indexOf(key.toLowerCase()) !== -1) {
req.body.headers[key] = req.body[key];
}
}
console.log('Params: ' + JSON.stringify(req.body));
if (req.files) {
req.body.attachments = req.files;
req.body.attachments.forEach(function (attachment) {
attachment.filename = attachment.originalname;
attachment.contentType = attachment.mimetype;
attachment.content = attachment.buffer;
});
}
transporter.sendMail(req.body, function (err, info) {
if (err) {
console.log(err);
res.status(400).send(err);
return;
}
console.log(info);
res.send(info);
});
});
module.exports = router;
An email does not only have a To header, but it also has the SMTP RCPT TO value. This value may be different from the To header that is contained inside the email.
See: In SMTP, must the RCPT TO: and TO: match?
Your code seems to use the mail content and does not appear to handle the transport recipient (SMTP RCPT TO) separately. I do not know if mailgun provides this value.
Just submit the mail to your smtp server and some library along the way probably extracts the recipient from the To header, however in the case of mailing lists this is often incorrect.
I'm trying to create a simple client for Lync using Nodejs.
Base on http://ucwa.lync.com/documentation/KeyTasks-CreateApplication I've made someting like this.
It works until last step #9 when I should register my app with UCWA. Server responds with code 500 and silly explanation
There is a problem with the resource you are looking for, and it cannot be displayed
And headers
x-ms-diagnostics': '28032;source="mysource";reason="The web ticket is invalid."'
var http = require('request-promise');
var lync = {};
lync.setup = function(email, password){
var self = this;
var hostname = email.split('#');
this.username = email;
//discover urls
return http.get('http://lyncdiscover.'+hostname[1])
.then(function(d) {
var parsed = JSON.parse(d);
self.urls = {
self: parsed._links.self.href,
user: parsed._links.user.href,
xframe: parsed._links.xframe.href
};
return http.get(self.urls.user);
}).catch(function(err){
if(err.statusCode == 401){
var toParse = err.response.headers['www-authenticate'];
var Oauth = toParse.match(/https:\/\/[\d\w\./_-]*/i)[0];
var loginPost = {
grant_type: 'password',
username: email,
password: password
};
return http.post(Oauth, {form:loginPost});
}
return false
}).then(function(data){
var parsed = JSON.parse(data);
//setup authorization
http = http.defaults({
headers: {Authorization: parsed.token_type + ' ' + parsed.access_token}
});
//console.log(self.urls.user);
//console.log('Authorization:'+ parsed.token_type + ' ' + parsed.access_token);
return http.get(self.urls.user);
}).then(function(data){
var parsed = JSON.parse(data);
self.urls.applications = parsed._links.applications.href;
var registerApp = {
culture : "en-us",
endpointId : "2d9dc28d-4673-4035-825c-feb64be28e4e",
userAgent : "Test"
};
var r = "{'userAgent': 'NodeJs', 'endpointId' : '2d9dc28d-4673-4035-825c-feb64be28e4e', 'culture': 'en-US'}";
return http.post(self.urls.applications, {body: registerApp, json:true});
})
.then(function(data){
console.log(data);
})
.catch(function(err){
console.log(err);
return false;
});
};
//run app
lync.setup('login#domain.com', 'password').then(function(ret){
});
One key point here. It's not my server. I just have an account over there and I can login with official Lync client or Pidgin plugin.
Are there some extra steps to "allow" my app to work with UCWA?
#ShelbyZ
I can easily authorize using Oauth. I'm receiving authorization token so I'm logged in.
I'm receiving json similar to
"_links":{
"self":{"href":"link"},
"applications":{"href":"i need this"},
"xframe":{"href":"link"}
} }
Now. I need to "register my application" doing POST.
In this last step I get 500 code response.
I hope It's not related with that #Matthew Proctor said..
becouse I cannot simple administrate the server
Thank you #ShelbyZ
You were right, it was split-domain scenario. Now authorization works, and I can register my app. Also example for future generations
var http = require('request-promise');
var lync = {};
lync._authorize = function(){
var self = this;
var orgDomain = self.urls.user.match(/https:\/\/([\w\d\.]+)/i)[0];
//console.log(orgDomain);
http.get(self.urls.user).catch(function(err){
if(err.statusCode == 401){
var toParse = err.response.headers['www-authenticate'];
var Oauth = toParse.match(/https:\/\/[\d\w\./_-]+/i)[0];
var loginPost = {
grant_type: 'password',
username: self.username,
password: self.password
};
return http.post(Oauth, {form:loginPost});
}
}).then(function(data){
if(data) {
var parsed = JSON.parse(data);
//setup authorization
http = http.defaults({
headers: {Authorization: parsed.token_type + ' ' + parsed.access_token}
});
return http.get(self.urls.user);
}
}).then(function(data){
//check for split-domain scenario
var parsed = JSON.parse(data);
var domain = parsed._links.self.href.match(/https:\/\/([\w\d\.]+)/i)[0];
console.log('[1] '+orgDomain);
console.log('[2] '+domain);
if(domain!== orgDomain){
//split domain scenario
self.urls.user = self.urls.user.replace(orgDomain, domain);
http = http.defaults({
headers: {Authorization: null}
});
self._authorize();
} else { //create app
var parsed = JSON.parse(data);
self.urls.applications = parsed._links.applications.href;
var registerApp = {
culture : "en-us",
endpointId : "2d9dc28d-4673-4035-825c-feb64be28e4e",
userAgent : "NodeJs client"
};
return http.post(self.urls.applications, {body: registerApp, json:true});
}
}).then(function(app){
console.log(app);
});
};
lync.setup = function(email, password){
var self = this;
var hostname = email.split('#');
this.username = email;
this.password = password;
//discover urls
return http.get('http://lyncdiscover.'+hostname[1])
.then(function(d) {
var parsed = JSON.parse(d);
self.urls = {
self: parsed._links.self.href,
user: parsed._links.user.href,
xframe: parsed._links.xframe.href
};
return self._authorize();
});
};
//run app
lync.setup('username#domain.com', 'password');
I was getting the same error before I added my test domain to the list of Allowed Domains.
This can be updated via PowerShell, full details below:
Enabling UCWA and Configuring Allowed Domains
I've had clients see similar errors when running my code from http://localhost/, their fix was to test their code on a FQDN (ie http://testmyucwacode.mydomain.com/).