Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

twitch-webhook

Package Overview
Dependencies
Maintainers
2
Versions
10
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

twitch-webhook - npm Package Compare versions

Comparing version 1.0.2 to 1.1.0

4

examples/main.js

@@ -22,4 +22,4 @@ const TwitchWebhook = require('twitch-webhook')

twitchWebhook.on('*', ({ topic, event }) => {
console.log(event)
twitchWebhook.on('*', ({ topic, endpoint, event }) => {
console.log(topic, event)
})

@@ -26,0 +26,0 @@

{
"name": "twitch-webhook",
"version": "1.0.2",
"version": "1.1.0",
"description": "A Node JS library for new Twitch Helix API Webhooks",

@@ -35,2 +35,3 @@ "main": "src/index.js",

"is-absolute-url": "^2.1.0",
"parse-link-header": "^1.0.1",
"querystring": "^0.2.0",

@@ -37,0 +38,0 @@ "request": "^2.83.0",

@@ -34,12 +34,12 @@ # Node.js Twitch Helix Webhooks

twitchWebhook.on('streams', ({ topic, event }) => {
twitchWebhook.on('streams', ({ topic, endpoint, event }) => {
console.log(event)
})
twitchWebhook.on('users/follows', ({ topic, event }) => {
twitchWebhook.on('users/follows', ({ topic, endpoint, event }) => {
console.log(event)
})
twitchWebhook.on('*', ({ topic, event }) => {
console.log(event)
twitchWebhook.on('*', ({ topic, endpoint, event }) => {
console.log(topic, event)
})

@@ -46,0 +46,0 @@

@@ -11,2 +11,3 @@ const errors = require('./errors')

const isAbsoluteUrl = require('is-absolute-url')
const parseLinkHeader = require('parse-link-header')

@@ -84,2 +85,3 @@ /**

* @param {...any} [args] - Arguments
* @throws {Promise<FatalError>} If listening is already started
* @return {Promise}

@@ -89,3 +91,3 @@ */

if (this.isListening()) {
return Promise.reject(new errors.FatalError('Listen already started'))
return Promise.reject(new errors.FatalError('Listening is already started'))
}

@@ -144,2 +146,3 @@

* @param {Object} options - Topic options
* @throws {Promise<RequestDenied>} If the hub finds any errors in the request
* @return {Promise}

@@ -179,9 +182,5 @@ */

.catch(err => {
throw new errors.FatalError(err)
throw new errors.RequestDenied(err)
})
.then(response => {
if (response.statusCode !== 202) {
throw new errors.RequestDenied(response)
}
if (this._options.secret) {

@@ -198,2 +197,4 @@ this._secrets[topic] = requestOptions.qs['hub.secret']

* @param {Object} options - Topic options
* @throws {RequestDenied} If hub finds any errors in the request
* @return {Promise}
*/

@@ -208,2 +209,3 @@ subscribe (topic, options = {}) {

* @param {string} topic - Topic name
* @throws {RequestDenied} If hub finds any errors in the request
* @return {Promise}

@@ -230,3 +232,3 @@ */

_processConnection (request, response) {
const queries = url.parse(request.url, true).query || {}
const queries = url.parse(request.url, true).query

@@ -255,3 +257,3 @@ switch (queries['hub.mode']) {

/**
* Fix fields with date in response
* Fix fields with date
*

@@ -263,3 +265,3 @@ * @private

*/
_fixDateInResponse (topic, data) {
_fixDate (topic, data) {
switch (topic) {

@@ -270,3 +272,5 @@ case 'users/follows':

case 'streams':
data.started_at = new Date(data.started_at)
for (let stream of data.data) {
stream.started_at = new Date(stream.started_at)
}
break

@@ -286,2 +290,11 @@ }

_processUpdates (request, response) {
const links = parseLinkHeader(request.headers.link)
const endpoint = links && links.self && links.self.url
const topic = endpoint && url.parse(endpoint, true).pathname.replace('/helix/', '')
if (!endpoint || !topic) {
response.writeHead(202, { 'Content-Type': 'text/plain' })
response.end()
return
}
let signature

@@ -323,16 +336,10 @@ if (this._options.secret) {

const topic = data && data.topic
const topicName =
topic && url.parse(topic).pathname.replace('/helix/', '')
if (!topic || !topicName) {
response.writeHead(202, { 'Content-Type': 'text/plain' })
response.end()
return
}
if (this._options.secret) {
const storedSign = crypto
.createHmac('sha256', this._secrets[topic])
.update(body)
.digest('hex')
let storedSign
if (this._secrets[endpoint]) {
storedSign = crypto
.createHmac('sha256', this._secrets[endpoint])
.update(body)
.digest('hex')
}

@@ -350,6 +357,7 @@ if (storedSign !== signature) {

let payload = {}
payload.topic = topicName
payload.event = this._fixDateInResponse(topicName, data)
payload.topic = topic
payload.endpoint = endpoint
payload.event = this._fixDate(topic, data)
this.emit(topicName, payload)
this.emit(topic, payload)
this.emit('*', payload)

@@ -356,0 +364,0 @@ })

@@ -5,7 +5,11 @@ const assert = require('assert')

function checkResponseCode (requestOptions, requiredCode) {
function sendRequest (requestOptions) {
requestOptions.resolveWithFullResponse = true
requestOptions.simple = false
return request(requestOptions).then(response => {
return request(requestOptions);
}
function checkResponseCode (requestOptions, requiredCode) {
return sendRequest(requestOptions).then(response => {
assert.equal(

@@ -42,2 +46,3 @@ response.statusCode,

module.exports = {
sendRequest,
checkResponseCode,

@@ -44,0 +49,0 @@ hasStartedListening,

@@ -45,2 +45,30 @@ const TwitchWebhook = require('../src/index')

it('should contain errors', (done) => {
assert(twitchWebhook.errors instanceof Object);
done()
})
it('should throw FatalError if the Twitch Client ID is not provided', (done) => {
try {
let testWebhook = new TwitchWebhook();
done(new Error('expected error'))
} catch (err) {
assert(err instanceof errors.FatalError);
done()
}
})
it('should throw FatalError if the Callback URL is not provided', (done) => {
try {
let testWebhook = new TwitchWebhook({
client_id
});
done(new Error('expected error'))
} catch (err) {
assert(err instanceof errors.FatalError);
done()
}
})
it('should automaticaly start listening by default', () => {

@@ -100,3 +128,3 @@ assert.equal(twitchWebhook.isListening(), true)

if (!response.body) {
throw new Error('expeced "hub.challenge"')
throw new Error('expected "hub.challenge"')
}

@@ -143,3 +171,3 @@ })

it('returns 200 response code if everything is ok', () => {
it('returns 202 error code if topic is incorrect', () => {
return helpers.checkResponseCode(

@@ -149,6 +177,20 @@ {

method: 'POST',
json: {
topic: 'https://api.twitch.tv/helix/users/follows?to_id=1337'
headers: {
link: '<https://api.twitch.tv/helix/>; rel="self"'
}
},
202
)
})
it('returns 200 response code if everything is ok', () => {
return helpers.checkResponseCode(
{
url: `http://127.0.0.1:${port}`,
method: 'POST',
headers: {
link: '<https://api.twitch.tv/helix/users/follows?to_id=1337>; rel="self"'
},
json: {}
},
200

@@ -174,2 +216,142 @@ )

describe('events', () => {
it('emits "denied" event if request with denied status was received', (done) => {
twitchWebhook.once(
'denied',
() => done()
)
helpers.sendRequest(
{
url: `http://127.0.0.1:${port}`,
qs: {
'hub.mode': 'denied',
'hub.topic': 'https://api.twitch.tv/helix/users/follows?to_id=1337',
'hub.reason': 'unauthorized'
}
}
);
})
it('emits "subscribe" event if the subscribe request was received', (done) => {
twitchWebhook.once(
'subscribe',
() => done()
)
helpers.sendRequest(
{
url: `http://127.0.0.1:${port}`,
qs: {
'hub.mode': 'subscribe',
'hub.topic': 'https://api.twitch.tv/helix/users/follows?to_id=1337',
'hub.lease_seconds': 864000,
'hub.challenge': 'HzSGH_h04Cgl6VbDJm7IyXSNSlrhaLvBi9eft3bw'
}
}
);
})
it('emits "unsubscribe" event if the unsubscribe request was received', (done) => {
twitchWebhook.once(
'unsubscribe',
() => done()
)
helpers.sendRequest(
{
url: `http://127.0.0.1:${port}`,
qs: {
'hub.mode': 'unsubscribe',
'hub.topic': 'https://api.twitch.tv/helix/users/follows?to_id=1337',
'hub.lease_seconds': 864000,
'hub.challenge': 'HzSGH_h04Cgl6VbDJm7IyXSNSlrhaLvBi9eft3bw'
}
}
);
})
it('emits "*" event if request with topic was received', (done) => {
twitchWebhook.once(
'*',
() => done()
)
helpers.sendRequest(
{
url: `http://127.0.0.1:${port}`,
method: 'POST',
headers: {
link: '<https://api.twitch.tv/helix/test>; rel="self"'
},
json: {}
}
)
})
})
describe('date fix', () => {
it('should fix "timestamp" field in "users/follows" topic', (done) => {
twitchWebhook.once('users/follows', ({event}) => {
assert(event.timestamp instanceof Date)
done()
})
helpers.sendRequest(
{
url: `http://127.0.0.1:${port}`,
method: 'POST',
headers: {
link: '<https://api.twitch.tv/helix/users/follows?to_id=1337>; rel="self"'
},
json: {
id: "436c70bb-a52f-4a6a-b4cc-6c57bc2ad227",
topic: "https://api.twitch.tv/helix/users/follows?to_id=1337",
type: "create",
data: {
from_id: 1336,
to_id: 1337
},
timestamp: "2017-08-07T13:52:14.403795077Z"
}
},
200
)
})
it('should fix "started_at" fields in "streams" topic', (done) => {
twitchWebhook.once('streams', ({event}) => {
for (let stream of event.data) {
assert(stream['started_at'] instanceof Date)
}
done()
})
helpers.sendRequest(
{
url: `http://127.0.0.1:${port}`,
method: 'POST',
headers: {
link: '<https://api.twitch.tv/helix/streams?user_id=5678>; rel="self"'
},
json: {
data: [{
id: '0123456789',
user_id: 5678,
game_id: 21779,
community_ids: [],
type: 'live',
title: 'Best Stream Ever',
'viewer_count': 417,
'started_at': '2017-12-01T10:09:45Z',
language: 'en',
'thumbnail_url': 'https://link/to/thumbnail.jpg',
}]
}
}
)
})
})
describe('#listen', () => {

@@ -207,32 +389,74 @@ afterEach(() => offlineWebhook.close())

describe.skip('#subscribe', () => {})
describe('#unsubscribe', () => {
it('should throw FatalError if the request status is bad', function () {
it('should throw RequestDenied if the request status is bad', function () {
this.timeout(timeout)
return twitchWebhook.unsubscribe('streams').catch(err => {
assert(err instanceof errors.FatalError)
assert(err instanceof errors.RequestDenied)
})
})
it('should throw RequestDenied if request status is denied', function () {
it('should return nothing if everything is ok', function () {
this.timeout(timeout)
return twitchWebhook
.unsubscribe('streams', {
return twitchWebhook.unsubscribe('streams', {
user_id: 123
})
})
it('should not supplement link if topic url is absolute', function () {
this.timeout(timeout)
return twitchWebhook.unsubscribe('https://api.twitch.tv/helix/streams', {
user_id: 123
})
})
it('should not supplement link if topic options is not exists', function () {
this.timeout(timeout)
return twitchWebhook.unsubscribe('https://api.twitch.tv/helix/streams?user_id=123')
})
})
describe('#subscribe', () => {
it('should throw RequestDenied if the request status is bad', function () {
this.timeout(timeout)
return twitchWebhook.subscribe('streams').catch(err => {
assert(err instanceof errors.RequestDenied)
})
})
it('should return nothing if everything is ok', function () {
this.timeout(timeout)
return twitchWebhook.subscribe('streams', {
user_id: 123
}).then(() => {
return twitchWebhook.unsubscribe('streams', {
user_id: 123
})
.catch(err => {
assert(err instanceof errors.RequestDenied)
})
})
})
it('should return nothing if everything is ok', function () {
it('should not supplement link if topic url is absolute', function () {
this.timeout(timeout)
return twitchWebhook.unsubscribe('streams', {
return twitchWebhook.subscribe('https://api.twitch.tv/helix/streams', {
user_id: 123
}).then(() => {
return twitchWebhook.unsubscribe('https://api.twitch.tv/helix/streams', {
user_id: 123
})
})
})
it('should not supplement link if topic options is not exists', function () {
this.timeout(timeout)
return twitchWebhook.subscribe('streams?user_id=123').then(() => {
return twitchWebhook.unsubscribe('streams?user_id=123')
})
})
})

@@ -239,0 +463,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc