How do I use #discordjs/voice AudioPlayer methods across modules? - discord

I am adding a music-playing feature to my bot using #discordjs/voice, following the voice guide on discordjs.guide. My slash commands are all stored on different files. I want to use the pause method of the AudioPlayer class outside of play.js.
The relevant parts of play.js are:
const { SlashCommandBuilder } = require('#discordjs/builders');
const { StreamType, createAudioPlayer, createAudioResource, joinVoiceChannel } = require('#discordjs/voice');
module.exports = {
data: new SlashCommandBuilder()
.setName('play')
.setDescription('An example of the play command.'),
async execute(interaction) {
const connection = joinVoiceChannel({ channelId: interaction.member.voice.channel.id, guildId: interaction.guild.id, adapterCreator: interaction.guild.voiceAdapterCreator });
const stream = ytdl('https://www.youtube.com/watch?v=jNQXAC9IVRw', { filter: 'audioonly' });
const resource = createAudioResource(stream, { inputType: StreamType.Arbitrary });
const player = createAudioPlayer();
player.play(resource);
connection.subscribe(player);
await interaction.reply('Playing.');
},
};
and of pause.js are:
const { SlashCommandBuilder } = require('#discordjs/builders');
module.exports = {
data: new SlashCommandBuilder()
.setName('pause')
.setDescription('An example of the pause command.'),
async execute(interaction) {
player.pause();
await interaction.reply('Paused.');
},
};
My console got the following error when I ran the /pause command after I tried to export player from player.js and require it in pause.js:
TypeError: Cannot read properties of undefined (reading 'pause')
What do I need to do to use player.pause() outside of play.js?

You should take a look at this part of the Discord.js guide, in the Access part. The getVoiceConnection method serves this purpose.
It allows you to use the connection created in play.js anywhere in your code. Then, you just need to change the state of the player with connection.state.subscription.player.pause().
I did my pause.js by checking whether the member using the pause command is in a channel and if he is, whether the bot is in it or not:
if (!interaction.member.voice.channelId) {
return interaction.reply('not in a channel.');
}
const connection = getVoiceConnection(interaction.member.voice.channel.guildId);
if (!connection || (connection.joinConfig.channelId != interaction.member.voice.channelId)) {
return interaction.reply('The bot is not in this channel.');
}
connection.state.subscription.player.pause();
await interaction.reply('Paused.');
discord.js

Related

DiscordJS Roles Won't Add

const { SlashCommandBuilder } = require("#discordjs/builders");
const { Permissions } = require("discord.js");
module.exports = {
data: new SlashCommandBuilder()
.setName("mute")
.setDescription("Mute a user on the server")
.addUserOption((option) =>
option
.setName("user")
.setDescription("The user you want to mute")
.setRequired(true)
),
execute: async (interaction) => {
const user = interaction.options.getUser("user");
if (!interaction.member.permissions.has(Permissions.FLAGS.MANAGE_ROLES)) {
return interaction.editReply({
content: `:x: | Manage Roles Permission is required to perform that action!`,
ephemeral: true,
});
}
user.roles.add(member.guild.roles.cache.get("958443243836702859"));
return interaction.editReply("Muted!")
},
};
TypeError: Cannot read properties of undefined (reading 'add')
It won't let me add a roles to a user please help!
You are calling user.roles.add() in the middle of the hash definition of module_exports.
Read through your code again. It's a syntax error.
The issue stems from this, roles is an element of a member which is the parent of a user. user does not contain the element of roles. The code was trying to add roles to a user rather than a member.
This will help you out.
const { SlashCommandBuilder } = require("#discordjs/builders");
const { Permissions } = require("discord.js");
module.exports = {
data: new SlashCommandBuilder()
.setName("mute")
.setDescription("Mute a user on the server")
.addUserOption((option) =>
option
.setName("user")
.setDescription("The user you want to mute")
.setRequired(true)
),
execute: async (interaction) => {
const user = interaction.options.getUser("user");
if (!interaction.member.permissions.has(Permissions.FLAGS.MANAGE_ROLES)) {
return interaction.editReply({
content: `:x: | Manage Roles Permission is required to perform that action!`,
ephemeral: true,
});
}
//adding the below line
const member2mute = interaction.guild.members.cache.get(user.id)
//changing the below line
member2mute.roles.add("958443243836702859");
return interaction.editReply("Muted!")
},
};

Saving an ID value from an API to a User with GraphQL

I'm working on a video game website where a user can save a game to a list. How this is supposed to work is when the user clicks "Complete Game", the ID of the game is saved to a state that holds the value. The value is then passed into the mutation, then the mutation runs, saving the ID of the game to the users list of completed games. However, all I'm seeing in the console is this:
"GraphQLError: Variable \"$addGame\" got invalid value { gameId: 740, name: \"Halo: Combat Evolved\",
The above error continues, listing the entirety of the API response, instead of just the gameId.
I was able to successfully add the game to the list in the explorer with the following mutation:
mutation completeGame($addGame: AddNewGame!) {
completeGame(addGame: $addGame) {
_id
completedGameCount
completedGames {
gameId
}
}
}
with the following variable:
{
"addGame": {"gameId": 740}
}
How can I trim down what is being passed into the mutation to just be the gameId?
Below is the entirety of the page, except the return statement at the bottom.
const [selectedGame, setSelectedGame] = useState([]);
const [savedGameIds, setSavedGameIds] = useState(getSavedGameIds());
const [completeGame, { error }] = useMutation(COMPLETE_GAME);
const { id: gameId } = useParams();
useEffect(() => {
return () => saveGameIds(savedGameIds);
});
useEffect(() => {
async function getGameId(gameId) {
const response = await getSpecificGame(gameId);
if (!response.ok) {
throw new Error('Something went wrong...');
}
const result = await response.json();
const gameData = result.map((game) => ({
gameId: game.id,
name: game.name,
cover: game.cover,
summary: game.summary,
platforms: game.platforms,
platformId: game.platforms,
genres: game.genres,
genreId: game.genres,
}));
setSelectedGame(gameData);
}
getGameId(gameId);
}, [])
const handleCompleteGame = async (gameId) => {
const gameToComplete = selectedGame.find((game) => game.gameId === gameId);
const token = Auth.loggedIn() ? Auth.getToken() : null;
if (!token) {
return false;
}
try {
const { data } = await completeGame({
variables: { addGame: { ...gameToComplete } },
});
console.log(data);
setSavedGameIds([...savedGameIds, gameToComplete]);
} catch (err) {
console.error(err);
}
};
With the mutation working in the explorer when I'm able to explicitly define the variable, I am led to believe that the issue is not with the resolver or the typedef, so I'm going to omit those from this post because I don't want it to get too long.
However, I'd be happy to attach any extra code (resolver, typeDef, getSavedGameIds function, etc) if it would allow anyone to assist. The issue (I think) lies in getting my response to match the syntax I used in the explorer, which means trimming down everything except the gameId.
I specifically am extremely suspicious of this line
const gameToComplete = selectedGame.find((game) => game.gameId === gameId)
but I have fiddled around with that for awhile to no avail.
Thank you to anyone who is able to help!
It sounds like you're trying to pass more into your mutation then your schema is defined to allow. In this part:
const { data } = await completeGame({
variables: { addGame: { ...gameToComplete } },
});
You're spreading gameToComplete here which means everything in the gameToComplete object is going to be sent as a variable. If your schema is setup to just expect gameId to be passed in, but your error message is showing that name is also being passed in, you just need to adjust your variables to exclude everything you can't accept. Try:
const { data } = await completeGame({
variables: { addGame: { gameId } },
});

Discord js Voice | How to use AudioResource

I'm trying to create an AudioResource according to the documentation, but I don't know what to fill in edges and streams. I only have yt(youtube-url) from ytdl-core. Please advise what information is required. I try create Audio Resource via new Voice.AudioResource on way documentation. I know about createAudioResource, but is there any possibility to do it differently?
client.on('messageCreate', (message) => {
if(message.content == '!test'){
const VoiceConnection = new Voice.VoiceConnection({
channelId: message.member.voice.channel.id,
guildId: message.guild.id,
selfDeaf: true,
}, {
adapterCreator: message.guild.voiceAdapterCreator,
debug: true
})
VoiceConnection.rejoin()
const Player = new Voice.AudioPlayer()
const Resource = new Voice.AudioResource([], [yt(youtube-url)]) // error
VoiceConnection.subscribe(Player)
Player.play(Resource)
}
})
createAudioPlayer isn't the most stable audio player but it is the only one fully supported by the Discord.js Developers at this time. It should be a good idea using the way they support.
Take some minutes to read this document it is very important.
I think you want something like this:
audioPlayer.play(createAudioResource(stream))
I did not test this code but I think it works:
const { joinVoiceChannel } = require('#discordjs/voice');
const connection = joinVoiceChannel({
channelId: channel.id,
guildId: channel.guild.id,
adapterCreator: channel.guild.voiceAdapterCreator,
});
const { createAudioPlayer } = require('#discordjs/voice');
const player = createAudioPlayer();
const songURL= 'https://www.youtube.com/watch?v=Ak5oJcXSnQw'
const stream = ytdl(songURL, {filter: 'audioonly'});
player.play(createAudioResource(stream));

FS access api & React: duplicate entries

I'm trying to build a movie dashboard (something like Plex); the user selects a folder and all the movies he has in the folder show up. The use of the new File System Access API allows me to create file handlers on the fly and be able to display movies using the browser video player.
The problem I'm facing is with duplicated entries, for instance "Ghostbusters" (can't really understand why, but that's the only one causing the issue)
This is the basic implementation of the file system:
try {
const folderHandle = await window.showDirectoryPicker();
const addedFilms = [];
history.push('/list');
// const entries = await folderHandle.values()
const entries = await folderHandle.values();
for await (const entry of entries) {
const movie = await readMoviesonDisk(folderHandle, entry);
console.log(addedFilms);
if (addedFilms.includes(entry.name)) continue;
addedFilms.push(entry.name);
setMovies((movies) => [...movies, movie]);
}
} catch (error) {
alert('Alert from reading files: ' + error);
}
setMovies just sets a Context with a movies array and readMoviesOnDisk is the following:
const readMoviesonDisk = async (folderHandle, entry) => {
if (entry.kind === 'file' && entry.name.endsWith('.mp4')) {
const path = await folderHandle.resolve(entry);
const handle = await folderHandle.getFileHandle(path);
const movie = await getMovie(entry.name);
if (movie) {
return { ...movie.data, file: handle, name: entry.name };
}
const movieData = await searchMovie(entry.name);
if (movieData) {
const actualData = await getMovieDetails(movieData.id);
if (actualData !== undefined) {
await insertMovie(entry.name, actualData, handle);
} else {
await insertMovie(entry.name, actualData, handle);
}
return { ...actualData, file: handle, name: entry.name };
}
return { name: entry.name, file: handle };
}
};
searchMovie and insertMovie only interact with IndexedDB to store movie info for offline use. getMovieDetails does API calls to TMDB to get movie info.
The key I use for displaying the movies is their TMDB id. Ghostbusters' id is "620".
Can anyone help me?
Without additional background it seems impossible to answer this properly. Can you iterate over all files in the folder and just log the names and kinds? This should work and show no duplicate entries.
const dirHandle = await window.showDirectoryPicker();
for await (const entry of dirHandle.values()) {
console.log(entry.kind, entry.name);
}

Run client.setActivity out of client.once('ready' etc. discord.js

I am trying to set the status of the bot based on a user message (not in the code below), but I keep getting the error
TypeError: Cannot read property 'setActivity' of null
code is below, thanks in advance
client.once('ready', () => {
console.log('Ready!');
client.user.setActivity("your every move", { type: "WATCHING" }) //works
});
function setStatus(game, type){
client.user.setActivity(game, type)
}
setStatus("your every move", { type: "WATCHING" }) //returns "TypeError: Cannot read property 'setActivity' of null"
client.login(config.token);
this code should set the activity. Usage would be +activity watching/playing/listening game
BTW the setStatus function also works if you have it in a function (if you call that function in client. or that function that calls the function is called by client.)
const Discord = require("discord.js");
const client = new Discord.Client();
const cfg = require("./config.json");
const prefix = "+";
client.once('ready', () => {
console.log('Ready!');
setStatus("your every move", { type: "WATCHING" })
});
client.on("message", message =>{
if(message.content.startsWith(prefix)){
args = message.content.substr(prefix.length).split(/ +/);
cmd = args[0].toLowerCase();
if(cmd == "activity"){
let type = args[1].toUpperCase();
console.log(type);
let game = args
game.splice(0,2);
setStatus(game.join(" "), { type: type });
}
}
});
function setStatus(game, type){
client.user.setActivity(game, type)
}
client.login(cfg.token);

Resources