JavaScript

API discord предоставляет инструмент для создания и использования ботов. Рассмотрим пример создания базового музыкального бота и добавления его на сервер. Бот сможет проигрывать, пропускать и останавливать музыку, а также будет поддерживать функцию очереди воспроизведения.

Установка discord-бота

Создаем новое приложение на портале разработки discord.

Skillbox

Переходим на портал и нажимаем на “new application”.

Затем вводим название приложения и нажимаем на кнопку “create”.

Затем переходим на вкладку бот и нажимаем на “add bot”.

Бот создан! Теперь можно перейти к добавлению его на сервер.

Добавление бота на сервер

Добавляем созданный бот с помощью генератора OAuth2 URL.

Для этого переходим на страницу OAuth2 и выбираем бота в панели scope.

Затем выбираем необходимые разрешения для проигрывания музыки и чтения сообщений.

Теперь копируем сгенерированный URL и вставляем его в браузер.

Затем выбираем сервер, на который хотим добавить URL и нажимаем на кнопку “authorize”.

Создание проекта

Переходим к созданию проекта с использованием терминала.

Для начала создаем директорию и переходим в нее, используя две следующие команды:

mkdir musicbot && cd musicbot

Затем создаем модули проекта с помощью команды npm init. После введения команды будут заданы несколько вопросов. Ответьте на них и продолжайте.

Создаем два файла, в которых мы будем работать.

touch index.js && touch config.json

Теперь откройте проект в текстовом редакторе. Я использую VS Code и открываю его с помощью следующей команды:

code.

Основы Discord js

Прежде чем начать, нужно установить несколько зависимостей.

npm install discord.js ffmpeg-binaries opusscript ytdl-core --save

После завершения установки продолжаем написание файла config.json. Сохраните для бота токен и префикс, который он должен слушать.

{
"prefix": "!",
"token": "your-toke"
}

Для получения токена снова зайдите на портал разработки discord и скопируйте его из раздела bot.

Это все, что нужно выполнить в файле config.json. Приступим к написанию кода javascript.

Сначала импортируем все зависимости.

const Discord = require('discord.js');
const {
 prefix,
 token,
} = require('./config.json');
const ytdl = require('ytdl-core');

Затем с помощью токена создаем клиента и логин.

const client = new Discord.Client();
client.login(token);

Добавляем несколько базовых listeners, выполняющих метод console.log при запуске.

client.once('ready', () => {
 console.log('Ready!');
});
client.once('reconnecting', () => {
 console.log('Reconnecting!');
});
client.once('disconnect', () => {
 console.log('Disconnect!');
});

Теперь можно начать работу с ботом с помощью команды node. Бот должен быть online в discord и в консоль выведется“Ready!”

node index.js

Чтение сообщений

Бот находится на сервере и может выходить online. Теперь можно начать читать сообщения в чате и отвечать на них.

Для чтения сообщений нужно написать лишь одну простую функцию.

client.on('message', async message => {
  
}

Создаем listener для события message, получаем сообщение и сохраняем его в объект message.

Проверяем: если сообщение пришло от бота, то игнорируем его.

if (message.author.bot) return;

В этой строке проверяется, является ли автором сообщения бот. Сообщение возвращается, если это так.

Затем проверяем, начинается ли сообщение с ранее определенного префикса. Сообщение возвращается, если нет.

if (!message.content.startsWith(prefix)) return;

После этого проверяем, какую команду нужно запустить. Это можно выполнить с помощью простых операторов if.

const serverQueue = queue.get(message.guild.id);

if (message.content.startsWith(`${prefix}play`)) {
 execute(message, serverQueue);
 return;
} else if (message.content.startsWith(`${prefix}skip`)) {
 skip(message, serverQueue);
 return;
} else if (message.content.startsWith(`${prefix}stop`)) {
 stop(message, serverQueue);
 return;
} else {
 message.channel.send('You need to enter a valid command!')
}

В этом блоке кода проверяется, какую команду нужно запустить, а также осуществляется вызов команды. Если полученная команда недопустима, то вводим сообщение об ошибке в чат с использованием функции send().

Узнав, какие команды нужно запустить, можно перейти к их реализации.

Добавление песен

Начнем с добавления команды play. Для этого понадобится песня и гильдия (гильдия представляет собой изолированную коллекцию пользователей и каналов и часто упоминается в качестве сервера). Также понадобится ранее установленная библиотека ytdl.

Для начала создаем map с названием очереди, в котором будут сохранены все песни, введенные в чат.

const queue = new Map();

Затем создаем функцию async под названием execute и проверяем, находится ли пользователь в голосовом чате, и есть ли у бота соответствующее разрешение. Если нет, то пишем сообщение об ошибке и возвращаем.

async function execute(message, serverQueue) {
 const args = message.content.split(' ');
 const voiceChannel = message.member.voiceChannel;
 if (!voiceChannel) return message.channel.send('You need to be in a voice channel to play music!');
  const permissions =     voiceChannel.permissionsFor(message.client.user);
 if (!permissions.has('CONNECT') || !permissions.has('SPEAK')) {
  return message.channel.send('I need the permissions to join and   speak in your voice channel!');
 }
}

Переходим к получению информации о песне и сохранении ее в объект song. Для этого используем библиотеку ytdl, которая получает информацию о песне по ссылке на youtube.

const songInfo = await ytdl.getInfo(args[1]);
const song = {
 title: songInfo.title,
 url: songInfo.video_url,
};

Необходимая информация сохраняется в объект song.

После сохранения информации нужно создать контракт для добавления в очередь. Для этого проверяем, определен ли serverQueue, что означает, что музыка уже играет. Если да, то добавляем песню в существующий serverQueue и отправляем сообщение об успешном выполнении. Если нет, то создаем его, подключаемся к голосовому каналу и начинаем проигрывать музыку.

if (!serverQueue) {
  
}else {
 serverQueue.songs.push(song);
 console.log(serverQueue.songs);
 return message.channel.send(`${song.title} has been added to the queue!`);
}

В этом фрагменте мы проверяем, является ли serverQueue пустым. Если нет добавляем туда песню. 

Если serverQueue имеет значение null, создаем контракт.

// Creating the contract for our queue
const queueContruct = {
 textChannel: message.channel,
 voiceChannel: voiceChannel,
 connection: null,
 songs: [],
 volume: 5,
 playing: true,
};
// Setting the queue using our contract
queue.set(message.guild.id, queueContruct);
// Pushing the song to our songs array
queueContruct.songs.push(song);

try {
 // Here we try to join the voicechat and save our connection into our object.
 var connection = await voiceChannel.join();
 queueContruct.connection = connection;
 // Calling the play function to start a song
 play(message.guild, queueContruct.songs[0]);
} catch (err) {
 // Printing the error message if the bot fails to join the voicechat
 console.log(err);
 queue.delete(message.guild.id);
 return message.channel.send(err);
}

В этом блоке кода создается контракт, а песня добавляется в массив songs. 

Затем присоединяемся к голосовому чату пользователя и вызываем функцию play(), которую затем реализуем.

Проигрывание песен

Поскольку теперь можно добавлять песни в очередь и создавать контракт при его отсутствии, можно приступить к реализации функцию проигрывания.

Сначала создаем функцию play, которая обладает двумя параметрами (гильдия и песня, которую нужно проиграть) и проверяет, является ли объект song пустым. Если да, то покидаем голосовой канал и удаляем очередь.

function play(guild, song) {
 const serverQueue = queue.get(guild.id);
 if (!song) {
  serverQueue.voiceChannel.leave();
  queue.delete(guild.id);
  return;
 }
}

Затем начинаем проигрывать песню с помощью функции playStream() и URL-адреса песни.

const dispatcher = serverQueue.connection.playStream(ytdl(song.url))
 .on('end', () => {
  console.log('Music ended!');
  // Deletes the finished song from the queue
  serverQueue.songs.shift();
  // Calls the play function again with the next song
  play(guild, serverQueue.songs[0]);
})
 .on('error', error => {
  console.error(error);
 });
dispatcher.setVolumeLogarithmic(serverQueue.volume / 5);

В этом фрагменте мы создаем stream и передаем его URL-адресу песни. Также добавляем два listeners, которые обрабатывают события end и error.

Примечание: это рекурсивная функция, которая повторяет вызов самой себя. Рекурсия используется для проигрывания следующей песни, когда другая заканчивается.

Теперь можно проиграть песню, введя !play URL в чат.

Пропуск песен

Переходим к реализации функции пропуска. Для этого нужно выполнить завершения диспетчера, созданного в функции play() для начала проигрывания следующей песни.

function skip(message, serverQueue) {
 if (!message.member.voiceChannel) return message.channel.send('You have to be in a voice channel to stop the music!');
 if (!serverQueue) return message.channel.send('There is no song that I could skip!');
 serverQueue.connection.dispatcher.end();
}

В этом фрагменте мы проверяем, находится ли пользователь, который ввел команду, в голосовом канале, а также есть ли песни для пропуска.

Остановка песен

Функция stop() похожа на skip(), за исключением того, что массив songs очищается, из-за чего бот удаляет очередь и покидает голосовой чат.

function stop(message, serverQueue) {
 if (!message.member.voiceChannel) return message.channel.send('You have to be in a voice channel to stop the music!');
 serverQueue.songs = [];
 serverQueue.connection.dispatcher.end();
}

Исходный код для index.js:

Полный исходный код для музыкального бота:

const Discord = require('discord.js');
const {
 prefix,
 token,
} = require('./config.json');
const ytdl = require('ytdl-core');

const client = new Discord.Client();

const queue = new Map();

client.once('ready', () => {
 console.log('Ready!');
});

client.once('reconnecting', () => {
 console.log('Reconnecting!');
});

client.once('disconnect', () => {
 console.log('Disconnect!');
});

client.on('message', async message => {
 if (message.author.bot) return;
 if (!message.content.startsWith(prefix)) return;

 const serverQueue = queue.get(message.guild.id);

 if (message.content.startsWith(`${prefix}play`)) {
 execute(message, serverQueue);
 return;
 } else if (message.content.startsWith(`${prefix}skip`)) {
 skip(message, serverQueue);
 return;
 } else if (message.content.startsWith(`${prefix}stop`)) {
 stop(message, serverQueue);
 return;
 } else {
 message.channel.send('You need to enter a valid command!')
 }
});

async function execute(message, serverQueue) {
 const args = message.content.split(' ');

 const voiceChannel = message.member.voiceChannel;
 if (!voiceChannel) return message.channel.send('You need to be in a voice channel to play music!');
 const permissions = voiceChannel.permissionsFor(message.client.user);
 if (!permissions.has('CONNECT') || !permissions.has('SPEAK')) {
 return message.channel.send('I need the permissions to join and speak in your voice channel!');
 }

 const songInfo = await ytdl.getInfo(args[1]);
 const song = {
 title: songInfo.title,
 url: songInfo.video_url,
 };

 if (!serverQueue) {
 const queueContruct = {
 textChannel: message.channel,
 voiceChannel: voiceChannel,
 connection: null,
 songs: [],
 volume: 5,
 playing: true,
 };

 queue.set(message.guild.id, queueContruct);

 queueContruct.songs.push(song);

 try {
 var connection = await voiceChannel.join();
 queueContruct.connection = connection;
 play(message.guild, queueContruct.songs[0]);
 } catch (err) {
 console.log(err);
 queue.delete(message.guild.id);
 return message.channel.send(err);
 }
 } else {
 serverQueue.songs.push(song);
 console.log(serverQueue.songs);
 return message.channel.send(`${song.title} has been added to the queue!`);
 }

}

function skip(message, serverQueue) {
 if (!message.member.voiceChannel) return message.channel.send('You have to be in a voice channel to stop the music!');
 if (!serverQueue) return message.channel.send('There is no song that I could skip!');
 serverQueue.connection.dispatcher.end();
}

function stop(message, serverQueue) {
 if (!message.member.voiceChannel) return message.channel.send('You have to be in a voice channel to stop the music!');
 serverQueue.songs = [];
 serverQueue.connection.dispatcher.end();
}

function play(guild, song) {
 const serverQueue = queue.get(guild.id);

 if (!song) {
 serverQueue.voiceChannel.leave();
 queue.delete(guild.id);
 return;
 }

 const dispatcher = serverQueue.connection.playStream(ytdl(song.url))
 .on('end', () => {
 console.log('Music ended!');
 serverQueue.songs.shift();
 play(guild, serverQueue.songs[0]);
 })
 .on('error', error => {
 console.error(error);
 });
 dispatcher.setVolumeLogarithmic(serverQueue.volume / 5);
}

client.login(token);

Заключение

У вас все получилось! Надеюсь, эта статья помогла вам разобраться в API Discord и создании с его помощью простого бота.

Читайте также:


Перевод статьи Gabriel Tanner: How to create a music bot using Discord.js