I have a nodejs application, I need to add a feature were I can send a survey using direct messaging on twitter to the user and when the user replies, the application will send the next question and so on (preferably if I can add options for the replies expected).
What I'm stuck in currently is sending the direct message, the examples that I found seem to work with the old version and not working with Twitter API v2.
One sample code that I tried but also not working is below:
const Twit = require('twit');
const config = require('./config.js');
const T = new Twit(config);
const stream = T.stream('user');
const SendMessage = user => {
const { screen_name, name } = user.source;
const obj = {
screen_name: screen_name,
text: "Hi there!"
};
timeout = 5000;
setTimeout(() => {
T.post("direct_messages/new", obj)
.catch(err => {
console.error("error", err.stack);
})
.then(result => {
console.log(`Message sent successfully To ${screen_name}!`);
});
}, timeout);
};
user = new Object;
user.source = { screen_name: 'name', name: 'userDisplayedName' };
SendMessage(user);
Output Error:
throw er; // Unhandled 'error' event
^
Error: Bad Twitter streaming request: 404
at Object.exports.makeTwitError (/../node_modules/twit/lib/helpers.js:74:13)
at Request.<anonymous> (/../node_modules/twit/lib/streaming-api-connection.js:96:29)
at Request.emit (events.js:327:22)
at IncomingMessage.<anonymous> (/../node_modules/request/request.js:1083:12)
at Object.onceWrapper (events.js:421:28)
at IncomingMessage.emit (events.js:327:22)
at endReadableNT (_stream_readable.js:1327:12)
at processTicksAndRejections (internal/process/task_queues.js:80:21)
Emitted 'error' event on StreamingAPIConnection instance at:
at Request.<anonymous> (/../node_modules/twit/lib/streaming-api-connection.js:99:14)
at Request.emit (events.js:327:22)
[... lines matching original stack trace ...]
at processTicksAndRejections (internal/process/task_queues.js:80:21) {
code: null,
allErrors: [],
twitterReply: '',
statusCode: 404
}
does anyone know how to send a direct message using Twitter API v2 with Nodejs?
At the time of writing this answer, the Direct Message APIs have not been migrated to v2, so you will need to continue to use Twitter API v1.1 for Direct Messages.
The error here is not in sending, but in receiving -> Bad Twitter streaming request -> 404. This is because the code you have found is trying to use the Twitter user streams API, which was removed in 2018. These have been replaced with the Account Activity API (webhooks). You will need to build an app that receives webhook events for Direct Messages, and then post replies. This is all in v1.1 of the API.
Related
I'm new to using webhooks and I've pored over the discord documentation to help set me up with a very basic bot. At this point all I want to do is for the bot to come online and use a webhook to make a post. I've even directly copied the basic code from The Discord Webhooks documentation and I cant get that to work.
Using some console logs I've determined that client.channels.cache.get(config.webhookID); returns undefined and therefore channel.fetchWebhooks() is not a function. Is this still the way to get webhooks that are in the channel or is the documentation behind?
I'm turning to any experienced user that could potentially help me with this problem. I would really appreciate any help you can provide.
const Discord = require('discord.js');
const config = require('./config.json');
const client = new Discord.Client();
const embed = new Discord.MessageEmbed()
.setTitle('Some Title')
.setColor('#0099ff');
client.once('ready', async () => {
const channel = client.channels.cache.get(config.webhookID); //This is the problem.
try {
const webhooks = await channel.fetchWebhooks(); //This will not execute because channel is undefined.
const webhook = webhooks.first();
await webhook.send('Webhook test', {
username: 'some-username',
avatarURL: 'https://i.imgur.com/wSTFkRM.png',
embeds: [embed],
});
} catch (error) {
console.error('Error trying to send: ', error);
}
});
client.login(config.token);
The error in the console is this:
Error trying to send: TypeError: Cannot read property 'fetchWebhooks' of undefined
at Client.<anonymous> (C:\Users\me\OneDrive\Desktop\Discord Bot\index.js:13:34)
at Object.onceWrapper (events.js:421:28)
at Client.emit (events.js:315:20)
at WebSocketManager.triggerClientReady (C:\Users\me\OneDrive\Desktop\Discord Bot\node_modules\discord.js\src\client\websocket\WebSocketManager.js:433:17)
at WebSocketManager.checkShardsReady (C:\Users\me\OneDrive\Desktop\Discord Bot\node_modules\discord.js\src\client\websocket\WebSocketManager.js:417:10) at WebSocketShard.<anonymous> (C:\Users\me\OneDrive\Desktop\Discord Bot\node_modules\discord.js\src\client\websocket\WebSocketManager.js:199:14)
at WebSocketShard.emit (events.js:315:20)
at WebSocketShard.checkReady (C:\Users\me\OneDrive\Desktop\Discord Bot\node_modules\discord.js\src\client\websocket\WebSocketShard.js:467:12)
at WebSocketShard.onPacket (C:\Users\me\OneDrive\Desktop\Discord Bot\node_modules\discord.js\src\client\websocket\WebSocketShard.js:439:16)
at WebSocketShard.onMessage (C:\Users\me\OneDrive\Desktop\Discord Bot\node_modules\discord.js\src\client\websocket\WebSocketShard.js:293:10)
I figured out the answer. I was using the field "id" when I should have been using the field "channel_id".
I set up a one firebase function to send messages to my bot. But when firebase gets request I can see that some error occurred.
FetchError: request to https://api.telegram.org/bot<BOT-TOKEN>/getMe failed, reason: getaddrinfo EAI_AGAIN api.telegram.org:443
at ClientRequest.<anonymous> (/srv/node_modules/node-fetch/lib/index.js:1453:11)
at emitOne (events.js:116:13)
at ClientRequest.emit (events.js:211:7)
at TLSSocket.socketErrorListener (_http_client.js:401:9)
at emitOne (events.js:116:13)
at TLSSocket.emit (events.js:211:7)
at emitErrorNT (internal/streams/destroy.js:66:8)
at _combinedTickCallback (internal/process/next_tick.js:139:11)
at process._tickDomainCallback (internal/process/next_tick.js:219:9)
and
Error: Can't set headers after they are sent.
at validateHeader (_http_outgoing.js:491:11)
at ServerResponse.setHeader (_http_outgoing.js:498:3)
at ServerResponse.header (/worker/node_modules/express/lib/response.js:767:10)
at ServerResponse.contentType (/worker/node_modules/express/lib/response.js:595:15)
at ServerResponse.sendStatus (/worker/node_modules/express/lib/response.js:357:8)
at /srv/index.js:30:13
at <anonymous>
at process._tickDomainCallback (internal/process/next_tick.js:229:7)
But secound one I suppose does not influence on the result.
As a result, I don't get any message to my to Bot.
The next script that i use:
const functions = require('firebase-functions');
const telegraf = require('telegraf');
const axios = require('axios');
const bot = new telegraf('<BOT-TOKEN>')
bot.start((ctx) => ctx.reply('Welcome!'))
bot.help((ctx) => ctx.reply('Send me a sticker'))
bot.on('sticker', (ctx) => ctx.reply('👍'))
bot.hears('hi', (ctx) => ctx.reply('Hey there'))
bot.launch()
exports.helloWorld = functions.https.onRequest((request, res) => {
const token = '<BOT-TOKEN>';
const url = `https://api.telegram.org/bot${token}/sendMessage`;
axios.post(url, {
chat_id: '154866113',
text: "just do it!"
})
.then(function (response) {
console.log(response);
res.send({ status: "O"});
return 'ok';
})
.catch(function (error) {
console.log(error);
res.sendStatus(500);
});
res.send("Hello from Firebase!");
});
The getaddrinfo EAI_AGAIN error means that you need to switch to the "Flame" or "Blaze" pricing plan.
As a matter of fact, the free "Spark" plan "allows outbound network requests only to Google-owned services". See https://firebase.google.com/pricing/ (hover your mouse on the question mark situated after the "Cloud Functions" title)
The telegram API is not a Google-owned service, so you need to switch to the "Flame" or "Blaze" plan.
I'm guessing the answer to the EAI_AGAIN error is due to a malformed URI. Are you sure https://api.telegram.org/bot<BOT-TOKEN>/getMe is the correct URI and is accessible from where ever this script is being run from?
The second error is due to you calling the res.send() method more than once. Express res objects wrap Node's ServerResponse object, and once the connection is closed via write or end, it cannot be sent again. Try removing the res.send("Hello from Firebase!"); section of your code and coming back.
Observed Application Behavior
I'm getting a UnhandledPromiseRejectionWarning: Error: Upload failed when using #google-cloud/storage in node.js.
These errors come when processing thousands of requests. It's a small percentage that cause errors, but due to the lack of ability to handle the errors, and the lack of proper context from the error message, it's very difficult to determine WHICH files are failing.
I know in general promises must have a .catch or be surrounded by a try/catch block. But in this case I'm using a write stream. I'm a little bit confused as to where the promise that's being rejected is actually located and how I would intercept it. The stack trace is unhelpful, as it only contains library code:
UnhandledPromiseRejectionWarning: Error: Upload failed
at Request.requestStream.on.resp (.../node_modules/gcs-resumable-upload/build/src/index.js:163:34)
at emitTwo (events.js:131:20)
at Request.emit (events.js:214:7)
at Request.<anonymous> (.../node_modules/request/request.js:1161:10)
at emitOne (events.js:121:20)
at Request.emit (events.js:211:7)
at IncomingMessage.<anonymous> (.../node_modules/request/request.js:1083:12)
at Object.onceWrapper (events.js:313:30)
at emitNone (events.js:111:20)
at IncomingMessage.emit (events.js:208:7)
My Code
The code that's creating the writeStream looks like this:
const {join} = require('path')
const {Storage} = require('#google-cloud/storage')
module.exports = (config) => {
const storage = new Storage({
projectId: config.gcloud.project,
keyFilename: config.gcloud.auth_file
})
return {
getBucketWS(path, contentType) {
const {bucket, path_prefix} = config.gcloud
// add path_prefix if we have one
if (path_prefix) {
path = join(path_prefix, path)
}
let setup = storage.bucket(bucket).file(path)
let opts = {}
if (contentType) {
opts = {
contentType,
metadata: {contentType}
}
}
const stream = setup.createWriteStream(opts)
stream._bucket = bucket
stream._path = path
return stream
}
}
}
And the consuming code looks like this:
const gcs = require('./gcs-helper.js')
module.exports = ({writePath, contentType, item}, done) => {
let ws = gcs.getBucketWS(writePath, contentType)
ws.on('error', (err) => {
err.message = `Could not open gs://${ws._bucket}/${ws._path}: ${err.message}`
done(err)
})
ws.on('finish', () => {
done(null, {
path: writePath,
item
})
})
ws.write(item)
ws.end()
}
Given that I'm already listening for the error event on the stream, I don't see what else I can do here. There isn't a promise happening at the level of #google-cloud/storage that I'm consuming.
Digging into the #google-cloud/storage Library
The first line of the stack trace brings us to a code block in the gcs-resumable-upload node module that looks like this:
requestStream.on('complete', resp => {
if (resp.statusCode < 200 || resp.statusCode > 299) {
this.destroy(new Error('Upload failed'));
return;
}
this.emit('metadata', resp.body);
this.deleteConfig();
this.uncork();
});
This is passing the error to the destroy method on the stream. The stream is being created by the #google-cloud/common project's utility module, and this is using the duplexify node module to create the stream. The destroy method is defined on the duplexify stream and can be found in the README documentation.
Reading the duplexify code, I see that it first checks this._ondrain before emitting an error. Maybe I can provide a callback to avoid this error being unhandled?
I tried ws.write(item, null, cb) and still got the same UnhandledPromiseRejectionWarning. I tried ws.end(item, null, cb) and even wrapped the .end call in a try catch, and ended up getting this error which crashed the process entirely:
events.js:183
throw er; // Unhandled 'error' event
^
Error: The uploaded data did not match the data from the server. As a precaution, the file has been deleted. To be sure the content is the same, you should try uploading the file again.
at delete (.../node_modules/#google-cloud/storage/build/src/file.js:1295:35)
at Util.handleResp (.../node_modules/#google-cloud/common/build/src/util.js:123:9)
at retryRequest (.../node_modules/#google-cloud/common/build/src/util.js:404:22)
at onResponse (.../node_modules/retry-request/index.js:200:7)
at .../node_modules/teeny-request/build/src/index.js:208:17
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:189:7)
My final code looks something like this:
let ws = gcs.getBucketWS(writePath, contentType)
const handleErr = (err) => {
if (err) err.message = `Could not open gs://${ws._bucket}/${ws._path}: ${err.message}`
done(err)
}
ws.on('error', handleErr)
// trying to do everything we can to handle these errors
// for some reason we still get UnhandledPromiseRejectionWarning
try {
ws.write(item, null, err => {
handleErr(err)
})
ws.end()
} catch (e) {
handleErr(e)
}
Conclusion
It's still a mystery to me how a user of the #google-cloud/storage library, or duplexify for that matter, is supposed to perform proper error handling. Comments from library maintainers of either project would be appreciated. Thanks!
I have some timeout problems when calling multiple times an HTTP[S] endpoint from node.js inside an Azure App Service.
Here my code to demostrate the problem.
const fetch = require('node-fetch');
const https = require("https");
const agent = new https.Agent();
function doWork() {
const works = [];
for (let i = 0; i < 50; i++) {
const wk = fetch('https://www.microsoft.com/robots.txt', { agent })
.then(res => res.text())
.then(body => console.log("OK", i))
.catch((err) => console.log("ERROR", i, err));
works.push(wk);
}
return Promise.all(works);
}
doWork()
.catch((err) => {
console.log(err);
});
When running this app 3 or 4 times inside a Standard Medium App Service (I'm running it using Kudu but I discover this error inside a standard web app) I get the following error for every requests:
{ FetchError: request to https://www.microsoft.com/robots.txt failed, reason: connect ETIMEDOUT 23.206.106.109:443
at ClientRequest.<anonymous> (D:\home\site\test\test-forge-calls\node_modules\node-fetch\lib\index.js:1393:11)
at emitOne (events.js:96:13)
at ClientRequest.emit (events.js:188:7)
at TLSSocket.socketErrorListener (_http_client.js:310:9)
at emitOne (events.js:96:13)
at TLSSocket.emit (events.js:188:7)
at emitErrorNT (net.js:1276:8)
at _combinedTickCallback (internal/process/next_tick.js:74:11)
at process._tickCallback (internal/process/next_tick.js:98:9)
message: 'request to https://www.microsoft.com/robots.txt failed, reason: connect ETIMEDOUT 23.206.106.109:443',
type: 'system',
errno: 'ETIMEDOUT',
code: 'ETIMEDOUT' }
After some minutes (5/6) without performing requests the above code works again.
I have tried with both node-fetch (https://www.npmjs.com/package/node-fetch) and request (https://www.npmjs.com/package/request). Same results.
The same problem occurs if I not specify an agent and is not related to the destination endpoint, I have tried with many different endpoints (private or public).
According to Microsoft Best Practices node.js applications should use a keep alive agent with the following configuration:
var keepaliveAgent = new Agent({
maxSockets: 40,
maxFreeSockets: 10,
timeout: 60000,
keepAliveTimeout: 300000
});
In fact when creating the agent with:
const agent = new https.Agent({ maxSockets: 100 });
everything works as expected.
Is this behavior expected? What is the best practice for node.js? It is fine to always specify an agent with maxSockets also outside Azure?
UPDATE:
The other strange behavior is that if I run the above code using node index 3 or 4 times I expect that connections are closed when node process exit, but seems that the connections remain open for some minutes. This can be the effect of the TIME_WAIT state?
Please tell me the whether I have called the methods in is correct format or not
var QB=require('quickblox');
var QuickBlox = require('quickblox').QuickBlox;
var QBChat = require('quickblox');
var QB2 = new QuickBlox();
var params, chatUser, chatService;
params = {email: 'raji#gmail.com', password: ''};
QB.init(21000,'NBRQfWA2OWwfBre','GNwMJzqPL8wZb8g');
// QuickBlox session creation
QB.createSession(params, function(err, result) {
if (err) {
console.log(err.detail);
} else {
console.log(result.user_id);
console.log(params.password)
chatUser = {
id: result.user_id,
pass: params.password
};
console.log(chatUser)
connectChat(chatUser);
}
});
function connectChat(chatUser) {
chatService = new QB.chat({
onConnectFailed: onConnectFailed,
onConnectSuccess: onConnectSuccess,
onConnectClosed: onConnectClosed,
onChatMessage: onChatMessage
});
// connect to QB chat service
console.log("connection")
//connection = new Strophe.Connection(CHAT.bosh_server);
chatService.connect(chatUser);
}
function onConnectFailed() {}
// Connection is success
function onConnectSuccess() {}
// Connection is closed
function onConnectClosed() {}
var recipientID = 978816;
var message = {
body: 'Hello world',
type: 'chat'
};
// send user message
//chatService.sendMessage(recipientID, 'hello');
// getting messages
function onChatMessage(senderID, message) {}
I'm getting the following error
TypeError: object is not a function
at connectChat (C:\Program Files\nodejs\chat1.js:33:16)
at C:\Program Files\nodejs\chat1.js:28:3
at C:\Program Files\nodejs\node_modules\quickblox\js\modules\qbAuth.js:52:27
at Request.requestCallback [as _callback] (C:\Program Files\nodejs\node_modu
les\quickblox\js\qbProxy.js:126:16)
at Request.self.callback (C:\Program Files\nodejs\node_modules\request\reque
st.js:373:22)
at Request.EventEmitter.emit (events.js:98:17)
at Request.<anonymous> (C:\Program Files\nodejs\node_modules\request\request
.js:1318:14)
at Request.EventEmitter.emit (events.js:117:20)
at IncomingMessage.<anonymous> (C:\Program Files\nodejs\node_modules\request
\request.js:1266:12)
at IncomingMessage.EventEmitter.emit (events.js:117:20)
How to connect quickblox chat server from node.js. I'm trying to connect using chat plugin but can't even create chat instance. It shows the error