박진수

Merge branch 'integration-2nd' into 'master'

2차 통합 완료

2차 통합을 완료했습니다. 고생하셨습니다 다들~!

## 개선사항

* frontend
  * login 후에는 login 창 안보이게 분기
  * 설정을 .env로 편리하게 할 수 있도록 변경
  * `/channels` API로 채널리스트를 받아와서 보여줄 수 있게

* backend
  * 영어채팅은 소켓에서 안 보내기

* tts
  * 재생 작업을 큐잉해서 순차적으로 재생하거나  max time을 지정

See merge request !14
...@@ -3,7 +3,8 @@ import Login from "./Login"; ...@@ -3,7 +3,8 @@ import Login from "./Login";
3 import Body from "./Body"; 3 import Body from "./Body";
4 4
5 function App() { 5 function App() {
6 - let accessToken = null; 6 + // let accessToken = null;
7 + let accessToken = 1;
7 8
8 return ( 9 return (
9 <div className="app"> 10 <div className="app">
......
...@@ -8,7 +8,7 @@ import { WatchOutlined } from "@material-ui/icons"; ...@@ -8,7 +8,7 @@ import { WatchOutlined } from "@material-ui/icons";
8 import io from 'socket.io-client' 8 import io from 'socket.io-client'
9 import tts from './tts'; 9 import tts from './tts';
10 10
11 -const socket = io.connect("http://localhost:3000") 11 +const socket = io.connect("http://localhost:3303")
12 socket.on("connect", event=>{ 12 socket.on("connect", event=>{
13 // 테스트용으로 umi0410에게 입장. 13 // 테스트용으로 umi0410에게 입장.
14 // 다른 거 아무거나 채널이름을 넣으면 되겠지만, 그렇게 하고싶으면 백엔드에서 인자설정을 해줘야함. 14 // 다른 거 아무거나 채널이름을 넣으면 되겠지만, 그렇게 하고싶으면 백엔드에서 인자설정을 해줘야함.
...@@ -18,6 +18,7 @@ socket.on("connect", event=>{ ...@@ -18,6 +18,7 @@ socket.on("connect", event=>{
18 // 말해야할 메시지가 왔을 때. 이건 따로 Component와 묶지 않아도 알아서 실행됩니다. 18 // 말해야할 메시지가 왔을 때. 이건 따로 Component와 묶지 않아도 알아서 실행됩니다.
19 socket.on('chat message', (name, msg)=>{ 19 socket.on('chat message', (name, msg)=>{
20 console.log(msg) 20 console.log(msg)
21 + console.log("got message")
21 tts.speak(msg) 22 tts.speak(msg)
22 }) 23 })
23 24
......
...@@ -2,7 +2,7 @@ import React from "react"; ...@@ -2,7 +2,7 @@ import React from "react";
2 import "./Login.css"; 2 import "./Login.css";
3 3
4 function Login(){ 4 function Login(){
5 - const OAuthUrl = ""; // oauth 인증용 url 5 + const OAuthUrl = `https://id.twitch.tv/oauth2/authorize?response_type=code&approval_prompt=auto&redirect_uri=http://localhost:3303/join&client_id=2d1gvcqyiyrk180qvnkec2fl23sv1o`; // oauth 인증용 url
6 return ( 6 return (
7 <div className="login"> 7 <div className="login">
8 <img 8 <img
......
1 -SOCKET_PORT=
2 -TOKEN=
3 -PAPAGO_ID=
4 -PAPAGO_SECRET=
5 -BOT_USERNAME=
6 -OAUTH_TOKEN=
1 +
2 +HOST_URI = [twitch end point url]
3 +SOCKET_PORT= [backend socket server port]
4 +TWITCH_CLIENT= [twitch dev client id]
5 +TWITCH_SECRET= [twitch dev client id secret]
6 +PAPAGO_ID= [papago client id]
7 +PAPAGO_SECRET= [papago client secret]
8 +BOT_USERNAME= [twitch bot name]
9 +OAUTH_TOKEN= [twitch user oauth token]
...@@ -48,45 +48,23 @@ exports.detectchat = (message, client, io, target) => { ...@@ -48,45 +48,23 @@ exports.detectchat = (message, client, io, target) => {
48 }); 48 });
49 } 49 }
50 50
51 - 51 +exports.trans = (message, client,io, target) => {
52 -// exports.trans = (message, lang, io, room) => { 52 + request.post(
53 -// request.post( 53 + {
54 -// { 54 + url: PAPAGO_URL,
55 -// url: PAPAGO_URL, 55 + headers: {
56 -// headers: { 56 + 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
57 -// 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', 57 + 'X-Naver-Client-Id': `${PAPAGO_ID}`,
58 -// 'X-Naver-Client-Id': `${PAPAGO_ID}`, 58 + 'X-Naver-Client-Secret': `${PAPAGO_SECRET}`
59 -// 'X-Naver-Client-Secret': `${PAPAGO_SECRET}` 59 + },
60 -// }, 60 + body: `source=ko&target=en&text=` + message,
61 -// body: `source=${lang}&target=ko&text=` + message, 61 + json:true
62 -// json:true 62 + },async (error, response, body) => {
63 -// },(error, response, body) => { 63 + if(!error && response.statusCode == 200) {
64 -// if(!error && response.statusCode == 200) { 64 + var Translated = await body.message.result.translatedText;
65 -// var Translated = body.message.result.translatedText; 65 + client.say(target, "(Trans) "+Translated);
66 -// io.to(room).emit('chat message', "trans", Translated); 66 + io.to(target.replace('#','')).emit('chat message', "Trans", Translated);
67 -// } 67 + }
68 -// }); 68 + });
69 -// } 69 +}
70 -
71 -
72 -// exports.detect = (message,io,room) => {
73 -// request.post(
74 -// {
75 -// url: dPAPAGO_URL,
76 -// headers: {
77 -// 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
78 -// 'X-Naver-Client-Id': `${PAPAGO_ID}`,
79 -// 'X-Naver-Client-Secret': `${PAPAGO_SECRET}`
80 -// },
81 -// body: `query=` + message,
82 -// json:true
83 -// },(error, response, body) => {
84 -// if(!error && response.statusCode == 200) {
85 -// var lang = body.langCode;
86 -// if(lang != 'ko'){
87 -// this.trans(message,lang,io,room)
88 -// }
89 -// }
90 -// });
91 -// }
92 70
......
...@@ -160,6 +160,14 @@ ...@@ -160,6 +160,14 @@
160 "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", 160 "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz",
161 "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" 161 "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA=="
162 }, 162 },
163 + "axios": {
164 + "version": "0.21.0",
165 + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.0.tgz",
166 + "integrity": "sha512-fmkJBknJKoZwem3/IKSSLpkdNXZeBu5Q7GA/aRsr2btgrptmSCxi2oFjZHqGdK9DoTil9PIHlPIZw2EcRJXRvw==",
167 + "requires": {
168 + "follow-redirects": "^1.10.0"
169 + }
170 + },
163 "balanced-match": { 171 "balanced-match": {
164 "version": "1.0.0", 172 "version": "1.0.0",
165 "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 173 "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
...@@ -488,6 +496,11 @@ ...@@ -488,6 +496,11 @@
488 "unpipe": "~1.0.0" 496 "unpipe": "~1.0.0"
489 } 497 }
490 }, 498 },
499 + "follow-redirects": {
500 + "version": "1.13.0",
501 + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.0.tgz",
502 + "integrity": "sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA=="
503 + },
491 "forever-agent": { 504 "forever-agent": {
492 "version": "0.6.1", 505 "version": "0.6.1",
493 "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", 506 "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
11 "license": "ISC", 11 "license": "ISC",
12 "dependencies": { 12 "dependencies": {
13 "cors": "^2.8.5", 13 "cors": "^2.8.5",
14 + "axios": "^0.21.0",
14 "dotenv": "^8.2.0", 15 "dotenv": "^8.2.0",
15 "ejs": "^3.1.5", 16 "ejs": "^3.1.5",
16 "express": "^4.17.1", 17 "express": "^4.17.1",
......
...@@ -11,6 +11,7 @@ const io = require('socket.io')(http, { ...@@ -11,6 +11,7 @@ const io = require('socket.io')(http, {
11 }); 11 });
12 const papago = require('./openAPIs/papago_api'); 12 const papago = require('./openAPIs/papago_api');
13 13
14 +
14 const tmi = require('tmi.js'); 15 const tmi = require('tmi.js');
15 // Define configuration options 16 // Define configuration options
16 var opts = { 17 var opts = {
...@@ -18,17 +19,18 @@ var opts = { ...@@ -18,17 +19,18 @@ var opts = {
18 username: process.env.BOT_USERNAME, 19 username: process.env.BOT_USERNAME,
19 password: process.env.OAUTH_TOKEN 20 password: process.env.OAUTH_TOKEN
20 }, 21 },
21 - channels: [ 22 + channels: ["tmwardo"]
22 - 'nnonuu'
23 - ]
24 }; 23 };
25 // Create a client with our options 24 // Create a client with our options
26 -const client = new tmi.client(opts); //twitch chatbot client 25 +var client = new tmi.client(opts); //twitch chatbot client
27 26
27 +var bodyParser = require('body-parser');
28 +const { default: Axios } = require('axios');
29 +app.use(bodyParser.urlencoded({ extended: false }));
30 +app.use(bodyParser.json());
28 app.set('view engine', 'ejs'); 31 app.set('view engine', 'ejs');
29 app.set('views', './testviews'); 32 app.set('views', './testviews');
30 33
31 -let room = ['nnonuu', 'bachelorchuckchuck'];
32 // client.opts.channels; 34 // client.opts.channels;
33 let a = 0; 35 let a = 0;
34 36
...@@ -38,13 +40,59 @@ app.get('/', (req, res) => { ...@@ -38,13 +40,59 @@ app.get('/', (req, res) => {
38 }); 40 });
39 41
40 app.get('/list',(req,res) => { 42 app.get('/list',(req,res) => {
41 - res.send(room); 43 + var result = []
44 + for (var i = 0; i<client.channels.length; i++){
45 + result.push(client.channels[i].slice(1))
46 + }
47 + res.send(result)
48 +});
49 +
50 +app.post('/jointest',async (req,res)=>{
51 + // requests.post(`https://id.twitch.tv/oauth2/token?client_id=<클라이언트 ID>&client_secret=${process.env.TOKEN}&grant_type=client_credentials`).json()
52 + JoinChannel(req.body.streamer);
53 + res.send(req.body.streamer)
54 +});
55 +console.log(`https://id.twitch.tv/oauth2/authorize?response_type=code&approval_prompt=auto&redirect_uri=${process.env.HOST_URI+':'+process.env.SOCKET_PORT}/join&client_id=${process.env.TWITCH_CLIENT}`)
56 +////////////////////////oauth////////////////////////
57 +const axios = require('axios')
58 +app.get('/oauth',(req,res)=>{
59 + let codeAddr = `https://id.twitch.tv/oauth2/authorize?response_type=code&approval_prompt=auto&redirect_uri=${process.env.HOST_URI+':'+process.env.SOCKET_PORT}/join&client_id=${process.env.TWITCH_CLIENT}`
60 +
61 + res.redirect(codeAddr)
62 +});
63 +app.get('/join', async (req,res)=>{
64 + let code = req.query.code
65 + let reqAddr = `https://id.twitch.tv/oauth2/token?client_id=${process.env.TWITCH_CLIENT}&client_secret=${process.env.TWITCH_SECRET}&code=${code}&grant_type=authorization_code&redirect_uri=${process.env.HOST_URI+':'+process.env.SOCKET_PORT}/test`
66 + axios.post(reqAddr).then(resp1=>{
67 + axios.get('https://id.twitch.tv/oauth2/validate',
68 + {
69 + headers:{
70 + Authorization : "Bearer "+ resp1.data.access_token
71 + }
72 + }
73 + ).then(resp2=>{
74 + JoinChannel(resp2.data.login)
75 + res.redirect("http://localhost:8000?authenticated=true") // 이거 프론트 유알엘임
76 + })
77 + })
78 +
42 }) 79 })
43 80
44 -app.post('/add',(req,res)=>{ 81 +app.get('/test',(req,res)=>{
45 - room.append(req.body.streamer); 82 + res.send("")
46 - res.send(req.body.streamer);
47 }) 83 })
84 +////////////////////////oauth////////////////////////
85 +
86 +
87 +async function JoinChannel(streamer){
88 + await client.action(streamer,'KhuwitchBot 두두등장');
89 + await opts.channels.push('#'+streamer);
90 + await delete client;
91 + client = await new tmi.client(opts);
92 + client.on('message', onMessageHandler);
93 + client.on('connected', onConnectedHandler);
94 + client.connect();
95 +}
48 96
49 97
50 io.on('connection', (socket) => { 98 io.on('connection', (socket) => {
...@@ -91,8 +139,17 @@ client.connect(); ...@@ -91,8 +139,17 @@ client.connect();
91 // Called every time a message comes in 139 // Called every time a message comes in
92 function onMessageHandler (target, context, msg, self) { 140 function onMessageHandler (target, context, msg, self) {
93 if (self) { return; } // Ignore messages from the bot 141 if (self) { return; } // Ignore messages from the bot
94 -
95 if (msg.startsWith('!')){ 142 if (msg.startsWith('!')){
143 + if(msg.startsWith('!번역')){
144 + io.to(target.replace('#','')).emit('chat message',context["display-name"],msg.slice(4))
145 + papago.trans(msg.slice(4), client, io, target)
146 + }
147 + }
148 + else if(context["display-name"] == "빵_떡"
149 + || context["display-name"]=="Nightbot"
150 + || context["display-name"]=="싹뚝"
151 + || context["display-name"]=="KhuwitchBot"){
152 + return;
96 } 153 }
97 else{ 154 else{
98 io.to(target.replace('#','')).emit('chat message',context["display-name"],msg) 155 io.to(target.replace('#','')).emit('chat message',context["display-name"],msg)
......
...@@ -50,8 +50,8 @@ ...@@ -50,8 +50,8 @@
50 </head> 50 </head>
51 <body> 51 <body>
52 <select> 52 <select>
53 + <option value="tmwardo">tmwardo</option>
53 <option value="nnonuu">nnonuu</option> 54 <option value="nnonuu">nnonuu</option>
54 - <option value="bachelorchuckchuck">bachelorchuckchuck</option>
55 </select> 55 </select>
56 <ul id="messages"></ul> 56 <ul id="messages"></ul>
57 <form action=""> 57 <form action="">
...@@ -64,7 +64,7 @@ ...@@ -64,7 +64,7 @@
64 $(() => { 64 $(() => {
65 const name = prompt('What your name'); 65 const name = prompt('What your name');
66 const socket = io(); 66 const socket = io();
67 - let room = ['nnonuu', 'bachelorchuckchuck']; 67 + let room = ['tmwardo','nnonuu'];
68 var streamer = room[0] 68 var streamer = room[0]
69 let num = 0; 69 let num = 0;
70 socket.emit('joinRoom', streamer, name); 70 socket.emit('joinRoom', streamer, name);
...@@ -78,11 +78,11 @@ ...@@ -78,11 +78,11 @@
78 }); 78 });
79 79
80 80
81 - $('form').submit(() => { 81 + // $('form').submit(() => {
82 - socket.emit('chat message', streamer, name, $('#m').val()); 82 + // socket.emit('chat message', streamer, name, $('#m').val());
83 - $('#m').val(''); 83 + // $('#m').val('');
84 - return false; 84 + // return false;
85 - }); 85 + // });
86 86
87 socket.on('chat message', (name, msg) => { 87 socket.on('chat message', (name, msg) => {
88 $('#messages').append($('<li>').text(name + ' : ' + 88 $('#messages').append($('<li>').text(name + ' : ' +
......