Markoudstaal / node-red-contrib-discord-advanced

Recieve, send, edit and delete Discord messages in node-red.
MIT License
46 stars 16 forks source link

Discord Interaction node #20

Closed javis86 closed 2 years ago

javis86 commented 2 years ago

Hi people! I´ve been working on a node to handle the "interactionCreate" event on discord. The node adds the capability of responding button click events, menu select events and Replying to slash commands. I added Component field to the discordMessageManager in order to create messages with buttons and menu selects also.

The Aproach to handling the flow in Node-red

In the case of buttons and menu selects, the node defers the update in order to edit the message with de DiscordMessageManager node, but for Slash Commands, the only way i found was replying inside the node. I didn't get working on passing inside the message the original interactionObject and replying in the subsecuent nodes.

I have several examples to add later to the repo for handling the scenarios. image

javis86 commented 2 years ago

I attach flow for testing

[
    {
        "id": "79ed38ee433c1da7",
        "type": "discordMessageManager",
        "z": "f6f2187d.f17ca8",
        "name": "",
        "channel": "",
        "token": "",
        "x": 850,
        "y": 420,
        "wires": [
            [
                "5f78ed9d5208fc9d"
            ]
        ]
    },
    {
        "id": "5f78ed9d5208fc9d",
        "type": "debug",
        "z": "f6f2187d.f17ca8",
        "name": "",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 1090,
        "y": 420,
        "wires": []
    },
    {
        "id": "243f616e9c249f70",
        "type": "function",
        "z": "f6f2187d.f17ca8",
        "name": "Add Button Components",
        "func": "\nmsg.payload = \"-- Take a decision --\";\nmsg.components = \n    [\n        {\n            \"type\": 1,\n            \"components\": [\n                {\n                    \"type\": 2,\n                    \"label\": \"Option 1\",\n                    \"style\": 3,\n                    \"custom_id\": \"click_opt1\"\n                },\n                {\n                    \"type\": 2,\n                    \"label\": \"Option 2\",\n                    \"style\": 4,\n                    \"custom_id\": \"click_opt2\"\n                }\n            ]\n\n        }\n    ];\nmsg.channel = \"954798049358671922\"; //test\n\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 530,
        "y": 400,
        "wires": [
            [
                "79ed38ee433c1da7"
            ]
        ]
    },
    {
        "id": "81b904b97a19920e",
        "type": "inject",
        "z": "f6f2187d.f17ca8",
        "name": "Inject message in channel",
        "props": [
            {
                "p": "channel",
                "v": "954798049358671922",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "x": 270,
        "y": 400,
        "wires": [
            [
                "243f616e9c249f70"
            ]
        ]
    },
    {
        "id": "46e331e958419bcb",
        "type": "inject",
        "z": "f6f2187d.f17ca8",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 240,
        "y": 800,
        "wires": [
            [
                "4e972a45ed68ae9a"
            ]
        ]
    },
    {
        "id": "082755a31f38cc5d",
        "type": "debug",
        "z": "f6f2187d.f17ca8",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 730,
        "y": 800,
        "wires": []
    },
    {
        "id": "e2e8b84b4c2888d3",
        "type": "discordMessageManager",
        "z": "f6f2187d.f17ca8",
        "name": "",
        "channel": "",
        "token": "",
        "x": 750,
        "y": 580,
        "wires": [
            [
                "644d832076a1b4df"
            ]
        ]
    },
    {
        "id": "898ae628b4296992",
        "type": "function",
        "z": "f6f2187d.f17ca8",
        "name": "Edit original message",
        "func": "let newMessage = {};\nnewMessage.payload = `Thanks for clicking option *${msg.payload.customId}*, <@!${msg.payload.user.id}>`;\nnewMessage.action = \"edit\";\nnewMessage.message = msg.payload.message.id;\nnewMessage.channel = msg.payload.message.channelId;\n\nreturn newMessage;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 480,
        "y": 540,
        "wires": [
            [
                "e2e8b84b4c2888d3"
            ]
        ]
    },
    {
        "id": "644d832076a1b4df",
        "type": "debug",
        "z": "f6f2187d.f17ca8",
        "name": "",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 990,
        "y": 580,
        "wires": []
    },
    {
        "id": "7ac2d6065bb625c0",
        "type": "function",
        "z": "f6f2187d.f17ca8",
        "name": "Add Menu Interaction",
        "func": "\nmsg.pamsgyload = \"💩 Select focking option\";\nmsg.components =  \n    [\n        {\n            \"type\": 1,\n            \"components\": [\n                {\n                    \"type\": 3,\n                    \"custom_id\": \"class_select_1\",\n                    \"options\":[\n                        {\n                            \"label\": \"Rogue\",\n                            \"value\": \"rogue\",\n                            \"description\": \"Sneak n stab\",\n                            \"emoji\": {\n                                \"name\": \"rogue\",\n                                \"id\": \"625891304148303894\"\n                            }\n                        },\n                        {\n                            \"label\": \"Mage\",\n                            \"value\": \"mage\",\n                            \"description\": \"Turn 'em into a sheep\",\n                            \"emoji\": {\n                                \"name\": \"mage\",\n                                \"id\": \"625891304081063986\"\n                            }\n                        },\n                        {\n                            \"label\": \"Priest\",\n                            \"value\": \"priest\",\n                            \"description\": \"You get heals when I'm done doing damage\",\n                            \"emoji\": {\n                                \"name\": \"priest\",\n                                \"id\": \"625891303795982337\"\n                            }\n                        }\n                    ],\n                    \"placeholder\": \"Choose a class\",\n                    \"min_values\": 1,\n                    \"max_values\": 3\n                }\n            ]\n        }\n    ];\nmsg.channel = \"954798049358671922\"; //test\n\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 520,
        "y": 440,
        "wires": [
            [
                "79ed38ee433c1da7"
            ]
        ]
    },
    {
        "id": "153571b3cd04ff78",
        "type": "discordInteraction",
        "z": "f6f2187d.f17ca8",
        "name": "Menu Selected Interaction",
        "token": "",
        "interactionType": "selectMenu",
        "custom_id": "class_select_1",
        "commandResponse": "",
        "x": 210,
        "y": 600,
        "wires": [
            [
                "edd5bfac50a6e20b",
                "0a20329bcc50d154"
            ]
        ]
    },
    {
        "id": "edd5bfac50a6e20b",
        "type": "function",
        "z": "f6f2187d.f17ca8",
        "name": "Edit original message",
        "func": "let newMessage = {};\nnewMessage.payload = `Thanks for selecting options *${msg.payload.values.join(\", \")}*, <@!${msg.payload.user.id}>`;\nnewMessage.action = \"edit\";\nnewMessage.message = msg.payload.message.id;\nnewMessage.channel = msg.payload.message.channelId;\n\nreturn newMessage;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 480,
        "y": 600,
        "wires": [
            [
                "e2e8b84b4c2888d3"
            ]
        ]
    },
    {
        "id": "5bc45799f6c36255",
        "type": "discordInteraction",
        "z": "f6f2187d.f17ca8",
        "name": "Button Interaction",
        "token": "",
        "interactionType": "button",
        "custom_id": "click_opt1,click_opt2",
        "commandResponse": "",
        "x": 220,
        "y": 540,
        "wires": [
            [
                "898ae628b4296992",
                "0a20329bcc50d154"
            ]
        ]
    },
    {
        "id": "4e972a45ed68ae9a",
        "type": "function",
        "z": "f6f2187d.f17ca8",
        "name": "create slash command",
        "func": "const data = new discordjsBuilders.SlashCommandBuilder()\n\t.setName('echo1')\n\t.setDescription('Replies with your input!')\n\t.addStringOption(option =>\n\t\toption.setName('input')\n\t\t\t.setDescription('The input to echo back')\n\t\t\t.setRequired(true));\n\t\t\t\nconst clientId = flow.get(\"clientId\");\nconst guildId = flow.get(\"guildId\");\nconst token = flow.get(\"botToken\");\n\nconst rest = new discordjsRest.REST({ version: '9' }).setToken(token);\n\n(async () => {\n\ttry {\n\t\tconsole.log('Started refreshing application (/) commands.');\n\n\t\tawait rest.put(\n\t\t\tdiscordApiTypesV9.Routes.applicationGuildCommands(clientId, guildId),\n\t\t\t{ body: [data] },\n\t\t);\n\n\t\tconsole.log('Successfully reloaded application (/) commands.');\n\t\tnode.send({\n\t\t    payload: 'Successfully reloaded application (/) commands.'\n\t\t})\n\t} catch (error) {\n\t\tconsole.error(error);\n\t}\n})();",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [
            {
                "var": "discordApiTypesV9",
                "module": "discord-api-types/v9"
            },
            {
                "var": "discordjsRest",
                "module": "@discordjs/rest"
            },
            {
                "var": "discordjsBuilders",
                "module": "@discordjs/builders"
            }
        ],
        "x": 500,
        "y": 800,
        "wires": [
            [
                "082755a31f38cc5d"
            ]
        ]
    },
    {
        "id": "0a20329bcc50d154",
        "type": "debug",
        "z": "f6f2187d.f17ca8",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 450,
        "y": 720,
        "wires": []
    },
    {
        "id": "d4ec417b2cb6073e",
        "type": "discordInteraction",
        "z": "f6f2187d.f17ca8",
        "name": "Command Interaction",
        "token": "",
        "interactionType": "command",
        "custom_id": "ping",
        "commandResponse": "Comando en proceso, espere!",
        "interactionObject": false,
        "x": 200,
        "y": 680,
        "wires": [
            [
                "0a20329bcc50d154",
                "d6a6524da3e8e716"
            ]
        ]
    },
    {
        "id": "993a8fffa4682bc3",
        "type": "discordMessageManager",
        "z": "f6f2187d.f17ca8",
        "name": "",
        "channel": "",
        "token": "",
        "x": 810,
        "y": 660,
        "wires": [
            []
        ]
    },
    {
        "id": "d6a6524da3e8e716",
        "type": "function",
        "z": "f6f2187d.f17ca8",
        "name": "New message for the reply",
        "func": "let newMessage = {};\nnewMessage.payload = `<@!${msg.payload.user.id}>, Thanks for using command *${msg.payload.commandName}*`;\nnewMessage.channel = msg.payload.channelId;\nreturn newMessage;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 500,
        "y": 660,
        "wires": [
            [
                "993a8fffa4682bc3"
            ]
        ]
    },
    {
        "id": "1d2411688f4d1e68",
        "type": "inject",
        "z": "f6f2187d.f17ca8",
        "name": "Inject message in channel",
        "props": [
            {
                "p": "channel",
                "v": "954798049358671922",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "x": 270,
        "y": 440,
        "wires": [
            [
                "7ac2d6065bb625c0"
            ]
        ]
    }
]
javis86 commented 2 years ago

i found the way to defers the reply to a slash command, in order to perform some actions in the flow

if(interaction.isCommand())
{
    message.payload.options = Flatted.parse(Flatted.stringify(interaction.options));
    message.payload.replyMessage = Flatted.parse(Flatted.stringify(await interaction.fetchReply()));  // Fetching reply         
}

in order to edit message in subsecuent node, ex:

let newMessage = {};
console.log("hola");
const optionString = msg.interactionObject.options.getString('email');
newMessage.payload = `<@!${msg.payload.user.id}>, APIKEY Generado erqw1312312312312saesd para ${optionString}`;
newMessage.action = "edit";
newMessage.message = msg.payload.replyMessage.id;
newMessage.channel = msg.payload.channelId;

return newMessage;

image

itsvrl commented 2 years ago

@javis86 Can I use your fork until the maintainers does things in this repo?

This is amazing! Thanks!

itsvrl commented 2 years ago

Also @javis86 I know this is totally unrelated to the PR, but do you know how to work with Discord Threads?

javis86 commented 2 years ago

@javis86 Can I use your fork until the maintainers does things in this repo?

This is amazing! Thanks!

Of course you can!

javis86 commented 2 years ago

Wiki information was updated too. Merge it from https://github.com/javis86/node-red-contrib-discord-advanced.wiki.git