Panel terminé + Refresh token auto
This commit is contained in:
25
app.js
25
app.js
@@ -5,12 +5,12 @@ require('dotenv').config()
|
|||||||
|
|
||||||
|
|
||||||
// UTILS
|
// UTILS
|
||||||
|
const checkUser = require('./utils/checkUser')
|
||||||
|
const checkChannel = require('./utils/checkChannel')
|
||||||
const getRewardData = require('./utils/getRewardData')
|
const getRewardData = require('./utils/getRewardData')
|
||||||
const getRewardID = require('./utils/getRewardID')
|
const getRewardID = require('./utils/getRewardID')
|
||||||
const getUserAccessToken = require('./utils/getUserAccessToken')
|
const getUserAccessToken = require('./utils/getUserAccessToken')
|
||||||
const getUserID = require('./utils/getUserID')
|
|
||||||
const getUserInfo = require('./utils/getUserInfo')
|
const getUserInfo = require('./utils/getUserInfo')
|
||||||
const getUserName = require('./utils/getUserName')
|
|
||||||
const oauthGen = require('./utils/oauthGen')
|
const oauthGen = require('./utils/oauthGen')
|
||||||
const parseMessage = require('./utils/parseMessage')
|
const parseMessage = require('./utils/parseMessage')
|
||||||
const rewardRedemption = require('./utils/rewardRedemption')
|
const rewardRedemption = require('./utils/rewardRedemption')
|
||||||
@@ -54,19 +54,21 @@ app.get('/twitch/oauth/login/:type', async (req, res) => {
|
|||||||
let type = req.params.type
|
let type = req.params.type
|
||||||
|
|
||||||
if (type === 'user') {
|
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_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)
|
writeEnv('TWITCH_USER_USERNAME', user_name)
|
||||||
|
|
||||||
clientChatBot.connect('wss://irc-ws.chat.twitch.tv:443')
|
clientChatBot.connect('wss://irc-ws.chat.twitch.tv:443')
|
||||||
}
|
}
|
||||||
else if (type === 'channel') {
|
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_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)
|
writeEnv('TWITCH_CHANNEL_USERNAME', channel_name)
|
||||||
|
|
||||||
clientEventSub.connect('wss://eventsub.wss.twitch.tv/ws')
|
clientEventSub.connect('wss://eventsub.wss.twitch.tv/ws')
|
||||||
@@ -102,9 +104,13 @@ const clientChatBot = new WebSocketClient()
|
|||||||
let connectionChatBot
|
let connectionChatBot
|
||||||
|
|
||||||
clientChatBot.on('connect', async connection => {
|
clientChatBot.on('connect', async connection => {
|
||||||
console.log('Twitch ChatBot WebSocket Connected !')
|
|
||||||
connectionChatBot = connection
|
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
|
// Authenticate to Twitch IRC and join channel
|
||||||
connection.sendUTF('CAP REQ :twitch.tv/commands twitch.tv/membership twitch.tv/tags')
|
connection.sendUTF('CAP REQ :twitch.tv/commands twitch.tv/membership twitch.tv/tags')
|
||||||
connection.sendUTF(`PASS oauth:${user_access_token}`)
|
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 => {
|
const clientEventSub = new WebSocketClient().on('connect', async connection => {
|
||||||
console.log('Twitch EventSub WebSocket Connected !')
|
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 => {
|
connection.on('message', async message => {
|
||||||
if (message.type === 'utf8') {
|
if (message.type === 'utf8') {
|
||||||
try {
|
try {
|
||||||
@@ -160,7 +169,7 @@ const clientEventSub = new WebSocketClient().on('connect', async connection => {
|
|||||||
if (data.metadata.message_type === 'session_welcome') {
|
if (data.metadata.message_type === 'session_welcome') {
|
||||||
|
|
||||||
// Get broadcaster user id and reward id
|
// 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)
|
writeEnv('TWITCH_CHANNEL_BROADCASTER_ID', broadcaster_user_id)
|
||||||
let reward_id = await getRewardID(client_id, channel_access_token, broadcaster_user_id, channel_reward_name)
|
let reward_id = await getRewardID(client_id, channel_access_token, broadcaster_user_id, channel_reward_name)
|
||||||
writeEnv('TWITCH_CHANNEL_REWARD_ID', reward_id)
|
writeEnv('TWITCH_CHANNEL_REWARD_ID', reward_id)
|
||||||
|
|||||||
9
public/panel/arrow.svg
Normal file
9
public/panel/arrow.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 39 KiB |
BIN
public/panel/background.png
Normal file
BIN
public/panel/background.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 44 KiB |
@@ -1,16 +1,113 @@
|
|||||||
p {
|
@import url('https://fonts.googleapis.com/css2?family=Annie+Use+Your+Telescope&display=swap');
|
||||||
color: white;
|
@import url('https://fonts.googleapis.com/css2?family=Advent+Pro&display=swap');
|
||||||
text-align: center;
|
|
||||||
|
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 {
|
th {
|
||||||
color: white;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
td {
|
td {
|
||||||
color: white;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
table .center {
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
}
|
|
||||||
@@ -1,16 +1,25 @@
|
|||||||
<!DOCTYPE HTML>
|
<!DOCTYPE HTML>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
<script src="https://extension-files.twitch.tv/helper/v1/twitch-ext.min.js"></script>
|
<script src="https://extension-files.twitch.tv/helper/v1/twitch-ext.min.js"></script>
|
||||||
<script src="https://angels-dev.fr/twitch/panel/script.js"></script>
|
<script src="https://angels-dev.fr/twitch/panel/script.js"></script>
|
||||||
<link rel="stylesheet" href="https://angels-dev.fr/twitch/panel/panel.css">
|
<link rel="stylesheet" href="https://angels-dev.fr/twitch/panel/panel.css">
|
||||||
|
<link rel="image/svg" href="https://angels-dev.fr/twitch/panel/arrow.svg">
|
||||||
|
|
||||||
<title>Panel Laytho</title>
|
<title>Laytho Panel</title>
|
||||||
<p>Classement des Daily Arrows</p>
|
</head>
|
||||||
<table id="table" class="center">
|
<body>
|
||||||
|
<div class="div__title">
|
||||||
|
<h2 class="title">Stand de tir à l'arc</h2>
|
||||||
|
</div>
|
||||||
|
<table id="table" class="table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr class="table__names">
|
||||||
<th scope="col">Pseudo</th>
|
<th scope="col" class="table__rank">Rank</th>
|
||||||
<th scope="col">Daily Arrows</th>
|
<th scope="col" class="table__pseudos">Pseudo</th>
|
||||||
|
<th scope="col" class="table__arrows">Carquois</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody></tbody>
|
<tbody class="table__body"></tbody>
|
||||||
</table>
|
</table>
|
||||||
|
</html>
|
||||||
@@ -8,10 +8,20 @@ xhr.onload = () => {
|
|||||||
|
|
||||||
let tbodyRef = document.getElementById('table').getElementsByTagName('tbody')[0]
|
let tbodyRef = document.getElementById('table').getElementsByTagName('tbody')[0]
|
||||||
|
|
||||||
for (let entry of data.scoreboard) {
|
for (entry of data.scoreboard) {
|
||||||
let row = tbodyRef.insertRow()
|
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.user_name))
|
||||||
row.insertCell().appendChild(document.createTextNode(entry.count))
|
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)
|
} else console.error('Error:', xhr.statusText)
|
||||||
}
|
}
|
||||||
|
|||||||
25
utils/checkChannel.js
Normal file
25
utils/checkChannel.js
Normal file
@@ -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]
|
||||||
|
}
|
||||||
25
utils/checkUser.js
Normal file
25
utils/checkUser.js
Normal file
@@ -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]
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
const mysql = require('mysql2/promise')
|
const mysql = require('mysql2/promise')
|
||||||
|
require('dotenv').config()
|
||||||
|
|
||||||
module.exports = async function () {
|
module.exports = async function () {
|
||||||
// Create a connection to the MySQL database
|
// Create a connection to the MySQL database
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ module.exports = async function (client_id, access_token, broadcaster_id, reward
|
|||||||
'Client-Id': client_id
|
'Client-Id': client_id
|
||||||
}
|
}
|
||||||
}).then(response => {
|
}).then(response => {
|
||||||
|
//console.log(response.data)
|
||||||
let reward = response.data.data.find(reward => reward.title === reward_name)
|
let reward = response.data.data.find(reward => reward.title === reward_name)
|
||||||
return reward.id
|
return reward.id
|
||||||
}).catch(error => { console.log(error.response.data) })
|
}).catch(error => { console.log(error.response.data) })
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ module.exports = async function (client_id, client_secret, code, redirect_uri) {
|
|||||||
redirect_uri,
|
redirect_uri,
|
||||||
grant_type: 'authorization_code'
|
grant_type: 'authorization_code'
|
||||||
}).then(response => {
|
}).then(response => {
|
||||||
console.log(response.data)
|
//console.log(response.data)
|
||||||
if (response.data.token_type === 'bearer') return response.data.access_token
|
if (response.data.token_type === 'bearer') return [response.data.access_token, response.data.refresh_token]
|
||||||
}).catch(error => { console.log(error.response.data) })
|
}).catch(error => { console.log(error.response.data) })
|
||||||
}
|
}
|
||||||
@@ -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) })
|
|
||||||
}
|
|
||||||
@@ -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) })
|
|
||||||
}
|
|
||||||
13
utils/refreshToken.js
Normal file
13
utils/refreshToken.js
Normal file
@@ -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) })
|
||||||
|
}
|
||||||
@@ -16,6 +16,7 @@ module.exports = async function (client_id, access_token, session_id, type, vers
|
|||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
}
|
}
|
||||||
}).then(response => {
|
}).then(response => {
|
||||||
|
//console.log(response.data)
|
||||||
return response.data.data[0].status
|
return response.data.data[0].status
|
||||||
}).catch(error => { return error.response.data })
|
}).catch(error => { return error.response.data })
|
||||||
}
|
}
|
||||||
13
utils/validateToken.js
Normal file
13
utils/validateToken.js
Normal file
@@ -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) })
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user