Repository: twitch-apis/twitch-js
Pull request #155 (2018-09-14 00:45:30)

Feature: add GIFT_PAID_UPGRADE event
## Proposed changes

Twitch implemented a new event for Subptember (and possibly applying to future promotional campaigns). This code change handles the said event.

## Types of changes

* [x] New feature (non-breaking change which adds functionality)

## Checklist

* [x] I have read the [CONTRIBUTING](https://github.com/twitch-apis/twitch-js/blob/master/CONTRIBUTING.md) doc
* [x] My PR is named according to [CONTRIBUTING](https://github.com/twitch-apis/twitch-js/blob/master/CONTRIBUTING.md) doc
* [x] Lint and unit tests pass locally with my changes
* [x] I have added tests that prove my fix is effective or that my feature works
* [x] I have added necessary documentation (if appropriate)
* [x] Any dependent changes have been merged and published in downstream modules


Patch:
diff --git a/__mocks__/ws/__fixtures__/tags.json b/__mocks__/ws/__fixtures__/tags.json
index 460a7df..fb73dfd 100644
--- a/__mocks__/ws/__fixtures__/tags.json
+++ b/__mocks__/ws/__fixtures__/tags.json
@@ -16,6 +16,7 @@
     "UPDATE_SLOW": "@slow=10 :tmi.twitch.tv ROOMSTATE #dallas"
   },
   "USERNOTICE": {
+    "GIFT_PAID_UPGRADE": "@badges=subscriber/0,premium/1;color=#8A2BE2;display-name=ElizabethSwann;emotes=;id=5bd3e064-c1e4-451b-9d7e-afe8cf5a44a2;login=elizabethswann;mod=0;msg-id=giftpaidupgrade;msg-param-promo-gift-total=123816;msg-param-promo-name=Subtember;msg-param-sender-login=cannibulcake;msg-param-sender-name=CannibulCake;room-id=7236692;subscriber=1;system-msg=ElizabethSwann\\sis\\scontinuing\\sthe\\sGift\\sSub\\sthey\\sgot\\sfrom\\sCannibulCake!\\sThey're\\sone\\sof\\s123816\\sgift\\ssubs\\sto\\scontinue\\sthis\\sSubtember.;tmi-sent-ts=1536880884587;turbo=0;user-id=85946347;user-type= :tmi.twitch.tv USERNOTICE #dansgaming",
     "SUBSCRIPTION": "@badges=staff/1,broadcaster/1,turbo/1;color=#008000;display-name=ronni;emotes=;id=db25007f-7a18-43eb-9379-80131e44d633;login=ronni;mod=0;msg-id=sub;msg-param-months=1;msg-param-sub-plan=Prime;msg-param-sub-plan-name=Prime;room-id=1337;subscriber=1;system-msg=ronni\\sjust\\ssubscribed\\swith\\sTwitch\\sPrime!;tmi-sent-ts=1507246572675;turbo=1;user-id=1337;user-type=staff :tmi.twitch.tv USERNOTICE #dallas :Great stream -- keep it up!",
     "RESUBSCRIPTION": "@badges=staff/1,broadcaster/1,turbo/1;color=#008000;display-name=ronni;emotes=;id=db25007f-7a18-43eb-9379-80131e44d633;login=ronni;mod=0;msg-id=resub;msg-param-months=6;msg-param-sub-plan=Prime;msg-param-sub-plan-name=Prime;room-id=1337;subscriber=1;system-msg=ronni\\shas\\ssubscribed\\sfor\\s6\\smonths!;tmi-sent-ts=1507246572675;turbo=1;user-id=1337;user-type=staff :tmi.twitch.tv USERNOTICE #dallas :Great stream -- keep it up!",
     "SUBSCRIPTION_GIFT": "@badges=staff/1,premium/1;color=#0000FF;display-name=TWW2;emotes=;id=e9176cd8-5e22-4684-ad40-ce53c2561c5e;login=tww2;mod=0;msg-id=subgift;msg-param-months=1;msg-param-recipient-display-name=Mr_Woodchuck;msg-param-recipient-id=89614178;msg-param-recipient-name=mr_woodchuck;msg-param-sub-plan-name=House\\sof\\sNyoro~n;msg-param-sub-plan=1000;room-id=19571752;subscriber=0;system-msg=TWW2\\sgifted\\sa\\sTier\\s1\\ssub\\sto\\sMr_Woodchuck!;tmi-sent-ts=1521159445153;turbo=0;user-id=13405587;user-type=staff :tmi.twitch.tv USERNOTICE #forstycup",
@@ -27,4 +28,4 @@
   "USERSTATE": {
     "JOIN": "@color=#0D4200;display-name=ronni;emote-sets=0,33,50,237,793,2126,3517,4578,5569,9400,10337,12239;mod=1;subscriber=1;turbo=1;user-type=staff :tmi.twitch.tv USERSTATE #dallas"
   }
-}
\ No newline at end of file
+}
diff --git a/docs/reference/class.md b/docs/reference/class.md
index 40734f4..e7c6e27 100644
--- a/docs/reference/class.md
+++ b/docs/reference/class.md
@@ -218,7 +218,7 @@ Chat client
 
 **Kind**: global class  
 **Extends**: [<code>EventEmitter3</code>](external#external_EventEmitter3)  
-**Emits**: [<code>\*</code>](#Chat+event_*), [<code>CLEARCHAT</code>](#Chat+event_CLEARCHAT), [<code>CLEARCHAT/USER_BANNED</code>](#Chat+event_CLEARCHAT/USER_BANNED), [<code>GLOBALUSERSTATE</code>](#Chat+event_GLOBALUSERSTATE), [<code>HOSTTARGET</code>](#Chat+event_HOSTTARGET), [<code>JOIN</code>](#Chat+event_JOIN), [<code>MODE</code>](#Chat+event_MODE), [<code>NAMES</code>](#Chat+event_NAMES), [<code>NAMES_END</code>](#Chat+event_NAMES_END), [<code>NOTICE</code>](#Chat+event_NOTICE), [<code>NOTICE/ROOM_MODS</code>](#Chat+event_NOTICE/ROOM_MODS), [<code>PART</code>](#Chat+event_PART), [<code>PRIVMSG</code>](#Chat+event_PRIVMSG), [<code>PRIVMSG/CHEER</code>](#Chat+event_PRIVMSG/CHEER), [<code>ROOMSTATE</code>](#Chat+event_ROOMSTATE), [<code>USERNOTICE/RAID</code>](#Chat+event_USERNOTICE/RAID), [<code>USERNOTICE/RESUBSCRIPTION</code>](#Chat+event_USERNOTICE/RESUBSCRIPTION), [<code>USERNOTICE/RITUAL</code>](#Chat+event_USERNOTICE/RITUAL), [<code>USERNOTICE/SUBSCRIPTION</code>](#Chat+event_USERNOTICE/SUBSCRIPTION), [<code>USERNOTICE/SUBSCRIPTION_GIFT</code>](#Chat+event_USERNOTICE/SUBSCRIPTION_GIFT), [<code>USERSTATE</code>](#Chat+event_USERSTATE)  
+**Emits**: [<code>\*</code>](#Chat+event_*), [<code>CLEARCHAT</code>](#Chat+event_CLEARCHAT), [<code>CLEARCHAT/USER_BANNED</code>](#Chat+event_CLEARCHAT/USER_BANNED), [<code>GLOBALUSERSTATE</code>](#Chat+event_GLOBALUSERSTATE), [<code>HOSTTARGET</code>](#Chat+event_HOSTTARGET), [<code>JOIN</code>](#Chat+event_JOIN), [<code>MODE</code>](#Chat+event_MODE), [<code>NAMES</code>](#Chat+event_NAMES), [<code>NAMES_END</code>](#Chat+event_NAMES_END), [<code>NOTICE</code>](#Chat+event_NOTICE), [<code>NOTICE/ROOM_MODS</code>](#Chat+event_NOTICE/ROOM_MODS), [<code>PART</code>](#Chat+event_PART), [<code>PRIVMSG</code>](#Chat+event_PRIVMSG), [<code>PRIVMSG/CHEER</code>](#Chat+event_PRIVMSG/CHEER), [<code>ROOMSTATE</code>](#Chat+event_ROOMSTATE), <code>Chat#event:USERNOTICE</code>, [<code>USERNOTICE/GIFT_PAID_UPGRADE</code>](#Chat+event_USERNOTICE/GIFT_PAID_UPGRADE), [<code>USERNOTICE/RAID</code>](#Chat+event_USERNOTICE/RAID), [<code>USERNOTICE/RESUBSCRIPTION</code>](#Chat+event_USERNOTICE/RESUBSCRIPTION), [<code>USERNOTICE/RITUAL</code>](#Chat+event_USERNOTICE/RITUAL), [<code>USERNOTICE/SUBSCRIPTION</code>](#Chat+event_USERNOTICE/SUBSCRIPTION), [<code>USERNOTICE/SUBSCRIPTION_GIFT</code>](#Chat+event_USERNOTICE/SUBSCRIPTION_GIFT), [<code>USERSTATE</code>](#Chat+event_USERSTATE)  
 
 * [Chat](#Chat) ⇐ [<code>EventEmitter3</code>](external#external_EventEmitter3)
     * [new Chat(options)](#new_Chat_new)
@@ -249,6 +249,7 @@ Chat client
     * ["PRIVMSG"](#Chat+event_PRIVMSG)
     * ["PRIVMSG/CHEER"](#Chat+event_PRIVMSG/CHEER)
     * ["PRIVMSG/HOSTED"](#Chat+event_PRIVMSG/HOSTED)
+    * ["USERNOTICE/GIFT_PAID_UPGRADE"](#Chat+event_USERNOTICE/GIFT_PAID_UPGRADE)
     * ["USERNOTICE/RAID"](#Chat+event_USERNOTICE/RAID)
     * ["USERNOTICE/RESUBSCRIPTION"](#Chat+event_USERNOTICE/RESUBSCRIPTION)
     * ["USERNOTICE/RITUAL"](#Chat+event_USERNOTICE/RITUAL)
@@ -889,6 +890,40 @@ When a user hosts your channel while connected as broadcaster.
 </table>
 
 
+* * *
+
+<a name="Chat+event_USERNOTICE/GIFT_PAID_UPGRADE"></a>
+
+### "USERNOTICE/GIFT_PAID_UPGRADE"
+On gifted subscription paid upgrade to a channel.
+
+**Kind**: event emitted by [<code>Chat</code>](class#Chat)  
+**Mixes**: [<code>UserStateMessage</code>](mixin#UserStateMessage)  
+**Properties**
+
+<table>
+  <thead>
+    <tr>
+      <th>Name</th><th>Type</th>
+    </tr>
+  </thead>
+  <tbody>
+<tr>
+    <td>event</td><td><code>&#x27;GIFT_PAID_UPGRADE&#x27;</code></td>
+    </tr><tr>
+    <td>parameters</td><td><code>Object</code></td>
+    </tr><tr>
+    <td>parameters.promoGiftTotal</td><td><code>number</code></td>
+    </tr><tr>
+    <td>parameters.promoName</td><td><code>string</code></td>
+    </tr><tr>
+    <td>parameters.senderLogin</td><td><code>string</code></td>
+    </tr><tr>
+    <td>parameters.senderName</td><td><code>string</code></td>
+    </tr>  </tbody>
+</table>
+
+
 * * *
 
 <a name="Chat+event_USERNOTICE/RAID"></a>
diff --git a/src/Chat/constants.js b/src/Chat/constants.js
index c152a07..60a313d 100644
--- a/src/Chat/constants.js
+++ b/src/Chat/constants.js
@@ -141,6 +141,7 @@ export const NOTICE_MESSAGE_IDS = {
 
 // See https://dev.twitch.tv/docs/irc/tags#usernotice-twitch-tags.
 export const USER_NOTICE_MESSAGE_IDS = {
+  GIFT_PAID_UPGRADE: 'giftpaidupgrade',
   RAID: 'raid',
   RESUBSCRIPTION: 'resub',
   RITUAL: 'ritual',
@@ -172,6 +173,7 @@ export const EVENTS = {
   ERROR_ENCOUNTERED: 'ERROR_ENCOUNTERED',
   PARSE_ERROR_ENCOUNTERED: 'PARSE_ERROR_ENCOUNTERED',
 
+  GIFT_PAID_UPGRADE: 'GIFT_PAID_UPGRADE',
   RAID: 'RAID',
   RESUBSCRIPTION: 'RESUBSCRIPTION',
   RITUAL: 'RITUAL',
diff --git a/src/Chat/index.js b/src/Chat/index.js
index 24ca84c..1499bc1 100644
--- a/src/Chat/index.js
+++ b/src/Chat/index.js
@@ -38,6 +38,8 @@ import * as validators from './utils/validators'
  * @emits Chat#PRIVMSG
  * @emits Chat#PRIVMSG/CHEER
  * @emits Chat#ROOMSTATE
+ * @emits Chat#USERNOTICE
+ * @emits Chat#USERNOTICE/GIFT_PAID_UPGRADE
  * @emits Chat#USERNOTICE/RAID
  * @emits Chat#USERNOTICE/RESUBSCRIPTION
  * @emits Chat#USERNOTICE/RITUAL
diff --git a/src/Chat/utils/parsers/index.js b/src/Chat/utils/parsers/index.js
index 7646d91..088a453 100644
--- a/src/Chat/utils/parsers/index.js
+++ b/src/Chat/utils/parsers/index.js
@@ -393,6 +393,19 @@ const userNoticeMessage = baseMessage => {
 
   /* eslint-disable no-fallthrough */
   switch (tags.msgId) {
+    case constants.USER_NOTICE_MESSAGE_IDS.GIFT_PAID_UPGRADE:
+    /**
+     * On gifted subscription paid upgrade to a channel.
+     * @event Chat#USERNOTICE/GIFT_PAID_UPGRADE
+     * @mixes UserStateMessage GiftPaidUpgradeMessage
+     * @property {'GIFT_PAID_UPGRADE'} event
+     * @property {Object} parameters
+     * @property {number} parameters.promoGiftTotal
+     * @property {string} parameters.promoName
+     * @property {string} parameters.senderLogin
+     * @property {string} parameters.senderName
+     */
+
     /**
      * On channel raid.
      * @event Chat#USERNOTICE/RAID
diff --git a/src/Chat/utils/parsers/tags.js b/src/Chat/utils/parsers/tags.js
index 721e19d..85cc7f3 100644
--- a/src/Chat/utils/parsers/tags.js
+++ b/src/Chat/utils/parsers/tags.js
@@ -80,6 +80,7 @@ const userNoticeMessageParameters = tags =>
       // Numbers.
       case 'Months':
       case 'MassGiftCount':
+      case 'PromoGiftTotal':
       case 'SenderCount':
       case 'ViewerCount':
         return { ...parameters, [camelCase(param)]: types.generalNumber(value) }
@@ -94,6 +95,8 @@ const userNoticeMessageParameters = tags =>
 
 const userNoticeEvent = tags => {
   switch (tags.msgId) {
+    case constants.USER_NOTICE_MESSAGE_IDS.GIFT_PAID_UPGRADE:
+      return constants.EVENTS.GIFT_PAID_UPGRADE
     case constants.USER_NOTICE_MESSAGE_IDS.RESUBSCRIPTION:
       return constants.EVENTS.RESUBSCRIPTION
     case constants.USER_NOTICE_MESSAGE_IDS.RAID:


Test patch:
diff --git a/src/Chat/__tests__/__snapshots__/index.test.js.snap b/src/Chat/__tests__/__snapshots__/index.test.js.snap
index e3dffb6..4015b35 100644
--- a/src/Chat/__tests__/__snapshots__/index.test.js.snap
+++ b/src/Chat/__tests__/__snapshots__/index.test.js.snap
@@ -903,6 +903,55 @@ Object {
 }
 `;
 
+exports[`Chat should handle messages USERNOTICE GIFT_PAID_UPGRADE 1`] = `
+Object {
+  "_raw": "@badges=subscriber/0,premium/1;color=#8A2BE2;display-name=ElizabethSwann;emotes=;id=5bd3e064-c1e4-451b-9d7e-afe8cf5a44a2;login=elizabethswann;mod=0;msg-id=giftpaidupgrade;msg-param-promo-gift-total=123816;msg-param-promo-name=Subtember;msg-param-sender-login=cannibulcake;msg-param-sender-name=CannibulCake;room-id=7236692;subscriber=1;system-msg=ElizabethSwann\\\\sis\\\\scontinuing\\\\sthe\\\\sGift\\\\sSub\\\\sthey\\\\sgot\\\\sfrom\\\\sCannibulCake!\\\\sThey're\\\\sone\\\\sof\\\\s123816\\\\sgift\\\\ssubs\\\\sto\\\\scontinue\\\\sthis\\\\sSubtember.;tmi-sent-ts=1536880884587;turbo=0;user-id=85946347;user-type= :tmi.twitch.tv USERNOTICE #dansgaming",
+  "channel": "#dansgaming",
+  "command": "USERNOTICE",
+  "event": "GIFT_PAID_UPGRADE",
+  "isSelf": false,
+  "message": undefined,
+  "parameters": Object {
+    "promoGiftTotal": 123816,
+    "promoName": "Subtember",
+    "senderLogin": "cannibulcake",
+    "senderName": "CannibulCake",
+  },
+  "systemMessage": "ElizabethSwann is continuing the Gift Sub they got from CannibulCake! They're one of 123816 gift subs to continue this Subtember.",
+  "tags": Object {
+    "badges": Object {
+      "premium": 1,
+      "subscriber": 0,
+    },
+    "bits": undefined,
+    "color": "#8A2BE2",
+    "displayName": "ElizabethSwann",
+    "emoteSets": undefined,
+    "emotes": Array [],
+    "id": "5bd3e064-c1e4-451b-9d7e-afe8cf5a44a2",
+    "isModerator": false,
+    "isSubGifter": false,
+    "isSubscriber": true,
+    "isTurboSubscriber": false,
+    "login": "elizabethswann",
+    "mod": "0",
+    "msgId": "giftpaidupgrade",
+    "msgParamPromoGiftTotal": "123816",
+    "msgParamPromoName": "Subtember",
+    "msgParamSenderLogin": "cannibulcake",
+    "msgParamSenderName": "CannibulCake",
+    "roomId": "7236692",
+    "subscriber": "1",
+    "systemMsg": "ElizabethSwann is continuing the Gift Sub they got from CannibulCake! They're one of 123816 gift subs to continue this Subtember.",
+    "tmiSentTs": "1536880884587",
+    "turbo": "0",
+    "userId": "85946347",
+    "userType": undefined,
+  },
+  "timestamp": 2018-01-01T00:00:00.000Z,
+}
+`;
+
 exports[`Chat should handle messages USERNOTICE RAID 1`] = `
 Object {
   "_raw": "@badges=turbo/1;color=#9ACD32;display-name=TestChannel;emotes=;id=3d830f12-795c-447d-af3c-ea05e40fbddb;login=testchannel;mod=0;msg-id=raid;msg-param-displayName=TestChannel;msg-param-login=testchannel;msg-param-viewerCount=15;room-id=56379257;subscriber=0;system-msg=15\\\\sraiders\\\\sfrom\\\\sTestChannel\\\\shave\\\\sjoined\\\\n!;tmi-sent-ts=1507246572675;tmi-sent-ts=1507246572675;turbo=1;user-id=123456;user-type= :tmi.twitch.tv USERNOTICE #othertestchannel",