How to stop a event (mineflayer) - javascript

(new to js)
I am making a discord to minecraft bot which uses discord.js on one file and mineflayer for the minecraft bot. When a command is run in discord, it makes the bot run it and runs a function which detects the right line in chat, problem is even after it detects the message, it constantly stays on and reads every line, forever. Meaning if someone else types what its detecting it interferes and thinks thats the right chat line..
Im not sure how to make it stop either, after running the event once, or after finding the line in chat, I would probably need it to stop after running it once incase the line doesnt show up for some reason.
function balOutput() {
bot.on('message', jsonMsg => {
if (Array.isArray(jsonMsg.json.extra)) {
var message = '';
jsonMsg.json.extra.forEach(function(element) {
message = message + element.text;
});
if (message.toLowerCase().includes('\'s balance')) {
var msg = message;
fs.writeFile('output.txt', msg, err => {
if (err) throw err;
});
} else if (message.toLowerCase().includes('is not locally online')) {
var msg = 'That player is not online!';
fs.writeFile('output.txt', msg, err => {
if (err) throw err;
});
}
}
continue;
});
}
That function is called once the discord command goes through and it runs it in minecraft. It does work for the one time but then other messages ruin it. The problem is I cant just use regex and make it detect that message of a player saying it because, some other commands need to run with the exact same output but be placed in different spots, so I need it not to trigger the bot.on('message') in the baloutput function... Pretty much I need to close that event after running it once.

I’m not entirely sure what jsonMsg represents, as Discord.js’s message event passes a Message object as its first argument, and that doesn’t have a .json property, while you are expecting jsonMsg.json.extra to be an array, and that seems to be the case sometimes.
However, whenever you call your balOutput function, it seems you are adding yet another listener callback function to bot’s message event. That could be causing the behaviour you’re faced with, where things only work the first time. You’ll want to add just one callback.
You may also want to look into using a normal for loop, rather than a .forEach(), from which you can break out of with break; the moment your if condition is first met.

Related

Handling service bus error messages in azure function using javascript

I have an azure function using service bus topic trigger and I want to handle error messages gracefully, I want to be able to do an abandon of the message and pass the exception to it so I can see it in a property when I read the dead letters queue.
this is my code:
const serviceBusTopicTrigger: AzureFunction = async function(context: Context, mySbMsg: any): Promise<void> {
// do something messy that may fail.
};
When my function fails with an exception the message goes to the DLQ as expected, but the problem is, that it doesn't save the exception thrown, it only tells you that it tried to execute the method 10 times and it couldn't.
What I want its to be able to catch the exception and add it to the message properties so, when I process the DLQ queue I will be able to know the reason for the error. Even more, as the code is failing with an exception I would like it to abandon from the first time it runs the message, so it doesn't have to retry 10 times.
I'm thinking something like this:
const serviceBusTopicTrigger: AzureFunction = async function(context: Context, mySbMsg: any): Promise<void> {
try{
// do something messy and that may fail
}
catch(error){
context.bindingData.userProperties['DeadLetterReason'] = 'Internal server error';
context.bindingData.userProperties['DeadLetterErrorDescription'] = JSON.stringify(error);
context.bindingData.abandonMsg();
}
};
I haven't been able to find any documentation about something like this, so is it possible? or can I force the message to the DLQ? with something like this:
const serviceBusTopicTrigger: AzureFunction = async function(context: Context, mySbMsg: any): Promise<void> {
try{
// do something messy and that may fail
}
catch(error){
context.bindings.deadLetterQueue.userProperties['DeadLetterReason'] = 'Internal server error';
context.bindings.deadLetterQueue.userProperties['DeadLetterErrorDescription'] = JSON.stringify(error);
context.bindings.deadLetterQueue = mySbMsg;
}
};
Or finally and sadly do I have to manage my error directly from the method and maybe send it directly from there to an azure storage table or queue to notify errors?, I wouldn't like that because then I would be handling, both errors from the dead letter queue, and from my functions in different places. Is this the only way?
Any more ideas?
Thanks.
First of all, I think nodejs can not do this. nodejs lost the type information in C#, it should have been sent like this:
https://learn.microsoft.com/en-us/dotnet/api/microsoft.servicebus.messaging.brokeredmessage.deadletterasync?view=azure-dotnet
When my function fails with an exception the message goes to the DLQ
as expected, but the problem is, that it doesn't save the exception
thrown, it only tells you that it tried to execute the method 10 times
and it couldn't.
Max Delivery Count is set to 10 by default when you create the subscription of the service bus topic. The default value is 10, and the minimum can be set to 1.
When you turn on the service bus information automatic completion, just like below:
host.json
{
"version": "2.0",
"extensions": {
"serviceBus": {
"messageHandlerOptions": {
"autoComplete": true
}
}
},
"logging": {
"applicationInsights": {
"samplingSettings": {
"isEnabled": true,
"excludedTypes": "Request"
}
}
},
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle",
"version": "[1.*, 3.1.0)"
}
}
At that time, when an error occurs, the function run failed, call the abandon method, Delivery Count +1, and send the message back at the same time.
Then once you use try-catch, you will not retry 10 times at all. The function is regarded as a successful operation, function run success and the message disappears without sending it back.(That is, once try-catch you will only execute it once.)
I haven't been able to find any documentation about something like
this, so is it possible? or can I force the message to the DLQ?
No, it can't be done. The related type is missing and cannot be manually sent to dead-letter via JavaScript.
Or finally and sadly do I have to manage my error directly from the
method and maybe send it directly from there to an azure storage table
or queue to notify errors?, I wouldn't like that because then I would
be handling, both errors from the dead letter queue, and from my
functions in different places. Is this the only way?
Why do you say that the error still appears in the dead-letter? Using try-catch in the case of automatic completion should not be sent to dead-letter.

How to send a message from inside a function (Discord.js)

So I'm working on a bot for a game me and my friends made and wanted to include a secret command. When it is triggered, it will count down (while sending some messages at certain intervals) then do a certain action.
In order to get the timing correct, I've used the setTimeout feature, but now I'm running into a problem: For some reason, I can't send a message from inside a function. Is there a way to get around this restriction? Alternatively, is there another way to complete the command after a certain delay?
if(command === 'purge') {
console.log('Starting first message/countdown')
message.channel.send('WARNING: PURGE PRECEPT INITATED. THIS SERVER WILL BE DELETED IN: 60 SECONDS.')
setTimeout(purge1, 15000);
}
function purge2() {
console.log('Starting third message/countdown')
message.channel.send('WARNING: PURGE PRECEPT INITIATED. THIS SERVER WILL BE DELETED IN: 30 SECONDS.');
setTimeout(purge3, 15000)
}
Not quite sure how to format code, sorry. Yes I do have the other stuff (Readying client, login, etc.) but that is the main part relevant to the question.
The reason it doesn't work is message is not defined within the function so what you should do is:
Add a message parameter in the function and then pass it when you call the function, for example:
client.on('message', async message => {
SendHello(message);
}
function SendHello(message){
message.channel.send('Hello');
}

Mineflayer: make bot only reply once

Im new to js, and mineflayer so I was wondering how I can make the bot.on('chat') run once, for say the first message in chat. This example the bot just replys to every single message sent, saying the same message back.
I dont know how I would fix this, I have tried putting it in a function so its only called then, but it seems to just like open a listener and keep saying messages even after the function is done running.
var bot = mineflayer.createBot({
host: "localhost",
port: 25565,
username: "",
password: "",
version: "1.8.9"
});
bot.on('chat', function(username, message) {
if (username === bot.username) return;
bot.chat(message);
});
Any method of making it only reply to the message when called on, or only running that event once could help. Thanks!
N.B. It is 2019; please use let/const over var in modern NodeJS environments and => instead of unnamed functions when a bound "this" isn't used. i.e., const bot = mineflayer...
The on handler runs every time the 'chat' event is fired. Using bot.once would not allow you to keep checking whether you've actually responded or not. You are new to js so I would not dive into the API for removing event listeners.
Given these constraints, let's keep it simple. You could have an external boolean flag that also allows you to easily toggle on/off as you wish at a later time during runtime by having something before if (responded) return; in the following code.
Your code could look something like this:
let responded = false;
bot.on('chat', (username, message) => {
if (responded) return;
if (username === bot.username) return;
bot.chat(`Received ${message}`);
responded = true;
return;
});
instead of using any stupid functions or statements just do bot.once('chat', (message)=>{bot.chat(message)}) or use if statements like bot.on('chat', (username,message)=>{if(message === 'hi'){bot.chat('hi')}})

Trying to catch errors that aren't related to the code but are instead things like the internet not working, how would I go about doing this?

I am creating some code that downloads a file from a website and unzips it automatically. I have made it so that it will check that the latest version of the file has been downloaded every 4 hours, and if it already has been downloaded, then it won't download it again. Now I want to make it so that if the download fails for some reason, rather than waiting another 4 hours to download the file again, it will attempt to download it again instantly, and will keep trying to do that until it downloads successfully.
I have used the "try" and "catch" statements to make it so that if it will try and download the file, and if the file download fails, it will catch the error, and then in the catch statement it will do the same thing but do it every 5 seconds instead of every 4 hours.
try{
interval()
//this is referencing a function that downloads the file every 4 hours
setTimeout(stopInterval, 5000);
//this is to prevent the function from stacking every time the try statement is called
}
}
catch(e){
checkInterval()
//this is referencing a function that does the same as what's in the try statement but does it every 5 seconds instead
}
}
what it successfully does is that if it detects an error within the code itself (e.g. the function it's trying to call is misspelt), it will do what I am asking it to do which is repeat the function every 5 seconds. But what I want it to do is exactly that, except that it will apply to other errors like the internet disconnecting. I have tried using the "Throw New Error" construct but don't really know how to use it properly, or how to specifically apply it to errors related to internet or other problems. If there is a way to do this with the try catch statement, that would be awesome, but if I have to do something else then any help would be appreciated. I am new to this site so sorry if this isn't explained very well, if there is anything you need me to clarify feel free to let me know. Thanks in advance!
I found the solution. For everyone else who wants to know what the solution is, this is what I did.
const dns = require('dns');
const navigator = global.navigator = {onLine: true};
((navigator) => {
const dns = require('dns');
setInterval(() => {
dns.resolve('www.google.com',
error => navigator.onLine = !error);
}, 5000);
})(navigator);
What this does is set "navigator.onLine" manually since it doesn't work by default in NodeJS. And then I cause it to create an error by doing:
var isOnline = navigator.onLine;
if (!isOnline) {
throw new Error('Connection Unsuccessful');
}
By creating the error, it allows the catch statement to detect it as an error and will perform the code that you want it to perform once the internet disconnects.

Firebase transactions coming back multiple times much later?

This is a fairly weird thing, and it's hard to reproduce. Not the best state of a bug report, I apologize.
I'm using .transaction() to write a value to a location in Firebase. Here's some pseudo-code:
var ref = firebase.child('/path/to/location');
var storeSafely = function(val) {
ref.transaction(
function updateFunc(currentData) {
console.log('Attempting update: ' + JSON.stringify(val));
if (currentData) return;
return val;
},
function onTransactionCompleteFunc(err, isCommitted, snap) {
if (err) {
console.log('Error in onTransactionCompleteFunc: ' + JSON.stringify(err));
return;
}
if (! isCommitted) {
console.log('Not committed');
return;
}
ref.onDisconnect().remove();
doSomeStuff();
});
};
var doSomeStuff = function() {
// Things get done, time passes.
console.log('Cleaning up');
ref.onDisconnect().cancel();
ref.set(
null,
function onSetCompleteFunc(err) {
if (err) {
console.log('Error in onSetCompleteFunc: ' + JSON.stringify(err));
}
});
};
storeSafely(1);
// later...
storeSafely(2);
// even later...
storeSafely(3);
I'm effectively using Firebase transactions as a sort of mutex lock:
Store a value at a location via transaction.
Set the onDisconnect for the location to remove the value in case my app dies while working.
Do some stuff.
Remove the onDisconnect for the location, because I'm done with the stuff.
Remove the value at the location.
I do this every few minutes, and it all works great. Things get written and removed perfectly, and the logs show me creating the lock, doing stuff, and then releasing the lock.
The weird part is what happens hours later. Occasionally Firebase has maintenance, and my app gets a bunch of permission denied errors. At the same time this happens, I suddenly start getting a bunch of this output in the logs:
Attempting update 1
Attempting update 2
Attempting update 3
...in other words, it looks like the transactions never fully completed, and they're trying to retry now that the location can't be read any more. It's almost like there's a closure in the transaction() code that never completed, and it's getting re-executed now for some reason.
Am I missing something really important here about how to end a transaction?
(Note: I originally posted this to the Firebase Google Group, but was eventually reminded that code questions are supposed to go to Stack Overflow. I apologize for the cross-posting.)
Just a guess, but I wonder if your updateFunc() function is being called with null when your app gets the permission-denied errors from Firebase. (If so, I could believe that's part of their "Offline Writes" support.)
In any case, you should handle null as a possible state. Saving Transactional Data says:
transaction() will be called multiple times and must be able to handle
null data. Even if there is existing data in your database it may not
be locally cached when the transaction function is run.
I don't know the intricacies of Firebase's transaction mechansim, but I would try changing your .set(null) to set the value to 0 instead, change your .remove() to also set the value with .set(0), and change your line in updateFunc() to:
if (currentData === null || currentData) return;
Unfortunately, that assumes that '/path/to/location' is initially set to 0 at some point. If that's a problem, maybe you can muck around with null versus undefined. For example, it would be nice if Firebase used one of those for non-existent data and another when it's offline.

Categories