나는 게임을 좋아한다.
공부를 한다고 2년 정도 안하다가 최근 다시 시작했다.
주로 하는 게임은 원피스 랜덤 디펜스(약칭 원랜디), 에이펙스 레전드 이 중 원랜디의 디스코드 봇을 만들어보고 싶어졌다.
큰 기능은 2가지로 생각했다.
1. 뽑기 기능 - 특정 캐릭터를 뽑는 기능
2. 코드 세이브/로드 기능 - 친구의 요청으로 넣게 된 기능
코드 세이브/로드 기능의 경우 OracleCloud의 DB와 연결하고 싶었지만 잘 되지 않아 notion API를 사용해보기로 하고, 배포는 OracleCloud free tier를 사용하기로 결정했다.
먼저 https://discord.com/developers/applications 로 들어가서 Application을 등록한다.
Bot 메뉴에서 bot을 추가해주고 View Token 버튼을 눌러 토큰값을 어딘가에 잘 저장해두고 유출되지 않게 조심하자.
그 후 아래 사진과 같이 OAuth2/URL Generator 메뉴에서 권한을 선택하고 하단의 url을 생성하여 링크로 들어가면 봇을 서버에 추가할 수 있다.
먼저 npm init
을 하여 프로젝트를 시작해주고 기본 정보를 작성해주고 npm install
을 해준다.
npm i discord.js@12.5.3
을 입력하여 discord.js를 설치해준다.
index.js 파일을 만들어주고
const Discord = require('discord.js'); // discord.js 라이브러리 호출
const client = new Discord.Client({ intents: ["GUILDS", "GUILD_MESSAGES"] }) // Client 객체 생성
const adCho = ['루피', '조로', '우솝', '쵸파', '로빈', '시라호시', '도플라밍고', '사보', '후지토라', '타시기', '바질 호킨스', '징베', '야마토', '료쿠규'];
const apCho = ['나미', '상디', '프랑키', '브룩', '샹크스', '시라호시', '검은수염', '아오키지', '아카이누', '키자루', '로우', '타시기', '루치', '스네이크맨', '키드'];
const adBool = ['로져', '레일리', '스코퍼 가반', '흰수염', '거프', '시키', '카이도'];
const apBool = ['센고쿠', '시키', '드래곤', '제트', '빅맘'];
const adYoung = ['핸콕', '카번딧슈', '버기', '니카'];
const apYoung = ['에이스', '핸콕', '비비', '우타'];
const adZe = ['크로커다일', '레베카', '카타쿠리', '킹'];
const apZe = ['에넬', '아인', '시노부', '레드필드'];
// discord 봇이 실행될 때 딱 한 번 실행할 코드를 적는 부분
client.on('ready', () => {
console.log(`Logged in as ${client.user.tag}!`);
});
client.on('message', msg => {
try {
let arr = [];
if (msg.content.indexOf('!뽑기') === 0) {
const detail = msg.content.split(' ')[1];
if(detail.indexOf('초') > -1){
arr = [...arr, ...adCho, ...apCho];
}
if(detail.indexOf('불') > -1){
arr = [...arr, ...adBool, ...apBool];
}
if(detail.indexOf('영') > -1){
arr = [...arr, ...adYoung, ...apYoung];
}
if(detail.indexOf('제') > -1){
arr = [...arr, ...adZe, ...apZe];
}
const set = new Set(arr);
const result = [...set];
msg.reply(set.size > 0 ? result[getRandom(set.size)] : '결과가 없습니다. 명령어를 확인해주세요.');
}
else if (msg.content.indexOf('!물뎀') === 0) {
const detail = msg.content.split(' ')[1];
if(detail.indexOf('초') > -1){
arr = [...arr, ...adCho];
}
if(detail.indexOf('불') > -1){
arr = [...arr, ...adBool];
}
if(detail.indexOf('영') > -1){
arr = [...arr, ...adYoung];
}
if(detail.indexOf('제') > -1){
arr = [...arr, ...adZe];
}
const set = new Set(arr);
const result = [...set];
msg.reply(set.size > 0 ? result[getRandom(set.size)] : '결과가 없습니다. 명령어를 확인해주세요.');
}
else if (msg.content.indexOf('!마뎀') === 0) {
const detail = msg.content.split(' ')[1];
if(detail.indexOf('초') > -1){
arr = [...arr, ...apCho];
}
if(detail.indexOf('불') > -1){
arr = [...arr, ...apBool];
}
if(detail.indexOf('영') > -1){
arr = [...arr, ...apYoung];
}
if(detail.indexOf('제') > -1){
arr = [...arr, ...apZe];
}
const set = new Set(arr);
const result = [...set];
msg.reply(set.size > 0 ? result[getRandom(set.size)] : '결과가 없습니다. 명령어를 확인해주세요.');
}
else if (msg.content.indexOf('!악몽') === 0) {
const cho = new Set([...adCho, ...apCho]);
let arrCho = [...cho];
const bool = new Set([...adBool, ...apBool]);
let arrBool = [...bool];
const young = new Set([...adYoung, ...apYoung]);
let arrYoung = [...young];
const ze = new Set([...adZe, ...apZe]);
let arrZe = [...ze];
for(let i = 0; i<7; i++){
arrCho.splice(getRandom(arrCho.length),1);
if(i <= 1){
arrBool.splice(getRandom(arrBool.length),1);
arrZe.splice(getRandom(arrZe.length),1);
}
if(i == 0) arrYoung.splice(getRandom(arrYoung.length),1);
}
msg.reply(`
악몽 룰을 적용합니다.
초월은 ${arrCho.join(', ')}
불멸은 ${arrBool.join(', ')}
영원은 ${arrYoung.join(', ')}
제한은 ${arrZe.join(', ')}
만 갈 수 있습니다.`);
}
}catch (e) {
console.log(e);
}
});
const getRandom = (max) => Math.floor(Math.random() * max);
// 봇과 서버를 연결해주는 부분
client.login('${discord token 값을 넣어주세요}');
위와 같이 간단한 코드를 작성하여 뽑기 코드를 작성해주었습니다.
https://www.notion.so/my-integrations 로 이동하여 새 API 통합을 진행합니다. api 키 값을 잘 복사해둡니다.
그 후 DB 페이지를 만들고 API와 연결을 해줍니다.
이후 링크 복사 시 https://www.notion.so/____?v=299c8&pvs=4
___ 위치의 값이 DB ID에 해당하는 값입니다. 잘 복사해둡니다.
index.js 위치로 다시 이동하여 npm install @notionhq/client
와 npm install dotenv --save
하여 notion api에 필요한 라이브러리를 설치해줍니다.
touch .env
를 입력하여 .env 파일을 만들어주고
NOTION_API_KEY=""
NOTION_DATABASE_ID=""
형태로 편집해줍니다.
다시 index.js로 돌아가서 notion 과 연결해주는 코드를 추가하고 save, load 기능을 추가해 주었습니다.
const Discord = require('discord.js'); // discord.js 라이브러리 호출
const client = new Discord.Client({ intents: ["GUILDS", "GUILD_MESSAGES"] }) // Client 객체 생성
const { Client } = require('@notionhq/client');
const dotenv = require('dotenv');
dotenv.config();
const notion = new Client({ auth: process.env.NOTION_API_KEY });
const adCho = ['루피', '조로', '우솝', '쵸파', '로빈', '시라호시', '도플라밍고', '사보', '후지토라', '타시기', '바질 호킨스', '징베', '야마토', '료쿠규'];
const apCho = ['나미', '상디', '프랑키', '브룩', '샹크스', '시라호시', '검은수염', '아오키지', '아카이누', '키자루', '로우', '타시기', '루치', '스네이크맨', '키드'];
const adBool = ['로져', '레일리', '스코퍼 가반', '흰수염', '거프', '시키', '카이도'];
const apBool = ['센고쿠', '시키', '드래곤', '제트', '빅맘'];
const adYoung = ['핸콕', '카번딧슈', '버기', '니카'];
const apYoung = ['에이스', '핸콕', '비비', '우타'];
const adZe = ['크로커다일', '레베카', '카타쿠리', '킹'];
const apZe = ['에넬', '아인', '시노부', '레드필드'];
// discord 봇이 실행될 때 딱 한 번 실행할 코드를 적는 부분
client.on('ready', () => {
console.log(`Logged in as ${client.user.tag}!`);
});
client.on('message', msg => {
try {
let arr = [];
if (msg.content.indexOf('!뽑기') === 0) {
const detail = msg.content.split(' ')[1];
if(detail.indexOf('초') > -1){
arr = [...arr, ...adCho, ...apCho];
}
if(detail.indexOf('불') > -1){
arr = [...arr, ...adBool, ...apBool];
}
if(detail.indexOf('영') > -1){
arr = [...arr, ...adYoung, ...apYoung];
}
if(detail.indexOf('제') > -1){
arr = [...arr, ...adZe, ...apZe];
}
const set = new Set(arr);
const result = [...set];
msg.reply(set.size > 0 ? result[getRandom(set.size)] : '결과가 없습니다. 명령어를 확인해주세요.');
}
else if (msg.content.indexOf('!물뎀') === 0) {
const detail = msg.content.split(' ')[1];
if(detail.indexOf('초') > -1){
arr = [...arr, ...adCho];
}
if(detail.indexOf('불') > -1){
arr = [...arr, ...adBool];
}
if(detail.indexOf('영') > -1){
arr = [...arr, ...adYoung];
}
if(detail.indexOf('제') > -1){
arr = [...arr, ...adZe];
}
const set = new Set(arr);
const result = [...set];
msg.reply(set.size > 0 ? result[getRandom(set.size)] : '결과가 없습니다. 명령어를 확인해주세요.');
}
else if (msg.content.indexOf('!마뎀') === 0) {
const detail = msg.content.split(' ')[1];
if(detail.indexOf('초') > -1){
arr = [...arr, ...apCho];
}
if(detail.indexOf('불') > -1){
arr = [...arr, ...apBool];
}
if(detail.indexOf('영') > -1){
arr = [...arr, ...apYoung];
}
if(detail.indexOf('제') > -1){
arr = [...arr, ...apZe];
}
const set = new Set(arr);
const result = [...set];
msg.reply(set.size > 0 ? result[getRandom(set.size)] : '결과가 없습니다. 명령어를 확인해주세요.');
}
else if (msg.content.indexOf('!악몽') === 0) {
const cho = new Set([...adCho, ...apCho]);
let arrCho = [...cho];
const bool = new Set([...adBool, ...apBool]);
let arrBool = [...bool];
const young = new Set([...adYoung, ...apYoung]);
let arrYoung = [...young];
const ze = new Set([...adZe, ...apZe]);
let arrZe = [...ze];
for(let i = 0; i<7; i++){
arrCho.splice(getRandom(arrCho.length),1);
if(i <= 1){
arrBool.splice(getRandom(arrBool.length),1);
arrZe.splice(getRandom(arrZe.length),1);
}
if(i == 0) arrYoung.splice(getRandom(arrYoung.length),1);
}
msg.reply(`
악몽 룰을 적용합니다.
초월은 ${arrCho.join(', ')}
불멸은 ${arrBool.join(', ')}
영원은 ${arrYoung.join(', ')}
제한은 ${arrZe.join(', ')}
만 갈 수 있습니다.`);
}
else if (msg.content.indexOf('!save') === 0){
saveCode();
}
else if (msg.content ==='!load') {
loadCode();
}
}catch (e) {
console.log(e);
}
async function saveCode(){
const code = msg.content.replace('!save','').trim();
const user = msg.author.username;
const response = await notion.pages.create({
parent: {
database_id: process.env.NOTION_DATABASE_ID,
},
properties: {
코드: {
title: [
{
text: {
content: code,
},
},
],
},
닉네임: {
select: {
name: user,
},
},
}
});
msg.reply('코드가 저장되었습니다.');
}
async function loadCode(){
const user = msg.author.username;
const response = await notion.databases.query({
database_id: process.env.NOTION_DATABASE_ID,
filter: {
property: "닉네임",
select: {
equals: user,
},
},
sort: [{
property: "상태",
direction: "descending"
}],
page_size: 1
})
const code = response.results[0].properties["코드"].title[0].text.content;
msg.reply(code ? code : '데이터가 없습니다. 세이브를 해주세요.');
}
});
const getRandom = (max) => Math.floor(Math.random() * max);
// 봇과 서버를 연결해주는 부분
client.login('');
이후 oracle cloud에 ftp를 통해 프로젝트를 옮겨주고
sudo npm install forever -g
sudo forever start index.js
를 실행하여 계속해서 실행되게 해주었습니다.
sudo forever list
, sudo forever stop 0
명령어를 통해 관리할 수 있습니다.
사진과 같이 잘 작동하는 것을 볼 수 있었습니다.
오 흥미로운 포스팅이군요 잘 보고 갑니다!