From 777021834954186d35a10b6209be5f9ac82a41eb Mon Sep 17 00:00:00 2001 From: Dinip <8941012+Dinip@users.noreply.github.com> Date: Sun, 15 Nov 2020 00:18:30 +0000 Subject: [PATCH 1/6] added support for 'community-points-channel-v1' --- README.md | 5 ++- main.js | 111 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index fcf09cd..8a42da0 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ Neat little [Node](http://nodejs.org) library which lets you easily interact wit | Bits | ```channel-bits-events-v1.```

```channel-bits-events-v2.```

Example: ```channel-bits-events-v1.44322889```| ```bits:read``` | Anyone cheers on a specified channel. | | Bits Badge Notification | ```channel-bits-badge-unlocks.```

Example: ```channel-bits-badge-unlocks.44322889```| ```bits:read``` | Message sent when a user earns a new Bits badge in a particular channel, and chooses to share the notification with chat. | | Channel Points | ```channel-points-channel-v1.```

Example: ```channel-points-channel-v1.44322889```| ```channel:read:redemptions``` | A custom reward is redeemed in a channel. | +| Community Channel Points | ```community-points-channel-v1.```

Example: ```community-points-channel-v1.44322889```| No scope needed | Access to more events than standard channel points. It also outputs to the ```channel-points``` event. **Check events table below** | | Channel Subscriptions | ```channel-subscribe-events-v1.```

Example: ```channel-subscribe-events-v1.44322889```| ```channel_subscriptions``` | Anyone subscribes (first month, gift sub) or resubscribes (subsequent months) to a channel. | | Whispers | ```whispers.```

Example: ```whispers.44322889``` | ```whispers:read``` | Anyone whispers the specified user. | | Stream Status | ```video-playback.```

Example: ```video-playback.summit1g``` | No scope needed | Status on stream going up, down, and viewer count. **Not officially supported by Twitch**| @@ -72,7 +73,9 @@ ps.on('stream-up', (data) => { | 'error' | origin - {string}
error - {string}
(optional if topic is involved) topic - {string} | | 'bits' | badge_entitlement - {object} (v2 only)
bits_used - {integer}
channel_id - {string}
channel_name - {string}
chat_message - {string}
context - {string}
is_anonymous - {boolean} (v2 only)
message_id - {string}
message_type - {string}
time - {string}
total_bits_used - {integer}
user_id - {string}
user_name - {string}
version - {string} | | 'bits-badge' | user_id - {string} - ID of user who earned the new Bits badge
user_name - {string} - Login of user who earned the new Bits badge
channel_id - {string} - ID of channel where user earned the new Bits badge
channel_name - {string} - Login of channel where user earned the new Bits badge
badge_tier - {int} - Value of Bits badge tier that was earned (1000, 10000, etc.)
chat_message - {string} - [Optional] Custom message included with share
time - {string} - Time when the bits were used. RFC 3339 format | -| 'channel-points' | timestamp - {string} - Time the pubsub message was sent
redemption - {object} - Data about the redemption, includes unique id and user that redeemed it
channel_id - {string} - ID of the channel in which the reward was redeemed.
redeemed_at - {string} - Timestamp in which a reward was redeemed
reward - {object} - Data about the reward that was redeemed
user_input - {string} - [Optional] A string that the user entered if the reward requires input
status - {string} - reward redemption status, will be FULFULLED if a user skips the reward queue, UNFULFILLED otherwise | +| 'channel-points'
(from ```channel-points-channel-v1``` &
```community-points-channel-v1```) | timestamp - {string} - Time the pubsub message was sent
redemption - {object} - Data about the redemption, includes unique id and user that redeemed it
channel_id - {string} - ID of the channel in which the reward was redeemed.
redeemed_at - {string} - Timestamp in which a reward was redeemed
reward - {object} - Data about the reward that was redeemed
user_input - {string} - [Optional] A string that the user entered if the reward requires input
status - {string} - reward redemption status, will be FULFULLED if a user skips the reward queue, UNFULFILLED otherwise | +| 'community-points-all' | type - {string}
timestamp - {string}
event - {object} | +| 'community-reward-created',
'community-reward-updated',
'community-reward-deleted',
'community-goal-created',
'community-goal-updated',
'community-goal-deleted' | timestamp - {string}
event - {object} | | 'subscribe' | user_name - {string}
display_name - {string}
channel_name - {string}
user_id - {string}
channel_id- {string}
time- {string}
sub_plan- {string}
sub_plan_name - {string}
months - {integer}
cumulative_months - {integer}
context - {string}
sub_message - {object}
sub_message.message - {string}
sub_message.emotes - {array}
recipient_id - {integer}
recipient_user_name - {string}
recipient_display_name - {string} | | 'whisper_sent' & 'whisper_received' | id - {integer}
body - {string}
thread_id - {string}
sender - {JSON}
sender.id - {integer}
sender.username - {string}
sender.display_name - {string}
sender.color - {string}
sender.badges - {Array}
sender.emotes - {Array}
recipient - {JSON}
recipient.id - {integer}
recipient.username - {string}
recipient.display_name - {string}
recipient.color - {string}
recipient.badges - {Array}
sent_ts - {integer}
nonce - {string} | | 'stream-up' | time - {integer}
channel_name- {string}
play_delay - {string} | diff --git a/main.js b/main.js index 483b6d9..592e019 100644 --- a/main.js +++ b/main.js @@ -117,6 +117,9 @@ class TwitchPS extends EventEmitter { case 'channel-points-channel-v1': self._onChannelPoints(message); break; + case 'community-points-channel-v1': + self._onCommunityPoints(message); + break; case 'channel-subscribe-events-v1': self._onSub(message); break; @@ -289,6 +292,114 @@ class TwitchPS extends EventEmitter { }); } + /** + * Handles Community Channel Points Event Message + * @param message - {object} - Message object received from pubsub-edge + * @param message.type - {string} - Type of message - Will always be 'MESSAGE' - Handled by _connect() + * @param message.data - {JSON} - JSON wrapper of topic/message fields + * @param message.data.topic - {string} - Topic that message pertains too - Will always be 'community-points-channel-v1.' - Handled by _connect() + * @param message.data.message - {JSON} - Parsed into JSON in _connect() - Originally received as string from Twitch + * @emits community-points-all, community-custom-reward-created, community-custom-reward-updated + * community-custom-reward-deleted, community-goal-created, community-goal-updated, + * community-goal-deleted, channel-points + * community-points-all + * JSON object - + * type - {string} - Type of the event + * timestamp - {string} - Time the pubsub message was sent + * event - {object} - All data from all points events + * community-reward-created + * JSON object - + * timestamp - {string} - Time the pubsub message was sent + * event - {object} - All data from the event + * community-reward-updated + * JSON object - + * timestamp - {string} - Time the pubsub message was sent + * event - {object} - All data from the event + * community-reward-deleted + * JSON object - + * timestamp - {string} - Time the pubsub message was sent + * event - {object} - All data from the event + * community-goal-created + * JSON object - + * timestamp - {string} - Time the pubsub message was sent + * event - {object} - All data from the event + * community-goal-updated + * JSON object - + * timestamp - {string} - Time the pubsub message was sent + * event - {object} - All data from the event + * community-goal-deleted + * JSON object - + * timestamp - {string} - Time the pubsub message was sent + * event - {object} - All data from the event + * channel-points + * JSON object - + * timestamp - {string} - Time the pubsub message was sent + * redemption - {object} - Data about the redemption, includes unique id and user that redeemed it + * channel_id - {string} - ID of the channel in which the reward was redeemed. + * redeemed_at - {string} - Timestamp in which a reward was redeemed + * reward - {object} - Data about the reward that was redeemed + * user_input - {string} - [Optional] A string that the user entered if the reward requires input + * status - {string} - reward redemption status, will be FULFULLED if a user skips the reward queue, UNFULFILLED otherwise + */ + _onCommunityPoints(message) { + this.emit('community-points-all', { + "type": message.data.message.type, + "timestamp": message.data.message.data.timestamp, + "event": message.data.message.data[`${Object.keys(message.data.message.data)[1]}`] + }); + switch(message.data.message.type){ + case "custom-reward-created": + this.emit('community-reward-created', { + "timestamp": message.data.message.data.timestamp, + "event": message.data.message.data.new_reward + }); + break; + case "custom-reward-updated": + this.emit('community-reward-updated', { + "timestamp": message.data.message.data.timestamp, + "event": message.data.message.data.updated_reward + }); + break; + case "custom-reward-deleted": + this.emit('community-reward-deleted', { + "timestamp": message.data.message.data.timestamp, + "event": message.data.message.data.deleted_reward + }); + break; + case "community-goal-created": + this.emit('community-goal-created', { + "timestamp": message.data.message.data.timestamp, + "event": message.data.message.data.community_goal + }); + break; + case "community-goal-updated": + this.emit('community-goal-updated', { + "timestamp": message.data.message.data.timestamp, + "event": message.data.message.data.community_goal + }); + break; + case "community-goal-deleted": + this.emit('community-goal-deleted', { + "timestamp": message.data.message.data.timestamp, + "event": message.data.message.data.community_goal + }); + break; + case "reward-redeemed": + this.emit('channel-points', { + "timestamp": message.data.message.data.timestamp, + "redemption": message.data.message.data.redemption, + "channel_id": message.data.message.data.redemption.channel_id, + "redeemed_at": message.data.message.data.redemption.redeemed_at, + "reward": message.data.message.data.redemption.reward, + "user_input": message.data.message.data.redemption.user_input, + "status": message.data.message.data.redemption.status + }); + break; + default: + // Do Nothing + } + } + /** * Handles Subscription Message * @param message - {object} - Message object received from pubsub-edge From bcbd64244613f4a805fa386e3b7b18f68ced06bf Mon Sep 17 00:00:00 2001 From: Dinip <8941012+Dinip@users.noreply.github.com> Date: Sun, 15 Nov 2020 00:28:35 +0000 Subject: [PATCH 2/6] little fix to readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8a42da0..156d29e 100644 --- a/README.md +++ b/README.md @@ -73,9 +73,9 @@ ps.on('stream-up', (data) => { | 'error' | origin - {string}
error - {string}
(optional if topic is involved) topic - {string} | | 'bits' | badge_entitlement - {object} (v2 only)
bits_used - {integer}
channel_id - {string}
channel_name - {string}
chat_message - {string}
context - {string}
is_anonymous - {boolean} (v2 only)
message_id - {string}
message_type - {string}
time - {string}
total_bits_used - {integer}
user_id - {string}
user_name - {string}
version - {string} | | 'bits-badge' | user_id - {string} - ID of user who earned the new Bits badge
user_name - {string} - Login of user who earned the new Bits badge
channel_id - {string} - ID of channel where user earned the new Bits badge
channel_name - {string} - Login of channel where user earned the new Bits badge
badge_tier - {int} - Value of Bits badge tier that was earned (1000, 10000, etc.)
chat_message - {string} - [Optional] Custom message included with share
time - {string} - Time when the bits were used. RFC 3339 format | -| 'channel-points'
(from ```channel-points-channel-v1``` &
```community-points-channel-v1```) | timestamp - {string} - Time the pubsub message was sent
redemption - {object} - Data about the redemption, includes unique id and user that redeemed it
channel_id - {string} - ID of the channel in which the reward was redeemed.
redeemed_at - {string} - Timestamp in which a reward was redeemed
reward - {object} - Data about the reward that was redeemed
user_input - {string} - [Optional] A string that the user entered if the reward requires input
status - {string} - reward redemption status, will be FULFULLED if a user skips the reward queue, UNFULFILLED otherwise | +| 'channel-points'
(from ```channel-points-channel-v1``` & ```community-points-channel-v1```) | timestamp - {string} - Time the pubsub message was sent
redemption - {object} - Data about the redemption, includes unique id and user that redeemed it
channel_id - {string} - ID of the channel in which the reward was redeemed.
redeemed_at - {string} - Timestamp in which a reward was redeemed
reward - {object} - Data about the reward that was redeemed
user_input - {string} - [Optional] A string that the user entered if the reward requires input
status - {string} - reward redemption status, will be FULFULLED if a user skips the reward queue, UNFULFILLED otherwise | | 'community-points-all' | type - {string}
timestamp - {string}
event - {object} | -| 'community-reward-created',
'community-reward-updated',
'community-reward-deleted',
'community-goal-created',
'community-goal-updated',
'community-goal-deleted' | timestamp - {string}
event - {object} | +| 'community-reward-created'
'community-reward-updated'
'community-reward-deleted'
'community-goal-created'
'community-goal-updated'
'community-goal-deleted' | timestamp - {string}
event - {object} | | 'subscribe' | user_name - {string}
display_name - {string}
channel_name - {string}
user_id - {string}
channel_id- {string}
time- {string}
sub_plan- {string}
sub_plan_name - {string}
months - {integer}
cumulative_months - {integer}
context - {string}
sub_message - {object}
sub_message.message - {string}
sub_message.emotes - {array}
recipient_id - {integer}
recipient_user_name - {string}
recipient_display_name - {string} | | 'whisper_sent' & 'whisper_received' | id - {integer}
body - {string}
thread_id - {string}
sender - {JSON}
sender.id - {integer}
sender.username - {string}
sender.display_name - {string}
sender.color - {string}
sender.badges - {Array}
sender.emotes - {Array}
recipient - {JSON}
recipient.id - {integer}
recipient.username - {string}
recipient.display_name - {string}
recipient.color - {string}
recipient.badges - {Array}
sent_ts - {integer}
nonce - {string} | | 'stream-up' | time - {integer}
channel_name- {string}
play_delay - {string} | From 64afdaccd802b8a4226a8b88412576c2d0cffa82 Mon Sep 17 00:00:00 2001 From: John Cummings Date: Tue, 17 Nov 2020 10:42:41 -0700 Subject: [PATCH 3/6] fix(community-points) Separated channel-points and community-points topics to emit separate events. --- README.md | 5 +++-- main.js | 22 +++++++++++----------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 156d29e..bfbc7ce 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Neat little [Node](http://nodejs.org) library which lets you easily interact wit | Bits | ```channel-bits-events-v1.```

```channel-bits-events-v2.```

Example: ```channel-bits-events-v1.44322889```| ```bits:read``` | Anyone cheers on a specified channel. | | Bits Badge Notification | ```channel-bits-badge-unlocks.```

Example: ```channel-bits-badge-unlocks.44322889```| ```bits:read``` | Message sent when a user earns a new Bits badge in a particular channel, and chooses to share the notification with chat. | | Channel Points | ```channel-points-channel-v1.```

Example: ```channel-points-channel-v1.44322889```| ```channel:read:redemptions``` | A custom reward is redeemed in a channel. | -| Community Channel Points | ```community-points-channel-v1.```

Example: ```community-points-channel-v1.44322889```| No scope needed | Access to more events than standard channel points. It also outputs to the ```channel-points``` event. **Check events table below** | +| Community Channel Points | ```community-points-channel-v1.```

Example: ```community-points-channel-v1.44322889```| No scope needed | Access to more events than standard channel points. It outputs to the ```reward-redeemed``` event. **Check events table below** | | Channel Subscriptions | ```channel-subscribe-events-v1.```

Example: ```channel-subscribe-events-v1.44322889```| ```channel_subscriptions``` | Anyone subscribes (first month, gift sub) or resubscribes (subsequent months) to a channel. | | Whispers | ```whispers.```

Example: ```whispers.44322889``` | ```whispers:read``` | Anyone whispers the specified user. | | Stream Status | ```video-playback.```

Example: ```video-playback.summit1g``` | No scope needed | Status on stream going up, down, and viewer count. **Not officially supported by Twitch**| @@ -73,7 +73,8 @@ ps.on('stream-up', (data) => { | 'error' | origin - {string}
error - {string}
(optional if topic is involved) topic - {string} | | 'bits' | badge_entitlement - {object} (v2 only)
bits_used - {integer}
channel_id - {string}
channel_name - {string}
chat_message - {string}
context - {string}
is_anonymous - {boolean} (v2 only)
message_id - {string}
message_type - {string}
time - {string}
total_bits_used - {integer}
user_id - {string}
user_name - {string}
version - {string} | | 'bits-badge' | user_id - {string} - ID of user who earned the new Bits badge
user_name - {string} - Login of user who earned the new Bits badge
channel_id - {string} - ID of channel where user earned the new Bits badge
channel_name - {string} - Login of channel where user earned the new Bits badge
badge_tier - {int} - Value of Bits badge tier that was earned (1000, 10000, etc.)
chat_message - {string} - [Optional] Custom message included with share
time - {string} - Time when the bits were used. RFC 3339 format | -| 'channel-points'
(from ```channel-points-channel-v1``` & ```community-points-channel-v1```) | timestamp - {string} - Time the pubsub message was sent
redemption - {object} - Data about the redemption, includes unique id and user that redeemed it
channel_id - {string} - ID of the channel in which the reward was redeemed.
redeemed_at - {string} - Timestamp in which a reward was redeemed
reward - {object} - Data about the reward that was redeemed
user_input - {string} - [Optional] A string that the user entered if the reward requires input
status - {string} - reward redemption status, will be FULFULLED if a user skips the reward queue, UNFULFILLED otherwise | +| 'reward-redeemed'
(from ```community-points-channel-v1```) | timestamp - {string} - Time the pubsub message was sent
redemption - {object} - Data about the redemption, includes unique id and user that redeemed it
channel_id - {string} - ID of the channel in which the reward was redeemed.
redeemed_at - {string} - Timestamp in which a reward was redeemed
reward - {object} - Data about the reward that was redeemed
user_input - {string} - [Optional] A string that the user entered if the reward requires input
status - {string} - reward redemption status, will be FULFULLED if a user skips the reward queue, UNFULFILLED otherwise | +| 'channel-points'
(from ```channel-points-channel-v1```) | timestamp - {string} - Time the pubsub message was sent
redemption - {object} - Data about the redemption, includes unique id and user that redeemed it
channel_id - {string} - ID of the channel in which the reward was redeemed.
redeemed_at - {string} - Timestamp in which a reward was redeemed
reward - {object} - Data about the reward that was redeemed
user_input - {string} - [Optional] A string that the user entered if the reward requires input
status - {string} - reward redemption status, will be FULFULLED if a user skips the reward queue, UNFULFILLED otherwise | | 'community-points-all' | type - {string}
timestamp - {string}
event - {object} | | 'community-reward-created'
'community-reward-updated'
'community-reward-deleted'
'community-goal-created'
'community-goal-updated'
'community-goal-deleted' | timestamp - {string}
event - {object} | | 'subscribe' | user_name - {string}
display_name - {string}
channel_name - {string}
user_id - {string}
channel_id- {string}
time- {string}
sub_plan- {string}
sub_plan_name - {string}
months - {integer}
cumulative_months - {integer}
context - {string}
sub_message - {object}
sub_message.message - {string}
sub_message.emotes - {array}
recipient_id - {integer}
recipient_user_name - {string}
recipient_display_name - {string} | diff --git a/main.js b/main.js index 592e019..9cfde96 100644 --- a/main.js +++ b/main.js @@ -348,6 +348,17 @@ class TwitchPS extends EventEmitter { "event": message.data.message.data[`${Object.keys(message.data.message.data)[1]}`] }); switch(message.data.message.type){ + case "reward-redeemed": + this.emit('reward-redeemed', { + "timestamp": message.data.message.data.timestamp, + "redemption": message.data.message.data.redemption, + "channel_id": message.data.message.data.redemption.channel_id, + "redeemed_at": message.data.message.data.redemption.redeemed_at, + "reward": message.data.message.data.redemption.reward, + "user_input": message.data.message.data.redemption.user_input, + "status": message.data.message.data.redemption.status + }); + break; case "custom-reward-created": this.emit('community-reward-created', { "timestamp": message.data.message.data.timestamp, @@ -384,17 +395,6 @@ class TwitchPS extends EventEmitter { "event": message.data.message.data.community_goal }); break; - case "reward-redeemed": - this.emit('channel-points', { - "timestamp": message.data.message.data.timestamp, - "redemption": message.data.message.data.redemption, - "channel_id": message.data.message.data.redemption.channel_id, - "redeemed_at": message.data.message.data.redemption.redeemed_at, - "reward": message.data.message.data.redemption.reward, - "user_input": message.data.message.data.redemption.user_input, - "status": message.data.message.data.redemption.status - }); - break; default: // Do Nothing } From a774777a9be990c249b9d0133f6102bb513006a3 Mon Sep 17 00:00:00 2001 From: John Cummings Date: Wed, 17 May 2023 10:13:55 -0600 Subject: [PATCH 4/6] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index bfbc7ce..990ba4b 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ [![dependency status](https://david-dm.org/jctrvlr/twitchps.svg)](https://david-dm.org/jctrvlr/twitchps) [![Downloads](https://img.shields.io/npm/dm/twitchps.svg?style=flat)](https://www.npmjs.org/package/twitchps) [![Version](https://img.shields.io/npm/v/twitchps.svg?style=flat)](https://www.npmjs.org/package/twitchps) [![GitHub issues](https://img.shields.io/github/issues/jctrvlr/twitchPS.svg)](https://github.com/jctrvlr/twitchPS/issues) [![Build Status](https://travis-ci.org/jctrvlr/twitchPS.svg?branch=master)](https://travis-ci.org/jctrvlr/twitchPS) + + ## NO LONGER MAINTAINED. Neat little [Node](http://nodejs.org) library which lets you easily interact with the Twitch.tv PubSub service. From 00ee13792a6d03ece5626251bea8fe12a46e358d Mon Sep 17 00:00:00 2001 From: John Cummings Date: Mon, 30 Dec 2024 17:08:13 -0500 Subject: [PATCH 5/6] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 990ba4b..cf88796 100644 --- a/README.md +++ b/README.md @@ -112,6 +112,6 @@ ps.removeTopic([{topic: "video-playback.starladder_cs_en"}]); ## Community -- Follow [@thecuriouseng](https://twitter.com/thecuriouseng) on Twitter +- Follow [@thehamsti](https://x.com/thehamsti) on X - Have a question that is not a bug report? - Tweet me [@thecuriouseng](https://twitter.com/thecuriouseng) - Found a bug ? [Submit an issue](https://github.com/jctrvlr/twitchps/issues/new). From 09ff02e3be816256c15904cc9f6cc4aa9ce81e70 Mon Sep 17 00:00:00 2001 From: John Cummings Date: Mon, 30 Dec 2024 17:09:06 -0500 Subject: [PATCH 6/6] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index cf88796..13dd57c 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -# [TwitchPS](https://github.com/jctrvlr/twitchps) +# [TwitchPS](https://github.com/thehamsti/twitchps) - [![dependency status](https://david-dm.org/jctrvlr/twitchps.svg)](https://david-dm.org/jctrvlr/twitchps) - [![Downloads](https://img.shields.io/npm/dm/twitchps.svg?style=flat)](https://www.npmjs.org/package/twitchps) [![Version](https://img.shields.io/npm/v/twitchps.svg?style=flat)](https://www.npmjs.org/package/twitchps) [![GitHub issues](https://img.shields.io/github/issues/jctrvlr/twitchPS.svg)](https://github.com/jctrvlr/twitchPS/issues) [![Build Status](https://travis-ci.org/jctrvlr/twitchPS.svg?branch=master)](https://travis-ci.org/jctrvlr/twitchPS) + [![dependency status](https://david-dm.org/thehamsti/twitchps.svg)](https://david-dm.org/thehamsti/twitchps) + [![Downloads](https://img.shields.io/npm/dm/twitchps.svg?style=flat)](https://www.npmjs.org/package/twitchps) [![Version](https://img.shields.io/npm/v/twitchps.svg?style=flat)](https://www.npmjs.org/package/twitchps) [![GitHub issues](https://img.shields.io/github/issues/thehamsti/twitchPS.svg)](https://github.com/thehamsti/twitchPS/issues) [![Build Status](https://travis-ci.org/thehamsti/twitchPS.svg?branch=master)](https://travis-ci.org/thehamsti/twitchPS) ## NO LONGER MAINTAINED.