Organizing Channels - discord.js

I am trying to create a bot that sets up the server for you when the command !setup is input. I have got to the stage at which the bot creates all of the roles and channels. However, I now need the bot to organize the channels and place text/voice channels inside category channels and move them to the correct position.
message.guild.createChannel('server-tests', {
type: 'text',
});

You can define the position and parent properties of a channel when you create it (see here).
You can use the keyword await (async context needed) to create the channels in order.
try {
const generalCategory = await message.guild.createChannel('General Stuff', {
type: 'category',
position: 3
});
await message.guild.createChannel('general-chat', {
type: 'text',
parent: generalCategory
});
} catch(err) {
console.error(err);
}

Related

Discord.js Filter Discord Events From Only 1 Server

I am working on trying to filter events for only 1 server rather than all servers the bot is in, but I'm trying to figure out how to exactly save each guild the bot is in as an collection so I can just filter events based on a guild ID I desire. Is this even possible? This is a snippet of the code I have to date, it's able to display the server names and ID's the bot is currently in, the events are working properly but triggering for all servers rather than the one I desire, how would I go about filtering for one guild collection?
const Discord = require('discord.js')
const bot = new Discord.Client()
const config = require('./config.json')
bot.on('ready', () => {
console.log(`Logged in as ${bot.user.tag}!`);
//Sets Activity
//client.user.setStatus('invisible')
bot.user.setActivity("Discord Cooks", { type: "WATCHING"})
console.log("Set User Activity!");
//Online Webhook
const webhookClient = new Discord.WebhookClient('','');
const embed = new Discord.MessageEmbed()
.setTitle(`${bot.user.tag} is online`)
.setColor('#FFFF00')
.setTimestamp()
webhookClient.send(embed);
bot.guilds.cache.forEach((guild) => {
console.log(guild.name, guild.id);
});
});
bot.on("channelCreate", (channel) => {
console.log(`channelCreate: ID: ${channel.id} Name: ${channel.name}`);
});
bot.on("channelUpdate", (oldChannel, newChannel) => {
console.log(`channelUpdate -> ${oldChannel.name} to ${newChannel.name}`);
});
bot.on("channelDelete", (channel) => {
console.log(`channelDelete: ID: ${channel.id} Name: ${channel.name}`);
});
bot.login(config.bottoken)
You can only execute code if event happened on exact server in much easier way:
if(guild.id == "GUILD_ID") {
// code
}
Also as #MrMythical said, you can just use if (message.guild.id !== "GUILD_ID") return; if you only need to run your code for 1 guild!

Multiple video call (n users) using Peerjs in React Native

I have an application in which I am trying to get video chatting to work in React Native.
Used packages like react-native-webrtc and react-native-peerjs.
Created peer js server using Node Js.
One to One Video call is working fine with react native Peerjs. But, Now I want more than 2 users to be connected upto n users.
Is it possible to convert one to one video call to Multiple video call. Kindly let me know how Multiple video call can be achieved using Peer js and web rtc.
Here is my code for one to one video call:
Initialize webrtc and PeerJS:
const initialize = async () => {
const isFrontCamera = true;
const devices = await mediaDevices.enumerateDevices();
const facing = isFrontCamera ? 'front' : 'environment';
const videoSourceId = devices.find(
(device: any) => device.kind === 'videoinput' && device.facing === facing,
);
const facingMode = isFrontCamera ? 'user' : 'environment';
const constraints: MediaStreamConstraints = {
audio: true,
video: {
mandatory: {
minWidth: 1280,
minHeight: 720,
minFrameRate: 30,
},
facingMode,
optional: videoSourceId ? [{ sourceId: videoSourceId }] : [],
},
};
const newStream = await mediaDevices.getUserMedia(constraints);
setLocalStream(newStream as MediaStream);
console.log("************ Started ************");
// const io = socketio(SERVER_URL);
// io.connect();
console.log(SERVER_URL);
const io = socketio.connect(SERVER_URL, {
reconnection: true,
autoConnect: true,
reconnectionDelay: 500,
jsonp: false,
reconnectionAttempts: Infinity,
// transports: ['websocket']
});
io.on('connect', () => {
console.log("----------- Socket Connected -----------");
setSocket(io);
io.emit('register', username);
});
io.on('users-change', (users: User[]) => {
console.log("----------- New User - " + JSON.stringify(users) + " -----------");
setUsers(users);
});
io.on('accepted-call', (user: User) => {
setRemoteUser(user);
});
io.on('rejected-call', (user: User) => {
setRemoteUser(null);
setActiveCall(null);
Alert.alert('Your call request rejected by ' + user?.username);
navigate('Users');
});
io.on('not-available', (username: string) => {
setRemoteUser(null);
setActiveCall(null);
Alert.alert(username + ' is not available right now');
navigate('Users');
});
const peerServer = new Peer(undefined, {
host: PEER_SERVER_HOST,
path: PEER_SERVER_PATH,
secure: false,
port: PEER_SERVER_PORT,
config: {
iceServers: [
{
urls: [
'stun:stun1.l.google.com:19302',
'stun:stun2.l.google.com:19302',
],
},
],
},
});
peerServer.on('error', (err: Error) =>
console.log('Peer server error', err),
);
peerServer.on('open', (peerId: string) => {
setPeerServer(peerServer);
setPeerId(peerId);
io.emit('set-peer-id', peerId);
});
io.on('call', (user: User) => {
peerServer.on('call', (call: any) => {
//Alert.alert("PeerServer Call");
setRemoteUser(user);
Alert.alert(
'New Call',
'You have a new call from ' + user?.username,
[
{
text: 'Reject',
onPress: () => {
io.emit('reject-call', user?.username);
setRemoteUser(null);
setActiveCall(null);
},
style: 'cancel',
},
{
text: 'Accept',
onPress: () => {
io.emit('accept-call', user?.username);
call.answer(newStream);
setActiveCall(call);
navigate('Call');
},
},
],
{ cancelable: false },
);
call.on('stream', (stream: MediaStream) => {
setRemoteStream(stream);
});
call.on('close', () => {
closeCall();
});
call.on('error', () => { });
});
});
};
When a user call another user:
const call = (user: User) => {
if (!peerServer || !socket) {
Alert.alert('Peer server or socket connection not found');
return;
}
if (!user.peerId) {
Alert.alert('User not connected to peer server');
return;
}
socket.emit('call', user.username);
setRemoteUser(user);
try {
const call = peerServer.call(user.peerId, localStream);
call.on(
'stream',
(stream: MediaStream) => {
setActiveCall(call);
setRemoteStream(stream);
},
(err: Error) => {
console.error('Failed to get call stream', err);
},
);
} catch (error) {
console.log('Calling error', error);
}
};
Now, how should I call multiple user from the code below and how multiple streams have to be handled.
const call = peerServer.call(user.peerId, localStream);
Is it possible to convert one to one video call to Multiple video call
It's not possible to "convert" a one to one video call to "multiple" in a peer-to-peer architecture. In a p2p architecture with n participants, each participant will have a separate, one-to-one connection with the rest n-1 other participants.
I may possibly be misunderstanding your question, but if you're asking whether it's possible to establish n-1 connections for each participant, then the answer is yes. Here's how I would implement:
Anytime a new participant joins a session, extract their peer information. This is the peerId provided by the peer.js library.
Next, let the rest of the participants know about the presence of this new user. For this, you'll share this new participant's name, peerID and any other metadata with the rest of the participants in the room. This can be done by the signalling logic that you have implemented using socket.io.
Now going forward, you have 2 options:
The new participant could initiate the one-to-one peer connection with others in the room, OR,
The rest of the participants could initiate a one-on-one connection with the new participant.
Personally I prefer the first. So continuing the process:
Using the same signalling logic via socket.io, the rest of the participants will let the new user know about their presence by providing their own peer information and other metadata.
Once the new participant gets everyone's peer information, initiate a new peer connection using call.on('stream', callback) and start broadcasting their video.
On the recipient side, when a call is received along with the stream, you'll create a new video element in react-native, and bind the received media stream to this element. Which means, each participant will have n-1 video elements for streaming the media of n-1 other participants. The recipient also starts to broadcast their own video to the initiator of the call.
Here's a tutorial showing how this can be done using vanilla JavaScript, along with the github repository with source code.
Now, to answer the next question:
Kindly let me know how Multiple video call can be achieved using Peer js and webrtc.
This depends on the number of participants, where they lie geographically, browser/device limits, device computational power, and network bandwidth. So there are multiple factors involved which makes it tricky to give any specific number.
Browsers can place their own upper limits on the maximum number of connections possible, and there might be other values for Android and iOS. On chrome, the max theoretical limit is 500. If you're developing for Android, you may want to check here. But I couldn't manage to find much info on this.
Most practical applications involving WebRTC don't rely on a mesh architecture. Common implementations involve using an SFU, which takes multiple media streams and forwards them. A slightly more sophisticated technique is an MCU architecture, which combines all those media streams from multiple participants into a single one, and send that single stream to the rest of the participants.
I discuss this in some detail here:
https://egen.solutions/articles/how-to-build-your-own-clubhouse-part-2/#architectures-scaling-and-costs
Here's a nice article that explains the difference between SFU and MCU.

Why am I getting a "TypeError: Cannot read property 'send' of undefined"?

So this was working earlier, but after adding my guildCache map in order to hold server specific variables, it stopped working. I'm not sure where the error occurs, so I will be sending all of my code until the error point. Basically, on my guildCreate event the bot would normally send an image and text to the server owner, but all of a sudden my working code is now broken. Any thoughts?
My intents are GUILDS, GUILD_MESSAGES, DIRECT_MESSAGES
// Require the necessary modules
const config = require('./Data/config.json');
const insulter = require('insult');
require('dotenv').config();
const { Client, Collection } = require('discord.js');
// Create a new client instance
const client = new Client({ intents: 4609 }); // GUILDS, GUILD_MESSAGES, DIRECT_MESSAGES
const guildCache = new Collection(); //For saving independent server variables
// Functions
function getUserFromMention(mention) {
if (!mention) return;
if (mention.startsWith('<#') && mention.endsWith('>')) {
mention = mention.slice(2, -1);
if (mention.startsWith('!')) {
mention = mention.slice(1);
}
return client.users.cache.get(mention);
}
}
// --------------- BOT EVENTS -----------------
client.once('ready', () => {
console.log(`[Client] Logged in as ${client.user.tag}!`);
client.user.setActivity('you cry.', {
type: 'WATCHING'
});
// Map servers to cache for variables (also done on server join and leave)
client.guilds.cache.forEach(guild => {
guildCache.set(guild.id, {
bullyTarget: undefined,
lastInsultGenerated: undefined
});
});
console.log(guildCache);
console.log('');
});;
// On join server
client.on('guildCreate', async (guild) => {
console.log(`[Client]: Joined \"${guild.name}\"!`);
guildCache.set(guild.id, {
bullyTarget: undefined,
lastInsultGenerated: undefined
});
console.log(guildCache);
console.log('');
await client.users.cache.get(guild.ownerId).send({
content: 'You\'ll ***regret*** adding me.',
files: [{
attachment: 'src/Data/trollge.jpg',
name: 'trollge.jpg'
}]
})
.then(console.log('[Client]: Sent join message to server owner :D\n'))
.catch(console.error);
});
This is because the owner isn't in the bot's cache. You should use UserManager.fetch (on client.users)
(await client.users.fetch(guild.ownerId)).send({
content: 'You\'ll ***regret*** adding me.',
files: [{
attachment: 'src/Data/trollge.jpg',
name: 'trollge.jpg'
}]
})
You can use the Guild.fetchOwner() method to also access their GuildMember info (roles, nickname, etc.)
const owner = await guild.fetchOwner()
owner.send({
content: 'You\'ll ***regret*** adding me.',
files: [{
attachment: 'src/Data/trollge.jpg',
name: 'trollge.jpg'
}]
})

How to get the channelid or name of a newly created channel

I have a command with my discord bot to make a channel, but I'm trying to figure out how to get the id or name of the channel right after its made.
Line of code that makes the channel:
message.guild.channels.create('ticket ' + message.member.displayName, { parent: '744477882730020965' });
The reason is that since displayname can have characters not possible in a channel name and discord will just automatically remove those characters there's no actual way to predict what the channel name will be in some cases. There's probably a simple solution I'm missing, and thanks in advance for any help.
The GuildChannelManager#create method returns a Promise with the channel that was just created. You can use Promise.then() to get the channel.
Guild.channels.create(`ticket-${message.member.displayName}`, {
parent: "744477882730020965"
}).then(channel => {
message.channel.send(`I've created the channel ${channel.name}!`);
}).catch(error => {
console.error(error);
message.channel.send(`Couldn't create the channel. Check the console for the error.`);
});
If you are creating the channel in an async function, you can use await to avoid .then() for readability.
const Channel = await Guild.channels.create(`ticket-${message.member.displayName}`, {
parent: "744477882730020965"
}).catch(error => {
console.error(error);
message.channel.send(`Couldn't create the channel. Check the console for the error.`);
});
What do you need the ID for? You can use a then function to do whatever to edit the channel.
Try this?
let role = message.guild.roles.find("name", "#everyone");
message.guild.channels.create('ticket ' + message.member.displayName, { parent: '744477882730020965' }).then(c => {
c.overwritePermissions(role, {
SEND_MESSAGES: false,
READ_MESSAGES: false
});
c.overwritePermissions(message.author, {
SEND_MESSAGES: true,
READ_MESSAGES: true
});
message.reply(`Application created!`)
c.send(`Wait for support staff`)
}).catch(console.error);

Grabbing the permissions from a TextChannel - Discord.js

Basically, I need to grab the permissions from the current text channel the user is in. I have already got the channel name, if I need to get the ID that should be pretty easy to do.
const Discord = require("discord.js");
module.exports.run = async (client, message, args) => {
let currentChannel = message.channel.name;
let category = message.channel.parent;;
message.guild.createChannel(currentChannel).then(mchannel => {
mchannel.setParent(category).then(() => {
message.channel.delete();
});
});
}
module.exports.help = {
name: "a.cleanchannel"
}
// Need the channel permissions to overwrite the new channel's permissions with the old ones
The expected results are that the channel should have the same permissions as the old one.
To answer your question directly, you can use GuildChannel#permissionOverwrites to create the new channel with the same permissions as the old one. For example...
message.guild.createChannel(message.channel.name, {
type: 'text',
permissionOverwrites: message.channel.permissionOverwrites
});
However, it looks like you're trying to clone a channel. To help make that easier, there's a method built into Discord.js for it - GuildChannel#clone(). You can use it like so...
message.channel.clone(undefined, true, true) // Same name, same permissions, same topic
.then(async clone => {
await clone.setParent(message.channel.parent);
await clone.setPosition(message.channel.position);
await message.channel.delete();
console.log(`Cloned #${message.channel.name}`);
})
.catch(console.error);

Resources