Deleting certain file type attachments in a specific channel? - discord

I'm trying to add certain files from being posted to the general channel as we have designated channels for certain attachments, clips, videos, music, etc. I'm fine on getting the bot to recognize links, however, having a hard time getting it to recognize attachments, more specifically, .mp4 attachments.
I added a whitelist of acceptable attachments in an array, then try and check the message author attachment to see if it's okay to post, if its an .mp4 it should be deleted.
The try function is within the on_message event decorator.
whiteList = ['bmp','jpeg','jpg','png']
try:
for attachment in message.attachments:
#Get general channel ID
channel = client.get_channel(521376573245358081)
if message.channel is channel and attachment['filename'].split('.')[-1] not in whiteList:
await message.delete()
botsMessage = await channel.send("{0.mention} Please refrain from posting videos in General. You may post them in #videos".format(message.author))
await asyncio.sleep(5)
await botsMessage.delete()
except:
print('Unknown error')
No error comes of this as when I test this the attachment remains, the bot passes over the function and prints the console message (used for debugging to make sure the code reaches that far). Any suggestions?

attachment['filename'].split('.')[-1]
You treated attachment as an dictionary that has a key called filename.
You should have treated attachment as an object that has a property called filename as follows:
attachment.filename.split('.')[-1]
Also, you should break the loop whenever the message is deleted,
# ...
botsMessage = await channel.send("{0.mention} Please refrain from posting videos in General. You may post them in #videos".format(message.author))
await asyncio.sleep(5)
await botsMessage.delete()
break
# ...
in the event that the user have sent mutiple video files, the loop will still continue even after you delete the message. Which may cause it to try to delete a deleted message
The break statement prevents the above from happening.

Related

How to (and is it possible to) make link markup work in a discord bot message?

What I'm trying to achieve is simple:
send a message like links: [link1](https://example.com/1), [link1](https://example.com/2) via a bot
get it displayed as "links: link1, link1"
In a ephemeral follow up, it works as expected:
const content = "links: [link1](https://example.com/1), [link1](https://example.com/2)"
await interaction.followUp({
ephemeral: true,
content,
})
But when I send this to a public channel like this:
await channel.send(content)
I'm getting it as plain text (links: [link1](https://example.com/1), [link1](https://example.com/2)) except the links are clickable.
Is it possible to get the same result as in an ephemeral message?
I've checked the permissions, there's only "embed" links (which sounds like "allow links in embeds", and not "allow links in messages) and it is enabled anyway on server for everyone, for the bot and in the channel. So what am I missing here?
PS Here they say that "it's only possible if the message was sent with webhook though", but I'm not quite sure what does this mean (can this be different for a public and an ephemeral message?)
You cannot use hyper links in normal messages sent by a bot. You need to use a Webhook. Considering you're using discord.js, see this guide on their documentation. When using something like that it will work as expected
const { WebhookClient } = require('discord.js');
const webhookClient = new WebhookClient({ url: "WEBHOOK_URL" });
webhookClient.send({
content: "[Hello world](https://github.com)",
username: "Webhook Username",
});
Otherwise you may use embeds and send one of these with your bot and the content you wish to have.
Right, so I've found a solution based on suggestion by Krypton. As an embed doesn't look nice and manual creation of a webhook is not an option for me, I've found that I can create a webhook on the go and use it.
After some testing, I've figured that such webhooks are also permanent (although they can be found in another part of settings than the manually created ones); they should be deleted as there's a limit of 15 webhooks – an attempt to create more fails (with DiscordAPIError[30007]: Maximum number of webhooks reached (15)).
Instead of doing
await channel.send(content)
I've put
// a guard, TypeScript requires us to be sure
if(channel instanceof TextChannel) {
const webhook = await channel.createWebhook({
// a message from a webhook has a different sender, here we set its name,
// for instance the same as the name of our bot (likewise, we can set an avatar)
name: "MyBot",
})
await webhook.send(content)
await webhook.delete()
}
This seems to be a minimal code change to get the links working.

Approval/Denial System using Discord.js

DISCORD.JS
Hey! So, recently, I was on a server that contained an amazing bot. That was an approval or denial system. So, what would happen for example, somebody would sign a google form and the google script will send the response via a webhook (I already know that code) in an embed to a private channel named "awaiting-result", now, the bot will automatically add reactions to the message, for example, ✅ and ❌. Then, a staff member will react with either one of those emojis and it will send to two different channels. If the reaction was a ✅, then the bot will remove all reactions from the original message, copy the exact embed from the google form response, and send it to a channel named "accepted-logs" with a message above it "Your log has been accepted by ${person}". If it was an ❌, it will do the exact same thing as the approved one. I have been trying hard, but cant find it. All I ready need is the bot code, not the form script. So basically, you react, copy the exact embed, send to another channel. Itll be very helpful, thanks!
List of useful links:
https://discordjs.guide/popular-topics/reactions.html#unicode-emojis
https://discordjs.guide/popular-topics/collectors.html#reaction-collectors
https://discordjs.guide/popular-topics/embeds.html#using-the-embed-constructor
I'm pretty sure you could just store the embed contents in an Object, then you can wait for the collector to collect a ✅ or ❌, check if the user has admin role (e.t.c), and then find the channel the embed needs to be sent too
let channel = client.channels.cache.find(channel => channel.name === "name");
and then you can send the embed to the channel via
channel.send(embed);
In order to make an embed, you just do:
let embed = new Discord.MessageEmbed();
And then you can add fields to it (see link #3). Then you can simply just do
let approvalChannel = client.channels.cache.find(channel => channel.name = "admin-approval");
approvalChannel.send(embed);
// Code for reaction collector

How to not send bots message edit discord.js

I have a message edit log but I want to stop sending the log if a mobs message was updated, I tried a few codes like
if(bot.oldMessage.content.edit()){
return;
}
It showed and error
cannot read property 'edit' of undefined
I then removed edit then content was undefined. The code for the message update is below.
The Code
module.exports = async (bot, oldMessage, newMessage) => {
let channels = JSON.parse(
fs.readFileSync('././database/messageChannel.json', 'utf8')
);
let channelId = channels[oldMessage.guild.id].channel;
let msgChannel = bot.channels.cache.get(channelId);
if (!msgChannel) {
return console.log(`No message channel found with ID ${channelId}`);
}
if (oldMessage.content === newMessage.content){
return;
}
let mEmbed = new MessageEmbed()
.setAuthor(oldMessage.author.tag, oldMessage.author.displayAvatarURL({dynamic: true}))
.setColor(cyan)
.setDescription(`**Message Editied in <#${oldMessage.channel.id}>**`)
.addField(`Before`, `${oldMessage.content}`)
.addField(`After`, `${newMessage.content}`)
.setFooter(`UserID: ${oldMessage.author.id}`)
.setTimestamp()
msgChannel.send(mEmbed)
}
How would I stop it from sending the embed if a bots message was updated.
Making a really simple check will resolve this issue. In Discord.js there is a user field that tells you if the user is a bot or not.
In fact, it is really recommended you add this in the "onMessage" part of your code as it stops other bots from using your bot, this is to make sure things are safe and no loopbacks/feedbacks happen, either way, you don't want a malicious bot taking advantage of your bot, which can get your bot in trouble too.
Here is what you want to do;
if (message.author.bot) return;
What this code specifically does is check if the message's author is a bot, if it returns true, it will break the code from running, if it returns a false, the code continues running.
You can do the same if you want to listen to bots ONLY by simply adding a exclamation mark before the message.author.bot like this;
if (!message.author.bot) return;
It is also possible to see what other kinds of information something holds, you can print anything to your console. For example, if you want to view what a message object contains, you can print it into your console with;
console.log(message) // This will show everything within that object.
console.log(message.author) // This will show everything within the author object (like ID's, name, discriminators, avatars, etc.)
Go ahead and explore what you can do!
Happy developing! ^ -^
That is really easy to do. All you need to do is check if the author of the message ist a bot and then return if true. You do that like this
if (oldMessage.author.bot) return;

Discord JS v12: How do you get a message's content by it's ID?

I'm relatively new to discord.js, and I've started building a bot project that allows a user to create a message via command, have that message stored in a hidden channel on my private server, and then said message can be extracted through the message ID.
I have the write working and it returns the message ID of the message sent in the hidden channel, but I'm completely stumped on the get command. I've tried searching around online but every method I tried would return errors like "Cannot read property 'fetch' of undefined" or "'channel' is not defined". Here are some examples of what I tried, any help would be appreciated. Note that my args is already accurate, and "args[0]" is the first argument after the command. "COMMAND_CHANNEL" is the channel where the command is being executed while "MESSAGE_DATABASE" is the channel where the targeted message is stored.
let msgValue = channel.messages.cache.get(args[0])
client.channels.cache.get(COMMAND_CHANNEL).send(msgValue.content)
let msgValue = msg.channel.message.fetch(args[0])
.then(message => client.channels.cache.get(COMMAND_CHANNEL).send(msgValue.content))
.catch(console.error);
I even tried using node-fetch to call the discord API itself
const api = require("node-fetch")
let msgValue = api(`https://discordapp.com/api/v8/channels/${MESSAGE_DATABASE}/messages/${args[0]}`)
.then(message => client.channels.cache.get(COMMAND_CHANNEL).send(msgValue.content))
.catch(console.error);
Am I missing something or am I making some sort of mistake?
Edit: Thanks for the help! I finished my bot, it's just a little experimental bot that allows you to create secret messages that can only be viewed through their ID upon executing the command :get_secret_message <message_id>. I posted it on top.gg but it hasn't been approved yet, so in the meantime if anyone wants to mess around with it here is the link: https://discord.com/api/oauth2/authorize?client_id=800368784484466698&permissions=76800&scope=bot
List of commands:
:write_secret_message - Write a secret message, upon execution the bot will DM you the message ID.
:get_secret_message <message_id> - Get a secret message by its ID, upon execution the bot will DM you the message content.
:invite - Get the bot invite link.
NOTE: Your DMs must be turned on or the bot won't be able to DM any of the info.
My test message ID: 800372849155637290
fetch returns the result as promise so you need to use the then to access that value instead of assigning it to a variable (msgValue). Also you made a typo (channel.message -> channel.messages).
I would recommend using something like this:
msg.channel.messages
.fetch(args[0])
.then(message => {
client.channels
.fetch(COMMAND_CHANNEL)
.then(channel => channel.send(message.content))
.catch(console.error)
})
.catch(console.error)
I think you were quite close with the second attempt you posted, but you made one typo and the way you store the fetched message is off.
The typo is you wrote msg.channel.message.fetch(args[0]) but it should be msg.channel.messages.fetch(args[0]) (the typo being the missing s after message). See the messages property of a TextChannel.
Secondly, but this is only a guess really as I can't be sure since you didn't provide much of your code, when you try to fetch the message, you are doing so from the wrong channel. You are trying to fetch the message with a given ID from in the channel the command was executed from (the msg.channel). Unless this command was executed from the "MESSAGE_DATABASE" channel, you would need to fetch the message by ID from the "MESSAGE_DATABASE" channel instead of the msg.channel.
Thirdly, if you fetch a message, the response from the Promise can be used in the .then method. You tried to assign the response to a variable msgValue with let msgValue = msg.channel.message.fetch(args[0]) but that won't do what you'll expect it to do. This will actual assign the entire Promise to the variable. What I think you want to do is just use the respone from the Promise directly in the .then method.
So taking all that, please look at the snippet of code below, with inspiration taken from the MessageManager .fetch examples. Give it a try and see if it works.
// We no longer need to store the value of the fetch in a variable since that won't work as you expect it would.
// Also we're now fetching the message from the MESSAGE_DATABASE channel.
client.channels.cache.get(MESSAGE_DATABASE).fetch(args[0])
// The fetched message will be given as a parameter to the .then method.
.then(fetchedMessage => client.channels.cache.get(COMMAND_CHANNEL).send(fetchedMessage.content))
.catch(console.error);

discord.py get webhooks of channel

I'm trying to make a webhook so if anyone says 'ez' it deletes it and sends a message with the webhook with a random message. Originally what I was doing was
if "ez" in message.content:
webhook = await message.create_webhook(name=ctx.author.name)
await webhook.send(ezmessages[random.randint(0, len(ezmessages))-1], username=message.author.name, avatar_url=message.author.avatar_url)
await message.delete()
await webhook.delete()
but the problem is this gets rate limited if webhooks are created and deleted too quickly. So instead what I want to do is check if the bot already has a webhook for the text channel, and if there is one use that but if not use a different one. I thought this would work:
for webhook in message.channel.webhooks:
await webhook.send(ezmessages[random.randint(0, len(ezmessages))-1], username=message.author.name, avatar_url=message.author.avatar_url)
but I get the error
TypeError: 'method' object is not iterable
Even though it should return a list
Anyone know how to correctly iterate over this?
TextChannel.webhooks it's not an attribute, its a function and a coroutine, so you need to call it and await it
webhooks = await message.channel.webhooks()
for webhook in webhooks:
...
docs

Resources