728x90
반응형
728x90
반응형
728x90
반응형

최근 유행했던 주사위 게임을 디스코드 봇으로 만들어 보았다.

 


https://discord.com/api/oauth2/authorize?client_id=671993218438791169&permissions=34880&scope=bot

 

Discord - A New Way to Chat with Friends & Communities

Discord is the easiest way to communicate over voice, video, and text. Chat, hang out, and stay close with your friends and communities.

discord.com

제 봇을 추가해서 테스트해 볼 수 있습니다.

 

명령어: >야추

 


 

 

 

정확히는 디스코드랑 상호작용하는 부분만 만들고 실제로 주사위를 굴리거나 점수를 반환하는 실질적인 게임 부분은 깃허브에서 찾아서 사용했다.

 

사용한 API 코드:

 

https://github.com/dkpark95/yahtzee

 

dkpark95/yahtzee

Code for a simple game of Yahtzee built with JS/jQuery on top of a simple Express API. - dkpark95/yahtzee

github.com

위 API를 돌려서 값만 주고 받아서 사용하는 거다.

 


구조

 

project/

├── bot/

│ ├── app.js (봇)

│ └── emojiCharacters.js (이모지 모음)

└── yahtzee/ (https://github.com/dkpark95/yahtzee)

 

 

emojiCharacters.js 파일은 아래 링크를 참조하여 생성하시면 됩니다.

https://discordjs.guide/popular-topics/miscellaneous-examples.html#emoji-characters

 

Discord.js Guide

A guide made by the community of discord.js for its users.

discordjs.guide


코드 작성하기

 

 

개발환경 : UBUNTU 18.04.3

개발언어: NODE JS V12.13.0

모듈 : discord.js v12

텍스트 에디터: ATOM (편하신거 사용하시면 됩니다.)

 

nodejs와 discordjs의 버전을 확인해주세요. 버전이 다를 경우 문제가 생길 수 있습니다.

discord.js의 버전은 무조건 V12이어야 합니다.

 

 

 

app.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
// Extract the required classes from the discord.js module
const Discord = require('discord.js');
const rp = require('request-promise');
const emojiCharacters = require('./emojiCharacters');
 
// Create an instance of a Discord client
const client = new Discord.Client();
 
const botName = 'M4ndU';
const devURL = 'https://mandu-mandu.tistory.com';
const yatchURL = 'http://127.0.0.1:3400';
 
const token = 'TOKEN_HERE';
 
const PREFIX = '?';
 
function showScore(game, i){
  if (game.scorecard[i].locked === true) {
    return game.scorecard[i].points;
  } else {
    var score = '0';
    if (game.scorecard[i].points===null) {
      score = 0;
    }else {
      score = game.scorecard[i].points;
    }
    return "0(+"+score+")";
  }
}
 
function showScoreEmoji(game, i){
  if (game.scorecard[i].locked === true) {
    return ' ✅ ';
  } else {
    return ' ❌ ';
  }
}
 
function showBonusScore(game){
  if (game.scorecard[7].points===null) {
    return `0(${game.scorecard[6].points}/63)(+35)`;
  } else {
    return `${game.scorecard[7].points===null?0:game.scorecard[7].points}(${game.scorecard[6].points===null?0:game.scorecard[6].points}/63)`;
  }
}
 
function showBonusEmoji(game){
  if (game.scorecard[7].points===null) {
    return ' ❌ ';
  } else if (game.scorecard[7].points=== 0) {
    return ' ⛔️ ';
  } else {
    return ' ✅ ';
  }
}
 
function embedScoreMsg(newGame, r, log, turn){
  var dices = "";
  for (var i = 0; i < 5; i++) {
    dices += emojiCharacters[`${newGame.round.dice[i].value}`] +' ';
  }
 
  var gameBoardEmbed = new Discord.MessageEmbed()
    .setColor('#0099ff')
    .setTitle(`야추! | 턴 ${turn}/13`)
    .setDescription('입력시간제한 : 1분')
    .setAuthor(botName, '''http://mandu-mandu.tistory.com/')
    .addFields(
      { name'주사위 🎲 ', value: `${dices}`, inline: true },
      { name'남은 횟수 🤹‍ ', value: `${r}`, inline: true },
      { name'점수 합계 🏆 ', value: `${newGame.scorecard[15].points===null?0:newGame.scorecard[15].points}`, inline: true },
      { name'-----------', value: '점수판', inline: false },
      { name: `#1: Ones${showScoreEmoji(newGame, 0)}`, value: `점수: ${ showScore(newGame, 0)}`, inline: true },
      { name: `#2: Twos${showScoreEmoji(newGame, 1)}`, value: `점수: ${ showScore(newGame, 1)}`, inline: true },
      { name: `#3: Threes${showScoreEmoji(newGame, 2)}`, value: `점수: ${ showScore(newGame, 2)}`, inline: true },
      { name: `#4: Fours${showScoreEmoji(newGame, 3)}`, value: `점수: ${ showScore(newGame, 3)}`, inline: true },
      { name: `#5: Fives${showScoreEmoji(newGame, 4)}`, value: `점수: ${ showScore(newGame, 4)}`, inline: true },
      { name: `#6: Sixs${showScoreEmoji(newGame, 5)}`, value: `점수: ${ showScore(newGame, 5)}`, inline: true },
      { name: `1-6 보너스!${showBonusEmoji(newGame)}`, value: `점수:${ showBonusScore(newGame)}`, inline: false },
      { name: `#7: Three of a Kind${showScoreEmoji(newGame, 8)}`, value: `점수: ${ showScore(newGame, 8)}`, inline: true },
      { name: `#8: Four of a Kind${showScoreEmoji(newGame, 9)}`, value: `점수: ${ showScore(newGame, 9)}`, inline: true },
      { name: `#9: Full House${showScoreEmoji(newGame, 10)}`, value: `점수: ${ showScore(newGame, 10)}`, inline: true },
      { name: `#10: Small Straight${showScoreEmoji(newGame, 11)}`, value: `점수: ${ showScore(newGame, 11)}`, inline: true },
      { name: `#11: Large Straight${showScoreEmoji(newGame, 12)}`, value: `점수: ${ showScore(newGame, 12)}`, inline: true },
      { name: `#12: Chance${showScoreEmoji(newGame, 13)}`, value: `점수: ${ showScore(newGame, 13)}`, inline: true },
      { name: `#13: Yahtzee${showScoreEmoji(newGame, 14)}`, value: `점수: ${ showScore(newGame, 14)}`, inline: true },
      { name'기록 📃', value: `${log}`, inline: false },
    )
    .setFooter('Made by M4ndU''');
 
    return gameBoardEmbed;
}
 
 
client.on('ready', async () => {
  console.log(`Logged in as ${client.user.tag}!`);
});
 
client.on('message', async msg => {
  if (msg.content.startsWith(PREFIX)) {
        const input = msg.content.slice(PREFIX.length).split(' ');
        var command = input.shift();
        var commandArgs = input;
 
    if (command === '야추') {
      //게임 시작
      const newGame = await rp({uri:yatchURL + '/api/game'}).then(function (repos) {
        return JSON.parse(repos);
      });
 
      var gameBoardEmbed = embedScoreMsg(newGame, 2,'.'1);
 
      var yachtmsg = await msg.channel.send(gameBoardEmbed).then(msg =>{
        return msg;
      });
 
      Promise.all([
            yachtmsg.react(emojiCharacters['1']),
            yachtmsg.react(emojiCharacters['2']),
            yachtmsg.react(emojiCharacters['3']),
        yachtmsg.react(emojiCharacters['4']),
        yachtmsg.react(emojiCharacters['5']),
        yachtmsg.react('🔁'), //주사위 굴리기
        yachtmsg.react('🔴'), //점수 바로 등록하기
        ]);
 
      //init
      var yachtGame = newGame;
      const filter = (reaction, user) => {
        return ['🔁','🔴'].includes(reaction.emoji.name&& user.id === msg.author.id;
      };
      var stopFlag = false;
      var exit = false;
      var options = {
          method: 'POST',
          uri:yatchURL + '/api/game/roll',
          body: yachtGame,
          json: true // Automatically stringifies the body to JSON
      };
      const filterm = response => {
        return ['1','2','3','4','5','6','7','8','9','10','11','12','13'].includes(response.content&& response.author.id === msg.author.id;
      };
      var choose = '0';
      var userReactions = new Object();
      //init
 
 
      //13턴 돌립니다.
      for (var gameTurn = 1; gameTurn < 14; gameTurn++) {
        //주사위 3라운드 굴리기
        for (var g = 2; g > 0; g--) {
          await yachtmsg.awaitReactions(filter, { max: 1, time: 60000, errors: ['time'] })
            .then(async function(collected) {
              const reaction = collected.first();
              if (reaction.emoji.name === '🔁') {
                for (var yrdl = 0; yrdl < 5; yrdl++) {
                  yachtGame.round.dice[yrdl].locked = yachtmsg.reactions.cache.get(emojiCharacters[`${yrdl+1}`]).users.cache.has(msg.author.id);
                }
                //roll
                  } else {
                stopFlag = true;
                g = 1;
                  }
            }).catch(async function(collected) {
              gameBoardEmbed = embedScoreMsg(yachtGame, 0,'시간초과.', gameTurn);
              yachtmsg.edit(gameBoardEmbed);
              stopFlag = true;
              exit = true;
            });
          options = {
              method: 'POST',
              uri:yatchURL + '/api/game/roll',
              body: yachtGame,
              json: true // Automatically stringifies the body to JSON
          };
          if (exit) {
            break;
          }
          if (!stopFlag) {
            yachtGame = await rp(options).then(function (repos) {
              return repos;
            });
          }
 
          if (g === 1) {
            gameBoardEmbed = embedScoreMsg(yachtGame, g-1,'점수로 등록할 번호를 입력하세요.', gameTurn);
            await yachtmsg.edit(gameBoardEmbed);
            break;
          } else {
            gameBoardEmbed = embedScoreMsg(yachtGame, g-1,'잠시 기다려주세요', gameTurn);
          }
 
          await yachtmsg.edit(gameBoardEmbed);
 
          //2 react remove
          await yachtmsg.reactions.resolve('🔁').users.remove(msg.author.id);
          await yachtmsg.reactions.resolve('🔴').users.remove(msg.author.id);
 
          gameBoardEmbed = embedScoreMsg(yachtGame, g-1,'아래 이모지를 클릭해주세요.', gameTurn);
          await yachtmsg.edit(gameBoardEmbed);
        }
        if (exit) {
          break;
        }
 
        //점수 등록과정
        choose = '0';
        stopFlag = false;
        for (var rcc = 0; rcc < 5; rcc++) {
          await msg.channel.awaitMessages(filterm, { max: 1, time: 60000, errors: ['time'] })
                .then(async collected => {
              var resmsg = collected.first();
                    if (isNaN(resmsg.content|| Number(resmsg.content> 14 || Number(resmsg.content< 1 || !Number.isInteger(Number(resmsg.content))) {
                gameBoardEmbed = embedScoreMsg(yachtGame, 0,'1에서 14사이의 정수를 입력.');
                yachtmsg.edit(gameBoardEmbed);
              }else {
                if (resmsg.content < 7) {
                  choose = Number(resmsg.content-1;
                } else {
                  choose = Number(resmsg.content+1;
                }
                if (yachtGame.scorecard[Number(choose)].locked) {
                  gameBoardEmbed = embedScoreMsg(yachtGame, 0,'이미 등록된 번호. 다른번호로 입력.', gameTurn);
                  yachtmsg.edit(gameBoardEmbed);
                }else {
                  options = {
                      method: 'PUT',
                      uri:yatchURL + '/api/game/select/' + yachtGame.scorecard[choose].id,
                      body: yachtGame,
                      json: true // Automatically stringifies the body to JSON
                  };
                  yachtGame = await rp(options).then(function (repos) {
                    return repos;
                  });
                  stopFlag = true;
                }
              }
              resmsg.delete();
                })
                .catch(collected => {
              gameBoardEmbed = embedScoreMsg(yachtGame, 0,'시간초과.', gameTurn);
              yachtmsg.edit(gameBoardEmbed);
              stopFlag = true;
              exit = true;
                });
          if (stopFlag || exit) {
            break;
          }
        }
 
        stopFlag = false;
        if (exit) {
          break;
        }
 
        if (gameTurn === 13) {
          gameBoardEmbed = embedScoreMsg(yachtGame, 0,'게임종료', gameTurn);
          break;
        }else {
          gameBoardEmbed = embedScoreMsg(yachtGame, 2,'기다려주세요', gameTurn+1);
        }
 
        await yachtmsg.edit(gameBoardEmbed);
        //all react remove
        await yachtmsg.reactions.resolve(emojiCharacters['1']).users.remove(msg.author.id);
        await yachtmsg.reactions.resolve(emojiCharacters['2']).users.remove(msg.author.id);
        await yachtmsg.reactions.resolve(emojiCharacters['3']).users.remove(msg.author.id);
        await yachtmsg.reactions.resolve(emojiCharacters['4']).users.remove(msg.author.id);
        await yachtmsg.reactions.resolve(emojiCharacters['5']).users.remove(msg.author.id);
        await yachtmsg.reactions.resolve('🔁').users.remove(msg.author.id);
        await yachtmsg.reactions.resolve('🔴').users.remove(msg.author.id);
 
        gameBoardEmbed = embedScoreMsg(yachtGame, 2,'아래 이모지를 클릭!', gameTurn+1);
        await yachtmsg.edit(gameBoardEmbed);
      }
 
    }
  }
});
 
client.login(token);
 
cs

 


https://discord.com/api/oauth2/authorize?client_id=671993218438791169&permissions=34880&scope=bot

 

Discord - A New Way to Chat with Friends & Communities

Discord is the easiest way to communicate over voice, video, and text. Chat, hang out, and stay close with your friends and communities.

discord.com

제 봇을 추가해서 테스트해 볼 수 있습니다.

 

명령어: >야추

728x90
반응형
  1. bakerko200 2020.11.26 02:51

    재미있는 글 잘 보고 갑니다

728x90
반응형

직접 MBTI를 검사하는 봇은 아니고, 외부 사이트 등에서 mbti를 검사하고서 그 결과를 등록한 뒤, 디스코드 구성원들과 궁합을 보는 봇이다.

 

인터넷에 떠돌아 다니는 MBTI 궁합 차트를 기반으로 했다.

 

물론 아무런 근거 없는 궁합이기 때문에 재미로만 보자.

 

 


명령어별 기능

 

 

mbti

명령어 모음 (help 명령어와 동일)

 

1. mbti 검사

MBTI를 검사할 수 있는 링크를 출력한다.

 

2. mbti 보기 [MBTI]

MBTI 유형을 입력해주면, 해당 유형의 내용을 볼 수 있는 링크를 출력한다.

 

3. mbti 등록 [MBTI]

자신의 MBTI 유형을 등록한다. (기존에 등록했어도 덮어쓰기 가능.)

 

4. mbti 등록현황

mbti 등록 현황을 출력한다.

 

5. mbti 궁합 @A @B

@자리에 원하는 사람을 언급해주면 된다.

@A만 언급하면 A와 나 자신의 궁합 결과를 출력한다.

@A @B 모두 언급해주면, @A와 @B의 궁합 결과를 표시해준다.

 

 


개발환경 : UBUNTU 18.04.3

개발언어: NODE JS V12

모듈 : discord.js v12

텍스트 에디터: ATOM (편하신거 사용하시면 됩니다.)

 

봇 생성하는 것은 아래 포스트를 보고 오자.

https://mandu-mandu.tistory.com/385?category=693080

 

노드자스로 디스코드 봇 만들기(1) - 봇 생성부터 테스트까지

이 글만을 통해서 기본적으로 텍스트를 출력하는 디스코드 봇을 만들 수 있습니다. 1. 봇 생성하고 초대하기 2. 코드 작성하기 3. 테스트 하기 봇 생성하고 초대하기 먼저 봇 계정을 생성해 주어��

mandu-mandu.tistory.com

 

 

 

일단 위 궁합표를 배열로 만들었다. (노가다다 노가다...)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//16가지 mbti 유형
const mbti = new Array("INFP","ENFP","INFJ","ENFJ""INTJ""ENTJ""INTP""ENTP""ISFP""ESFP""ISTP""ESTP""ISFJ""ESFJ""ISTJ""ESTJ");
 
//Relation : A, B, C, D, F
var mbtiR = new Array(16);
mbtiR[0]  = ["A"];
mbtiR[1]  = ["A""A"];
mbtiR[2]  = ["A""B""A"];
mbtiR[3]  = ["B""A""A""A"];
mbtiR[4]  = ["A""B""A""A""A"];
mbtiR[5]  = ["B""A""A""A""A""A"];
mbtiR[6]  = ["A""A""A""A""A""B""A"];
mbtiR[7]  = ["A""A""B""A""B""A""A""A"];
mbtiR[8]  = ["F""F""F""B""C""C""C""C""D"];
mbtiR[9]  = ["F""F""F""F""C""C""C""C""D""D"];
mbtiR[10= ["F""F""F""F""C""C""C""C""D""D""D"];
mbtiR[11= ["F""F""F""F""C""C""C""C""D""D""D""D"];
mbtiR[12= ["F""F""F""F""D""C""D""D""C""B""C""B""A"];
mbtiR[13= ["F""F""F""F""D""C""D""D""B""C""B""C""A""A"];
mbtiR[14= ["F""F""F""F""D""C""D""D""C""B""C""B""A""A""A"];
mbtiR[15= ["F""F""F""F""D""C""B""D""B""C""B""C""A""A""A""A"];
 
cs

A, B, C, D, F 5단계이며, 위 표 기준으로 A가 파랑, F가 빨강이다.

 

 

 

 

 

 

 

이제 명령어 별로 6가지의 기능을 구현해야 하는데, 1번과 2번은 단술 입출력이기 때문에 아래 포스트를 보고 구현이 가능하다.

https://mandu-mandu.tistory.com/385?category=693080

 

노드자스로 디스코드 봇 만들기(1) - 봇 생성부터 테스트까지

이 글만을 통해서 기본적으로 텍스트를 출력하는 디스코드 봇을 만들 수 있습니다. 1. 봇 생성하고 초대하기 2. 코드 작성하기 3. 테스트 하기 봇 생성하고 초대하기 먼저 봇 계정을 생성해 주어��

mandu-mandu.tistory.com

https://mandu-mandu.tistory.com/386?category=693080

 

노드자스로 디스코드 봇 만들기(2) - 노래/사진/상태/삭제

이번에 추가할 기능은 아래와 같습니다. [ 로컬 노래파일 재생, 로컬 사진 전송, 상태메세지 설정, 메세지 지우기 ] 개발환경 : UBUNTU 18.04.3 개발언어: NODE JS V8 모듈 : discord.js v11 텍스트 에디터: ATOM

mandu-mandu.tistory.com

 

 

 


 

3번 4번 5번의 핵심 기능은 동일하다.

 

 

 

배열을 하나 만들어서

유저의 id (고유값), username, mbti 유형값을 받아서 저장하고

json으로 만들어서 외부 파일에 저장해둔다.

 

저장된 유저의 mbti값을 가져와서 궁합 값을 출력해주면 된다.

 

 

간단하다.

 

 

 

 

봇의 핵심인 3번과 5번 명령에 해당되는 코드:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
if (msg.content.startsWith('mbti 등록 ')) {
    var reg_mbti = msg.content.split(' ');
 
    //type
    var type = 16;
    var input_mbti = reg_mbti[2].toUpperCase();
    for(var i=0; i<16; i++) {
      if (mbti[i] == input_mbti) {
        type = i;
        break;
      }
    }
    if(type == 16){
      msg.channel.send("실패!");
    }else {
 
      //json to Array
      var data = fs.readFileSync('./user_list.json''utf8');
      var user_list_arr = JSON.parse(data);
 
      //search
      var j = -1;
      for(var k=0; k<user_list_arr.length; k++) {
        if (user_list_arr[k].user_id == msg.member.user.id) {
          j = k;
          break;
        }
      }
 
      //patch or push
      if (j == -1) {
        var user_list = new Object();
        user_list.user_id = msg.member.user.id;
        user_list.username = msg.member.user.username;
        user_list.mbti  = type;
        user_list_arr.push(user_list);
      } else {
        user_list_arr[j].username = msg.member.user.username;
        user_list_arr[j].mbti  = type;
      }
 
      //Array to json
      data = JSON.stringify(user_list_arr);
      fs.writeFileSync('./user_list.json', data, 'utf8');
 
      msg.channel.send("성공!");
    }
  }
 
  if (msg.content.startsWith('mbti 궁합')) {
    var comp_mbti = msg.content.split(' ');
 
    //json to Array
    var data = fs.readFileSync('./user_list.json''utf8');
    var user_list_arr = JSON.parse(data);
 
    //search user 1
    var user_1 = "";
    if (comp_mbti[2=== undefined) {
      user_1 = "";
    } else {
      user_1 = comp_mbti[2].replace(/[^0-9]/g,'');
    }
 
    var user1type = 16;
    var user1name = "";
    for(var k=0; k<user_list_arr.length; k++) {
      if (user_list_arr[k].user_id == user_1) {
        user1type = user_list_arr[k].mbti;
        user1name = user_list_arr[k].username;
        break;
      }
    }
 
    //search user 2
    var user_2 = "";
    if (comp_mbti[3=== undefined) {
      user_2 = msg.member.user.id;
    } else if (comp_mbti[3== "") {
      comp_mbti[3= comp_mbti[4]; //중간에 띄어쓰기가 한 번 더 들어간 경우에도 정상적인 처리를 위해서
      user_2 = comp_mbti[3].replace(/[^0-9]/g,'');
    } else {
      user_2 = comp_mbti[3].replace(/[^0-9]/g,'');
    }
 
    var user2type = 16;
    var user2name = "";
    for(var k=0; k<user_list_arr.length; k++) {
      if (user_list_arr[k].user_id == user_2) {
        user2type = user_list_arr[k].mbti;
        user2name = user_list_arr[k].username;
        break;
      }
    }
 
    //result
    if (user1type == 16 || user2type == 16) {
      msg.channel.send(noresultEmbed);
    } else{
 
      if (user2type > user1type) {
        [user1type, user2type] = [user2type, user1type];
      }
 
      var result = mbtiR[user1type][user2type];
 
      var explain_text1 = " 와(과) ";
      var explain_text2 = " 의 궁합";
 
      var resultEmbed = new Discord.MessageEmbed()
          .setColor('#0099FF')
          .setTitle(user1name.concat(explain_text1,user2name,explain_text2))
        .addField('결과', result, true)
          .setFooter('Powered by node.js  Made by M4ndU''');
 
      msg.channel.send(resultEmbed);
 
    }
  }
cs

 

 

 

 

 

 

 

 

 

 

 


실행

 

user_list.json 파일을 하나 만들어두고 봇을 실행해야한다.

 

 

실행 후에 오류나면 아래 글에서 해결법을 찾아보자. 아래 글에도 없다면 구글링!

https://mandu-mandu.tistory.com/381

 

discord js 설치, 실행 오류 / 오디오 재생 오류 문제 해결

1. npm으로 discord.js 설치 후 봇 실행시 발생하는 오류 해결 /Client.js:39 } catch { ^ 봇 실행시 위와 같은 오류가 발생할 경우입니다. 저같은 경우에는 discord.js github에 나와있는 설치방법으로 설치를 했

mandu-mandu.tistory.com

 

728x90
반응형
728x90
반응형

이번에 추가할 기능은 아래와 같습니다.

 

[

로컬 노래파일 재생,

로컬 사진 전송,

상태메세지 설정,

메세지 지우기

]

 


개발환경 : UBUNTU 18.04.3

개발언어: NODE JS V8

모듈 : discord.js v11

텍스트 에디터: ATOM (편하신거 사용하시면 됩니다.)

 

 

 

 

로컬 경로에 존재하는 mp3파일을 재생하는 기능입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
  if (msg.content === 'r.play') {
    // Only try to join the sender's voice channel if they are in one themselves
    if (msg.member.voiceChannel) {
      msg.member.voiceChannel.join()
        .then(connection => { // Connection is an instance of VoiceConnection
          msg.reply('playing music!');
          const dispatcher = connection.playFile('./music.mp3');
          dispatcher.on("end", end => {});
        })
        .catch(console.log);
    } else {
      msg.reply('먼저 방에 들어가');
    }
  }
  if (msg.content === 'r.leave') {
    // Only try to join the sender's voice channel if they are in one themselves
    if (msg.member.voiceChannel) {
      msg.member.voiceChannel.leave();
      msg.reply('bye!');
    } else {
      msg.reply('이미 나왔는데..');
    }
  }
 
cs

 

r.play는 내가 들어간 음성 채팅방으로 들어가서 music.mp3를 재생합니다. (7행에서 파일명, 경로를 설정할 수 있습니다.)

내가 음성채팅방에 들어가 있지 않은 경우 12행 메세지를 출력합니다.

 

r.leave로 방을 나갈 수 있습니다.

 

 

 

 

오디오 재생시 오디오가 재생되지 않고 바로 종료되는 문제

또는 Error: FFMPEG not found 에러 발생시 아래 포스트 참조

 

https://mandu-mandu.tistory.com/381

 

discord js 설치 후 catch 오류 / 오디오 재생 오류 문제 해결

1. npm으로 discord.js 설치 후 봇 실행시 발생하는 오류 해결 /Client.js:39 } catch { ^ 봇 실행시 위와 같은 오류가 발생할 경우입니다. 저같은 경우에는 discord.js github에 나와있는 설치방법으로 설치를 했..

mandu-mandu.tistory.com

 

 

 

728x90

로컬경로에 존재하는 이미지를 채팅방에 전송하는 기능입니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  if (msg.content.startsWith('r.gif')) {
    const args = msg.content.split(' ').slice(1); // All arguments behind the command name with the prefix
    var no = args.join(' '); // Amount of messages which should be deleted
 
    if (!no) no = '1';
    if (isNaN(no) || no > 14return msg.reply('only number 1-14');
 
   // Create the attachment using Attachment
   var path = "./gif/";
   path = path.concat(no,".gif");
   console.log(path);
   const attachment = new Attachment(path);
   // Send the attachment in the message channel with a content
   msg.channel.send(attachment);
  }
cs

명령어는 r.gif n 입니다. n에는 정수가 들어갑니다. ex) r.gif 1

 

저는 gif 이미지를 보내도록 했습니다. 필요에 따라서 10번행의 확장자명을 gif에서 png, jpg등으로 바꾸실 수 있습니다.

저는 ./gif/ 디렉토리 안에 1.gif, 2.gif, 3.gif ... 파일을 넣어 놨습니다.

 

파일 명을 정수로 설정해주세요!

 

 

5번행은 명령어 뒤에 정수가 입력되지 않은 경우 자동으로 1로 하도록

6번행은 숫자인지 검증, 14까지만 입력되도록 한 것입니다.

 


 

상태메세지 설정은 아래 사진과 같이 별명 아래에 메세지를 표시하는 것입니다.

ready 안에 아래 코드 한줄을 추가만 해주시면 됩니다. (전체 코드는 밑에서 확인하실 수 있습니다.)

  client.user.setActivity('봇 만들자', { type: 'WATCHING' })

 

type에는 WATCHING (시청 중) 외에도 

  • PLAYING   (하는 중)
  • STREAMING
  • LISTENING

으로 설정하실 수 있습니다.

 

 

 


메세지 지우기 기능

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
  if (msg.content.toLowerCase().startsWith("r.clear")) {
      const args = msg.content.split(' ').slice(1); // All arguments behind the command name with the prefix
      const amount = args.join(' '); // Amount of messages which should be deleted
 
      if (!amount) return msg.reply('You haven\'t given an amount of messages which should be deleted!'); // Checks if the `amount` parameter is given
      if (isNaN(amount)) return msg.reply('The amount parameter isn`t a number!'); // Checks if the `amount` parameter is a number. If not, the command throws an error
 
      if (amount > 10return msg.reply('You can`t delete more than 10 messages at once!'); // Checks if the `amount` integer is bigger than 100
      if (amount < 1return msg.reply('You have to delete at least 1 message!'); // Checks if the `amount` integer is smaller than 1
 
      msg.channel.fetchMessages({ limit: amount }).then(dmsg => { // Fetches the messages
      msg.channel.bulkDelete(dmsg // Bulk deletes all messages that have been fetched and are not older than 14 days (due to the Discord API)
      ).catch(console.log);});
    }
cs

 

r.clear n 과 같이 입력하면, n개의 메세지를 삭제해줍니다.

 

8행 9행에서 각각 최대 최소 개수를 설정할 수 있습니다.

 

 

 

 


전체 봇 소스코드 bot.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
// Extract the required classes from the discord.js module
const { Client, Attachment } = require('discord.js');
 
// Create an instance of a Discord client
const client = new Client();
 
 
client.on('ready', () => {
  console.log(`Logged in as ${client.user.tag}!`);
  client.user.setActivity('봇 만들자', { type: 'WATCHING' })
});
 
client.on('message', msg => {
  if (msg.content.startsWith('r.help')) {
    msg.reply('Powered by node.js\nMade by M4ndU');
  }
  if (msg.content.startsWith('r.gif')) {
    const args = msg.content.split(' ').slice(1); // All arguments behind the command name with the prefix
    var no = args.join(' '); // Amount of messages which should be deleted
 
    if (!no) no = '1';
    if (isNaN(no) || no > 14return msg.reply('only number 1-14');
 
   // Create the attachment using Attachment
   var path = "./gif/";
   path = path.concat(no,".gif");
   console.log(path);
   const attachment = new Attachment(path);
   // Send the attachment in the message channel with a content
   msg.channel.send(attachment);
  }
 
 
  if (msg.content === 'r.play') {
    // Only try to join the sender's voice channel if they are in one themselves
    if (msg.member.voiceChannel) {
      msg.member.voiceChannel.join()
        .then(connection => { // Connection is an instance of VoiceConnection
          msg.reply('playing music!');
          const dispatcher = connection.playFile('./music.mp3');
          dispatcher.on("end", end => {});
        })
        .catch(console.log);
    } else {
      msg.reply('먼저 방에 들어가');
    }
  }
  if (msg.content === 'r.leave') {
    // Only try to join the sender's voice channel if they are in one themselves
    if (msg.member.voiceChannel) {
      msg.member.voiceChannel.leave();
      msg.reply('bye!');
    } else {
      msg.reply('이미 나왔는데..');
    }
  }
 
  if (msg.content.toLowerCase().startsWith("r.clear")) {
      const args = msg.content.split(' ').slice(1); // All arguments behind the command name with the prefix
      const amount = args.join(' '); // Amount of messages which should be deleted
 
      if (!amount) return msg.reply('You haven\'t given an amount of messages which should be deleted!'); // Checks if the `amount` parameter is given
      if (isNaN(amount)) return msg.reply('The amount parameter isn`t a number!'); // Checks if the `amount` parameter is a number. If not, the command throws an error
 
      if (amount > 10return msg.reply('You can`t delete more than 10 messages at once!'); // Checks if the `amount` integer is bigger than 100
      if (amount < 1return msg.reply('You have to delete at least 1 message!'); // Checks if the `amount` integer is smaller than 1
 
      msg.channel.fetchMessages({ limit: amount }).then(dmsg => { // Fetches the messages
      msg.channel.bulkDelete(dmsg // Bulk deletes all messages that have been fetched and are not older than 14 days (due to the Discord API)
      ).catch(console.log);});
    }
 
  if (msg.content === 'r.whoami') {
    // Send the user's avatar URL
    msg.reply(msg.author.avatarURL);
  }
});
 
client.login('token');
 
cs

 

79행 token에 반드시 자신의 봇의 토큰값을 입력하세요

728x90
반응형
  1. 구강민 2020.09.20 09:56

    npm install --save ffmpeg-binaries
    를 치면
    Error: Cannot find module 'C:\musicbot\node_modules\lzma-native\binding-v3.0.8-node-v72-win32-x64\lzma_native.node'
    Require stack:
    - C:\musicbot\node_modules\lzma-native\index.js
    - C:\musicbot\node_modules\decompress-tarxz\index.js
    - C:\musicbot\node_modules\ffmpeg-binaries\install.js
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:965:15)
    at Function.Module._load (internal/modules/cjs/loader.js:841:27)
    at Module.require (internal/modules/cjs/loader.js:1025:19)
    at require (internal/modules/cjs/helpers.js:72:18)
    at C:\musicbot\node_modules\lzma-native\index.js:13:14
    at Object.<anonymous> (C:\musicbot\node_modules\lzma-native\index.js:597:3)
    at Module._compile (internal/modules/cjs/loader.js:1137:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1157:10)
    at Module.load (internal/modules/cjs/loader.js:985:32)
    at Function.Module._load (internal/modules/cjs/loader.js:878:14) {
    code: 'MODULE_NOT_FOUND',
    requireStack: [
    'C:\\musicbot\\node_modules\\lzma-native\\index.js',
    'C:\\musicbot\\node_modules\\decompress-tarxz\\index.js',
    'C:\\musicbot\\node_modules\\ffmpeg-binaries\\install.js'
    ]
    }
    npm WARN discord.js@11.6.4 requires a peer of @discordjs/uws@^10.149.0 but none is installed. You must install peer dependencies yourself.
    npm WARN musicbot@1.0.0 No description
    npm WARN musicbot@1.0.0 No repository field.
    npm WARN musicbot@1.0.0 No license field.

    npm ERR! code ELIFECYCLE
    npm ERR! errno 1
    npm ERR! ffmpeg-binaries@4.0.0 install: `node install`
    npm ERR! Exit status 1
    npm ERR!
    npm ERR! Failed at the ffmpeg-binaries@4.0.0 install script.
    npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

    npm ERR! A complete log of this run can be found in:
    npm ERR! C:\Users\rnrkd\AppData\Roaming\npm-cache\_logs\2020-09-20T05_49_53_169Z-debug.log

    이러한 오류가 떠요

    • M4ndU 2020.09.20 15:20 신고

      discordjs/opus는 설치하셨나요?

      npm install discord.js @discordjs/opus

  2. 구강민 2020.09.20 19:19

    • M4ndU 2020.09.20 21:45 신고

      npm install @discordjs/uws@11.149.1

      설치해보세요.

  3. LUKE 2020.12.09 16:15

    노래 재생이 안돼고 계속 음성 서버에 들어가라고만 하네요ㅠ
    들어갔는데도 그래요ㅠㅠ 에러는 안떠요

    • M4ndU 2020.12.09 19:49 신고

      discord.js v12를 사용 중이신것으로 추정됩니다.
      본문에 있는 코드는 v11을 기반으로 작성되었습니다.

      따라서 v12에서 사용하기 위해서는
      채널접속 여부를 확인하는 36행과 50행 조건문 부분에서 msg.member.voiceChannel 을 msg.member.voice.channel로 수정하세요.

      그 다음 행 msg.member.voiceChannel.join()도 msg.member.voice.channel.join()으로 수정하시면 됩니다.
      msg.member.voice.channel.leave()

  4. ㅇㅇ 2021.03.01 14:22

    혹시 전체메세지가 아닌 본인이 작성한 메세지만 삭제하게끔 할 수 있는 방법이 있을까요?

  5. 그건막찌 2021.04.20 13:27

    댓글을 보면서 문제점을 고치고 해서 봇을 통화방에 들어오고 나가기 까지 실행을 했어요(노래 듣는 것)
    그런데 음악을 틀어주지는 않아요. 음악을 틀고 있는데 안들리는 건지 아니면 아예 안틀어주는 건지 모르겠어요
    ./music.mp3 를 뭐 어떻게 하나요?? 저는 music.mp3 라는 파일을 만들어서 거기 안에 노래를 담는 건줄 알았는데 아닌거 같아요..

    • M4ndU 2021.04.20 18:35 신고

      노래 파일명을 music.mp3로 하시면 됩니다.

    • 그건막찌 2021.04.22 09:21

      노래파일명을 music.mp3로 하면 1곡 밖에 못 넣지 않나요..?
      예를 들어서 노래 제목이 방탄소년단의 다이너마이트라고 했을떄 그 파일 이름을 music.mp3 로 하면 따른 노래를 담고 싶으면 파일명이 중복된다고 뜰텐데..

    • M4ndU 2021.04.22 11:54 신고

      명령어 여러 개로 추가 확장하시고, 개별 명령어들에 대해서 connection.playFile('./music.mp3'); 의 music.mp3부분을 수정하시면 됩니다.

    • 그건막찌 2021.04.22 12:09

      그럼 music이라는 파일 안에 다가 music1.mp3 , music2.mp3 이런식으로 넣으면 되는 건가요??

    • M4ndU 2021.04.23 18:03 신고

      그러면 코드에서 경로를
      ./music/music1.mp3 이런 식으로 수정하시면 됩니다.

  6. 어려운 코딩 2021.04.22 18:43

    음악 듣는 쪽에서 질문이 있는데요
    봇이 들어오고 나가기도 되고 멘트도 잘 치는데..
    음악을 틀어주지를 않습니다ㅠ
    TypeError: connection.playFile is not a function
    라고 뜨면서 플레이파일이 없다고 해요ㅠ
    봇 만든 폴더 안에 music파일을 만들어서 안에다가 잠시.mp3 파일(노래 이름이 잠시입니다.)을 1개 넣어두기는 했는데,,
    여러개 넣고 싶기도 한데 파일 1개 넣는 것도 안되서... 물어보려고 해요ㅠ

    if (msg.content === '//재생') {
    if (msg.member.voice.channel) {
    msg.member.voice.channel.join()
    .then(connection => {
    msg.reply('음악을 재생합니다.');
    const dispatcher = connection.playFile("./music.mp3");
    dispatcher.on("end", end => {});
    })
    .catch(console.log);
    } else {
    msg.reply('먼저 통화방에 들어가주세요.');
    }
    }

    if (msg.content === '//중지') {
    if (msg.member.voice.channel) {
    msg.member.voice.channel.leave();
    msg.reply('음악을 중지합니다.');
    } else {
    msg.reply('이미 나왔는데..');
    }
    }

    • M4ndU 2021.04.23 18:05 신고

      playFile함수는 v11에서 사용되는 함수입니다. v12에서는 https://stackoverflow.com/questions/61161044/discord-js-bot-error-typeerror-connection-playfile-is-not-a-function 를 참고하시기 바랍니다.

  7. 일린이 2021.06.27 14:50

    안녕하세요.

    디코봇으로 사진전송관련해서 의문이 있어 댓글 남겨요

    포스팅에는 로컬에 저장된 사진파일을 보내는방법이 있던데

    혹시 프린트스크린샷이 된 그림파일을 보내는 방법이 있을까요?

    제가 원하는건 봇이 1시간 단위로 프린트스크린샷을해서 계속 메세지 보내는것을 구현하고싶은데... 이런것도 가능할까요

    • M4ndU 2021.06.30 18:32 신고

      스크린샷 기능이 포함된 패키지를 찾아보셔야 할 것 같습니다.

728x90
반응형

이 글만을 통해서 기본적으로 텍스트를 출력하는 디스코드 봇을 만들 수 있습니다.

 

1. 봇 생성하고 초대하기

2. 코드 작성하기

3. 테스트 하기

 


 

봇 생성하고 초대하기

 

먼저 봇 계정을 생성해 주어야 합니다.

 

 

디스코드 홈페이지로 이동합니다. -> https://discordapp.com/

 

Discord — A New Way to Chat with Friends & Communities

Discord is the easiest way to communicate over voice, video, and text. Chat, hang out, and stay close with your friends and communities.

discordapp.com

 

상단에 개발자 > 개발자 포털

 

 

 

 

New Application을 누릅니다.

 

 

 

 

이름을 설정하고 Create!

 

 

 

왼쪽 탭에서 Bot을 클릭하고, 오른쪽에 있는 Add Bot을 클릭하여 봇을 만들어 줍시다!

 

 

현재 페이지에서 봇의 프로필사진, 이름을 수정하실 수 있으며,

중요한 Token 값을 확인하실 수 있습니다. (절대 외부로 유출되서는 안됩니다. Regenerate버튼을 통해 재생성 가능합니다.)

Public Bot 설정은 아무나 이 봇을 초대할 수 있는지, 나만 초대할 수 있는지를 설정하는 것입니다.

체크가 해제되어 있다면, 나만 이 봇을 초대할 수 있습니다.

 

아래로 스크롤하면, 봇의 권한을 설저할 수 있는 탭이 나옵니다.

여기서 설정하지 않아도, 디스코드 서버 역할을 통해서 권한을 부여해 줄 수도 있습니다.

 

 

 

 

 

 

이제 봇 초대코드를 생성하겠습니다.

 

왼쪽 두번째 탭 OAuth2를 클릭하시고, 중간에 bot을 체크하시면 아래에 링크가 생성됩니다.

 

해당 링크로 접속하여 자신의 서버로 봇을 초대할 수 있습니다.

 

 

 

 

현재 봇은 오프라인 상태가 됩니다.

 


 

728x90

코드 작성하기

 

 

개발환경 : UBUNTU 18.04.3

개발언어: NODE JS V8

모듈 : discord.js v11

텍스트 에디터: ATOM (편하신거 사용하시면 됩니다.)

 

 

 

설치방법

 

 바로가기 <-- (https://github.com/discordjs/discord.js#installation)

 

 

먼저 npm과 node가 설치되어 있는지 확인해보세요.

node -v
npm -v

 

 

음성 봇을 사용하지 않는 경우

npm install discord.js

 


음성 봇을 사용할 경우

npm install discord.js opusscript

 

 

 

 

 

 

설치를 마쳤다면,

이제 텍스트 에디터로 가서 소스를 작성해 봅시다.

 

 

기본적인 소스는 다음과 같습니다. node_modules폴더가 있는 폴더에서 작업해주세요!

저는 파일명을 bot.js로 했습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Extract the required classes from the discord.js module
const { Client, Attachment } = require('discord.js');
 
// Create an instance of a Discord client
const client = new Client();
 
 
client.on('ready', () => {
  console.log(`Logged in as ${client.user.tag}!`);
});
 
client.on('message', msg => {
  if (msg.content.startsWith('r.help')) {
    msg.reply('Powered by node.js\nMade by M4ndU');
  }
 
  if (msg.content === 'r.whoami') {
    // Send the user's avatar URL
    msg.reply(msg.author.avatarURL);
  }
});
 
client.login('token');
 
cs

 

 

23행 token 자리에 디스코드 봇 관리 페이지에서 확인가능한 token 값을 넣어주시면 됩니다.

 

콘솔에 node bot.js을 입력해주시면 봇이 실행되면서, 디스코드 서버내에서 봇이 온라인 상태가 되는 것을 확인할 수 있습니다.

 

 

 

 

봇의 기능을 당담하는 12~21행의 내용을 분석해보겠습니다.

 

client.on('message', msg => {
  if (msg.content.startsWith('r.help')) {
    msg.reply('Powered by node.js\nMade by M4ndU');
  }

채팅창에 r.help로 시작하는 메세지를 보내면 메세지를 출력합니다.

Powered by node.js\nMade by M4ndU 이 부분을 수정하여 본인이 원하는 메세지를 넣을 수도 있습니다.

r.help를 수정하여 본인이 원하는 명령어로 변경하실 수도 있습니다.

 

 

 

if (msg.content === 'r.whoami') { 
    // Send the user's avatar URL 
    msg.reply(msg.author.avatarURL); 
  } 
});

메세지의 내용이 r.whoami와 동일하다면

그 유저의 정보를 보냅니다.

r.whoami를 수정하여 본인이 원하는 명령어로 변경하실 수도 있습니다.

msg.author.avatarURL 대신 'msg'로 자신이 원하는 메세지를 넣을 수도 있습니다.

 

 

msg.reply는 명령어를 입력한 사람을 언급과 함께 메세지를 보냅니다. 메세지만 보내고 싶으신 경우 msg.channel.send함수를 이용하시면 됩니다.

 

 

다음 포스트부터는 이 봇에 여러가지 기능을 추가해 보겠습니다.

728x90
반응형
728x90
반응형

30초 마다 경고창과 함께 크롬/파이어폭스로 특정 주소 창 띄우기

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@echo off
echo msgbox "아무거나 막 실행하지 말자",0,"수상한 프로그램" > msg.vbs
start /wait msg.vbs
del msg.vbs
 
:_loop
start chrome https://www.youtube.com/watch?v=zwhbV4LfBrc^&t=148s
start chrome https://mandu-mandu.tistory.com
start firefox https://www.youtube.com/watch?v=zwhbV4LfBrc^&t=148s
 
timeout 30 > NUL
 
echo msgbox "귀여우셔라",0,"o kawaii koto" > msg.vbs
start /wait msg.vbs
del msg.vbs
 
goto _loop
 
cs

 

 

위 코드 내용을 메모장에 붙여넣기 하고 파일명.bat 으로 저장하시면 됩니다.

한글을 사용하실 경우 인코딩은 ANSI로 설정하시면 됩니다.

 

 

 

 

Bat_To_Exe_Converter 를 이용해서 bat파일을 exe파일로 변환해줍니다.

 

 

'보이지않는 응용프로그램'으로 해두셔야 프로그램을 실행했을 때 콘솔창이 뜨지 않습니다.

 

 

 

 

 

프로그램 종료 방법


Ctrl + Shift + ESC = 작업관리자

 

세부정보 탭

 

실행한 프로그램명을 찾아서 [작업끝내기]

cmd.exe 찾아서 전부 [작업끝내기]

728x90
반응형
728x90
반응형

네이버 카페의 글 제목, 작성자 닉네임, 작성일을 파싱하는 코드입니다.

 

 

 

로그인 절차가 필요 없습니다.

 

게시판 페이지를 파싱합니다.

 

게시판 링크는 게시판 링크 우클릭해서 링크 복사 해서 /ArticleList.nhn 부분부터 복사하여

카페 링크 뒤에 붙이시면 됩니다.

 

 

 

추가 인자값들은 아래 코드에서 확인하세요. (userDisplay 이나 search.page 등)

 

 

 

 

simple_html_dom.php 파일을 필요로 합니다:

https://simplehtmldom.sourceforge.io/

 

PHP Simple HTML DOM Parser

$html = str_get_html(' Hello World '); $html->find('div', 1)->class = 'bar'; $html->find('div[id=hello]', 0)->innertext = 'foo'; echo $html; $html = file_get_html('http://slashdot.org/'); foreach($html->find('div.article') as $article) {     $item['title']

simplehtmldom.sourceforge.io

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
<?php
/*ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);*/
 
include($_SERVER['DOCUMENT_ROOT'].'simple_html_dom.php');
//simple_html_dom.php 파일 수정 필요합니다.
//https://mandu-mandu.tistory.com/358
 
 
function naver_cafe_article_parser($page_no){
  //작성자 M4ndU
 
  //카페 url
  //카페 링크 aaaaa처리
  //search.clubid, search.menuid 0 처리
  //하였기 때문에 본인 카페 링크및 게시판 확인하셔서 변경하셔야합니다.
  $url = "https://cafe.naver.com/aaaaa/ArticleList.nhn?search.clubid=000000000&search.boardtype=L&search.menuid=00&search.marketBoardTab=D%&search.specialmenutype=&userDisplay=50&search.page=".$page_no;
 
  $html = file_get_html($url);
 
  $board = $html->find('div[class=article-board m-tcol-c]');
  foreach ($board[1]->find('tr'as $article) {
 
    $article_title_link = $article->find('a[class=article]')[0];
    $article_title = $article_title_link->plaintext;
    $article_link = $article_title_link->href;
    $article_publisher = $article->find('td[class=p-nick]')[0]->plaintext;
    $article_date = $article->find('td[class=td_date]')[0]->innertext;
 
    if ($article_title == "") {
      continue;
    }
 
 
    echo "<tr>";
    echo "<td>".$article_title."</td>";
    echo "<td>".$article_publisher."</td>";
    echo "<td>".$article_date."</td>";
    echo "<td> https://cafe.naver.com/aaaaa".$article_link."</td>"//카페링크 aaaaa처리하였음. 본인이 변경하세요.
    echo "</tr>";
  }
}
?>
<!DOCTYPE html>
<html lang="kr" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title></title>
  </head>
  <body>
    <table class="table">
        <thead>
            <th>글제목</th>
            <th>작성자</th>
            <th>등록일시</th>
            <th>링크</th>
        </thead>
<?php
for ($i=1$i<10 ; $i++) { // 파싱할 게시판의 최대 페이지를 고려하세요.
  naver_cafe_article_parser($i);
}
?>
</table>
</body>
</html>
 
cs
728x90
반응형
728x90
반응형

Day1은 올클인데 Day2부터 난이도가 높게 느껴졌다...

 

Day3때는 14번 문제 50점 받은것 외엔 문제를 풀지 못했다.

 

 

어떤 문제는 시간초과로 점수를 받지 못했는데, 시간 복잡도를 줄이는 것이 제일 큰 관건이었던 것 같다. (대부분 해결하지 못했다.)

 

 

그리고 코드챌린지와는 채점방식이 달랐다. 코드챌린지는 채점 케이스 하나마다 점수가 있었는데, NYPC는 케이스를 묶어서 종류로 주는데, 이 종류안에 있는 케이스를 모두 통과해야 그 종류에 배정된 점수를 주었다.

 

그래서 야매로 점수를 조금이라도 받는걸 할 수 없었다. 코드챌린지때는 야매로 풀어서 점수를 조금이라도 받은 것이 도움이 되어 본선에 갈 수 있었다.

 

 

어짜피 알고리즘은 공부한 적이 없고 재미로 푸는 거긴 하지만

 

문제 풀이 (python3):

github.com/M4ndU/algorithm_task/tree/master/NYPC2019

728x90
반응형
  1. kakaotalk 2020.09.05 23:08

    소스코드 언제올라오나요 ㅠㅠ

    • M4ndU 2020.09.07 02:05 신고

      늦어서 죄송합니다. 수정했습니다!

      https://github.com/M4ndU/algorithm_task/tree/master/NYPC2019

728x90
반응형

리캡챠를 적용할 페이지는 login.php (.html이어도 적용 가능합니다.), 검증을 하는 페이지는 login_chk.php (php로 작성하였습니다.)로 가정을 하겠습니다.




1. 리캡챠 키를 생성


아래 링크에 접속합니다.

https://www.google.com/recaptcha/admin






Label 에는 그냥 이름 하나 적어줍니다. ex) login page


reCAPTCHA v3를 선택하고,


Domains에 사이트 도메인을 적어주고,


정책 동의에 체크한 후 register을 누릅니다.



Site key 와 Secret key는 기록해 둡니다.






2. 코드 삽입 (login.php)


login.php에서 <head></head> 태그 사이에 아래 코드를 삽입합니다.

<Site Key> 에 자신의 Site key를 입력합니다.


<script src='https://www.google.com/recaptcha/api.js?render=<Site key>'></script>


<script type="text/javascript">

  grecaptcha.ready(function() {

   grecaptcha.execute('<Site Key>', {action: 'loginpage'})

   .then(function(token) {

    document.getElementById('g-recaptcha-response').value = token;

   });

  });

</script>



login page 에서 로그인 <form> 태그안에 아래 코드를 삽입합니다.



    <form name="login_form" method="post" action="./login_chk.php">

...(중략)....

      <input type="hidden" id="g-recaptcha-response" name="g-recaptcha-response">

    </form>






3. 코드 삽입 (login_chk.php)



php 코드 안에 아래 코드를 삽입합니다.

<Secret key> 안에 자신의 Secret key를 입력하면 됩니다.


//recaptcha

$captcha=$_POST['g-recaptcha-response'];

$secretKey = "<Secret key>";

$ip = $_SERVER['REMOTE_ADDR'];

$response = file_get_contents("https://www.google.com/recaptcha/api/siteverify?secret=".$secretKey."&response=".$captcha."&remoteip=".$ip);

$responseKeys = json_decode($response,true);


if($responseKeys["success"]) {

  //통과한 경우의 코드를 작성

 }

else {

  //통과하지 못한 경우의 코드를 작성

 }

//recaptcha


조건 if($responseKeys["success"]) 이 외에도 https://developers.google.com/recaptcha/docs/v3 문서의 Site Verify Response 부분을 참조하여 통과여부를 설정할 수 있습니다. 저는 간단히 success 부분만 받아 검증한 것입니다.



728x90
반응형
728x90
반응형

반복적으로 같은 요청을 하는 (도배)를 막는 기능을 추가하겠습니다.




kakao auto_reply에서 제공하는 user_key를 이용하겠습니다.

user_key는 옐로우 아이디 마다 부여되는 고유 키값입니다.


처음에는 user_key값을 이용하여 반복적으로 동일한 요청을 해올 경우 딜레이를 주는 방법을 사용하려고 했지만

구현할 방법을 모르겠어서...


직전에 요청한 유저와 다음 요청해온 유저가 같을 경우 함수를 실행하지 않고 리턴하는 방법을 사용하겠습니다.

이 경우 다른 유저가 요청해오지 않는 이상 한 유저가 연속으로 요청을 할 수가 없게 되죠.


도배로 고통받는 개발자...




모든 요청(버튼)에 대해서 적용하는 방법과 각각의 요청(버튼)에 적용하는 방법이 있습니다.

모든 요청(버튼)에 적용하게 되면 한 요청을 했을 때 바로 다른 요청을 할 수가 없으므로

각각에 요청에 적용하는 방법을 사용하겠습니다.

그러기 위해서 class 를 사용하겠습니다.





코드 작성하기


적용된 전체 코드 :  https://github.com/M4ndU/inhun_kakao_chat_bot_2/blob/master/views.py




1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class user_chk():
 
    def __init__(self):
        self.pre_key = "" #이전 user_key값
        self.now_key = "" #현재 user_key값
 
    def check(self, key):
        self.now_key = key # now_key값에 현재 user_key값 대입
 
        if self.pre_key == self.now_key : # 비교 하여 같으면 1을 반환
            passcode = 1
        else :
            self.pre_key = self.now_key # 다를 경우 pre_key값에 now_key값을 덮어쓰고 0 반환
            passcode = 0
        return passcode
cs



바로 다음 코드들을 작성하여 값 초기화 설정을 해 둡니다.

필요한 개수 만큼 작성합니다.


1
2
3
4
 
u0 = user_chk()
u1 = user_chk()
 
cs



이렇게 하면, u0.check(user_key)를 실행하게 될 경우

이전 user_key값(=pre_key)과 비교하여 같으면 1 반환, 다르면 pre_key에 저장하고 0을 반환합니다.


u0, u1각각 독립적으로 사용되기 때문에 u0.check("dd")를 했다고 해서 u1.pre_key에 dd가 드러가지 않는 것이죠.

이를 통해 각각 요청(버튼)에 적용하여 사용할 수 있습니다.



이제 user_key값을 가져와 봅시다.


1
2
3
4
...
    datacontent = received_json_data['content']
    user_key = received_json_data['user_key']
 
cs


기존 작성했던 datacontent = received_json_data['content'] 바로 밑에 작성해 줍니다.




user_key값도 불러왔으니 이제 각 분기마다 적용해줍니다.


https://github.com/M4ndU/inhun_kakao_chat_bot_2/blob/master/views.py

92~104


92
93
94
95
96
97
98
99
100
101
102
103
104
    if datacontent == '오늘 급식':
        if u0.check(user_key):
            return ret_proc(PlzStopIt)
        dt = datetime.datetime.today()
        diet = get_meal(dt)
        return ret_proc(diet)
 
    elif datacontent == '내일 급식':
        if u1.check(user_key):
            return ret_proc(PlzStopIt)
        dt = datetime.datetime.today() + datetime.timedelta(days=1)
        diet = get_meal(dt)
        return ret_proc(diet)
cs



        if u0.check(user_key):

            return ret_proc(PlzStopIt)


를 통해서 u0.pre_key = u0.now_key가 되어 1을 반환하게 되면

조건이 True가 되어 밑에 작업을 진행하지 않고 바로 문자열을 바로 반환합니다.

그럴려면 PlzStopIt에 문자열을 저장해주어야 겠죠?


728x90
반응형
  1. 에러 2018.06.04 17:39

    잘못된 응답입니다. response is not OK(200) : 500 / Internal Server Error
    원인이 뭘까요..

    • M4ndU 2018.06.04 19:22 신고

      해당 에러코드만 가지고 제가 원인을 알 수는 없습니다...

  2. 파싱 오류인가용? 2018.06.05 22:24

    자동 응답 시스템이 응답을 하지 않습니다. 라는 말을 뿜어대네요ㅠㅠ. 그냥 텍스트 출력하는 BOT정보, 중간고사 버튼들은 잘 작동하는데 급식 관련이 작동을 안해융....
    초보인데 좀 도와주실 수 있나여?

    [05/Jun/2018 22:21:23] "POST /mealapp/message HTTP/1.1" 500 71923
    오늘 급식
    Internal Server Error: /mealapp/message
    Traceback (most recent call last):
    File "C:\Users\Administrator\mealbot\myvenv\lib\site-packages\django\core\handlers\exception.py", line 41, in inner
    response = get_response(request)
    File "C:\Users\Administrator\mealbot\myvenv\lib\site-packages\django\core\handlers\base.py", line 187, in _get_response
    response = self.process_exception_by_middleware(e, request)
    File "C:\Users\Administrator\mealbot\myvenv\lib\site-packages\django\core\handlers\base.py", line 185, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
    File "C:\Users\Administrator\mealbot\myvenv\lib\site-packages\django\views\decorators\csrf.py", line 58, in wrapped_view
    return view_func(*args, **kwargs)
    File "C:\Users\Administrator\mealbot\mealapp\views.py", line 96, in answer
    diet = get_meal(dt)
    File "C:\Users\Administrator\mealbot\mealapp\views.py", line 48, in get_meal
    l_l = get_diet(2, local_date, local_weekday)
    NameError: name 'get_diet' is not defined

    • 파싱 오류인가용? 2018.06.05 22:53

      (request) 들어간 줄이 다 문제인데 으으 리퀘스트 모듈 문제인가요?

    • M4ndU 2018.06.05 22:55 신고

      오류코드 마지막줄을 보세요.
      File "C:\Users\Administrator\mealbot\mealapp\views.py", line 48, in get_meal
      l_l = get_diet(2, local_date, local_weekday)
      NameError: name 'get_diet' is not defined

      get_deit라는 함수가 정의되지 않은 것입니다.

  3. 파싱 오류인가용? 2018.06.05 23:21

    엇...
    https://github.com/M4ndU/inhun_kakao_chat_bot_2/blob/master/views.py 만두님 소스 사용 하였으며
    def get_meal <- 정의 되어있는 거 아닌가용?

    def get_meal(dt):
    local_date = dt.strftime("%Y.%m.%d")
    local_weekday = dt.weekday()

    l_l = get_diet(2, local_date, local_weekday)

    if len(l_l) == 1:
    lunch = "급식이 없습니다."
    return lunch

    • M4ndU 2018.06.05 23:26 신고

      get_meal과 get_diet는 엄연히 다른 함수입니다. get_diet함수는 parser.py에 정의된 함수이며 제소스를 그대로 사용하셨다면 parser.py파일이 없는게 아닌지 확인해보세요

  4. 파싱 오류인가용? 2018.06.05 23:34

    자꾸 질문해서 죄송합니다 ㅠㅠㅠㅠ
    마지막으로 여쭤보겠습니다. 안되면 어떻게든 해볼게요!

    parser.py 파일을 확인해보았습니다.
    아래와 같이 만두님이 알려주신 parser.py 에 get_diet 함수가 정의되어 있는것을 확인했어요!
    그런데... parser.py 파일은 항상 있었습니다...

    def get_diet(code, ymd, weekday):
    schMmealScCode = code #int 1조식2중식3석식
    schYmd = ymd #str 요청할 날짜 yyyy.mm.dd
    if weekday == 5 or weekday == 6: #토요일,일요일 버림
    element = " " #공백 반환

  5. 파싱 오류인가용? 2018.06.05 23:39

    startapp 폴더. 즉 애플리케이션 폴더에 (views폴더) 에 있었습니다!

    • 아아 2018.06.05 23:40

      views.py 있는 폴더입니다1!

    • M4ndU 2018.06.10 17:48 신고

      python3로 실행하신거 맞으시죠?

      저도 잘 모르겠습니다.. :(

  6. 2018.09.01 13:16

    비밀댓글입니다

  7. onetop 2018.09.13 22:43

    우선 소스 정말 잘봤습니다 공유해주셔서 감사합니다

    다름이 아니라 parser.py 에 문제가 있는것으로 보입니다.
    다른 기능들은 모두 작동하는데
    해당 파일에서 급식을 파싱을 못해오는듯 합니다

    • 1111 2018.09.13 22:45

      해당프로필의 자동응답 시스템이 응답을 하지 않습니다. 라고 뜨네요

      확실히 교육청사이트에서 제대로 파싱을 못하고 있나봅니다

      로그에는
      [13/Sep/2018 22:44:15] "POST /message HTTP/1.1" 500 27

      뜹니다

    • M4ndU 2018.09.13 23:05 신고

      방금 테스트 해본 결과, parser.py는 정상작동 합니다. 급식 파싱 정상적으로 동작합니다.

    • 1111 2018.09.13 23:44

      네 저도 테스트 해본 결과 정상 동작합니다

      결국에는 github 에 올려주신
      것중 sys.path.insert(0, "inhun_discord_chat_bot_2 경로") 때문인듯 한데 설명부탁드립니다

  8. 1111 2018.09.13 23:42

    sys.path.insert(0, "inhun_discord_chat_bot_2 경로")

    git 허브에 보면 이런 부분이 있는데
    저것이 모듈 임포트 아닌가요?
    https://github.com/M4ndU/inhun_discord_chat_bot_2/blob/master/inhun_bot.py 를 저장하고 경로를 넣으라는 설명같은데
    아무리 봐도 이부분은 디스코드 관련 파일이라
    카카오 api 하고 상관없을듯 한데 아닌가요??
    이부분 때문에 제대로 파싱을 못하는것 같아요

    • M4ndU 2018.09.14 00:00 신고

      import sys
      sys.path.insert(0, "inhun_discord_chat_bot_2 경로")

      이 코드가 추가되어있는 이유는 parser.py파일이 (github기준)해당 경로에 있었기 때문입니다. (혼란을 드린 것 같네요.. 죄송합니다.)

      만약 parser.py를 views.py가 있는 경로로 옮겨 두셨다면 위의 코드는 지우셔도 됩니다.

    • 1111 2018.09.14 00:04

      아닙니다 혼란은 무슨 이해못한 제가 멍청한거죠...

      한가지 질문 더 드리겠습니다

      import requests, re
      from bs4 import BeautifulSoup

      def get_diet(code, ymd, weekday):
      schMmealScCode = code #int 1조식2중식3석식
      schYmd = ymd #str 요청할 날짜 yyyy.mm.dd
      if weekday == 5 or weekday == 6: #토요일,일요일 버림
      element = " " #공백 반환
      else:
      num = weekday + 1 #int 요청할 날짜의 요일 0월1화2수3목4금5토6일 파싱한 데이터의 배열이 일요일부터 시작되므로 1을 더해줍니다.
      URL = (
      "https://stu.sen.go.kr/sts_sci_md01_001.do?"
      "schulCode=B100000659"
      "&schulCrseScCode=4"
      "&schulKndScCode=04"
      "&schMmealScCode=%d&schYmd=%s" % (schMmealScCode, schYmd)
      )
      #http://stu.AAA.go.kr/ 관할 교육청 주소 확인해주세요.
      #schulCode= 학교고유코드
      #schulCrseScCode= 1유치원2초등학교3중학교4고등학교
      #schulKndScCode= 01유치원02초등학교03중학교04고등학교

      #기존 get_html 함수부분을 옮겨왔습니다.
      html = ""
      resp = requests.get(URL)
      if resp.status_code == 200 : #사이트가 정상적으로 응답할 경우
      html = resp.text
      soup = BeautifulSoup(html, 'html.parser')
      element_data = soup.find_all("tr")
      element_data = element_data[2].find_all('td')
      try:
      element = str(element_data[num])

      #filter
      element_filter = ['[', ']', '<td class="textC last">', '<td class="textC">', '</td>', '&amp;', '(h)', '.']
      for element_string in element_filter :
      element = element.replace(element_string, '')
      #줄 바꿈 처리
      element = element.replace('<br/>', '\n')
      #모든 공백 삭제
      element = re.sub(r"\d", "", element)

      #급식이 없을 경우
      except:
      element = " " # 공백 반환
      return element

      이게 git 에 있는 코드고

      import requests
      from bs4 import BeautifulSoup
      import re


      def get_html(url):
      _html = ""
      resp = requests.get(url)
      if resp.status_code == 200:
      _html = resp.text
      return _html


      def get_diet(code, ymd, weekday):
      schMmealScCode = code #int
      schYmd = ymd #str

      num = weekday + 1 #int 0월1화2수3목4금5토6일
      URL = (
      "http://stu.sen.go.kr/sts_sci_md01_001.do?"
      "schulCode=B100000659&schulCrseScCode=4&schulKndScCode=04"
      "&schMmealScCode=%d&schYmd=%s" % (schMmealScCode, schYmd)
      )
      html = get_html(URL)
      soup = BeautifulSoup(html, 'html.parser')
      element = soup.find_all("tr")
      element = element[2].find_all('td')
      try:
      element = element[num] #num
      element = str(element)
      element = element.replace('[', '')
      element = element.replace(']', '')
      element = element.replace('<br/>', '\n')
      element = element.replace('<td class="textC last">', '')
      element = element.replace('<td class="textC">', '')
      element = element.replace('</td>', '')
      element = element.replace('(h)', '')
      element = element.replace('.', '')
      element = re.sub(r"\d", "", element)
      except:
      element = " "
      return element

      이것이 블로그에 있는 코드입니다

      해당 코드들로

      from parser import *
      from django.shortcuts import render
      from django.views.decorators.csrf import csrf_exempt
      from django.http import JsonResponse
      from pytz import timezone
      import datetime , json, sys
      sys.path.insert(0, "/home/user/kakao/app/inhun_bot.py")

      dt = datetime.datetime.today()
      local_date = dt.strftime("%Y.%m.%d")
      local_weekday = dt.weekday()

      print (local_date,local_weekday)
      diet = get_diet(2, local_date, local_weekday)
      print(diet)

      이명령어로 실행해보면 블로그 코드는 실행이 되지만 git 코드는 None 라는 값을 출력합니다

    • M4ndU 2018.09.14 00:13 신고

      제가 테스트해본 결과, 둘 다 정상작동하여 같은 결과를 보여줍니다.

      두개의 코드에서 기능적 차이는 없습니다.
      밖으로 나갔던 get_html함수를 안으로 넣은 것과 필터링 부분을 반복문을 사용해 코드 길이를 줄인 것 뿐입니다.

    • M4ndU 2018.09.14 00:15 신고

      아 from parser import * 부분이 코드 최상단에 위치하고 있네요.

      sys.path.insert(0, "/home/user/kakao/app/inhun_bot.py")
      이 코드 하단으로 내려주세요.

    • 1111 2018.09.14 00:27

      흠 저는 아무리 해봐도
      이상하게 git 에 올려주신 코드는 Null 만 출력되네요 무튼 이건 제가 내일 다시 해봐야겠네요....

      그리고 혹시 views 파일에서

      def get_meal(dt):
      local_date = dt.strftime("%Y.%m.%d")
      local_weekday = dt.weekday()

      l_l = get_diet(2, local_date, local_weekday)

      if len(l_l) == 1:
      lunch = "급식이 없습니다."
      return lunch

      else:
      d_d = get_diet(3, local_date, local_weekday)
      if len(d_d) == 1:
      lunch = local_date + " 중식\n" + l_l
      dinner = ""
      return lunch
      else:
      lunch = local_date + " 중식\n" + l_l + "\n"
      dinner = local_date + " 석식\n" + d_d

      return lunch + dinner

      테스트 하려면 어떻게 해야할까요??
      블로그 소스로 만든 parser 정상 받아오는건 확인했고 views 에 정상 동작하는 parser 을 넣었는데 카톡에서는 자동응답 시스템이 응답을 하지 않는다 [14/Sep/2018 00:26:33] "POST /message HTTP/1.1" 500 27
      라고 로그가 뜨네요 ....

    • 1111 2018.09.14 00:27

      Null 이 아니라 None 입니다.

    • M4ndU 2018.09.14 00:31 신고

      코드 중간중간에 print("1")같은 코드를 삽입해주어서 실행했을 때 터미널에 출력되는 개수들을 보고 어디까지 실행되어서 어디에서 막혔는지를 파악하는 방법이 있습니다

  9. 1111 2018.09.14 00:35

    그렇군요 내일 테스트 해보면서 오류 찾아야 겠네요 ..
    늦은시간까지 감사합니다.

    그리고 이건 Nods.js 짠 api 라는데 보시면 req 말고 res 하고 next 를 주던데
    파이썬도 저런옵션 안줘도 되나요? 가끔 req만 주니 메뉴선택창이 사라지는 버그가 있는것 같아서요
    /* 처음 들어왔을 때 */
    router.get('/', function(req, res, next) {
    const menu = {
    type: 'buttons',
    buttons: ["노래 추천해줘.", "네 노래로 추천해줘.","오늘 급식좀."]
    };
    res.set({
    'content-type': 'application/json'
    }).send(JSON.stringify(menu));
    });


    출처: http://wingnim.tistory.com/6 [jinyo의 뇌]

    • M4ndU 2018.09.14 07:34 신고

      파이썬도 return 값에 keyboard값을 포함하여 보내기 때문에 메뉴가 사라지지 않습니다.
      아마 중간에 오류가 나서 return 이 이루어지지 않아서 메뉴가 사라진것 같습니다.

    • 1111 2018.09.14 23:06

      그렇군요 감사합니다
      카카오 측에 물어보니

      안녕하세요.
      확인해보니 Content-Type 이 application/json 으로 내려오고 있습니다.
      https://github.com/plusfriend/auto_reply#specification 를 참고하셔서
      application/json; charset=utf-8 로 변경해주시기 바랍니다.
      수정 후에도 문제가 계속되면 코멘트 달아주세요.
      감사합니다.

      라고 하더군요 .

    • 1111 2018.09.16 12:17

      아무리 구글링을 해봐도
      application/json; charset=utf-8 바꾸는 법을 모르겠네요 .... ㅠㅠ
      그나마 비슷한사례가
      https://github.com/plusfriend/auto_reply/issues/381
      같은데 혹시 어떻게 수정해야 할까요??

    • M4ndU 2018.09.16 13:46 신고

      저도 잘 모르겠네요...

    • 1111 2018.09.16 18:53

      몇가지 연구를 해봤는데

      diet 에 데이터가 담길때

      흑미밥
      차돌박이고추장찌개
      도라지오이생채
      모듬스크램블에그
      갈치카레구이
      배추김치

      형태로 담기는데
      그러면
      diet = "흑미밥 \n 차돌박이고추장찌개 \n"
      담겨야 하는데 이렇게 안담기나 보네요
      print(diet) 해서 터미널에 python views.py 실행해도 출력이 안뜨니 확인도 못하고 답답하네요

      그리고 소스 보니 "텍스트" , '텍스트' 이렇게 2가지 방식을 섞어 쓰던데
      '' 로만 써야 되는거 아닌가요?파이썬은

    • M4ndU 2018.09.16 18:56 신고

      views.py엔 함수정의밖에 없으니 아무것도 출력되지 않는게 당연하죠..

      파이썬은 "string"과 'string' 모두 사용 가능합니다.

728x90
반응형

이번에는 이번에는 맴버 관련 기능을 구현해보도록 하겠습니다.


목차입니다.



1. 맴버가 서버 접속, 퇴장시 서버 텍스트 채널에 메세지를 보내는 기능

2. 맴버가 서버 접속시 1:1 채팅에 메세지를 보내는 기능

3. 주기적으로 공지메세지를 보내는 기능 (백그라운드로 동작)










1. 맴버가 서버 접속, 퇴장시 서버 텍스트 채널에 메세지를 보내는 기능



먼저 코드를 봅시다.


1
2
3
4
5
6
7
8
9
10
11
@client.event
async def on_member_join(member):
    fmt = '{1.name} 에 오신것을 환영합니다., {0.mention} 님'
    channel = member.server.get_channel("channel_id_here")
    await client.send_message(channel, fmt.format(member, member.server))
 
@client.event
async def on_member_remove(member):
    channel = member.server.get_channel("channel_id_here")
    fmt = '{0.mention} 님이 서버에서 나가셨습니다.'
    await client.send_message(channel, fmt.format(member, member.server))
cs



on_member_join 함수는 맴버가 서버에 들어 왔을 때 실행되는 함수이고,

on_member_remove 함수는 맴버가 서버를 나갔을 때 실행되는 함수입니다.


format를 통해서 {0.mention}  에는 맴버가 언급되고,  {1.name} 에는 서버이름이 자동적으로 대입됩니다.


channel_id_here에 들어갈 id를 찾는 방법은 본문 하단에 설명되어 있습니다.









2. 맴버가 서버 접속시 1:1 채팅에 메세지를 보내는 기능



간단합니다. 아래의 코드를 이용하면 끝입니다.


await client.send_message(member, "내용")


여기서 우리는 맴버가 서버에 들어왔을 때 보내도록 할 것이므로, on_member_join 함수 밑에 두겠습니다.



1
2
3
4
5
6
@client.event
async def on_member_join(member):
    fmt = '{1.name} 에 오신걸 환영합니다, {0.mention} 님'
    channel = member.server.get_channel("channel_id_here")
    await client.send_message(channel, fmt.format(member, member.server))
    await client.send_message(member, "내용")
csv



"내용" 에 원하는 내용을 작성하시면 됩니다.









3. 주기적으로 공지사항등의 메세지를 보내는 기능입니다.



백그라운드로 동작합니다.


코드입니다. :


1
2
3
4
5
6
    async def my_background_task():
        await client.wait_until_ready()
        channel = discord.Object(id='channel_id_here')
        while not client.is_closed:
            await client.send_message(channel, "hi")
            await asyncio.sleep(5
cs


@client.event 가 필요 없습니다.



----------------channel_id_here 구하기---------------


channel_id_here 에 메세지를 보낼 텍스트 채널의 id를 입력해주시면 되는데요.

id를 확인하는 방법은 다음과 같습니다:

1. 디스코드 채널을 웹을 통해 접속한다.

2. 텍스트 채널에 들어간다.

3. URL마지막에 있는 18자리 숫자가 id다.




"hi" 대신에 보낼 메세지를 넣어주시면 됩니다.


await asyncio.sleep(5)

에서 5 대신에 자신이 원하는 시간 간격을 초 단위로 작성하여 주시면 됩니다.


EX) await asyncio.sleep(60*60*24) 이라고 하면 1일마다 메세지를 보내게 됩니다.




이 부분을 다 작성하셨으면 전체 코드에서 하단으로 가셔서 client.run() 부분을 찾습니다.

client.run() 코드 바로 위에 아래의 코드를 추가합니다 :


client.loop.create_task(my_background_task())


그러면


client.loop.create_task(my_background_task())

cleint.run('token')


처럼 됩니다.


만약 discord.errors.Forbidden: FORBIDDEN (status code: 403): Missing Access 이런 오류가 발생한다면

권한 문제이니 서버에서 봇의 권한을 설정해 주시면 됩니다.







전체 코드는 다음 링크를 참조해주세요.


https://github.com/M4ndU/inhun_discord_chat_bot_2/blob/master/inhun_bot.py

728x90
반응형
  1. boringpinn 2020.03.05 18:21

    혹시 유저들에게 전체dm을 보내는법도 알고 계신가요?? 응용해보려 하는데 힘드네요...

  2. 국대밥 2020.04.06 07:54

    요즘은 파이썬으로 디스코드 봇만드는 법 강의 안하시나요...?

    • M4ndU 2020.04.06 14:58 신고

      네.. 요즘엔 디스코드 봇을 안만들고 있다보니 그렇네요..

  3. Hoing 2020.08.24 19:34

    요일이나 시간대마다 알림을 주는 명령어는 어떻게 만들수있나요..?ㅠㅠ

    • M4ndU 2020.08.26 15:22 신고

      APScheduler 같은 모듈 이용하시면 될 것 같습니다.

+ Recent posts