あなたの不和の音楽コマンドを書く.JSボット(2020年3月更新)


11月20日:このガイドは、再生コマンドのコードがすべての時間を変更するため、時代遅れです.私は私のボットのクローンをお勧めしますGitHub あなたが保存されたプレイリストのような機能を持つ音楽ボットを探しているなら
あなたがボットを設定する方法についてのガイドが必要な場合は、お読みください
不条理は、人気の声とゲーマーや非ゲーマーのためのチャットアプリは、以前のようにボイスサーバーを支払うことなく、友人と通信するために使用されます.
ほぼすべての大きなサーバーは、それを管理するために使用することができます不条理ボット、BANとキック、GIFと再生音楽のようなコマンドを管理します.では、なぜあなたが無料で1つを作成することができます音楽ボットを支払う?
時間やエネルギーは、このガイドを読むか?ちょうど私の音楽ボットGithub , 前提条件セクションに従ってくださいnpm install そして、あなたは作業音楽ボットを持っているよ!
このガイドでは、すでに設定されたコマンドを使用して基本的な不協和ロボットを持っていると仮定します.ボットが不和を使用しないならば.私は非常にあなたが読むことをお勧めしますthis guide として、コマンドはあなたの人生はとても簡単になり、それはこのガイドに必要です.
音楽コマンドのコードが利用可能ですhere .

必要条件
確認するffmpeg , python 2.7 , とノード( v 12 atleast !インストール済みです.
YouTube APIキーを取得します.

パッケージのインストール
次のように動作します.
NPM :npm install discordjs/discord.js discordjs/Commando ffmpeg-static node-opus simple-youtube-api ytdl-coreyarn add discordjs/discord.js discordjs/Commando ffmpeg-static node-opus simple-youtube-api ytdl-core@latest
インデックス.JS (メインファイル)
遊ぶ前に.JSは、ギルドキューを保持するプロパティを追加できるように、ギルドクラスを拡張する必要があります.これはボットが一度に複数のサーバーで音楽を再生することができます.
そのためにはインデックスの先頭に「構造」をインポートします.JSを使ってギルドクラスを拡張します.
// your index.js should look similar to this:
const { CommandoClient } = require('discord.js-commando');
const { Structures } = require('discord.js');
const path = require('path');
const { prefix, token } = require('./config.json');
// It's vital this is before the initiation of the client
Structures.extend('Guild', Guild => {
  class MusicGuild extends Guild {
    constructor(client, data) {
      super(client, data);
      this.musicData = {
        queue: [],
        isPlaying: false,
        volume: 1,
        songDispatcher: null
      };
    }
  }
  return MusicGuild;
});
const client = new CommandoClient({
  commandPrefix: prefix,
  owner: 'your-discord-user-id',
  unknownCommandResponse: false
});

client.registry
  .registerDefaultTypes()
  .registerGroups([
    ['music', 'Music Command Group']
  ])
  .registerDefaultGroups()
  .registerDefaultCommands()
  .registerCommandsIn(path.join(__dirname, 'commands'));

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

client.login(token);


プレイ.js
あなたの“コマンド”フォルダーで、音楽という名前のフォルダーを作成し、それを再生中に作成します.js
パッケージとYouTube APIキーのインポートから始めます.
const { Command } = require('discord.js-commando');
const { MessageEmbed } = require('discord.js');
const Youtube = require('simple-youtube-api');
const ytdl = require('ytdl-core');
const { youtubeAPI } = require('../../config.json');
const youtube = new Youtube(youtubeAPI);
次に' command 'クラスを宣言します.
module.exports = class PlayCommand extends Command {
  constructor(client) {
    super(client, {
      name: 'play', 
      memberName: 'play',
      group: 'music', // this means the folder the file is inside
      description: 'Play any song or playlist from youtube',
      guildOnly: true, // make this command available only in servers not dm's
      clientPermissions: ['SPEAK', 'CONNECT'],
      args: [
        {
          key: 'query', // here we name the variable that will hold the input
          prompt: 'What song would you like to listen to?', // send this msg if
          // the user hasn't provided any arg or if the arg was not a string
          type: 'string',
          validate: query => query.length > 0 && query.length < 200 
        }
      ]
    });
  }
すべてのコマンドはRUNメソッド(コマンドが使用されたときにBOTを実行するコード)から始まります.
  async run(message, { query }) {
    // don't let users run this command if they are not in a voice channel
    var voiceChannel = message.member.voice.channel;
    if (!voiceChannel) return message.say('Join a channel and try again');
ユーザはこのコマンドを実行する際に3つのオプションを持っています:
  • 歌名でそれを走らせてください
  • YouTube URLでURLを実行する
  • YouTubeプレイリストURLでそれを走らせてください
  • 例えば、
    !play Darude Sandstorm
    !play https://www.youtube.com/watch?v=y6120QOlsfU (and other url kinds)
    !play https://www.youtube.com/playlist?list=PLuUrokoVSxlfUJuJB_D8j_wsFR4exaEmy
    
    そのためには、if文をregex YouTubeのURLの任意の種類.入力が正規表現と一致するならば、我々は我々が歌名によって質問に応募するものより異なる論理を適用します.
    まず最初に、問い合わせがプレイリストURLであるかどうかを調べます.
        if (
          query.match(
            /^(?!.*\?.*\bv=)https:\/\/www\.youtube\.com\/.*\?.*\blist=.*$/
          )
        ) {
          try {
            const playlist = await youtube.getPlaylist(query); // get playlist data 
            const videosObj = await playlist.getVideos(); // songs data object
            //const videos = Object.entries(videosObj); // turn the object to array
            // iterate through the videos array and make a song object out of each vid
            for (let i = 0; i < videosObj.length; i++) { 
              const video = await videosObj[i].fetch();
    
              const url = `https://www.youtube.com/watch?v=${video.raw.id}`;
              const title = video.raw.snippet.title;
              let duration = this.formatDuration(video.duration);
              const thumbnail = video.thumbnails.high.url;
              if (duration == '00:00') duration = 'Live Stream';
              const song = {
                url,
                title,
                duration,
                thumbnail,
                voiceChannel
              };
    
              message.guild.musicData.queue.push(song); // if you remember, the queue lives in the guild object so each server has its own queue
    
            }
            if (message.guild.musicData.isPlaying == false) { // if nothing is playing
              message.guild.musicData.isPlaying = true;
              return this.playSong(message.guild.musicData.queue, message); // play the playlist
            } else if (message.guild.musicData.isPlaying == true) { // if something is already playing
              return message.say(
                `Playlist - :musical_note:  ${playlist.title} :musical_note: has been added to queue`
              );
            }
          } catch (err) {
            console.error(err);
            return message.say('Playlist is either private or it does not exist');
          }
        }
    
    YouTube url regex :
        if (query.match(/^(http(s)?:\/\/)?((w){3}.)?youtu(be|.be)?(\.com)?\/.+/)) {
          const url = query; // temp variable
          try {
            query = query
              .replace(/(>|<)/gi, '')
              .split(/(vi\/|v=|\/v\/|youtu\.be\/|\/embed\/)/);
            const id = query[2].split(/[^0-9a-z_\-]/i)[0];
            const video = await youtube.getVideoByID(id);
            const title = video.title;
            let duration = this.formatDuration(video.duration);
            const thumbnail = video.thumbnails.high.url;
            if (duration == '00:00') duration = 'Live Stream';
            const song = {
              url,
              title,
              duration,
              thumbnail,
              voiceChannel
            };
            message.guild.musicData.queue.push(song);
            if (
              message.guild.musicData.isPlaying == false ||
              typeof message.guild.musicData.isPlaying == 'undefined'
            ) {
              message.guild.musicData.isPlaying = true;
              return this.playSong(message.guild.musicData.queue, message);
            } else if (message.guild.musicData.isPlaying == true) {
              return message.say(`${song.title} added to queue`);
            }
          } catch (err) {
            console.error(err);
            return message.say('Something went wrong, please try again later');
          }
        }
    
    
    ユーザが引数として曲名を入力した場合、
        try {
          // search for the song and get 5 results back
          const videos = await youtube.searchVideos(query, 5);
          if (videos.length < 5) {
            return message.say(
              `I had some trouble finding what you were looking for, please try again or be more specific`
            );
          }
          const vidNameArr = [];
          // create an array that contains the result titles
          for (let i = 0; i < videos.length; i++) {
            vidNameArr.push(`${i + 1}: ${videos[i].title}`);
          }
          vidNameArr.push('exit'); // push 'exit' string as it will be an option
          // create and display an embed which will present the user the 5 results
          // so he can choose his desired result
          const embed = new MessageEmbed()
            .setColor('#e9f931')
            .setTitle('Choose a song by commenting a number between 1 and 5')
            .addField('Song 1', vidNameArr[0])
            .addField('Song 2', vidNameArr[1])
            .addField('Song 3', vidNameArr[2])
            .addField('Song 4', vidNameArr[3])
            .addField('Song 5', vidNameArr[4])
            .addField('Exit', 'exit'); // user can reply with 'exit' if none matches
          var songEmbed = await message.say({ embed });
          try {
            // wait 1 minute for the user's response
            var response = await message.channel.awaitMessages(
              msg => (msg.content > 0 && msg.content < 6) || msg.content === 'exit',
              {
                max: 1,
                maxProcessed: 1,
                time: 60000,
                errors: ['time']
              }
            );
            // assign videoIndex to user's response
            var videoIndex = parseInt(response.first().content);
          } catch (err) {
            console.error(err);
            songEmbed.delete();
            return message.say(
              'Please try again and enter a number between 1 and 5 or exit'
            );
          }
          // if the user responded with 'exit', cancel the command
          if (response.first().content === 'exit') return songEmbed.delete();
          try {
            // get video data from the API
            var video = await youtube.getVideoByID(videos[videoIndex - 1].id);
          } catch (err) {
            console.error(err);
            songEmbed.delete();
            return message.say(
              'An error has occured when trying to get the video ID from youtube'
            );
          }
          const url = `https://www.youtube.com/watch?v=${video.raw.id}`;
          const title = video.title;
          let duration = this.formatDuration(video.duration);
          const thumbnail = video.thumbnails.high.url;
            if (duration == '00:00') duration = 'Live Stream';
            const song = {
              url,
              title,
              duration,
              thumbnail,
              voiceChannel
            };
    
            message.guild.musicData.queue.push(song);
    
            if (message.guild.musicData.isPlaying == false) {
              message.guild.musicData.isPlaying = true;
              songEmbed.delete(); // delete the selection embed
              this.playSong(message.guild.musicData.queue, message);
            } else if (message.guild.musicData.isPlaying == true) {
              songEmbed.delete();
              // add the song to queue if one is already playing
              return message.say(`${song.title} added to queue`);
            }
        } catch (err) {
          // if something went wrong when calling the api:
          console.error(err);
          if (songEmbed) {
            songEmbed.delete();
          }
          return message.say(
            'Something went wrong with searching the video you requested :('
          );
        }
      }
    
    それで、我々は上記の倍数と呼ばれたそのPlaysong機能は何ですか?この関数はキューとメッセージオブジェクトを引数として受け取ります.呼び出されると、それはボットのユーザーのチャンネルに参加し、音楽を再生を開始指示します!
    // this is inside the PlayCommand class
      playSong(queue, message) {
        let voiceChannel;
        queue[0].voiceChannel
          .join() // join the user's voice channel
          .then(connection => {
            const dispatcher = connection
              .play(
                ytdl(queue[0].url, { // pass the url to .ytdl()
                  quality: 'highestaudio',
                  // download part of the song before playing it
                  // helps reduces stuttering
                  highWaterMark: 1024 * 1024 * 10
                })
              )
              .on('start', () => {
                // the following line is essential to other commands like skip
                message.guild.musicData.songDispatcher = dispatcher;
                dispatcher.setVolume(message.guild.musicData.volume);
                voiceChannel = queue[0].voiceChannel;
                // display the current playing song as a nice little embed
                const videoEmbed = new MessageEmbed()
                  .setThumbnail(queue[0].thumbnail) // song thumbnail
                  .setColor('#e9f931')
                  .addField('Now Playing:', queue[0].title)
                  .addField('Duration:', queue[0].duration);
                // also display next song title, if there is one in queue
                if (queue[1]) videoEmbed.addField('Next Song:', queue[1].title);
                message.say(videoEmbed); // send the embed to chat
                return queue.shift(); //  dequeue the song
              })
              .on('finish', () => { // this event fires when the song has ended
                if (queue.length >= 1) { // if there are more songs in queue
                  return this.playSong(queue, message); // continue playing
                } else { // else if there are no more songs in queue
                  message.guild.musicData.isPlaying = false;
                  return voiceChannel.leave(); // leave the voice channel
                }
              })
              .on('error', e => {
                message.say('Cannot play song');
                message.guild.musicData.queue.length = 0;
                message.guild.musicData.isPlaying = false;
                message.guild.musicData.nowPlaying = null;
                console.error(e);
                return voiceChannel.leave();
              });
          })
          .catch(e => {
            console.error(e);
            return voiceChannel.leave();
          });
      }
    
    書式指定関数
      formatDuration(durationObj) {
        const duration = `${durationObj.hours ? durationObj.hours + ':' : ''}${
          durationObj.minutes ? durationObj.minutes : '00'
        }:${
          durationObj.seconds < 10
            ? '0' + durationObj.seconds
            : durationObj.seconds
            ? durationObj.seconds
            : '00'
        }`;
        return duration;
      }
    

    それだ!
    あなたはボットの他の音楽コマンドをチェックアウトすることができますrepo
    あなたが問題に走らせているならば、以下のどちらかのコメントはボットのGithubリポジトリで問題を開きます.
    私はまた、音楽クイズ(トリビア)コマンドを書く上でガイドを書いている場合は、それをチェックアウトすることができます