How can I track previous responses? - discord.js

I'm working on a discord bot for a D&D game. I want the bot to be able to recognize when someone has recently used a command. This is what I have so far;
const barid = require("./tavernarray.json")
var tavernarray = barid.guests;
message.channel.send('Tavern Keeper Emotes and replies;')
if (message.member.roles.cache.has('770678762944725003')){
if (tavernarray.includes(message.author.id)){
message.channel.send("Weren't you just here?");
} else;
tavernarray.push(message.author.id);
message.channel.send(` Welcome to the Tavern Guild Master ${message.author}`);
setInterval (() => {
tavernarray.pop(message.author.id)
}, 30000)
} else {
message.channel.send("Error no role");
}
From what I can tell the code works in that on the first command, we get the welcome message expected and the user ID is added to the array. On the second command though, there's a short delay and then we get both messages. Should I be using setTimeout instead of setInterval? Or is it an issue with using a .json array? I tried keeping the array in the program, but it just kept resetting every time I ran it.

Yes, you should be using setTimeout(). The reason you might have an issue is because the code is trying to remove the variable from the JSON array every 30 seconds which will cause two issues: higher memory usage and potential errors. The difference between setInterval() and setTimeout() is timeout executes the function once, whilst the other loops continuously until it is told to break. Along with this, the way you're using else is the issue. When you use else; (Take into note the semi colon), you're telling the code that if the ID isn't there, it shouldn't execute any code, as a semi colon indicates the end of a line of code. A snippet is shown below
if (tavernarray.includes(message.author.id)){
message.channel.send("Weren't you just here?");
} else { // Instead of ; we'll use {}
tavernarray.push(message.author.id);
message.channel.send(` Welcome to the Tavern Guild Master ${message.author}`);
setInterval (() => {
tavernarray.pop(message.author.id)
}, 30000)
}

Related

How to implement API call to server and display data on button click (React, Node, Express)

EDIT: I've debugged and provided an answer to some of my issues below.
I've been researching this issue for a while now and can't seem to find an adequate resolution. As you will see looking at my code, I have a lot to learn. The issue seems to come from trying to display data that hasn't finished fetching despite my redundant placement of await. I am also curious if I should place my getData() function within a useEffect hook? The problem with this is I use the getData() function in my submit button's onClick. So when I run my app, getData() is only available in useEffect's scope.
const getData = async () => {
if(searchData.platform !== "" && searchData.platformUserIdentifier !== ""){
setValidInput(true);
const response = await fetch(`http://localhost:8080/data?platform=${searchData.platform}&username=${searchData.platformUserIdentifier}`);
const json = await response.json();
await finishAllOperations(json)
} else {
setValidInput(false);
}
}
function finishAllOperations(json) {
if(json.code === "ERR_BAD_REQUEST"){
console.log(`Request failed with a status code ${json.status}`);
setErrorMessage(true);
setDisplayTable(false);
} else {
console.log("Request successful");
setMatches(json);
setErrorMessage(false);
setDisplayTable(true)
}
}
const displayMatchRows = matches.map((match,index) => {
//dummy data to populate if reward is available
function checkRewardAvailability(match){
const value = Math.random()
if (value <= .5) {
return true
} else {
return false
}
}
return (
<tr key={match.id}>
<td>{index+1}</td>
<td>{parseDate(match.metadata.endDate.value)}</td>
<td>{match.stats.kills.value}</td>
<td>{match.stats.rankScore.value}</td>
<td>{checkRewardAvailability()?<button className="reward-button">Claim Reward</button>:""}</td>
</tr>
)
})
When I go to deploy my code to my Node server and attempt to submit an API call with the above code I receive the following error:
TypeError: b.map is not a function
Furthermore, when I run my program with my client and server running on separate terminals my code does work and the data does properly get displayed. However, the "Request successful" console log occurs before the fetch has finished running. Obviously, I would like for the "Request successful" (see attached screenshot) to occur after I have completely finished fetching all data.
"Request Success" before fetch finish
I really appreciate any input as on I'm the verge of smashing my computer. Thank you in advance.
I am happy to report after several hours of debugging, the error came from my .env file providing an undefined value because it was not getting moved alongside my package.json file.
Because the .env file was in the wrong place, I was not getting returned an array that .map would work on. Once I realized the error I was receiving had something to do with my server's API call, I was getting an error that went
{"code":"ERR_HTTP_INVALID_HEADER_VALUE"}
This seemed very vague to me and took a while to locate. If you're receiving this error and you are using a .env file, check to be sure it's not returning an undefined value. If it is undefined, then ensure that the .env files is in the same directory as your package.json file.

React - use Dependencies in useEffect without trigger the effect itself when the Dependency change

I have a react state
const [list, setList] = useState([])
and a react effect that is triggered when the list is modified, and do some work with the first element in the list:
useEffect( () => {
if(list.length <= 0) return
//Do something with the first element of the list
//Remove first element of the list
}, [list])
In this way, the effect trigger itself n times where n is the number of the element in the list.
Then i have another method in my component that insert elements in the list with the setList() method, let's call it
insertElemInList = () => {
//insert one or more elements in list
}
when insertElemInList is called, the useEffect trigger and start working for n times.
I don't know how many times the insertElemInList() is called, and how many elements is inserted every time, since this method is called after some actions of the user on the page.
So if an user call the insertElemInList() two or more times, before the last iteration of the effect is finished, then the effect trigger in the wrong way, in fact it will activate due to the change of state given by insertElemInList, but also by itself, resulting in more iterations and wrong behaviour.
So i'm trying to figure out how to use something inside the effect that doesn't trigger the effect itself, but can be used correctly.
for example I was thinking of modifying the effect and the state adding
const [semWait, setSem] = useState(1)
and then, continue to update the list state with the insertElemInList() method, but now:
useEffect( () => {
let doSomething = () => {
if(list.length < 0) return
//Do Something with the first element of the list
//Remove first element from the list
if(list.length > 0) doSomething()
}
doSomething()
setSem(1)
}, [semWait])
insertElemInList = () => {
//insert one or more elements in list
if(semWait == 1) setSem(0)
}
the above code is just an example of how I can solve the problem, I don't think it is the best solution and I gave you this example just to make you understand what I would like to do.
however, as you can see in this way I could add as many value as i want to my state whith insertElemInList() ​​and trigger the effect only if it is not already active (in other word, only if the semaphore is reset by the effect itself). However, I know it's not a good thing to use a state in the effect, without including it in dependencies, and if i add the state list as dependency of the useEffect the problem return.
the problem is that I can't figure out how to use a value inside useEffect without including it in the dependency
EDIT:
sorry for the late reply, i tried to implement this code on my own but there are workflow problems in my work, i'll try to explain the problems:
the code is a snippet to download some file from an API, the user on the site have a list of files to download, he can click on the files to download them as many times as he wants. my intent is to create a request queue, so as not to send too many requests to the server.
the code below show my work, i've inserted some comments to let you figure out how my code should work:
const [queue, setQueue] = useState({
"op_name": "NO_OP"
})
//file download function
let requestFileDownload = (fileId) => {
/*
This function construct the object to put in queue state and call the method 'insertInQueue'
*/
let workState = appState
insertInQueue({
"op_name": "DOWNLOAD_FILE",
"file_id": fileId,
"username": workState.user.username,
"token": workState.user.token
})
}
//Function insertInQueue to insert an element in the queue
let insertInQueue = (objQueue) => {
//Some control to check if the object passed exist, and have valid fields
if (!objQueue || !objQueue.op_name || objQueue.op_name === "NO_OP") return //nothing to insert in queue
//calling method to insert in timeline div, this work only whith front-end dom elements (full synchronous)
insertElemInTimeline(objQueue.op_name)
//setting timeout in which try to insert the object passed in queue
setTimeout(function run() {
let workQueue = queue //gettind queue object
if (workQueue && workQueue.op_name === "NO_OP") {
/*
if queue exist and the op_name is "NO_OP", this mean that the previus operations on the queue
is finished, so we can start this operation
*/
setQueue(objQueue) //set the queue with the object passed as paramether to trigger the effect
return;
}
// if the queue op_name was != "NO_OP" call the function again for retry to insert in 1 second
setTimeout(run, 1000)
}, 0)
}
//Effect triggered when queue object change
useEffect(() => {
if (queue.op_name === "NO_OP") return //no operation to do
//Effective file download
let downloadFileEffect = async () => {
let objQueue = queue //getting queue state
//Two functions to download the element by calling backend api
let downloadFileResponse = await downloadFile(objQueue.file_id, objQueue.username, objQueue.token)
download(downloadFileResponse.data, downloadFileResponse.headers['x-suggested-filename'])
//after the method have completed, i can set a new state for the queue with "op_name": "NO_OP"
let appoStateQueue = {
"op_name": "NO_OP"
}
setQueue(appoStateQueue)
//method for remove the element from the dom
removeElemFromTimeline()
}
//calling function to trigger the donwload.
downloadFileEffect()
}, [queue])
now the problem is, that when i try to reset the queue state in the effect, when i call:
let appoStateQueue = {
"op_name": "NO_OP"
}
setQueue(appoStateQueue)
the queue is not resetted in the case the user have clicked two download one after the first is running.
In fact the queue stops with the first object inserted in it, and is not reset by the effect, so the second download never starts, because it sees forever the queue occupied by the first download.
In case user click one download, then wait for the download, and only then click the second, then there's no problem, and the queue is resetted correctly by the effect
First, useEffect doesn't run for "trigger itself n times where n is the number of the element in the list". useEffect will run every time list changes in length or resides in a different memory space than it did in a previous render. This is how "shallow" comparison works with javascript objects in react. Your main issue is that you are changing your dependency from within the effect. This means that while the effect runs, it updates the dependency and forces it to run again and again and again...memory leak!
Your solution might work, but as you stated is not best practice. A better solution (imo) would be to allow for a "parsedList" state that can be the end result of parsing the list. Let the source of truth with the list only be impacted by the client interaction. You monitor these changes and change your parsedList based on these changes.

Having problems making a command that allows people to turn the blacklist on and off

I thought that allowing people to turn my blacklist on and off for their server would be kinda neat, but I didn't find much success so far. Also I was wondering, is it hard to make a command that allows people to add their own words to the blacklist? Here's the code:
let blacklisted = ['bad words here']
let foundInText = false;
for (var i in blacklisted) {
if (message.content.toLowerCase().includes(blacklisted[i].toLowerCase())) foundInText = true;
}
if (foundInText) {
message.delete();
}
});
You could use a variable for turning on or off the blacklist. BacklistOn = true, And use a if statement before the code you copied in here. Then make a command that changes that variable. You would need to store the variable in a JSON file if you want the setting to be saved when restarting the bot or if the bot crashes. Tutorial on reading/writing to JSON with node.js https://medium.com/#osiolabs/read-write-json-files-with-node-js-92d03cc82824

Akka.net - Streams with parallelism, backpressure and ActorRef

Tying to learn how use Akka.net Streams to process items in parallel from a Source.Queue, with the processing done in an Actor.
I've been able to get it to work with calling a function with Sink.ForEachParallel, and it works as expected.
Is it possible to process items in parallel with Sink.ActorRefWithAck (as I would prefer it utilize back-pressure)?
About to press Post, when tried to combine previous attempts and viola!
Previous attempts with ForEachParallel failed when I tried to create the actor within, but couldn't do so in an async function. If I use an single actor previous declared, then the Tell would work, but I couldn't get the parallelism I desired.
I got it to work with a router with roundrobin configuration.
var props = new RoundRobinPool(5).Props(Props.Create<MyActor>());
var actor = Context.ActorOf(props);
flow = Source.Queue<Element>(2000,OverflowStrategy.Backpressure)
.Select(x => {
return new Wrapper() { Element = x, Request = ++cnt };
})
.To(Sink.ForEachParallel<Wrapper>(5, (s) => { actor.Tell(s); }))
.Run(materializer);
The Request ++cnt is for console output to verify the requests are being processed as desired.
MyActor has a long delay on every 10th request to verify the backpressure was working.

rx.js catchup subscription from two sources

I need to combine a catch up and a subscribe to new feed. So first I query the database for all new records I've missed, then switch to a pub sub for all new records that are coming in.
The first part is easy do your query, perhaps in batches of 500, that will give you an array and you can rx.observeFrom that.
The second part is easy you just put an rx.observe on the pubsub.
But I need to do is sequentially so I need to play all the old records before I start playing the new ones coming in.
I figure I can start the subscribe to pubsub, put those in an array, then start processing the old ones, and when I'm done either remove the dups ( or since I do a dup check ) allow the few dups, but play the accumulated records until they are gone and then one in one out.
my question is what is the best way to do this? should I create a subscribe to start building up new records in an array, then start processing old, then in the "then" of the oldrecord process subscribe to the other array?
Ok this is what I have so far. I need to build up the tests and finish up some psudo code to find out if it even works, much less is a good implementation. Feel free to stop me in my tracks before I bury myself.
var catchUpSubscription = function catchUpSubscription(startFrom) {
EventEmitter.call(this);
var subscription = this.getCurrentEventsSubscription();
// calling map to start subscription and catch in an array.
// not sure if this is right
var events = rx.Observable.fromEvent(subscription, 'event').map(x=> x);
// getPastEvents gets batches of 500 iterates over and emits each
// till no more are returned, then resolves a promise
this.getPastEvents({count:500, start:startFrom})
.then(function(){
rx.Observable.fromArray(events).forEach(x=> emit('event', x));
});
};
I don't know that this is the best way. Any thoughts?
thx
I would avoid mixing your different async strategies unnecessarily. You can use concat to join together the two sequences:
var catchUpSubscription = function catchUpSubscription(startFrom) {
var subscription = this.getCurrentEventsSubscription();
return Rx.Observable.fromPromise(this.getPastEvents({count:500, start:startFrom}))
.flatMap(x => x)
.concat(Rx.Observable.fromEvent(subscription, 'event'));
};
///Sometime later
catchUpSubscription(startTime).subscribe(x => /*handle event*/)

Resources