186 lines
7.4 KiB
JavaScript
186 lines
7.4 KiB
JavaScript
// PACKAGES
|
|
const WebSocketClient = require('websocket').client
|
|
const express = require('express')
|
|
require('dotenv').config()
|
|
|
|
|
|
// UTILS
|
|
const getReward = require('./utils/getReward')
|
|
const getUserAccessToken = require('./utils/getUserAccessToken')
|
|
const getUserID = require('./utils/getUserID')
|
|
const getUserName = require('./utils/getUserName')
|
|
const oauthGen = require('./utils/oauthGen')
|
|
const parseMessage = require('./utils/parseMessage')
|
|
const rewardRedemption = require('./utils/rewardRedemption')
|
|
const subscribeToEvents = require('./utils/subscribeToEvents')
|
|
const writeEnv = require('./utils/writeEnv')
|
|
|
|
|
|
// VARIABLES
|
|
let client_id = process.env.TWITCH_APP_ID
|
|
let client_secret = process.env.TWITCH_APP_SECRET
|
|
|
|
let user_access_token = process.env.TWITCH_USER_ACCESS_TOKEN
|
|
let user_name = process.env.TWITCH_USER_USERNAME
|
|
|
|
let channel_access_token = process.env.TWITCH_CHANNEL_ACCESS_TOKEN
|
|
let channel_name = process.env.TWITCH_CHANNEL_USERNAME
|
|
|
|
const user_scope = ['chat:read', 'chat:edit', 'channel:moderate']
|
|
const channel_scope = ['channel:manage:redemptions']
|
|
|
|
const redirect_uri = 'https://angels-dev.fr/twitch/oauth/login/'
|
|
const chatBeginMsg = `PRIVMSG #${channel_name}`
|
|
|
|
|
|
// EXPRESS
|
|
const port = 3000
|
|
const app = express()
|
|
app.use(express.json())
|
|
app.use(express.static('panel'))
|
|
|
|
app.get('/twitch/oauth/:type', async (req, res) => {
|
|
let type = req.params.type
|
|
let url = await oauthGen(client_id, redirect_uri + type, type === 'user' ? user_scope : type === 'channel' ? channel_scope : [])
|
|
|
|
return res.redirect(url)
|
|
})
|
|
|
|
app.get('/twitch/oauth/login/:type', async (req, res) => {
|
|
console.log(req.query)
|
|
let type = req.params.type
|
|
|
|
if (type === 'user') {
|
|
user_access_token = await getUserAccessToken(req.query.code, client_id, client_secret, redirect_uri + type)
|
|
writeEnv('TWITCH_USER_ACCESS_TOKEN', user_access_token)
|
|
|
|
user_name = await getUserName(client_id, user_access_token)
|
|
writeEnv('TWITCH_USER_USERNAME', user_name)
|
|
|
|
clientChatBot.connect('wss://irc-ws.chat.twitch.tv:443')
|
|
}
|
|
else if (type === 'channel') {
|
|
channel_access_token = await getUserAccessToken(req.query.code, client_id, client_secret, redirect_uri + type)
|
|
writeEnv('TWITCH_CHANNEL_ACCESS_TOKEN', channel_access_token)
|
|
|
|
channel_name = await getUserName(client_id, channel_access_token)
|
|
writeEnv('TWITCH_CHANNEL_USERNAME', channel_name)
|
|
|
|
clientEventSub.connect('wss://eventsub.wss.twitch.tv/ws')
|
|
}
|
|
return res.send('Login successful !')
|
|
})
|
|
|
|
app.listen(port, () => { console.log(`Listening at ${redirect_uri}`) })
|
|
|
|
|
|
// CHATBOT
|
|
const clientChatBot = new WebSocketClient()
|
|
let connectionChatBot
|
|
|
|
clientChatBot.on('connect', async connection => {
|
|
console.log('Twitch ChatBot WebSocket Connected !')
|
|
connectionChatBot = connection
|
|
|
|
// 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}`)
|
|
connection.sendUTF(`NICK ${user_name}`)
|
|
connection.sendUTF(`JOIN #${channel_name}`)
|
|
connection.sendUTF(`PRIVMSG #${channel_name} :Salut tout le monde !`)
|
|
|
|
connection.on('message', async message => {
|
|
if (message.type === 'utf8') {
|
|
try {
|
|
let data = parseMessage(message.utf8Data)
|
|
|
|
// Handle incoming messages
|
|
if (data.command.command === 'PRIVMSG') {
|
|
let message = data.parameters.split('\r\n')[0]
|
|
console.log(`${data.source.nick}: ${message}`)
|
|
|
|
if (message.includes('@Bot_Laytho')) {
|
|
connection.sendUTF(`@reply-parent-msg-id=${data.tags.id} ${chatBeginMsg} :Kestuveu @${data.tags['display-name']} ?`)
|
|
connection.sendUTF(`${chatBeginMsg} :/timeout ${data.tags['display-name']} 60 T'as pas à me parler comme ça !`)
|
|
}
|
|
else if (message === '!ping') {
|
|
connection.sendUTF(`@reply-parent-msg-id=${data.tags.id} ${chatBeginMsg} :Pong !`)
|
|
}
|
|
} else if (data.command.command === 'NOTICE') {
|
|
if (data.parameters.includes('Login authentication failed')) {
|
|
console.log('Erreur de connexion ChatBot, veuillez vous reconnecter !\nhttps://angels-dev.fr/twitch/oauth/user')
|
|
}
|
|
}
|
|
} catch (error) { } // catch (error) { console.error(error) }
|
|
} })
|
|
.on('error', error => { console.error(error) })
|
|
.on('close', () => { console.log('Twitch ChatBot Connection Closed !') })
|
|
}).on('connectFailed', error => { console.error(error) })
|
|
|
|
clientChatBot.connect('wss://irc-ws.chat.twitch.tv:443')
|
|
|
|
|
|
// EVENTSUB
|
|
const clientEventSub = new WebSocketClient().on('connect', async connection => {
|
|
console.log('Twitch EventSub WebSocket Connected !')
|
|
|
|
connection.on('message', async message => {
|
|
if (message.type === 'utf8') {
|
|
try {
|
|
let data = JSON.parse(message.utf8Data)
|
|
|
|
// Check when Twitch asks to login
|
|
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, channel_name)
|
|
let reward_id = await getReward(client_id, channel_access_token, broadcaster_user_id)
|
|
let topics = {
|
|
'channel.channel_points_custom_reward_redemption.add': { version: '1', condition: { broadcaster_user_id, reward_id } },
|
|
'stream.online': { version: '1', condition: { broadcaster_user_id } }
|
|
}
|
|
|
|
// Subscribe to all events required
|
|
for (let type in topics) {
|
|
console.log(`Creating ${type}...`)
|
|
let { version, condition } = topics[type]
|
|
|
|
let status = await subscribeToEvents(channel_access_token, data.payload.session.id, client_id, type, version, condition)
|
|
if (!status) return console.error(`Failed to create ${type}`)
|
|
|
|
else if (status.error) {
|
|
console.error(status)
|
|
console.log('Erreur de connexion EventSub, veuillez vous reconnecter !\nhttps://angels-dev.fr/twitch/oauth/channel')
|
|
return connection.sendUTF(`${chatBeginMsg} :@${channel_name} Erreur de connexion EventSub, veuillez vous reconnecter !\nhttps://angels-dev.fr/twitch/oauth/channel`)
|
|
} else console.log(`Successfully created ${type}`)
|
|
}
|
|
}
|
|
|
|
// Handle notification messages for reward redemption
|
|
else if (data.metadata.message_type === 'notification') {
|
|
let{ subscription, event } = data.payload
|
|
|
|
//if (subscription.type === 'channel.channel_points_custom_reward_redemption.add' && event.reward.id === reward_id) {
|
|
if (subscription.type === 'channel.channel_points_custom_reward_redemption.add') {
|
|
let { user_id, user_name } = event
|
|
console.log(`User ${user_name} claimed reward ${event.reward.title} !`)
|
|
rewardRedemption(user_id, user_name)
|
|
}
|
|
else if (subscription.type === 'stream.online' && event.broadcaster_user_login === channel_name) {
|
|
console.log(`Stream from ${event.broadcaster_user_name} is now online, connecting to chat...`)
|
|
clientChatBot.connect('wss://irc-ws.chat.twitch.tv:443')
|
|
}
|
|
}
|
|
|
|
// Don't log ping/pong messages
|
|
else if (data.metadata.message_type === 'session_keepalive') return
|
|
|
|
// Log unknown messages
|
|
else console.log(data)
|
|
} catch (error) { console.error(error) }
|
|
} })
|
|
.on('error', error => { console.error(error) })
|
|
.on('close', () => { console.log('Twitch EventSub Connection Closed !') })
|
|
}).on('connectFailed', error => { console.error(error) })
|
|
|
|
clientEventSub.connect('wss://eventsub.wss.twitch.tv/ws') |