Mineflayer: make bot only reply once - javascript

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')}})

Related

Discord JS - interactionCreate and messageCreate

I've been trying to use interactionCreate event, but somehow it's not working. I'm not sure why, and I didn't find exact documentation about this event, only that it's used for executing slash commands. However for this purpose I use messageCreate event, and it's working fine.
const Event = require('../handlers/Event.js');
module.exports = new Event('messageCreate', (client, message) => {
if (!message.content.startsWith(client.prefix) || message.author.bot) return;
const args = message.content.substring(client.prefix.length).split(/ +/);
try {
const command = client.commands.find(cmd => cmd.name == args[0] || cmd.alias == args[0]);
command.run(message, args, client);
} catch (error) {
message.channel.send('Wrong command.');
}
});
What's wrong with my interactionCreate event?
const Event = require('../handlers/Event.js');
module.exports = new Event('interactionCreate', async (interaction) => {
if (!interaction.isCommand()) return;
if (interaction.commandName === 'join') {
await interaction.reply('join');
}
});
"An Interaction is the message that your application receives when a user uses an application command or a message component." Discord Interactions
The messageCreate listener will trigger for everything else pretty much.
In your case, what are you trying to do that will trigger the interaction? built in slash command perhaps?
Application commands are commands that an application can register to Discord, the built in slash commands I believe don't match this description. So for you to see an interaction firing you will either have to register an applicationCommand yourself (ApplicationCommandBuilder) or perhaps create an embed message with a button (DiscordButtons) and click the button which should trigger an interaction
I solved this problem just now. I have the same problem but maybe for a different reason. It's nothing about the code in my situation.
Before I use discord.js, I use the original API here.
And I set the INTERACTIONS ENDPOINT URL as the tutorial described
enter image description here
Then I started to use discord.js. But interactionCreate cannot work.
I re-set INTERACTIONS ENDPOINT URL to be nothing. Everything is OK now.
Maybe you should wrap the entire code into one event .
I believe that your event code was created twice , is not good for listening multiple event at the same time, to do that you should have to considering to declare one new event but iterate trough the event file.
This what i use on my project last month in my index.js.
Note that i wasnt use slash command at the moment but i use that for the button and any other component interaction, but slash command can be use that too.
const eventFiles = fs.readdirSync('./event').filter(file => file.endsWith('.js'));
for (const file of eventFiles) {
const event = require(`./event/${file}`);
client.on(event.name, (...args) => event.execute(...args, any parameter here));
}
That could be the cleanest way to listen event at the same time.
I hope this help 😃

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');
}

How to stop a event (mineflayer)

(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.

Reconnecting serveral peerConnections after page reload

I'm creating a web application for monitoring by smartphones using WebRTC, and for the signalling server I'm using socket.io.
When I'm sending a stream, I create an RTCPeerConnection object on the "watch" page that receives this stream. Streams I send on separate pages. The user can attach up to four streams from a smartphone, so on the "watch" page there are up to four RTCPeerConnection objects.
Streams are received automatically as soon as the offer from the "transmit" page appears, then the RTCPeerConnection object is created on the "watch" page and connected to the standard WebRTC schema.
"transmit" page:
function onCreateOfferSuccess(sdp){
//console.log(sdp);
pc.setLocalDescription(new RTCSessionDescription(sdp));
console.log('ask');
socket.emit('ask', {"sdp":JSON.stringify(pc.localDescription),
"user": loggedUserID,
"fromSocket": ownSocket});
}
"watch" page:
socket.on('ask', function(offer){
if (offer.user === loggedUserID){
TempDescriptions = JSON.parse(offer.sdp);
console.log(TempDescriptions)
currTransmiterSocket = offer.fromSocket;
console.log(currTransmiterSocket);
getStream();
}
function getStream(){
try {
setTimeout(function(){
console.log(time, 'getStream()');
connection = getPeerConnection();
connection.setRemoteDescription(
new RTCSessionDescription(TempDescriptions),
function() {
connection.createAnswer(gotDescription, function(error){
console.log(error)
});
}, function(error){
console.log(error)
});
}, getStreamDelay*3000)
getStreamDelay++
}
catch(err){
console.log(err);
}
};
My web application requires functionality in which when we exit from the "watch" page and return to it again, all previously included streams must be displayed.
To implement this functionality, I use the oniceconnectionstatechange method. If the stream is disconnected, the iceRestart function is executed which creates the offer with the option {iceRestart: true}
"transmit" page:
var options_with_restart = {offerToReceiveAudio: false,
offerToReceiveVideo: true,
iceRestart: true};
function iceRestart(event){
try{
setTimeout(function(){
pc.createOffer(options_with_restart).then(onCreateOfferSuccess, onCreateOfferError);
},1000);
} catch(error) {
console.log(error);
The problem is that when I restart the "watch" page, all pages "transmit" send to ask at once. Only one object is connected, although four RTCPeerConnection objects are created at once (let's assume that the user sends four streams).
I have been struggling with this problem for several days. I tried to set an increasing time delay on subsequent calls to the getStream() function as seen in the above code, I tried to check the signallingState connections before executing the getStream() function, I tried several other methods but none of them worked.
If you need some part of my code to help, please write.
edit:
gotDescription() method in "watch" page.
function gotDescription(sdp) {
try{
connection.setLocalDescription(sdp,
function() {
registerIceCandidate();
socket.emit('response', {"sdp": sdp,
"user": loggedUserID,
"fromSocket": ownSocket,
"toSocket": currTransmiterSocket});
}, function(error){
console.log(error)
});
} catch(err){
console.log(err);
}
}
I add console.log with RTCPeerConnection object
console output:
https://i.stack.imgur.com/dQXkE.png1
log shows that the signalingState of connection is "stable" but when I develop the object, signalingState is equal to "have-remote-offer"
like here
Remove the TempDescriptions global variable, and pass the sdp to getStream(offer.sdp) directly.
Otherwise, you've socket.on('ask', function(offer){ called 4 times, overwriting TempDescriptions. Then 3+ seconds later your 4 setTimeouts roll around, all accessing the final value of TempDescriptions only.
That's probably why only one RTCPeerConnection re-connects.
In general, using time delay to separate connections seems like a bad idea, as it slows down re-connection. Instead, why not send an id? E.g.
socket.emit('ask', {id: connectionNumber++,
sdp: JSON.stringify(pc.localDescription),
user: loggedUserID,
fromSocket: ownSocket});
Update: Stop adding global variables to window
Whenever you assign to an undeclared variable like this:
connection = getPeerConnection();
...it creates a global on window, e.g. window.connection, and you have the same problem. You have 4 connection, but you're storing them in one variable.
Type "use strict"; at the head of your source file to catch this:
ReferenceError: assignment to undeclared variable connection
Scoping: The general problem
You're dealing with 4 connections here, but you lack an approach for scoping each instance.
Most other languages would tell you to create a class and make object instances, and put everything including connection on this. That's one good approach. In JS you can use closures instead. But at minimum you still need 4 variables holding the 4 connections, or an array of connections. Then you look up—e.g. from the id I mentioned—which connection to deal with.
Also, your try/catches aren't going to catch asynchronous errors. Instead of defining all these callbacks, I strongly recommend using promises, or even async/await when dealing with the highly asynchronous WebRTC API. This makes scoping trivial. E.g.
const connections = [];
socket.on('ask', async ({user, id, sdp, fromSocket}) => {
try {
if (user != loggedUserID) return;
if (!connections[id]) {
connections[id] = getPeerConnection();
registerIceCandidate(connections[id]);
}
const connection = connections[id];
await connection.setRemoteDescription(JSON.parse(sdp));
await connection.setLocalDescription(await connection.createAnswer());
socket.emit('response', {sdp,
user: loggedUserID,
fromSocket: ownSocket,
toSocket: fromSocket});
} catch (err) {
console.log(err);
}
};
This way the error handling is solid.

Categories