diff --git a/README.md b/README.md index df4dfb9..332872d 100755 --- a/README.md +++ b/README.md @@ -1,16 +1,23 @@ # Twitch PubSub Javascript Sample + Here you find a simple JavaScript PubSub implementation with client-side authentication using the Implicit Grant Flow. The project structure section will give you a quick overview of this code. +## Update (2022) + +Modified for more modern coding practices. Replaced Twitch Kraken with Helix API. Removed jQuery, using vanilla JS. Removed Bootstrap CSS and added some basic styles directly. + ## Structure + `index.html` Provides the view of all incoming and outgoing pubsub messages. `main.js` -Provides the actual functionality of the PubSub client. If you are not authenticated, you will see the Connect to Twitch button and will be prompted through the authentication flow. After authenticating, you will be able to send and receive raw PubSub messages. +Provides the actual functionality of the PubSub client. If you are not authenticated, you will see the Connect to Twitch button and will be prompted through the authentication flow. After authenticating, you will be able to send and receive raw PubSub messages. ## License Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. +Modified 2022 Arno Richter (https://arnorichter.de) Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance with the License. A copy of the License is located at diff --git a/index.html b/index.html index aa34856..59dc8c3 100755 --- a/index.html +++ b/index.html @@ -1,42 +1,65 @@ - - - - - - Twitch PubSub Example - - -

Twitch PubSub Example

-
-
- - -
-
- - - - - \ No newline at end of file + + + + Twitch PubSub Example + + + + +

Twitch PubSub Example

+ +
+ + +
+ + + diff --git a/main.js b/main.js index 08d5ab9..70a1b6c 100755 --- a/main.js +++ b/main.js @@ -1,122 +1,134 @@ -var clientId = ''; -var redirectURI = ''; -var scope = 'user_read+chat_login'; -var ws; - -function parseFragment(hash) { - var hashMatch = function(expr) { - var match = hash.match(expr); - return match ? match[1] : null; - }; - var state = hashMatch(/state=(\w+)/); - if (sessionStorage.twitchOAuthState == state) - sessionStorage.twitchOAuthToken = hashMatch(/access_token=(\w+)/); - return -}; +const clientId = 'ja7y2ey1ddv1ayx5v9urjyv4xiu9el'; // YOUR CLIENT ID HERE +const redirectURI = 'https://oelna.github.io/pubsub-javascript-sample'; // YOUR REDIRECT URL HERE +const scope = 'user_read+channel:read:subscriptions+channel:read:redemptions+bits:read'; +let ws; +const wsOutput = document.querySelector('.ws-output'); + +function parseFragment (hash) { + const hashMatch = function (expr) { + const match = hash.match(expr); + return match ? match[1] : null; + }; + const state = hashMatch(/state=(\w+)/); + if (sessionStorage.twitchOAuthState == state) + sessionStorage.twitchOAuthToken = hashMatch(/access_token=(\w+)/); + return; +} -function authUrl() { - sessionStorage.twitchOAuthState = nonce(15); - var url = 'https://api.twitch.tv/kraken/oauth2/authorize' + - '?response_type=token' + - '&client_id=' + clientId + - '&redirect_uri=' + redirectURI + - '&state=' + sessionStorage.twitchOAuthState + - '&scope=' + scope; - return url +function authUrl () { + sessionStorage.twitchOAuthState = nonce(15); + const url = 'https://id.twitch.tv/oauth2/authorize' + + '?response_type=token' + + '&client_id=' + clientId + + '&redirect_uri=' + redirectURI + + '&state=' + sessionStorage.twitchOAuthState + + '&scope=' + scope; + return url; } // Source: https://www.thepolyglotdeveloper.com/2015/03/create-a-random-nonce-string-using-javascript/ -function nonce(length) { - var text = ""; - var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - for (var i = 0; i < length; i++) { - text += possible.charAt(Math.floor(Math.random() * possible.length)); - } - return text; +function nonce (length) { + let text = ""; + const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + for (let i = 0; i < length; i++) { + text += possible.charAt(Math.floor(Math.random() * possible.length)); + } + return text; } -function heartbeat() { - message = { - type: 'PING' - }; - $('.ws-output').append('SENT: ' + JSON.stringify(message) + '\n'); - ws.send(JSON.stringify(message)); +function heartbeat () { + message = { + 'type': 'PING' + } + wsOutput.append('SENT: ' + JSON.stringify(message) + '\n'); + ws.send(JSON.stringify(message)); } -function listen(topic) { - message = { - type: 'LISTEN', - nonce: nonce(15), - data: { - topics: [topic], - auth_token: sessionStorage.twitchOAuthToken - } - }; - $('.ws-output').append('SENT: ' + JSON.stringify(message) + '\n'); - ws.send(JSON.stringify(message)); +function listen (topic) { + message = { + 'type': 'LISTEN', + 'nonce': nonce(15), + 'data': { + 'topics': [topic], + 'auth_token': sessionStorage.twitchOAuthToken + } + } + wsOutput.append('SENT: ' + JSON.stringify(message) + '\n'); + ws.send(JSON.stringify(message)); } -function connect() { - var heartbeatInterval = 1000 * 60; //ms between PING's - var reconnectInterval = 1000 * 3; //ms to wait before reconnect - var heartbeatHandle; - - ws = new WebSocket('wss://pubsub-edge.twitch.tv'); - - ws.onopen = function(event) { - $('.ws-output').append('INFO: Socket Opened\n'); - heartbeat(); - heartbeatHandle = setInterval(heartbeat, heartbeatInterval); - }; - - ws.onerror = function(error) { - $('.ws-output').append('ERR: ' + JSON.stringify(error) + '\n'); - }; - - ws.onmessage = function(event) { - message = JSON.parse(event.data); - $('.ws-output').append('RECV: ' + JSON.stringify(message) + '\n'); - if (message.type == 'RECONNECT') { - $('.ws-output').append('INFO: Reconnecting...\n'); - setTimeout(connect, reconnectInterval); - } - }; +function connect () { + const heartbeatInterval = 1000 * 60; //ms between PING's + const reconnectInterval = 1000 * 3; //ms to wait before reconnect + let heartbeatHandle; + + ws = new WebSocket('wss://pubsub-edge.twitch.tv'); + + ws.onopen = function (event) { + wsOutput.append('INFO: Socket Opened\n'); + heartbeat(); + heartbeatHandle = setInterval(heartbeat, heartbeatInterval); + } + + ws.onerror = function (error) { + wsOutput.append('ERR: ' + JSON.stringify(error) + '\n'); + } + + ws.onmessage = function (event) { + message = JSON.parse(event.data); + wsOutput.append('RECV: ' + JSON.stringify(message) + '\n'); + if (message.type == 'RECONNECT') { + wsOutput.append('INFO: Reconnecting …\n'); + setTimeout(connect, reconnectInterval); + } + } + + ws.onclose = function () { + wsOutput.append('INFO: Socket Closed\n'); + clearInterval(heartbeatHandle); + wsOutput.append('INFO: Reconnecting …\n'); + setTimeout(connect, reconnectInterval); + } +} - ws.onclose = function() { - $('.ws-output').append('INFO: Socket Closed\n'); - clearInterval(heartbeatHandle); - $('.ws-output').append('INFO: Reconnecting...\n'); - setTimeout(connect, reconnectInterval); - }; +if (document.location.hash.match(/access_token=(\w+)/)) { + parseFragment(document.location.hash); +} +if (sessionStorage.twitchOAuthToken) { + connect(); + document.querySelector('.socket').classList.remove('hidden'); + + fetch('https://api.twitch.tv/helix/users', { + method: 'GET', + headers: new Headers({ + "Accept": "application/json", + "Client-ID": clientId, + "Authorization": "Bearer " + sessionStorage.twitchOAuthToken + }) + }) + .then(function (response) { return response.json(); }) + .then(function (user) { + if (user && user.data && user.data.length > 0) { + document.querySelectorAll('.user-id').forEach(function (ele, i) { + ele.textContent = user.data[0].id; + }); + } + }); +} else { + const url = authUrl() + document.querySelector('#auth-link').setAttribute('href', url); + document.querySelector('.auth').classList.remove('hidden'); } -$(function() { - if (document.location.hash.match(/access_token=(\w+)/)) - parseFragment(document.location.hash); - if (sessionStorage.twitchOAuthToken) { - connect(); - $('.socket').show() - $.ajax({ - url: "https://api.twitch.tv/kraken/user", - method: "GET", - headers: { - "Client-ID": clientId, - "Authorization": "OAuth " + sessionStorage.twitchOAuthToken - }}) - .done(function(user) { - $('#topic-label').text("Enter a topic to listen to. For example, to listen to whispers enter topic 'whispers."+user._id+"'"); - }); - } else { - var url = authUrl() - $('#auth-link').attr("href", url); - $('.auth').show() - } +document.querySelector('#topic-form').addEventListener('submit', function (e) { + e.preventDefault(); + listen(document.querySelector('#topic-text').value); }); -$('#topic-form').submit(function() { - listen($('#topic-text').val()); - event.preventDefault(); +document.querySelectorAll('.endpoints li').forEach(function (ele, i) { + ele.addEventListener('click', function (e) { + e.preventDefault(); + document.querySelector('#topic-text').value = this.textContent; + }); }); - -