Discord.js collector collects button interaction 2 times when I run the same command twice? - node.js

I am currently working on a /inventory command, the way it works is that the user does /inventory and I saved that user.id with the page they are on { user.id: page } and based on that generate the page. The way you move from page to page is with buttons and collectors, but my problem is that when the same user does /inventory twice so that there is 2 inventory embeds both with the exact same buttons whenever the user presses one button, the code checks if I am the user who did the /inventory commands (so true) and what customId the button has (both embeds have the same button customId). Due to this both inv embeds are updated and I get an error "Interaction has already been acknowledged."
Is there anyway to differentiate which button has been pressed to update the corresponding embed correctly?
Since there is no actual bug with the code I am not posting the code here, if you need the code just ask. I just want to know what I should do to avoid this.

I encountered a similar issue right now. You should create your collector on a message instead of a channel. You can use fetchReply: true on your reply() method to fetch a message and then use it to call createMessageComponentCollector() method from it.
I know this question is 4 months old, but maybe it'll help someone anyway.

The best way to do this is to create your collector on the message you want to send.
// For example:
const MSG = await message.channel.send({embeds: [yourEmbed]});
const collector = await MSG.createMessageComponentCollector({ filter, idle: 20000 });
Another way to solve this is to create a random ID for your buttons. Like this:
const randomID = Math.floor(100000 + Math.random() * 900000);
const button = new MessageButton()
.setCustomId(`button${randomID}`)
.setLabel('Just a button')
.setStyle('PRIMARY')
There is still a small possibility of getting the same ID on two interactions, but it's really low. You can also create longer IDs to lower the chances even further.
And lastly, another way would be to prevent the user from using the command again while your inventory command is active. This would be achievable by creating a
Map and when the user triggers the command you add user's ID and the command's name.
After the collector ends or the command is stopped you can clear the Map.

Related

How To Know When A CS Cart Hook Fires?

I need to confirm if I have selected the right hook from the hooks database. I need to auto order a vehicle on confirming an order. I chose the change_order_status and registered it in my init.php using
fn_register_hooks(
'change_order_status'
);
now in my func.php I have
if (!defined('AREA')) {die('Access denied');}
function fn_dellyman_change_order_status(&$status_to,&$status_from,&$order_info,&$force_notification,&$order_statuses,&$place_order) {
//Getting authentication data to identify user
$auth = $_SESSION['auth'];
var_dump($auth);
}
when I go to orders and switch the order from say open to complete, I expect to see the contents of auth rendered to the page at least as part of the request response. However I see no indication that the hook selected is the right one. How can i ensure the hook called is correct.
depending on your CS-Cart version since 4.6.x is Tygh::$app['session']['auth'] but also depend if is done by AJAX request or normal place/edit order
On AJAX request you will not get any notification.
please try to use for a nicer notification:
fn_print_r(Tygh::$app['session']['auth']);
Use
fn_set_notification('W','Description', var_export($varialble,true) );
this gives a notificcation after the hook fires and I found it very useful for my dev purposes.
The W can also be I and E for information and Error. Basically all it does is change the style of the popup

Liferay IPC listener runs multiple times

First of all sorry if this question has been already asked somewhere, but after a few hours on google I still can't find an answer.
I am pretty new in portlet development, (but we have a shortage of developers and I have to work with it time to time), so the solution might be something trivial, but I really don't have enough experience with it.
The problem is I have two portlets on a page and I try to let one of them know about changes in the other. For this I use IPC. In the first one I have a Liferay.fire function:
function fire(key,value){
Liferay.fire(
'category',{
id: key,
name: value
}
);
}
In the other I have a Liferay.on('category',function(category){...}) function with an ajax call inside and some rendering methods.
Now if I visit the mentioned page and click on the corresponding buttons, at first everything works just fine. However, if I navigate from this page and come back, the listener will run two times. Navigating again -> three times. And so on... But if I reload the page (with F5 or CTRL+F5), it starts over, so until further navigation the listener runs only once.
The other strange thing is no matter how many times the function runs, the input parameters are all the same for each.
For example, if I have left the page and went back to it 3 times and last time I chose the category with 'id=1', then the function will run 3 times with 'id=1'. Now if I choose 'id=2' it will run 3 times with 'id=2'.
If anyone has any idea I would be really grateful as I am stuck for almost a day now.
Thank you very much in advance and please let me know if you need any further info.
the problem you're having is caused by the global Liferay.on listeners that are being created but never removed.
In Liferay Portal 7.x, SPA navigation is enabled by default. This means that when you are navigating, the page isn't being completely refreshed, but simply updated with new data coming from the server.
In a traditional navigation scenario, every page refresh resets everything, so you don't have to be so careful about everything that's left behind. In an SPA scenario, however, global listeners such as Liferay.on or Liferay.after or body delegates can become problematic. Every time you're executing that code, you're adding yet another listener to the globally persisted Liferay object. The result is the observed multiple invocations of those listeners.
To fix it, you simply need to listen to the navigation event in order to detach your listeners like this:
var onCategory = function(event) {...};
var clearPortletHandlers = function(event) {
if (event.portletId === '<%= portletDisplay.getRootPortletId() %>') {
Liferay.detach('onCategoryHandler', onCategory);
Liferay.detach('destroyPortlet', clearPortletHandlers);
}
};
Liferay.on('category', onCategory);
Liferay.on('destroyPortlet', clearPortletHandlers);

How to delete an item in expressJS view

I am building an ExpressJS app and want to add a delete icon on a collection to delete individual items.
I am a bit confused how to do this.
One method I thought of is binding a click event to the icon in the express view and doing an ajax call to the server when clicked.
Another method is to create a form around the icon and the icon will be a button that when clicked submits the form.
I am not confident of the two approaches, anybody have thought on an elegant way to do this the express way
i recommend the second method because it's more easy to understand at this moment.
from your words i understand the delete button could be vulnerability or security hole if you did in the wrong way. Sure it's delete button on any way.
the most easy way to do it with more secure is to use session variable.
the user can't delete unless he is authorized (logged in). if so then you open session on your server and give the user the key of that session.
In the session you can store securely data about the user who interact with the server via providing the session key you gave him at login process.
at this step the user will click the button to delete document but guess what he should be authorized to delete this document. This the time for session key he provide to you to inform you his identity. then the decision is up to you either to delete or reject his request.
all of the above word are concept of what will happen in the code.
i will write two part one part for the login controller to give the user authorization. And the second is the delete document controller.
login:
if(var user = isUser(username, password){
//open session
req.session.user_id = user._id
}
delete document controller:
if(req.session.user_id){
//if true that means he is logged in authorized user
//you can also to check by his user_id if he has the privilege to delete the document
document.delete();//in mongoose model.remove();
}
this solution is for security in deleting any document.
There are multiple ways you can achieve the result you seek.
You could use a link that has the id of the item you want to delete YourURL.com/item/delete/id and attach a click event to it. When the link is clicked your handler should be able to get the id query params and use it to make an AJAX call to the server.
Also, you can use a button like you said, which is basically the same thing as the one above
Bottom line is, both methods are pretty standard, some people could use hidden elements, HTML elements, almost anything that can store an ID or a value, but you will find that most people also use the above methods as well, Which IMHO is pretty standard.
Below is a snippet of how it should work, I am not sure if you are using any Javascript libraries so I scripted it with Vanilla Javascript, if you are not using any Javascript libraries or frameworks I strongly advice you do, it helps reduce a lot of headaches you get from browser difference in the way the handle Javascript.
PS: Include codes you have tried to help give context to how your question could be answered.
var makeDeleteCall = function(e, id) {
e.preventDefault();
console.log(id);
// Make AJAX Call here to server to delete item with the ID
//After call to remote server you can then update the DOM to remove the item
document.getElementById(id).remove();
}
<!-- Using Link -->
<a href="#/delete/1" onclick="makeDeleteCall(event, 1);" id="1">
Delete Item
</a>
<br/>
<br/>
<!-- Using a Button-->
<button onclick="makeDeleteCall(event, 2);" id="2">
Delete Button
</button>
Link to JSFiddle

Async, Azure and program flow

I have searched this forum for my problem but didn't find anything that suited, Im having a problem with my program flow.
I have a MobileService on Azure that has a question table, my app has a main menu and quiz button that takes the user to the quiz page, on the quiz page I have a start quiz button that shows the first question in the list.
This is the code im using to get the questions from the database, I placed it in the pages constructor and now when the user presses the quiz button there is a delay in the page opening which isn't that bad as its not a long wait, only a few seconds, is there a better way to do this?
Task<IMobileServiceTable<Question>> getDataFromDatabase = new Task<IMobileServiceTable<Question>>(getQuestions);
getDataFromDatabase.Start();
QuestionList = await getDataFromDatabase;
In the same function I have this code which modifies the start quiz button isEnabled attribute. This stops the quiz going forward unless the data has came through from the server, but its not working all the time and sometimes the start button isenabled is set to true and I get nullreference from my MobileServiceCollectionView QuestionList even though the task has completed.
Task<bool> assignData = new Task<bool>(assignTabletoitems);
assignData.Start();
startbutton.IsEnabled = await assignData;
Any help on this would be appreciated.
Thanks
You shouldn't have to create a new instance of Task<T> to query the database (you haven't provided the definition of getQuestions which is used in your first code snippet, so I can't tell whether that code is doing what it's supposed to). What you'd typically do is to get a table for the appropriate type from the MobileServiceClient object, and then query on it:
var client = new MobileServiceClient(appUrl, appKey);
var table = client.GetTable<Question>();
var questionList = await table.ToListAsync();
Regarding your second code snippet, without the definition of assignTabletoitems it's really hard to know what you're intending that code to do.

Validation before submitting the Search form generated using filterGrid in JQGrid

I have a search form I generated using the filterGrid option in JqGrid. I want to add a JavaScript logic which is invoked before I submit the Search form. I have added a method which is invoked by the beforeSubmit property for the filterGrid. It goes into the method before submitting, but always submits the form regardless of the value returned. I would like the form to not submit if the javascript returns false.
Have any of you guys implemented anything like this before. Or is there any othe rbetter way to implement this. Any help on this will be really appreciated.
Code:
$("#search").filterGrid("#resultsGrid",
{gridModel:true,gridNames:true,enableSearch:true,
formtype:"vertical",buttonclass:"submitButton",
enableClear:true,beforeSearch:validateDate});
function validateDate(dateDiff) {
if(daysDiff < 0){
return [false,"Message"];
}
} // ??? (commented by Oleg)
return [true,""];
}
There are at least three different ways how searching can be used: Toolbar Searching, Custom Searching which you use and Single field searching or Advanced Searching which share the same code. So one have currently three different implementations of close things.
Only Toolbar Searching has beforeSearch event handler which can return false to stop searching. In case of Custom Searching the value returned by the event handler beforeSearch will not used. Single field searching or Advanced Searching don't call any event handler before searching. In all cases for the searching will set searching filter and the jqGrid parameter search to true and then force grid reloading with the code like
$("#gridId").trigger("reloadGrid",[{page:1}]);
To be able to make any validations and stop reloading of the grid I see no simple way. So I suggest only following.
You can overwrite the standard reloadGrid event handler and chain it. The corresponding code con look like following:
var grid = $("#gridId");
var events = grid.data("events"); // read all events bound to
var originalReloadGrid; // here we will save the original event handle
var skipRefresh = false; // this can be changed by owe validation function
// Verify that one reloadGrid event hanler is set. It is typical sitation
if (events && events.reloadGrid && events.reloadGrid.length === 1) {
originalReloadGrid = events.reloadGrid[0].handler; // save old
grid.unbind('reloadGrid');
var newEvents = grid.data("events");
grid.bind('reloadGrid', function(e,opts) {
if (!skipRefresh && grid[0].p.search) {
originalReloadGrid(e,opts);
}
});
}
Probably I will create later a demo which demonstrate this on an example and place the link to the demo here. Moreover I will try to suggest code changes to jqGrid so, that in all different implementations of searching will be possible to stop serching by returning false by beforeSearch event handle.
UPDATED: OK! I prepared a demo for you. In the demo I use no server components, so it will not really do searching, but you can see the results if the grid will be refreshed and goes to the page 1.
To test the demo you can do following:
type in the "Client" input field a text not starting with 'test' and click "search" button. You receive an alert which simulate the validation dialog.
type in the "Client" input field a text starting with 'test' like test1 and click "search" button. Now the grig will refreshed because the validation will be OK.

Resources