Salutations! Thanks for looking at this! I am working on trying to use the provided example for how to receive data from channel point redeems via EventSub (to eventually show in a simple browser source). I think i have everything connecting properly with oAuth and the log is showing that i have indeed subscribed to the desired events and im getting keepalive messages from the connected websocket. However, when a channel point reward is redeemed on the connected channel, the log shows nothing and there does not appear to be any response. Is there a way to show the channel point reward redeemâs info in the exampleâs log upon redeem? (for instance the reward name and user who redeemed and the rewardsâ image url?) Thanks a million!!
EventSub WebSockets with Implicit Auth Example<style>
#log {
box-sizing: border-box;
padding: 5px;
width: 100%;
height: 300px;
overflow: auto;
border: 1px solid #FFFFFF;
border-radius: 20px;
margin-bottom: 20px;
}
#log span {
margin-right: 5px;
}
#subscriptions_refresh {
cursor: pointer;
}
table {
width: 100%;
}
.delete_button {
border: 1px solid red;
cursor: pointer;
}
</style>
This example demonstrates how to connect to, create subscriptions and recieve data from EventSub WebSockets
It will just "dumb log" an EventSub notification.
It'll use Implicit auth to obtain a token to use
<ul>
<li>
<a id="authorize" target=""></a>
</li>
</ul>
<div id="log">
<div>LOG</div>
</div>
<div>
<p>From the RFC - <a href="https://discuss.dev.twitch.tv/t/rfc-0016-eventsub-websockets/32652" target="_blank">https://discuss.dev.twitch.tv/t/rfc-0016-eventsub-websockets/32652</a></p>
<ul>
<li>When using a user access token, the developer may create up to 3 WebSocket connections per ClientID/user tuple.</li>
<li>Additionally, a WebSocket connection is limited to 300 subscriptions.</li>
<li>However when using a user access token, the default value of max_total_cost will be 10 rather than the
default of 10,000 for an application access token.</li>
<li>A given websocket session ID can only use one users access token. You can't connect `fred` and `bob` subscriptions authenticated using each users access token to the same WebSocket SessionID</li>
</ul>
<p>The above may be out of date check the <a href="https://dev.twitch.tv/docs/eventsub/handling-websocket-events/#subscription-limits">Subscription limits</a> for changes</p>
<p>A Websocket can subscribe to 10 cost 1 subscriptions <span id="subscriptions_cost"></span></p>
</div>
<div>
<p>Add another user to your socket to listen to</p>
<form action="https://servername.com/strim/GYPoints4/" method="post" id="form">
<fieldset>
<label for="add_user">Username</label>
<input type="text" name="add_user" id="add_user">
<input type="submit" value="Add User" id="add_user_go">
</fieldset>
</form>
</div>
<div>Last KeepAlive: <span id="keepalive"></span></div>
<div id="subscriptions_refresh">Click to Refresh Subscriptions</div>
<div>
<table>
<thead>
<tr>
<th>Subscription ID</th>
<th>Topic</th>
<th>Condition User ID</th>
<th>Cost</th>
<th>Status</th>
<th></th>
</tr>
</thead>
<tbody id="subscriptions"></tbody>
</table>
</div>
<div id="drawing"></div>
<script type="text/javascript" src="eventsub.js"></script>
<script type="text/javascript">
// These are set for the GitHub Pages Example
// Substitute as needed
var client_id = 'x0mvw507bahn3c61z73x5psrvntcno';
var redirect = 'https://servername.com/strim/GYPoints4/';
var access_token = '';
var socket_space = '';
var session_id = '';
var my_user_id = '';
document.getElementById('authorize').setAttribute('href', 'https://id.twitch.tv/oauth2/authorize?client_id=' + client_id + '&redirect_uri=' + encodeURIComponent(redirect) + '&response_type=token&scope=channel:read:redemptions');
if (document.location.hash && document.location.hash != '') {
log('Checking for token');
var parsedHash = new URLSearchParams(window.location.hash.slice(1));
if (parsedHash.get('access_token')) {
log('Got a token');
processToken(parsedHash.get('access_token'));
}
// snip
//history.replaceState(null, '', window.location.origin + window.location.pathname);
}
function log(message) {
let p = document.createElement('div');
document.getElementById('log').prepend(p);
let tim = document.createElement('span');
let t = [
new Date().getHours(),
new Date().getMinutes(),
new Date().getSeconds()
]
t.forEach((v, i) => {
t[i] = v < 10 ? '0' + v : v;
});
tim.textContent = t.join(':');
p.append(tim);
let l = document.createElement('span');
p.append(l);
l.textContent = message;
}
function processToken(token) {
access_token = token;
fetch(
'https://api.twitch.tv/helix/users',
{
"headers": {
"Client-ID": client_id,
"Authorization": "Bearer " + access_token
}
}
)
.then(resp => resp.json())
.then(resp => {
socket_space = new initSocket(true);
// and build schnanaigans
socket_space.on('connected', (id) => {
log(`Connected to WebSocket with ${id}`);
session_id = id;
my_user_id = resp.data[0].id;
requestHooks(resp.data[0].id, my_user_id);
});
socket_space.on('session_keepalive', () => {
document.getElementById('keepalive').textContent = new Date();
});
})
.catch(err => {
console.log(err);
log('Error with Users Call');
});
}
function addUser(username) {
let url = new URL('https://api.twitch.tv/helix/users');
url.search = new URLSearchParams([['login', username]]).toString();
fetch(
url,
{
"headers": {
"Client-ID": client_id,
"Authorization": "Bearer " + access_token
}
}
)
.then(resp => resp.json())
.then(resp => {
log(`Got ${resp.data[0].id} for ${username}`);
requestHooks(resp.data[0].id, my_user_id);
})
.catch(err => {
console.log(err);
log('Error with Users Call');
});
}
function requestHooks(user_id, my_id) {
let eventSubTypes = {
// 'channel.update': { version: "1", condition: { broadcaster_user_id: user_id } },
// 'channel.follow': { version: "2", condition: { broadcaster_user_id: user_id, moderator_user_id: my_id } },
//inbound raid
// 'channel.raid': { version: "1", condition: { to_broadcaster_user_id: user_id } },
// 'stream.online': { version: "1", condition: { broadcaster_user_id: user_id } },
// 'stream.offline': { version: "1", condition: { broadcaster_user_id: user_id } },
// 'user.update': { version: "1", condition: { user_id: user_id } },
'channel.channel_points_custom_reward.add': { version: "1", condition: { broadcaster_user_id: user_id } },
'channel.channel_points_custom_reward.update': { version: "1", condition: { broadcaster_user_id: user_id } },
'channel.channel_points_custom_reward_redemption.add': { version: "1", condition: { broadcaster_user_id: user_id } },
'channel.channel_points_custom_reward_redemption.update': { version: "1", condition: { broadcaster_user_id: user_id } }
}
log(`Spawn Topics for ${user_id}`);
for (let type in eventSubTypes) {
log(`Attempt create ${type} - ${user_id}`);
let { version, condition } = eventSubTypes[type];
spawnHook(type, version, condition);
}
}
function spawnHook(type, version, condition) {
fetch(
'https://api.twitch.tv/helix/eventsub/subscriptions',
{
"method": "POST",
"headers": {
"Client-ID": client_id,
"Authorization": "Bearer " + access_token,
'Content-Type': 'application/json'
},
"body": JSON.stringify({
type,
version,
condition,
transport: {
method: "websocket",
session_id
}
})
}
)
.then(resp => resp.json())
.then(resp => {
if (resp.error) {
log(`Error with eventsub Call ${type} Call: ${resp.message ? resp.message : ''}`);
} else {
log(`Created ${type}`);
}
})
.catch(err => {
console.log(err);
log(`Error with eventsub Call ${type} Call: ${err.message ? err.message : ''}`);
});
}
document.getElementById('subscriptions_refresh').addEventListener('click', (e) => {
fetchSubs();
});
let subscriptions = document.getElementById('subscriptions');
function fetchSubs(after) {
let url = new URL('https://api.twitch.tv/helix/eventsub/subscriptions');
let params = {
first: 100,
status: 'enabled'
};
if (after) {
params.after = after;
}
url.search = new URLSearchParams(params).toString();
fetch(
url,
{
"method": "GET",
"headers": {
"Client-ID": client_id,
"Authorization": "Bearer " + access_token,
'Content-Type': 'application/json'
}
}
)
.then(resp => resp.json())
.then(resp => {
subscriptions.textContent = '';
resp.data.forEach(sub => {
let tr = document.createElement('tr');
subscriptions.append(tr);
add(tr, sub.id);
add(tr, sub.type);
let keys = Object.keys(sub.condition);
if (sub.condition[keys[0]]) {
add(tr, sub.condition[keys[0]]);
} else {
add(tr, sub.condition[keys[1]]);
}
add(tr, sub.cost);
add(tr, sub.status);
let td = document.createElement('td');
tr.append(td);
td.textContent = 'Delete';
td.classList.add('delete_button');
td.addEventListener('click', (e) => {
deleteSub(sub.id)
.then(resp => {
console.log('Delete', resp.status);
if (resp.status) {
td.textContent = 'Deleted';
} else {
td.textContent = `Err ${resp.status}`;
}
})
.catch(err => {
console.log(err);
log(`Error with eventsub delete`);
});
});
});
document.getElementById('subscriptions_cost').textContent = `${resp.total_cost}/${resp.max_total_cost}`;
if (resp.pagination) {
if (resp.pagination.cursor) {
fetchSubs(resp.pagination.cursor);
}
}
})
.catch(err => {
console.log(err);
log(`Error with eventsub Fetch`);
});
}
function add(tr, text) {
let td = document.createElement('td');
td.textContent = text;
tr.append(td);
}
function deleteSub(id) {
let url = new URL('https://api.twitch.tv/helix/eventsub/subscriptions');
url.search = new URLSearchParams([['id', id]]).toString();
return fetch(
url,
{
"method": "DELETE",
"headers": {
"Client-ID": client_id,
"Authorization": "Bearer " + access_token,
'Content-Type': 'application/json'
}
}
)
}
document.getElementById('form').addEventListener('submit', (e) => {
e.preventDefault();
let username = document.getElementById('add_user').value;
log(`Lets lookup and add ${username}`);
addUser(username);
});
</script>
--