-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Notification: Twitch (automated)
Hi,
I have struggled with the Twitch Notifications myself; going through the OAUTH2 Twitch Developer documentation, I was able to figure it out! In an effort to make this available in an easy automated form, I have written a script that does the leg work for you.
The first steps are identical to Jef's approach. Creating an Application from https://dev.twitch.tv/console/apps:
Must use unique name. Use https://www.uuidgenerator.net/ to get a UUID and append it to streetmerchant-.
From there, you should get the TWITCH_CLIENT_ID, TWITCH_CLIENT_SECRET, and TWITCH_CHANNEL (be careful, TWITCH_CLIENT_SECRET can only be seen one time when generated).
Add the three lines to your 'dotenv' file and save your file.
TWITCH_CLIENT_ID= TWITCH_CLIENT_SECRET= TWITCH_CHANNEL=<your twitch channel/username>
My script requires puppeteer, dotenv, readline, yesno, querystring, and node-libcurl (apologies, but this is my first NODE.js script ever), so please make sure you install the necessary dependencies
npm i puppeteer dotenv readline yesno querystring node-libcurl
Copy and paste the following code into Twitch_Auto.js (or any name of choosing):
const querystring = require('querystring');
const { curly } = require('node-libcurl');
global.__basedir = __dirname;
global.code='';
global.foundatoken=false;
global.foundrtoken=false;
const urlp = require('url');
const path = require('path');
const tls = require('tls');
const http = require('http');
const fs = require('fs');
const puppeteer = require('puppeteer');
const yesno = require('yesno');
const url = 'http://curl.haxx.se/ca/cacert.pem';
const filePath = path.join(__basedir, 'cacert.pem');
const readline = require('readline');
function sleep(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
async function processLineByLine() {
const fileStream = fs.createReadStream(path.join(__basedir, '.env'));
const rl = readline.createInterface({
input: fileStream,
crlfDelay: Infinity
});
for await (const line of rl) {
if(line.includes('TWITCH_ACCESS_TOKEN')){
fs.readFile(path.join(__basedir, '.env'), 'utf8', function(err, data) {
let searchString1 = 'TWITCH_ACCESS_TOKEN';
let re1 = new RegExp('^.*' + searchString1 + '.*$', 'gm');
let formatted1 = data.replace(re1, 'TWITCH_ACCESS_TOKEN=' + access_token);
`fs.writeFile(path.join(__basedir, '.env'), formatted1, 'utf8', function(err) {`
`if (err) return console.log(err);`
`});`
`});`
`foundatoken=true; `
`await sleep(1000);`
`}`
`if(line.includes('TWITCH_REFRESH_TOKEN')){`
`fs.readFile(path.join(__basedir, '.env'), 'utf8', function(err, data) {`
`let searchString2 = 'TWITCH_REFRESH_TOKEN';`
`let re2 = new RegExp('^.*' + searchString2 + '.*$', 'gm');`
`let formatted2 = data.replace(re2, 'TWITCH_REFRESH_TOKEN=' + refresh_token);`
`fs.writeFile(path.join(__basedir, '.env'), formatted2, 'utf8', function(err) {`
`if (err) return console.log(err);`
`});`
`});`
`foundatoken=true; `
`await sleep(1000);`
`}`
}
if (foundatoken==false){
`fs.appendFile(path.join(__basedir, '.env'), "\n" + 'TWITCH_ACCESS_TOKEN=' + access_token, function (err) {`
`if (err) {`
`// append failed`
`} else {`
`// done`
`}`
`})`
`}`
if (foundrtoken==false){
fs.appendFile(path.join(__basedir, '.env'), "\n" + 'TWITCH_REFRESH_TOKEN=' + refresh_token, function (err) {
if (err) {
// append failed
} else {
// done
}
})
}
}
fs.copyFile('dotenv','.env', (err) => { if (err) throw err;});
require('dotenv').config();
const atoken = process.env.TWITCH_ACCESS_TOKEN;
const rtoken = process.env.TWITCH_REFRESH_TOKEN;
const cid = process.env.TWITCH_CLIENT_ID;
const csec = process.env.TWITCH_CLIENT_SECRET;
const chan = process.env.TWITCH_CHANNEL;
console.log(__dirname + ' d ' + __basedir);
if (( typeof cid !== 'undefined' && cid) || ( typeof csec !== 'undefined' && csec) || ( typeof chan !== 'undefined' && chan) )
{
console.log('--Parameters from dotenv--');
console.log('TWITCH_CLIENT_ID: ' + cid);
console.log('TWITCH_CLIENT_SECRET: ' + csec);
console.log('TWITCH_CHANNEL: ' + chan);
console.log('--Getting Access Token and Refresh Token--');
const file = fs.createWriteStream(filePath);
const request = http.get(url, (response) => {response.pipe(file);});
(async () => {
const browser = await puppeteer.launch({ headless: false , userDataDir: "./user_data"});
const page = await browser.newPage();
await page.setDefaultNavigationTimeout(0);
//Check for previous session
try {
await page.goto('https://id.twitch.tv/oauth2/authorize?response_type=code&client_id='+cid+'&redirect_uri=http://localhost&scope=chat:edit%20chat:read', {waitUntil: 'load', timeout: 0});
page.on('response', response => {
const status = response.status()
if ((status >= 300) && (status <= 399)) {
if (response.headers()['location'].includes('localhost') == true){
let parsedUrl = urlp.parse(response.headers()['location']);
const object = querystring.parse(parsedUrl.query);
code = object['code'];
console.log('CODE: ' + code);
`}`
}
})
await page.waitForNavigation();
await browser.close();
} catch (e)
{
console.log('Session previously saved in ./user_data... Using previous session. No login needed!');
const pages = await browser.pages();
var x = await browser.targets();
for(let i=0;i<x.length;i++)
{
var y = x[i].url();
if((x[i].type()==='page') && (y.includes('localhost')==true))
{
let parsedUrl = urlp.parse(x[i].url());
const object = querystring.parse(parsedUrl.query);
code = object['code'];
console.log('CODE: ' + code);
await browser.close();
}
}
}
`await sleep(2500);`
`const certFilePath = path.join(__basedir, 'cacert.pem')`
`const tlsData = tls.rootCertificates.join('\n')`
`fs.access(certFilePath, (err) => {`
`if (err) {`
`console.log("The file does not exist, downloading...");`
`fs.writeFileSync(certFilePath, tlsData);`
`} else {`
`console.log("The file exists, deleting old cacert.pem");`
`fs.unlink(certFilePath, (err) => {`
`if (err) {`
`throw err;`
`}`
`console.log("File is deleted.");`
});
}
});
`await sleep(2500);`
`fs.writeFileSync(certFilePath, tlsData);`
`const { statusCode, data, headers } = await curly.post('https://id.twitch.tv/oauth2/token', {postFields: querystring.stringify({ client_id: cid,client_secret: csec, code: global.code, grant_type: 'authorization_code', redirect_uri: 'http://localhost'}),`
`caInfo: certFilePath,`
`})`
`global.access_token = data.access_token;`
global.refresh_token = data.refresh_token;
processLineByLine();
await sleep(5000);
console.log('--Parameters from dotenv--');
console.log('TWITCH_CLIENT_ID: ' + cid);
console.log('TWITCH_CLIENT_SECRET: ' + csec);
console.log('TWITCH_CHANNEL: ' + chan);
console.log('--New Parameters obtained in script--');
console.log('TWITCH_ACCESS_TOKEN: ' + access_token);
console.log('TWITCH_REFRESH_TOKEN: ' + refresh_token);
const ok = await yesno({
question: 'Do you want to update dotenv?'
});
if (ok==true){
console.log('Creating dotenv.bak...');
fs.copyFile('dotenv','dotenv.bak', (err) => { if (err) throw err;});
console.log('Copying .env to dotenv');
fs.copyFile('.env','dotenv', (err) => { if (err) throw err;});
console.log('Done! exiting ...');
console.log('Test Twitch Notifications with: npm run test:notification');
process.exit();
}
})();
}
else
{
console.log('Twitch API not configured');
console.log('Please register app at http://dev.twitch.tv');
console.log('And populate TWITCH_CLIENT_ID, TWITCH_CLIENT_SECRET, and TWITCH_CHANNEL in the dotenv file');
}
↩️ Go back to Home