nodejs discord get nickname from a users ID - node.js

My goals are to obtain the users nickname by using their ID.
Their ID's are stored as variables which are being collected from a reaction collector.
I have tried a few methods and failed, most of which either return nothing or errors.
The below code returns nothing, the getnames() function is empty. This method was recommended to me buy 2 people from a nodejs discord server which aims to help solve issues, similar to here.
// returns player ID's
function getPlayers() {
let players = [];
players.push(queue.tank[0]); // First (1) in TANK queue
players.push(queue.heal[0]); // First (1) in HEAL queue
players.push(queue.dps[0]); // First (2) in DPS queue
players.push(queue.dps[1]);
return players;
}
// get nick names from ID's
function getnames() {
let players = getPlayers();
let playerNicks = [];
let newPlayer = "";
players.forEach(async player => {
newPlayer = await message.guild.members.fetch(player).then(function (user) {return user.displayName });
playerNicks.push(newPlayer)
return playerNicks;
})}
//formats nicknames into string
function formatnicknames() {
let formatted_string2 = '';
let playerNicks = getnames();
if (playerNicks)
formatted_string2 = `${playerNicks[0]} \n${playerNicks[1]} \n${playerNicks[2]} \n${playerNicks[3]}`;
return formatted_string2;
}
I have also tried a few variations of the below code, still unable to obtain nickname.
message.guild.members.cache.get(user.id)
Edit #1
now tried the following code with no success. (boost1ID contains the ID of 1 user)
var mem1 = message.guild.members.fetch(boost1ID).nickname
Edit #2
tried a new method of obtaining displayname from ID.
var guild = client.guilds.cache.get('guildid');
var mem1 = guild.member(boost1ID);
var mem2 = guild.member(boost2ID);
var mem3 = guild.member(boost3ID);
var mem4 = guild.member(boost4ID);
var nickname1 = mem1 ? mem1.displayName : null;
var nickname2 = mem2 ? mem2.displayName : null;
var nickname3 = mem3 ? mem3.displayName : null;
var nickname4 = mem4 ? mem4.displayName : null;
var Allnicknames = `${nickname1} ${nickname2} ${nickname3} ${nickname4}`
message.channel.send(`testing nicknames: ${Allnicknames}`)
I managed to only return my own name since i dont have a nickname on this server, but the other three users who does have a nickname returned null.

This is the simplest solution:
// your users ids
const IDs = [ '84847448498748974', '48477847847844' ];
const promises = IDs.map((userID) => {
return new Promise(async (resolve) => {
const member = message.guild.member(userID) || await message.guild.members.fetch(userID);
resolve(member.displayName || member.user.username);
});
});
const nicknames = await Promise.all(promises);
// you now have access to ALL the nicknames, even if the members were not cached!
The members you are trying to get the nicknames of are not necessarily cached, and this fixes that.

I made an example that could help you.
let testUsers = [];
module.exports = class extends Command {
constructor(...args) {
super(...args, {
description: 'Testing.',
category: "Information",
});
}
async run(message) {
function getNicknames(userArr, guild) {
let playerNicks = [];
for(var i = 0; i < userArr.length; i++) {
playerNicks.push(guild.member(userArr[i]).displayName);
}
return playerNicks;
}
let testUser = message.guild.members.cache.get(message.author.id);
testUsers.push(testUser);
let guild = message.guild;
console.log(getNicknames(testUsers, guild));
}
}
I created a function getNicknames that takes in two parameters. The first one is an Array of users (as you get one from your function getPlayers()) and the second one is the guild you are playing in. You need to provide the guild, because every user should be a GuildMember, because you want to use .displayName. I created a user Array outside of my command code, because otherwise there will only be one user in the Array everytime you use the command. Inside of the getNicknames() function I have created a new Array playerNicks that I basically fill with the user nicknames we get from our provided user Array.
Now you have to implement that into your code.
The call of the function getNicknames(), for your code should look like this:
getNicknames(getPlayers(), message.guild);

Related

How can I store the value of a promise and use it once resolved?

I am currently developing an app which interacts with uniswap, and I have developed a Wrapper class to contain the info and variables I'll need about some pair (e.g DAI/WETH).
As some of this values are asynchronous, I have coded a build() async function to get those before calling the constructor, so I can store them. I want to store the result of this build function, which is an instance of the class I have defined, inside a variable to use it later, but I need to know whether the Promise that that build function returns is resolved before using it, so how can I make it?
Here is the code of the class:
'use strict'
const { ChainId, Fetcher, WETH, Route, Trade, TokenAmoun, TradeType, TokenAmount } = require('#uniswap/sdk')
const { toChecksumAddress } = require('ethereum-checksum-address')
const Web3 = require('web3')
const web3 = new Web3()
const chainId = ChainId.MAINNET;
let tok1;
let tok2;
let pair;
let route;
let trade;
class UniswapTokenPriceFetcher
{
constructor(async_params)
{
async_params.forEach((element) => {
if (element === 'undefined')
{
throw new Error('All parameters must be defined')
}
});
this.trade = async_params[0];
this.route = async_params[1];
this.pair = async_params[2];
this.tok1 = async_params[3];
this.tok2 = async_params[4];
}
static async build(token1, token2)
{
var tok1 = await Fetcher.fetchTokenData(chainId, toChecksumAddress(token1))
var tok2 = await Fetcher.fetchTokenData(chainId, toChecksumAddress(token2))
var pair = await Fetcher.fetchPairData(tok1, tok2)
var route = new Route([pair], tok2)
var trade = new Trade(route, new TokenAmount(tok2, web3.utils.toWei('1', 'Ether')), TradeType.EXACT_INPUT)
return new UniswapTokenPriceFetcher([trade, route, pair, tok1, tok2])
}
getExecutionPrice6d = () =>
{
return this.trade.executionPrice.toSignificant(6);
}
getNextMidPrice6d = () =>
{
return this.trade.nextMidPrice.toSignificant(6);
}
}
module.exports = UniswapTokenPriceFetcher
Thank you everybody!
EDIT: I know Uniswap only pairs with WETH so one of my token variables is unneccesary, but the problem remains the same! Also keep in mind that I want to store an instance of this class for latter use inside another file.
You should either call the build function with await
const priceFetcher = await UniswapTokenPriceFetcher.build(token1, token2)
or followed by then
UniswapTokenPriceFetcher.build(token1, token2).then(priceFetcher => {...})
I don't see any other way.

Calling a method within a dynamically named object

I am trying to invoke a method of a dynamically named object.
I have a few objects each containing a method named 'getWeight'. I need to invoke this method of a subset of these objects. However, the subset of objects depends on user inputted info, which is why I'm attempting to dynamically construct the object name and invoke it's 'getWeight' method within a loop.
My code is below:
// Importing objects containing 'getWeight' method
const Apple = require('../classses/Apple');
const Pear = require('../classes/Pear);
const Carrot = require('../classes/Carrot);
const Potato = require('../classes/Potato);
const promises = {};
const user = 'userX'; // This is the inputted info, could also equal 'userY'.
const searchableFoods = {
userX: ['Apple', 'Pear'],
userY: ['Carrot', 'Potato']
};
for (i = 0; i < searchableFoods[user].length; ++i) {
promises[searchableFoods[user][i]] = new Promise(function(resolve, reject) {
// Below line should behave like: Apple.getWeight(arg1, function....
searchableFoods[user][i].getWeight(arg1, function(response) {
resolve(response);
})
})
}
Unfortunately, I get this error:
[searchableFoods[user][i].getweight] is not a function
I've tried a number of variations but can't make it work. Is it possible to do this?
Require all of those into a single object rather than many standalone variables, and then you can use simple property lookup to get to the appropriate requireed value. Also, forEach will likely result in more readable code, and you can pass the function name alone (resolve) rather than defining an extra anonymous function for the getWeight callback:
const foods = {
Apple: require('../classses/Apple'),
Pear: require('../classes/Pear),
Carrot: require('../classes/Carrot),
Potato: require('../classes/Potato)
};
// ...
searchableFoods[user].forEach((foodName) => {
promises[foodName] = new Promise((resolve) => {
foods[foodName].getWeight(arg1, resolve);
});
});
Can you define array not using strings? Something like that?
const searchableFoods = {
userX: [Apple, Pear],
userY: [Carrot, Potato]
};
You're trying to access getWeight as a property of string 'Apple' and not of the actual object Apple that you are importing.
Change the subset array to something like this
const searchableFoods = {
userX: [Apple, Pear],
userY: [Carrot, Potato]
};
Which makes the final code to be
const Apple = require('../classses/Apple');
const Pear = require('../classes/Pear);
const Carrot = require('../classes/Carrot);
const Potato = require('../classes/Potato);
const promises = {};
const user = 'userX' // This is the inputted info, could also equal 'userY'.
const searchableFoods = {
userX: [Apple, Pear],
userY: [Carrot, Potato]
};
for (i = 0; i < searchableFoods[user].length; ++i) {
promises[searchableFoods[user][i]] = new Promise(function(resolve, reject) {
// Below line should behave like: Apple.getWeight(arg1, function....
searchableFoods[user][i].getWeight(arg1, function(response) {
resolve(response);
})
})
}

copy data from one node1 child to another node2 child with using firebase cloud functions

Below is my code of cloud functions I am trying to copy the data from users node to challenge node
exports.copyChallengeDate = functions.database.ref(`Users/{pushId}/DateChallengeAccept`).onCreate((snapshot, context)=>{
var DateChallengeAccept = snapshot.val();
console.log('Challenge', context.params.pushId, DateChallengeAccept);
var challenge = DateChallengeAccept;
return admin.database().ref('Challenge/' + context.params.pushId).child('DateChallengeAccept').set(challenge);
});
But the thing is when I am trying to copy the date from user table to challenge node it's only occur in QS5h99YxS0ZGpT42fpUFvzOdpTi1
D uid same as of Users node. I want to copy it in both uid's(QS5h99YxS0ZGpT42fpUFvzOdpTi1, 7aH9Ag8414VzM0n7P6ur4LvcepI2)
D present in challenge node. I was stuck in it from last two days please help me out
Update following you remark that DateChallengeAccept is not initialized when you create the record. You should use another method, like onUpdate() (or onWrite()) and not onCreate(), as you do in your question. Do as follows:
exports.copyChallengeDate = functions.database.ref(`Users/{pushId}`).onUpdate((change, context) =>
const DateChallengeAccept = change.after.val().DateChallengeAccept;
if ( DateChallengeAccept === undefined) {
return false;
}
const referredBy = change.after.val().referredBy;
console.log('Challenge', context.params.pushId, DateChallengeAccept);
const dateChallengeAcceptObj = {
"DateChallengeAccept": DateChallengeAccept
};
let updates = {};
updates['Challenge/' + context.params.pushId + '/DateChallengeAccept'] = dateChallengeAcceptObj;
updates['Challenge/' + referredBy + '/DateChallengeAccept'] = dateChallengeAcceptObj;
return admin.database().ref().update(updates);
});
You need to get the referredId value in your Function, because it is this data item that holds the ID of the other user (i.e. 7aH9Ag8414VzM0n7P6ur4LvcepI2). So you have to trigger the event on the parent node, not at the DateChallengeAccept node.
Then you have to use the update() method to write simultaneously to two nodes, see the doc here: https://firebase.google.com/docs/database/web/read-and-write#update_specific_fields
So you should do as follows:
exports.copyChallengeDate = functions.database.ref(`Users/{pushId}`).onCreate((snapshot, context)=>{
const DateChallengeAccept = snapshot.val().DateChallengeAccept;
if ( DateChallengeAccept === undefined) {
return false;
}
const referredBy = snapshot.val().referredBy;
console.log('Challenge', context.params.pushId, DateChallengeAccept);
const dateChallengeAcceptObj = {
"DateChallengeAccept": DateChallengeAccept
};
let updates = {};
updates['Challenge/' + context.params.pushId + '/DateChallengeAccept'] = dateChallengeAcceptObj;
updates['Challenge/' + referredBy + '/DateChallengeAccept'] = dateChallengeAcceptObj;
return admin.database().ref().update(updates);
});

Using Redis SCAN in NODE

I have Redis with a lot of keys in some format and I want to get keys that match some pattern and do some operations on them. I don't use KEYS method since it's not recommend in production. Using SCAN I'm wondering what is the best way to write it in code. I have to do something like a while loop but using promises, my current solution looks like this (code is simplified a little):
'use strict'
const Promise = require('bluebird');
const config = require('./config');
const client = require('./clinet');
let iterator = 0;
Promise.coroutine(function* () {
do {
iterator = yield clinet.scanAsync(iterator, 'myQuery', 'COUNT', config.scanChunkSize)
.then(data => {
let nextIterator = data[0];
let values = data[1];
//do some magic with values
return nextIterator;
})
} while (iterator !== '0');
})();
Is there a better way to do it that I'm missing?
I realize this is a really old question, but I found all of the other answers very unsatisfying. Here is yet another attempt to scan in a relatively clean way using async await (WITHOUT the use of yet another external dependency). You can easily modify this to continuously delete each set of found keys (you would want to tackle them in batches like this in case there are LOTS). Pushing them into an array just demonstrates one very basic thing you could do with them during this stage.
const redis = require('redis');
const { promisify } = require('util');
const client = redis.createClient({...opts});
const scan = promisify(client.scan).bind(client);
const scanAll = async (pattern) => {
const found = [];
let cursor = '0';
do {
const reply = await scan(cursor, 'MATCH', pattern);
cursor = reply[0];
found.push(...reply[1]);
} while (cursor !== '0');
return found;
}
You can use recursion to keep calling scan until done.
function scanAsync(cursor, pattern, returnSet){
return redisClient.scanAsync(cursor, "MATCH", pattern, "COUNT", "100").then(
function (reply) {
cursor = reply[0];
var keys = reply[1];
keys.forEach(function(key,i){
returnSet.add(key);
});
if( cursor === '0' ){
return Array.from(returnSet);
}else{
return scanAsync(cursor, pattern, returnSet)
}
});
}
Pass in a Set() to make sure keys aren't duplicated
myResults = new Set();
scanAsync('0', "NOC-*[^listen]*", myResults).map(
function( myResults ){ console.log( myResults); }
);
You can try this snippet to scan (1000) keys per iteration and 'delete`.
var cursor = '0';
function scan(pattern,callback){
redisClient.scan(cursor, 'MATCH',pattern,'COUNT', '1000', function(err, reply){
if(err){
throw err;
}
cursor = reply[0];
if(cursor === '0'){
return callback();
}else{
var keys = reply[1];
keys.forEach(function(key,i){
redisClient.del(key, function(deleteErr, deleteSuccess){
console.log(key);
});
});
return scan(pattern,callback);
}
});
}
scan(strkey,function(){
console.log('Scan Complete');
});
Nice option for node-redis module is to use scan iterators. Example:
const redis = require("redis");
const client = redis.createClient();
async function getKeys(pattern="*", count=10) {
const results = [];
const iteratorParams = {
MATCH: pattern,
COUNT: count
}
for await (const key of client.scanIterator(iteratorParams)) {
results.push(key);
}
return results;
}
(Of course you can also process your keys on the fly in for await loop without storing them in additional array if that's enough for you).
If you do not want to override scan parameters (MATCH/COUNT) you can just skip them and execute client.scanIterator() without parameter (defaults will be used then, MATCH="*", COUNT=10).
I think the node bindings for Redis are pushing too much responsibility to the caller here. So I created my own library for scanning as well, using generators in node:
const redis = require('redis')
const client = redis.createClient(…)
const generators = require('redis-async-gen')
const { keysMatching } = generators.using(client)
…
for await (const key of keysMatching('test*')) {
console.info(key)
}
It's the last bit that obviously is the thing that you should care about. Instead of having to carefully control an iterator yourself, all you need to do is use a for comprehension.
I wrote more about it here.
Go through this, it may help.
https://github.com/fritzy/node-redisscan
do not use the library as it, go through the code available at
https://github.com/fritzy/node-redisscan/blob/master/index.js

Resolve Clients and rooms - Migration from socket.io 0.9 to +1.x

I am trying to adapt a piece of code that was coded with socket.io 0.9 that returns a list of clients in a specific room and list of rooms(typical chat-room example)
Users in room
var usersInRoom = io.sockets.clients(room);
List of rooms
socket.on('rooms', function() {
var tmp = io.sockets.manager.rooms;
socket.emit('rooms', tmp);
});
tmp looks like this
{
0: Array[1],
1: /Lobby: Array[1]
}
So I can show the list in the client with this javascript run on the browser.
socket.on('rooms', function(rooms) {
$('#room-list').empty();
debugger;
for(var room in rooms) {
room = room.substring(1, room.length);
if (room != '') {
$('#room-list').append(divEscapedContentElement(room));
}
}
$('#room-list div').click(function() {
chatApp.processCommand('/join ' + $(this).text());
$('#send-message').focus();
});
});
But for version >1.x I just found the clients/rooms changed.
Following some links I found here, I could manage to get a list of rooms by doing this:
socket.on('rooms', function(){
var tmp = socket.rooms;
socket.emit('rooms', tmp);
});
The problem here is that socket.rooms returns
{
0: "RandomString",
1: "Lobby",
length: 2
}
And I just need to pass the 'Lobby' room. I don't know from where the random string come from.
EDIT
Through some debugging I discovered, the randomstring is the socket.id ... Is it normal this behavior? Returning the room and the socket.id together?
Updated
I finally got some results
Users in room
var usersInRoom = getUsersByRoom('/', room);
function getUsersByRoom(nsp, room) {
var users = []
for (var id in io.of(nsp).adapter.rooms[room]) {
users.push(io.of(nsp).adapter.nsp.connected[id]);
};
return users;
};
List of rooms
function getRooms(io){
var allRooms = io.sockets.adapter.rooms;
var allClients = io.engine.clients;
var result = [];
for(var room in allRooms){
// check the value is not a 'client-socket-id' but a room's name
if(!allClients.hasOwnProperty(room)){
result.push(room);
}
}
return result;
}
Better and more straightforward ways to achieve the results?
These are the links I checked:
How to get room's clients list in socket.io 1.0
socket.io get rooms which socket is currently in
It seems there is no better approach, so I will use my own answer. In case any of you has a better solution, I would update the accepted one.
Users in room
var usersInRoom = getUsersByRoom('/', room);
function getUsersByRoom(nsp, room) {
var users = []
for (var id in io.of(nsp).adapter.rooms[room]) {
users.push(io.of(nsp).adapter.nsp.connected[id]);
};
return users;
};
List of rooms
function getRooms(io){
var allRooms = io.sockets.adapter.rooms;
var allClients = io.engine.clients;
var result = [];
for(var room in allRooms){
// check the value is not a 'client-socket-id' but a room's name
if(!allClients.hasOwnProperty(room)){
result.push(room);
}
}
return result;
}

Resources