[include(틀:Discord)] ||<-2><tablealign=right><tablewidth=250><tablebordercolor=#4b50c9><bgcolor=#4b50c9> {{{+3 {{{#000000 discord.js }}} }}} || ||<-2> [[파일:discordjs.png]] || || 필요한 Node.js 버전 || v12.0.0 이상 || || 최신 버전 || v12.5.1 || || 지원되는 가장 오래된 버전 || v11.0.0[* 2020년 10월에 지원 중단. 11월 22일 기준 아직 작동은 한다.] || ||<-2> 관련 링크 || || [[https://discord.js.org/|공식 웹사이트]] || [[https://github.com/discordjs/discord.js|[[파일:github-logo.png|width=25]]]] || || [[https://www.npmjs.com/package/discord.js|[[파일:npm.png|width=40]]]] || [[https://discord.gg/bRCvFy9|[[파일:디스코드 아이콘.png|width=25]]]] || [목차] [clearfix] == 개요 == [[디스코드]] 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@<버전번호>}}}로 설치할 수 있다[* 삼각괄호는 빼야한다.] == 개발 시 주의사항 == 우선 이 예제를 따라하기 전에, discord.Collection() 객체를 '''완벽히''' 사용할 수 있어야 한다. Collection 객체는 거의 모든 종류의 cache에서[* 유저, 길드 등] 리턴하는 값이기 때문에 .filter, .map 등의 메서드를 사용하는데 익숙하지 않다면 해당 기능들을 활용하기 어렵다. == 예제 == === 예제 1 === {{{#!syntax javascript 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#s-2.2]]로 개발하는 사람들에게서도 많이 볼 수 있는 문제이다. === 예제 2 === index.js {{{#!syntax javascript 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 {{{#!syntax javascript const Discord = require('discord.js'); //run이라는 메소드(function)을 export(수출) exports.run = (client, msg, args) => { msg.reply(`${client.ws.ping}ms`); }; exports = { name: 'ping' }; }}} 이런 식으로 파일을 분리하고 가져오는 것을 자동화하면 앞의 문제를 바로 해결할 수 있다. == Commando 방식 == 이는 discord.js Client가 아닌 discord.js CommandoClient를 사용하여 봇을 만드는 것이다. 위의 파일 핸들링의 방식을 이 라이브러리로 대체할 수 있으며, 또 sqlite3를 지원하여 데이터베이스 저장도 쉽다. CommandoClient는 단순히 discord.js Client의 확장이므로 Client에 있던 메서드, 클래스 등은 여기서도 사용할 수 있다. === 예제 === {{{#!syntax javascript 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 폴더에 저장하고 코드를 짜는건 [[https://github.com/discordjs/Commando/tree/master/test|여기]]에서 확인하면 된다. == discord RPC == discord.js에는 RPC 기능도 있다. ~~참 정말 기능이 많다~~ === 예제 === {{{#!syntax javascript 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 }); }}} === 사용 불가 === Discord Developer Portal에, RPC에 대한 신청을 더 이상 받지 않는다고 [[https://discord.com/developers/docs/topics/rpc|나와있다]]. 따라서 만들고 싶어도 만들지 못하며, 거의 필요가 없어진 라이브러리이다. == 여담 == * Commando는 데이터베이스에 저장, 명령어 인식 등이 자동화 되어있어 더 편하다고 느껴질 수 있겠지만 대부분은 사용하지 않는다. Commando는 너무 틀에 맞춰져있어 선호되지 않는다. * Windows XP/Vista에서[* 정확히는 Node.js 6.0 미만에서.] 봇을 호스팅할 수 있는 마지막 버전은 8.2.0이다. 9.x부터 Node.js 6.x, 12.x부터는 Node.js 12.x 버전을 요구한다. [[분류:프로그래밍]][[분류:라이브러리]][[분류:Discord]]