์ด ๋ฌธ์„œ์˜ ์›๋ณธ์€ ์™ธ๋ถ€ ์œ„ํ‚ค์—์„œ ๊ฐ€์ ธ์™”์Šต๋‹ˆ๋‹ค.
discord.js
ํ•„์š”ํ•œ Node.js ๋ฒ„์ „
v12.0.0 ์ด์ƒ
์ตœ์‹  ๋ฒ„์ „
v12.5.1
์ง€์›๋˜๋Š” ๊ฐ€์žฅ ์˜ค๋ž˜๋œ ๋ฒ„์ „
v11.0.0[1]
๊ด€๋ จ ๋งํฌ

1. ๊ฐœ์š”2. ๊ฐœ๋ฐœ ์‹œ ์ฃผ์˜์‚ฌํ•ญ3. ์˜ˆ์ œ
3.1. ์˜ˆ์ œ 13.2. ์˜ˆ์ œ 2
4. Commando ๋ฐฉ์‹
4.1. ์˜ˆ์ œ
5. discord RPC
5.1. ์˜ˆ์ œ5.2. ์‚ฌ์šฉ ๋ถˆ๊ฐ€
6. ์—ฌ๋‹ด

1. ๊ฐœ์š”[ํŽธ์ง‘]

๋””์Šค์ฝ”๋“œ API๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ด‡์„ ๋งŒ๋“œ๋Š” Node.js ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค.

discord.js๋Š” npm๋˜๋Š” yarn์—์„œ ์„ค์น˜ํ•  ์ˆ˜ ์žˆ๋‹ค.

npm์—์„œ๋Š”
npm install discord.js

yarn์—์„œ๋Š”
yarn add discord.js
์ด๋ ‡๊ฒŒ ์„ค์น˜ ๊ฐ€๋Šฅํ•˜๋‹ค.

ํ˜„์žฌ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋กœ ๋””์Šค์ฝ”๋“œ ๋ด‡์„ ๊ฐœ๋ฐœํ•  ๋•Œ ๊ฐ€์žฅ ๋งŽ์ด ์“ฐ์ด๊ณ  ์žˆ์œผ๋ฉฐ, discord RPC๋ฅผ ์ง€์›ํ•˜๊ณ  Commando๋ผ๋Š” ๋ช…๋ น์–ด๋ฅผ ์•Œ์•„์„œ ํ•ธ๋“ค๋งํ•ด์ฃผ๋Š” ๊ณต์‹ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์žˆ๋‹ค.

์ฐธ๊ณ ๋กœ ๊ตฌ๋ฒ„์ „์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ์—๋Š” npm์˜ ๊ฒฝ์šฐ npm install discord.js@<๋ฒ„์ „๋ฒˆํ˜ธ>๋กœ ์„ค์น˜ํ•  ์ˆ˜ ์žˆ๋‹ค[2]

2. ๊ฐœ๋ฐœ ์‹œ ์ฃผ์˜์‚ฌํ•ญ[ํŽธ์ง‘]

์šฐ์„  ์ด ์˜ˆ์ œ๋ฅผ ๋”ฐ๋ผํ•˜๊ธฐ ์ „์—, discord.Collection() ๊ฐ์ฒด๋ฅผ ์™„๋ฒฝํžˆ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค. Collection ๊ฐ์ฒด๋Š” ๊ฑฐ์˜ ๋ชจ๋“  ์ข…๋ฅ˜์˜ cache์—์„œ[3] ๋ฆฌํ„ดํ•˜๋Š” ๊ฐ’์ด๊ธฐ ๋•Œ๋ฌธ์— .filter, .map ๋“ฑ์˜ ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š”๋ฐ ์ต์ˆ™ํ•˜์ง€ ์•Š๋‹ค๋ฉด ํ•ด๋‹น ๊ธฐ๋Šฅ๋“ค์„ ํ™œ์šฉํ•˜๊ธฐ ์–ด๋ ต๋‹ค.

3. ์˜ˆ์ œ[ํŽธ์ง‘]

3.1. ์˜ˆ์ œ 1[ํŽธ์ง‘]

const Discord = require('discord.js');
const client = new Discord.Client(); // Discord.Client ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

client.on('ready', () => { // ready ์ด๋ฒคํŠธ์‹œ ์‹คํ–‰ํ•  ํ•จ์ˆ˜
  console.log(`Logged in as ${client.user.tag}!`); // client.user ๋Š” ์ž์‹ ์˜ ์œ ์ € ๊ฐ์ฒด์ด๊ณ  tag ๋Š” ์œ ์ € ๊ฐ์ฒด์˜ ํ”„๋กœํผํ‹ฐ ์ž…๋‹ˆ๋‹ค.
});

client.on('message', msg => { // message ์ด๋ฒคํŠธ์‹œ msg (Discord.Message) ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ๋ฐ›๊ณ  ์‹คํ–‰ํ•  ํ•จ์ˆ˜
  if (msg.content === 'ping') { // Discord.Message ๊ฐ์ฒด์˜ content ํ”„๋กœํผํ‹ฐ๊ฐ€ 'ping' ์ผ ๋•Œ
    msg.reply('Pong!'); // reply ๋Š” ๋ฉ˜์…˜ + , msg ๋กœ ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค.
  }
});

client.login('token'); // ํ† ํฐ์„ ์ž…๋ ฅํ•ฉ๋‹ˆ๋‹ค. ์˜ฌ๋ฐ”๋ฅด์ง€ ์•Š์€ ํ† ํฐ์ผ ์‹œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.


์ด ์ฝ”๋“œ๋Š” if ๋ฌธ์œผ๋กœ ๋ช…๋ น์–ด๋ฅผ ํŒ๋‹จํ•œ๋‹ค. ํ•˜์ง€๋งŒ ์ด๋Ÿฐ ์‹์œผ๋กœ ํ•œ ํŒŒ์ผ์— if ๋ฌธ์„ ๋Š˜๋ ค๊ฐ€๋‹ค ๋ณด๋ฉด ์–ด๋А์ƒŒ๊ฐ€ ์ฝ”๋“œ๊ฐ€ 300์ค„, 500์ค„, ์‹ฌํ•˜๋ฉด 3000์ค„๊นŒ์ง€ ๋ถˆ์–ด๋‚˜๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ํ•ด๋‹น ๋ฌธ์ œ๋Š” ์ŠคํŒŒ๊ฒŒํ‹ฐ ์ฝ”๋“œ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ฌ์ˆ˜ ์žˆ์–ด ํ›„์— ์œ ์ง€ ๋ณด์ˆ˜๊ฐ€ ๋งค์šฐ ์–ด๋ ค์›Œ์ง€๊ธฐ ๋•Œ๋ฌธ์— ๋งŽ์€ ์œ ๋ช…ํ•œ discord.js ๋ด‡ ๋“ค์€ ๊ฑฐ์˜ ๋‹ค ๋ช…๋ น์–ด ๋˜๋Š” ์ด๋ฒคํŠธ ํŒŒ์ผ์„ ๋ถ„๋ฆฌํ•˜๊ณ , ๋ช…๋ น์–ด ์ถ”๊ฐ€๋ฅผ ์ž๋™ํ™”ํ•˜๊ณ , ๊ฐ€๋…์„ฑ์„ ์ค‘์š”์‹œํ•œ๋‹ค. ๋”ฐ๋ผ์„œ ์˜ˆ์ œ 1์€ ๋ณ„๋กœ ์ข‹์ง€ ์•Š์€ ์˜ˆ์‹œ์ด๋‹ค. ์ด ๋ฌธ์ œ์ ์€ discord.py๋กœ ๊ฐœ๋ฐœํ•˜๋Š” ์‚ฌ๋žŒ๋“ค์—๊ฒŒ์„œ๋„ ๋งŽ์ด ๋ณผ ์ˆ˜ ์žˆ๋Š” ๋ฌธ์ œ์ด๋‹ค.

3.2. ์˜ˆ์ œ 2[ํŽธ์ง‘]

index.js
const Discord = require('discord.js');
const client = new Discord.Client();
const fs = require('fs');
const prefix = '!';

client.commands = new Discord.Collection() 
// ๋ช…๋ น์–ด ์บ์‹œ ์ปฌ๋ ‰์…˜์„ ํด๋ผ์ด์–ธํŠธ ๋‚ด์— ์„ ์–ธํ•œ๋‹ค. ํ•ด๋‹น ๋ฐฉ๋ฒ•์œผ๋กœ ๋ช…๋ น์–ด ํŒŒ์ผ ๋‚ด์—์„œ๋„ client.commands๋กœ ๋‹ค๋ฅธ ๋ช…๋ น์–ด๋“ค์— ์ ‘๊ทผํ• ์ˆ˜ ์žˆ๋‹ค.

client.commands.load = dir => {
    for (const file of fs.readdirSync(dir)) {
      const cmd = require(`./commands/${file}`);
      client.commands.set(cmd.name, cmd);
    }
    console.log(client.commands.map(c => c.name).join(', ') + ' ๋ช…๋ น์–ด๊ฐ€ ๋กœ๋“œ๋จ.');
}

client.commands.load(__dirname + "/commands");
//ํ•ด๋‹น ํŒŒ์ผ์ด ์œ„์น˜ํ•œ ๋””๋ ‰ํ„ฐ๋ฆฌ์—์„œ "/commands" ๊ฒฝ๋กœ๋ฅผ ์ถ”๊ฐ€

client.on('ready', () => console.log(`${client.user.tag} ์— ๋กœ๊ทธ์ธ๋จ`));

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

    const args = msg.content.slice(prefix.length).trim().split(/ +/g);
    const command = args.shift().toLowerCase();

    let cmd = client.commands.get(command);
    //get๋Š” ์ปฌ๋ ‰์…˜ ๋‚ด์— ํ•ด๋‹น key ๊ฐ’์„ ๊ฐ€์ง„ ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์œผ๋ฉด falsy ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๋ฏ€๋กœ ๋ถ€๋ถ„์ ์œผ๋กœ Collection#has์ฒ˜๋Ÿผ ์‚ฌ์šฉํ• ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    if(cmd) cmd.run(client, msg, args);
})

client.login('token');


commands/ping.js
const Discord = require('discord.js');

//run์ด๋ผ๋Š” ๋ฉ”์†Œ๋“œ(function)์„ export(์ˆ˜์ถœ)
exports.run = (client, msg, args) => {
    msg.reply(`${client.ws.ping}ms`);
};

exports = {
    name: 'ping'
};


์ด๋Ÿฐ ์‹์œผ๋กœ ํŒŒ์ผ์„ ๋ถ„๋ฆฌํ•˜๊ณ  ๊ฐ€์ ธ์˜ค๋Š” ๊ฒƒ์„ ์ž๋™ํ™”ํ•˜๋ฉด ์•ž์˜ ๋ฌธ์ œ๋ฅผ ๋ฐ”๋กœ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.

4. Commando ๋ฐฉ์‹[ํŽธ์ง‘]

์ด๋Š” discord.js Client๊ฐ€ ์•„๋‹Œ discord.js CommandoClient๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ด‡์„ ๋งŒ๋“œ๋Š” ๊ฒƒ์ด๋‹ค.
์œ„์˜ ํŒŒ์ผ ํ•ธ๋“ค๋ง์˜ ๋ฐฉ์‹์„ ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ๋Œ€์ฒดํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๋˜ sqlite3๋ฅผ ์ง€์›ํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ €์žฅ๋„ ์‰ฝ๋‹ค.
CommandoClient๋Š” ๋‹จ์ˆœํžˆ discord.js Client์˜ ํ™•์žฅ์ด๋ฏ€๋กœ Client์— ์žˆ๋˜ ๋ฉ”์„œ๋“œ, ํด๋ž˜์Šค ๋“ฑ์€ ์—ฌ๊ธฐ์„œ๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

4.1. ์˜ˆ์ œ[ํŽธ์ง‘]

const Commando = require('discord.js-commando')

const client = new Commando.Client({
    owner: '์†Œ์œ ์ž ์•„์ด๋””'
})

const path = require('path')

client.registry
    // ๋ช…๋ น์–ด๋“ค์˜ ๊ทธ๋ฃน๋“ค์„ ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค.
    .registerGroups([
        ['fun', 'Fun commands'],
        ['some', 'Some group'],
        ['other', 'Some other group']
    ])

    // ๊ธฐ๋ณธ ๋ช…๋ น์–ด, ๊ทธ๋ฃน ๋“ฑ์„ ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค.
    .registerDefaults()

    // ๋‹ค๋ฅธ ํด๋” (์—ฌ๊ธฐ์„œ๋Š” commands) ์— ์žˆ๋Š” ๋ช…๋ น์–ด ํŒŒ์ผ ๋“ค์„ ๋ถˆ๋Ÿฌ์˜ค๊ณ  ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค.
    .registerCommandsIn(path.join(__dirname, 'commands'));

const sqlite = require('sqlite');
// Commando์—๋Š” ๊ธธ๋“œ ๋ณ„ ์ ‘๋‘์‚ฌ, ๋ช…๋ น์–ด ํ™œ์„ฑํ™” ๋˜๋Š” ๋น„ํ™œ์„ฑํ™” ๋“ฑ์˜ ๊ธฐ๋Šฅ์ด ์žˆ์ง€๋งŒ, ์ด๋ฅผ ์ €์žฅํ•ด ๋†“์œผ๋ ค๋ฉด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊ฐ€ ํ•„์š”ํ•˜๊ธฐ ๋•Œ๋ฌธ์— sqlite๋ฅผ ์ด์šฉํ•ฉ๋‹ˆ๋‹ค.
client.setProvider(
    sqlite.open(path.join(__dirname, 'settings.sqlite3')).then(db => new Commando.SQLiteProvider(db))
).catch(console.error);

client.login('token goes here'); // ๋งˆ์ง€๋ง‰์œผ๋กœ discord.js Client ์ฒ˜๋Ÿผ ๋กœ๊ทธ์ธํ•ฉ๋‹ˆ๋‹ค.


์ด๋Ÿฌ๋ฉด ๋ชจ๋“  ์ค€๋น„๋Š” ๋์ด๋‹ค.
๋ช…๋ น์–ด๋ฅผ commands ํด๋”์— ์ €์žฅํ•˜๊ณ  ์ฝ”๋“œ๋ฅผ ์งœ๋Š”๊ฑด ์—ฌ๊ธฐ์—์„œ ํ™•์ธํ•˜๋ฉด ๋œ๋‹ค.

5. discord RPC[ํŽธ์ง‘]

discord.js์—๋Š” RPC ๊ธฐ๋Šฅ๋„ ์žˆ๋‹ค. ์ฐธ ์ •๋ง ๊ธฐ๋Šฅ์ด ๋งŽ๋‹ค

5.1. ์˜ˆ์ œ[ํŽธ์ง‘]

const clientId = '187406016902594560';
const scopes = ['rpc', 'rpc.api', 'messages.read'];

const client = new RPC.Client({ transport: 'websocket' });

client.on('ready', () => {
  console.log('Logged in as', client.application.name);
  console.log('Authenticated as user: ' + client.user.username);

  client.selectVoiceChannel('81384788862181376');
});

client.login({ clientId, scopes });

5.2. ์‚ฌ์šฉ ๋ถˆ๊ฐ€[ํŽธ์ง‘]

Discord Developer Portal์—, RPC์— ๋Œ€ํ•œ ์‹ ์ฒญ์„ ๋” ์ด์ƒ ๋ฐ›์ง€ ์•Š๋Š”๋‹ค๊ณ  ๋‚˜์™€์žˆ๋‹ค.
๋”ฐ๋ผ์„œ ๋งŒ๋“ค๊ณ  ์‹ถ์–ด๋„ ๋งŒ๋“ค์ง€ ๋ชปํ•˜๋ฉฐ, ๊ฑฐ์˜ ํ•„์š”๊ฐ€ ์—†์–ด์ง„ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค.

6. ์—ฌ๋‹ด[ํŽธ์ง‘]

  • Commando๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅ, ๋ช…๋ น์–ด ์ธ์‹ ๋“ฑ์ด ์ž๋™ํ™” ๋˜์–ด์žˆ์–ด ๋” ํŽธํ•˜๋‹ค๊ณ  ๋А๊ปด์งˆ ์ˆ˜ ์žˆ๊ฒ ์ง€๋งŒ ๋Œ€๋ถ€๋ถ„์€ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค. Commando๋Š” ๋„ˆ๋ฌด ํ‹€์— ๋งž์ถฐ์ ธ์žˆ์–ด ์„ ํ˜ธ๋˜์ง€ ์•Š๋Š”๋‹ค.
  • Windows XP/Vista์—์„œ[4] ๋ด‡์„ ํ˜ธ์ŠคํŒ…ํ•  ์ˆ˜ ์žˆ๋Š” ๋งˆ์ง€๋ง‰ ๋ฒ„์ „์€ 8.2.0์ด๋‹ค. 9.x๋ถ€ํ„ฐ Node.js 6.x, 12.x๋ถ€ํ„ฐ๋Š” Node.js 12.x ๋ฒ„์ „์„ ์š”๊ตฌํ•œ๋‹ค.
[1] 2020๋…„ 10์›”์— ์ง€์› ์ค‘๋‹จ. 11์›” 22์ผ ๊ธฐ์ค€ ์•„์ง ์ž‘๋™์€ ํ•œ๋‹ค.
[2] ์‚ผ๊ฐ๊ด„ํ˜ธ๋Š” ๋นผ์•ผํ•œ๋‹ค.
[3] ์œ ์ €, ๊ธธ๋“œ ๋“ฑ
[4] ์ •ํ™•ํžˆ๋Š” Node.js 6.0 ๋ฏธ๋งŒ์—์„œ.