Telegram Bot Chain of Multiple Messages Sending NodeJS - Crashing - javascript

I am trying to send multiple messages to telegram bot through telegraf API in node backend. There are various conditions which when true, should send an telegram alert. The example of condition is:
if(condition is true) {
message = "Hey there";
sendToTGBot(message)
}
There are approximately 100 conditions which get checked within few seconds when the data is fetched as a stream from the API. So, the telegram bot limit gets hit when trying to process more than 30 messages in a second and the server crashes.
The code used to send messages from the bot is:
export const sendToTGBot = (alert) => {
bot.telegram.sendMessage(chat_id, alert);
}
I have also tried putting a delay using setTimeout() in the above code as:
export const sendToTGBot = (alert) => {
setTimeout(() => {
bot.telegram.sendMessage(chat_id, alert);
}, 500);
}
This code above puts the delay (I think so), but due to approximately 30-50 conditions getting true at once in a second. The function gets called that many times and after approx. 10-15 seconds all the alerts get sent simultaneously, again hitting the telegram bot limit.
Also, tried it like this:
export const sendToTGBot =
setTimeout((alert) => {
bot.telegram.sendMessage(chat_id, alert);
}, 500);
This results in a straight error.
Kindly help me with this problem. I have to send multiple alerts within a duration. The number of alerts simultaneously can increase. So, recommend me the best possible solution using JavaScript. Thanks.

You can do this by adding the messages in a queue/array, then utilize async await to loop it
let queue = [];
if(condition == true) {
message = "Hey there";
queue.push(message);
}
setInterval(async ()=>{
if(queue.length>0) {
await bot.telegram.sendMessage(chat_id, queue[0]);
queue.shift();
}
}, 1000)

Related

How to add a delay between function excutions

I am creating this little chatbox thing with socket.io, and I am wondering how I can add a delay between message sending to prevent spam and such.
Here is my code for sending a message.
// Sends a chat message
const sendMessage = () => {
let message = $inputMessage.val();
// Prevent markup from being injected into the message
message = cleanInput(message);
// if there is a non-empty message and a socket connection
if (message && connected) {
$inputMessage.val("");
addChatMessage({ username, message });
// tell server to execute 'new message' and send along one parameter
socket.emit("new message", message);
}
};
You could add a variable that tracks the time of the most recently sent message and a comparison of this variable and the current time; if the difference is too small, refuse to send it.
Minimal example:
window.lastMessageTime = 0;
function sendMessage(message) {
if(Date.now() - window.lastMessageTime < 5000)
return false;
window.lastMessageTime = Date.now();
// Proceed
};
Try using setTimeout to execute your main function. Javascript is not supporting any delays as it is single threaded.

how can I abort previous axios call or how to do autocomplete perfectly

I am trying to build an application where user can search for various items. now my question is how can I send efficiently user search request to the server ?
example , user will type something (say 15 characters). For every 1 character he is sending one request to the server (using axios). now he is typing so fast, within 3 seconds he has sent 15 request to the server.. due to 15 parallel requests, my app is showing the actual result lately (or sometime failing). please help me how can i solve it.. I am using react native with axios.
example:
async searchLocation(text) {
if (text.length > 3) {
this.setState({searching: true, searchResultWindow: true});
var getSearchResult = await searchPlace(text); /// Here I am making axios call to our server
this.setState({searching: false, searchResults: getSearchResult.message});
}
}
You can use setTimeout to handle that case
let timer //the global variable to track the timer
async searchLocation(text) {
if (text.length > 3) {
//remove the API trigger within 3 seconds
clearTimeout(timer)
//assign new timer for the API call
timer = setTimeout(() => {
this.setState({searching: true, searchResultWindow: true});
var getSearchResult = await searchPlace(text); /// Here I am making axios call to our server
this.setState({searching: false, searchResults: getSearchResult.message});
}, 3000) //the API only triggers once users stop typing after 3 seconds
}
}
If you want to have deeper understanding, you can read this article
https://www.freecodecamp.org/news/javascript-debounce-example/
I think you can divide the issue into two steps
At the first you must find a word that the user wants to type for search (not result of search)
in this step, you can just use a dictionary
The second step is finding user search results after pressing Enter key or Click to the search button

Manage a long-running operation node.js

I am creating a telegram bot, which allows you to get some information about the destiny 2 game world, using the Bungie API. The bot is based on the Bot Framework and uses Telegram as a channel (as a language I am using JavaScript).
now I find myself in the situation where when I send a request to the bot it sends uses series of HTTP calls to the EndPoints of the API to collect information, format it and resubmit it via Adaptive cards, this process however in many cases takes more than 15 seconds showing in chat the message "POST to DestinyVendorBot timed out after 15s" (even if this message is shown the bot works perfectly).
Searching online I noticed that there doesn't seem to be a way to hide this message or increase the time before it shows up. So the only thing left for me to do is to make sure it doesn't show up. To do this I tried to refer to this documentation article. But the code shown is in C #, could someone give me an idea on how to solve this problem of mine or maybe some sample code?
I leave here an example of a call that takes too long and generates the message:
//Mostra l'invetraio dell'armaiolo
if (LuisRecognizer.topIntent(luisResult) === 'GetGunsmith') {
//Take more 15 seconds
const mod = await this.br.getGunsmith(accessdata, process.env.MemberShipType, process.env.Character);
if (mod.error == 0) {
var card = {
}
await step.context.sendActivity({
text: 'Ecco le mod vendute oggi da Banshee-44:',
attachments: [CardFactory.adaptiveCard(card)]
});
} else {
await step.context.sendActivity("Codice di accesso scaduto.");
await this.loginStep(step);
}
}
I have done something similar where you call another function and send the message once the function is complete via proactive message. In my case, I set up the function directly inside the bot instead of as a separate Azure Function. First, you need to save the conversation reference somewhere. I store this in conversation state, and resave it every turn (you could probably do this in onMembersAdded but I chose onMessage when I did it so it resaves the conversation reference every turn). You'll need to import const { TurnContext } = require('botbuilder') for this.
// In your onMessage handler
const conversationData = await this.dialogState.get(context, {});
conversationData.conversationReference = TurnContext.getConversationReference(context.activity);
await this.conversationState.saveChanges(context);
You'll need this for the proactive message. When it's time to send the API, you'll need to send a message (well technically that's optional but recommended), get the conversation data if you haven't gotten it already, and call the API function without awaiting it. If your API is always coming back around 15 seconds, you may just want a standard message (e.g. "One moment while I look that up for you"), but if it's going to be longer I would recommend setting the expectation with the user (e.g. "I will look that up for you. It may take up to a minute to get an answer. In the meantime you can continue to ask me questions."). You should be saving user/conversation state further down in your turn handler. Since you are not awaiting the call, the turn will end and the bot will not hang up or send the timeout message. Here is what I did with a simulation I created.
await dc.context.sendActivity(`OK, I'll simulate a long-running API call and send a proactive message when it's done.`);
const conversationData = await this.dialogState.get(context, {});
apiSimulation.longRunningRequest(conversationData.conversationReference);
// That is in a switch statement. At the end of my turn handler I save state
await this.conversationState.saveChanges(context);
await this.userState.saveChanges(context);
And then the function that I called. As this was just a simulation, I have just awaited a promise, but obviously you would call and await your API(s). Once that comes back you will create a new BotFrameworkAdapter to send the proactive message back to the user.
const request = require('request-promise-native');
const { BotFrameworkAdapter } = require('botbuilder');
class apiSimulation {
static async longRunningRequest(conversationReference) {
console.log('Starting simulated API');
await new Promise(resolve => setTimeout(resolve, 30000));
console.log('Simulated API complete');
// Set up the adapter and send the message
try {
const adapter = new BotFrameworkAdapter({
appId: process.env.microsoftAppID,
appPassword: process.env.microsoftAppPassword,
channelService: process.env.ChannelService,
openIdMetadata: process.env.BotOpenIdMetadata
});
await adapter.continueConversation(conversationReference, async turnContext => {
await turnContext.sendActivity('This message was sent after a simulated long-running API');
});
} catch (error) {
//console.log('Bad Request. Please ensure your message contains the conversation reference and message text.');
console.log(error);
}
}
}
module.exports.apiSimulation = apiSimulation;

Discord bot: send message and wait to send another one

I want my bot to send a message then wait like 1-2 seconds before sending another one. I'm using discord.js. I've attempted looking around on the internet and I couldn't find anything for my situation.
You can set a piece of code to run after a given duration using setTimeout.
channel.send("First message...")
.then(() => { // Wait until the first message is sent
setTimeout(() => {
channel.send("Second message...");
}, 2000) // Wait 2000 milliseconds (2 seconds) before sending again.
})

Referencing a DM channel

I'm trying to create a command that allows users to create their password following prompts through DM. I'm able to send a message to the user, but not able to read a message sent back with a MessageCollector because I cannot find a way to reference the DM channel.
I have tried using another instance of bot.on("message", message) here, but that creates a leak in the system causing the second instance to never disappear.
I also can't let users use a command say !CreatePassword *** because this function is linked with many others in a strict order.
Maybe I'm doing something fundamentally wrong, or approaching the problem in a bad way, but I need a way to reference a DM channel.
This is the best iteration of my code so far.
function createAccount(receivedMessage, embedMessage)
{
const chan = new Discord.DMChannel(bot, receivedMessage.author);
const msgCollector = new Discord.MessageCollector(chan , m => m.author.id == receivedMessage.author.id);
msgCollector.on("collect", (message) =>
{
// Other Code
msgCollector.stop();
// Removing an embed message on the server, which doesn't have a problem.
embedMessage.delete();
})
}
I can show the rest of my code if necessary.
Thank you for your time. I've lost an entire night of sleep over this.
I would do it like this (I'll assume that receivedMessage is the message that triggered the command, correct me if I'm wrong)
async function createAccount(receivedMessage, embedMessage) {
// first send a message to the user, so that you're sure that the DM channel exists.
let firstMsg = await receivedMessage.author.send("Type your password here");
let filter = () => true; // you don't need it, since it's a DM.
let collected = await firstMsg.channel.awaitMessages(filter, {
maxMatches: 1, // you only need one message
time: 60000 // the time you want it to run for
}).catch(console.log);
if (collected && collected.size > 0) {
let password = collected.first().content.split(' ')[0]; // grab the password
collected.forEach(msg => msg.delete()); // delete every collected message (and so the password)
await firstMsg.edit("Password saved!"); // edit the first message you sent
} else await firstMsg.edit("Command timed out :("); // no message has been received
firstMsg.delete(30000); // delete it after 30 seconds
}

Categories