diff --git a/app.js b/app.js index 17d74cc..a26e8c1 100644 --- a/app.js +++ b/app.js @@ -5,12 +5,12 @@ require('dotenv').config() // UTILS +const checkUser = require('./utils/checkUser') +const checkChannel = require('./utils/checkChannel') const getRewardData = require('./utils/getRewardData') const getRewardID = require('./utils/getRewardID') const getUserAccessToken = require('./utils/getUserAccessToken') -const getUserID = require('./utils/getUserID') const getUserInfo = require('./utils/getUserInfo') -const getUserName = require('./utils/getUserName') const oauthGen = require('./utils/oauthGen') const parseMessage = require('./utils/parseMessage') const rewardRedemption = require('./utils/rewardRedemption') @@ -54,19 +54,21 @@ app.get('/twitch/oauth/login/:type', async (req, res) => { let type = req.params.type if (type === 'user') { - user_access_token = await getUserAccessToken(client_id, client_secret, req.query.code, redirect_uri + type) + [ user_access_token, user_refresh_token ] = await getUserAccessToken(client_id, client_secret, req.query.code, redirect_uri + type) writeEnv('TWITCH_USER_ACCESS_TOKEN', user_access_token) + writeEnv('TWITCH_USER_REFRESH_TOKEN', user_refresh_token) - user_name = await getUserName(client_id, user_access_token).login + user_name = await getUserInfo(client_id, user_access_token).login writeEnv('TWITCH_USER_USERNAME', user_name) clientChatBot.connect('wss://irc-ws.chat.twitch.tv:443') } else if (type === 'channel') { - channel_access_token = await getUserAccessToken(client_id, client_secret, req.query.code, redirect_uri + type) + [ channel_access_token, channel_refresh_token ] = await getUserAccessToken(client_id, client_secret, req.query.code, redirect_uri + type) writeEnv('TWITCH_CHANNEL_ACCESS_TOKEN', channel_access_token) + writeEnv('TWITCH_CHANNEL_REFRESH_TOKEN', channel_refresh_token) - channel_name = await getUserName(client_id, channel_access_token).login + channel_name = await getUserInfo(client_id, channel_access_token).login writeEnv('TWITCH_CHANNEL_USERNAME', channel_name) clientEventSub.connect('wss://eventsub.wss.twitch.tv/ws') @@ -102,9 +104,13 @@ const clientChatBot = new WebSocketClient() let connectionChatBot clientChatBot.on('connect', async connection => { - console.log('Twitch ChatBot WebSocket Connected !') connectionChatBot = connection + console.log('Twitch ChatBot WebSocket Connected !') + + // Check if the user access token is still valid + //[user_access_token, user_name] = await checkUser(user_access_token) + // Authenticate to Twitch IRC and join channel connection.sendUTF('CAP REQ :twitch.tv/commands twitch.tv/membership twitch.tv/tags') connection.sendUTF(`PASS oauth:${user_access_token}`) @@ -151,6 +157,9 @@ clientChatBot.connect('wss://irc-ws.chat.twitch.tv:443') const clientEventSub = new WebSocketClient().on('connect', async connection => { console.log('Twitch EventSub WebSocket Connected !') + // Check if the channel access token is still valid + //[channel_access_token, channel_name] = await checkChannel(channel_access_token) + connection.on('message', async message => { if (message.type === 'utf8') { try { @@ -160,7 +169,7 @@ const clientEventSub = new WebSocketClient().on('connect', async connection => { if (data.metadata.message_type === 'session_welcome') { // Get broadcaster user id and reward id - let broadcaster_user_id = await getUserID(client_id, channel_access_token) + let broadcaster_user_id = await getUserInfo(client_id, channel_access_token).id writeEnv('TWITCH_CHANNEL_BROADCASTER_ID', broadcaster_user_id) let reward_id = await getRewardID(client_id, channel_access_token, broadcaster_user_id, channel_reward_name) writeEnv('TWITCH_CHANNEL_REWARD_ID', reward_id) diff --git a/public/panel/arrow.svg b/public/panel/arrow.svg new file mode 100644 index 0000000..c06756a --- /dev/null +++ b/public/panel/arrow.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/panel/background.png b/public/panel/background.png new file mode 100644 index 0000000..a277494 Binary files /dev/null and b/public/panel/background.png differ diff --git a/public/panel/panel.css b/public/panel/panel.css index ce59694..bbe85d9 100644 --- a/public/panel/panel.css +++ b/public/panel/panel.css @@ -1,16 +1,113 @@ -p { - color: white; - text-align: center; +@import url('https://fonts.googleapis.com/css2?family=Annie+Use+Your+Telescope&display=swap'); +@import url('https://fonts.googleapis.com/css2?family=Advent+Pro&display=swap'); + +body { + /* background-color: #B9F1FD; */ + background: url(https://angels-dev.fr/twitch/panel/background.png); + background-repeat: no-repeat; + /* border: 2px solid black; */ + font-family: 'Advent Pro', sans-serif; + font-weight: 400; + color: black; } + +.div__title { + width: 205px; + height: 34px; + border-radius: 15px; + border-bottom: 1px solid #205E8B; + background-color: #5FB5F4; + justify-content: center; + margin: auto; + margin-bottom: 15px; +} + +.title { + color: black; + text-align: center; + font-family: 'Annie Use Your Telescope', cursive; + font-size: 24px; + font-weight: 600; +} + +.table { + margin: auto; + border-collapse : separate; + border-spacing: 4px; +} + +.table__body { + overflow-y: scroll; + scrollbar-width: thin; /* "auto" or "thin" */ + scrollbar-color: #24AEEA #7A7979; /* scroll thumb and track */ +} + +.table__rank { + background-color: #5FB6F4; + width: 30px; + height: 20px; + border-radius: 5px; + font-size: 12px; + font-weight: 600; +} + +.table__pseudos { + background-color: #5FB6F4; + width: 200px; + height: 20px; + border-radius: 5px; + text-align: left; + padding-left: 5px; + font-size: 12px; + font-weight: 600; +} + +.table__arrows { + background-color: #5FB6F4; + width: 54px; + height: 20px; + border-radius: 5px; + font-size: 12px; + font-weight: 600; +} + +.row__rank { + background-color: #99CFEE; + width: 30px; + height: 20px; + border-radius: 5px; + font-size: 12px; + font-weight: 800; +} + +.row__pseudos { + background-color: #99CFEE; + width: 200px; + height: 20px; + border-radius: 5px; + text-align: left; + padding-left: 5px; + font-size: 16px; + font-weight: 400; +} + +.row__arrows { + background-color: #99CFEE; + width: 54px; + height: 20px; + border-radius: 5px; + font-size: 14px; + font-weight: 400; +} + +.row__arrows::after { + content: url(https://angels-dev.fr/twitch/panel/arrow.svg); + margin-left: 7.5px; +} + th { - color: white; text-align: center; } td { - color: white; text-align: center; } -table .center { - margin-left: auto; - margin-right: auto; -} \ No newline at end of file diff --git a/public/panel/panel.html b/public/panel/panel.html index 9c6fe81..1a53276 100644 --- a/public/panel/panel.html +++ b/public/panel/panel.html @@ -1,16 +1,25 @@ - - - + + + + + + -Panel Laytho -

Classement des Daily Arrows

- - - - - - - - -
PseudoDaily Arrows
\ No newline at end of file + Laytho Panel + + +
+

Stand de tir à l'arc

+
+ + + + + + + + + +
RankPseudoCarquois
+ \ No newline at end of file diff --git a/public/panel/script.js b/public/panel/script.js index def9cbf..cdfe5b0 100644 --- a/public/panel/script.js +++ b/public/panel/script.js @@ -8,10 +8,20 @@ xhr.onload = () => { let tbodyRef = document.getElementById('table').getElementsByTagName('tbody')[0] - for (let entry of data.scoreboard) { + for (entry of data.scoreboard) { let row = tbodyRef.insertRow() + + row.insertCell().appendChild(document.createTextNode(data.scoreboard.indexOf(entry) + 1)) row.insertCell().appendChild(document.createTextNode(entry.user_name)) row.insertCell().appendChild(document.createTextNode(entry.count)) + + let i = 0 + for (cell of row.cells) { + i++ + if (i === 1) cell.className = "row__rank" + if (i === 2) cell.className = "row__pseudos" + if (i === 3) cell.className = "row__arrows" + } } } else console.error('Error:', xhr.statusText) } diff --git a/utils/checkChannel.js b/utils/checkChannel.js new file mode 100644 index 0000000..f0eaeae --- /dev/null +++ b/utils/checkChannel.js @@ -0,0 +1,25 @@ +require('dotenv').config() + +const validateToken = require('./validateToken') +const refreshToken = require('./refreshToken') +const writeEnv = require('./writeEnv') + +let client_id = process.env.TWITCH_APP_ID +let client_secret = process.env.TWITCH_APP_SECRET + +module.exports = async function (channel_access_token) { + let channel_refresh_token = '' + + // Check if channel_access_token is valid + if (!await validateToken(channel_access_token)) { + + // If not, refresh it + [channel_access_token, channel_refresh_token] = await refreshToken(client_id, client_secret, channel_access_token) + writeEnv('TWITCH_CHANNEL_ACCESS_TOKEN', channel_access_token) + writeEnv('TWITCH_CHANNEL_REFRESH_TOKEN', channel_refresh_token) + + let channel_name = await getUserInfo(client_id, channel_access_token).login + writeEnv('TWITCH_CHANNEL_USERNAME', channel_name) + } + return [channel_access_token, channel_name] +} \ No newline at end of file diff --git a/utils/checkUser.js b/utils/checkUser.js new file mode 100644 index 0000000..8eaeb34 --- /dev/null +++ b/utils/checkUser.js @@ -0,0 +1,25 @@ +require('dotenv').config() + +const validateToken = require('./validateToken') +const refreshToken = require('./refreshToken') +const writeEnv = require('./writeEnv') + +let client_id = process.env.TWITCH_APP_ID +let client_secret = process.env.TWITCH_APP_SECRET + +module.exports = async function (user_access_token) { + let user_refresh_token = '' + + // Check if user_access_token is valid + if (!await validateToken(user_access_token)) { + + // If not, refresh it + [user_access_token, user_refresh_token ] = await refreshToken(client_id, client_secret, user_access_token) + writeEnv('TWITCH_USER_ACCESS_TOKEN', user_access_token) + writeEnv('TWITCH_USER_REFRESH_TOKEN', user_refresh_token) + + let user_name = await getUserInfo(client_id, user_access_token).login + writeEnv('TWITCH_USER_USERNAME', user_name) + } + return [user_access_token, user_name] +} \ No newline at end of file diff --git a/utils/getRewardData.js b/utils/getRewardData.js index 9d70f7e..2eb9dd3 100644 --- a/utils/getRewardData.js +++ b/utils/getRewardData.js @@ -1,4 +1,5 @@ const mysql = require('mysql2/promise') +require('dotenv').config() module.exports = async function () { // Create a connection to the MySQL database diff --git a/utils/getRewardID.js b/utils/getRewardID.js index 10bb1fa..bc14613 100644 --- a/utils/getRewardID.js +++ b/utils/getRewardID.js @@ -7,6 +7,7 @@ module.exports = async function (client_id, access_token, broadcaster_id, reward 'Client-Id': client_id } }).then(response => { + //console.log(response.data) let reward = response.data.data.find(reward => reward.title === reward_name) return reward.id }).catch(error => { console.log(error.response.data) }) diff --git a/utils/getUserAccessToken.js b/utils/getUserAccessToken.js index 203247b..cc19003 100644 --- a/utils/getUserAccessToken.js +++ b/utils/getUserAccessToken.js @@ -8,7 +8,7 @@ module.exports = async function (client_id, client_secret, code, redirect_uri) { redirect_uri, grant_type: 'authorization_code' }).then(response => { - console.log(response.data) - if (response.data.token_type === 'bearer') return response.data.access_token + //console.log(response.data) + if (response.data.token_type === 'bearer') return [response.data.access_token, response.data.refresh_token] }).catch(error => { console.log(error.response.data) }) } \ No newline at end of file diff --git a/utils/getUserID.js b/utils/getUserID.js deleted file mode 100644 index 2d37d81..0000000 --- a/utils/getUserID.js +++ /dev/null @@ -1,13 +0,0 @@ -const axios = require('axios') - -module.exports = async function (client_id, access_token) { - return await axios.get(`https://api.twitch.tv/helix/users`, { - headers: { - 'Authorization': `Bearer ${access_token}`, - 'Client-Id': client_id - } - }).then(response => { - //console.log(response.data) - return response.data.data[0].id - }).catch(error => { console.log(error.response.data) }) -} \ No newline at end of file diff --git a/utils/getUserName.js b/utils/getUserName.js deleted file mode 100644 index 83e5e2f..0000000 --- a/utils/getUserName.js +++ /dev/null @@ -1,13 +0,0 @@ -const axios = require('axios') - -module.exports = async function (client_id, access_token) { - return await axios.get(`https://api.twitch.tv/helix/users`, { - headers: { - 'Authorization': `Bearer ${access_token}`, - 'Client-Id': client_id - } - }).then(response => { - //console.log(response.data) - return response.data.data[0].login - }).catch(error => { console.log(error.response.data) }) -} \ No newline at end of file diff --git a/utils/oauthGen.js b/utils/oauthGen.js index b5dba34..515cfae 100644 --- a/utils/oauthGen.js +++ b/utils/oauthGen.js @@ -1,7 +1,7 @@ module.exports = async function (client_id, redirect_uri, scope) { console.log(scope) let queries = { - response_type: 'code', + response_type: 'code', client_id, redirect_uri, scope: scope.join('+') diff --git a/utils/refreshToken.js b/utils/refreshToken.js new file mode 100644 index 0000000..3305da4 --- /dev/null +++ b/utils/refreshToken.js @@ -0,0 +1,13 @@ +const axios = require('axios') + +module.exports = async function (client_id, client_secret, refresh_token) { + return await axios.post('https://id.twitch.tv/oauth2/token', { + client_id, + client_secret, + refresh_token, + grant_type: 'refresh_token' + }).then(response => { + //console.log(response.data) + if (response.data.token_type === 'bearer') return [response.data.access_token, response.data.refresh_token] + }).catch(error => { console.log(error.response.data) }) +} \ No newline at end of file diff --git a/utils/subscribeToEvents.js b/utils/subscribeToEvents.js index 96c8c8b..1bac78e 100644 --- a/utils/subscribeToEvents.js +++ b/utils/subscribeToEvents.js @@ -16,6 +16,7 @@ module.exports = async function (client_id, access_token, session_id, type, vers 'Content-Type': 'application/json' } }).then(response => { + //console.log(response.data) return response.data.data[0].status }).catch(error => { return error.response.data }) } \ No newline at end of file diff --git a/utils/validateToken.js b/utils/validateToken.js new file mode 100644 index 0000000..cd9ae37 --- /dev/null +++ b/utils/validateToken.js @@ -0,0 +1,13 @@ +const axios = require('axios') + +module.exports = async function (access_token) { + return await axios.get('https://id.twitch.tv/oauth2/validate', { + headers: { + 'Authorization': `OAuth ${access_token}`, + } + }).then(response => { + //console.log(response.data) + if (response.data.status === '401') return false + else return true + }).catch(error => { console.log(error.response.data) }) +} \ No newline at end of file diff --git a/utils/writeEnv.js b/utils/writeEnv.js index f56bdb0..bf256ab 100644 --- a/utils/writeEnv.js +++ b/utils/writeEnv.js @@ -1,7 +1,7 @@ const fs = require('fs') module.exports = function (variable, value) { - let parsedFile = fs.readFileSync('./.env', 'utf8') - parsedFile = parsedFile.replace(new RegExp(`${variable}=.*`, 'g'), `${variable}=${value}`) - fs.writeFileSync('./.env', parsedFile) + let parsedFile = fs.readFileSync('./.env', 'utf8') + parsedFile = parsedFile.replace(new RegExp(`${variable}=.*`, 'g'), `${variable}=${value}`) + fs.writeFileSync('./.env', parsedFile) } \ No newline at end of file