0

Context :

I'm making a Discord bot using the discord.js library and MongoDB (with mongoose). I created a script that can construct messages from data stored in my MongoDB database. For exemple, if an user on my Discord server type "!hello", it will query my database to construct a reply like "Hello to you!".

I store my reply message data in an document like this :

{
    "command": "!hello",
    "content": "Hello to you!"
}

(I voluntary omitted some properties here that are useless in the context of my question)

And retrieve it in my code with something like :

let retrievedReply;
await mongo().then(async mongoose => {
    try {
        let query = {command: message.content};
        retrievedReply = await replySchema.findOne(query);
    } finally {
        mongoose.connection.close();
    }
});
message.reply(retrievedReply.content);

My issue :

Now, imagine I want it to respond "Hello username!".

If I didn't construct my reply using data from mongo I could simply do something like :

message.reply(`Hello ${message.member.nickname}!`);

or

message.reply("Hello " + message.member.nickname + "!");

So, in other words, using backticks and ${} syntax or (but I like it less), splitting my string and concatenate my property value like the second example.

The thing is, we can't store a string inside backticks in json format (used by MongoDB). So even if I build my document like so :

{
    "command": "hello",
    "content": "Hello ${message.member.nickname}!"
}

${message.member.nickname} would not be interpreted like a property value but just like a simple string.

The 1000$ question :

How can I use this property inside my code? I thought about extracting it using regex but it does not change the fact that it will still be a string. Is there a way to "convert" from a string to an usable variable?

EDIT

Since Brettski asked me (rightfully), here is an example of what I want :

message.member.send(`Hello ${message.member}!`);

This message.member property here represent a member object of a Discord guild. It contains a lot of things like its permissions on the guild, its roles, etc. When we use a member object into a message, Discord will make a mention of the member like bellow.

Message received from the bot into the application

It works the same with other properties like channels for example. Users can then click on those to see informations about the member or to go directly to the channel, etc. I want to be able to use this.

JusteThom
  • 80
  • 1
  • 3
  • 9
  • set message.member.nickname equal to a variable and use that in your string – Polydynamical Feb 24 '21 at 21:18
  • @cbracketdash Hum thanks for your comment but it doesn't change anything. The thing is I can not use variable in my string. – JusteThom Feb 24 '21 at 21:25
  • Does [this](https://stackoverflow.com/questions/12979335/creating-json-object-with-variables) help you out? – Polydynamical Feb 24 '21 at 21:27
  • @0x435d2d nope, your link is the opposite of what I'm trying to do. He/she has an object with variables and he/she want to convert it into a json format. This is not what I'm trying to achieve. – JusteThom Feb 24 '21 at 21:31
  • What if you put a token in the string and then replace it with the name? So your content could be something like `"hello ~membernickname~!"` and in or before `message.reply` do a replacement on the value. – Brettski Feb 25 '21 at 01:31

2 Answers2

1

That is correct you will not be able to store a string literal in Mongo or other way.

One though I had is to put a token in your string and then do a replace on it in message.reply() or before it.

example:

const helloCommand = {
  "command": "hello",
  "content": "Hello ~~membernickname~~!"
};

In this example the token to replace is ~~membernickname~~

You can use something like this to do the replace:

const command = JSON.parse(helloCommand)
message.member.send(command.content.replace('~~membernickname~~', message.member));

The resulting string being sent to the send() command is the same. So if the nickname is @brettski, the string 'Hello @brettski' will be sent to the send() command.

In your example:

message.reply(`Hello ${message.member.nickname}!`);

the part:

`Hello ${message.member.nickname}!`

is a string literal (template literals). What the function message.reply() ends up getting is 'Hello Nickname' a string. So the replace should work. As a test you can hard code the string used in message.reply() to a known nickname and it should provide the results you expect.

Brettski
  • 19,351
  • 15
  • 74
  • 97
  • Yeah, it would work but it will still be a string. I want it to be clickable. I didn't explained it very well but in discord.js, if I use a variable that represents an user, it will be recognized by the discord client and it will be written as a mention where we can click on the name to see infos about the member, or the channel, etc. – JusteThom Feb 25 '21 at 07:43
  • What is it that you need to pass back to Discord? Can you show an example of what it is expecting? A reference to the place in the Discord.js api (discord.js.org) may be helpful as well. – Brettski Feb 25 '21 at 13:32
  • Sorry for the response time. I edited my question in the last part. – JusteThom Feb 25 '21 at 19:19
  • Seeing that you are sending `messsage.reply()` a string I don't see any reason why using a replace wouldn't work. You will be sending the message with the Nickname included in the string, no different than what the literal template is doing. – Brettski Feb 25 '21 at 20:04
  • Because if I just send it as a string, the name or channel or else will not be clickable and I want it to be. The replace method is simple of course but it just works with strings. – JusteThom Feb 25 '21 at 20:25
  • Maybe I don't understand you as I should be. Can you provide a code you would write? Thanks for your time btw. – JusteThom Feb 25 '21 at 20:40
  • @JusteThom What I am saying is that there is no difference between `message.reply('hi @brettski')` and `message.reply(`hi ${message.member.nickname}`)` where message.member.nickname is @brettski – Brettski Feb 26 '21 at 13:15
  • But there is! Because in your first example, it does not make the "@brettski" a clickable name. It's just printing the name as a string. – JusteThom Feb 26 '21 at 17:31
  • But there it is? I don't understand. By the way, what is the 'exact' value of `message.nickname`? – Brettski Feb 26 '21 at 21:44
  • This SO answer mentions that the user id needs to be used: https://stackoverflow.com/a/46081585/5836. Perhaps replace the token with `user.toString()`? Which it looks like will return the userId. If you like to setup an Activity at https://THAT.us, I will be more than happy to jump on and work through this live. I am sure we can figure it out. – Brettski Feb 26 '21 at 21:50
  • Thanks for your time Brettski, I think I figured something out that I will put in here after testing it out. I have to go to sleep, it's late here in France. :) – JusteThom Feb 26 '21 at 21:57
0

Ok so, @Brettski put me on the right track with the fact that I can use users' or channels' IDs directly as strings within the message and between specific tags.

To reuse the example in my question, I can use this syntax to mention an user :

"<@idOfTheUser>"

What I did is if I want to mention the author of the command in my reply, I put the text in my mongoDB document as so :

const helloCommand = {
  "command": "hello",
  "content": "Hello $member$!"
};

Then, in my code, I can simply use :

message.reply((retrieveReply.content).replace(/\$member\$/g, `<@${message.member.id}>`))

Of course, I wanted to use more properties so I did a function like :

const messageContentReplace = function(content) {
    const replaceableStrings = {
                "$member$": `<@${message.member.id}>`,
                "$memberName$": message.member.displayName,
                "$guild$": message.guild.name
    }
    return content.replace(/(\$member\$)|(\$memberName\$)|(\$guild\$)/g, match => {
        return replaceableStrings[match];
});

(in reality, I did something more complexe because I use an embed message as my response with multiple fields etc... See here about embeds)

JusteThom
  • 80
  • 1
  • 3
  • 9