I have a node.js app that I have created a discord bot to interact with. I would like it so that if a particular event happens with my node.js app that it will send a message to a particular channel in my discord server.
This is my first time using discord.js. However, my thought was to create a function that I can call to send my messages. However, it would seem that I need to wait for my client to be ready first.
Alternatively, would I just have to instantiate a new client every time I want to send a message and wait for it to come available before I send the message I want?
I feel like there has to be a better way... Is there a clean way that I can set up this basic discord bot that I can just call a function to send a message from anywhere within my app?
Here is the code that I have now:
import { Client, Events, GatewayIntentBits } from "discord.js";
import { botToken, CHANNEL_ID } from "../../config.js";
const client = new Client({ intents: [GatewayIntentBits.Guilds] }); // Create a new client instance
// When the client is ready, run this code (only once)
// We use 'c' for the event parameter to keep it separate from the already defined 'client'
client.once(Events.ClientReady, c => {
console.log(`Ready! Logged in as ${c.user.tag}`);
client.channels.cache.get(CHANNEL_ID).send("Hello!");
});
// Log in to Discord with your client's token
client.login(botToken);
"I would like it so that if a particular event happens with my node.js app that it will send a message to a particular channel in my discord server."
It sounds like you're looking for webhooks. Webhooks are a way for an external source, such as your Node.js app, to send messages to a Discord channel without having to log in as a bot. Instead of using a Discord bot, you can use a webhook to send messages to a channel as if they were posted by a bot.
Using a webhook is simple; you just need to make an HTTP POST request to a URL provided by Discord, with the message you want to send in the body of the request. Discord will then post that message to the specified channel.
This is useful in cases where you want to receive notifications from your app in a Discord channel, or simply want to send messages to a channel without having to log in as a bot. It's a clean and efficient way to integrate your app with Discord.
Here is an example of a sendMessage function. It takes two arguments, payload and webhookUrl. If the payload is not a string, it is assumed to be an object that conforms to the Discord webhook format and will be used as is.
function sendMessage(payload, webhookUrl) {
const data = typeof payload === 'string' ? { content: payload } : payload;
return new Promise((resolve, reject) => {
fetch(webhookUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
})
.then((response) => {
if (!response.ok) {
reject(new Error(`Could not send message: ${response.status}`));
}
resolve();
})
.catch((error) => {
console.error(error);
reject(error);
});
});
}
If you're using node.js v18+, you can use the built-in fetch, if not, you'll need to install a library like node-fetch, axios, or something else.
Here is an example how you could use it:
// send a simple message
sendMessage('Hello from my app!', WEBHOOK_URL).catch((error) =>
console.error(error),
);
// send a message, but change the username and the avatar
sendMessage(
{
content: 'Hello from my app!',
avatar_url: 'https://i.imgur.com/KEungv8.png',
username: 'Test Hook',
},
WEBHOOK_URL,
).catch((error) => console.error(error));
// send an embed and change the user again
sendMessage(
{
avatar_url: 'https://i.imgur.com/ofPowdD.png',
username: 'Cattian Hook',
embeds: [
{
title: 'Cats, cats, cats',
color: 0xe7d747,
thumbnail: {
url: 'https://i.imgur.com/2bvab7y.jpeg',
},
},
],
},
WEBHOOK_URL,
).catch((error) => console.error(error));
And here is the result:
If you're already using discord.js, you can use the WebhookClient:
import { WebhookClient } from 'discord.js';
const webhookClient = new WebhookClient({
url: WEBHOOK_URL,
});
webhookClient.send({
content: 'Discord.js webhook test',
username: 'DJS Webhook',
avatarURL: 'https://i.imgur.com/KEungv8.png',
});
To create a webhook, you'll need to go to your discord server settings and click on APPS => Integrations. Then click on the Create Webhook button. There you can select the channel where you want the messages to be sent.
Once the webhook is created, you can click the Copy webhook URL button and use that URL.
Related
I'm following the Discord.js guide to create slash commands for my bot, but I'm stuck at this point here:
https://discordjs.guide/interactions/slash-command-permissions.html#user-permissions
There are two things I can't figure out:
where I'm supposed to write this block of code I linked above,
how I'm supposed to find each command's ID
I'm creating my commands using the SlashCommandBuilder and a deploy-commands.js script as described here:
https://discordjs.guide/creating-your-bot/creating-commands.html#command-deployment-script
and here:
https://discordjs.guide/interactions/registering-slash-commands.html#guild-commands
If you can help me on either one of these two things, that would be great!
Thanks!
I've looked into the codes of the #discordjs/builders and #discordjs/rest and there is no way to set custom permissions with these packages. What you can do is create the slash commands with the Discord.js package. By creating them in the Discord.js package the id of the slash command will be returned in the fullfilled Promise. With this id you can set the permissions for the command. The only problem by doing it in this way is that it can take a while before the slash commands are working again. Here is an example:
const { Client } = require('discord.js');
const client = new Client({intents: ['Your Intents here']});
client.once('ready', () => {
client.application.commands.create({
name: 'your_command',
description: "Your command's description"
}).then(id => {
client.application.commands.set({command: id, permissions: [
id: 'some_user_id',
type: 'USER',
permission: false // Can not use the slash command
]}).catch(console.log);
});
});
client.login('Your token here');
I thought that there is another way to do it, but I'm not pretty sure. If I'm right you can also fetch all the commands after you've refreshed them with the #discordjs/builders and #discordjs/rest packages. Once you've fetched them the Promise will return a Collection once it's got fullfilled. In the Collection will be all the ids of all the slash commands which you can use to set the permissions. So if this theory works, this will be the example:
const { Client } = require('discord.js');
const client = new Client({intents: ['Your Intents here']});
client.once('ready', () => {
// Your refresh code here
client.application.commands.fetch().then(collection => {
collection.forEach(command => {
if(command.name === `The specified command name`){
client.application.commands.permissions.set({command: command.id, permissions: [
{
id: 'some_user_id',
type: 'USER',
permission: false // Can not use the slash command
}
]}).catch(console.log);
}
});
}).catch(console.log);
});
client.login('Your token here');
I am currently sending a message to the slack channel using below function. But I want to send a private message which should be visible to selected member of the slack channel.
How can I do that ?
async function sendSlackMessage() {
const url = 'https://slack.com/api/chat.postMessage';
const inputBody = {
channel: "Slack_Channel_ID",
text: `Hey Welcome to the slack`,
};
const slackHeaders = {
'Content-Type': 'application/json;charset=utf-8',
'Authorization': 'Slack_Token',
};
const slackRes = await axios.post(url, inputBody, { headers: slackHeaders });
console.log(slackRes)
}
sendSlackMessage()
Solution using Boltjs for Javascript:
To send a private message visible only to a specific user on a channel on Slack, we may use a different method chat.postEphemeral from Bolt for JavaScript. Using the above method you can send an ephemeral message to a users in a channel that is visible only to a specific user that you can choose to display.
Note: I have offered my solution as simple blocks, you need to encapsulate it within the function you need this feature to operate on.
Requirements:
For using the chat.postEphemeral you are required to send the following arguments to work.
token: Slack_Bot_Token {Authentication token bearing required scopes. Tokens should be passed as an HTTP Authorization header or alternatively, as a POST parameter.}
channel: {Channel, private group, or IM channel to send message to. Can be an encoded ID, or a name}
user: {id of the user who will receive the ephemeral message. The user should be in the channel specified by the channel argument.}
text: "Anything you need here"
blocks: "Pack and Send from Block Kit Message Builder, not necessary though"
Note:
extract the channel id from the function or pass it as args to the async function
extract the user id from the function or pass it as args to the async function
text field is not enforced when blocks are used.
Methods Access: app.client.
chat.postEphemeral
Required Scopes in Slack App:
Bot Tokens
User Tokens
Example Code:
// Building the args object from body (Can also use, action, context, and few other slack parameters from Bolt API for js)
const args = {
user: body.user.id,
channel: body.container.channel_id,
team: body.user.team_id,
token: body.user.id,
trigger: body.trigger_id,
url: body.response_url,
};
Slack App Code:
try {
// Call the chat.postEphemeral method using the WebClient
const result = await client.chat.postEphemeral({
channel: channelId,
user: userId,
token: userToken,
text: "Shhhh only you can see this :shushing_face:"
});
console.log(result);
}
catch (error) {
console.error(error);
}
Documentation:
View this documentation for more Information: Slack API for Methods
Check here to Create Message Block Kits for Slack: Slack Block Kit Builder
I am trying to give my Firebase Cloud Messaging notification a tag property on Android as described here and here so I can replace notifications that have been received if necessary.
I am using React Native, React Native Firebase and the ConnectyCube API. ConnectyCube works with Firebase to handle user management and push notifications - I have all of this working.
What I can't figure out is how to format my payload object to include optional properties such as tag as the documentation is fairly cryptic. I am successfully sending a message which is included in the message property, and in the ConnectyCube docs you will see that the iOS optional property of badge is just another property in the payload object, but in the case of tag for android, the below code is not working:
export const sendNotification = async (calleeId, callLength, tagUUID) => {
const callersUserName = await getUserNameFromStorage();
const payload = JSON.stringify({
message: callersUserName + '-' + callLength,
tag: tagUUID,
});
const pushParameters = {
notification_type: 'push',
user: { ids: [calleeId] }, // recipients.
environment: 'production', // environment, can be 'production'.
message: ConnectyCube.pushnotifications.base64Encode(payload)
};
ConnectyCube.pushnotifications.events.create(pushParameters, function (error, result) {
});
setTimeout(() => {
const payload2 = JSON.stringify({
message: 'replacement-notification',
tag: tagUUID,
});
const pushParameters2 = {
notification_type: 'push',
user: { ids: [calleeId] }, // recipients.
environment: 'production', // environment, can be 'production'.
message: ConnectyCube.pushnotifications.base64Encode(payload2)
};
ConnectyCube.pushnotifications.events.create(pushParameters2, function (error, result) {
});
}, 3000)
}
When push notifications have the same tag, each notification will be replaced with the newer one which I am trying to mimic with setTimeout.
I am receiving both messages but the first is not being replaced with the second!
Any help is much appreciated! :)
tag is a payload key for Notification type pushes, but ConnectyCube sends all pushes as Data.
With Data pushes there is full control over what to do with notification (to show or not to show), so there is a way to add a code in app to hide an existing notification and then show a new one once a Data push received
I have a use case where i would like to send a slack user a message of which i know the id of a notification once and a while using the bot framework.
Right now i have the following:
server.get("/api/notify", async (req, res) => {
await adapter.createConversation(conversationReference, async turnContext => {
await turnContext.sendActivity("proactive hello");
});
res.setHeader("Content-Type", "text/html");
res.writeHead(200);
res.write(
"<html><body><h1>Proactive messages have been sent.</h1></body></html>"
);
res.end();
});
where a conversation reference looks like:
const conversationReference = {
user: { id: "ID3:ID2", name: "user1" },
bot: { id: "ID1:ID2", name: "bot1" },
conversation: {
isGroup: false,
id: "ID1:ID2:ID3",
conversationType: "slack",
tenantId: "",
name: ""
},
channelId: "slack",
serviceUrl: "https://slack.botframework.com/"
};
But it only works if the user has talked to the bot since the bot has booted. But after a restart this won't work anymore until the user initiates a conversation.
When I try to send a pro active message after the bot rebooted and the user hasn't started a conversation after that i get the following exception:
UnhandledPromiseRejectionWarning: Error
at new RestError (/usr/app/node_modules/#azure/ms-rest-js/dist/msRest.node.js:1397:28)
at /usr/app/node_modules/#azure/ms-rest-js/dist/msRest.node.js:1849:37
at process._tickCallback (internal/process/next_tick.js:68:7)
My question is: How can i persist this state, so i can still send pro active messages after a reboot?
Aha! This part of your question is the key:
But it only works if the user has talked to the bot since the bot has booted. But after a restart this won't work anymore until the user initiates a conversation.
This is almost definitely a TrustServiceUrl Issue. Please see this answer for additional context.
Basically, on reboot, the bot forgets that it's okay to talk to that user. You need to "Trust" the ServiceUrl of the user/activity to ensure the bot knows it's okay to message them.
I have been working on a websocket client application.
I am currently using the ws client library, because it is easy to add some headers (I need this for authentication purposes). I have made a successful connection to the server, but now I need to connect to a specific channel.
Current code:
const WebSocket = require('ws');
var option = {
headers: {
api_key: 'xxxxx'
}
};
// I know this is not a correct url, but I removed it for security reasons
const url = '../websocket/';
const wss = new WebSocket(url, option);
wss.on('open', () => {
console.log("Connection is succesfull");
wss.on('message', message => {
console.log(message);
});
});
When I ran the code it prints the "Connection succesfull", but now I want to connect to a channel called /people. How can I do this.
I have tried several things like:
Changed the url websocket/people.
This doesn't work because it first needs to authenticate to user, before making a connection to a channel
Changed the url to websocket/?people.
I don't get an error, but I also don't get response back when something is send to this channel.
Add this in the open function:
wss.on('/people', message => {
console.log(message);
});
I don't get an error, but I also don't get response back when something is send to this channel.
For the record. I only have access to the documentation and not to the server.