Discord JS | Server: detect re-joined user - discord.js

I would like that my bot detect when a user is joining again, back, to the server. I have made a JSON file containing the ID of the users that left the server. If the new member's ID is included in the JSON file with IDs, then it (the bot) should give a special welcome message.
The code shown is not working, meaning it is not really taking any ID from the JSON file. I guess that it needs a bucle with the lenght of the JSON's content to compare the Member ID with Badmember.USERS_LEFT_ID[].
JSON file: user_left.json
{
"USERS_LEFT_ID" : [
"1",
"2",
"3",
"4",
]
}
JS File: guildMemberAdd.js
const Badmember = require('users_left.json')
exports.run = async(client, newmember) => {
const channel = newmember.guild.channels.cache.get(Wlc_ch);
if (newmember.user.id == Badmember.USERS_LEFT_ID) {
channel.send('Special message!')
} else {
console.log(newmember);
console.log(newmember.user.id + ' has joined');
}
}

To check if the user is in the ID list, need to somehow iterate through the array. One option is to use the built-in includes() method.
const Badmember = require('users_left.json')
exports.run = async(client, newmember) => {
const channel = newmember.guild.channels.cache.get(Wlc_ch);
if (Badmember.USERS_LEFT_ID.includes(newmember.user.id)) {
channel.send('Special message!')
} else {
console.log(newmember);
console.log(newmember.user.id + ' has joined');
}
}
Check out MDN docs for more details on Array.prototype.includes(): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes

Related

How can I remove an Object from a MongoDB array without $pull?

I'm trying to make a social media app, so I wrote a route that allows users to like posts, but If someone already liked it and pressed "like" again - It should remove the like, right? The point is - I can't use $pull since it uses findByIdAndUpdate, which can only be used if likes have their own separated model, but I don't have it because I think that overworks db a bit.
I use for to cycle trough an array and scan an Id of an Object and validate if it matches an Id of the logged user
const post = await Post.findById(request.params.post_id)
for (let i = 0; i < post.likes.length; i++) {
if (post.likes[i].userId === request.user.id) {
// Remove post.likes.[i] from the database completely
}
}
Here is how post looks like in Mongo documents:
{ "userId": "627eae4ed5b20f818d7a8142", "title": "Simple Post Title", "body": "Whatever",
"likes": [{ "userId": "627eae4ed5b20f818d7a8142", "likedAt": "14:58:20" }], }
If there is something else I need to provide - don't hesitate to ask.
Nevermind guys! I did it, I only needed to update the Post collection, nothing more!
for (let i = 0; i < post.likes.length; i++) {
if (post.likes[i].userId === request.user.id) {
Post
.findByIdAndUpdate(post._id, {
$pull: { likes: post.likes[i] }
}, {
new: true,
runValidators: true
})
.then((updated_post) => {
response.status(200).json({ success: true, updated_post: updated_post })
})
.catch(() => {
return next(
new ErrorResponse(`An error occurred while updating the post`, 400)
)
})
}
}
It does gives me an error, but I think I'll manage
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client

Discord JS - forEach looped embed

I'm quite new to Javascript, normally a Python person. I've looked at some other answers but my embed does not add the fields as expected. The embed itself is sent.
My Discord bot follows the guide provided by the devs (primary file, slash commands, command files). I am trying to loop through the entries in an SQLite query and add them as fields.
My command file is below.
const { SlashCommandBuilder } = require('#discordjs/builders');
const { MessageEmbed } = require('discord.js')
const sqlite = require('sqlite3').verbose();
module.exports = {
data: new SlashCommandBuilder()
.setName('rank')
.setDescription('Rank all points.'),
async execute(interaction) {
const rankEmbed = new MessageEmbed()
.setColor('#0099ff')
.setTitle('Rank Board')
let db = new sqlite.Database('./databases/ranktest.db', sqlite.OPEN_READWRITE);
let queryall = 'SELECT name, points FROM pointstable ORDER BY points DESC'
db.all(queryall, [], (err, rows) => {
if (err) {
console.log('There was an error');
} else {
rows.forEach((row) => {
console.log(row.name, row.points)
rankEmbed.addField('\u200b', `${row.name}: ${row.points}`, true);
});
}
})
return interaction.reply({embeds: [ rankEmbed ] });
}
}
I would also like to convert row.name - held as Discord IDs - to usernames i.e. MYNAME#0001. How do I do this by interaction? I was able to obtain the User ID in another command by using interaction.member.id, but in this case I need to grab them from the guild. In Python I did this with await client.fetch_user but in this case the error await is only valid in async functions and the top level bodies of modules is thrown.
Thanks.
OK I've solved the first aspect, I had the return interaction.reply in the wrong place.
Relevant snippet:
rows.forEach((row) => {
console.log(row.name, row.points)
rankEmbed.addField('\u200b', `${row.name}: ${row.points}`, false);
})
return interaction.reply({embeds: [rankEmbed ]} );
Would still appreciate an answer to the converting row.name (user ID) to user name via fetch.
I've solved the second aspect also. Add the below into the loop.
rows.forEach((row) => {
let client = interaction.client
const uname = client.users.cache.get(row.name);
rankEmbed.addField('\u200b', `${uname}: ${row.points}`, false);

how do i make sure that only one reaction can be added to a message?

I am creating a role reaction bot and I want that if a member has already clicked on a reaction and clicks another it removes the previous one and the role associated with it and gives him the role that corresponds to the last one clicked (there are 15 reactions available) , I was writing code similar to this:
let msg = reaction.message;
let msgGuild = msg.guild;
let userGuild = msgGuild.members.cache.get(user.id);
let userRole = userGuild.roles;
if(reaction.message.channel.id === "764148072498200589") {
if(reaction.emoji.name === "RedRoleID") {
if(userRole.cache.has("BaseRoleID")) {
userRole.remove("BaseRoleID");
userRole.add("RedRoleID");
} else if(userRole.cache.has("OrangeRoleID")) {
userRole.remove("OrangeRoleID");
userRole.add("RedRoleID");
}
};
};
is there an easier and shorter way to do what I want without creating an else if for each role?
I think you could probably make an array of all the emoji names, then use Array.prototype.forEach to check all of them.
let { emoji, message, message: { guild, channel }} = reaction;
let { roles } = guild.member(user.id);
const emojis = ['BaseRoleID', 'RedRoleID', 'OrangeRoleID', 'etc'];
if (channel.id === '764148072498200589') {
emojis.forEach((id) => (roles.cache.has(id) ? roles.remove(id) : id));
roles.add(emoji.name);
}
Edit: My mistake, I thought you had named the emojis as the corresponding role IDs. Here's an alternate method if you do not want to do that:
let { emoji, message, message: { guild, channel }} = reaction;
let { roles } = guild.member(user.id);
const emojis = [
{ emote: 'EmojiID', role: 'RoleID' },
'continue this pattern for all roles and emotes'
];
if (channel.id === '764148072498200589') {
emojis.forEach(({ emote, role }) => {
if (roles.cache.has(role))
return roles.remove(role)
if (emoji.id === emote) roles.add(role)
});
};

HTTPS Post URL with an Array of Objects

I've been trying to format a URL POST with objects in my array as they're required to post to my REST API. I'm forced to use an archaic API POST system from a vendor and have been trying to hack together a solution.
Basically the JSON looks similar to:
{"api_key": "12234",
"server_id:"qwp2222",
"recipients": [
{"email":"john#doe.com",
"name": "john doe"}]
}
I am trying to format the Array'd key-value pairs as part of the URL so it would post to the endpoint without much fanfare.
I have the rest of the URL together without issue, it is just the recipients' array that is the problem.
How should I write the POST URL in order to make sure that I can post the array of objects correctly?
I appreciate all the help in advance!
Does not work here, but this code will make linear structure from your JSON, create form from and submit to itself.
If you run it on local or anywhere else and add f.action = 'your get or post page', it will send data properly.
var data = {
api_key: '12234',
server_id: 'qwp2222',
recipients: [{
email: 'john#doe.com',
name: 'john doe'
}]
};
function collectItems(res, json) {
for (var a in json) {
if (json[a].constructor === Array ||
json[a].constructor === Object) {
collectItems(res, json[a]);
} else {
res.push([a, json[a]]);
}
}
}
var all = [];
collectItems(all, data);
var f = document.createElement('FORM');
f.method = 'post';
// default get: ...?api_key=12234&server_id=qwp2222&email=john#doe.com&name=john+doe
for (var e in all) {
var i = document.createElement('INPUT');
i.name = all[e][0];
i.value = all[e][1];
i.type = 'hidden'; // do not show values sent
f.appendChild(i);
}
if (location.search) {
alert("Submit result:" & location.href);
} else {
document.body.appendChild(f);
f.submit();
}

Firestore - Simple full text search solution

I know that firestore doesn't support full text search and it giving us solution to use third party services. However I found a simple solution to simple "full text search" and I think this might help others who doesn't want to use third party services as me for such a simple task.
I'm trying to search for company name which is saved in firestore collection under my companyName which can be in any format for example "My Awesome Company". When adding new company with companyName or updating a value in companyName I'm also saving searchName with it which is the same value as company name but in lower case without spaces
searchName: removeSpace(companyName).toLowerCase()
removeSpace is my simple custom function which remove all spaces from a text
export const removeSpace = (string) => {
return string.replace(/\s/g, '');
}
That turns our company name to myawesomecompany which is saved in searchName
Now I've got a firestore function to search for company which indexing through searchName and returning companyName. Minumum search value is a searched value without last character and maximum search value is a searched value with added "zzzzzzzzzzzzzzzzzzzzzzzz" transformed to lower case. That means if you search for My Aw then min value will be mya and max value will be myawzzzzzzzzzzzzzzzzzzzzzzz
exports.handler = ((data) => {
const searchValue = data.value.replace(/\s/g, '').toLowerCase()
const minName = searchValue.substr(0, searchName.length-1)
const maxName = searchValue + "zzzzzzzzzzzzzzzzzzzzzzzz"
let list = []
const newRef = db.collection("user").where("profile.searchName", ">=", minName).where("profile.searchName", "<=", maxName)
return newRef.get()
.then(querySnapshot => {
querySnapshot.forEach(doc => {
list.push({ name: doc.data().profile.companyName})
})
return list
})
})
I didn't have time to fully test it but so far it works without any problems. Please let me know if you spot anything wrong with it. Now the question is
Is "z" character the highest value character in firestore or is there any other more decent way to add into the search value maximum amount without adding "zzzzzzzzzzzzz"?
I like your decision to preprocess the text so that it can be queried, but you could provide for a more flexible search by storing lowercase keywords with the users and searching those. In other words, transform:
"My Awesome Company"
to...
{ my: true, awesome: true, company: true }
...and test against that.
When adding/updating the property:
// save keywords on the user
let keywords = {}
companyName.split(' ').forEach(word => keywords[word.toLowerCase()] = true)
When querying:
let searchKeywords = userInputString.split(' ').map(word => word.toLowerCase())
let collection = db.collection("user")
searchKeywords.forEach(keyword => {
collection = collection.where(`keywords.${keyword}` , '==' , true);
});
With a little modification of previous answer I have made another simple text search. I'm saving keyword to an array instead of saving it in object like this
nameIndex: textIndexToArray(companyName)
where textIndexToArray is my custom function
export const textIndexToArray = (str) => {
const string = str.trim().replace(/ +(?= )/g,'')
let arr = []
for (let i = 0; i < string.trim().length; i++) {
arr.push(string.substr(0,i+1).toLowerCase());
}
return arr
}
which transfer a text into array. For example
"My Company"
will return
[m, my, my , my c, my co, my com, my comp, my compa, my compan, my company]
with nameIndex saved in firestore we can simply query the data thorough nameIndex and return companyName
exports.handler = ((data) => {
const searchValue = data.value.toLowerCase()
let list = []
const newRef = db.collection("user").where("nameIndex", "array-contains", searchValue)
return newRef.get()
.then(querySnapshot => {
querySnapshot.forEach(doc => {
list.push({ name: doc.data().companyName, })
})
return list
})
})

Resources