sending pro active messages to slack using bot framework - javascript

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.

Related

How to create a function to send messages

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.

Delete Button/Component of a specifc message (discord.js)

I am now trying in vain to remove buttons from a bot message. I have created a command for this purpose. When executing it, it should at best remove a specific, the last or at least all buttons that are on a specific message.
I have tried various things, but in all attempts the buttons are not removed.
module.exports = {
category: 'Utilities',
description: 'Delete Buttons',
permissions: ['ADMINISTRATOR'],
callback: async ({ client, message }) => {
const channel = client.channels.cache.get('the channel id')
channel.messages.fetch('the message id').then(msg => msg.edit({components: []}));
console.log(msg)
}
}
When I try to do this, I get the following console error:
TypeError: Cannot read properties of undefined (reading 'messages')
When I try this, I neither get a console log, nor does the bot do anything ...
const { Client, Message } = require("discord.js");
module.exports = {
category: 'Utilities',
description: 'Delete Buttons',
permissions: ['ADMINISTRATOR'],
callback: async ({ client, message }) => {
client.on('ready', async () => {
const channel = client.channels.cache.get('the channel id')
channel.messages.fetch('the message id').then(msg => {
msg.edit({ components: [] })
});
},
)}
}
Maybe one of you knows a solution or an idea. :)
Thanks a lot!
When I try this, I neither get a console log, nor does the bot do anything
The second example does not do anything because you are creating a ready event handler on running your command. Meaning, it's waiting for the bot to once again be "ready", i.e. the state of having logged in to the API as it does on startup. But your bot is already ready, and will not become ready again until the next time it restarts, so nothing will happen.
As for the first example, the error you are getting suggests channel is undefined, meaning either:
A) You have the incorrect channel ID
- OR -
B) The specified channel is no longer in the channel cache
If you are 100% sure the ID is correct, we can assume the issue you are having is the latter (the channel not being in the cache). There are many ways to solve this, but one simple way is to simply fetch the channel similar to how you are trying to fetch the message. Here's an example:
const channel = await client.channels.fetch('the channel id');
const msg = await channel.messages.fetch('the message id');
msg.edit({components: []});
That should fix the issue. If it doesn't, then the issue is much more complicated and not enough information has been provided. Also note that in the above example, I swapped Promise syntax for async/await since you are using an async function anyways; I did this just to make this answer more readable, you can choose whichever format you prefer.

New Discord Slash Commands

Recently, discord added support for slash commands for your own application. I read through the documentation for it, and I've tried to search for some videos (however the feature did JUST come out) but I do not understand what I actually have to do to get it working. I am using WebStorm (js, node.js). Has anyone successfully made a slash command, and if so, how?
Documentation
You can use the regular discord.js, by now its v12.5.1.
This is just a sample, but worked for me.
const Discord = require('discord.js');
const client = new Discord.Client();
client.on('ready', () => {
client.api.applications(client.user.id).guilds(YOUR_GUILD_ID_HERE).commands.post({
data: {
name: "hello",
description: "hello world command"
// possible options here e.g. options: [{...}]
}
});
client.ws.on('INTERACTION_CREATE', async interaction => {
const command = interaction.data.name.toLowerCase();
const args = interaction.data.options;
if (command === 'hello'){
// here you could do anything. in this sample
// i reply with an api interaction
client.api.interactions(interaction.id, interaction.token).callback.post({
data: {
type: 4,
data: {
content: "hello world!!!"
}
}
})
}
});
});
client.login(token);
Of course you can have options, see documentation...
IDE won't register the new code...at least my php storm currently does'nt :)
However, its important to give the bot the correct permissions to use this type of command!
So go to Discord.com/developers, select your application, go to OAuth2 and select
application.commands
from the scope section. This should be at the bottom of the middle column. You should select bot as well and under Bot Permissionsyou set some other specific permissions. Then use that new invite link to reinvite the bot.
Without application.commands permission, the command won't work and you will receive some error like Missing Access or some similar.
IMPORTANT THINGS
Use .guilds('11231...').comma to test these commands. When not using this, it takes round about 1h to get active. Using it will activate it immediately.
Give the bot the correct permission. application.commands
Hi I don't usually work with discord.js but I was able to find some good documentation on this. You should be able to do it like this with "client" defined.
const interactions = require("discord-slash-commands-client");
const client = new interactions.Client(
"you unique bot token",
"your bots user id"
);
If the client is defined as shown, then a /command should work if defined like this.
// will create a new command and log its data.
//If a command with this name already exist will that be overwritten.
client.createCommand({
name: "your command name",
description: "description for this command",
})
.catch(console.error)
.then(console.log);

How do you set a custom status in discord.js? [duplicate]

According to new update youu can set your own custom status without playing or listening this is't added in documentation yet what can I do here. There is no option to set custom activity in the discord.js git see the image stable/src/util/Constants.js here too
discord status image
You can get custom status to appear on a bot, but it won't be able to say anything. According to a Github issue on discord-api-docs, More specifically, this issue, and even more specifically, this comment on that issue. You can basically set the activity type to 4 for the client's cs by using the ClientUser.setActivity() method, and check it's full profile to see that custom status is technically available for bots.
bot.user.setActivity(`this won't appear in the bot's custom status!`, {type: 4})
You can use this code
client.on("ready", () =>{
console.log(`Logged in as ${client.user.tag}!`);
client.user.setPresence({
status: "online", //You can show online, idle....
game: {
name: "Using !help", //The message shown
type: "STREAMING" //PLAYING: WATCHING: LISTENING: STREAMING:
}
});
});
Discord libaries should only be used for bots, using the API for user-account clients violates Discords Terms of Service as this is considered as API Abuse.
Moreover the recent announcement does not apply for bots, for many months Bots are able to set custom presence, including listening and watching.
To set this custom presence, the custom activity, take a look at the Discord.js Documentation here
You can set a Bot's custom activity, but I don't think you can add images or buttons to it.
Here's the closest thing I've got:
const updatePresence = async (client, state) => {
// Set the presence
const activity = {
name: 'Depression',
type: 'STREAMING',
details: 'discord.gg/inviteCode',
state: state,
timestamps: {
start: Date.now(),
},
};
client.user.setPresence({
pid: process.pid,
activity: activity,
status: 'online',
});
};

Discord.js open DM stream with certain user by ID

Is there any option to open a DM stream or something like that with certain user by ID.
What do I mean?
It's like ssh terminal function, when I enter command to bot like:
me: DM#{user.id}
bot: {connection established message}
/**--------------------------------------------
# and for now, every message from me, bot will deliver to {user.id} until I
# don't send `{message_stop}` command, for example:
----------------------------------------------*/
me: "Hi, how are you?" -> client.fetchUser('user_id')
me: "Are you ok?"
me: -stop command
Without using -prefix every time in the beginning of each message?
Or probably implement this function like that:
Creating a channel on server(guild) and every message that I write there, will be delivered to a certain user?
The actual question is, if it's possible then how should I manipulate with client.on('trigger') state to achieve such result?
As for now, I have written the command, which DMs message to user, based on prefix in the begging of each message like:
me: dm#user_id: Hello!
me: dm#user_id: How are you?
Probably I should store session data (user.id) somewhere, to help bot understood that I want to communicate only with specific user?
I receive this answer in Discord.js discord. So it's possible with MessageCollector. I'm using this guide for building my bot with dynamic commands. So here is my code:
module.exports = {
name: 'direct',
description: 'Ping!',
args: true,
execute(message, args, client) {
const u = message.content.split(/(?<=^\S+)#/)[1];
console.log(message.content);
message.channel.send(`Connection established: ${message.author.id}`);
const filter = m => m.content.includes('discord');
const collector = message.channel.createMessageCollector(filter, {
maxMatches: 10,
time: 30000,
errors: ['time']
});
collector.on('collect', m => {
console.log(`${Date.now()} Collected ${m.content}`);
client.fetchUser('user_id').then((user) => {
return user.send(`${m.content}`);
});
});
collector.on('end', collected => {
message.channel.send(`Connection cancelled with ${message.author.id}. You send ${collected.size} messages`);
console.log(`${Date.now()} Collected ${collected.size} items`);
if (collected.size === 0) {
console.log('here');
}
});
}
};
You just enter command name (direct) in this case, and then every message that incudes discord DMs to user_id 10 times or for 30 seconds. You could use arguments, if you want to change it. Hope it helps anyone.
Also, I'm very thankful to Monbrey#4502 from Discord.js community for providing this information.

Categories

Resources