bulkDelete does not exist on channel - node.js

A little note to start with, this is a typescript file. I am having trouble with a purge/clear command. This command worked on discord v11 however is having an issue on v12. the issue I am seeing is bulkDelete does not exist on type 'TextChannel | DMChannel | NewsChannel' I don't know where it is going wrong. I am not looking for just a fix, an explanation of why it doesn't work would be appreciated. Thanks in advance!
import * as Discord from "discord.js";
import { IBotCommand } from "../api";
export default class purge implements IBotCommand {
private readonly _command = "purge"
help(): string {
return "(Admin only) Deletes the desired numbers of messages.";
}
isThisCommand(command: string): boolean {
return command === this._command;
}
async runCommand(args: string[], msgObject: Discord.Message, client: Discord.Client): Promise<void> {
//converts args into a number
let numberOfMessagesToDelete = parseInt(args[0],10);
//make sure number is interger
numberOfMessagesToDelete = Math.round(numberOfMessagesToDelete + 1);
//clean up msgs
msgObject.delete()
.catch(console.error);
if(!msgObject.member.hasPermission("KICK_MEMBERS")) {
msgObject.channel.send(`${msgObject.author.username}, AH! AH! AH! You didn't say the magic word!`)
.then(msg => {
(msg as Discord.Message).delete({timeout:10000})
});
return;
}
//message amount
if(!args[0]){
msgObject.channel.send(`Improper format! Proper format is "-purge 12"`)
.then(msg => {
(msg as Discord.Message).delete({timeout:10000})
.catch(console.error);
});
return;
}
//verifies args[0] actual number
if(isNaN(numberOfMessagesToDelete)){
msgObject.channel.send(`Improper number! Pick a number between 1 and 99!`)
.then(msg => {
(msg as Discord.Message).delete({timeout:10000})
.catch(console.error);
});
return;
}
//delete desired number of messages
let auditLog = client.channels.cache.find(channel => channel.id === "623217994742497290") as Discord.TextChannel;
const bdEmbed = new Discord.MessageEmbed()
.setTitle(`**Message Purge**`)
.setDescription(`${numberOfMessagesToDelete} messages were deleted in ${msgObject.channel.toString()} by ${msgObject.author.username}`)
.setTimestamp()
.setColor([38,92,216])
.setThumbnail(msgObject.author.displayAvatarURL())
msgObject.channel.bulkDelete(numberOfMessagesToDelete)
auditLog.send(bdEmbed)
.catch(console.error);
}
}

Related

Discord.js maximum number of webhooks error

Have this slash command code and turned it into webhook. It worked when I used it once but it stopped working after that. I got this error DiscordAPIError: Maximum number of webhooks reached (10). Does anyone have any idea on how to fix this?
Code:
run: async (client, interaction, args) => {
if(!interaction.member.permissions.has('MANAGE_CHANNELS')) {
return interaction.followUp({content: 'You don\'t have the required permission!', ephemeral: true})
}
const [subcommand] = args;
const embedevent = new MessageEmbed()
if(subcommand === 'create'){
const eventname = args[1]
const prize = args[2]
const sponsor = args[3]
embedevent.setDescription(`__**Event**__ <a:w_right_arrow:945334672987127869> ${eventname}\n__**Prize**__ <a:w_right_arrow:945334672987127869> ${prize}\n__**Donor**__ <a:w_right_arrow:945334672987127869> ${sponsor}`)
embedevent.setFooter(`${interaction.guild.name}`, `${interaction.guild.iconURL({ format: 'png', dynamic: true })}`)
embedevent.setTimestamp()
}
await interaction.followUp({content: `Event started!`}).then(msg => {
setTimeout(() => {
msg.delete()
}, 5000)
})
interaction.channel.createWebhook(interaction.user.username, {
avatar: interaction.user.displayAvatarURL({dynamic: true})
}).then(webhook => {
webhook.send({content: `<#&821578337075200000>`, embeds: [embedevent]})
})
}
}
You cannot fix that error, discord limits webhooks per channel (10 webhooks per channel).
However, if you don't want your code to return an error you can just chock that code into a try catch or add a .catch
Here is an example of how to handle the error:
try {
interaction.channel.createWebhook(interaction.user.username, {
avatar: interaction.user.displayAvatarURL({dynamic: true})
}).then(webhook => {
webhook.send({content: `<#&821578337075200000>`, embeds: [embedevent]})
})
} catch(e) {
return // do here something if there is an error
}

TypeError: Cannot read property 'roles' of undefined - input being read incorrectly

Not sure how to get the id of my target because it keeps reading it as undefined. I may have set up my argument incorrectly but I'm fairly sure that's not the issue. My command to kick is !o kick #user kick reason if that helps.
const { message } = require("discord.js")
module.exports = {
name: 'kick',
description: 'This command kicks a member',
execute(message, args){
if (message.member.hasPermission(["KICK_MEMBERS"])) {
const target = message.mentions.members.first();
if(target){
let [target, ...reasonKick] = args;
[...reasonKick].join(' ');
message.guild.members.cache.find(target => target.id)
const memberTarget = message.guild.members.cache.get(target.id);
if (message.guild.me.hasPermission("KICK_MEMBERS")) {
if (message.author.id === memberTarget) {
message.reply('why would you want to kick yourself?');
}
else if (message.client.user.id === memberTarget) {
message.channel.send('Nice try, but you can\'t kick me.')
}
else if (message.member.roles.highest.position > message.guild.members.cache.get(memberTarget).roles.highest.position) {
memberTarget.kick()
message.channel.send(`**${target.tag}** has been kicked. Reason: **${reasonKick}**.`);
}
else {
message.reply(`I don\'t have the required permission to kick **${target.tag}**`);
}
} else {
message.reply('I don\'t have the required permission to kick members');
}
} else{
message.reply('you must mention the member you want to kick.')
.catch;
}
} else {
message.reply("you do not have the permission to do that.")
.catch;
}
},
};
// - someCollection.find('property', value);
// + someCollection.find(element => element.property === value)
A mention already returns a member object, theres no need to try and get a member based on the mention's id.
Replace all of your memberTarget, with target

Update an imported module in Typescript

I'm sorry, but I'm kinda new in this language.
I was creating a custom discord bot these days and I got stucked on this problem...
I gave this bot the possibility to load the commands dynamically from a folder with one module for each command, but now I was trying to make a command to reload them all, but each time after the commands are reloaded the output is always the same.
Here is the code:
refreshCommands = () => {
this.commands = {};
console.log("Refreshing commands");
Promise.all(fs.readdirSync("./dist/commands").map(file => {
return new Promise(async resolve => {
const tmp = (await import(`./commands/${file}`)).default;
this.commands[tmp.name] = tmp;
resolve(tmp);
});
})).then(() => {
console.log("Listing commands: ");
console.log(Object.keys(this.commands));
});
}
Of course I update the commands from the js file, and not from the ts 'cause I would have to compile it again.
I tried to make a simple "ping! Pong!" like command, and then to edit it to "ping! ping!" on runtime before using the //reload command, but it keeps writing "ping! Pong!"
Edit 1:
The modules I have to import are made like this one:
import command from "../utils/command";
import { Guild, GuildEmoji, GuildEmojiManager, Message, MessageEmbed, Role } from "discord.js";
import { games } from "../utils/games";
import app from "../app";
import ReactionListener from "../utils/reactionListener";
const roleMessage: command = {
name: "rolesMessage",
description: "",
execute: async (message, bot) => {
message.delete();
createRoles(message.guild as Guild);
const embed = new MessageEmbed()
.setColor('#F00')
.setTitle("React to set your ROLE!");
games.forEach(game => {
let emoji = message.guild?.emojis.cache.find(emoji => emoji.name === game.emoji);
console.log(emoji);
embed.fields.push({
name: game.name,
value: (emoji as GuildEmoji).toString(),
inline: false
});
});
const msg = await message.channel.send(embed);
app.reactionListeners.push(new ReactionListener(msg,
(reaction, user) => {
let tmp = games.find(game=> reaction.emoji.name === game.emoji);
if(tmp){
//msg.channel.send(tmp);
const role = (message.guild as Guild).roles.cache.find(role => role.name === tmp?.roleName) as Role;
message.guild?.members.cache.find(member => member.id === user.id)?.roles.add(role);
}else{
reaction.remove();
}
}, (reaction, user)=>{
let tmp = games.find(game=> reaction.emoji.name === game.emoji);
if(tmp){
//msg.channel.send(tmp);
const role = (message.guild as Guild).roles.cache.find(role => role.name === tmp?.roleName) as Role;
message.guild?.members.cache.find(member => member.id === user.id)?.roles.remove(role);
}
})
);
games.forEach(game => {
msg.react((message.guild?.emojis.cache.find(emoji => emoji.name === game.emoji) as GuildEmoji));
});
}
}
const createRoles = (guild: Guild) => {
games.forEach(game => {
if(!guild.roles.cache.find(role => role.name === game.roleName)){
guild.roles.create({
data: {
name: game.roleName,
color: "#9B59B6",
},
reason: 'we needed a role for Super Cool People',
})
.then(console.log)
.catch(console.error);
}
});
}
export default roleMessage;
This is a different one from the one I was talking about earlier, but the problem is the same... Once I update and reload it (from the js compiled version), the old version keeps being runned
I managed to find a solution to the problem.
As node js chaches every module once imported, I deleted it from the cache like this
refreshCommands = () => {
Promise.all(fs.readdirSync("./dist/commands").map(file => {
return new Promise(async resolve => {
delete require.cache[require.resolve('./commands/' + file)];
resolve(file);
});
})).then(() => {
this.commands = {};
console.log("Refreshing commands");
Promise.all(fs.readdirSync("./dist/commands").map(file => {
return new Promise(async resolve => {
const tmp = (await import(`./commands/${file}`)).default;
this.commands[tmp.name] = tmp;
resolve(tmp);
});
})).then(() => {
console.log("Listing commands: ");
console.log(Object.keys(this.commands));
});
});
}
The code might look like garbage, but it actually works... I'm on my way to make it better, but meanwhile I can rely on it.
Any suggestion is well accepted

How can I convert from a firebase document to a custom class in Node JS

In node.js I'm getting the error below. Any ideas why?
Conversion of type 'Promise' to type 'Member[]' may be a mistake
because neither type sufficiently overlaps with the other. If this was
intentional, convert the expression to 'unknown' first. Type
'Promise' is missing the following properties from type
'Member[]': length, pop, push, concat, and 26 more.
export async function getFamilyMembers(tenantId: string, familyCode: string): Promise<Member[]> {
return db.collection(`tenants/${tenantId}/members`)
.where('familyCode', '==', familyCode)
.get()
.then(snaps => {
snaps.docs.forEach(doc => {
return { id: doc.id, ...doc.data()}
});
}) as Member[];
}
EDIT:
If I remove the types and change it to
export async function getFamilyMembers(tenantId: string, familyCode: string) {
return db.collection(`tenants/${tenantId}/members`)
.where('familyCode', '==', familyCode)
.get()
.then(snaps => {
snaps.docs.forEach(doc => {
return { id: doc.id, ...doc.data()}
});
});
}
I just have to deal with the problem later.
I get the error
Property 'length' does not exist on type 'void'.
const familyMembers: Member[] | void = await getFamilyMembers(tenantId, familyCode);
if (familyMembers === null) {
isVerified = false;
verificationFailMessage = `Sorry we can't find this code. Please check it is correct.`;
} else if (familyMembers.length === 0) {
I needed to add Promise in front of Member.
export async function getFamilyMembers(tenantId: string, familyCode: string): Promise<Member[]> {
return db.collection(`tenants/${tenantId}/members`)
.where('familyCode', '==', familyCode)
.get()
.then(snaps => {
snaps.docs.forEach(doc => {
return { id: doc.id, ...doc.data()}
});
}) as Promise<Member[]>;
}

NodeJS streams not awaiting async

I have run into an issue when testing NodeJS streams. I can't seem to get my project to wait for the output from the Duplex and Transform streams after running a stream.pipeline, even though it is returning a promise. Perhaps I'm missing something, but I believe that the script should wait for the function to return before continuing. The most important part of the project I'm trying to get working is:
// Message system is a duplex (read/write) stream
export class MessageSystem extends Duplex {
constructor() {
super({highWaterMark: 100, readableObjectMode: true, writableObjectMode: true});
}
public _read(size: number): void {
var chunk = this.read();
console.log(`Recieved ${chunk}`);
this.push(chunk);
}
public _write(chunk: Message, encoding: string,
callback: (error?: Error | null | undefined, chunk?: Message) => any): void {
if (chunk.data === null) {
callback(new Error("Message.Data is null"));
} else {
callback();
}
}
}
export class SystemStream extends Transform {
public type: MessageType = MessageType.Global;
public data: Array<Message> = new Array<Message>();
constructor() {
super({highWaterMark: 100, readableObjectMode: true, writableObjectMode: true});
}
public _transform(chunk: Message, encoding: string,
callback: TransformCallback): void {
if (chunk.single && (chunk.type === this.type || chunk.type === MessageType.Global)) {
console.log(`Adding ${chunk}`);
this.data.push(chunk);
chunk = new Message(chunk.data, MessageType.Removed, true);
callback(undefined, chunk); // TODO: Is this correct?
} else if (chunk.type === this.type || chunk.type === MessageType.Global) { // Ours and global
this.data.push(chunk);
callback(undefined, chunk);
} else { // Not ours
callback(undefined, chunk);
}
}
}
export class EngineStream extends SystemStream {
public type: MessageType = MessageType.Engine;
}
export class IOStream extends SystemStream {
public type: MessageType = MessageType.IO;
}
let ms = new MessageSystem();
let es = new EngineStream();
let io = new IOStream();
let pipeline = promisify(Stream.pipeline);
async function start() {
console.log("Running Message System");
console.log("Writing new messages");
ms.write(new Message("Hello"));
ms.write(new Message("world!"));
ms.write(new Message("Engine data", MessageType.Engine));
ms.write(new Message("IO data", MessageType.IO));
ms.write(new Message("Order matters in the pipe, even if Global", MessageType.Global, true));
ms.end(new Message("Final message in the stream"));
console.log("Piping data");
await pipeline(
ms,
es,
io
);
}
Promise.all([start()]).then(() => {
console.log(`Engine Messages to parse: ${es.data.toString()}`);
console.log(`IO Messages to parse: ${io.data.toString()}`);
});
Output should look something like:
Running message system
Writing new messages
Hello
world!
Engine Data
IO Data
Order Matters in the pipe, even if Global
Engine messages to parse: Engine Data
IO messages to parse: IO Data
Any help would be greatly appreciated. Thanks!
Note: I posted this with my other account, and not this one that is my actual account. Apologies for the duplicate.
Edit: I initially had the repo private, but have made it public to help clarify the answer. More usage can be found on the feature/inital_system branch. It can be run with npm start when checked out.
Edit: I've put my custom streams here for verbosity. I think I'm on a better track than before, but now getting a "null" object recieved down the pipeline.
As the documentation states, stream.pipeline is callback-based doesn't return a promise.
It has custom promisified version that can be accessed with util.promisify:
const pipeline = util.promisify(stream.pipeline);
...
await pipeline(...);
After some work of the past couple of days, I've found my answer. The issue was my implementation of the Duplex stream. I have since changed the MessageSystem to be a Transform stream to be easier to manage and work with.
Here is the product:
export class MessageSystem extends Transform {
constructor() {
super({highWaterMark: 100, readableObjectMode: true, writableObjectMode: true});
}
public _transform(chunk: Message, encoding: string,
callback: TransformCallback): void {
try {
let output: string = chunk.toString();
callback(undefined, output);
} catch (err) {
callback(err);
}
}
}
Thank you to #estus for the quick reply and check. Again, I find my answer in the API all along!
An archived repository of my findings can be found in this repository.

Resources